Quiz jouable : budget de récompense, boîte à quiz, widget score
- Récompense d'une quête = budget MAXIMUM pour ses questions : la somme des points des "❓ Question" ne peut plus dépasser recompense_score (routes/quests/quest_dialogues.py à l'enregistrement des dialogues, quest_update.py si on abaisse la récompense sous ce qui est déjà réparti) — message d'erreur explicite (400) dans les deux sens, affiché via alert() côté éditeur (quest-editor.js), jamais enregistré silencieusement dans un état incohérent. - Nombre de choix par question plafonné à 4 (au lieu de 8). - Deux nouveaux widgets "🖥️ Interface" (mêmes fondations que "💬 Boîte de dialogue" — screens/rendering/dialogue_box_style.py, même panneau "🎨 Style") : - "❓ Boîte à quiz" : affiche la question et ses choix, sans pied (une question se résout au clic sur un choix, pas de "Suivant"). - "🏆 Score" : affiche en continu les points gagnés, TOUJOURS visible une fois posé (contrairement aux boîtes de dialogue/quiz, masquées par défaut). - Moteur d'exécution (static/js/play/dialogue-box-controller.js, refonte) : une conversation de quête alterne maintenant répliques (boîte de dialogue) et questions (boîte à quiz) selon le type de chaque ligne. Bonne réponse -> crédite le score (compteur runtime dédié, jamais lié au score du moteur document) et avance ; mauvaise réponse -> rien, la question reste affichée pour réessayer ; aucune boîte à quiz posée -> la question est ignorée plutôt que de bloquer la conversation. Le dialogue "en_cours" épuisé (donc, s'il contenait des questions, toutes répondues) fait passer la quête "terminee" (en mémoire seulement, comme le reste de ce système — voir accepter/ refuser une offre de quête) : le joueur peut alors quitter la scène normalement, ces widgets n'étant jamais des obstacles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e972acf9e6
commit
b5546df044
@@ -262,7 +262,7 @@ function questQuestionBubbleHtml(line, status, index) {
|
||||
'<div class="questBubbleRow">' +
|
||||
'<div class="questBubble questBubble-question">' +
|
||||
'<div class="questBubbleHeader">' +
|
||||
'<label class="questQuestionHeaderField" title="Nombre de choix">🔢 <input type="number" min="2" max="8" value="' + line.choices.length + '" ' +
|
||||
'<label class="questQuestionHeaderField" title="Nombre de choix">🔢 <input type="number" min="2" max="4" value="' + line.choices.length + '" ' +
|
||||
'onchange="questSetQuestionChoiceCount(\'' + status + '\', ' + index + ', this.value)"></label>' +
|
||||
'<label class="questQuestionHeaderField" title="Récompense (points de score)">🏆 <input type="number" min="0" value="' + line.reward_amount + '" ' +
|
||||
'onchange="questSetQuestionRewardAmount(\'' + status + '\', ' + index + ', this.value)"> pts</label>' +
|
||||
@@ -362,10 +362,15 @@ function questUpdateField(questId, field, value) {
|
||||
fetch('/game/' + GAME_SLUG + '/quests/' + questId, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (json) {
|
||||
if (!json.quest) return;
|
||||
Object.assign(quest, json.quest);
|
||||
.then(function (r) { return r.json().then(function (json) { return { ok: r.ok, json: json }; }); })
|
||||
.then(function (res) {
|
||||
// Refusé (400) : recompense_score abaissé sous le total déjà
|
||||
// réparti entre les questions (voir routes/quests/quest_update.py)
|
||||
// — message d'erreur demandé explicitement, et le champ est
|
||||
// remis à sa valeur RÉELLE (inchangée côté serveur).
|
||||
if (!res.ok) { alert(res.json.error || "Impossible d'enregistrer."); renderQuestFieldsModalBody(questId); return; }
|
||||
if (!res.json.quest) return;
|
||||
Object.assign(quest, res.json.quest);
|
||||
renderQuestListPage();
|
||||
renderQuestFieldsModalBody(questId); // seule modale à porter des champs éditables
|
||||
});
|
||||
@@ -385,7 +390,15 @@ function questUpdateField(questId, field, value) {
|
||||
function questSaveDialogues(quest) {
|
||||
fetch('/game/' + GAME_SLUG + '/quests/' + quest.id + '/dialogues', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ dialogues: quest.dialogues }),
|
||||
});
|
||||
})
|
||||
.then(function (r) {
|
||||
// Refusé (400) : la somme des récompenses des questions dépasse la
|
||||
// récompense totale de la quête (voir routes/quests/quest_dialogues.py)
|
||||
// — message d'erreur demandé explicitement. La bulle en trop reste
|
||||
// affichée localement (non enregistrée) : l'auteur baisse son
|
||||
// montant ou celui d'une autre question pour repasser sous le budget.
|
||||
if (!r.ok) return r.json().then(function (json) { alert(json.error || "Impossible d'enregistrer."); });
|
||||
});
|
||||
}
|
||||
|
||||
// Alterne "Joueur" <-> le premier personnage nommé (ou "PNJ" faute de
|
||||
@@ -437,7 +450,7 @@ function questSetQuestionChoiceCount(status, index, value) {
|
||||
var quest = questById(_currentQuestModalId());
|
||||
if (!quest) return;
|
||||
var line = quest.dialogues[status][index];
|
||||
var count = Math.max(2, Math.min(8, parseInt(value, 10) || 2));
|
||||
var count = Math.max(2, Math.min(4, parseInt(value, 10) || 2));
|
||||
while (line.choices.length < count) line.choices.push('');
|
||||
while (line.choices.length > count) line.choices.pop();
|
||||
if (line.correct_index >= line.choices.length) line.correct_index = 0;
|
||||
|
||||
Reference in New Issue
Block a user