Principe
Vous l’aurez certainement remarqué; certaines entités (puces/batiments) du projet Yota sont controlées par le moteur du jeu de manière autonome. Cette intelligence artificielle fonctionne à base de scripts, codés dans un langage propre au jeu et dédié à cet usage.
Syntaxe
- Section/procédure (#) :
Symbolise un nom de section. Une section est un bloc d’expression pouvant être executé et réferencé par ce nom.
SECTION #Section [expressions] ... END
Execute(#Section)
- Variable locale ($)
Les variables permettent de stocker localement (voir sémantique) des valeurs dans le contexte du script.
Echo($v)
Set($v,123)
IF $v>0 THEN ... END
- Fonction (@) et procédure ([aucun prefixe])
Appels aux fonctions natives du langage. Lorsque le nom de la fonction est prefixé, la valeur de retour est stockée dans la pile d’appel et peut être récupérée. Liste non-exhaustive des fonctions natives :
Stop, Execute, Echo, Set, SetVolatile, Store, Restore, Now, GetVolatile, MyPR, MyPE, GetRandomReachablePos, GetPos, MyPEMax, NearestEnemyPlayer, HaveWeaponReady, AvailWeapon, MoveToPlayer, Attack, MoveToPoint, IsBot
Echo("toto") Attack($id) Set($variable,@Now()) IF @MyPR() > 75 THEN Echo("j'ai pas peur") END
Sémantique
La sémantique de ce langage se veut simple, de manière à inciter les joueurs les plus motivés à participer à l’amélioration et au développement des scripts. C’est un langage de type procédural, avec les spécificités suivantes :
- Tout le code dans des sections, ormis les constantes (DEFINE)
- Il est possible d’implementer autant de sections que nécéssaire
... [ en cours de rédaction ]
- Pas de boucle (for,while,do, etc ...) pour plusieurs raisons :
- Le point d’entrée du script est executé à intervalles réguliers
- Les scripts ne doivent pas consommer trop de temps processeur
- La recursivité permet (dans certaines mesures) le developpement d’algorithmes suffisament complexes pour une prise de décision de l’entité contrôlée.
- Branchements conditionnels pondérés. Ceux-ci permettent l’inclusion de facteurs aléatoires de manière simple, claire, et securisée (difficile à faire bugger :p).
Voici la syntaxe des branchements conditionnels :
IF [condition] THEN WEIGHT [poids p1] [expression a1] WEIGHT [poids p2] [expression a2] ... ELSE WEIGHT [poids q1] [expression b1] WEIGHT [poids q2] [expression b2] ... END
Si la condition est remplie, la probabilité que l’expression (ou bloc d’expression) a1 soit executée sera : prob.a1 = p1/Σ(p,1 à n)
Ainsi, si la somme des poids vaut 100, les poids corresponderont au pourcentage de chance d’execution pour chaque bloc d’expression correspondant.
Si le mot-clef WEIGHT est omis, le bloc d’expressions suivant aura 100% de chance de s’executer si la condition est vraie.
- Differents types de variables :
- Variables locales (notées $nom_de_la_variable)
- Durée de vie d’une execution du script
- Accès en lecture/ecriture aisés :
Set($variable,123) Set($variable,$variable+2) Echo($variable) Set($variable,@fonction()) Echo($variable)
- Variables persistantes (identifiées par une chaine de caractères “nom_de_la_variable”)
- Durée de vie illimitée
- Accès en lecture/ecriture via ces 2 méthodes :
Store("variable",1) ... Restore($tmp,"variable") Echo($tmp)
- Variables volatiles (identifiées par un ID numérique, l’utilisation du mot clef DEFINE est conseillé)
- Durée de vie semi-illimité (en cas de rédemarrage du serveur, les valeurs sont perdues)
- Accès en lecture/ecriture via 2 méthodes :
//definition de l'ID de la variable DEFINE VARIABLE 1 SetVolatile(VARIABLE,123) ... $tmp = @GetVolatile(VARIABLE) Echo($tmp)
L’utilité de ce type de variable par rapport aux variables persistentes se justifie uniquement en terme de performances. En effet les variables persistantes sont stockées dans la base de données, tandis que les variables volatiles sont stockées en mémoire. Cependant, leur caractère volatile impose certaines contraintes; l’entité controlée ne doit pas “se bloquer” (c’est à dire être dans un état transitoire qui la placerait dans l’incapacité de poursuivre sa logique de fonctionnement) en cas de coupures du serveur, plus ou moins frequentes et nécessaires lors de mises à jour. Typiquement, ces variables peuvent être utilisées pour :
- se souvenir de l’ennemi qu’on est en train d’attaquer
- se souvenir d’un point de patrouille choisit au hasard (parce qu’on a que ça à faire)
... Notez que dans ces 2 cas, un redemarrage du serveur (et donc une reinitialisation de ces variables) n’engendrera pas forcement un comportement stupide ou illogique de l’entité.
- Differents types de données pour les variables :
- Variables Chaînes : “bla”
- Variables Numériques : 12345
- Variables Points : [12.15]
Fonctions natives
Set(var,val) // renvoie 1 Store(name,var) // renvoie 1 (ou 0 si problème interne) Restore(var,name) // renvoie 1 si la variable a été restaurée, sinon 0 GetVolatile(vid) // renvoie la variable ou 0 si elle n'existe pas SetVolatile(vid,val) // renvoie 1 Stop() // renvoie 1 Execute(section) // renvoie 1 ou 0 si la section n'existe pas ou si la recursion est trop grande (profondeur 50) Echo(str) // renvoie 1 Attack(eid,wid,target) // renvoie : // -1 si erreur interne // 0 si l'entité n'existe pas // -2 ou -6 si hors de portée (arme ou vision) // -3 si les PE sont insuffisants // -4 si l'arme n'est pas prète // -5 si l'arme n'existe pas ou n'est plus en possession // -7 si les PR de la cible sont déjà au max (pour une réparation) // -8 si inconscient // -9 si paralysé // 1 si l'attaque a detruit la cible // 2 si l'attaque n'a pas detruit la cible AttackPos(pos,wid) // renvoie les memes codes d'erreur que Attack, mais toujours 1 pour succès MoveToPlayer(eid) // renvoie : // -6 si hangar (celui qui essaie de bouger) // -5 si en surcharge // -4 si innaccessible // -8 si inconscient // -9 si paralysé // sinon, le nombre de déplacements nécessaire pour aller à destination MoveToPoint(pos) // memes codes d'erreur que MoveToPlayer Now() // renvoie le timestamp unix courant Rand(min,max) // renvoie un entier aléatoire entre min et max IsBot(eid) // renvoie 1 si l'entité eid est une puce GetPos() // renvoie la position actuelle GetPosX() GetPosY() GetRandomReachablePos() // renvoie une position aléatoire sur la map accessible, -1 si aucune trouvée MyPR() // renvoie les PR courants MyPRMax() MyPE() // renvoie les milli PE courants MyPEMax() MyID() // renvoie l'id de l'entité courante NearestEnemy() // renvoie l'id de l'ennemi le plus proche, avec comme ordre de priorité : batiment offensif, puce, batiment NearestEnemyPlayer() NearestEnemyBuilding() AvailWeapon(eid) // renvoie l'id de la meilleure arme utilisable sur l'ennemi passé en parametre (eid peut etre -1) HaveWeaponReady() // renvoie 1 si une arme est dispo, sinon 0 SelfDestruction() // renvoie 1 si ok, -1 si erreur interne
Exemples de scripts d'IA
Tourelle de défense
//------------------------------------------------------------------------------------------- // // yota ia script v1 // // script pour tourelle de defense //------------------------------------------------------------------------------------------- DEFINE VOLATILE_LAST_ACT 1 DEFINE VOLATILE_WAIT 2 SECTION #Root Echo("#Root") Set($wait_val,@GetVolatile(VOLATILE_WAIT)) Set($last_act,@GetVolatile(VOLATILE_LAST_ACT)) Set($act_since,(@Now()-$last_act)) IF $act_since<$wait_val THEN Stop() END Execute(#Combat) END SECTION #OnPlayerMove IF 1 THEN WEIGHT 50 SetVolatile(VOLATILE_WAIT,30) Execute(#Root) WEIGHT 50 Echo("pas vu") Stop() END END SECTION #OnPlayerAttack END //--------------------------------------------------------------\ // #Wait : Comportement d'attente | // | //--------------------------------------------------------------/ SECTION #Wait //Echo("#Wait") //2 Minutes d'attente SetVolatile(VOLATILE_LAST_ACT,@Now()) SetVolatile(VOLATILE_WAIT,120) Stop() END SECTION #Combat Echo("#Combat") Set($nearest_enemy,@NearestEnemy()) IF !$nearest_enemy THEN Echo("pas d'ennemi proche") Execute(#Wait) Stop() END Set($availweapon,@AvailWeapon($nearest_enemy)) IF $availweapon THEN Execute(#Attack) Stop() END END SECTION #Attack Echo("#Attack") IF @IsBot($nearest_enemy) THEN WEIGHT 60 // 1 == pas de ciblage Set($target,1) WEIGHT 10 // 2 == tete Set($target,2) WEIGHT 30 // 3 == prop Set($target,3) END Attack($nearest_enemy,$availweapon,$target) //ajout d'un delai d'attente aléatoire IF 1 THEN WEIGHT 30 SetVolatile(VOLATILE_WAIT,30) WEIGHT 30 SetVolatile(VOLATILE_WAIT,45) WEIGHT 30 SetVolatile(VOLATILE_WAIT,60) END SetVolatile(VOLATILE_LAST_ACT,@Now()) END
Bot
//-------------------------------------------------------------------------------------------\ // | // yota ia script v1 - Bot2 - | // | // Données persistantes : context => [int] action courante | // target_pos => [point] point de patrouille courant | // current_target => [pid] cible courante | // | // Données volatiles : VOLATILE_LAST_ACT => [int] derniere action effectuée | // | //-------------------------------------------------------------------------------------------/ DEFINE CONTEXT_NONE 0 DEFINE CONTEXT_PATROL 1 DEFINE CONTEXT_COMBAT 2 DEFINE CONTEXT_FLEE 3 DEFINE VOLATILE_LAST_ACT 1 //--------------------------------------------------------------\ // #Root : Point d'entrée du script | // | //--------------------------------------------------------------/ SECTION #Root //Echo("#Root") //Si la derniere action effectuée est trop récente, on attend. Set($last_act,@GetVolatile(VOLATILE_LAST_ACT)) Set($act_since,(@Now()-$last_act)) IF $act_since<10 THEN Stop() END Execute(#ReevaluateContext) END //--------------------------------------------------------------\ // #ReevaluateContext : Réévalue l'état et donc le | // comportement à adopter. | //--------------------------------------------------------------/ SECTION #ReevaluateContext Echo("#ReevaluateContext") //Rappel de l'action en cours Restore($context,"context") Set($lowpr,@MyPR() < 20) Set($lowpe,@MyPE() < 10000) IF ($context==CONTEXT_FLEE) || $lowpr THEN WEIGHT 50 Execute(#Flee) Stop() WEIGHT 50 Execute(#Combat) Stop() END IF $lowpe THEN Execute(#Wait) Stop() END Execute(#Combat) END //--------------------------------------------------------------\ // #OnPlayerMove : Trigger sur déplacement d'un joueur à | // portée. | //--------------------------------------------------------------/ SECTION #OnPlayerMove Echo("#OnPlayerMove") Execute(#Root) END //--------------------------------------------------------------\ // #Flee : Comportement de fuite | // | //--------------------------------------------------------------/ SECTION #Flee Echo("#Flee") Restore($target_pos,"target_pos") IF !$target_pos THEN Echo($target_pos) Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END IF $target_pos==@GetPos() THEN Store("target_pos",0) Execute(#Flee) Stop() END IF 1 THEN WEIGHT 70 MoveToPoint($target_pos) WEIGHT 10 //Changement de direction aléatoirement Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) MoveToPoint($target_pos) WEIGHT 20 //Arret de la fuite Store("context",CONTEXT_NONE) END SetVolatile(VOLATILE_LAST_ACT,@Now()) Stop() END //--------------------------------------------------------------\ // #Wait : Comportement d'attente (energie faible) | // | //--------------------------------------------------------------/ SECTION #Wait Echo("#Wait") //1 Minute d'attente SetVolatile(VOLATILE_LAST_ACT,@Now()+60) Stop() END //--------------------------------------------------------------\ // #Patrol : Effectue un déplacement aléatoire sur la | // carte. | //--------------------------------------------------------------/ SECTION #Patrol Echo("#Patrol") //En dessous d'un seuil, on considère qu'on a pas assez d'énergie pour patrouiller //( et garder de quoi attaquer au cas ou) IF @MyPE() < (@MyPEMax()-10000) THEN Execute(#Wait) Stop() END //Nouveau point de patrouille si besoin Restore($target_pos,"target_pos") IF !$target_pos THEN Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END //Déplacement si on n'est pas encore à destination IF $target_pos==@GetPos() THEN //RAZ du point de patrouille && réévaluation de l'état Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Set($move_ret,@MoveToPoint($target_pos)) IF $move_ret<0 THEN Echo("Impossible de bouger ... patrouille") Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Store("context",CONTEXT_PATROL) Stop() END //--------------------------------------------------------------\ // #FindTarget : Renvoie un ennemi à portée, ou le | // précedent s'il n'est pas mort. | //--------------------------------------------------------------/ SECTION #FindTarget Echo("#FindTarget") Restore($nearest_enemy,"current_target") IF !$nearest_enemy THEN Set($nearest_enemy,@NearestEnemyPlayer()) END END //--------------------------------------------------------------\ // #Combat : Cherche une cible à portée en ce déplacant | // si besoin (via #Patrol si aucune n'est | // visible), puis attaque. | //--------------------------------------------------------------/ SECTION #Combat Echo("#Combat") //Aucune arme prete => fuite ou attente IF !@HaveWeaponReady() THEN WEIGHT 50 Execute(#Flee) Stop() WEIGHT 50 Execute(#Wait) Stop() END //Recherche de cible, ou récupération d'une cible existante Execute(#FindTarget) Echo($nearest_enemy) //Rien à portée, patrouille IF !$nearest_enemy THEN Execute(#Patrol) Stop() END //Un ennemi est à portée, on est réellement en combat ... // sauvegarde de l'ennemi courant. Store("context",CONTEXT_COMBAT) Store("current_target",$nearest_enemy) Set($availweapon,@AvailWeapon($nearest_enemy)) IF $availweapon THEN Execute(#Attack) Stop() END //Aucune arme dispo pour la distance actuelle; on se rapproche. Set($move_ret,@MoveToPlayer($nearest_enemy)) IF $move_ret<0 THEN Store("current_target",0) Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END SetVolatile(VOLATILE_LAST_ACT,@Now()) Stop() END //--------------------------------------------------------------\ // #Attack : Effectue l'attaque sur $nearest_enemy avec | // l'arme $availweapon | // | //--------------------------------------------------------------/ SECTION #Attack Echo("#Attack") //Choix du ciblage IF 1 THEN WEIGHT 60 // 1 == pas de ciblage Set($target,1) WEIGHT 10 // 2 == tete Set($target,2) WEIGHT 30 // 3 == prop Set($target,3) END Set($attack_ret,@Attack($nearest_enemy,$availweapon,$target)) Echo($attack_ret) IF ($attack_ret==1) || ($attack_ret==-2) THEN // Si @Attack a renvoyé 1, c'est que l'ennemi est mort, reinitialisation des données persistantes. // -2 => hors de portée (hangar) Store("current_target",0) Store("context",CONTEXT_NONE) END SetVolatile(VOLATILE_LAST_ACT,@Now()) END
Bot poseur de mine
//-------------------------------------------------------------------------------------------\ // | // yota ia script v1 - Bot2 - | // | // Données persistantes : context => [int] action courante | // target_pos => [point] point de patrouille courant | // current_target => [pid] cible courante | // | // Données volatiles : VOLATILE_LAST_ACT => [int] derniere action effectuée | // | //-------------------------------------------------------------------------------------------/ DEFINE CONTEXT_NONE 0 DEFINE CONTEXT_PATROL 1 DEFINE CONTEXT_COMBAT 2 DEFINE CONTEXT_FLEE 3 DEFINE VOLATILE_LAST_ACT 1 DEFINE VOLATILE_WAITING 2 //--------------------------------------------------------------\ // #Root : Point d'entrée du script | // | //--------------------------------------------------------------/ SECTION #Root Echo("#Root") //Si la derniere action effectuée est trop récente, on attend. Set($waiting,@GetVolatile(VOLATILE_WAITING)) IF $waiting THEN Set($delay,60) ELSE Set($delay,10) END Set($last_act,@GetVolatile(VOLATILE_LAST_ACT)) Set($act_since,(@Now()-$last_act)) IF $act_since<$delay THEN Echo("Waiting") Stop() END Execute(#ReevaluateContext) END //--------------------------------------------------------------\ // #ReevaluateContext : Réévalue l'état et donc le | // comportement à adopter. | //--------------------------------------------------------------/ SECTION #ReevaluateContext Echo("#ReevaluateContext") //Rappel de l'action en cours Restore($context,"context") Set($lowpr,@MyPR() < 20) Set($lowpe,@MyPE() < 10000) IF ($context==CONTEXT_FLEE) || $lowpr THEN WEIGHT 100 Execute(#Flee) Stop() END IF $lowpe THEN Execute(#Wait) Stop() END Execute(#Patrol) END //--------------------------------------------------------------\ // #OnPlayerMove : Trigger sur déplacement d'un joueur à | // portée. | //--------------------------------------------------------------/ SECTION #OnPlayerMove Echo("#OnPlayerMove") SetVolatile(VOLATILE_WAITING,0) Execute(#Root) END //--------------------------------------------------------------\ // #OnPlayerDrop : Trigger sur un objet posé | // à portée. | //--------------------------------------------------------------/ SECTION #OnPlayerDrop Echo("#OnPlayerMove") TryLoot() Execute(#Flee) END //--------------------------------------------------------------\ // #OnPlayerAttack : Trigger sur déplacement d'un joueur à | // portée. | //--------------------------------------------------------------/ SECTION #OnPlayerAttack Echo("#OnPlayerMove") SetVolatile(VOLATILE_WAITING,0) IF 1 THEN WEIGHT 5 Execute(#Drop) WEIGHT 95 Execute(#Flee) END END //--------------------------------------------------------------\ // #Flee : Comportement de fuite | // | //--------------------------------------------------------------/ SECTION #Flee Echo("#Flee") Restore($target_pos,"target_pos") IF !$target_pos THEN Echo($target_pos) Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END IF $target_pos==@GetPos() THEN Store("target_pos",0) Execute(#Flee) Stop() END IF 1 THEN WEIGHT 80 WEIGHT 20 Execute(#Drop) END IF 1 THEN WEIGHT 70 MoveToPoint($target_pos) WEIGHT 10 //Changement de direction aléatoirement Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) MoveToPoint($target_pos) WEIGHT 20 //Arret de la fuite Store("context",CONTEXT_NONE) END SetVolatile(VOLATILE_LAST_ACT,@Now()) Stop() END //--------------------------------------------------------------\ // #Wait : Comportement d'attente (energie faible) | // | //--------------------------------------------------------------/ SECTION #Wait Echo("#Wait") SetVolatile(VOLATILE_WAITING,1) //1 Minute d'attente //SetVolatile(VOLATILE_LAST_ACT,@Now()+60) IF 1 THEN WEIGHT 99 Stop() WEIGHT 1 Echo("WaitLoot") TryLoot() END Stop() END //--------------------------------------------------------------\ // #Patrol : Effectue un déplacement aléatoire sur la | // carte. | //--------------------------------------------------------------/ SECTION #Patrol Echo("#Patrol") //En dessous d'un seuil, on considère qu'on a pas assez d'énergie pour patrouiller //( et garder de quoi attaquer au cas ou) IF @MyPE() < (@MyPEMax()-(@MyPEMax()/2)) THEN Execute(#Wait) Stop() END //Nouveau point de patrouille si besoin Restore($target_pos,"target_pos") IF !$target_pos THEN Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END //Déplacement si on n'est pas encore à destination IF $target_pos==@GetPos() THEN //RAZ du point de patrouille && réévaluation de l'état Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END IF 1 THEN WEIGHT 10 Execute(#Drop) Stop() WEIGHT 90 Set($move_ret,@MoveToPoint($target_pos)) END IF $move_ret<0 THEN Echo("Impossible de bouger ... patrol") Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Store("context",CONTEXT_PATROL) Stop() END //--------------------------------------------------------------\ // #Drop : Tente de poser une mine | //--------------------------------------------------------------/ SECTION #Drop Echo("#Drop") Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) Set($drop_ret,@DropMine()) IF $drop_ret==1 THEN InvokeObject(6,17,2) END Set($move_ret,@MoveToPoint($target_pos)) IF $move_ret<0 THEN Echo("Impossible de bouger ... patrol") Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Store("context",CONTEXT_PATROL) Stop() END
Bot combat
//-------------------------------------------------------------------------------------------\ // | // yota ia script v2 - Bot2 - | // | // Données persistantes : context => [int] action courante | // target_pos => [point] point de patrouille courant | // current_target => [pid] cible courante | // | // Données volatiles : VOLATILE_LAST_ACT => [int] derniere action effectuée | // | //-------------------------------------------------------------------------------------------/ DEFINE CONTEXT_NONE 0 DEFINE CONTEXT_PATROL 1 DEFINE CONTEXT_COMBAT 2 DEFINE CONTEXT_FLEE 3 DEFINE CONTEXT_FINDGENE 4 DEFINE VOLATILE_LAST_ACT 1 DEFINE VOLATILE_WAITING 2 //--------------------------------------------------------------\ // #Root : Point d'entrée du script | // | //--------------------------------------------------------------/ SECTION #Root Echo("#Root") //Si la derniere action effectuée est trop récente, on attend. Set($waiting,@GetVolatile(VOLATILE_WAITING)) IF $waiting THEN Set($delay,60) ELSE Set($delay,10) END Set($last_act,@GetVolatile(VOLATILE_LAST_ACT)) Set($act_since,(@Now()-$last_act)) IF $act_since<$delay THEN Echo("Waiting") Stop() END Execute(#ReevaluateContext) END //--------------------------------------------------------------\ // #ReevaluateContext : Réévalue l'état et donc le | // comportement à adopter. | //--------------------------------------------------------------/ SECTION #ReevaluateContext Echo("#ReevaluateContext") //Rappel de l'action en cours Restore($context,"context") Set($lowpr,@MyPR() < 20) Set($lowpe,@MyPE() < 50000) IF ($context==CONTEXT_FLEE) || $lowpr THEN WEIGHT 50 Execute(#Flee) Stop() WEIGHT 50 Execute(#Combat) Stop() END // IF // $lowpe || ($context==CONTEXT_FINDGENE) // THEN // Execute(#FindGenerator) // Stop() // END Execute(#Combat) END //--------------------------------------------------------------\ // #OnPlayerMove : Trigger sur déplacement d'un joueur à | // portée. | //--------------------------------------------------------------/ SECTION #OnPlayerMove Echo("#OnPlayerMove") SetVolatile(VOLATILE_WAITING,0) Execute(#Root) END //--------------------------------------------------------------\ // #OnPlayerDrop : Trigger sur un objet posé | // à portée. | //--------------------------------------------------------------/ SECTION #OnPlayerDrop IF 1 THEN WEIGHT 80 TryLoot() Stop() WEIGHT 20 Stop() END END //--------------------------------------------------------------\ // #OnPlayerAttack : Trigger sur déplacement d'un joueur à | // portée. | //--------------------------------------------------------------/ SECTION #OnPlayerAttack Echo("#OnPlayerMove") SetVolatile(VOLATILE_WAITING,0) Execute(#Root) END //--------------------------------------------------------------\ // #Flee : Comportement de fuite | // | //--------------------------------------------------------------/ SECTION #Flee Echo("#Flee") Restore($target_pos,"target_pos") IF !$target_pos THEN Echo($target_pos) Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END IF $target_pos==@GetPos() THEN Store("target_pos",0) Execute(#Flee) Stop() END IF @MyPE() < 50000 THEN WEIGHT 70 Store("context",CONTEXT_NONE) Stop() WEIGHT 30 Execute(#Wait) Stop() END IF 1 THEN WEIGHT 70 MoveToPoint($target_pos) WEIGHT 10 //Changement de direction aléatoirement Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) MoveToPoint($target_pos) WEIGHT 20 //Arret de la fuite Store("context",CONTEXT_NONE) END SetVolatile(VOLATILE_LAST_ACT,@Now()) Stop() END //--------------------------------------------------------------\ // #Wait : Comportement d'attente (energie faible) | // | //--------------------------------------------------------------/ SECTION #Wait Echo("#Wait") SetVolatile(VOLATILE_WAITING,1) //1 Minute d'attente //SetVolatile(VOLATILE_LAST_ACT,@Now()+60) Stop() END SECTION #FindGenerator Restore($target_pos,"target_pos") IF !$target_pos THEN Set($target_pos,@GetNearestReachableGenPos()) Store("target_pos",$target_pos) END //Déplacement si on n'est pas encore à destination IF $target_pos==@GetPos() THEN //RAZ du point de patrouille && réévaluation de l'état Store("target_pos",0) Store("context",CONTEXT_NONE) Execute(#Wait) Stop() END Set($move_ret,@MoveToPoint($target_pos)) IF $move_ret<0 THEN Echo("Impossible de bouger ... FindGenerator") Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Store("context",CONTEXT_FINDGENE) END //--------------------------------------------------------------\ // #Patrol : Effectue un déplacement aléatoire sur la | // carte. | //--------------------------------------------------------------/ SECTION #Patrol Echo("#Patrol") //En dessous d'un seuil, on considère qu'on a pas assez d'énergie pour patrouiller //( et garder de quoi attaquer au cas ou) IF @MyPE() < (@MyPEMax()-10000) THEN Execute(#Wait) Stop() END IF @MyPE() < (@MyPEMax()/3) THEN Execute(#FindGenerator) Stop() END //Nouveau point de patrouille si besoin Restore($target_pos,"target_pos") IF !$target_pos THEN Set($target_pos,@GetRandomReachablePos()) Store("target_pos",$target_pos) END //Déplacement si on n'est pas encore à destination IF $target_pos==@GetPos() THEN //RAZ du point de patrouille && réévaluation de l'état Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Set($move_ret,@MoveToPoint($target_pos)) IF $move_ret<0 THEN Echo("Impossible de bouger ... patrol") Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END Store("context",CONTEXT_PATROL) Stop() END //--------------------------------------------------------------\ // #FindTarget : Renvoie un ennemi à portée, ou le | // précedent s'il n'est pas mort. | //--------------------------------------------------------------/ SECTION #FindTarget Echo("#FindTarget") Restore($nearest_enemy,"current_target") IF !$nearest_enemy THEN Set($nearest_enemy,@NearestEnemyPlayer()) END END //--------------------------------------------------------------\ // #Combat : Cherche une cible à portée en ce déplacant | // si besoin (via #Patrol si aucune n'est | // visible), puis attaque. | //--------------------------------------------------------------/ SECTION #Combat Echo("#Combat") IF @MyPE() < 15000 THEN Echo("Low PE => Wait") Execute(#Wait) Stop() END //TryLoot() //Aucune arme prete => fuite ou attente IF !@HaveWeaponReady() THEN WEIGHT 30 //TryLoot() Echo("No weapon => Flee") Execute(#Flee) Stop() WEIGHT 30 //TryLoot() Echo("No weapon => Wait") Execute(#Wait) Stop() WEIGHT 30 Echo("No weapon => Reload") ReloadSomething() Stop() END //Recherche de cible, ou récupération d'une cible existante Execute(#FindTarget) Echo($nearest_enemy) //Rien à portée, patrouille IF !$nearest_enemy THEN Execute(#Patrol) Stop() END //Un ennemi est à portée, on est réellement en combat ... // sauvegarde de l'ennemi courant. Store("context",CONTEXT_COMBAT) Store("current_target",$nearest_enemy) Set($availweapon,@AvailWeapon($nearest_enemy)) IF $availweapon THEN Execute(#Attack) Stop() END //Aucune arme dispo pour la distance actuelle; on se rapproche. Set($move_ret,@MoveToPlayer($nearest_enemy)) IF $move_ret<0 THEN Echo("cant move to target, reeval ... combat") Store("current_target",0) Store("target_pos",0) Store("context",CONTEXT_NONE) Stop() END SetVolatile(VOLATILE_LAST_ACT,@Now()) Stop() END //--------------------------------------------------------------\ // #Attack : Effectue l'attaque sur $nearest_enemy avec | // l'arme $availweapon | // | //--------------------------------------------------------------/ SECTION #Attack Echo("#Attack") //Choix du ciblage IF 1 THEN WEIGHT 60 // 1 == pas de ciblage Set($target,1) WEIGHT 10 // 2 == tete Set($target,2) WEIGHT 30 // 3 == prop Set($target,3) END Set($attack_ret,@Attack($nearest_enemy,$availweapon,$target)) Echo($attack_ret) IF ($attack_ret==1) || ($attack_ret==-2) THEN // Si @Attack a renvoyé 1, c'est que l'ennemi est mort, reinitialisation des données persistantes. // -2 => hors de portée (hangar) Store("current_target",0) Store("context",CONTEXT_NONE) TryLoot() END SetVolatile(VOLATILE_LAST_ACT,@Now()) SetVolatile(VOLATILE_WAITING,0) END
