Hello, Avatar!

Second Life et Programmation Créative

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

Du code...

Vous aimez le LSL ?
En voici, en voilà des kilomètres ! Le niveau de la difficulté du code présenté dans les articles est visible dans les tags. La difficulté est établie en fonction des critères suivants :

  • LSL Facile : Programmation élémentaire, algorithmie de base
  • LSL Assez Facile : Communication inter-primitives, boucles, tests
  • LSL Moyen : Physique, rotations, particules
  • LSL Assez Difficile : Algorithmes poussés, véhicules, astuces techniques
  • LSL Difficile : Communication externe, persistance de données, gestion d'exceptions...

Fil des billets - Fil des commentaires

Commentaires en blocs

Un truc souvent pénible avec LSL, c'est l'impossibilité de définir des blocs de commentaires, comme on le ferait dans à peu près n'importe quel langage traditionnel.

Par exemple, pour commenter un bloc de code, il faut se fader à taper "//" devant chaque ligne. LSLEditor le fait automatiquement, mais pas l'éditeur in-world, et c'est très pénible. On aimerait pouvoir écrire "/*" et "*/" à la fin comme en C#, par exemple, mais bon, on peut pas...

//    integer i = 0;
//    do
//    {
//        integer Idx = llListFindList(NewXYList, llList2List(OldXYList, i, i+1));
//        if (Idx % 2 > 0)
//        {
//            i += 2;
//        }
//        else
//        {
//            OldXYList = llDeleteSubList(OldXYList, i, i+1);
//            NewXYList = llDeleteSubList(NewXYList, Idx, Idx+1);
//            Count -= 2;
//        }
//    }
//    while(i < Count);
//    }

L'astuce que j'utilise est la suivante : j'ajoute "if (false) {" au début du bloc que je ne souhaite pas exécuter, puis "}" à la fin.

if(false) { //*
    integer i = 0;
    do
    {
        integer Idx = llListFindList(NewXYList, llList2List(OldXYList, i, i+1));
        if (Idx % 2 > 0)
        {
            i += 2;
        }
        else
        {
            OldXYList = llDeleteSubList(OldXYList, i, i+1);
            NewXYList = llDeleteSubList(NewXYList, Idx, Idx+1);
            Count -= 2;
        }
    }
    while(i < Count);
} // */

C'est plutôt dégueulasse, mais ça fait son boulot facilement et rapidement :-)

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

InterCom 2.1 : Lave plus blanc !

InterCom.jpg

Il y'a peu j'avais publié le code source d'un InterCom pour communiquer entre deux grilles.

Cette version améliorée est plus robuste, plus commentée, et surtout, elle affiche au dessus du prim toutes les régions ou se trouvent les InterComs connectés au même canal.

Retrouvez ici l'intégralité des codes sources

L'InterCom entre Grids : le code source

[Update 16/07/2008] : Nouvelle version 2.1 ici

InterCom.jpg

J'avais bricolé vite fait un InterCom pour chatter entre la Maingrid de Linden Labs et la Francogrid. J'ai repris ce proto pour l'élargir à toute grilles OpenSim.

Comme j'étais parti sur un truc un peu compliqué et assez lourd pour être publié ici, j'ai pensé très fort à Keru, qui est radio-amateur, si je ne m'abuse, et je suis revenu sur quelque chose de plus simple : un genre de poste de radio qui permet de communiquer avec toutes les personnes qui possèdent un équipement identique, pourvu qu'ils soient connectés sur le même canal.

Comment ça marche ?

L'utilisation de l'InterCom est on ne peut plus simple :

  • touchez le pour afficher le menu qui permet de l'allumer ou de l'éteindre,
  • tapez une commande dans le chat pour changer son numéro de canal.
    Par défaut, l'InterCom utilise le canal 0. Tapez /100 250 dans le chat, et le numéro de canal sera le 250.

Pour que deux InterComs puissent communiquer entre eux, qu'ils soient sur la Maingrid des Lindens ou sur la Francogrid, ou l'OSGrid, ou la NewWorldGrid, ou la Machingrid... il faut qu'ils utilisent le même numéro de canal.

Le Script !! Le Script !!

Retrouvez l'intégralité des codes sources ici.

Voilà. Amusez-vous bien avec l'InterCom ^^ !

Un InterCom entre la Francogrid et la Maingrid

InterCom.jpg

Le principe, c'est prim qui achemine les communications du chat principal de la Francogrid à la Maingrid... et inversement.

Cliquez pour agrandir l'image : Forest à gauche sur la Maingrid, qui parle à Grumly à droite, sur la Francogrid. Une façon de faire là aussi, un peu d'intéropérabilité.

Voilà. Le concept est validé, maintenant, il ne me reste plus qu'à peindre l'accessoire, et à le perfectionner un peu !

J'ai dû réparer llHTTPRequest qui contenait un bug bloquant. Un peu la même histoire que pour le XMLRpc...

Il ne me reste plus qu'à attendre que la correction soit intégré au code.

Retrouvez ici l'intégralité des codes sources

- page 1 de 6