Table of Contents

About

The script is a countdown script that displays the number of years, months, days, hours and minutes till a certain set term using a configuration notecard.

Exports

Setup

To set-up the object, you will need a 5-pieces xyzzytext text board. In the root primitive, you can add the script below along with a notecard called configuration.

A sample configuration notecard contains the following:

#### START CONFIGURATION ####

# goal in llGetTimestamp format
# YYYY-MM-DDTHH:MM:SS.MSZ
alarm = 2014-07-27T06:00:00.000000Z

# repeat every: yearly, monthly, weekly, daily, hourly
# this is an optional parameter and can be commented out
repeat = weekly

# the text to display when the countdown reaches the alarm
alarm_text = GOAL REACHED!

##### END CONFIGURATON ######

The alarm has to be set in UTC time in the format YYYY-MM-DDTHH:MM:SS.MS where:

  • YYYY stands for year,
  • MM stands for month
  • DD stands for day,
  • T is a separator,
  • HH stands for hour (in 24 hour format)
  • MM stands for minutes,
  • SS stands for seconds,
  • MS stands for milliseconds,
  • Z is a separator.

The repeat can be set to either: yearly, monthly, weekly, daily or hourly.

For example, you could set the alarm to:

alarm = 2014-07-29T13:37:52.270346Z

and with a repeat of yearly:

repeat = yearly

which will make the countdown trigger the alarm every year on the 29th July at time 13:37:52.

Code

countdown.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2021 - 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) 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: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(data, ["&", "="], []);
    integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    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 ];
}
 
///////////////////////////////////////////////////////////////////////////
//    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    //
///////////////////////////////////////////////////////////////////////////
integer getYearDays(integer year) {
    integer leap = (year % 4 == 0 && year % 100 != 0) || 
            (year % 400 == 0);
    if(leap == TRUE) {
        return 366;
    }
    return 365;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
integer getMonthDays(integer month, integer year) {
    if (month == 4 || month == 6 || month == 9 || month == 11) {
        return 30;
    }
    if(month == 2) {
        integer leap = (year % 4 == 0 && year % 100 != 0) || 
            (year % 400 == 0);
        if(leap == TRUE) {
            return 29;
        }
        return 28;
    }
    return 31;
}
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
 
default {
    state_entry() {
        if(llGetInventoryType(llGetInventoryName(INVENTORY_NOTECARD, 0)) != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find an inventory notecard.");
            return;
        }
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD, 
                0
            ),
            line
        );
    }
    dataserver(key id, string data) {
        if(data == EOF) state countdown; // invariant, length(tuples) % 2 == 0
        if(data == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD,
                    0
                ), 
                ++line
            );
            return;
        }
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        string k = llStringTrim(llList2String(o, 0), STRING_TRIM);
        string v = llStringTrim(llList2String(o, 1), STRING_TRIM);
        if(k == "" || v == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD,
                    0
                ), 
                ++line
            );
            return;
        }
        tuples += k;
        tuples += v;
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD,
                0
            ), 
            ++line
        );
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state countdown {
    state_entry() {
        llSetTimerEvent(1);
    }
    timer() {
        list alarm = llList2List(
            llParseString2List(
                wasKeyValueGet("alarm", wasKeyValueEncode(tuples)), 
                ["-",":","T", "."],[""]
            ), 
            0, 5
        );
        list stamp = llList2List(
            llParseString2List(
                llGetTimestamp(),
                ["-",":","T", "."],[""]
            ),
            0, 5
        );
 
        integer delta = wasDateTimeToStamp(
            llList2Integer(alarm, 0),
            llList2Integer(alarm, 1),
            llList2Integer(alarm, 2),
            llList2Integer(alarm, 3),
            llList2Integer(alarm, 4),
            llList2Integer(alarm, 5)
        ) - wasDateTimeToStamp(
            llList2Integer(stamp, 0),
            llList2Integer(stamp, 1),
            llList2Integer(stamp, 2),
            llList2Integer(stamp, 3),
            llList2Integer(stamp, 4),
            llList2Integer(stamp, 5)
        );
 
        list count = [];
 
        // handle repeat
        if(delta < 0) {
            string repeat = wasKeyValueGet("repeat", wasKeyValueEncode(tuples));
            if(repeat == "") {
                count += wasKeyValueGet("alarm_text", wasKeyValueEncode(tuples));
                jump display;
            }
            if(repeat == "yearly") {
                delta = (getYearDays(llList2Integer(stamp, 0)) * 86400)-(-delta % (getYearDays(llList2Integer(stamp, 0)) * 86400));
                jump set;
            }
            if(repeat == "monthly") {
                delta = (getMonthDays(llList2Integer(stamp, 1), llList2Integer(stamp, 0)) * 86400) - (-delta % (getMonthDays(llList2Integer(stamp, 1), llList2Integer(stamp, 0)) * 86400));
                jump set;
            }
            if(repeat == "weekly") {
                delta = 7*86400-(-delta % (7*86400));
                jump set;
            }
            if(repeat == "daily") {
                delta = 86400-(-delta % 86400);
                jump set;
            }
            if(repeat == "hourly") {
                delta = 3600-(-delta % 3600);
                jump set;
            }
            if(repeat == "minutely") {
                delta = 60-(-delta % 60);
                jump set;
            }
        }
@set;
        // get date and remove epoch 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);
 
        // start display
        integer Y = llList2Integer(remaining, 0);
        if(Y == 0) jump MO;
        count += (string)Y;
        if(Y > 1) {
            count += "years";
            jump MO;
        }
        count += "year";
@MO;
        integer M = llList2Integer(remaining, 1);
        if(M == 0) jump DA;
        count += (string)M;
        if(M > 1) {
            count += "months";
            jump DA;
        }
        count += "month";
@DA;
        integer D = llList2Integer(remaining, 2);
        if(D == 0) jump HO;
        count += (string)D;
        if(D > 1) {
            count += "days";
            jump HO;
        }
        count += "day";
@HO;
        integer H = llList2Integer(remaining, 3);
        if(H == 0) jump MI;
        count += (string)H;
        if(H > 1) {
            count += "hours";
            jump MI;
        }
        count += "hour";
@MI;
        integer I = llList2Integer(remaining, 4);
        if(I == 0) jump done;
        count += (string)I;
        if(I > 1) {
            count += "minutes";
            jump done;
        }
        count += "minute";  
@done;
        if(llGetListLength(count) == 0) {
            count += wasKeyValueGet("alarm_text", wasKeyValueEncode(tuples));
            jump display;
        }
        count += "left";
 
@display;
 
        // pad with spaces so the message appears centered
        string result = llDumpList2String(count, " ");
        integer spaces = (60 - llStringLength(result))/2;
        string space = "";
        do {
            space += " ";
        } while(--spaces >-1);
 
        // send the spaces and result to xyz text
        llMessageLinked(LINK_ROOT, 204000, space + result, "0");
 
        // reschedule every minute
        llSetTimerEvent(60);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
}

secondlife/countdown.txt · Last modified: 2022/11/24 07:46 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.