ChangeLog

20 March 2012

  • Added llResetScript to switch to the destination owner key when the game is given away. Marketplace product will be updated since people may have had problems because the scripts used my own avatar key (bug, feature, idk… It is documented at llGetOwner).

Introduction

This is the implementation of a very popular game, which appeared around the 70s and became a standard for Nokia phones in 1988. This is an implementation of the game in Second Life by using a HUD and a frame for the snake.

YouTube Video

Overview

The game has 3 main components:

  • the snake frame, the one that gives the bounds of movement for the snake.
  • the snake body, the objects that will be rezzed depending on the snake's tail length.
  • the snake food which the snake will have to eat in order to grow.

The task is to eat as much snake food as possible by moving around using the HUD and without colliding with the snake's body or going out of bounds. Any collision will end the game.

Marketplace

The full implementation may be found on marketplace.

Code: [K] Snake

This part goes in the snake frame.

snake.lsl
//////////////////////////////////////////////////////////
//     WaS (c) grimore.org - 2012, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
integer _comChannel = 0;
vector oPos = ZERO_VECTOR;
vector sPos = ZERO_VECTOR;
integer tItra = 0;
integer fItra = 0;
list tail = [];
list food = [];
integer TRAIL = 1;
 
//pragma inline
processFood(integer fIdx) {
    if(fIdx == -1) return;
    if(llVecDist(llList2Vector(food, fIdx), llGetPos() + oPos) > .5) jump next;
    ++TRAIL;
    food = llListReplaceList(food, (list)"", fIdx, fIdx);
    generateFood(1);
    llRegionSay(_comChannel, "EAT," + (string)(fIdx+1));
@next;
    processFood(--fIdx);
}
 
generateFood(integer num) {
    if(num == 0) return;
    vector fPos = <sPos.x + llFrand(5)*llList2Float([-1, 1], (integer)llFrand(2)), sPos.y, sPos.z + llFrand(5)*llList2Float([-1, 1], (integer)llFrand(2))>;
    food += fPos;
    llRezObject("[K] Snake Food", fPos, ZERO_VECTOR, ZERO_ROTATION, ++fItra);
    generateFood(--num);  
}
 
default
{
    state_entry() {
        sPos = llGetPos();
        _comChannel = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llSetLinkPrimitiveParamsFast(2, [PRIM_POSITION, ZERO_VECTOR]);
        llRegionSay(_comChannel, "CLR");
        generateFood(1+(integer)llFrand(10));
        llListen(_comChannel, "", "", "");
    }
    at_target(integer tnum, vector targetPos, vector ourPos) {
        llStopMoveToTarget();
    }
    listen(integer chan,string name,key id,string mes) {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        if(mes == "UP") state up;
        if(mes == "DOWN") state down;
        if(mes == "LEFT") state left;
        if(mes == "RIGHT") state right;
    }
    changed(integer change) {
        if(change & CHANGED_OWNER) state cleanup;
    }
    on_rez(integer param) {
        state cleanup;
    }
}
 
state up {
    state_entry() {
        llListen(_comChannel, "", "", "");
        llSetTimerEvent(1);
    }
    listen(integer chan,string name,key id,string mes) {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        if(mes == "UP") state up;
        if(mes == "DOWN") state down;
        if(mes == "LEFT") state left;
        if(mes == "RIGHT") state right;
    }
    timer() {
        processFood(llGetListLength(food)-1);
        if(llFabs(oPos.z + .5) > 5 || ~llListFindList(tail, (list)oPos)) {
            llOwnerSay("Game Over.");
            state cleanup;
        }
        llRegionSay(_comChannel, "DIE," + (string)(tItra-TRAIL));
        if(llGetListLength(tail) == TRAIL) tail = llDeleteSubList(tail, 0, 0);
        llSetLinkPrimitiveParams(2, [PRIM_POS_LOCAL, (oPos+=<0,0,.5>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos - <0,0,.5>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos-<0,0,.5>;
    }
}
state down {
    state_entry() {
        llListen(_comChannel, "", "", "");
        llSetTimerEvent(1);
    }
    listen(integer chan,string name,key id,string mes) {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        if(mes == "UP") state up;
        if(mes == "DOWN") state down;
        if(mes == "LEFT") state left;
        if(mes == "RIGHT") state right;
    }
    timer() {
        processFood(llGetListLength(food)-1);
        if(llFabs(oPos.z - .5) > 5 || ~llListFindList(tail, (list)oPos)) {
            llOwnerSay("Game Over.");
            state cleanup;
        }
        llRegionSay(_comChannel, "DIE," + (string)(tItra-TRAIL));
        if(llGetListLength(tail) == TRAIL) tail = llDeleteSubList(tail, 0, 0);
        llSetLinkPrimitiveParams(2, [PRIM_POS_LOCAL, (oPos-=<0,0,.5>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos + <0,0,.5>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos+<0,0,.5>;
    }
}
state left {
    state_entry() {
        llListen(_comChannel, "", "", "");
        llSetTimerEvent(1);
    }
    listen(integer chan,string name,key id,string mes) {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        if(mes == "UP") state up;
        if(mes == "DOWN") state down;
        if(mes == "LEFT") state left;
        if(mes == "RIGHT") state right;
    }
    timer() {
        processFood(llGetListLength(food)-1);
        if(llFabs(oPos.x - .5) > 5 || ~llListFindList(tail, (list)oPos)) {
            llOwnerSay("Game Over.");
            state cleanup;
        }
        llRegionSay(_comChannel, "DIE," + (string)(tItra-TRAIL));
        if(llGetListLength(tail) == TRAIL) tail = llDeleteSubList(tail, 0, 0);
        llSetLinkPrimitiveParams(2, [PRIM_POS_LOCAL, (oPos-=<.5,0,0>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos + <.5,0,0>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos+<.5,0,0>;
    }
}
state right {
    state_entry() {
        llListen(_comChannel, "", "", "");
        llSetTimerEvent(1);
    }
    listen(integer chan,string name,key id,string mes) {
        if(llGetOwnerKey(id) != llGetOwner()) return;
        if(mes == "UP") state up;
        if(mes == "DOWN") state down;
        if(mes == "LEFT") state left;
        if(mes == "RIGHT") state right;
    }
    timer() {
        processFood(llGetListLength(food)-1);
        if(llFabs(oPos.x + .5) > 5 || ~llListFindList(tail, (list)oPos)) {
            llOwnerSay("Game Over.");
            state cleanup;
        }
        llRegionSay(_comChannel, "DIE," + (string)(tItra-TRAIL));
        if(llGetListLength(tail) == TRAIL) tail = llDeleteSubList(tail, 0, 0);
        llSetLinkPrimitiveParams(2, [PRIM_POS_LOCAL, (oPos+=<.5,0,0>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos - <.5,0,0>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos-<.5,0,0>;
    }
}
 
state cleanup {
    state_entry() {
        llRegionSay(_comChannel, "CLR");
        llResetScript();
    }
}

Code: [K] HUD - Direction

This will have to be copied and modified for all directions: UP, DOWN, LEFT and RIGHT.

snake_hud.lsl
//////////////////////////////////////////////////////////
//     WaS (c) grimore.org - 2012, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
integer _comChannel;
 
default
{
    state_entry() {
        _comChannel = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(_comChannel, "", llGetOwner(), "");
    }
    touch_start(integer total_number) {
        llRegionSay(_comChannel, "LEFT"); // Change this to either UP, DOWN, LEFT or RIGHT
    }
    on_rez(integer param) {
        llResetScript();
    }
}

Code: [K] Snake Body

snake_body.lsl
///////////////////////////////////////////////////////////////////////////
//  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.        //
///////////////////////////////////////////////////////////////////////////
 
default
{
    state_entry() {
        integer comChannel = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(comChannel, "", "", "");
    }
 
    listen(integer chan,string name,key id,string mes) {
        if(mes == "DIE," + llGetObjectDesc()) jump die;
        if(mes == "CLR") jump die;
        return;
@die;
        llDie();
    }
    on_rez(integer param) {
        llSetObjectDesc((string)param);
        llResetScript();
    }
}

Code: [K] Snake Food

snake_food.lsl
//////////////////////////////////////////////////////////
//     WaS (c) grimore.org - 2012, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
default
{
    state_entry() {
        integer comChannel = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(comChannel, "", "", "");
    }
 
    listen(integer chan, string name, key id, string mes) {
        if(mes == "EAT," + llGetObjectDesc()) jump die;
        if(mes == "CLR") jump die;
        return;
@die;
        llDie();
    }
    on_rez(integer param) {
        llSetObjectDesc((string)param);
        llResetScript();
    }
}

Code: [K] Score

This snippet will keep track of the food eaten by the snake. This script is optional and should be placed in the snake head.

snake_score.lsl
///////////////////////////////////////////////////////////////////////////
//  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 eat = 0;
 
default
{
    state_entry() {
        llSetText("", <0,0,0>, .0);
        integer _comChannel = ((integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        llListen(_comChannel, "", "", "");
    }
 
    listen(integer chan,string name,key id,string mes) {
        if(~llSubStringIndex(mes, "EAT")) {
            llSetText("Eaten: " + (string)(++eat), <1,1,1>, 1);
        }
        if(~llSubStringIndex(mes, "CLR")) {
            llResetScript();
        }
    }
}

secondlife/snake.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.