Quête "nouvelle" : écran Accepter/Refuser à la fin du dialogue
Une fois le dialogue épuisé, si la quête est encore "nouvelle", la boîte de dialogue bascule sur un écran d'offre au lieu de se masquer : header "Quête : <titre>", body l'objectif, pied deux boutons. - Accepter -> la quête passe "en_cours" (en MÉMOIRE seulement, gameData.quests — jamais persisté en base : le statut en base est celui de DÉPART pour toute nouvelle partie, pas un état de partie en cours, voir PLAYER_SHARED/full_game_payload.py). - Refuser -> la boîte se referme SANS toucher au statut, qui reste "nouvelle" : la prochaine interaction rejoue exactement le même dialogue depuis le début — impossible d'avancer la quête sans l'accepter un jour. Pour tout autre statut (déjà "en_cours"/"terminee"), la boîte se masque normalement à la fin du dialogue, comme avant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
df34106752
commit
e90d941527
@@ -19,7 +19,10 @@ Object.assign(global, actionsMod);
|
||||
require('../personnage-controller.js');
|
||||
const { forgeDialogueBoxAdvance } = require('../dialogue-box-controller.js'); // pas mocké : la vraie logique métier
|
||||
|
||||
const { forgeCollisionRuleTick } = require('../collision-rules-controller.js');
|
||||
const {
|
||||
forgeCollisionRuleTick,
|
||||
} = require('../collision-rules-controller.js');
|
||||
const { forgeDialogueBoxAcceptQuest, forgeQuestById } = require('../dialogue-box-controller.js');
|
||||
|
||||
function fakeEl(style) {
|
||||
const classes = new Set();
|
||||
@@ -33,13 +36,14 @@ function fakeEl(style) {
|
||||
if (sel === '.dialogueBoxWidget') return children.find(c => c._isDialogueBoxWidget);
|
||||
if (sel === '[data-dialogue-role="header"]') return this._header;
|
||||
if (sel === '[data-dialogue-role="body"]') return this._body;
|
||||
if (sel === '[data-dialogue-role="footer"]') return this._footer;
|
||||
return undefined;
|
||||
},
|
||||
getAttribute(attr) { return attr === 'data-object-id' ? this._objectId : null; },
|
||||
};
|
||||
}
|
||||
|
||||
test('collision "quete" -> boîte de dialogue affiche la 1ère réplique, "Suivant" avance, se masque à la fin', () => {
|
||||
test('collision "quete" -> dialogue puis offre de quête -> Accepter fait passer la quête en_cours', () => {
|
||||
const player = { id: 1, kind: 'personnage', personnage_role: 'joueur' };
|
||||
const mrPinguin = {
|
||||
id: 2, kind: 'personnage',
|
||||
@@ -54,12 +58,13 @@ test('collision "quete" -> boîte de dialogue affiche la 1ère réplique, "Suiva
|
||||
boxWidget._objectId = '3';
|
||||
boxWidget._header = { textContent: '' };
|
||||
boxWidget._body = { textContent: '' };
|
||||
boxWidget._footer = { innerHTML: '' };
|
||||
|
||||
global.window.currentScreenId = 1;
|
||||
global.gameData = {
|
||||
personnage_animations: {},
|
||||
quests: [{
|
||||
id: 99, status: 'nouvelle',
|
||||
id: 99, title: 'Retrouver Charlie', status: 'nouvelle', objectif: 'Parler à Mr. Pinguin',
|
||||
dialogues: {
|
||||
nouvelle: [
|
||||
{ speaker: 'Mr. Pinguin', text: 'Bonjour !' },
|
||||
@@ -102,7 +107,14 @@ test('collision "quete" -> boîte de dialogue affiche la 1ère réplique, "Suiva
|
||||
assert.equal(boxWidget._body.textContent, 'Salut !');
|
||||
assert.equal(boxWidget.classList.contains('is-active'), true);
|
||||
|
||||
forgeDialogueBoxAdvance('3'); // dernière réplique consommée -> la modale disparaît
|
||||
assert.equal(boxWidget.classList.contains('is-active'), false);
|
||||
forgeDialogueBoxAdvance('3'); // dernière réplique consommée -> quête "nouvelle" -> écran d'offre, PAS masqué
|
||||
assert.equal(boxWidget.classList.contains('is-active'), true);
|
||||
assert.equal(boxWidget._header.textContent, 'Quête : Retrouver Charlie');
|
||||
assert.equal(boxWidget._body.textContent, 'Parler à Mr. Pinguin');
|
||||
assert.match(boxWidget._footer.innerHTML, /Accepter/);
|
||||
|
||||
forgeDialogueBoxAcceptQuest(99, '3'); // clic "Accepter"
|
||||
assert.equal(forgeQuestById(99).status, 'en_cours');
|
||||
assert.equal(boxWidget.classList.contains('is-active'), false); // la modale disparaît
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
@@ -10,12 +10,14 @@ global.gameData = {};
|
||||
|
||||
const {
|
||||
forgeQuestById, forgeShowDialogueBox, forgeDialogueBoxAdvance,
|
||||
forgeDialogueBoxAcceptQuest, forgeDialogueBoxDeclineQuest,
|
||||
} = require('../dialogue-box-controller.js');
|
||||
|
||||
function fakeWidget(objectId) {
|
||||
const roles = {
|
||||
header: { textContent: '' },
|
||||
body: { textContent: '' },
|
||||
footer: { innerHTML: '' },
|
||||
};
|
||||
const classes = new Set();
|
||||
return {
|
||||
@@ -24,6 +26,7 @@ function fakeWidget(objectId) {
|
||||
querySelector: sel => {
|
||||
if (sel === '[data-dialogue-role="header"]') return roles.header;
|
||||
if (sel === '[data-dialogue-role="body"]') return roles.body;
|
||||
if (sel === '[data-dialogue-role="footer"]') return roles.footer;
|
||||
return null;
|
||||
},
|
||||
_roles: roles, _classes: classes,
|
||||
@@ -34,8 +37,9 @@ test.beforeEach(() => {
|
||||
global.window.currentScreenId = 1;
|
||||
global.gameData = {
|
||||
quests: [
|
||||
{ id: 5, status: 'nouvelle', dialogues: { nouvelle: [{ speaker: 'Le Sorcier', text: 'Bonjour !' }, { speaker: 'Joueur', text: 'Salut.' }], en_cours: [], terminee: [] } },
|
||||
{ id: 5, title: 'La quête du sorcier', status: 'nouvelle', objectif: 'Parler au sorcier', dialogues: { nouvelle: [{ speaker: 'Le Sorcier', text: 'Bonjour !' }, { speaker: 'Joueur', text: 'Salut.' }], en_cours: [], terminee: [] } },
|
||||
{ id: 6, status: 'en_cours', dialogues: { nouvelle: [{ speaker: 'PNJ', text: 'x' }], en_cours: [], terminee: [] } },
|
||||
{ id: 8, status: 'terminee', dialogues: { nouvelle: [], en_cours: [], terminee: [{ speaker: 'PNJ', text: 'Merci !' }] } },
|
||||
],
|
||||
};
|
||||
});
|
||||
@@ -53,11 +57,28 @@ test('forgeShowDialogueBox — quête introuvable : aucun effet (pas de plantage
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
test('forgeShowDialogueBox — quête sans réplique pour son statut actuel : aucun effet', () => {
|
||||
test('forgeShowDialogueBox — quête sans réplique pour son statut "en_cours" (pas "nouvelle") : aucun effet', () => {
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: () => { throw new Error('ne devrait jamais chercher de widget'); } };
|
||||
global.document = { querySelector: () => widget };
|
||||
try {
|
||||
assert.doesNotThrow(() => forgeShowDialogueBox(6)); // "en_cours" -> dialogues.en_cours est vide
|
||||
forgeShowDialogueBox(6); // "en_cours" -> dialogues.en_cours est vide, et statut != "nouvelle"
|
||||
assert.equal(widget._classes.has('is-active'), false);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
test('forgeShowDialogueBox — quête "nouvelle" SANS réplique : propose directement l\'offre (accepter/refuser)', () => {
|
||||
global.gameData.quests.push({ id: 7, title: 'Le trésor perdu', status: 'nouvelle', objectif: 'Trouver le trésor', dialogues: {} });
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: () => widget };
|
||||
try {
|
||||
forgeShowDialogueBox(7);
|
||||
assert.equal(widget._roles.header.textContent, 'Quête : Le trésor perdu');
|
||||
assert.equal(widget._roles.body.textContent, 'Trouver le trésor');
|
||||
assert.match(widget._roles.footer.innerHTML, /Accepter/);
|
||||
assert.match(widget._roles.footer.innerHTML, /Refuser/);
|
||||
assert.equal(widget._classes.has('is-active'), true);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
@@ -69,22 +90,59 @@ test('forgeShowDialogueBox — aucun widget posé sur l\'écran : aucun effet',
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
test('forgeShowDialogueBox puis forgeDialogueBoxAdvance — cycle toutes les répliques puis se masque', () => {
|
||||
test('forgeShowDialogueBox puis forgeDialogueBoxAdvance — cycle toutes les répliques puis se masque (quête pas "nouvelle")', () => {
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: sel => (sel.indexOf('data-object-id="42"') !== -1 ? widget : (sel.indexOf('dialogueBoxWidget') !== -1 ? widget : null)) };
|
||||
try {
|
||||
forgeShowDialogueBox(8); // statut "terminee" -> pas d'offre à la fin, se masque normalement
|
||||
assert.equal(widget._roles.header.textContent, 'PNJ');
|
||||
assert.equal(widget._roles.body.textContent, 'Merci !');
|
||||
assert.equal(widget._classes.has('is-active'), true);
|
||||
|
||||
forgeDialogueBoxAdvance(42); // dernière (et unique) réplique consommée -> se masque
|
||||
assert.equal(widget._classes.has('is-active'), false);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
test('quête "nouvelle" : le dialogue épuisé bascule sur l\'offre (Accepter/Refuser) au lieu de se masquer', () => {
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: sel => (sel.indexOf('data-object-id="42"') !== -1 ? widget : (sel.indexOf('dialogueBoxWidget') !== -1 ? widget : null)) };
|
||||
try {
|
||||
forgeShowDialogueBox(5);
|
||||
assert.equal(widget._roles.header.textContent, 'Le Sorcier');
|
||||
assert.equal(widget._roles.body.textContent, 'Bonjour !');
|
||||
assert.equal(widget._classes.has('is-active'), true);
|
||||
|
||||
forgeDialogueBoxAdvance(42);
|
||||
assert.equal(widget._roles.header.textContent, 'Joueur');
|
||||
assert.equal(widget._roles.body.textContent, 'Salut.');
|
||||
assert.equal(widget._classes.has('is-active'), true); // encore une réplique visible
|
||||
forgeDialogueBoxAdvance(42); // dernière réplique consommée -> écran d'offre, PAS masqué
|
||||
assert.equal(widget._classes.has('is-active'), true);
|
||||
assert.equal(widget._roles.header.textContent, 'Quête : La quête du sorcier');
|
||||
assert.equal(widget._roles.body.textContent, 'Parler au sorcier');
|
||||
assert.match(widget._roles.footer.innerHTML, /Accepter/);
|
||||
assert.match(widget._roles.footer.innerHTML, /Refuser/);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
forgeDialogueBoxAdvance(42); // dernière réplique consommée -> se masque
|
||||
test('forgeDialogueBoxAcceptQuest — fait passer la quête "en_cours" (en mémoire seulement) et masque la boîte', () => {
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: () => widget };
|
||||
try {
|
||||
forgeShowDialogueBox(5);
|
||||
widget._classes.add('is-active');
|
||||
forgeDialogueBoxAcceptQuest(5, 42);
|
||||
assert.equal(forgeQuestById(5).status, 'en_cours');
|
||||
assert.equal(widget._classes.has('is-active'), false);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
test('forgeDialogueBoxDeclineQuest — masque la boîte SANS changer le statut (reste "nouvelle")', () => {
|
||||
const widget = fakeWidget(42);
|
||||
const previousDoc = global.document;
|
||||
global.document = { querySelector: () => widget };
|
||||
try {
|
||||
forgeShowDialogueBox(5);
|
||||
widget._classes.add('is-active');
|
||||
forgeDialogueBoxDeclineQuest(42);
|
||||
assert.equal(forgeQuestById(5).status, 'nouvelle'); // inchangé -> le dialogue rejouera depuis le début
|
||||
assert.equal(widget._classes.has('is-active'), false);
|
||||
} finally { global.document = previousDoc; }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user