///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2011 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
integer failFind = 10;
vector iPos;
integer targetID;
list cmd = [];
 
vector wasCirclePoint(float radius) {
    float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    if(llPow(x,2) + llPow(y,2) <= llPow(radius,2)) {
        return iPos + <x, y, llFrand(2)>;
    }
    return wasCirclePoint(radius);
}
 
moveTo(vector position) {
    targetID = llTarget(position, .8);
    llLookAt(position, .5, 1);
    float speed = 1+llFrand(10);
    if(llGetAgentInfo(llGetOwner()) & AGENT_WALKING)
        speed = llFrand(1);
    llMoveToTarget(position, 1+llFrand(4));    
}
 
list jumps = [];
// Calculate jump gates
list jumpGates(vector iPos, vector dPos, integer jumpDistance) {
    list gates = [];
    if(jumpDistance == 0) return gates;
    float dist = llVecDist(iPos, dPos);
    if(dist > jumpDistance) {
        iPos = iPos + jumpDistance * (dPos-iPos) / dist;
        gates += iPos;
        return gates + jumpGates(iPos, dPos, jumpDistance);
    }
    return gates + jumpGates(iPos, dPos, --jumpDistance);
}
 
default {
    state_entry() {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetStatus(STATUS_BLOCK_GRAB_OBJECT, TRUE);
        llSetStatus(STATUS_ROTATE_X, TRUE);
        llSetStatus(STATUS_ROTATE_Y|STATUS_ROTATE_Z, FALSE);
        llVolumeDetect(TRUE);
        state wander;
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}
 
state wander
{
    state_entry() {
        iPos = llGetPos();
        failFind = 10;
        llSensorRepeat("", llGetOwner(), AGENT, 64.0, TWO_PI, 1);
        llSetStatus(STATUS_PHYSICS, TRUE);
        llSetForce(<0,0,9.81> * llGetMass(), 0);
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llListen(0, "", llGetOwner(), "");
        llInstantMessage(llGetOwner(), "Roaming freely...");
    }
    sensor(integer num) {
        failFind = 10;
        iPos = llDetectedPos(0);
        moveTo(wasCirclePoint(5));
    }
    listen(integer channel, string name, key id, string message) {
        cmd = llParseString2List(message, [" "], [""]);
        if(llList2String(cmd, 0) != llGetObjectDesc()) return;
        llSensorRemove();
        llStopMoveToTarget();
        llStopLookAt();
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llSetTimerEvent(0);
        state order;
    }
    no_sensor() {
        --failFind;
        if(failFind >=0) return;
        failFind = 0;
        list odt = llGetObjectDetails(llGetOwner(), [OBJECT_POS]);
        if(odt == []) llDie();
        list pos = llParseString2List(llList2String(odt,0), ["<", ">", ","], [""]);
        iPos.x = llList2Float(pos, 0);
        iPos.y = llList2Float(pos, 1);
        iPos.z = llList2Float(pos, 2);
        llSensorRemove();
        llStopMoveToTarget();
        llStopLookAt();
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llSetTimerEvent(0);
        state jumpdrive;
    }
    collision_start(integer num) {
        moveTo(wasCirclePoint(5));
    }
    at_target(integer tnum, vector targetpos, vector ourpos) {
        if(tnum != targetID) return;
        integer info = llGetAgentInfo(llGetOwner());
        moveTo(wasCirclePoint(5));
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}
 
state order {
    state_entry() {
        // Match any selected state.
        if(llList2String(cmd,1) == "stay") state stay;
        if(llList2String(cmd,1) == "move") state move;
        if(llList2String(cmd,1) == "free") state wander;
        if(llList2String(cmd,1) == "vanish") llDie();
        // Hook
        llMessageLinked(LINK_THIS, 0, llList2CSV(cmd), NULL_KEY);
        // Alarm
        llSetTimerEvent(1 + (1-llGetRegionTimeDilation()));
        llListen(0, "", llGetOwner(), "");
    }
    link_message(integer sender_num, integer num, string str, key id) {
        if(str == "glow:MAIN:HALT") llSetTimerEvent(0);
        if(str != "glow:MAIN:CONT") return;
        // Any non-matching state makes glow wander.
        if(llList2String(cmd,1)== "stay") state stay;
        if(llList2String(cmd,1) == "move") state move;
        state wander;
    }
    listen(integer channel, string name, key id, string message) {
        cmd = llParseString2List(message, [" "], [""]);
        if(llList2String(cmd, 0) != llGetObjectDesc()) return;
        llMessageLinked(LINK_THIS, 0, "glow:PLUG:HALT", NULL_KEY);
    }
    timer() {
        llSetTimerEvent(0);
        state wander;
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}
 
state move {
    state_entry() {
        llSetStatus(STATUS_PHYSICS, TRUE);
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llSetTimerEvent(1);
        llListen(0, "", llGetOwner(), "");
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
        llInstantMessage(llGetOwner(), "Guide me...");
    }
    control(key id,integer level,integer edge) {
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        vector impulse = ZERO_VECTOR;
        if (level & CONTROL_UP)
        {
            impulse += <-.1,0,0>;
        }
        if (level & CONTROL_DOWN)
        {
            impulse += <.1,0,0>;
        }
        if (level & CONTROL_FWD)
        {
            impulse += <0,0,.1>;
        }
        if (level & CONTROL_BACK)
        {
            impulse += <0,0,-.1>;
        }
        if (level & CONTROL_ROT_RIGHT)
        {
            llApplyRotationalImpulse(<.01,0,0>, TRUE);
        }
        if (level & CONTROL_ROT_LEFT)
        {
            llApplyRotationalImpulse(<-.01,0,0>, TRUE);
        }
        llApplyImpulse(impulse,TRUE);
    }
    run_time_permissions(integer perm) {
        if (perm & PERMISSION_TAKE_CONTROLS) {
            llTakeControls(
                            CONTROL_FWD |
                            CONTROL_BACK |
                            CONTROL_LEFT |
                            CONTROL_RIGHT |
                            CONTROL_ROT_LEFT |
                            CONTROL_ROT_RIGHT |
                            CONTROL_UP |
                            CONTROL_DOWN,
                            TRUE, FALSE);
        }
    }
    listen(integer channel, string name, key id, string message) {
        cmd = llParseString2List(message, [" "], [""]);
        if(llList2String(cmd, 0) != llGetObjectDesc()) return;
        llReleaseControls();
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llSetTimerEvent(0);
        state order;
    }
    timer() {
        list odt = llGetObjectDetails(llGetOwner(), [OBJECT_POS]);
        if(odt == []) llDie();
        list pos = llParseString2List(llList2String(odt,0), ["<", ">", ","], [""]);
        iPos.x = llList2Float(pos, 0);
        iPos.y = llList2Float(pos, 1);
        iPos.z = llList2Float(pos, 2);
        if(llVecDist(llGetPos(), iPos) < 10) return;
        llReleaseControls();
        llSetVelocity(<0,0,0>, TRUE);
        llSetAngularVelocity(<0,0,0>, TRUE);
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetTimerEvent(0);
        state wander;
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}
 
state stay {
    state_entry() {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetTimerEvent(1);
        llListen(0, "", llGetOwner(), "");
        llInstantMessage(llGetOwner(), "Staying...");
    }
    listen(integer channel, string name, key id, string message) {
        cmd = llParseString2List(message, [" "], [""]);
        if(llList2String(cmd, 0) != llGetObjectDesc()) return;
        llSetTimerEvent(0);
        state order;
    }
    timer() {
        list odt = llGetObjectDetails(llGetOwner(), [OBJECT_POS]);
        if(odt == []) llDie();
        list pos = llParseString2List(llList2String(odt,0), ["<", ">", ","], [""]);
        iPos.x = llList2Float(pos, 0);
        iPos.y = llList2Float(pos, 1);
        iPos.z = llList2Float(pos, 2);
        if(llVecDist(llGetPos(), iPos) < 10) return;
        llSetTimerEvent(0);
        state wander;
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}
 
state jumpdrive
{
    state_entry() {
 
        llSetStatus(STATUS_PHYSICS, FALSE);
        // Calculate list of intermediary jump gates.
        jumps = jumpGates(llGetPos(), iPos, 10);
        llSetTimerEvent(1.175494351E-38);
        llInstantMessage(llGetOwner(), "Juuuuuuuuuuuump!"); 
    }
    timer() {
        if(llGetListLength(jumps) == 0) {
            llSetTimerEvent(0);
            state wander;
        }
        vector nPos = llList2Vector(jumps, 0);
        jumps = llDeleteSubList(jumps, 0, 0);
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, nPos]);
    }
    on_rez(integer num) {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llResetScript();
    }
}