# Préparer un déplacement et un tir en Lua

Ces fonctions nécessitent une version de Jitsuri intégrant la recherche tactique avancée. Utilisez-les dans `onTurn`, avec le moteur de combat Lua. En équipe, elles concernent le personnage dont le tour est exécuté.

La liste de toutes les méthodes, le cycle des callbacks et les plans d'attaque natifs se trouvent dans [IA de combat avancée](ia-combat-avancee.md).

| Fonction | Résultat |
| --- | --- |
| `fightAction:getSpellApCost(spellId)` | Coût en PA du niveau de sort connu ; `0` si le sort n'est pas disponible dans les données. Ne pas interpréter ce dernier cas comme un sort gratuit. |
| `fightAction:getTacticalCells(requiredAp, reserveMp)` | Liste Lua de positions candidates en conservant les PA demandés et une réserve de PM, selon le calcul de tacle. La position actuelle peut être incluse même si aucun déplacement n'est possible. |
| `fightAction:getSpellZoneFromCell(spellId, fromCell, targetCell)` | Liste Lua des cellules de la zone d'effets depuis une position hypothétique, sans déplacer le personnage. |
| `fightAction:getSpellSelfRangePenalty(spellId)` | Estimation prudente du malus de portée sur soi : effets 116 ciblant `C`, pire cas normal/critique du niveau connu. Ne simule pas tous les effets d'un boost. |
| `fightAction:getSpellCellsWithRangeOffset(spellId, fromCell, offset)` | Cellules possibles avec une variation hypothétique de la caractéristique portée, bornée à −100/+100. Respecte les sorts à portée non modifiable ; ne dépense aucun PA et ne lance rien. |
| `fightAction:canCastSpellInSimulation(fromCell, spellId, targetCell, occupiedCells)` | Booléen : contrôle de disponibilité, de PA et de géométrie avec une liste Lua de toutes les cellules occupées simulées. N'envoie aucun sort et ne modifie aucun combattant. |

`getTacticalCells` autorise le recul ainsi qu'une approche contrôlée vers une position de tir. L'approche doit conserver, sur tout le chemin, une distance au moins égale au minimum entre la distance initiale et 6 cases, sans entrer dans une nouvelle zone de tacle. Les contrôles de repli et de pertes supplémentaires au tacle restent conservés. C'est une recherche prudente, pas une liste exhaustive de tous les déplacements que le serveur pourrait autoriser. Cette API ne limite pas automatiquement les déplacements selon les challenges.

Les entités de `fightAction:getAllEntities()` exposent `ActionPoints` et `MovementPoints`, ou `nil` si les caractéristiques ne sont pas connues. Le contrôle `canCastSpellOnCell` et l'envoi avancé vérifient les PA disponibles en plus des restrictions de lancer.

## Exemple générique

### Challenges et déplacement d'un pas

| Fonction | Résultat |
| --- | --- |
| `fightAction:getActiveChallengeIds()` | Liste Lua des identifiants reçus comme actifs pour le combattant courant. Les propositions ne sont pas incluses. Un résultat de réussite ou d'échec retire l'identifiant. |
| `fightAction:areChallengesKnown()` | `true` après réception de la liste complète du combat. Avant cela, des ajouts peuvent déjà apparaître dans `getActiveChallengeIds()` ; une liste vide ne prouve donc pas l'absence de challenge. |
| `fightAction:moveOneCell(cellId)` | Demande un déplacement dont le chemin contient exactement un pas. Ne contourne pas un obstacle par un chemin plus long. Vérifier ensuite la position réelle ; `false` ne garantit pas qu'aucun mouvement n'a été effectué si sa confirmation a été perdue. |

Ces fonctions concernent le personnage dont le tour Lua est exécuté, y compris lorsqu'un seul trajet pilote une équipe. Le suivi est remis à zéro au changement de combat. Elles ne sélectionnent pas de challenge et n'imposent aucune stratégie automatiquement.

Pour une règle limitant le déplacement, le script doit compter ses actions par personnage et par tour, contrôler **tous** ses chemins de déplacement et éviter de renvoyer une demande après une confirmation incertaine. Déplacer un pas avant une attaque potentiellement finale évite de terminer volontairement le combat avant d'avoir effectué ce déplacement. Les pertes au tacle, les effets différés et les actions des autres combattants nécessitent également une stratégie adaptée.

### Abri et fuite au tacle

| Fonction | Résultat |
| --- | --- |
| `fightAction:getTerrainLineOfSight(fromCell, toCell)` | `1` si la ligne de vue traverse le terrain, `0` si un obstacle la bloque, `-1` si la carte ou les cellules ne sont pas exploitables. Ignore volontairement les combattants comme obstacles : un personnage mobile n'est pas un abri durable. |
| `fightAction:getSafeMovementCells()` | Cellules accessibles avec le budget estimé après le tacle initial, selon la politique de tacle configurée. Écarte les chemins traversant ou terminant dans une nouvelle zone de tacle avec pertes estimées. Ne vérifie pas les challenges, les glyphes ni les lignes de vue ennemies. |
| `fightAction:moveToCellSafely(cellId)` | Revalide les mêmes contraintes au moment de l'envoi. Retourne la confirmation du déplacement ; relisez aussi la cellule réellement atteinte. |

Une ligne de vue bloquée depuis la position actuelle d'un ennemi ne garantit pas qu'il ne puisse pas la rouvrir en se déplaçant. Ces fonctions ne prédisent ni ses sorts ni ses déplacements.

Après une arrivée partielle, une perte inattendue de PA ou une confirmation incertaine, ne relancez pas aveuglément une fuite. Conservez un verrou pour le tour et réévaluez les sorts disponibles depuis la position réellement observée. Une fuite au contact peut déjà avoir consommé des PA/PM même si la destination n'a pas été atteinte.

### Exemple de recherche

Les identifiants ci-dessous sont fictifs et doivent être remplacés par ceux de votre stratégie.

```lua
local SORT = 123456 -- fictif
local cible = 250  -- exemple seulement : lire la position réelle de la cible
local cout = fightAction:getSpellApCost(SORT)
if cout > 0 then
    for _, position in ipairs(fightAction:getTacticalCells(cout, 1)) do
        if fightAction:canCastSpellOnCell(position, SORT, cible) == 0 then
            local zone = fightAction:getSpellZoneFromCell(SORT, position, cible)
            -- Comparer les positions et les zones ; vérifier les alliés.
            -- Ce calcul seul ne déplace pas et ne lance pas le sort.
        end
    end
end
```

Après chaque déplacement ou sort, relisez les positions, les états et les PA/PM. Revalidez le prochain lancer depuis la position réellement atteinte. Une simulation de poussée ne garantit ni le déplacement du monstre ni les dégâts : collisions, immunités, morts et événements serveur peuvent changer le résultat.

La simulation remplace seulement l'occupation géométrique : elle ne simule pas les états, les dégâts ni l'historique futur des lancers par cible. Gardez des limites de calcul et revalidez toujours sur l'état réel avant d'agir.
