Tic Tac Toe/Summary - Second Life Wiki

Tic Tac Toe/Summary

From Second Life Wiki

Second Life Wiki > Tic Tac Toe/Summary
Jump to: navigation, search

The object we are scripting is composed of 9 cubes with an X/O texture pre-set and a back pane prim as the root prim. The link order of the child prims is assumed to be in any sequential reading order (left to right, top to bottom or similar).

Contents

Root Prim

The root prim is populated with the scripts below:

RootNanny (processes upload requests onto the child prims and manages object name)

// Message constants
integer MSG_RESET = 0;
integer MSG_LINK_QUERY = 10;
integer MSG_LINK_REPLY = 11;
 
// Globals to propagate
string version = "1.5";
integer pin = 321;
 
 
// State
integer current_link_nr;
 
// Stuff to send out
list remote_scripts = [ "TouchDisplay", "LinkNanny" ];
 
request_next_object_id(integer link_nr)
{
    // Send message, transmitting pin, hoping to get back objectid
    llMessageLinked(link_nr, MSG_LINK_QUERY, (string)pin, NULL_KEY);
 
    // Set timer in case link isn't replying
    llSetTimerEvent(2); // 2 secs seems generous
}
 
default
{
    state_entry()
    {
        string name = llGetObjectName();
        integer space = llSubStringIndex(name, " v");
        if (space < 0) llSetObjectName(name + " v" + version);
        else llSetObjectName(llDeleteSubString(name, space+2, -1) + version);
        llListen(1, "", llGetOwner(), "update");
    }
 
    listen(integer channel, string name, key id, string message)
    {
        state uploading;
    }
}
 
state uploading
{
    state_entry()
    {
        current_link_nr = llGetNumberOfPrims();
        // Check if it's more than one
        if (1 < current_link_nr)
        {
            // avatars sitting on us get added at the end, so subtract...
            while (llGetAgentSize(llGetLinkKey(current_link_nr)))
                --current_link_nr;
            request_next_object_id(current_link_nr);
        }
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (from == current_link_nr && msg_id == MSG_LINK_REPLY)
        {
            llSetTimerEvent(0); // Cancel timeout
            integer i;
            for (i = 0; i < llGetListLength(remote_scripts); ++i)
            {
                string script = llList2String(remote_scripts, i);
                llSay(0, "Uploading '"+script+"' to link nr "+(string)current_link_nr);
                llRemoteLoadScriptPin(id, script, pin, TRUE, 0);
            }
            current_link_nr--;
            if (1 < current_link_nr)
            {
                request_next_object_id(current_link_nr);
            }
            else
            {
                llSay(0, "Done uploading remote scripts.");
                state default;
            }
        }
    }
 
    timer()
    {
        llSay(0, "Failed to receive reply from link nr "+(string)current_link_nr);
        current_link_nr--;
        if (1 < current_link_nr)
        {
            request_next_object_id(current_link_nr);
        }
        else
        {
            llSay(0, "Done uploading remote scripts.");
            state default;
        }
    }
}

Controller (game logic)

// Message constants
integer MSG_RESET      = 0;
integer MSG_TOUCH      = 1;
integer MSG_SET_X      = 2;
integer MSG_SET_O      = 3;
integer MSG_IDLE       = 4;
integer MSG_IDLE_TOUCH = 5;
integer MSG_WIN        = 6;
 
// Game timeout
integer GAME_TIMEOUT = 20;
 
// Game state
key player_x;
key player_o;
string turn;
string game;
 
string at(integer u, integer v)
{
    integer p = u*3+v;
    return llGetSubString(game, p, p);
}
 
// return TRUE if game ends
integer game_ends(string player, integer move)
{
    game = llInsertString(llDeleteSubString(game, move, move), move, player);
    llSay(0, "game = "+game);
 
    // Check if this made three in a row
    integer u = move / 3;
    integer v = move % 3;
    integer i;
    integer c;
 
    // check horizontal
    c = 0;
    for (i = 0; i < 3; i++) if (at(i, v) == player) c++;
    if (c == 3)
    {
        for (i = 0; i < 3; i++) llMessageLinked(2 + 3*i + v, MSG_WIN, "", NULL_KEY);
        return TRUE;
    }
 
    // check vertical
    c = 0;
    for (i = 0; i < 3; i++) if (at(u, i) == player) c++;
    if (c == 3)
    {
        for (i = 0; i < 3; i++) llMessageLinked(2 + 3*u + i, MSG_WIN, "", NULL_KEY);
        return TRUE;
    }
 
    // check if we are on one diagonal
    c = 0;
    if (u == v) for (i = 0; i < 3; i++) if (at(i, i) == player) c++;
    if (c == 3)
    {
        for (i = 0; i < 3; i++) llMessageLinked(2 + 4*i, MSG_WIN, "", NULL_KEY);
        return TRUE;
    }
 
    // check if we are on the other diagonal
    c = 0;
    if (u + v == 2) for (i = 0; i < 3; i++) if (at(i, 2-i) == player) c++;
    if (c == 3)
    {
        for (i = 0; i < 3; i++) llMessageLinked(4 + 2*i, MSG_WIN, "", NULL_KEY);
        return TRUE;
    }
 
    // Check if there are any spaces left
    if (llSubStringIndex(game, "-") < 0) return TRUE;
 
    // Game goes on
    return FALSE;
}
 
default
{
    state_entry()
    {
        player_x = NULL_KEY;
        player_o = NULL_KEY;
        game = "---------";
        turn = "x";
 
        llMessageLinked(LINK_ALL_CHILDREN, MSG_RESET, "", NULL_KEY);
        state playing;
    }
}
 
state playing
{
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_TOUCH)
        {
            llSay(0, "touch from "+(string)from);
            if (turn == "x" && NULL_KEY == player_x)
            {
                llSetTimerEvent(0);
                llMessageLinked(from, MSG_SET_X, "", NULL_KEY);
                player_x = id;
                if (game_ends(turn, from-2)) state idle;
                turn = "o";
                llSetTimerEvent(GAME_TIMEOUT);
            }
            else if (turn == "o" && NULL_KEY == player_o)
            {
                llSetTimerEvent(0);
                llMessageLinked(from, MSG_SET_O, "", NULL_KEY);
                player_o = id;
                if (game_ends(turn, from-2)) state idle;
                turn = "x";
                llSetTimerEvent(GAME_TIMEOUT);
            }
            else if (turn == "x" && id == player_x)
            {
                llSetTimerEvent(0);
                llMessageLinked(from, MSG_SET_X, "", NULL_KEY);
                if (game_ends(turn, from-2)) state idle;
                turn = "o";
                llSetTimerEvent(GAME_TIMEOUT);
            }
            else if (turn == "o" && id == player_o)
            {
                llSetTimerEvent(0);
                llMessageLinked(from, MSG_SET_O, "", NULL_KEY);
                if (game_ends(turn, from-2)) state idle;
                turn = "x";
                llSetTimerEvent(GAME_TIMEOUT);
            }
        }
    }
 
    timer()
    {
        llSetTimerEvent(0);
        state idle;
    }
}
 
state idle
{
    state_entry()
    {
        llSay(0, "state idle");
        llMessageLinked(LINK_ALL_CHILDREN, MSG_IDLE, "", NULL_KEY);
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_IDLE_TOUCH) state default;
    }
}

Child Prims

The child prims all have the same set of scripts:

LinkNanny (answers queries from the RootNanny and manages the upload pin)

// Message constants
integer MSG_RESET = 0;
integer MSG_LINK_QUERY = 10;
integer MSG_LINK_REPLY = 11;
 
default
{
    state_entry()
    {
        // If we are in the the root prim...
        if (llGetLinkNumber() < 2)
        {
            // ... disable ourselves
            llSetScriptState(llGetScriptName(), FALSE);
            llSleep(2);
        }
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_LINK_QUERY)
        {
            // Set pin given
            llSetRemoteScriptAccessPin((integer)str);
            // Tell caller who we are
            llMessageLinked(from, MSG_LINK_REPLY, "", llGetKey());
        }
    }
}

TouchDisplay (handles clicks, displays game elements)

// Locations of various bits on the texture
float e_pos_s = 0.75;
float e_pos_t = 0.75;
float x_pos_s = 0.75;
float x_pos_t = 0.25;
float o_pos_s = 0.25;
float o_pos_t = 0.25;
 
// Face on wjich the texture lives
integer DISPLAY_FACE = 4;
vector  RED   = <1,0,0>;
vector  WHITE = <1,1,1>;
 
// Message constants
integer MSG_RESET      = 0;
integer MSG_TOUCH      = 1;
integer MSG_SET_X      = 2;
integer MSG_SET_O      = 3;
integer MSG_IDLE       = 4;
integer MSG_IDLE_TOUCH = 5;
integer MSG_WIN        = 6;
 
default
{
    state_entry()
    {
        // If we are in the the root prim...
        if (llGetLinkNumber() < 2)
        {
            // ... disable ourselves
            llSetScriptState(llGetScriptName(), FALSE);
            llSleep(2);
        }
 
        // Reset color and texture
        llSetColor(WHITE, DISPLAY_FACE);
        llOffsetTexture(e_pos_s, e_pos_t, DISPLAY_FACE);
    }
 
    touch_start(integer touching_agents)
    {
        while (touching_agents--)
        {
            llMessageLinked(LINK_ROOT, MSG_TOUCH, "", llDetectedKey(touching_agents));
        }   
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_SET_X) state x;
        if (msg_id == MSG_SET_O) state o;
        if (msg_id == MSG_IDLE) state idle;
    }
}
 
state x
{
    state_entry()
    {
        llOffsetTexture(x_pos_s, x_pos_t, DISPLAY_FACE);
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_RESET) state default;
        if (msg_id == MSG_IDLE) state idle;
        if (msg_id == MSG_WIN) llSetColor(RED, DISPLAY_FACE);
    }
}
 
state o
{
    state_entry()
    {
        llOffsetTexture(o_pos_s, o_pos_t, DISPLAY_FACE);
    }
 
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_RESET) state default;
        if (msg_id == MSG_IDLE) state idle;
        if (msg_id == MSG_WIN) llSetColor(RED, DISPLAY_FACE);
    }
}
 
state idle
{
    link_message(integer from, integer msg_id, string str, key id)
    {
        if (msg_id == MSG_RESET) state default;
    }
 
    touch_start(integer touching_agents)
    {
        llMessageLinked(LINK_ROOT, MSG_IDLE_TOUCH, "", NULL_KEY);
    }
}