Imbrication de OR dans les triggers de script

Répondre
Selphira
Adepte de Grondemarteau
Blême
Messages : 998
Enregistré le : dim. 25 juil. 2004, 21:28
Contact :
Statut : En ligne

Imbrication de OR dans les triggers de script

.

Message par Selphira »

Bonjour

Toujours dans le contexte du développement d'ItemDispatcher, je génère des conditions qui déterminent si un objet sera distribué sur une créature ou dans un magasin.

Par exemple, voici une condition générée qui indique que l'objet sera disponible durant les chapitre 2, 4, 6, uniquement la nuit, si le joueur joue en très facile ou difficile.

Code : Tout sélectionner

OR(3)
  Global("Chapter","GLOBAL",2)
  Global("Chapter","GLOBAL",4)
  Global("Chapter","GLOBAL",6)
TimeOfDay(NIGHT)
OR(2)
  Difficulty(EASIEST)
  Difficulty(HARD)
Je voudrais alors générer la condition inverse, afin de supprimer l'objet s'il a été rendu disponible, mais que l'une des conditions n'est plus valide.
C'est ici que je bloque, car là où il est assez simple d'écrire des conditions inversées grâce aux parenthèses, il n'existe, à ma connaissance, aucun mécanisme pour les scripts du jeu.
Si je traite chaque "groupe" de condition indépendamment, c'est très facile, la difficulté survient lorsque je dois combiner ces inversions.

Code : Tout sélectionner

OR(3)
  Global("Chapter","GLOBAL",2)
  Global("Chapter","GLOBAL",4)
  Global("Chapter","GLOBAL",6)
Devient

Code : Tout sélectionner

!Global("Chapter","GLOBAL",2)
!Global("Chapter","GLOBAL",4)
!Global("Chapter","GLOBAL",6)

Code : Tout sélectionner

TimeOfDay(NIGHT)
Devient

Code : Tout sélectionner

!TimeOfDay(NIGHT)

Code : Tout sélectionner

OR(2)
  Difficulty(EASIEST)
  Difficulty(HARD)
devient

Code : Tout sélectionner

!Difficulty(EASIEST)
!Difficulty(HARD)
Dans la condition de base, on indique que TOUS les groupes de condition doivent être valides pour que l'action s'exécute.
Avec la condition inversée, on veut que si UN SEUL des groupes est valide, l'action s'exécute.
Je voudrais donc trouver la solution pour écrire l'équivalent de ceci

Code : Tout sélectionner

IF (
  (
    !Global("Chapter","GLOBAL",2) 
    AND !Global("Chapter","GLOBAL",4) 
    AND !Global("Chapter","GLOBAL",6)
  )
  OR !TimeOfDay(NIGHT)
  OR (
    !Difficulty(EASIEST)
    AND !Difficulty(HARD)
  )
)
J'ai bien une solution, un peu plus complexe à mettre en oeuvre et qui va générer un nombre énorme de triggers à vérifier, mais j'aimerais l'éviter si possible.
Elle consiste à vérifier pour toutes les autres valeurs disponibles lorsqu'on a un OR(x) dans la condition de base.

Code : Tout sélectionner

OR(23)
  Global("Chapter","GLOBAL",1)
  Global("Chapter","GLOBAL",3)
  Global("Chapter","GLOBAL",5)
  Global("Chapter","GLOBAL",7)
  Global("Chapter","GLOBAL",8)
  Global("Chapter","GLOBAL",9)
  Global("Chapter","GLOBAL",10)
  Global("Chapter","GLOBAL",11)
  Global("Chapter","GLOBAL",12)
  Global("Chapter","GLOBAL",13)
  Global("Chapter","GLOBAL",14)
  Global("Chapter","GLOBAL",15)
  Global("Chapter","GLOBAL",16)
  Global("Chapter","GLOBAL",17)
  Global("Chapter","GLOBAL",18)
  Global("Chapter","GLOBAL",19)
  Global("Chapter","GLOBAL",20)
  Global("Chapter","GLOBAL",21)
  Global("Chapter","GLOBAL",22)
  !TimeOfDay(NIGHT)
  Difficulty(EASY)
  Difficulty(NORMAL)
  Difficulty(HARDEST)
Cette solution est optimisable évidemment, en utilisant GlobalGT("Chapter","GLOBAL",6) par exemple. Mais elle reste plus complexe à mettre en oeuvre de manière générique
Modifié en dernier par Selphira le sam. 14 déc. 2024, 08:53, modifié 1 fois.
JohnBob
d'Oghmatique
Troll
Messages : 1429
Enregistré le : mar. 28 sept. 2021, 19:52
Localisation : France
Statut : Hors ligne
.

Message par JohnBob »

Salut,

Est-ce que deux blocks pourraient convenir ?

Code : Tout sélectionner

IF
  !Global("Chapter","GLOBAL",2)
  !Global("Chapter","GLOBAL",4)
  !Global("Chapter","GLOBAL",6)
  !Difficulty(EASIEST)
  !Difficulty(HARD)
  Global("TimeOfDay_DISPATCHER","GLOBAL",0)
THEN RESPONSE #100
          SetGlobal("TimeOfDay_DISPATCHER","GLOBAL",1)
END

IF
  !Global("TimeOfDay_DISPATCHER","GLOBAL",0)
  Global("CHANGEITEM_DISPATCHER","GLOBAL",0)
  !TimeOfDay(NIGHT)
THEN RESPONSE #100
          SetGlobal("CHANGEITEM_DISPATCHER","GLOBAL",1)
          Les modifications inversées voulues
END
Serait le contraire de :

Code : Tout sélectionner

IF
  Global("Chapter","GLOBAL",2)
  Global("Chapter","GLOBAL",4)
  Global("Chapter","GLOBAL",6)
  TimeOfDay(NIGHT)
  OR(2)
  Difficulty(EASIEST)
  Difficulty(HARD)
  Global("TimeOfDay_DISPATCHER","GLOBAL",0)
THEN RESPONSE #100
          SetGlobal("TimeOfDay_DISPATCHER","GLOBAL",1)
          SetGlobal("CHANGEITEM_DISPATCHER","GLOBAL",0)
          Les modifications voulues
END

C'est pas forcément plus simple à implémenter...
Modifié en dernier par JohnBob le sam. 14 déc. 2024, 12:11, modifié 6 fois.
Avatar du membre
Luren
Adepte de Grondemarteau
Ver charognard
Messages : 660
Enregistré le : dim. 20 juin 2010, 08:51
Localisation : Sûrement quelque part mais je n'arrive pas à bien voir où.
Statut : Hors ligne
.

Message par Luren »

Si je comprends bien, pour le second cas de figure, tu voudrais cumuler des Or(). D'où l'utilisation du AND que tu voudrais faire, mais qui malheureusement n'existe pas.
Pourquoi ne pas décomposer alors les conditions en stockant une valeur, de manière à pouvoir les interroger ensemble dans un bloc ?

Code : Tout sélectionner

IF
	Or(3)
		!Global("Chapter","GLOBAL",2)
 		!Global("Chapter","GLOBAL",4)
 		!Global("Chapter","GLOBAL",6)
THEN 
	RESPONSE #100
		SetGlobal("condition1","GLOBAL",1)
		Continue()
END

IF
	Or(2)
		!Difficulty(EASIEST)
		!Difficulty(HARD)
THEN
	RESPONSE #100
		SetGlobal("condition2","GLOBAL",1)
		Continue()
END

IF
	Or(3)
		Global("condition1),"GLOBAL",1)
		!TimeOfDay(NIGHT)
		Global("condition2),"GLOBAL",1)
THEN
	 RESPONSE #100
	 	...
END
Modifié en dernier par Luren le sam. 14 déc. 2024, 15:17, modifié 2 fois.
Selphira
Adepte de Grondemarteau
Blême
Messages : 998
Enregistré le : dim. 25 juil. 2004, 21:28
Contact :
Statut : En ligne
.

Message par Selphira »

Merci pour vos propositions.
Elles sont malheureusement encore plus compliquées à implémenter, du moins, avec mon code actuel.
Je les garde tout de même sous le coude, peut-être que j'aurais l'idée d'un algorithme qui permettra de gérer ce genre de cas complexe sans trop de difficulté.
JohnBob
d'Oghmatique
Troll
Messages : 1429
Enregistré le : mar. 28 sept. 2021, 19:52
Localisation : France
Statut : Hors ligne
.

Message par JohnBob »

Pour rester sur l'idée de Luren, ne serait-il pas "assez simple" d'ajouter une série de conditions à Baldur.bcs grace à un baf qui contiendrait toutes les conditions et qui serait utilisées pour toutes les situations ?

Afin que tu n'aies qu'à ajouter la Global condition 1 / 2 ou 3 et... aux scripts dédiés à une carte, créature magasin.

Ex :

// Baldur.bcs

Code : Tout sélectionner

IF
		!Difficulty(EASIEST)
		!Difficulty(HARD)
THEN
	RESPONSE #100
		SetGlobal("condition_Difficulty","GLOBAL",0)
		Continue()
END

IF
	Or(2)
		Difficulty(EASIEST)
		Difficulty(HARD)
THEN
	RESPONSE #100
		SetGlobal("condition_Difficulty","GLOBAL",1)
		Continue()
END

// Ton script

Code : Tout sélectionner

IF
	Or(2)
		Global("condition_Difficulty","GLOBAL",1)
		!TimeOfDay(NIGHT)
THEN
	RESPONSE #100
	.............Changements...............
END

IF
	Or(2)
		!Global("condition_Difficulty","GLOBAL",1)
		TimeOfDay(NIGHT)
THEN
	RESPONSE #100
	.............Changements inversés...............
END

Du coup il faudrait définir plusieurs blocs qui regrouperaient un certain nombre de facteurs afin de les transformer en variable.

Alors, je comprends que tu viens de dire que cela ne convenait pas à ton code actuel, mais en cas, si cette nuance change quelque chose.
Modifié en dernier par JohnBob le dim. 15 déc. 2024, 22:12, modifié 2 fois.
rivvers
Worg
Messages : 358
Enregistré le : sam. 21 janv. 2012, 14:36
Statut : Hors ligne
.

Message par rivvers »

Sur l'idée de JohnBob, en créant quelques GLOBAL sur mesure… mais en utilisant des variables en base 16 plutôt qu'en base 10.
Ça permet de traiter en une ligne beaucoup de conditions qui t'éviteront de faire des AND dans des OR.

Code : Tout sélectionner

IF
    Difficulty(EASIEST)
THEN RESPONSE #100
    BitSet("DifficultyBit", "GLOBAL", BIT1)
END

IF
    Difficulty(…)
THEN RESPONSE #100
    BitSet("DifficultyBit", "GLOBAL", BIT2)
END

IF
    Difficulty(HARD)
THEN RESPONSE #100
    BitSet("DifficultyBit", "GLOBAL", BIT3)
END
Le BitSet ici n'est peut être pas la meilleure idée mais il est explicite, tu comprends où je veux en venir.

Du coup pour la gestion des conditions, on reste sobre :

Code : Tout sélectionner

IF
    BitCheck("DifficultyBit", "GLOBAL", 9) // EASIEST ou HARD
    BitCheck("ChapterBit", "GLOBAL", 42) // 2 ou 4 ou 6
    TimeOfDay(NIGHT)
THEN RESPONSE #100
    …
END
C'est pratique mais ça ne t'avance guère.

Maintenant, la magie. Traiter les inverses est fortement facilité car toutes les conditions sont traitées en une ligne

Code : Tout sélectionner

IF
    OR(3)
        !BitCheck("DifficultyBit", "GLOBAL", 9) // tout sauf EASIEST ou HARD
        !BitCheck("ChapterBit", "GLOBAL", 42) // tout sauf 2 ou 4 ou 6
        !TimeOfDay(NIGHT) // pas la nuit
THEN RESPONSE #100
    …
END
C'est rapide à exécuter et en terme de code à produire pour générer ça dynamiquement, ça me paraît vraiment pas mal.

Pour info on peut aller jusqu'au BIT32, suffisant pour traiter les cas présentés ici mais probablement pas tous.
Vous êtes à la recherche d'un mod merveilleux ? Trouvez votre bonheur ici.
Vous êtes un artisan moddeur et perfectionniste ? Découvrez une traduction améliorée de DLTCEP .
Vous êtes un explorateur insatiable des mécaniques de l'InfinityEngine ? Plongez dans les secrets du Codex.
Selphira
Adepte de Grondemarteau
Blême
Messages : 998
Enregistré le : dim. 25 juil. 2004, 21:28
Contact :
Statut : En ligne
.

Message par Selphira »

Merci pour vos nouvelles propositions.

Depuis, j'ai un peu avancé sur le sujet, et j'ai réussi à créer un algorithme (plus facilement que prévu) qui génère les conditions et leur inverse en étant un minimum optimisé.
Pour le moment, je n'utilise pas de bloc spécifique pour remplir des variables qui serviraient à d'autres vérifications. Mais je n'exclus pas cette possibilité à l'avenir.

Je suis arrivée à une autre étape, inerrante à la nature du mod, qui me bloque psychologiquement et m'empêche d'avancer. Celle du mix des conditions pour différents objets qui devraient être distribués sur un même emplacement d'équipement.
Je vous explique avec un cas simple

- Le mod Alpha veut équiper sw1h01.itm sur thief1.cre entre le chapitre 2 et 3
- Le mod Beta veut équiper sw1h02.itm sur thief1.cre entre le chapitre 3 et 4
- Le mod Gamma veut équiper sw1h03.itm sur thief1.cre entre le chapitre 2 et 3

Cela va générer 2 blocs (4 avec les conditions inverses), un bloc qui distribuera aléatoirement sw1h01.itm ou sw1h03.itm, et un autre bloc qui distribuera sw1h02.itm.

Pour le chapitre 3, ce sera toujours le premier des 2 blocs qui sera pris en compte, on perd une partie de l'aléatoire, bien qu'elle soit théoriquement codée.

J'ai pensé à plusieurs solutions, mais aucune ne me satisfait pleinement :
- Regrouper les parties de conditions communes, ce qui créera + de blocs.
Cette solution semble pas mal pour le cas ci-dessus, mais cela se complexifie grandement si on ajoute les autre critères possibles. Très compliqué à implémenter.
- Ajouter un tirage aléatoire "RandomNum()" pour les blocs concernés, avec une chance donc qu'aucun objet ne soit jamais distribué.
C'est, je pense, la solution qui offre le meilleur compromis entre facilité d'implémentation, et résultat voulu.
- Sélectionner aléatoirement, lors de l'installation, une seule règle conditionnée par emplacement d'objet.
On perd une grande partie de l'aléatoire, et il faudra réinstaller pour éventuellement prendre en compte une autre règle.

Un autre problème en lien avec les conditions inversées, est de faire en sorte de revenir à l'item initial lorsque la condition de distribution n'est plus valide (changement de chapitre, de difficulté ou de moment de la journée)
Pour cela, mon algorithme détermine l'objet actuel qui se trouve dans le fichier .itm, et dans le bloc inverse, j'inscris en dur quel objet il faudra remettre lorsque la condition n'est plus valide.
Cette partie reste simple, là où cela se complique, c'est lorsque je dois déterminer quel objet sera supprimé. Le jeu ne permet pas de créer de variable de type string, il faut donc créer une variable par objet. Et comme si cela ne suffisait pas, il y a le cas des trésors aléatoires ! Alors oui quand on le donne c'est simple (Exemple RNDWAND.itm), par contre, lorsque l'on veut le supprimer, il faut supprimer l'objet qui a été tiré par le jeu ! Et donc, le script devra faire une suppression de tous les objets possibles pour ce trésor aléatoire, avec un risque de supprimer plus que nécessaire si la créature possède 2 objets de cette liste (1 distribué aléatoirement, et 1 autre distribué "en direct")

Car oui, pour éviter d'accumuler plein d'objets sur la créature, il faut détruire ou remplacer l'objet qui se trouve actuellement à l'emplacement désiré avant d'ajouter le nouveau.
Le moteur possède plusieurs actions qui permettent cela, mais il faut toujours connaitre l'objet exact que l'on veut détruire ou remplacer. Je ne peux par exemple pas détruire l'objet qui se trouve ç l'emplacement de l'amulette. Ca existe peut-être, mais je n'ai pas trouvé.

Voici les différentes actions qui, de mon point de vue, peuvent être utiles pour ce cas de figure.

DestroyItem(S:ResRef*)
EquipItem(S:Object*) -> Remplacé par XEquipItem
EquipItemEx(S:Object*,I:EquipUnEquip*) -> Remplacé par XEquipItem
XEquipItem(S:Item*,O:Object*,I:Slot*Slots,I:EquipUnEquip*Xequip) -> Parfaite pour équiper un objet dans un emplacement particulier
CreateItem(S:ResRef*,I:Usage1*,I:Usage2*,I:Usage3*)
GiveItemCreate(S:ResRef*,O:Object*,I:Usage1*,I:Usage2*,I:Usage3*) -> Remplacé par CreateItem
TakeItemReplace(S:Give*,S:Take*,O:Object*) -> Il faut absolument connaitre quel objet remplacer, et de plus, l'objet remplacé est déplacé dans l'inventaire
TransformItem(S:OldItem*,S:NewItem*) -> Il faut absolument connaitre quel objet transformer, et si la créature en possède un dans son inventaire et un équipé, lequel sera transformé ?

C'est pour moi un casse-tête, mais je ne doute pas trouver un jour une solution satisfaisante en trouvant les bons compromis.
rivvers
Worg
Messages : 358
Enregistré le : sam. 21 janv. 2012, 14:36
Statut : Hors ligne
.

Message par rivvers »

Je ne suis pas certain de la pertinence de mettre de l'aléatoire, en tout cas pas comme unique solution.
Le cadre (malheureusement) rigide me paraît nécessaire. Sinon ça serait comme pouvoir faire des méga-install dans n'importe quel ordre. Des fois ça serait tel mod qui s'appliquerait mais si tu recharges la partie ça serait tel autre.

Supprimer un objet sans connaître son nom…
FillSlot aurait été super avec quelques arrangements mais il a été "corrigé".
Cela dit, il reste utile pour des actions génériques.
Vous êtes à la recherche d'un mod merveilleux ? Trouvez votre bonheur ici.
Vous êtes un artisan moddeur et perfectionniste ? Découvrez une traduction améliorée de DLTCEP .
Vous êtes un explorateur insatiable des mécaniques de l'InfinityEngine ? Plongez dans les secrets du Codex.
Selphira
Adepte de Grondemarteau
Blême
Messages : 998
Enregistré le : dim. 25 juil. 2004, 21:28
Contact :
Statut : En ligne
.

Message par Selphira »

Pour information, la problématique de la suppression de l'objet dans un emplacement bien précis peut être résolue grâce à un sort et l'opcode 143 avec un timing très petit :)

L'astuce a été donnée par Argent77 ici : https://www.gibberlings3.net/forums/top ... ent-346013
rivvers
Worg
Messages : 358
Enregistré le : sam. 21 janv. 2012, 14:36
Statut : Hors ligne
.

Message par rivvers »

Oh, sympa. Il va falloir que je repasse sur cet opcode, cette subtilité m'avait échappée.
Depuis la 2.6 tu peux même laisser le champ resource vide avec un timing nul. Ça évitera des problèmes inutiles.
Vous êtes à la recherche d'un mod merveilleux ? Trouvez votre bonheur ici.
Vous êtes un artisan moddeur et perfectionniste ? Découvrez une traduction améliorée de DLTCEP .
Vous êtes un explorateur insatiable des mécaniques de l'InfinityEngine ? Plongez dans les secrets du Codex.
Répondre

Retourner vers « Modding et Outils de développement »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 2 invités