corrade-lsl-templates

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 4  →  ?path2? @ 5
/source/rental-system/configuration.txt
@@ -0,0 +1,37 @@
####################### START CONFIGURATION ##################################
 
# The UUID of the owner of the rental box.
owner = "ccad6083-cb3b-44e0-b1ba-26c640d1236d"
 
##
# Corrade settings.
##
##
# All these settings must correspond to the settings in Corrade.ini.
##
# This is the UUID of the Corrade bot.
corrade = "0afe54ca-93fb-4c2f-b7da-0623aec25a63"
# The name of the group - it can also be the UUID of the group.
group = "My Group"
# The password for the group.
password = "mypassword"
##
 
##
# Rental settings.
##
# The rental settings define a price for an amount of time such that the renter may pay any amount and
# the script is designed to compute the time that will be added to their rent.
##
# The price for renting this place in L$.
price = 100
# The time that this place can be rented for in seconds.
# Cheatsheet:
# 1 day is 86400 seconds (default)
# 1 week is 604800 seconds
rent = 120
# The name of the role to invite renters to.
role = "Ecto"
##
 
####################### END CONFIGURATION ###################################
/source/rental-system/rental-system.lsl
@@ -0,0 +1,975 @@
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
// rights of fair usage, the disclaimer and warranty conditions. //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// escapes a string in conformance with RFC1738
string wasURLEscape(string i) {
string o = "";
do {
string c = llGetSubString(i, 0, 0);
i = llDeleteSubString(i, 0, 0);
if(c == "") jump continue;
if(c == " ") {
o += "+";
jump continue;
}
if(c == "\n") {
o += "%0D" + llEscapeURL(c);
jump continue;
}
o += llEscapeURL(c);
@continue;
} while(i != "");
return o;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// unescapes a string in conformance with RFC1738
string wasURLUnescape(string i) {
return llUnescapeURL(
llDumpList2String(
llParseString2List(
llDumpList2String(
llParseString2List(
i,
["+"],
[]
),
" "
),
["%0D%0A"],
[]
),
"\n"
)
);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
list k = llList2ListStrided(data, 0, -1, 2);
list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
data = [];
do {
data += llList2String(k, 0) + "=" + llList2String(v, 0);
k = llDeleteSubList(k, 0, 0);
v = llDeleteSubList(v, 0, 0);
} while(llGetListLength(k) != 0);
return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
if(llStringLength(data) == 0) return "";
if(llStringLength(k) == 0) return "";
list a = llParseString2List(data, ["&", "="], []);
integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
if(i != -1) return llList2String(a, 2*i+1);
return "";
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
list wasCSVToList(string csv) {
list l = [];
list s = [];
string m = "";
do {
string a = llGetSubString(csv, 0, 0);
csv = llDeleteSubString(csv, 0, 0);
if(a == ",") {
if(llList2String(s, -1) != "\"") {
l += m;
m = "";
jump continue;
}
m += a;
jump continue;
}
if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
m += a;
csv = llDeleteSubString(csv, 0, 0);
jump continue;
}
if(a == "\"") {
if(llList2String(s, -1) != a) {
s += a;
jump continue;
}
s = llDeleteSubList(s, -1, -1);
jump continue;
}
m += a;
@continue;
} while(csv != "");
// postcondition: length(s) = 0
return l + m;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasDateTimeToStamp(
integer year,
integer month,
integer day,
integer hour,
integer minute,
integer second
) {
month -= 2;
if (month <= 0) {
month += 12;
--year;
}
return
(
(((year / 4 - year / 100 + year / 400 + (367 * month) / 12 + day) +
year * 365 - 719499
) * 24 + hour
) * 60 + minute
) * 60 + second;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
float wasFmod(float a, float p) {
if(p == 0) return (float)"nan";
return a - ((integer)(a/p) * p);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
// Original: Clive Page, Leicester University, UK. 1995-MAY-2 //
///////////////////////////////////////////////////////////////////////////
list wasUnixTimeToDateTime(integer seconds) {
integer mjday = (integer)(seconds/86400 + 40587);
integer dateYear = 1858 + (integer)( (mjday + 321.51) / 365.25);
float day = (integer)( wasFmod(mjday + 262.25, 365.25) ) + 0.5;
integer dateMonth = 1 + (integer)(wasFmod(day / 30.6 + 2.0, 12.0) );
integer dateDay = 1 + (integer)(wasFmod(day,30.6));
float nsecs = wasFmod(seconds, 86400);
integer dateSeconds = (integer)wasFmod(nsecs, 60);
nsecs = nsecs / 60;
integer dateMinutes = (integer)wasFmod(nsecs, 60);
integer dateHour = (integer)(nsecs / 60);
return [ dateYear,
dateMonth, dateDay, dateHour, dateMinutes, dateSeconds ];
}
 
// for changing states
string nextstate = "";
 
// notecard reading
integer line = 0;
list tuples = [];
 
// corrade data
key CORRADE = NULL_KEY;
string GROUP = "";
string PASSWORD = "";
// the owner of the rental system
// the dude or dudette that gets paid
key OWNER = NULL_KEY;
// the price of the rent
integer PRICE = 0;
// the time for the rent in seconds
integer RENT = 0;
string URL = "";
// the role to invite rentants to
string ROLE = "";
 
default {
state_entry() {
if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
llSetText("Sorry, could not find an inventory notecard.", <1, 0, 0>, 1.0);
return;
}
llSetText("Reading configuration notecard...", <1, 1, 0>, 1.0);
llGetNotecardLine("configuration", line);
}
dataserver(key id, string data) {
if(data == EOF) {
// invariant, length(tuples) % 2 == 0
if(llGetListLength(tuples) % 2 != 0) {
llSetText("Error in configuration notecard.", <1, 0, 0>, 1.0);
return;
}
CORRADE = (key)llList2String(
tuples,
llListFindList(
tuples,
[
"corrade"
]
)
+1);
if(CORRADE == NULL_KEY) {
llSetText("Error in configuration notecard: corrade", <1, 0, 0>, 1.0);
return;
}
GROUP = llList2String(
tuples,
llListFindList(
tuples,
[
"group"
]
)
+1);
if(GROUP == "") {
llSetText("Error in configuration notecard: group", <1, 0, 0>, 1.0);
return;
}
PASSWORD = llList2String(
tuples,
llListFindList(
tuples,
[
"password"
]
)
+1);
if(PASSWORD == "") {
llSetText("Error in configuration notecard: password", <1, 0, 0>, 1.0);
return;
}
OWNER = (key)llList2String(
tuples,
llListFindList(
tuples,
[
"owner"
]
)
+1);
if(OWNER == NULL_KEY) {
llSetText("Error in configuration notecard: owner", <1, 0, 0>, 1.0);
return;
}
PRICE = (integer)llList2String(
tuples,
llListFindList(
tuples,
[
"price"
]
)
+1);
if(PRICE == 0) {
llSetText("Error in configuration notecard: price", <1, 0, 0>, 1.0);
return;
}
RENT = (integer)llList2String(
tuples,
llListFindList(
tuples,
[
"rent"
]
)
+1);
if(RENT == 0) {
llSetText("Error in configuration notecard: rent", <1, 0, 0>, 1.0);
return;
}
ROLE = llList2String(
tuples,
llListFindList(
tuples,
[
"role"
]
)
+1
);
if(ROLE == "") {
llSetText("Error in configuration notecard: role", <1, 0, 0>, 1.0);
return;
}
llSetText("Read configuration notecard.", <0, 1, 0>, 1.0);
// if data is set switch to rented state
if(llGetObjectDesc() != "") state rented;
// otherwise switch to the payment state
state payment;
}
if(data == "") jump continue;
integer i = llSubStringIndex(data, "#");
if(i != -1) data = llDeleteSubString(data, i, -1);
list o = llParseString2List(data, ["="], []);
// get rid of starting and ending quotes
string k = llDumpList2String(
llParseString2List(
llStringTrim(
llList2String(
o,
0
),
STRING_TRIM),
["\""], []
), "\"");
string v = llDumpList2String(
llParseString2List(
llStringTrim(
llList2String(
o,
1
),
STRING_TRIM),
["\""], []
), "\"");
if(k == "" || v == "") jump continue;
tuples += k;
tuples += v;
@continue;
llGetNotecardLine("configuration", ++line);
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state trampoline {
state_entry() {
llSetTimerEvent(5);
}
timer() {
llSetTimerEvent(0);
// State jump table
if(nextstate == "url") state url;
if(nextstate == "getmembers") state getmembers;
if(nextstate == "getrolemembers") state getrolemembers;
if(nextstate == "addtorole") state addtorole;
if(nextstate == "invite") state invite;
if(nextstate == "rented") state rented;
if(nextstate == "demote") state demote;
// automata in invalid state
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state payment {
state_entry() {
llSetPayPrice(PRICE, [PRICE]);
llSetClickAction(CLICK_ACTION_PAY);
llSetText("☀ Touch me to rent this place! ☀", <0, 1, 0>, 1.0);
}
money(key id, integer amount) {
// Get the current time stamp.
list stamp = llList2List(
llParseString2List(
llGetTimestamp(),
["-",":","T", "."],[""]
),
0, 5
);
// convert to seconds and add the rent
integer delta = wasDateTimeToStamp(
llList2Integer(stamp, 0),
llList2Integer(stamp, 1),
llList2Integer(stamp, 2),
llList2Integer(stamp, 3),
llList2Integer(stamp, 4),
llList2Integer(stamp, 5)
) +
// the amount of time is the amount paid
// times the rent time divided by the price
amount * (integer)(
(float)RENT /
(float)PRICE
);
// convert back to a timestamp
stamp = wasUnixTimeToDateTime(delta);
// and set the renter and the eviction date
llSetObjectDesc(
wasKeyValueEncode(
[
"rentantUUID", id,
"rentantName", llKey2Name(id),
"expiresDate", llList2String(stamp, 0) +
"-" + llList2String(stamp, 1) +
"-" + llList2String(stamp, 2) +
"T" + llList2String(stamp, 3) +
":" + llList2String(stamp, 4) +
":" + llList2String(stamp, 5)
]
)
);
nextstate = "getmembers";
state url;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state url {
state_entry() {
// release any previous URL
llReleaseURL(URL);
// request a new URL
llRequestURL();
}
http_request(key id, string method, string body) {
if(method != URL_REQUEST_GRANTED) {
llSetText("☀ Unable to get an URL! ☀", <1, 0, 0>, 1.0);
nextstate = "url";
state trampoline;
}
URL = body;
// state URL jump table
if(nextstate == "getmembers") state getmembers;
if(nextstate == "demote") state demote;
// automata in invalid state
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state getmembers {
state_entry() {
llSetText("☀ Getting group members... ☀", <1, 1, 0>, 1.0);
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "getmembers",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
// we just care if the agent is in the group
// so we use Corrade's sifting ability in order
// to reduce the script memory usage
"sift", wasURLEscape(
"(" +
wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
) +
")*"
),
"callback", wasURLEscape(URL)
]
)
);
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
if(wasKeyValueGet("success", body) != "True") {
llSetText("☀ Could not get group members! ☀", <1, 0, 0>, 1.0);
nextstate = "getmembers";
state trampoline;
}
// check that the payer is part of the role
integer i =
llListFindList(
wasCSVToList(
wasURLUnescape(
wasKeyValueGet(
"data",
body
)
)
),
(list)wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
)
);
llSetTimerEvent(0);
// if they are in the group then check roles.
if(i != -1) state getrolemembers;
// otherwise invite them to the group role.
state invite;
}
timer() {
llSetTimerEvent(0);
nextstate = "getmembers";
state trampoline;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state getrolemembers {
state_entry() {
llSetText("☀ Getting role members... ☀", <1, 1, 0>, 1.0);
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "getrolemembers",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"role", wasURLEscape(ROLE),
// we just care if the agent is in the renter role
// so we use Corrade's sifting ability in order
// to reduce the script memory usage
"sift", wasURLEscape(
"(" +
wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
) +
")*"
),
"callback", wasURLEscape(URL)
]
)
);
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
if(wasKeyValueGet("success", body) != "True") {
llSetText("☀ Could not get role members! ☀", <1, 0, 0>, 1.0);
nextstate = "getrolemembers";
state trampoline;
}
// check that the payer is part of the role
integer i =
llListFindList(
wasCSVToList(
wasURLUnescape(
wasKeyValueGet(
"data",
body
)
)
),
(list)wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
)
);
llSetTimerEvent(0);
// if they are in the role then skip inviting them.
if(i != -1) state rented;
// otherwise add them to the land role.
state addtorole;
}
timer() {
llSetTimerEvent(0);
nextstate = "getrolemembers";
state trampoline;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state addtorole {
state_entry() {
llSetText("☀ Adding to role... ☀", <1, 1, 0>, 1.0);
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "addtorole",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"agent", wasURLEscape(
wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
)
),
"role", wasURLEscape(ROLE),
"callback", wasURLEscape(URL)
]
)
);
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
if(wasKeyValueGet("success", body) != "True") {
llSetText("☀ Could not add to role! ☀", <1, 0, 0>, 1.0);
nextstate = "addtorole";
state trampoline;
}
// otherwise invite them to the group role.
state rented;
}
timer() {
llSetTimerEvent(0);
nextstate = "addtorole";
state trampoline;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state invite {
state_entry() {
llSetText("☀ Please accept the group invite! ☀", <1, 1, 0>, 1.0);
// invite the agent to the land group
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "invite",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"agent", wasURLEscape(
wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
)
),
"role", wasURLEscape(ROLE),
"callback", wasURLEscape(URL)
]
)
);
// handle any Corrade timeouts
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
llHTTPResponse(id, 200, "OK");
// Checks if the invite was sent successfully or if that fails but the
// agent is already in the group (status 15345) then continue.
// Otherwise, jump to the trampoline and send the invite again.
// Status codes:
// http://grimore.org/secondlife/scripted_agents/corrade/status_codes/progressive
if(wasKeyValueGet("success", body) != "True" &&
wasKeyValueGet("status", body) != "15345") {
llSetText("☀ Group invite could not be sent! ☀", <1, 0, 0>, 1.0);
nextstate = "invite";
state trampoline;
}
llSetText("☀ Group invitation sent! ☀", <1, 1, 0>, 1.0);
llSetTimerEvent(0);
state rented;
}
timer() {
llSetTimerEvent(0);
nextstate = "invite";
state trampoline;
}
}
 
state rented {
state_entry() {
// Get the expiration date
list expires = llList2List(
llParseString2List(
wasKeyValueGet(
"expiresDate",
llGetObjectDesc()
),
["-",":","T", "."],[""]
),
0, 5
);
// Get the current date
list stamp = llList2List(
llParseString2List(
llGetTimestamp(),
["-",":","T", "."],[""]
),
0, 5
);
integer delta = wasDateTimeToStamp(
llList2Integer(expires, 0),
llList2Integer(expires, 1),
llList2Integer(expires, 2),
llList2Integer(expires, 3),
llList2Integer(expires, 4),
llList2Integer(expires, 5)
) - wasDateTimeToStamp(
llList2Integer(stamp, 0),
llList2Integer(stamp, 1),
llList2Integer(stamp, 2),
llList2Integer(stamp, 3),
llList2Integer(stamp, 4),
llList2Integer(stamp, 5)
);
// the rent has expired, now evict the rentant
if(delta <= 0) {
llSetTimerEvent(0);
nextstate = "demote";
state url;
}
// otherwise, update the remaining time
list remaining = wasUnixTimeToDateTime(delta);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 0)-1970 ], 0, 0);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 1)-1 ], 1, 1);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 2)-1 ], 2, 2);
llSetText(
"☀ Private property! ☀"
+ "\n" +
"Rented by: " +
wasKeyValueGet(
"rentantName",
llGetObjectDesc()
)
+ "\n" +
"Expires on: " +
wasKeyValueGet(
"expiresDate",
llGetObjectDesc()
)
+ "\n" +
"Remaining: " +
llList2String(remaining, 0) + "-" +
llList2String(remaining, 1) + "-" +
llList2String(remaining, 2) + " " +
llList2String(remaining, 3) + ":" +
llList2String(remaining, 4)
+ "\n" +
"Touch to extend rent.",
<0, 1, 1>,
1.0
);
llSetPayPrice(PRICE, [PRICE]);
llSetClickAction(CLICK_ACTION_PAY);
// set the countdown every minute
llSetTimerEvent(60);
}
money(key id, integer amount) {
// Get the expiration date
list stamp = llList2List(
llParseString2List(
wasKeyValueGet(
"expiresDate",
llGetObjectDesc()
),
["-",":","T", "."],[""]
),
0, 5
);
// convert to seconds and add the extended rent
integer delta = wasDateTimeToStamp(
llList2Integer(stamp, 0),
llList2Integer(stamp, 1),
llList2Integer(stamp, 2),
llList2Integer(stamp, 3),
llList2Integer(stamp, 4),
llList2Integer(stamp, 5)
) +
// the amount of time to extend is the amount
// paid times the rent time divided by the price
amount * (integer)(
(float)RENT /
(float)PRICE
);
// convert back to a timestamp
stamp = wasUnixTimeToDateTime(delta);
// and set the renter and the eviction date
llSetObjectDesc(
wasKeyValueEncode(
[
"rentantUUID", wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
),
"rentantName", wasKeyValueGet(
"rentantName",
llGetObjectDesc()
),
"expiresDate", llList2String(stamp, 0) +
"-" + llList2String(stamp, 1) +
"-" + llList2String(stamp, 2) +
"T" + llList2String(stamp, 3) +
":" + llList2String(stamp, 4) +
":" + llList2String(stamp, 5)
]
)
);
llSetText("☀ Updating... ☀", <1, 1, 0>, 1.0);
llSetTimerEvent(0);
nextstate = "rented";
state trampoline;
}
timer() {
// Get the expiration date
list expires = llList2List(
llParseString2List(
wasKeyValueGet(
"expiresDate",
llGetObjectDesc()
),
["-",":","T", "."],[""]
),
0, 5
);
// Get the current date
list stamp = llList2List(
llParseString2List(
llGetTimestamp(),
["-",":","T", "."],[""]
),
0, 5
);
integer delta = wasDateTimeToStamp(
llList2Integer(expires, 0),
llList2Integer(expires, 1),
llList2Integer(expires, 2),
llList2Integer(expires, 3),
llList2Integer(expires, 4),
llList2Integer(expires, 5)
) - wasDateTimeToStamp(
llList2Integer(stamp, 0),
llList2Integer(stamp, 1),
llList2Integer(stamp, 2),
llList2Integer(stamp, 3),
llList2Integer(stamp, 4),
llList2Integer(stamp, 5)
);
// the rent has expired, now evict the rentant
if(delta <= 0) {
llSetTimerEvent(0);
nextstate = "demote";
state url;
}
// otherwise, update the remaining time
list remaining = wasUnixTimeToDateTime(delta);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 0)-1970 ], 0, 0);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 1)-1 ], 1, 1);
remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 2)-1 ], 2, 2);
llSetText(
"☀ Private property! ☀"
+ "\n" +
"Rented by: " +
wasKeyValueGet(
"rentantName",
llGetObjectDesc()
)
+ "\n" +
"Expires on: " +
wasKeyValueGet(
"expiresDate",
llGetObjectDesc()
)
+ "\n" +
"Remaining: " +
llList2String(remaining, 0) + "-" +
llList2String(remaining, 1) + "-" +
llList2String(remaining, 2) + " " +
llList2String(remaining, 3) + ":" +
llList2String(remaining, 4)
+ "\n" +
"Touch to extend rent.",
<0, 1, 1>,
1.0
);
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) {
llResetScript();
}
}
}
 
state demote {
state_entry() {
llSetText("☀ Rent has expired! ☀", <1, 0, 0>, 1.0);
// demote the agent from the renter role
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "deletefromrole",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"agent", wasURLEscape(
wasKeyValueGet(
"rentantUUID",
llGetObjectDesc()
)
),
"role", wasURLEscape(ROLE),
"callback", wasURLEscape(URL)
]
)
);
// handle any Corrade timeouts
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
llHTTPResponse(id, 200, "OK");
// Checks if the demote was sent successfully or if that fails but the
// agent has already left the group (status 11502) then continue.
// Otherwise, jump to the trampoline and send the demote again.
// Status codes:
// http://grimore.org/secondlife/scripted_agents/corrade/status_codes/progressive
if(wasKeyValueGet("success", body) != "True" &&
wasKeyValueGet("status", body) != "11502") {
llSetText("☀ Could not demote! ☀", <1, 0, 0>, 1.0);
nextstate = "demote";
state trampoline;
}
llSetText("☀ Renter demoted! ☀", <1, 1, 0>, 1.0);
llSetTimerEvent(0);
// Now clean up the rental and restart.
llSetObjectDesc("");
llResetScript();
}
timer() {
llSetTimerEvent(0);
nextstate = "demote";
state trampoline;
}
}