Bienvenue sur JeuxOnLine - MMO, MMORPG et MOBA !
Les sites de JeuxOnLine...
 

Panneau de contrôle

Recherche | Retour aux forums

JOL Archives

Script : Taverne dynamique

Par SidSilver le 7/11/2002 à 19:33:29 (#2507242)

Etapes du script :

- La serveuse va voir le client et lui demande ce qu'il veut
- Le client répond et s'assoit
- La serveuse va chercher la commande au bar
- Elle la raporte au client
- Le client la remercie

Pour utiliser ce script :

- Créer un barman avec pour tag "Barman"
- Créer un waypoint "NW_Barman" juste à côté du barman
- Créer une serveuse (ou plusieurs) avec pour tag "Serveuse"
- Créer des Clients avec pour tag "Client"
- Créer autant de chaises que de clients avec pour tag "ChaiseClient"
- Placer les clients chacun à côté d'une chaise
- Placer le script dans le OnHeartBeat de la serveuse


//------------------------------------------------------------
// Script de Barmaid.
// Barmaid States
// 0 -> Rnd Walk, wanders for Client.
// 1 -> Client Found . Moves to he nearest (rnd) customer.
// 2 -> Client sits, Ask for Command
// 3 -> Client answers
// 4 -> Moves to the bar to get the Drinks
// 5 -> Announce the comand
// 6 -> Moves back to the custommer to deliver.
// 7 -> Deliver
//------------------------------------------------------------


string usr_Rndspeak( int type );

//----------------------------------------------------------
//
// Main routine.
//
//
//----------------------------------------------------------
object oCustomer; //= GetLocalObject(OBJECT_SELF, "Client");
object oBar = GetWaypointByTag("NW_Barman"); //This waypoint is where she gets drinks from

void main()
{
int nRandom;
// Get from context the Last chair seen !
object oNearestChair = GetLocalObject (OBJECT_SELF, "ChaiseClient" );
object oCustomer = GetLocalObject(OBJECT_SELF, "Client");
int state = GetLocalInt(OBJECT_SELF, "BARMAID_STATE");
location lServeuse = GetLocation(OBJECT_SELF);
// customer answer
string CustAnswer= GetLocalString(OBJECT_SELF , "CUSTANSW" );

// Barmaid state from context

// Get the User Defined Event that triggered this script

int nUser = GetUserDefinedEventNumber();

object oPlayer = GetNearestCreature( CREATURE_TYPE_PLAYER_CHAR , PLAYER_CHAR_IS_PC);

SetCommandable(TRUE ,oCustomer);
switch(nUser)
{
case 1001 : // UD HeartBeat

switch( state )
{
case 0 : // Wander for customers

ClearAllActions(); // too many actions ?

oCustomer = GetNearestCreature( CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, OBJECT_SELF, Random(5));

// search the Nearest Chair from Customer
oNearestChair = GetNearestObjectByTag( "ChaiseClient", oCustomer);

// If Customer Found is Not he barKeeper
// && not him/herself
// && a valid object

if ( (oCustomer != GetObjectByTag("Barman")) &&
(oCustomer != GetObjectByTag("Serveuse")) &&
GetIsObjectValid(oCustomer)
)
{
// Move to Customer and ask what he/she wants

SetLocalInt (OBJECT_SELF, "BARMAID_STATE", 1);
SetLocalObject (OBJECT_SELF, "Client",oCustomer );
SetLocalObject (OBJECT_SELF, "ChaiseClient",oNearestChair );

SetCommandable(TRUE ,oCustomer);
// Barmaid moves near Customer
}
else
{
// State is still 0 , and no Customer Found.
// Next HeartBeat will find a cusotmer *
ActionRandomWalk();
return;
}

case 1:
ActionMoveToObject(oCustomer, FALSE,2.0f);
ActionDoCommand( SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 2));

case 2:
// Ask a Rnd question: Type 1
ActionDoCommand( AssignCommand( oCustomer ,ActionDoCommand(ActionSit( oNearestChair )) ));
ActionSpeakString(usr_Rndspeak(1)) ;
// immediatly stores the answer
CustAnswer =usr_Rndspeak(2);
SetLocalString(OBJECT_SELF , "CUSTANSW" ,CustAnswer);
// ce assign command pour synchroniser la question et la reponse.

ActionDoCommand(AssignCommand( oCustomer ,SpeakString(CustAnswer)));
ActionDoCommand(SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 4));

case 4:

ActionDoCommand(ActionMoveToObject(oBar));
ActionDoCommand(SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 5));
// break;
case 5:
ActionDoCommand( ActionSpeakString( "Il me faut "+ CustAnswer));
ActionDoCommand(SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 6));
break; // Waits 6 sec for the command (the next HeartBeat)

case 6:
ActionDoCommand (ActionMoveToObject(oCustomer));
ActionDoCommand (ActionSpeakString( usr_Rndspeak(3)));
ActionPlayAnimation(ANIMATION_PLACEABLE_ACTIVATE,1.0f, 1.0f);
AssignCommand(OBJECT_SELF,SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 7));
break;

case 7: //Type 3 : deliver

// No More Busy !
AssignCommand(OBJECT_SELF,SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 0));

// customer thanks.
// Waits 10 sec & leave the chair !
AssignCommand( oCustomer ,SpeakString( usr_Rndspeak(4)));
AssignCommand( oCustomer ,ActionWait(2.0));
AssignCommand( oCustomer ,ActionMoveAwayFromObject(oNearestChair , FALSE , 3.0));
ActionMoveToLocation(lServeuse, FALSE);


break;



} // switch (state)

break; //case 1001

case 1004:
// This is just in here so you can interrupt her if you want to talk to her
SetLocalObject (OBJECT_SELF, "Client", OBJECT_INVALID);
SetLocalInt (OBJECT_SELF, "BARMAID_STATE", 0);
break;
}
}



//--------------------------------------------
// user Random Speak string !
//--------------------------------------------
string usr_Rndspeak( int type )
{
string toSpeak =";
int Rnd;
switch ( type )
{
case 1: // barmaid questions
Rnd = Random(3);
switch(Rnd)
{
case 0:
toSpeak = "Que prendrez-vous ?";
break;
case 1:
toSpeak = "Que desirez-vous ?";
break;
case 2:
toSpeak = "Et pour vous ce sera?";
break;
}
break;

case 2: // custommer answer
Rnd = Random(5); // be sure to set n to max case+1
switch(Rnd)
{
case 0:
toSpeak = "Deux bieres bien fraiches";
break;
case 1:
toSpeak = "Un verre d'Aventurion";
break;
case 2:
toSpeak = "Un cocktail maison";
break;
case 3:
toSpeak = "Un verre de tord boyaux";
break;
case 4:
toSpeak = "Une chope de biere";
break;
}
break;

case 3:
Rnd = Random(4);
switch(Rnd)
{
case 0:
toSpeak = "Tenez, voici votre commande";
break;
case 1:
toSpeak = "Et voila, c'est pret.";
break;
case 2:
toSpeak = "Aussitot commande, aussitot servi";
break;
case 3:
toSpeak = "Vous etes servi";
break;

}
break;

case 4:
Rnd = Random(3);
switch(Rnd)
{
case 0:
toSpeak = "Merci bien";
break;
case 1:
toSpeak = "Ah ! enfin";
break;
case 2:
toSpeak = "Hum... ca a l'air delicieux";
break;
}
break;
}

return toSpeak;
}


Dans le OnSpawn de la serveuse, éditer le script nw_C2_default9
et supprimez les // devant :
- SetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT);
- SetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT);

Imposant !

Par Frolo Xeres le 8/11/2002 à 14:20:16 (#2512986)

J'avoue que c'est beau et que çà anime un peu l'affaire...
M'enfin c'est quoi ta machine ? La mienne ralentie un peut quand même !

Par Tynril le 8/11/2002 à 15:45:24 (#2513640)

*Balance une tarte à la crème à SidSilver*

Le CPU de mon 486DX2 a cramé !!!

Serieusement, depuis le temps qu'on le dit, qu'on le répète et qu'on le répète encore, n'utilisez pas OnHeartBeat, OnHeartBeat c'est mal, OnHeartBeat ca bouffe le CPU, OnHeartBeat c'est pas propre...

Je veux bien qu'on aie pas convaincu Mr. Tout-le-monde et son OnHeartBeat de deux lignes qu'il a pas envie de s'emmerder à mettre ailleurs...
Mais que des gens commencent à refiler leurs usines à gaz au public en disant que c'est bien, désolé, ça me fout les nerfs.

Par Nylou le 8/11/2002 à 15:51:00 (#2513688)

Ok, le onheartbeat ça pas bien.

Cependant tu pourrais etre plus constructif et aguiller SidSilver vers une solution similaire mais moins gourmande en CPU.


Condoléances pour ton 486DX2.

Par eMRaistlin le 8/11/2002 à 16:12:43 (#2513855)

486DX, c'est dans les prerequis pour NWN ?? :mdr: :mdr:



Nan, serieux, faut aussi faire attention a une chose : un DelayCommand"ExecuteScript", ca bouffe presque autant le CPU...

Pour moi, la seule solution (et c'est dailleurs sans vergogne que je l'utilise) c'est d'utiliser le OnHeartBeat sans vergogne, mais de ne pas laisser trainer des objets dans une zone ou ils ne servent pas... Je m'explique : sur une map, j'ai par exemple un 50aine de monstre / joueurs... J'ai 40 map, mais je ne cree les habitants de la map que lorsque le nombre de PC dans l'area est > 0, et je detruit toute ma zone quand le dernier PC la quitte...

Bon, ca necessite un miniomum sur les OnEnter et les OnExi, mais du coup, j'ai des zones vivantes sans surcharge...

Perso, je ne vois que ca, vu que de toute facon, un evenement repetitif sera TOUJOURS gourmand...

Par miriandel le 8/11/2002 à 16:27:25 (#2513949)

Constructif ?

Ma foi, j'ai développé un système complet de peuplement dynamique de zones pour Althea, qui inclus nombres d'activités usuelles des PNJs, y compris la taverne.
Il ne faut même plus programmer une taverne, il suffit de placer une serveuse dans la zone et de poser à terre les waypoints de point de base de la serveuse, bar, porte d'entrée et pissotière (ben oui, s'ils boivent...).

Une seule ligne de code dans l'entrée de zones appelle une fonction générique qui définit:
1° Le nombre de PNJs d'ambiance souhaités
2° Leurs type de vêtements (robes, armures léfères ou medium)
3° Leur armement (aucun ou mêlée, ou distance)
4° Leur point de spawn
5° Leur activité (simples passants, clients de taverne, joueurs de casino, mineurs, forgerons)
6° Leur race.

Ces personnages chaque fois différents (apparence et équipement) sont créés à chaque entrée dans la zone, et sont détruits quand la zone est vide. Ils inter-agissent par user defined events. Ainsi la charge sur le serveur est toujours minimale.

Tu vois Nylou, je t'avais dit quand tu as démarré ton projet, qu'il était préférable que les développeurs se rassemblent pour créer quelque chose de grand, en communauté.
C'est pour cela que j'ai rejoint Althea, qui était un des plus anciens projets NWN.
Tu m'as très gentiment rétorqué que ce n'était pas ta vision des choses, tu comprends sans doute mieux aujourd'hui combien je le regrette.

Par SidSilver le 8/11/2002 à 16:30:18 (#2513974)

Je suppose que tu n'as pas le droit de partager ton script...
Parcequ'a vrai dire c'est vrai qu'il a l'air tres intéressant

Par miriandel le 8/11/2002 à 16:32:24 (#2513989)

Provient du message de eMRaistlin
un DelayCommand"ExecuteScript", ca bouffe presque autant le CPU...

Pour moi, la seule solution (et c'est dailleurs sans vergogne que je l'utilise) c'est d'utiliser le OnHeartBeat sans vergogne


Il n'existe qu'une seule façon de créer des événements dynamiques, indépendants les uns des autres, qui s'enchaînent quelle que soit la durée des événements qui les précèdent et qui produisent leur effet au moment exact où on le souhaite: les user defined events, tels que recommandés par les concepteurs mêmes d'Aurora.

Par SidSilver le 8/11/2002 à 16:33:22 (#2514000)

le problème c'est que c'est beaucoup plus dur à utiliser

Par eMRaistlin le 8/11/2002 à 16:42:09 (#2514082)

Tu m'as très gentiment rétorqué que ce n'était pas ta vision des choses, tu comprends sans doute mieux aujourd'hui combien je le regrette.


Euh... etant le codeur principal de NWNRO, je me permettrait de confirmer ce qui avait été dit a l'époque... ^^

Mais ton point de vue est tout a fait valable, même si j'en regrette 2 choses :
- C'est dommage de se restreindre.
- C'est vraiment dommage de se restreindre.

Enfin, C'est sur que c'est important de coder a plusieur, mais c'est encore plus important d'avoir des scripts personnalisés...
Nous voici donc avec le même resultat, que ce soit sur NWNRo et sur Althea (puisque apres tout, C'est la solution la plus viable, il etait ineluctable qu'on arrive a la même conclusion...).

En fait, je pense que le Soucis de Nylou, c'etait plutot de faire remarquer a tyn' que sa remarque manquait juste d'une chose pour etre parfaite : une solution pour Sidsylver... Car une remarque sans proposition est pure vanité...
(je signale toutefois que je ne pense pas que tyn' ait quoique se soit a prouver ^^)

Par miriandel le 8/11/2002 à 16:45:24 (#2514110)

Provient du message de SidSilver
Je suppose que tu n'as pas le droit de partager ton script...
Parcequ'a vrai dire c'est vrai qu'il a l'air tres intéressant


Pour être complet autant que clair, d'où ma réponse à Nylou la zilleuse de grigris, je parcours régulièrement ces forums à la recherche de talents qui ne soient déjà impliqués dans un projet.

J'ai même essayé de rassembler les deux projets qui se basent sur T4C, et aurais pu réussir si j'avais pu intervenir avant que n'éclatent des querelles qui ont sabordé l'idée.
Et pourtant, je n'ai pas joué à t4c, c'est dire à quel point la fusion des énergies prime à mon sens sur les visions personnelles.

Si je diffuse mes librairies (on ne peut plus parler de simples scripts quand il s'agit de systèmes complets d'artisanat ou de peuplement), que se passera-t-il ?
Ils seront utilisés par nombre de projets différents, ce qui est le contraire de ce que je recherche.
Je crains que l'épouvantable complexité de mise en oeuvre de mondes persistants ne condamne à l'échec la plupart des projets entamés.
Ce qui me désole, et me fait très peur pour l'avenir de ce jeu fabuleux.

Donc, si quelqu'un maîtrise Aurora et veut suer sang et eau pour donner corps aux rêves des joueurs, la porte est grande ouverte.

Par eMRaistlin le 8/11/2002 à 16:45:53 (#2514115)

Il n'existe qu'une seule façon de créer des événements dynamiques, indépendants les uns des autres, qui s'enchaînent quelle que soit la durée des événements qui les précèdent et qui produisent leur effet au moment exact où on le souhaite: les user defined events, tels que recommandés par les concepteurs mêmes d'Aurora.


Euh... c'est plus ou moins le même combat : a partir du moment ou tu as un evenement qui verifie a heure fixe qu'un appel est fait ou non a l'userDefined, ca va bouffer ton CPU, même si ca bouffe moins que le HeartBeat... De toute facon, c'est comme le SetListeningPattern : quoi qu'il advienne, même si c'est une commande OnSpawn, il continue a la gerer tout au long de la vie de ton NPC, donc ca bouffe du CPU...

UNE ACTION QUI EST VERIFIEE EN CONTINUE EST GOURMANDE.

C'est un axiome de base. Il faut essayer de contourner le pb, et il faut reduire un maximum ce genre d'opération. Le userDefinied, c'est chouette, mais si tu l'appele tout les 6 secondes pour verifier un truc, c'est comme le HeartBeat...

Par SidSilver le 8/11/2002 à 16:51:58 (#2514177)

miriandel,
désolé de te décevoir mais j'ai vraiment envi de me construire un monde personnel (pas persistant faut pas abuser) et donc seul autant que faire se peut.
Autrement dit, a part pour les scripts ou des fois j'ai un peu besoin d'aide, je tiens a faire mon module seul.
Je rejoins donc l'avis de Nylou (qui est une excellente compagnone de jeu online... je tiens à préciser).

Par miriandel le 8/11/2002 à 17:01:59 (#2514280)

Ce n'est pas du tout la même chose Raist.

Par exemple, quand tu as un client au fond de la taverne qui doit aller satisfaire un besoin urgent, il devra parcourir un chemin plus long pour aller ouvrir la porte des pissotières, et donc mettra plus de temps, qu'un client assis à côté de cette porte.

L'utilisation des user defined events permet d'enchaîner un événement quand celui qui le conditionne est terminé.

On est très loin du déclenchement d'actions à moments fixes.

Les user events ne consomment aucun cycle CPU supperflu, puisqu'ils s'enclenchent quand ils sont appelés par d'autres events.

De plus, cette technique permet de "croiser" des événéments, dans la mesure où les events sont totalement indépendants les uns des autres, et des activités différentes peuvent ainsi coexister sur un même personnage, sans crainte d'entre en conflit entre elles puisque chaque activité est gérée indépendamment des autres.

Provient du message de eMRaistlin
Nous voici donc avec le même resultat, que ce soit sur NWNRo et sur Althea (puisque apres tout, C'est la solution la plus viable, il etait ineluctable qu'on arrive a la même conclusion...).


Je serais surpris que nous arrivions aux mêmes résultats en utilisant des techniques aussi radicalement différentes.

Par eMRaistlin le 8/11/2002 à 17:08:39 (#2514355)

Par exemple, quand tu as un client au fond de la taverne qui doit aller satisfaire un besoin urgent, il devra parcourir un chemin plus long pour aller ouvrir la porte des pissotières, et donc mettra plus de temps, qu'un client assis à côté de cette porte.


C'est une remarque tres pertinente...

Ce n'est pas du tout la même chose Raist.


Ca par contre ^^... nan, ce n'est evidement pas ce que j'ai dit... je me suis mal exprimé... je voulais juste dire que de toute facon, le UserDefined declenche l'emission d'un message qui va etre entendu par le module ou l'area, et qui va repondre par un evenment.

Mais ceci va de toutefacon concommer du CPU, vu que tu utilise une "fonctionnalité" de NWN qui ecoute en permanence ces UD. Cette ecoute bouffe du CPU, tout comme le heartBeat. Il ne faut pas forcement etre contre un OnHeart qui soit bien fait, mais plutot faire tout ce qui est possible pour gerer ce qui est gérable d'une autre facon... Mais bon, si tu rajoute un test de Variable simple sur un HeartBeat d'un NPC qui a deja un HeartBeat avec WalkWaypoint, tu ne tuera pas vraiment le CPU. Si c'est ce test qui envoi un UD, alors tu verra que le CPU ne payera pas tant que ca...

Je dis juste qu'avant toute chose, et avant même la facon de gerer un evenement, il faut surtout reflechir a en optimiser la gestion et utiliser les meilleurs outils au meilleur endroit... ^^

Par miriandel le 8/11/2002 à 17:35:04 (#2514594)

Provient du message de eMRaistlin
... toute facon, le UserDefined declenche l'emission d'un message qui va etre entendu par le module ou l'area, et qui va repondre par un evenment... ceci va de toutes façons concommer du CPU, vu que tu utilises une "fonctionnalité" de NWN qui ecoute en permanence ces UD.


Absolument pas.
Les user events sont générés par SignalEvent(récipiendaire, event).
Une et une seule creature/objet (le récipiendaire) aura conscience de cet événement (event).

Les user events ont l'incommensurable avantage de se placer au bon endroit dans la pile d'évenements, et donc de se résoudre au moment opportun, par opposition à une onheartbeat qui s'exécute à intervalle régulier (donc trop tôt ou trop tard mais jamais au bon moment) que la créature/objet ait quelque chose à faire ou pas.

C'est le génie du moteur d'Aurora d'avoir créé une pile événementielle.

Par Tynril le 8/11/2002 à 18:07:09 (#2514837)

Provient du message de eMRaistlin
En fait, je pense que le Soucis de Nylou, c'etait plutot de faire remarquer a tyn' que sa remarque manquait juste d'une chose pour etre parfaite : une solution pour Sidsylver... Car une remarque sans proposition est pure vanité...
(je signale toutefois que je ne pense pas que tyn' ait quoique se soit a prouver ^^)
:sanglote: :sanglote: :sanglote: :sanglote: :sanglote:


Quelqu'un peut compter combien de foi on (je, aussi) a conseillé les UserDefinedEvent à la place ?!

*Craque*

:sanglote: :sanglote:

Par eMRaistlin le 8/11/2002 à 19:46:02 (#2515582)

3156224321236246325,6 fois non ?

Par LeProctophantasmiste le 8/11/2002 à 19:47:21 (#2515593)

A priori j'étais plutôt de l'avis de Miriandel , avant même que le jeu ne sorte à vrai dire :D. Je ne sais pas si vous fréquentiez NWVault à l'époque de la conception virtuelle de "Fred", mais j'avais tenté, pendant un bref moment, de faire entendre une voix contre le OnHeartbeat, et en faveur de SignalEvent, je n'avais pas été très écouté :D, David Gaider m'ayant fait remarqué que Bio utilisait le OnHeartbeat pour construire des routines et que cela marchait très bien, pour être honnête ce que je proposais n'était pas très brillant non plus.

A mon humble avis, il y a des problèmes de perfs des deux côtés. Même si ce que tu dis est exact Miriandel, à savoir que les UserDefinedEvent permettent de lancer les scripts au moment opportun uniquement, il n'en reste pas moins qu'en augmentant la complexité tu peux te retrouver avec autant, voir plus d'appel de scripts qu'avec un heartbeat par créature, et plus tu fractionnes ton code en UD différents plus cela est vrai. Le problème c'est que si tu ne le fractionnes pas, l'appel des Uds est plus lourd, compte tenu de la façon dont marchent ActionDoCommand et DelayCommand (copie des variables vivantes) et tu te retrouves avec des interruptions à gérer et des ClearAllActions qui correspondent à une perte d'efficassité du code. M'enfin ce que j'en dis... Evidamment tout cela n'est valable que si tu fais ce que propose eMRaistlin à savoir détruire les objets porteurs de heartbeat quand, par exemple, la zone est inactive.

Autant je suis d'accord pour dire qu'il ne faut pas abuser du OnHeartbeat, autant la même chose est vraie des solutions de rechange. Construire un timer avec des DelayCommand par exemple, cela me semble absurde (cf l'histoire des variables) et très peu fiable. Pour ce type de trucs on pourrait faire un objet avec un OnHeartBeat, et des "silent shouts" pour donner l'heure aux différents objets sensés réagir, porteur de patterns de type "HEURE_4" ou bien "HEURE_*n".

Pour un module persistant je ne sais pas trop quoi penser à vrai dire, or c'est essentiellement là qu'est l'enjeux pour la charge serveur, plus le module est gros, plus il y a de joueurs connectés, plus ça devient le bordel, plus il y aura de scripts actifs, et même créer et détruire des objets ça n'est pas sans impact non plus, étant donné l'importance qu'ils ont dans nwscript notamment. Si tu as 40 joueurs (improbable certes) les changements de zone peuvent devenir fréquents.

Les SetListeningPattern, pour certaines choses, c'est la meilleure méthode à mon avis. Quand tu veux faire réagir un ensemble d'objet à un événement (de la même façon ou non), c'est plus économique à la fois qu'un onHeartBeat vérifiant une variable globale, ou que de parcourir une liste d'objet pour leur signaler un événement à chacun.

Par miriandel le 8/11/2002 à 20:59:07 (#2516164)

C'est assez curieux, car c'est David lui-même qui, en juillet, a posté un message essentiel à ce sujet.
David avait demandé à je sais plus qui (c'est pas Derek ?) comment faire pour reproduire des effets très spéciaux, justement le genre de truc qui ne peut être fait proprement avec une ohb.
C'est à ce moment qu'il avait expliqué que seuls les udf étaient à même de résoudre les problèmes les plus complexes, car le moteur du jeu est entirèrement basé sur la gestion de la pile événementielle des objets, et que je m'étais dis "merde, va falloir creuser sec pour piger leur brol". Il faut dire qu'au release, on avait que dalle comme docs !

Pour revenir à ton post, mes udf n'appellent jamais de scripts, nulle part dans mes travaux je n'utilise des ExecuteScript. Je ne pige d'ailleurs pas pourquoi certains s'obstinent à utiliser Executescript qui ralentit tout le truc et rend le code illisible, mais soit.

Les routines les plus complexes que j'ai écrites (notamment une zone de fin de campagne avec plus de 100 udf qui tournent dessus) tiennent sur les méthodes udf de la zone et des créatures qui la peuplent, qui n'apparâissent souvent qu'à des moments déterminés d'ailleurs.

Je n'utilise pas non plus ActionDoCommand, je ne l'ai jamais utilisé une seule fois pour dire vrai.
Pas davantage que des ClearAllActions, sauf dans les rares cas où je dois faire se lever un PNJ de sa chaise (bug NWN) ou arrêter un combat.

J'utilise exclusivement des SignalEvent et des DelayCommand, ce qui signifie que je place des événements dans la pile, sans copier quoi que ce soit comme variables.
Je prévois le comportement de mes créatures et je le code comme une intelligence artificielle: chaque udf est un comportement déclenché par un stimulus.

Tout le système de NWN utilise la pile événementielle.
Ce que fait un ohb, c'est s'envoyer un

DelayCommand(6.0f, SignalEvent(OBJECT_SELF, EventUserDefined(1006)));

Donc, la ohb est un udf, mais simplifié pour le rendre accessible à tous.
Avec une ohb, tu ne peux contrôler avec précision le déroulement des events, tu ne peux créer d'interactions entre les événements sans créer des tonnes de variables, et tu ne peux retarder un événement de plusieurs heures sans toutes les 6 secondes tester une variable inutile.

J'ajoute que dans les gros modules, la ohb ne se produit plus toutes les 6 secondes. Le patapouf qu'Archgeo (c plus son nom, celui qui a pondu l'Horreur Cataclysmique Confondante) va mettre en persistant, avec ses 60 Mo, exécute ses ohb toutes les 12 secondes !

Donc, sauf erreur de ma part, les udf ne sont pas une solution de rechange, mais le coeur technique de la gestion du temps d'Aurora.

Je soupçonne le code d'Aurora de fonctionner comme un moteur de base de données.
Les événements sont des records, et à chaque INSERT, l'index des événements (ici le temps d'exécution souhaité) est recalculé.
A intervalles fixes (c'est très rapide, quelques millisecondes par objet actif), cet index est parcouru, et tous les événements qui correspondent au temps courant sont exécutés.
Dès qu'un événement est rencontré qui ne doit pas être excécuté immédiatement, on recommence à scanner du dessus de la pile.

Si mon analyse est exacte, cela signifie que les udf sont non seulement la manière la plus propre, mais aussi la plus efficace et la plus précise de contrôler le déroulement des actions.

Si mon analyse est erronée, eh ben j'ai tout faux :bouffon:

Par eMRaistlin le 8/11/2002 à 22:09:30 (#2516581)

C'est une analyse interressante...

Je vais essayer de tester...

Par LeProctophantasmiste le 8/11/2002 à 22:17:52 (#2516632)


C'est assez curieux, car c'est David lui-même qui, en juillet, a posté un message essentiel à ce sujet.


Oui, je crois qu'il a changé d'avis depuis :D, la discution sur FRED a commencé en avril dernier, au moment ou Bio a laché quelques infos précises sur NWScript. Le projet était carrément stérile puisqu'on ne connaissait pas la moitié des fonctions essentielles, David nous renseignait petit à petit, et testait (le pauvre :D) ce qui en sortait, mais c'était un bon moyen de se préparer du point de vue théorique :), personnellement j'ai suivi cela d'assez loin mais je m'étais entiché des UDFs.


J'utilise exclusivement des SignalEvent et des DelayCommand, ce qui signifie que je place des événements dans la pile, sans copier quoi que ce soit comme variables.


Là tu fais erreur, toutes les variables actives vont être copiées pour être rendues accessibles dans le DelayCommand.


int nvar;
....
DelayCommand(SignalEvent(...));


et

{
int nvar;
....
}
DelayCommand(SignalEvent(...));


Ce n'est pas la même chose, quelque soit le code exécuté par le DelayCommand, on paie ici le prix de la puissance de cette instruction comparée aux équivalents dans d'autres langages. J'ai failli poster à ce propos, mais comme c'est absolument indémontrable du côté utilisateur, je me suis retenu :D. Mais il se trouve que je suis tombé sur un post de Torlack confirmant le problème, et comme il a écrit un compilateur pour NWScript j'ai tendance à le croire :).


J'ajoute que dans les gros modules, la ohb ne se produit plus toutes les 6 secondes. Le patapouf qu'Archgeo (c plus son nom, celui qui a pondu l'Horreur Cataclysmique Confondante) va mettre en persistant, avec ses 60 Mo, exécute ses ohb toutes les 12 secondes !

Je sais bien, j'ai reproduis cette citation dans une réponse à l'un de tes messages :D.

A mon avis entre UDF et ExecuteScript il n'y a pas tellement de différence, remarquons qu'il serait facile (et profondément idiot) de reproduire le système d'UDF via des ExecuteScript, l'inverse est mons vrai. Les SignalEvent me semblent n'être qu'un simple moyen de désigner un script, via un pointeur associé aux objets, éventuellement un peu plus efficasse que ExecuteScript, mais je ne vois pas en quoi cela serait plus intimement lié à la pile d'événements. Pour moi le principal intérêt c'est la structure de script que cela suggère, qui est bien meilleur que le foullis qu'entrainent les ExecuteScript.

DelayCommand(6.0f, SignalEvent(OBJECT_SELF, EventUserDefined(1006)));

Tu exagères! La mauvaise foi cela me connaît , mais la c'est un peu gros :D.

Tu aurais écrit:

DelayCommand(6.0f,SignalEvent(OBJECT_SELF, EventOnHeartbeat());

J'aurais été d'accord sur le principe ;), c'est d'ailleurs véritablement comme ça que le OnItemActivated et le SpellCastAt sont implémentés (via les nw_s*). Mais comme c'est Bioware qui l'a fait, il n'y a pas de variable vivante au moment du DelayCommand.

Edit: phrase mozaïque

Par eMRaistlin le 8/11/2002 à 22:25:49 (#2516687)

Afin de clore ce HS (on pourrait plutot en faire un topic, non ?)

Je suppose que tu n'as pas le droit de partager ton script...


C'est INUTILE : ne demandez pas des grosses machines toutes faite : Mirandiel a fait ses script pour lui, tout comme les miens sont adaptés a ce que je fais de mes modules (même s'ils sont loin d'etre parfait). L'avantage : je les maitrise parfaitement, comme Mirandiel les siens... Et je le prouve :

OnEnterArea :

//Script d'area_enter qui cree des monstres
#include "ro_createmonster"

void main()
{
//Declaration des variables
object oPCentering = GetEnteringObject();
object oAreaEntered = OBJECT_SELF;
int nNBdePC = GetLocalInt(oAreaEntered,"nNBdePC");

//Incrementation du compteur de PC dans l'area
if (GetIsPC(oPCentering))
{
nNBdePC = nNBdePC+1;
SetLocalInt(oAreaEntered, "nNBdePC",nNBdePC);


//Si c'est le premier PC a entrer, generation des monstres en fonction de l'area
if (nNBdePC==1)
{
CreateMonsters(oAreaEntered);
}
else
{
WriteTimestampedLogEntry(GetName(oPCentering)+GetTag(oAreaEntered)+"enter correct");
}
//LOG~DEBUG
}
}


OnExit :
void main()
{
//DECLARATION DES VARIABLES
object oPCexiting=GetExitingObject();
object oAreaLeaved = OBJECT_SELF;
object oObjetaDetruire = GetFirstObjectInArea(oAreaLeaved);
object oFirstPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
int nNBdePC = GetLocalInt(oAreaLeaved,"nNBdePC");

//Mise a jour du Compteur
if (GetIsPC(oPCexiting))
{
nNBdePC = nNBdePC-1;
SetLocalInt(oAreaLeaved,"nNBdePC",nNBdePC);
}

//TEST POUR VERIFIER SI IL RESTE DES PC DANS L'Area

if (nNBdePC == 0)
{

//BOUCLE DE DESTRUCTION DES CREATURES/ITEM
while (GetIsObjectValid(oObjetaDetruire))
{
if (
(GetObjectType(oObjetaDetruire)== OBJECT_TYPE_CREATURE)||
(GetObjectType(oObjetaDetruire)== OBJECT_TYPE_ITEM)
)
{
if (!GetPlotFlag(oObjetaDetruire))
{
if (GetName(GetMaster(oObjetaDetruire))==")
DestroyObject (oObjetaDetruire);
}
}
oObjetaDetruire=GetNextObjectInArea();
}
}

//LOG~DEBUG
}


#include CreateMonster
#include "ro_listmonster"
#include "ro_list_area"

void CreateMonsters(object oArea)
{
//Declaration des variables
int nRandom ;
int nRandomLoc ;
string sAreaName = GetTag(oArea);
location lRespawnLoc;
int nNBMonstres = 50;
int nNumeroArea=0;
string sMonstre;
string sRandLoc;

//affectation d'un numero d'area
nNumeroArea = GetAreaNumber(oArea);

//Boucle de creation du monstre

while (nNBMonstres > 1)
{
nRandom = Random(100)+1;
nRandomLoc = Random(40)+1;
sRandLoc = IntToString(nRandomLoc);
if (nRandomLoc < 10)
{
sRandLoc = "0"+sRandLoc;
}

// Determination de la location de creation du monstre
lRespawnLoc = GetLocation(GetObjectByTag("RSPWN_"+IntToString(nNumeroArea)+"_"+sRandLoc));

//Appel du monstre en fonction de l'Area
sMonstre = GetMonsterByArea(nNumeroArea,nRandom);
CreateObject(OBJECT_TYPE_CREATURE,sMonstre,lRespawnLoc,FALSE);
nNBMonstres = nNBMonstres-1;
}
//DEBUG
//}void main(){
}


#include listArea

int GetAreaNumber(object oArea)
{
int nNumero;
string sAreaName = GetTag(oArea);

nNumero = StringToInt(GetStringRight(sAreaName,3));
return nNumero;
//DEBUG
SendMessageToPC(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC), sAreaName);
}


#include listmonster

string GetMonsterByArea(int nAreaNB=0, int nPourcent=0)
{
switch (nAreaNB)
{
case 1: //Area = Abysses 1
if ((0<=nPourcent)&&(nPourcent<=30))
{
return "sladred001";
}
if ((31<=nPourcent)&&(nPourcent<=50))
{
return "deathporing";
}
if ((51<=nPourcent)&&(nPourcent<=60))
{
return "greenslaad";
}
if ((61<=nPourcent)&&(nPourcent<=70))
{
return "grayslaad";
}
if ((71<=nPourcent)&&(nPourcent<=80))
{
return "blueslaad";
}
if ((81<=nPourcent)&&(nPourcent<=86))
{
return "deathslaad";
}
if ((87<=nPourcent)&&(nPourcent<=94))
{
return "slaaddthboss";
}
if ((95<=nPourcent)&&(nPourcent<=100))
{
return "grayslaadlord";
}

case 2: //Area =ElementalPlane Fire/Air
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "elderairelemen";
}
if ((41<=nPourcent)&&(nPourcent<=80))
{
return "elderfireeleme";
}
if ((81<=nPourcent)&&(nPourcent<=90))
{
return "skyporing";
}
if ((91<=nPourcent)&&(nPourcent<=100))
{
return "drops";
}

case 3: //Area = ElementalPlane Water/Earth
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "elederwaterelem";
}
if ((41<=nPourcent)&&(nPourcent<=80))
{
return "elderearthelem";
}
if ((81<=nPourcent)&&(nPourcent<=90))
{
return "heartporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "waterporing";
}

case 4: //Area = Abysses Infernales
if ((0<=nPourcent)&&(nPourcent<=15))
{
return "deathslaad";
}
if ((16<=nPourcent)&&(nPourcent<=30))
{
return "grayslaad";
}
if ((31<=nPourcent)&&(nPourcent<=40))
{
return "blueslaad";
}
if ((41<=nPourcent)&&(nPourcent<=60))
{
return "slaaddthboss";
}
if ((61<=nPourcent)&&(nPourcent<=80))
{
return "grayslaadlord";
}
if ((81<=nPourcent)&&(nPourcent<=90))
{
return "deathporing";
}
if ((91<=nPourcent)&&(nPourcent<=100))
{
return "magicalporing";
}



case 11: //Area = Culvert 1
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "thiefbug";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "familiar";
}
if ((76<=nPourcent)&&(nPourcent<=85))
{
return "acidporing";
}
if ((86<=nPourcent)&&(nPourcent<=100))
{
return "spore";
}

case 12: //Area = Culvert 2
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "familiar";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "thiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "spore";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "acidporing";
}

case 13: //Area = Culvert 3
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "thiefbug";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "spore";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "spidergiant";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "acidporing";
}

case 14: //Area = Culvert 4
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "thiefbug";
}
if ((41<=nPourcent)&&(nPourcent<=50))
{
return "spore";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "greenthiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "familiar";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "acidporing";
}

case 21: //Area = Desert Y
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "wolf";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "drops";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "lunatik";
}
if ((96<=nPourcent)&&(nPourcent<=98))
{
return "poring";
}
if ((99<=nPourcent)&&(nPourcent<=100))
{
return "minotaur";
}

case 22: //Area = Water Desert
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "wolf";
}
if ((51<=nPourcent)&&(nPourcent<=65))
{
return "poring";
}
if ((66<=nPourcent)&&(nPourcent<=75))
{
return "spore";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "waterporing";
}
if ((96<=nPourcent)&&(nPourcent<=98))
{
return "lunatik";
}
if ((99<=nPourcent)&&(nPourcent<=100))
{
return "desertwolf";
}

case 23: //Area = Metaller
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "metaller";
}
if ((51<=nPourcent)&&(nPourcent<=60))
{
return "wolf";
}
if ((61<=nPourcent)&&(nPourcent<=80))
{
return "drops";
}
if ((81<=nPourcent)&&(nPourcent<=100))
{
return "scorpion";
}

case 24: //Area = Golem
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "golem";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "heartporing";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "golem";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "wolf";
}

case 31: //Area = Geffen Donjon 1
if ((0<=nPourcent)&&(nPourcent<=20))
{
return "skeleton";
}
if ((21<=nPourcent)&&(nPourcent<=60))
{
return "poisonspore";
}
if ((61<=nPourcent)&&(nPourcent<=75))
{
return "familiar";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "golem";
}

case 32: //Area = Geffen Donjon 2
if ((0<=nPourcent)&&(nPourcent<=16))
{
return "skeletonwarrior";
}
if ((17<=nPourcent)&&(nPourcent<=35))
{
return "skeletonarcher";
}
if ((36<=nPourcent)&&(nPourcent<=40))
{
return "poporing";
}
if ((41<=nPourcent)&&(nPourcent<=45))
{
return "poisonspore";
}
if ((46<=nPourcent)&&(nPourcent<=55))
{
return "magicalporing";
}
if ((56<=nPourcent)&&(nPourcent<=60))
{
return "slaadred001";
}
if ((61<=nPourcent)&&(nPourcent<=65))
{
return "blueslaad";
}
if ((66<=nPourcent)&&(nPourcent<=80))
{
return "nightmare";
}
if ((81<=nPourcent)&&(nPourcent<=100))
{
return "pumpkin";
}

case 41: //Area =Payon Cave 1
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "skeleton";
}
if ((41<=nPourcent)&&(nPourcent<=60))
{
return "spore";
}
if ((61<=nPourcent)&&(nPourcent<=75))
{
return "zombie";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "familiar";
}

case 42: //Area = Payon Cave 2
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "skeletonwarrior";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "skeletonarcher";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "zombie";
}

case 43: //Area = Payon Cave 3
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "munak";
}
if ((41<=nPourcent)&&(nPourcent<=50))
{
return "hydra";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "skeletonwarrior";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "skeletonarcher";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "grayporing";
}

case 51: //Area = Pyramid 1
if ((0<=nPourcent)&&(nPourcent<=30))
{
return "poring";
}
if ((31<=nPourcent)&&(nPourcent<=50))
{
return "spore";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "thiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "familiar";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "poporing";
}

case 52: //Area = Pyramid 2
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "mummy";
}
if ((41<=nPourcent)&&(nPourcent<=60))
{
return "isis";
}
if ((61<=nPourcent)&&(nPourcent<=80))
{
return "familiar";
}
if ((81<=nPourcent)&&(nPourcent<=90))
{
return "poporing";
}
if ((91<=nPourcent)&&(nPourcent<=95))
{
return "skeletonarcher";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "skeletonwarrior";
}

case 53: //Area = Pyramid 3
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "mummy";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "skeletonwarrior";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "skeletonarcher";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "poporing";
}

case 54: //Area = Pyramid 4
if ((0<=nPourcent)&&(nPourcent<=25))
{
return "skeletonwarrior";
}
if ((26<=nPourcent)&&(nPourcent<=50))
{
return "isis";
}
if ((51<=nPourcent)&&(nPourcent<=70))
{
return "mummy";
}
if ((71<=nPourcent)&&(nPourcent<=90))
{
return "skeletonarcher";
}
if ((91<=nPourcent)&&(nPourcent<=100))
{
return "poporing";
}

case 61: //Area = SouthProntera
if ((0<=nPourcent)&&(nPourcent<=45))
{
return "poring";
}
if ((46<=nPourcent)&&(nPourcent<=60))
{
return "lunatik";
}
if ((61<=nPourcent)&&(nPourcent<=75))
{
return "spore";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "thiefbug";
}
if ((96<=nPourcent)&&(nPourcent<=98))
{
return "drops";
}
if ((99<=nPourcent)&&(nPourcent<=100))
{
return "mastering";
}

case 62: //Area = PayonForest
if ((0<=nPourcent)&&(nPourcent<=30))
{
return "bigfoot";
}
if ((31<=nPourcent)&&(nPourcent<=50))
{
return "spore";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "poporing";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "wolf";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "horrong";
}

case 63: //Area = Payon field
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "poring";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "lunatik";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "skyporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "spore";
}

case 64: //Area = Morroc field
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "poring";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "thiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "heartporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "savage";
}

case 65: //Area = GeffenField
if ((0<=nPourcent)&&(nPourcent<=80))
{
return "poring";
}
if ((90<=nPourcent)&&(nPourcent<=100))
{
return "thiefbug";
}

case 66: //Area = geffenField 2
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "poring";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "spidergiant";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "thiefbug";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "spore";
}

case 71: //Area = Goblin field
if ((0<=nPourcent)&&(nPourcent<=15))
{
return "goblinbrother1";
}
if ((16<=nPourcent)&&(nPourcent<=35))
{
return "goblinbrother2";
}
if ((36<=nPourcent)&&(nPourcent<=50))
{
return "goblinbrother3";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "goblinbrother4";
}
if ((76<=nPourcent)&&(nPourcent<=90))
{
return "goblinbrother5";
}
if ((91<=nPourcent)&&(nPourcent<=95))
{
return "orcbarbarian";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "goblinworgrider";
}

case 72: //Area = OrcLand
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "orcbarbarian";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "orcarcher";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "poisonspore";
}

case 73: //Area = OrcVillage
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "orcbarbarian";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "orcarcher";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "goblinworgrider";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "poisonspore";
}

case 84: //Area = Rocker Field
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "poring";
}
if ((41<=nPourcent)&&(nPourcent<=65))
{
return "rocker";
}
if ((66<=nPourcent)&&(nPourcent<=85))
{
return "thiefbug";
}
if ((86<=nPourcent)&&(nPourcent<=100))
{
return "spore";
}

case 81: //Area = WestPront
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "poring";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "thiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "lunatik";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "rocker";
}

case 82: //Area = West West
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "rocker";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "thiefbug";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "horrong";
}

case 83: //Area = Hornet field
if ((0<=nPourcent)&&(nPourcent<=50))
{
return "dryad";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "poporing";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "waterporing";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "spore";
}

case 101: //Area = Poring Land
if ((0<=nPourcent)&&(nPourcent<=40))
{
return "poring";
}
if ((41<=nPourcent)&&(nPourcent<=50))
{
return "drops";
}
if ((51<=nPourcent)&&(nPourcent<=60))
{
return "waterporing";
}
if ((61<=nPourcent)&&(nPourcent<=70))
{
return "heartporing";
}
if ((71<=nPourcent)&&(nPourcent<=80))
{
return "skyporing";
}
if ((81<=nPourcent)&&(nPourcent<=95))
{
return "poporing";
}
if ((96<=nPourcent)&&(nPourcent<=98))
{
return "mastering";
}
if ((99<=nPourcent)&&(nPourcent<=100))
{
return "angelring";
}

case 112: //Area = DeathRow
if ((0<=nPourcent)&&(nPourcent<=25))
{
return "orcbarbarian";
}
if ((26<=nPourcent)&&(nPourcent<=50))
{
return "poisonspore";
}
if ((51<=nPourcent)&&(nPourcent<=75))
{
return "horrong";
}
if ((76<=nPourcent)&&(nPourcent<=100))
{
return "deathporing";
}
case 113: //Area = Zone Of the Enders
if ((0<=nPourcent)&&(nPourcent<=20))
{
return "isis";
}
if ((26<=nPourcent)&&(nPourcent<=45))
{
return "poisonspore";
}
if ((51<=nPourcent)&&(nPourcent<=70))
{
return "mummy";
}
if ((76<=nPourcent)&&(nPourcent<=95))
{
return "munak";
}
if ((21<=nPourcent)&&(nPourcent<=25))
{
return "pseudodragon";
}
if ((71<=nPourcent)&&(nPourcent<=75))
{
return "scarecrow";
}
if ((46<=nPourcent)&&(nPourcent<=50))
{
return "nightmare";
}
if ((96<=nPourcent)&&(nPourcent<=100))
{
return "poring";
}
}

//Area = Non-trouve ou ville
return "invisibleporing";
//DEBUG
//}void main(){
}


Voila... c'est quasi illisible est inadaptable hors-module...

M'enfin, puisque tu l'a demandé !!

Nan serieux :

DEMANDEZ DE L'AIDE : NE DEMANDEZ PAS DU PREMACHE !!!

JOL Archives 1.0.1
@ JOL / JeuxOnLine