Snake Game

From Second Life Wiki
Jump to: navigation, search

Created by Kira Komarov.

Tile-Related Tree

+-------------------------+ | Planar Tile Generator | +------------+------------+ | +---------------------+---------------------------+-------------------------+ | | | | +------------+-------------+ +----------------+ +-------------+-------------+ +--------+-------+ | Spatial Tile Generator | | Holo Display | | Planar Fibbonacci Tiles | | Snake Game | +--------------------------+ +----------------+ +---------------------------+ +----------------+

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

Soundtrack: Ulrich Schnauss - Passing By.

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 here, free of charge and full permission.

Code: [K] Snake

This part goes in the snake frame.

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 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) > 0.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 + 0.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,0.5>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos - <0,0,0.5>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos-<0,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 - 0.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,0.5>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos + <0,0,0.5>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos+<0,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 - 0.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.5,0,0>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos + <0.5,0,0>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos+<0.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 + 0.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.5,0,0>) / llGetRootRotation()]);
        llRezObject("[K] Snake Body", llGetPos() + oPos - <0.5,0,0>, ZERO_VECTOR, ZERO_ROTATION, tItra++);
        tail += oPos-<0.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.

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 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

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, 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 == "DIE," + llGetObjectDesc()) jump die;
        if(mes == "CLR") jump die;
        return;
@die;
        llDie();
    }
    on_rez(integer param) {
        llSetObjectDesc((string)param);
        llResetScript();
    }
}

Code: [K] Snake Food

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 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.

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, 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 eat = 0;
 
default
{
    state_entry() {
        llSetText("", <0,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.0);
        }
        if(~llSubStringIndex(mes, "CLR")) {
            llResetScript();
        }
    }
}