Hello, Avatar!

Second Life et Programmation Créative

Aller au contenu | Aller au menu | Aller à la recherche

Bubble Breaker, testé et approuvé par Mascottus

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

Les évolutions d'opensim.ini

Pas mal de temps sans remplir Hello, Avatar, pour plusieurs raisons : nouveau travail, nouvelle organisation, quelques projets d'ordre privé donc prioritaires... A présent je commence juste à toucher terre et on va pouvoir y retourner !

Ca faisait un bout que je n'avais pas repris mon opensim.ini pour y intégrer les évolutions et les changements. Parce que l'air de rien, si ce fichier de paramètres n'a pas bougé depuis un bout de temps, le petit fichier opensim.ini.example qui l'accompagne bouge drôlement, lui...

De ce fait, un beau jour, mon moteur de scripting (XEngine) s'est mis à rechigner, parce qu'un nouveau paramètre a été ajouté par melanie pour son bon fonctionnement.
Melanie l'a donc ajouté dans opensim.ini.example, mais comme il n'est pas venu tout seul dans opensim.ini, ça a fait "kerboom" comme dirait l'autre (que je salue au passage).

Pour refaire un opensim.ini à jour, il y'a deux manière :

  • Soit on copie opensim.ini.example, on le renomme en opensim.ini et on remet un par un tous les paramètres spécifiques à son installation.
    • Ca c'est la solution dernière chance en cas de gros gros changements. C'est parfois assez pénible
  • Soit on ouvre opensim.ini d'un côté, opensim.ini.example de l'autre, et puis on fait les modifications.
    • Quand il y'a un tout petit paramètre, ça va encore. Mais quand une floppée de fonctionalités s'ajoutent, ça devient une catastrophe et on est tenté d'en venir à la première solution.

Heureusement, pour ceux qui utilisent TortoiseSVN, il y'a un outil appelé TortoiseMerge que j'ai découvert il y'a pas deux jours, permettant d'ouvrir deux fichiers côte à côte, de prendre l'un ou l'autre côté, et d'éditer le résultat final.

tortue.png

Dans tous les cas, j'ouvre mon fichier opensim.ini, le fichier opensim.example.ini avec TortoiseMerge. Les différences et les ajouts apparaissement alors de façon claire et lisible, et il ne me reste plus qu'à les intégrer en quelque clics.

En vacances...

...jusqu'au 29 août :-)

A la rentrée, quelques petits travaux de scripts pour le projet Sail Away (promis, Rafale, je les finirais à mon retour), un projet avec OpenSim et Silverlight techniquement assez ambitieux, mais là, je m'en vais 15 jours sans ordinateur, sans rien (tout nu, quoi), pour revenir plein d'énergie.

Comme je suis pas là pour surveiller Hello, Avatar!, la modération des commentaires est activée ;-)

Merci de votre passage ici, je salue bien bas mes amis et amies InWorld, et je vous dis à bientôt !

Remember et le projet Sail Away

J'avais écris ici il y'a peu à propos du projet Sail Away, découvert grâce à Mallory Destiny.

Voir ici le site officiel du projet : http://www.sailawayproject.eu/

Le projet avance bien, et l'île est prête à accueillir des visiteurs. Il est possible d'y louer bateaux, maisons, et d'y acheter vêtements et des objets relatifs à la piraterie. Et les amateurs de freebies ne seront pas en reste...

Snapshot_009.jpg

Cliquez sur le tonneau pour faire apparaitre ce magnifique bateau volant buildé par Apocalypto Wolfzahn (et scripté par devinez qui ?)...

Snapshot_008.jpg

...afin de vous rendre sur la plateforme ou se trouvent les décors pour le projet.

Snapshot_010.jpg

Vous pourrez alors vous rendre compte du travail somptueux accompli par l'équipe de builders

Snapshot_003.jpg

Snapshot_004.jpg

Snapshot_001.jpg

Snapshot_005.jpg

N'oubliez pas d'aller contempler le travail d'Apocalypto, l'armurier en chef.

Snapshot_002.jpg

Un sabre serti d'un petit crâne sculpté. Pas très rassurant...

Snapshot_007.jpg

Des pistolet d'époque, munis d'une baïonnettes. Ca doit faire mal.

Snapshot_006.jpg

Un grand merci à toi Rafale, de nous régaler les yeux avec cette île superbe et ce projet extraordinaire !

Migration de "Hello, Avatar!" vers DotClear 2.0

Tout s'est bien passé, mais j'ai perdu le thème : les codes sources ne sont pas très lisibles... Je m'y recolle dès que je trouve un peu de temps.

- page 2 de 29 -