A peine j'ai eu le temps de finir ce petit jeu et en offrir un exemplaire à quelques amis, vlatipas que l'ami Mascottus Phlox se fend d'un billet sur Bubble Breaker après deux excellentes revues de liens.

bubblebreaker.jpg

Pour fabriquer votre bubble breaker sur la maingrid, il vous faudra :

  • Rezzer une baballe de taille 0.25, 0.25, 0.25
  • Lui insérer ce petit script (ci-dessous)
default
{
        link_message(integer p, integer i, string s, key k)
        {
                llSetText(s, <1,1,1>, 1);
        }
}
  • Dupliquer cette sphère 80 fois, puis lier l'ensemble sans vous fatiguer à les positionner précisément.
    Au final, vous devez vous retrouver avec un objet composés de 81 sphères.
  • Insérez le script ci-dessous dans l'objet une fois qu'il est lié
// Copyright (c) 2008 - Forest Klaar (Second Life)
//
// Redistribution and use in source forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Bubble Breaker v0.2

list gBubbles;
list gColors = [<1,1,1>, <1,0,0>,<0,1,0>,<0,0,1>,<1,1,0>,<1,0,1>];
list gSelect;
integer gScore;
string gPlayer;
list gBest;
integer gLastSelected;

// Magic numbers
integer TEXT_PRIM = 45; // This prim is responsible of text display

integer Ran()
{
    return llFloor(llFrand(4))+1;
}

BubbleText(integer n, string s)
{
    llMessageLinked(n, 0, s, NULL_KEY);
}

integer IsGameOver()
{
    integer i;
    for(i=0; i<81; i++)
    {
        integer Type = llList2Integer(gBubbles, i);
        if (Type != 0)
        {
            if (i/9 != 8)
            {
                if (Type == llList2Integer(gBubbles, i+9))
                    return FALSE;
            }

            if (i%9 != 8)
            {
                if (Type == llList2Integer(gBubbles, i+1))
                    return FALSE;
            }
        }
    }

    return TRUE;
}

Refresh(float alpha)
{
    integer i;
    for(i=0; i<81; i++)
    {
        integer Type = llList2Integer(gBubbles, i);
        if (Type == 0)
            llSetLinkAlpha(i+1, 0, ALL_SIDES);
        else
        {
            llSetLinkColor(i+1, llList2Vector(gColors, Type), ALL_SIDES);
            llSetLinkAlpha(i+1, alpha, ALL_SIDES);
        }
    }
}

Shift(integer n)
{
    integer Col = n/9;

    if (n%9 == 8)
    {
        gBubbles = llListReplaceList(gBubbles, [0], Col*9+8, Col*9+8);
        return;
    }

    list Zone = llList2List(gBubbles, n+1, Col*9+8);

    gBubbles = llListReplaceList(gBubbles, Zone + [0], n, Col*9+8);
}

Roll()
{
    integer i;
    for(i=0; i<9; i++)
        if (llDumpList2String(llList2List(gBubbles, i*9, i*9+8),"") == "000000000")
            gBubbles = [0,0,0,0,0,0,0,0,0] + llListReplaceList(gBubbles, [], i*9, i*9+8);
}

Select(integer n, integer type)
{
    if (n < 0 || n > 80) return;
    if (llList2Integer(gBubbles, n) != type) return;
    if (llListFindList(gSelect, [n]) != -1) return;

    gSelect += n;

    if (n/9 != 0)
        Select(n-9, type);

    if (n/9 != 8)
        Select(n+9, type);

    if (n%9 != 0)
        Select(n-1, type);

    if (n%9 != 8)
        Select(n+1, type);
}

Build()
{
    llSay(0, "Building bubbles..");
    float Radius = .25;

    integer i; integer j;
    for(i=0; i<9; i++)
        for(j=0; j<9; j++)
            if (i+j != 0)
                llSetLinkPrimitiveParams(i*9+j+1,
        [PRIM_POSITION, <0, Radius * (float) i, Radius * (float) j>]);

    llSay(0, "Finished !");
}

Demo()
{
    gBubbles = [];
    integer i; integer c = 1;
    for (i=0; i<81; i++)
    {
        gBubbles += [c++];
        if (c > 5) c = 1;
    }

    Refresh(1);
}

Init()
{
    BubbleText(gLastSelected, "");
    gBubbles = [];
    integer i;
    for (i=0; i<9; i++)
        gBubbles += [Ran(),Ran(),Ran(),Ran(),Ran(),Ran(),Ran(),Ran(),Ran()];

    Refresh(1);
}

string DisplayScores()
{
    string Display;
    integer i; integer n=llGetListLength(gBest)/2;
    for(i=0; i<n; i++)
        Display += llList2String(gBest, i*2+1) + " : " + llList2String(gBest, i*2) + "\n";
    return Display;
}

SelectHightlight(float alpha)
{
    integer i; integer cn = llGetListLength(gSelect);
    for (i=0; i<cn; i++)
        llSetLinkAlpha(llList2Integer(gSelect, i)+1, alpha, ALL_SIDES);
}

default
{
    state_entry()
    {
        Demo();
        state waiting;
    }
}

state waiting
{
    state_entry()
    {
        BubbleText(TEXT_PRIM, "Bubble Breaker\n" + DisplayScores() + "\nTouch me to start a game");
        llListen(5, "", llGetOwner(), "");
    }

    listen(integer c, string n, key k, string m)
    {
        if (m == "build")
            Build();
    }

    touch_start(integer c)
    {
        gPlayer = llDetectedName(0);
        Init();
        state game;
    }
}

state game
{
    state_entry()
    {
        gScore = 0;
        BubbleText(TEXT_PRIM, gPlayer + " is playing\nScore: " + (string) gScore);
    }

    touch_start(integer c)
    {
        integer n = llDetectedLinkNumber(0)-1;
        integer Type = llList2Integer(gBubbles, n);

        if (Type == 0) return;

        BubbleText(gLastSelected, "");

        integer i;

        if (llListFindList(gSelect, [n]) != -1)
        {
            gSelect = llListSort(gSelect, 1, FALSE);

            integer ct = llGetListLength(gSelect);
            for (i=0; i<ct; i++)
                Shift(llList2Integer(gSelect, i));

            Roll();

            gScore += ct * (ct - 1);

            if (IsGameOver())
            {
                Refresh(.5);

                string Text = "* Game Over *\n" + gPlayer + " scored " + (string) gScore;

                list ScoreEntry = [gScore, gPlayer];

                gBest += ScoreEntry;
                gBest = llListSort(gBest, 2, FALSE);

                gBest = llList2List(gBest, 0, 9);

                if (llListFindList(gBest, ScoreEntry) != -1)
                    Text += "\nCongratulations ! You are in the top 5!";

                BubbleText(n+1, Text);

                state waiting;
            }

            Refresh(1);
            gSelect = [];

            BubbleText(TEXT_PRIM, gPlayer + " is playing\nScore: " + (string) gScore);
            return;
        }

        SelectHightlight(1);

        gSelect = [];

        Select(n, Type);

        integer cn = llGetListLength(gSelect);

        if (cn < 2)
        {
            BubbleText(TEXT_PRIM, gPlayer + " is playing\nScore: " + (string) gScore);
            gSelect = [];
            return;
        }

        BubbleText(TEXT_PRIM, gPlayer + " is playing\nScore: " + (string) gScore + "\n" + (string) cn + " bubbles selected");

        BubbleText(n+1, (string) (cn * (cn-1)));
        gLastSelected = n+1;

        SelectHightlight(.5);
    }
}

Voilà. Donc le jeu se lance...

Ne cliquez rien encore : l'ordre dans lequel les primitives est primordial pour son bon fonctionnement.

Pour vous épargner ce travail fastidieux, un bout de script se chargera de ce sale boulot une bonne fois pour toutes.
Tapez dans le tchat la commande suivante :

/5build

Après ça, les petites boules que vous avez entassé en vrac vont comme par magie se ranger toutes seules, et vous pourrez ranger Bubble Breaker dans votre inventaire !

Ca marche très bien sur un OpenSim !! (voir la photo)
Mais le rangement des boules automatique avec /5build ne fonctionne pas sur un OpenSim. Il faudra ranger à la main et côte à côte 9 rangées de 9 boules.
C'est très facile avec la règle : créer trois boules, recopier la ligne obtenue deux fois pour faire un blog de 3x3 boules, puis répétez l'opération avec ce bloc pour enfin obtenir 81 boules bien alignées.
Sélectionnez ensuite les boules une par une, en commançant en haut à droite, puis en redescendant sur la même colonne jusqu'à la boule du bas, puis même chose à la colonne suivante, jusqu'au dernier prim en bas à gauche. Faites Outils/Lier. Et voilà ! Vous pouvez glisser le script dans le jeu !

Et maintenant, c'est à vous de jouer ! Record perso à battre : 950 :-D