Lorsque je teste un jeu, j’y vais au feeling, donc je ne teste pas de manière exhaustive (ce qui est de toutes façons quasi impossible, la combinatoire est trop grande dans la plupart des jeux).
En revanche, pour mon jeu Station Spatiale S16 Prologue, j’ai commencé à suivre deux principes :
- Des classes qui me permettent de gérer par avance des caractéristiques telles que “élément du décor non prenable”, “object lointain” “objet mentionné mais pas là”… Inform est très souple, mais la contrepartie est que sa modélisation du monde est assez faible (contrairement à TADS 3 notamment, dont j’ai trouvé le code horrible, mais la puissance géniale. Malheureusement, on n’a jamais fait de traduction en Français.). Je rejoins donc ton approche consistant à te faire un framework pour blinder un peu les choses, même si je pense qu’en j’en suis loin avec mes petites tentatives.
Exemples :
Class LieuObjet ! objet dans le lieu représentant le lieu pour l'interaction avec le joueur
class Decor
with description [; <<Look>>;],
react_before [;
EmptyT: if (second == self) "Tentez plutôt de vider ", (the) noun, " sur autre chose. Du moins, si cela a un sens...";
Remove: if (second == self) {
if (noun == joueur) <<Exit self>>;
"Tentez plutôt de prendre ", (the) noun, " avec vous et de sortir avec. Du moins, si c'est possible...";
}
],
before [;
Enter: "Vous y êtes déjà.";
Exit: <<Go out_obj>>;
Smell: <<Smell>>;
Listen: <<Listen>>;
Taste: "L'endroit est peut-être à votre goût, mais ce n'est pas la maison en pain d'épices de la sorcière !";
Rub: "Faire le ménage ne fait pas partie de votre mission.";
Dig: <<Dig>>;
Search, LookUnder: "Veuillez choisir un objet plus précis que le lieu entier pour ce type d'action.";
Receive: "Ça y est déjà, directement ou indirectement.";
! Actions pour lesquelles les messages par défaut sont généralement OK
!Take: Drop: Pull: Push: Turn: PushDir: Attack: Squeeze: Cut: Consult: Drink: Burn:
!Climb: JumpOver: Swing: Buy: Fill: Empty: Tie: Set: SetTo: SwitchOff: SwitchOn: Wave:
!Unlock: Lock: Eat: Wear: Disrobe: Blow:
];
Class Decor
has scenery;
Class LieuObjet ! objet dans le lieu représentant le lieu pour l'interaction avec le joueur
class Decor
with description [; <<Look>>;],
react_before [;
EmptyT: if (second == self) "Tentez plutôt de vider ", (the) noun, " sur autre chose. Du moins, si cela a un sens...";
Remove: if (second == self) {
if (noun == joueur) <<Exit self>>;
"Tentez plutôt de prendre ", (the) noun, " avec vous et de sortir avec. Du moins, si c'est possible...";
}
],
before [;
Enter: "Vous y êtes déjà.";
Exit: <<Go out_obj>>;
Smell: <<Smell>>;
Listen: <<Listen>>;
Taste: "L'endroit est peut-être à votre goût, mais ce n'est pas la maison en pain d'épices de la sorcière !";
Rub: "Faire le ménage ne fait pas partie de votre mission.";
Dig: <<Dig>>;
Search, LookUnder: "Veuillez choisir un objet plus précis que le lieu entier pour ce type d'action.";
Receive: "Ça y est déjà, directement ou indirectement.";
! Actions pour lesquelles les messages par défaut sont généralement OK
!Take: Drop: Pull: Push: Turn: PushDir: Attack: Squeeze: Cut: Consult: Drink: Burn:
!Climb: JumpOver: Swing: Buy: Fill: Empty: Tie: Set: SetTo: SwitchOff: SwitchOn: Wave:
!Unlock: Lock: Eat: Wear: Disrobe: Blow:
];
class LieuObjetVoisin ! objet dans un lieu représentant un autre lieu voisin et potentiellement accessible
class Decor
with description 0,
enter_to 0,
before [;
Examine: if (self.description == 0) return self.default_msg();
Enter: if (self.enter_to ~= 0) <<Go self.enter_to>>;
default: return self.default_msg();
],
default_msg [; "Si ", (the) noun, " vous intéresse, tentez plutôt d'y aller.";];
Class Inaccessible
class Decor
with before [;
! On laisse quelques actions quand même
Examine, Listen, Smell, Montrer:
! Ainsi que celles qui ont une réponse par défaut déjà utile
Exit:
! Et on rejette le reste
default :
self.default_msg();
rtrue;
],
default_msg [; "C'est inaccessible.";];
class Lointain
class Inaccessible
with before [;
Smell: self.default_msg(); rtrue;
],
default_msg [; print_ret (The) self, " ", (isorare) self, " hors de portée.";];
class PasLa ! Je voulais Absent mais c'est déjà un nom réservé par inform pour un attribut
class Decor
with before [;
Ask, Answer: <<ParlerSansPrecision self>>; ! Pour éviter le message "pour parler avec..."
! Pour quelque chose qui n'est pas là, on rejette toutes les actions, purement et simplement
default:
self.default_msg();
rtrue;
],
with default_msg [; print_ret (The) self, " ", (isorarenot) self, " pas là.";];
class Porte
with before [;
LookUnder:
if (self has open) "Regarder sous une porte ouverte, je n'arrive pas à conceptualiser.";
else "Aucun interstice ne permet de voir dessous.";
Turn, Wave: "C'est une porte coulissante, pas le type de porte qu'on peut faire pivoter.";
Pull, Push: "Vous essayez de faire coulisser la porte, mais elle ne bouge pas.";
ThrownAt: "La porte vous lance - métaphoriquement - un regard dissuasif.";
Attack: "La porte reste intacte.";
Climb: "C'est bien trop lisse. Et puis pour aller où ?";
Squeeze: "Vous en êtes incapable";
Dig: "La porte est trop solide.";
],
affiche_statut [;
if (self has open) print (The) self, " est ouvert", (es) self, ".";
else print (The) self, " est fermé", (es) self, ".";
],
has female scenery door openable ~open;
- Des templates de code à copier-coller pour chaque objet. Leur tête peut faire peur car ils sont très longs, mais je fais juste un petit passage rapide sur la liste des actions, je supprime tout ce que je n’ai pas vraiment besoin de gérer, et ne garde que le reste. Ainsi, peu de chances d’oublier des actions importantes à gérer (mais on en oublie quand même toujours).
Lieu lieu_unlieu ""
with description "",
!in_to "FIXME",
!out_to [; return self.cant_go();],
!cant_go "FIXME",
compass_look [;
!if (noun == u_obj) "FIXME"; ! regarder en haut
!if (noun == d_obj) "FIXME"; ! regarder en bas
!if (noun == out_obj) "FIXME"; ! regarder dehors
],
before [;
!Listen: if (~~noun) rfalse;
!Smell: if (~~noun) rfalse;
!CrierSansPrecision: rfalse;
!Jump: rfalse;
!Swim: rfalse;
!WaveHands: rfalse;
!Dig: rfalse;
!Sing: rfalse;
!Pray: rfalse;
!Think: rfalse;
!Sleep: rfalse;
!SeLever: rfalse;
! Penser aussi au sol, au plafond, à l'intérieur et à l'extérieur, si cela a du sens
],
has voit_pas_dehors; ! OU simulation;
LieuObjet -> lieu_unlieu_proxy ""
with name '',
adjective '',
before [;
!Open: rfalse;
!Close: rfalse;
!Lock: rfalse;
!Unlock: rfalse;
!Voir la liste des actions ci-dessous pour le reste
!Enter: déjà géré : "Vous y êtes déjà."
!Exit: déjà redirigé en GoOut
],
has ;
Decor -> unlieu_un_objet ""
with name '',
adjective '',
description "",
before [;
! Actions à un objet
!LookUnder: rfalse;
!Search: rfalse;
!Take: rfalse;
!Drop: rfalse;
!Pull: rfalse;
!Push: rfalse;
!Turn: rfalse;
!PushDir: rfalse;
!Wave: rfalse;
!Smell: rfalse;
!Listen: rfalse;
!Taste: rfalse;
!Touch: rfalse;
!Rub: rfalse;
!Enter: rfalse;
!Exit: rfalse;
!Open: rfalse;
!Close: rfalse;
!Attack: rfalse; ! peut aussi être à deux objets : "casser x avec y"
!Squeeze: rfalse;
!Cut: rfalse; ! peut aussi être à deux objets : "couper x avec y"
!Eat: rfalse;
!Drink: rfalse;
!Burn: rfalse; ! peut aussi être à deux objets : "brûler x avec y"
!Consult: rfalse;
!Climb: rfalse;
!JumpOver: rfalse;
!Swing: rfalse;
!Buy: rfalse;
!Fill: rfalse;
!Empty: rfalse;
!Wear: rfalse;
!Disrobe: rfalse;
!Dig: rfalse; ! peut aussi être à deux objets : "creuser x avec y"
!Tie: rfalse; ! peut aussi être à deux objets : "attacher x avec y". Attention, formulation réversible.
!Blow: rfalse;
!Set: rfalse;
!SetTo: rfalse; ! supprimé du jeu
!SwitchOff: rfalse;
!SwitchOn: rfalse;
! Actions à deux objets
!Unlock: rfalse;
!Lock: rfalse;
!Give: rfalse;
!Insert: rfalse;
!PutOn: rfalse;
!Remove: rfalse;
!ThrowAt: rfalse;
! "Fake actions", générées en réaction à une autre action
! On peut aussi vouloir intervenir en react_before si on veut réagir sans vérifier que 'noun' accepte l'action
!Receive: rfalse;
!LetGo: rfalse;
!ThrownAt: rfalse;
! Conversation et interaction avec les personnages
!ParlerSansPrecision: rfalse;
!Ask: rfalse;
!Tell: rfalse;
!Answer: rfalse;
!AskFor: rfalse;
!AskTo: rfalse;
!WaveHands: rfalse;
!WakeOther: rfalse;
!Kiss: rfalse;
!default: rfalse;
],
! Pour les créatures
react_before [;
! Actions à deux objets - partie interceptée dans react_before
!Montrer: if (second == self) rfalse;
!EmptyT: if (second == self) rfalse;
],
life [;
! Actions à deux objets - partie interceptée dans life: Give et Show, en tant que "receveur"
! (mais dans ce jeu pas de Show, remplacé par Montrer)
!Give: rfalse;
],
orders [;
!rfalse;
]
has ;
Cela m’aide d’aimer jouer et d’être le type de joueur qui aime vérifier la “finition” des jeux, car j’arrive assez bien à me mettre dans la peau d’un joueur de mon type. Bien sûr, cela n’est pas suffisant, mais je crois pouvoir dire qu’avant même les beta-tests, mon jeu était déjà d’une qualité honorable.