Rental Cube

From Second Life Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Just a simple cube for renting a stall or store. Supports refunding rent to an avatar if they wish to end the lease early.

//Rental Script v1.5.3
//Copyright 2003-2009 by Hank Ramos

//Options
vector  rentalOffset   = <0,0,10>;
float   updateInterval = 60.0; //seconds
string  infoNotecard   = "Rent This Space Info";

//Variables
string  tierName;
float   rentalCost;
integer primCount;
integer rentalVolume;
float   refundFee;
key     renterID;
string  renterName;
float   rentalTime;
integer listenQueryID;
vector  initPos;
vector  initScale;
integer count;
integer lineCount;
key     readKey;
string  rentalGrade;
integer primAllotment;

//Constants
float ONE_WEEK = 604800.0;
float ONE_DAY  = 86400.0;
float ONE_HOUR = 3600.0;


dispString(string value)
{
    llSetText(value, <1,1,1>, 1);
}
sendReminder(string message)
{ 
    llInstantMessage(renterID, "Your lease located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") will expire " + message); 
}
saveData()
{
    list saveData;
    vector storageVector;
    
    saveData += renterID;
    saveData += renterName;
    saveData += llRound(rentalTime);
    storageVector = initPos * 1000;
    saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";
    storageVector = initScale * 1000;
    saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";
    
    llSetObjectDesc(llDumpList2String(saveData, "|"));
}
string getTimeString(integer time)
{
    integer days;
    integer hours;
    integer minutes; 
    integer seconds;
    
    days = llRound(time / 86400);
    time = time % 86400;
    
    hours = (time / 3600);
    time  = time % 3600;

    minutes = time / 60;
    time    = time % 60;

    seconds = time;
    
    return (string)days + " days, " + (string)hours + " hours, " + (string)minutes + " minutes"; // + ":" + (string)seconds; 
}

integer setupDialogListen()
{
    integer chatChannel = (integer)llFrand(2000000);
    llListenRemove(listenQueryID);
    listenQueryID = llListen(chatChannel, "", NULL_KEY, "");
    return chatChannel;
}

updateTimeDisp()
{ 
    dispString("Leased by: " + renterName + "\nTime Remaining: " + getTimeString(llRound(rentalTime)));   
}

dispData()
{
    llSay(0, "========================");
    llSay(0, "Rental Space Information");
    llSay(0, "========================");
    llSay(0, "This space is currently leased by " + renterName);
    llSay(0, "The current rental price is L$" + (string)((integer)rentalCost) + " per week.");
    llSay(0, "This space will be open for lease in " + getTimeString(llRound(rentalTime)) + "."); 
    llSay(0, "Memory Free: " + (string)llGetFreeMemory());
}
default
{
    state_entry()
    {
        state initialize;
    }
}

state initialize
{
    state_entry()
    {
        llSetTimerEvent(300);
        llOwnerSay("Waiting to obtain Debit Permissions.");
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }
    run_time_permissions(integer permissions)
    {
        //Only wait for payment if the owner agreed to pay out money
        if (permissions & PERMISSION_DEBIT)
        {
            state loadSettings;
        }
    }    
    on_rez(integer start_param)
    {
        llResetScript();
    } 
    timer()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }
    touch_start(integer total_number)
    {
        integer x;
        for (x = 0; x < total_number; x += 1)
        {
            if (llDetectedKey(x) == llGetOwner())
            {
                llResetScript();
            }
        }
        llSay(0, "Waiting to obtain Debit Permissions from Owner.");
    }
    state_exit()
    {
        llSetTimerEvent(0);
        llSay(0, "Initialized.");
    }
}

state loadSettings
{
    state_entry()
    {
        integer found = FALSE;
        integer x;
        
        count = 0;
        lineCount = 0;
        
        list savedList = llCSV2List(llGetObjectDesc());
        
        if (llGetListLength(savedList) == 4)
        {
            rentalGrade = llList2String(savedList, 0);
        }
        else
        {
            rentalGrade = llGetObjectDesc();
        }
        for (x = 0; x < llGetInventoryNumber(INVENTORY_NOTECARD); x += 1)
        {
            if (llGetInventoryName(INVENTORY_NOTECARD, x) == "Settings")
            {
                found = TRUE; 
            }
        }
        if (found)
        {
            llOwnerSay("Reading Settings Notecard...");
            readKey = llGetNotecardLine("Settings", lineCount); 
        }
        else
        {
            llOwnerSay("Settings Notecard Not Found.");
            llResetScript();
        }
    }
    dataserver(key requested, string data)
    {
        integer integerData;
        float   floatData;
        
        if (requested == readKey) 
        { 
            if (data != EOF)
            {
                if ((llSubStringIndex(data, "#") != 0) && (data != "") && (data != " "))
                {
                    integerData = (integer)data;
                    floatData   = (float)data;
                    
                    if (count == 0)
                    {
                        tierName = data;
                    }
                    else if (count == 1)
                    {
                        if (integerData >= 0)
                        {
                            rentalCost = integerData;
                        }
                        else
                        {
                            rentalCost = 0;
                        }
                    }
                    else if (count == 2)
                    {
                        if (integerData >= 1)
                        {
                            primCount = integerData;
                        }
                        else
                        {
                            primCount = 1;
                        }
                    }
                    else if (count == 3)
                    {
                        if (integerData >= 16)
                        {
                            rentalVolume = integerData;
                        }
                        else
                        {
                            rentalVolume = 16;
                        }
                    }
                    else if (count == 4)
                    {
                        if (integerData >= 0)
                        {
                            refundFee = integerData;
                        }
                        else
                        {
                            refundFee = 0;
                        }
                    }
                    else if (count == 5)
                    {
                        rentalOffset = (vector)data;
                    }
                    else if (count == 6)
                    {
                        infoNotecard = data;
                    }
                    count += 1;
                }
                lineCount += 1;
                readKey = llGetNotecardLine("Settings", lineCount);
            }
            else
            {
                llOwnerSay("===============");
                llOwnerSay("Settings Loaded");
                llOwnerSay("===============");
                llOwnerSay("Space Name: " + tierName);
                llOwnerSay("Rental Cost: L$" + (string)llRound(rentalCost));
                llOwnerSay("Prim Count: " + (string)primCount);
                llOwnerSay("Space Volume: " + (string)rentalVolume + " sqm");
                llOwnerSay("Refund Fee: L$" + (string)refundFee);
                llOwnerSay("===============");
                llOwnerSay("Ready for Service!");

                list savedList = llParseString2List(llGetObjectDesc(), ["|"], []);

                if (llGetListLength(savedList) == 5)
                {
                    renterID    = llList2Key(savedList, 01);
                    renterName  = llList2String(savedList, 1);
                    rentalTime  = llList2Integer(savedList, 2);
                    initPos     = (vector)llList2String(savedList, 3) / 1000;
                    initScale   = (vector)llList2String(savedList, 4) / 1000;
                    state rented;
                }
                else
                {
                    renterID   = NULL_KEY;
                    renterName = "Nobody";
                    rentalTime = 0;
                    initPos    = llGetPos();
                    initScale  = llGetScale();
                    state idle;
                }
            }
        }
    }
}
state idle
{
    state_entry()
    {        
        llSetObjectDesc("");
        llSetTexture("rentthisspace", ALL_SIDES);
        llSetScale(initScale);
        llSetPos(initPos);
        llSetTimerEvent(updateInterval);

        dispString(tierName + "\nLease this space for L$" + (string)llRound(rentalCost) + " per week.\n" + (string)rentalVolume + " sq meters\n" + (string)primCount + " prims\nPay this Sign to begin your lease.");
    }
    moving_end()
    {
        initPos = llGetPos();
    }
    changed(integer change)
    {
        if (change & CHANGED_SCALE)
        {
            initScale = llGetScale();
        }
    }
    touch_start(integer num_detected)
    {
        integer x;
        integer chatChannel;
        
        for (x = 0; x < num_detected; x += 1)
        {
            if (llDetectedKey(x) == llGetOwner())
            {
                llDialog(llGetOwner(), "Owner Options.  Select one of the options below...", ["Info", "Reset"], setupDialogListen());
                return;
            } 
        }
               
        llSay(0, "Lease this space for L$" + (string)llRound(rentalCost) + " per week. " + (string)rentalVolume + " sq meters. " + (string)primCount + " prims. Pay this Sign to begin your lease.");
        
        for (x = 0; x < num_detected; x += 1)
        {
            llGiveInventory(llDetectedKey(x), infoNotecard);
        } 
    }
    listen(integer channel, string name, key id, string message)
    {
        if (message == "Reset")
        {
            llResetScript();
        }
        else if (message == "Info")
        {
            llListenRemove(listenQueryID);
            dispData();
            llSay(0, "Lease this space for L$" + (string)llRound(rentalCost) + " per week. " + (string)rentalVolume + " sq meters. " + (string)primCount + " prims. Pay this Sign to begin your lease.");
            llGiveInventory(id, infoNotecard);
        }
    }    
    money(key id, integer amount)
    {
        if (amount >= rentalCost)
        {
            renterID   = id;
            renterName = llKey2Name(renterID);
            rentalTime = ONE_WEEK * amount / rentalCost;
            saveData();
            
            llSay(0, "Thank you " + renterName + " for leasing!  Your lease will expire in " + getTimeString((integer)rentalTime) + ".");
            
            state rented;
        }
        else
        {
            llSay(0, "This space costs L$" + (string)rentalCost + " to rent. Refunding paid balance.");
            llGiveMoney(id, amount);
        }
    }
}

state rented
{
    state_entry()
    {
        llSetTexture("infosign", ALL_SIDES);
        llSetScale(<0.5, 0.5, 0.5>);
        llSetPos(initPos + rentalOffset);
        
        updateTimeDisp();
        llResetTime();
        llSetTimerEvent(updateInterval);
    }
    touch_start(integer num_detected)
    {
        integer x;
        key     detectedKey;
        
        for (x = 0; x < num_detected; x += 1)
        {
            detectedKey = llDetectedKey(x);
            if (detectedKey == llGetOwner())
            {
                llDialog(detectedKey, "Lease Options. Select one of the options below...", ["Refund Time", "Info", "Release", "Reset"], setupDialogListen());
            }
            else if (detectedKey == renterID)
            {
                llDialog(detectedKey, "Lease Options. Select one of the options below...", ["Refund Time", "Info"], setupDialogListen());
            }
            else
            {
                dispData();
                llGiveInventory(detectedKey, infoNotecard);
            }
        }
    }
    money(key id, integer amount)
    {
        if ((id == renterID)||(id == llGetOwner()))
        {
            float addTime;
            
            addTime = ONE_WEEK*amount/rentalCost;
            rentalTime += addTime;
            
            llInstantMessage(id, "Adding " + getTimeString(llRound(addTime)) + " to your lease. Lease Time is Now: " + getTimeString(llRound(rentalTime)) + ".");
            saveData();
            updateTimeDisp();
        }
        else
        {
            llInstantMessage(id, "Refunding Money...");
            llGiveMoney(id, amount);
            llInstantMessage(id, "This space is currently leased by " + renterName + ". This space will be open for lease in " + getTimeString(llRound(rentalTime)) + "."); 
        }
    }
    listen(integer channel, string name, key id, string message)
    {
        integer refundAmount;
        
        llListenRemove(listenQueryID);

        if (message == "Info")
        {
            dispData();
            llGiveInventory(id, infoNotecard);
        }
        else if (message == "Refund Time")
        {
            llDialog(id, "Are you sure you want to TERMINATE your lease and refund your money, minus a L$" + (string)refundFee + " fee?", ["YES", "NO"], setupDialogListen());
        }
        else if (message == "YES")
        {
            refundAmount = llRound((rentalTime/ONE_WEEK)*rentalCost - refundFee);
            llInstantMessage(renterID, "Refunding L$" + (string)refundAmount + ", which includes a L$" + (string)refundFee + " termination fee.");
            llGiveMoney(renterID, refundAmount);
            llInstantMessage(llGetOwner(), "LEASE REFUNDED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has ended. Refunded L$" + (string)refundAmount + ".");
            state idle;
        }
        else if (message == "Release")
        {
            llDialog(id, "Are you sure you want to TERMINATE this lease with NO REFUND?", ["Yes", "No"], setupDialogListen());
        }
        else if (message == "Yes")
        {
            llInstantMessage(llGetOwner(), "LEASE TERMINATED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has ended. Refunded L$0.");
            state idle;            
        }
        else if (message == "Reset")
        {
            llResetScript();
        }
    }
    timer()
    {
        float timeElapsed = llGetAndResetTime();
        if (timeElapsed > (updateInterval * 4))
        {
            timeElapsed = updateInterval;
        }
        rentalTime -= timeElapsed;
        
        saveData();
 
        updateTimeDisp(); 
        
        //Process Reminders
        if (rentalTime <= 0)
        {
            llInstantMessage(llGetOwner(), "LEASE EXPIRED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has expired.");

            state idle;
        }
        if ((rentalTime <= ONE_DAY)&&(rentalTime >= ONE_DAY - (updateInterval*2)))
        {
            sendReminder("in one day.");
        }              
        else if ((rentalTime <= ONE_HOUR*12)&&(rentalTime >= ONE_HOUR*12 - (updateInterval*2)))
        {
            sendReminder("in 12 hours.");
        }        
        else if ((rentalTime <= ONE_HOUR)&&(rentalTime >= ONE_HOUR - (updateInterval*2)))
        {
            sendReminder("in one hour.");
        }        
    }
}


Here is the associated settings notecard (not a script):

#Rent This Space
#Options Notecard
#All lines beginning with "#" are comments, and are ignored
#DO NOT CHANGE the order of the options!!!

#Spot Name
This Place for Rent!

#Rental Cost
#The weekly rate to rent this space
50

#Prim Count
#The number of prims allowed by the renter in this space
20

#Rental Size
#The volume of the rental space, in square meters
100

#Refund Fee
#The fee charged to obtain a refund on the currently paid rent
20

#Rental Offset
#The relative position of the prim when rented, when compared
#to the non-rented position
#e.g. 6.75m above the rented spot: <0,0,6.75>
#e.g. 15m above and 5m to the West: <5,0,15>
<0,0,6.75>

#Info Notecard Name
#The name of the notecard to give to someone
#who clicks the cube. Note: case-sensitive
Rental Info