Ajoute la condition de visibilité et les variables globales
Nouveau panneau "Condition de visibilité" disponible dans les propriétés
de TOUT élément (widget) : permet de masquer un élément en mode jouable
selon deux moyens, au choix -
- une variable globale (nom + type + valeur, une seule par jeu, stockée
dans une nouvelle table _global_variables) ;
- le champ d'un objet de données existant (même convention "état de
partie" - une seule ligne - déjà utilisée par la Jauge).
Une variable ne servant à rien si elle ne peut jamais changer en cours de
partie, ajoute aussi une nouvelle action de flow "Modifier une variable
globale" (parallèle à "Modifier une donnée"), avec sa propre route
d'exécution serveur et son sous-formulaire dans l'éditeur de logique de
scène. Une variable peut aussi se créer à la volée depuis le sélecteur du
panneau de visibilité, sans quitter les propriétés de l'élément.
La condition n'est évaluée qu'en mode jouable (/game/<slug>/play), jamais
dans l'éditeur, pour que l'élément reste toujours sélectionnable. Un
élément masqué se réévalue en direct après toute action "Modifier une
donnée/variable", via le même mécanisme de rafraîchissement déjà utilisé
par la Jauge et le Répéteur.
Corrige au passage deux bugs découverts en testant bout en bout : (1)
apply_ctx plantait sur le nouveau marqueur interne _forge_play_mode (un
booléen parmi les {{champ}} à substituer, qui attend des chaînes) ; (2)
_compare traitait toute valeur booléenne stockée en chaîne ("0" inclus,
donc toujours vraie en Python) comme vraie - correct pour les champs
d'objet (entiers SQLite) mais faux pour les variables globales (toujours
stockées en texte).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3c6d07cb3a
commit
c04bc0b926
@@ -343,7 +343,11 @@
|
||||
{% set ns.last_group = c.group %}
|
||||
{% set ns.started = True %}
|
||||
{% endif %}
|
||||
<div class="field controlRow">
|
||||
<div class="field controlRow{{ ' visCondRow' if c.key.startswith('visibilite_cond_') and c.key != 'visibilite_cond_mode' else '' }}"
|
||||
{% if c.key == 'visibilite_cond_variable' %}data-vis-cond-scope="variable"
|
||||
{% elif c.key in ('visibilite_cond_definition_id', 'visibilite_cond_champ') %}data-vis-cond-scope="objet"
|
||||
{% elif c.key in ('visibilite_cond_operateur', 'visibilite_cond_valeur') %}data-vis-cond-scope="any"
|
||||
{% endif %}>
|
||||
{% if c.type in ('checkbox', 'toggle_token') %}
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
@@ -437,13 +441,16 @@
|
||||
{% elif c.type in ('select', 'preset') %}
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select name="ctrl_{{ c.key }}" id="field-{{ c.key }}">
|
||||
<select name="ctrl_{{ c.key }}" id="field-{{ c.key }}" {% if c.key == 'visibilite_cond_mode' %}onchange="toggleVisCondFields(this)"{% endif %}>
|
||||
{% for opt in c.options %}
|
||||
<option value="{{ opt[0] }}" {{ 'selected' if c.value == opt[0] else '' }}>{{ opt[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% if c.key == 'visibilite_cond_variable' %}
|
||||
<button type="button" class="button is-small is-fullwidth" style="margin-top:6px;" onclick="createVisCondVariable()">+ Créer une nouvelle variable</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -618,6 +625,24 @@
|
||||
<input type="text" id="nf-action-data-value" style="margin-top:6px;" placeholder="Valeur / montant">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="nf-action-variable-fields" style="display:none;">
|
||||
{% if not global_variables %}
|
||||
<p class="hint">Aucune variable globale pour l'instant — crées-en une depuis "Condition de visibilité" dans les propriétés d'un élément.</p>
|
||||
{% else %}
|
||||
<select id="nf-action-variable-name">
|
||||
{% for v in global_variables %}
|
||||
<option value="{{ v.name }}">{{ v.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select id="nf-action-variable-operation" style="margin-top:6px;">
|
||||
{% for key, label in data_operations %}
|
||||
<option value="{{ key }}">{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="text" id="nf-action-variable-value" style="margin-top:6px;" placeholder="Valeur / montant">
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actionsRow"><button type="button" class="smallBtn button is-small is-primary" onclick="submitNodeForm()">+ Ajouter ce nœud</button></div>
|
||||
@@ -1448,7 +1473,10 @@ function initBuilderPanel() {
|
||||
bindJaugeDefinitionSelect();
|
||||
bindDefinitionFieldSelects('field-definition_id', ['field-filtre_champ', 'field-filtre2_champ']);
|
||||
bindDefinitionFieldSelects('field-data_definition_id', ['field-data_filtre_champ', 'field-data_filtre2_champ']);
|
||||
bindDefinitionFieldSelects('field-visibilite_cond_definition_id', ['field-visibilite_cond_champ']);
|
||||
initFilterValuePickers();
|
||||
var visCondModeSel = document.getElementById('field-visibilite_cond_mode');
|
||||
if (visCondModeSel) toggleVisCondFields(visCondModeSel);
|
||||
restoreTreeCollapsedState();
|
||||
applySelectionHighlight(CURRENT_SELECTED_ID);
|
||||
restoreFloatPanelState('props', 'propsPanelFloat', 'propsPanelReopenBtn');
|
||||
@@ -1497,6 +1525,41 @@ function bindJaugeDefinitionSelect() {
|
||||
// généralisé pour s'appliquer à un nombre quelconque de listes "champ"
|
||||
// rattachées à un même sélecteur d'objet (ici : filtre_champ + filtre2_champ,
|
||||
// ou data_filtre_champ + data_filtre2_champ). ----------
|
||||
// ---------- Condition de visibilité (voir VISIBILITY_CONDITION_CONTROLS) :
|
||||
// n'affiche que les réglages pertinents pour le mode choisi (variable
|
||||
// globale, ou champ d'un objet) — les deux autres restent en base (peu
|
||||
// importe, seul le mode courant est lu par is_element_visible()) mais sont
|
||||
// masqués pour ne pas embrouiller. ----------
|
||||
function createVisCondVariable() {
|
||||
var name = window.prompt("Nom de la nouvelle variable globale (ex. niveau_debloque) :");
|
||||
if (!name || !name.trim()) return;
|
||||
var body = new URLSearchParams({ name: name.trim(), type: 'texte', default_value: '' });
|
||||
fetch("{{ url_for('create_global_var', slug=game.slug) }}", { method: 'POST', body: body })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (json) {
|
||||
if (!json.ok) { window.alert(json.error || "Échec de la création."); return; }
|
||||
var sel = document.getElementById('field-visibilite_cond_variable');
|
||||
if (!sel) return;
|
||||
if (!sel.querySelector('option[value="' + CSS.escape(json.name) + '"]')) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = json.name;
|
||||
opt.textContent = json.name;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
sel.value = json.name;
|
||||
submitPropsForm();
|
||||
});
|
||||
}
|
||||
|
||||
function toggleVisCondFields(sel) {
|
||||
var mode = sel.value;
|
||||
document.querySelectorAll('.visCondRow').forEach(function (row) {
|
||||
var scope = row.dataset.visCondScope;
|
||||
var show = scope === 'any' ? !!mode : scope === mode;
|
||||
row.style.display = show ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function bindDefinitionFieldSelects(defSelId, fieldSelIds) {
|
||||
var defSel = document.getElementById(defSelId);
|
||||
if (!defSel) return;
|
||||
@@ -1769,6 +1832,9 @@ function nodeLabel(node) {
|
||||
} else if (node.action_type === 'modifier_donnee') {
|
||||
base += ' → ' + (node.target_field || '?') + ' : ' + (DATA_OPERATION_LABELS_MAP[node.data_operation] || node.data_operation);
|
||||
if (node.data_value) base += ' ' + node.data_value;
|
||||
} else if (node.action_type === 'modifier_variable') {
|
||||
base += ' → ' + (node.target_variable || '?') + ' : ' + (DATA_OPERATION_LABELS_MAP[node.data_operation] || node.data_operation);
|
||||
if (node.data_value) base += ' ' + node.data_value;
|
||||
} else if (node.action_type === 'attendre') {
|
||||
base += ' (' + (node.data_value || '0') + ' s)';
|
||||
}
|
||||
@@ -2052,11 +2118,13 @@ function toggleFlowActionFields() {
|
||||
var screenSel = document.getElementById('nf-action-target-screen');
|
||||
var elementFields = document.getElementById('nf-action-element-fields');
|
||||
var dataFields = document.getElementById('nf-action-data-fields');
|
||||
var variableFields = document.getElementById('nf-action-variable-fields');
|
||||
var waitFields = document.getElementById('nf-action-wait-fields');
|
||||
var isOnglet = typeSel.value === 'activer_onglet';
|
||||
screenSel.style.display = (typeSel.value === 'aller_a' || typeSel.value === 'ouvrir_ligne') ? 'block' : 'none';
|
||||
elementFields.style.display = (typeSel.value === 'modifier_element' || isOnglet) ? 'block' : 'none';
|
||||
dataFields.style.display = typeSel.value === 'modifier_donnee' ? 'block' : 'none';
|
||||
variableFields.style.display = typeSel.value === 'modifier_variable' ? 'block' : 'none';
|
||||
waitFields.style.display = typeSel.value === 'attendre' ? 'block' : 'none';
|
||||
document.getElementById('nf-action-onglet-hint').style.display = isOnglet ? 'block' : 'none';
|
||||
document.getElementById('nf-action-element-property-wrap').style.display = isOnglet ? 'none' : 'block';
|
||||
@@ -2185,6 +2253,10 @@ function submitNodeForm() {
|
||||
payload.target_field = document.getElementById('nf-action-data-field').value;
|
||||
payload.data_operation = document.getElementById('nf-action-data-operation').value;
|
||||
payload.data_value = document.getElementById('nf-action-data-value').value;
|
||||
} else if (actionType === 'modifier_variable') {
|
||||
payload.target_variable = document.getElementById('nf-action-variable-name').value;
|
||||
payload.data_operation = document.getElementById('nf-action-variable-operation').value;
|
||||
payload.data_value = document.getElementById('nf-action-variable-value').value;
|
||||
} else if (actionType === 'attendre') {
|
||||
payload.data_value = document.getElementById('nf-action-wait-seconds').value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user