LSL アナログ時計スクリプト

時間の針、分の針、秒の針のプリム名をそれぞれ
"_hour" "_min" "_sec" として以下のスクリプトを実行するとSLから時間をとってきてアナログ時刻表示します
（回転中心軸を合わせるなどはしてある前提）

タッチしてSL時間との差分を設定できますので、日本時間のアナログ表示も可能です。
(Date機能はありません。24時管理はしていません。)

```
/*
    Clock Script
    
    Written by Esforco
    
    
    Please set child object name this one.
    
    hour objectname = "_hour"
    min objectname = "_min"
    sec objectname = "_sec"
*/

integer rot_dir = -1; // or -1

rotation rootRot;
rotation hourRot;
rotation minRot;
rotation secRot;

list prim_list = [];
integer hour_hand_link_num;
integer min_hand_link_num;
integer sec_hand_link_num;

integer pst_hour = 0;
integer pst_min = 0;
integer pst_sec = 0;
float hourRot_FloatVal = 0.0;
float minRot_FloatVal = 0.0;
float secRot_FloatVal = 0.0;
float pre_hourRot_FloatVal;
float pre_minRot_FloatVal;
float pre_secRot_FloatVal;

key avatarKey = NULL_KEY;
integer dlg_channel = 0;
integer handle_dlg = -1;
float DIALOG_TIME_OUT = 30.0;
float time_dlg_rate = DIALOG_TIME_OUT;
string language = "English";
list mode_list = ["[Hour]", "[Min]"];
integer mode_index = 0;
string button_blank_char = "-";

integer offset_hour = 0; // ユーザー調整時間 (時)
integer offset_min = 0; // ユーザー調整時間 (分)
integer offset_hour_temp = 0; // 時刻調整中の調整時間 (時)
integer offset_min_temp = 0; // 時刻調整中のユーザー調整時間 (分)

// Prim List 作成
createIndex()
{
    prim_list = [];
    
    integer i;
    prim_list = [""];    // ルート ( 1 : 起算 )
    prim_list += [llGetObjectName()];
    for(i = 2; i <= llGetNumberOfPrims() ; i++)
    {
        string prim_name = llGetLinkName(i);
        prim_list += [prim_name];
    }
}

// Prim Nmae から インデックス を返す
integer getIndex(string prim_name)
{
    return llListFindList(prim_list, [prim_name]);   
}

integer genCh()
{
    integer gen;

    string str = llGetSubString((string)llGetKey(),0,3);
    gen = -1-(integer)("0x"+str);
    
    return gen;
}

// 時針を指定角度移動
set_rot_hour_hand(float rot_val)
{
    float rot = rot_val - pre_hourRot_FloatVal;
    if(rot >= 360)
    {
        rot -= 360;
    }

    hourRot = llList2Rot(llGetLinkPrimitiveParams(hour_hand_link_num,[PRIM_ROT_LOCAL]),0);      
    
    vector targetEluer= DEG_TO_RAD*<0.0,0.0,rot * rot_dir>;
    rotation targetQ = llEuler2Rot(targetEluer);
    rootRot = targetQ * hourRot;
    llSetLinkPrimitiveParams(hour_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
    llMessageLinked(LINK_SET,10,"","");
    
    pre_hourRot_FloatVal = rot_val;
}

// 分針を指定角度移動
set_rot_min_hand(float rot_val)
{
    float rot = rot_val - pre_minRot_FloatVal;
    if(rot >= 360)
    {
        rot -= 360;
    }
    //llSay(0, "min rot : " + (string)rot);
    
    minRot = llList2Rot(llGetLinkPrimitiveParams(min_hand_link_num,[PRIM_ROT_LOCAL]),0);
    
    vector targetEluer= DEG_TO_RAD*<0.0,0.0,rot * rot_dir>;
    rotation targetQ = llEuler2Rot(targetEluer);
    rootRot = targetQ * minRot;
    llSetLinkPrimitiveParams(min_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
    llMessageLinked(LINK_SET,11,"","");
    
    pre_minRot_FloatVal = rot_val;
}

// 秒針を指定角度移動
set_rot_sec_hand(float rot_val)
{
    float rot = rot_val - pre_secRot_FloatVal;
    if(rot >= 360)
    {
        rot -= 360;
    }
    //llSay(0, "min rot : " + (string)rot);
    
    secRot = llList2Rot(llGetLinkPrimitiveParams(sec_hand_link_num,[PRIM_ROT_LOCAL]),0);
    
    vector targetEluer= DEG_TO_RAD*<0.0,0.0,rot * rot_dir>;
    rotation targetQ = llEuler2Rot(targetEluer);
    rootRot = targetQ * secRot;
    llSetLinkPrimitiveParams(sec_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
    llMessageLinked(LINK_SET,12,"","");
    
    pre_secRot_FloatVal = rot_val;
}

// 指定時間に時計の針を動かす
set_time(integer in_hour, integer in_min, integer in_sec)
{
    integer hour = in_hour + offset_hour + offset_hour_temp;
    integer min = in_min + offset_min + offset_min_temp;
    integer sec = in_sec;
    
    // 24 時制 を 12 時間に置き換え
    if(min >= 60)
    {
        min -= 60;
        hour += 1;
    }
    if(hour >= 12)
    {
        hour -= 12;
    }

    if(min < 0)
    {
        min += 60;
        hour -= 1;
    }
    if(hour < 0)
    {
        hour += 12;
    }
    
    // Hour の角度計算
    float rot_by_1_hour = 30.0;// 360 ÷ 12;
    float rot_by_1_min_for_hour = 0.5;//rot_by_1_hour ÷ 60;
    hourRot_FloatVal = (float)hour * rot_by_1_hour + (float)min * rot_by_1_min_for_hour;
    
    // Min の角度計算
    float rot_by_1_min = 6.0; // 360 ÷ 60
    minRot_FloatVal = (float)min * rot_by_1_min;
    
    // Sec の角度計算
    float rot_by_1_sec = 6.0; // 360 ÷ 60
    secRot_FloatVal = (float)sec * rot_by_1_sec;
    
    // 時計の針を動かす
    set_rot_sec_hand(secRot_FloatVal);
    set_rot_min_hand(minRot_FloatVal);
    set_rot_hour_hand(hourRot_FloatVal);
}

// 初期化
init()
{
    llSetLinkPrimitiveParams(hour_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
    llSetLinkPrimitiveParams(min_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
    llSetLinkPrimitiveParams(sec_hand_link_num,[PRIM_ROT_LOCAL,rootRot]);
}

// SL 時間の取得
get_PST_time()
{
    string utc_time = llGetTimestamp();

    list tokens = llParseString2List (utc_time, ["T"], []);
    list times = llParseString2List (llList2String(tokens,1), [":"], []);
    pst_hour = (integer)llList2Integer(times,0) - 8; // UTC 時間から 8 時間遅らせる
    pst_min = (integer)llList2Integer(times,1);
    pst_sec = (integer)llList2Integer(times,2);
    
    if(pst_hour < 0) pst_hour+=12;

    // PST Time をチャットに表示
    //llOwnerSay((string)now_hour + ":"+(string)now_min + ":"+(string)now_sec);
}

do_clock()
{
    //init();
    // 現在時刻の取得
    get_PST_time();
    // 時刻表示
    set_time(pst_hour, pst_min, pst_sec);    
}

// 時刻合わせ設定ダイアログを開く
setting_clock_dlg(key av, string mode)
{
    string message;
    
    if(language == "English")
    {
        message = "\n\n\Time adjustment Setting\n";
    }
    else
    {
        message = "\n\n\時刻合わせ 設定\n";
    }
    
    integer hour = pst_hour + offset_hour + offset_hour_temp;
    integer min = pst_min + offset_min + offset_min_temp;
    
    // 24 時制 を 12 時間に置き換え
    if(min >= 60)
    {
        min -= 60;
        hour += 1;
    }
    if(hour >= 12)
    {
        hour -= 12;
    }

    if(min < 0)
    {
        min += 60;
        hour -= 1;
    }
    if(hour < 0)
    {
        hour += 12;
    }
    
    
    string hour_str = (string)hour;
    string min_str = (string)min;
    
    if(llStringLength(hour_str) == 1)
    {
        hour_str = "0" + hour_str;
    }
    if(llStringLength(min_str) == 1)
    {
        min_str = "0" + min_str;
    }
    
    message += "\n"+ hour_str + " : " + min_str;
    
    list buttons = [
        "OK", button_blank_char, "Cancel",
        "-1","-5","-10",
        "+1","+5","+10",
        mode, button_blank_char, "Reset"];
    
    llDialog(av,message, buttons, dlg_channel);
    
    buttons = []; // 解放的な
    time_dlg_rate = DIALOG_TIME_OUT;
}

// 時刻合わせ中の Offset 時間を適正値にする
check_offset_temp_time()
{
    if(offset_min_temp >= 60)
    {
        offset_min_temp -= 60;
    }
    if(offset_hour_temp >= 12)
    {
        offset_hour_temp -= 12;
    }

    if(offset_min_temp < 0)
    {
        offset_min_temp += 60;
    }
    if(offset_hour_temp < 0)
    {
        offset_hour_temp += 12;
    }
}

// Offset 時間を適正値にする
check_offset_time()
{
    if(offset_min >= 60)
    {
        offset_min -= 60;
        offset_hour += 1;
    }
    if(offset_hour >= 12)
    {
        offset_hour -= 12;
    }

    if(offset_min < 0)
    {
        offset_min += 60;
        offset_hour -= 1;
    }
    if(offset_hour < 0)
    {
        offset_hour += 12;
    }
}

default
{
    state_entry()
    {
        createIndex();
        hour_hand_link_num = getIndex("_hour");
        min_hand_link_num = getIndex("_min");
        sec_hand_link_num = getIndex("_sec");
        
        rootRot = ZERO_ROTATION;
        
        dlg_channel = genCh();
        
        init();
        
        // 現在時刻の取得
        get_PST_time();
        // 時刻表示
        set_time(pst_hour, pst_min, pst_sec);
        
        llSetTimerEvent(1.0);
        
    }
    
    timer()
    {
        do_clock();
        
        time_dlg_rate -= 1.0;
        if(time_dlg_rate <= 0.0 && handle_dlg != -1)
        {
            llListenRemove(handle_dlg);
            avatarKey = NULL_KEY;
            handle_dlg = -1;
            //llOwnerSay("Timer handle_dlg : " + (string)handle_dlg);
        }
    }

    touch_start(integer total_number)
    {
        // オーナーであれば
        if(llDetectedKey(0) == llGetOwner())
        {
            avatarKey = llDetectedKey(0);
            
            if(handle_dlg == -1)
            {
                // 時刻合わせダイアログの表示
                handle_dlg = llListen(dlg_channel,"",avatarKey,"");
                //llOwnerSay("handle_dlg : " + (string)handle_dlg);
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }
        }
    }
    
    listen(integer channel, string name, key id, string message)
    {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        
        if(channel == dlg_channel)
        {
            if(message == "OK")
            {
                offset_hour += offset_hour_temp;
                offset_min += offset_min_temp;
                check_offset_time();
                
                offset_hour_temp = 0;
                offset_min_temp = 0;
                
                llListenRemove(handle_dlg);
                avatarKey = NULL_KEY;
                handle_dlg = -1;
            }
            else if(message == "Cancel")
            {
                offset_hour_temp = 0;
                offset_min_temp = 0;

                llListenRemove(handle_dlg);
                avatarKey = NULL_KEY;
                handle_dlg = -1;
            }
            else if(message == "Reset")
            {
                llResetScript();
                /*
                offset_hour_temp = 0;
                offset_min_temp = 0;
                offset_hour = 0;
                offset_min = 0;
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
                */
            }
            else if(message == button_blank_char)
            {
                llListenRemove(handle_dlg);
                avatarKey = NULL_KEY;
                handle_dlg = -1;
            }
            else if(message == "+1")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp += 1;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp += 1;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }
            else if(message == "+5")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp += 5;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp += 5;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }
            else if(message == "+10")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp += 10;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp += 10;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }


            else if(message == "-1")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp -= 1;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp -= 1;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }
            else if(message == "-5")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp -= 5;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp -= 5;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }
            else if(message == "-10")
            {
                if("[Hour]" == llList2String(mode_list, mode_index))
                {
                    offset_hour_temp -= 10;
                }
                else if("[Min]" == llList2String(mode_list, mode_index))
                {
                    offset_min_temp -= 10;
                }
                check_offset_temp_time();
                do_clock();
                // 時刻合わせダイアログを開く
                setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
            }



            else
            {
                // Hour Min モードボタン押下なら
                if(message == llList2String(mode_list, mode_index))
                {
                    mode_index++;
                    if(mode_index > llGetListLength(mode_list)-1)
                    {
                        mode_index = 0;
                    }
                    
                    // 時刻合わせダイアログを開く
                    setting_clock_dlg(avatarKey, llList2String(mode_list, mode_index));
                }
            }
        }
    }
    
    /*
    link_message(integer snd_num,integer num,string str,key id)
    {
        if(num==100)
        {
            llSetLinkPrimitiveParamsFast(2, [PRIM_TEXT,(string)rootRot+"\n"+str,<1.0,1.0,1.0>,1.0]);
        }
    }*/
    
    changed(integer change)
    {
        if(change & CHANGED_OWNER)
        {
            llResetScript();
        }
    }
    
    /*
    on_rez(integer val)
    {
        llResetScript();
    }*/
}
```