import random from typing import Any def compute_new_value(operation: str, current: Any, raw_value: Any, is_decimal: bool) -> Any: """Calcule la nouvelle valeur d'un champ d'objet ou d'une variable globale pour une opération de DATA_OPERATIONS (voir game_engine/labels/data_operations.py) — logique partagée par apply_data_action.py (champ d'objet) et apply_variable_action.py (variable globale), qui ne diffèrent que par l'origine de `current` et la façon dont le résultat est persisté. `is_decimal` : True pour un champ/une variable de type "nombre_decimal" (résultat numérique gardé en flottant), False sinon (résultat toujours entier). Lève ValueError si `operation` n'est pas reconnue — à l'appelant de la traduire en son propre échec (ex. `return False`).""" if operation == "definir_texte": return raw_value or "" if operation == "definir_nombre": try: return float(raw_value) if is_decimal else int(float(raw_value)) except (TypeError, ValueError): return 0 if operation == "definir_bool_vrai": return 1 if operation == "definir_bool_faux": return 0 if operation == "basculer_bool": return 0 if current else 1 if operation in ("incrementer", "decrementer"): try: amount = float(raw_value) if raw_value not in (None, "") else 0 except (TypeError, ValueError): amount = 0 if operation == "decrementer": amount = -amount result = (current or 0) + amount return result if is_decimal else int(result) # Phase 2 — hasard + opérations mathématiques. "multiplier"/"diviser"/ # "modulo" reprennent le même patron qu'incrementer/decrementer (un # seul opérande numérique dans raw_value) ; "minimum"/"maximum" # bornent la valeur ACTUELLE (utile pour une variable globale, qui n'a # pas de min_value/max_value comme un champ d'objet, voir # _clamp_to_field_bounds dans apply_data_action.py) ; "alea" est la # seule opération à deux opérandes ("min,max" dans raw_value plutôt # qu'une nouvelle colonne de nœud — voir updateDataValuePlaceholder() # dans static/game/js/screen_edit/flow-editor.js). if operation == "multiplier": try: factor = float(raw_value) if raw_value not in (None, "") else 1 except (TypeError, ValueError): factor = 1 result = (current or 0) * factor return result if is_decimal else int(result) if operation in ("diviser", "modulo"): try: divisor = float(raw_value) if raw_value not in (None, "") else 0 except (TypeError, ValueError): divisor = 0 if divisor == 0: # Garde-fou division par zéro : valeur inchangée plutôt qu'une # ZeroDivisionError qui interromprait tout le graphe de logique. return current result = (current or 0) / divisor if operation == "diviser" else (current or 0) % divisor return result if is_decimal else int(result) if operation in ("minimum", "maximum"): try: bound = float(raw_value) except (TypeError, ValueError): return current result = max(current or 0, bound) if operation == "minimum" else min(current or 0, bound) return result if is_decimal else int(result) if operation == "alea": parts = (raw_value or "").split(",") try: lo, hi = float(parts[0]), float(parts[1]) except (IndexError, TypeError, ValueError): lo, hi = 0, 0 if lo > hi: lo, hi = hi, lo if is_decimal: return random.uniform(lo, hi) # nosec B311 # noqa: S311 - tirage aleatoire de jeu, jamais crypto # NOSONAR S2245 return random.randint(int(lo), int(hi)) # nosec B311 # noqa: S311 - tirage aleatoire de jeu, jamais crypto # NOSONAR S2245 raise ValueError(f"opération inconnue : {operation!r}")