Éditeur de quêtes : onglet à côté de Collision, pas une page à part
Corrige la mauvaise interprétation de la demande initiale : la page dédiée "🗺️ Quêtes" (nav du jeu) est retirée au profit d'un nouvel onglet "🗺️ Quêtes" DANS l'éditeur de scène 2D, juste à côté de "🧩 Collision" — même liste tabulaire que l'onglet "📣 Événements" (titre/objectif/récompense/statut/résultat par ligne, pas des cartes), un clic sur une ligne ouvrant la modale d'arbre de dialogue déjà construite. L'assistant "+ Action" de l'éditeur de collision ouvre toujours cette même modale pour "Déclencher une quête". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
51427b5297
commit
78c1aba8a0
@@ -1,56 +1,63 @@
|
||||
// ---------- Éditeur de quêtes ----------
|
||||
// Module partagé entre templates/quests.html (page dédiée : liste de
|
||||
// cartes + "+ Nouvelle quête") et static/js/scenes/collision-rules-editor.js
|
||||
// (l'assistant "+ Action" de l'éditeur de collision ouvre CETTE MÊME
|
||||
// modale de dialogue quand l'auteur choisit "Déclencher une quête",
|
||||
// plutôt qu'un simple champ texte pour un id). La modale (arbre de
|
||||
// dialogue) est créée dynamiquement (ensureQuestDialogueModal) plutôt que
|
||||
// dupliquée dans deux templates.
|
||||
// Onglet "🗺️ Quêtes" à côté de "🧩 Collision" (templates/scene_edit.html,
|
||||
// jamais une page à part) : une LISTE (mêmes tableau/lignes que l'onglet
|
||||
// "📣 Événements") plutôt qu'une grille de cartes, chaque ligne portant
|
||||
// toutes les infos de la quête + un clic dessus ouvrant la modale
|
||||
// d'arbre de dialogue. Cette même modale est aussi ouverte depuis
|
||||
// l'assistant "+ Action" de l'éditeur de collision (voir
|
||||
// static/js/scenes/collision-rules-editor.js) quand l'auteur choisit
|
||||
// "Déclencher une quête" — créée dynamiquement (ensureQuestDialogueModal)
|
||||
// plutôt que dupliquée dans deux endroits.
|
||||
//
|
||||
// Une quête (voir db/quests/) : id, title, objectif, recompense_score,
|
||||
// status (nouvelle/en_cours/terminee — statut COURANT, informatif),
|
||||
// resultat (reussi/echoue), dialogues (une liste de répliques PAR
|
||||
// STATUT — 3 colonnes dans l'éditeur : quel dialogue jouer selon l'état
|
||||
// STATUT — 3 colonnes dans la modale : quel dialogue jouer selon l'état
|
||||
// de la quête au moment où le joueur parle au PNJ).
|
||||
|
||||
var QUEST_STATUS_LABELS = { nouvelle: 'Nouvelle', en_cours: 'En cours', terminee: 'Terminée' };
|
||||
var QUEST_RESULT_LABELS = { reussi: '✅ Réussi', echoue: '❌ Échoué' };
|
||||
var QUEST_STATUS_ORDER = ['nouvelle', 'en_cours', 'terminee'];
|
||||
|
||||
// Quêtes connues côté client — préremplies par QUESTS_JSON (quests.html)
|
||||
// si présent, sinon vide tant qu'on n'a pas encore fetch (contexte
|
||||
// éditeur de collision, voir questPickerEnsureLoaded).
|
||||
// Quêtes connues côté client — préremplies par QUESTS_JSON (voir
|
||||
// templates/scene_edit.html), toujours présentes puisque l'onglet
|
||||
// Quêtes vit dans le même éditeur que l'onglet Collision qui en a
|
||||
// besoin (collisionRuleActionSummary).
|
||||
var QUESTS = (typeof QUESTS_JSON !== 'undefined') ? QUESTS_JSON : [];
|
||||
|
||||
function questById(questId) {
|
||||
return QUESTS.find(function (q) { return q.id === questId; });
|
||||
}
|
||||
|
||||
function questCardHtml(quest, i) {
|
||||
var resultatHtml = quest.resultat ? '<span class="questPill questPill-resultat">' + QUEST_RESULT_LABELS[quest.resultat] + '</span>' : '';
|
||||
function questListRowHtml(quest) {
|
||||
var resultatHtml = quest.resultat ? '<span class="questPill questPill-resultat">' + QUEST_RESULT_LABELS[quest.resultat] + '</span>' : '<span class="hint">—</span>';
|
||||
return (
|
||||
'<div class="questCard" style="animation-delay:' + (i * 50) + 'ms" onclick="openQuestDialogueModal(' + quest.id + ')">' +
|
||||
'<div class="questCardHeader">' +
|
||||
'<h3 class="questCardTitle">' + (quest.title || '').replace(/</g, '<') + '</h3>' +
|
||||
'<span class="questPill questPill-status questPill-status-' + quest.status + '">' + QUEST_STATUS_LABELS[quest.status] + '</span>' +
|
||||
'</div>' +
|
||||
'<p class="questCardObjectif">' + (quest.objectif ? quest.objectif.replace(/</g, '<') : '<span class="hint">Aucun objectif défini.</span>') + '</p>' +
|
||||
'<div class="questCardFooter">' +
|
||||
'<span class="questPill questPill-score">🏆 +' + (quest.recompense_score || 0) + '</span>' +
|
||||
resultatHtml +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
'<tr class="questListRow" onclick="openQuestDialogueModal(' + quest.id + ')">' +
|
||||
'<td><strong>' + (quest.title || '').replace(/</g, '<') + '</strong></td>' +
|
||||
'<td>' + (quest.objectif ? quest.objectif.replace(/</g, '<') : '<span class="hint">—</span>') + '</td>' +
|
||||
'<td><span class="questPill questPill-score">🏆 +' + (quest.recompense_score || 0) + '</span></td>' +
|
||||
'<td><span class="questPill questPill-status questPill-status-' + quest.status + '">' + QUEST_STATUS_LABELS[quest.status] + '</span></td>' +
|
||||
'<td>' + resultatHtml + '</td>' +
|
||||
'<td class="fieldsTableActions">' +
|
||||
'<button type="button" class="button is-small is-danger" title="Supprimer" onclick="event.stopPropagation(); questDeleteFromList(' + quest.id + ')">🗑️</button>' +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
}
|
||||
|
||||
function renderQuestListPage() {
|
||||
var container = document.getElementById('questsGrid');
|
||||
var container = document.getElementById('questsTabPanel');
|
||||
if (!container) return;
|
||||
var wrap = document.getElementById('questsListWrap');
|
||||
if (!QUESTS.length) {
|
||||
container.innerHTML = '<p class="hint">Aucune quête pour l\'instant — crée-en une avec le bouton ci-dessus.</p>';
|
||||
wrap.innerHTML = '<p class="hint">Aucune quête pour l\'instant — crée-en une avec le bouton ci-dessus.</p>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = QUESTS.map(questCardHtml).join('');
|
||||
wrap.innerHTML =
|
||||
'<div class="fieldsTableWrap"><table class="table is-fullwidth is-hoverable fieldsTable">' +
|
||||
'<thead><tr><th>Titre</th><th>Objectif</th><th>Récompense</th><th>Statut</th><th>Résultat</th><th></th></tr></thead>' +
|
||||
'<tbody>' + QUESTS.map(questListRowHtml).join('') + '</tbody>' +
|
||||
'</table></div>';
|
||||
}
|
||||
|
||||
function createQuestFromListPage() {
|
||||
@@ -65,6 +72,16 @@ function createQuestFromListPage() {
|
||||
});
|
||||
}
|
||||
|
||||
function questDeleteFromList(questId) {
|
||||
var quest = questById(questId);
|
||||
if (!quest || !confirm('Supprimer la quête « ' + quest.title + ' » ? Toute règle de collision qui la référence perdra sa cible.')) return;
|
||||
fetch('/game/' + GAME_SLUG + '/quests/' + questId + '/delete', { method: 'POST' })
|
||||
.then(function () {
|
||||
QUESTS = QUESTS.filter(function (q) { return q.id !== questId; });
|
||||
renderQuestListPage();
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Modale "arbre de dialogue" ----------
|
||||
// Créée une seule fois (au premier appel), réutilisée ensuite — partagée
|
||||
// entre la page dédiée et l'assistant "+ Action" de l'éditeur de
|
||||
|
||||
Reference in New Issue
Block a user