🤖 Structure IA combat

L'IA de combat est un script Lua indépendant qui s'exécute pendant les combats. Il gère le placement, les déplacements, les sorts et la logique de tour.

IA combat vs Script de déplacement

OnlyBot utilise deux types de scripts Lua distincts :

Type Fichier RĂ´le Fonctions disponibles
Script de déplacement Script principal Gère les déplacements, récoltes et déclenchement des combats map_*, npc_*, storage_*, global_*…
Script IA combat Script IA (upload séparé) Gère le déroulement du combat tour par tour fight_* uniquement
Sandbox : Le script IA est isolé — les fonctions io, os, require, loadfile, coroutine et toute introspection système sont désactivées pour des raisons de sécurité.

Structure obligatoire — main() et fight_placement()

Un script IA doit exposer deux fonctions Lua nommées exactement ainsi. Le moteur C# les recherche par nom et les appelle automatiquement.

Fonction Obligatoire RĂ´le
function main() Oui Contient toute la logique de combat. Si elle n'existe pas, l'IA est abandonnée avec une erreur.
function fight_placement(possiblePos, availablePos) Non Gère le placement avant le début du combat. Si absente, le placement par défaut du système est utilisé.
Attention au nom exact : les fonctions doivent s'appeler main et fight_placement — toute autre orthographe est ignorée par le moteur.

Squelette minimal d'un script IA :

-- Optionnel : placement avant le combat function fight_placement(possiblePos, availablePos) -- possiblePos : table des cellules de votre camp (indexées depuis 0) -- availablePos : table des cellules encore libres -- Retourner l'ID d'une cellule pour s'y placer automatiquement -- Retourner nil ou -1 pour gérer le placement manuellement avec fight_PlaceOnCell() return availablePos[0] end -- Obligatoire : toute la logique de combat va ici function main() while fight_GetFightStatus() == "ACTIVE" do -- Attendre son tour while not fight_IsMyTurn() do fight_Delay(200) end if fight_GetFightStatus() ~= "ACTIVE" then break end -- ============================ -- Vos actions ici -- ============================ fight_PassTurn() end end
Toujours terminer par fight_PassTurn() — sans cela, le bot reste bloqué indéfiniment à attendre la fin de son tour.

Phase de placement — fight_placement()

La fonction fight_placement reçoit deux tables de cellules et doit retourner l'ID de la cellule souhaitée, ou nil / -1 pour gérer le placement manuellement avec fight_PlaceOnCell().

function fight_placement(possiblePos, availablePos) -- availablePos contient les cellules libres de votre camp (index 0, 1, 2…) -- Exemple : prendre la première cellule disponible local cell = availablePos[0] if cell and cell > 0 then return cell -- Le moteur appelle fight_PlaceOnCell(cell) automatiquement end return nil -- Placement manuel : appeler fight_PlaceOnCell() + fight_Ready() soi-même end

Fonctions essentielles

État du combat et du personnage

Fonction Retour Description
fight_GetFightStatus() string "ACTIVE" si le combat est en cours, autre valeur sinon.
fight_IsMyTurn() bool Retourne true si c'est actuellement votre tour.
fight_GetPA() int Points d'Action restants ce tour.
fight_GetPM() int Points de Mouvement restants ce tour.
fight_GetLife() int Points de vie actuels de votre personnage.
fight_GetMyCell() int ID de la cellule oĂą se trouve votre personnage.
fight_GetTurnCount() int Numéro du tour actuel (commence à 1).
fight_PassTurn() — Passe le tour. À appeler obligatoirement à la fin de chaque tour.
fight_GetCurrentFighter() table Table complète de votre personnage : AP, MP, cellId, lifePoints, maxLifePoints, range.

Informations sur les ennemis et alliés

Fonction Retour Description
fight_GetAllEnnemyFighter() table Table de tous les ennemis vivants avec leur id, cellId. Itérable avec pairs().
fight_GetAllAllyFighter() table Table de tous les alliés vivants.
fight_GetClosestEnemyCell() int Cellule de l'ennemi le plus proche (distance Manhattan).
fight_GetFarthestEnemyCell() int Cellule de l'ennemi le plus loin.
fight_GetWeakestEnemyCell() int Cellule de l'ennemi avec le moins de vie.
fight_GetStrongestEnemyCell() int Cellule de l'ennemi avec le plus de vie.
fight_GetFighterLife(fighterId) int Points de vie actuels du combattant ciblé.
fight_GetFighterCell(fighterId) int Cellule actuelle du combattant ciblé.
fight_IsEnemy(fighterId) bool Retourne true si ce combattant est un ennemi.
fight_IsAlly(fighterId) bool Retourne true si ce combattant est un allié.
fight_GetEnemyLife(cellId) int Points de vie de l'ennemi sur la cellule donnée.

Déplacement

Fonction Retour Description
fight_MoveToCell(cellId) bool Déplace le personnage vers la cellule cible. Retourne true si le déplacement a réussi.
fight_MoveToClosestEnemy() bool Se déplace automatiquement vers l'ennemi le plus proche en utilisant les PM disponibles.
fight_GetDistanceToEnemy(cellId) int Distance (en cases) entre votre personnage et la cellule cible.
fight_GetDistanceToFighter(fighterId) int Distance entre votre personnage et un combattant donné.
fight_GetDistanceBetweenCells(cell1, cell2) int Distance entre deux cellules.
fight_FindBestCellToHit(targetCell, spellName) table Retourne { bestCell, pmCost } — la meilleure cellule depuis laquelle frapper la cible avec le sort donné.
fight_FindSafeCell(priority) int Retourne un ID de cellule sûre (éloignée des ennemis). priority : "far" ou "safe".

Sorts

Fonction Retour Description
fight_CastSpell(spellName, cellId) — Lance le sort par son nom (ex: "Stase") sur la cellule cible.
fight_CastSpellById(spellId, cellId) — Lance le sort par son ID numérique sur la cellule cible.
fight_CanCastSpell(spellName, cellId) bool Vérifie si le sort peut être lancé sur cette cellule (PA suffisants, portée, ligne de vue).
fight_GetSpellAPCost(spellId) int Coût en PA du sort.
fight_GetSpellRange(spellName) int Portée maximale du sort.
fight_GetSpellCost(spellName) int Coût en PA du sort (par nom).
fight_GetSpellInfo(spellId) table Retourne les informations complètes du sort : apCost, minRange, maxRange, lineOfSight, maxCastPerTurn.

Cellules et ligne de vue

Fonction Retour Description
fight_HasLineOfSight(cell1, cell2) bool Vérifie si une ligne de vue existe entre deux cellules.
fight_IsCellOccupied(cellId) bool Retourne true si la cellule est occupée par un combattant.
fight_IsCellWalkable(cellId) bool Retourne true si la cellule est accessible.
fight_IsCellAligned(cell1, cell2) bool Retourne true si les deux cellules sont alignées (même ligne/colonne).
fight_GetAdjacentCells(cellId) table Retourne les cellules adjacentes à la cellule donnée.
fight_GetFighterIdOnCell(cellId) string ID du combattant présent sur cette cellule, ou nil.

Utilitaires

Fonction Paramètres Description
fight_SendLogs(message, color) string, string Envoie un message dans la console. Couleurs : "Green", "Red", "Cyan", "Orange", "Yellow", "White"…
fight_Delay(ms) int Attend le nombre de millisecondes indiqué. Utiliser entre les actions pour éviter les problèmes de timing.
fight_GiveUp() — Abandonne le combat.
fight_GetMyCharacterId() — Retourne l'ID du personnage contrôlé par ce script.

Exemple 1 — Approche mêlée avec Stase

Ce script illustre une IA offensive qui se rapproche de l'ennemi et enchaîne les sorts de mêlée. Le sort utilisé est Stase (ID 12728) : coût 3 PA, portée 1–5 cases, nécessite une ligne de vue, max 3 lancers par tour.

Sort ID PA Portée Ligne de vue Max/tour
Stase 12728 3 1–5 Oui 3
-- ============================================================ -- IA Mêlée — Approche et Stase -- Sort principal : Stase (ID 12728) — 3 PA, portée 1-5, LoS requise -- ============================================================ local SORT_STASE = "Stase" local SORT_STASE_PA = 3 local SORT_STASE_RANGE = 5 -- Placement optionnel : prendre la première cellule disponible function fight_placement(possiblePos, availablePos) return availablePos[0] end -- Obligatoire : logique de combat function main() while fight_GetFightStatus() == "ACTIVE" do while not fight_IsMyTurn() do fight_Delay(200) end if fight_GetFightStatus() ~= "ACTIVE" then break end local pa = fight_GetPA() local pm = fight_GetPM() local target = fight_GetWeakestEnemyCell() fight_SendLogs("Tour | PA:" .. pa .. " PM:" .. pm .. " -> cible cellule " .. target, "Cyan") if target == 0 then fight_SendLogs("Aucun ennemi, passage du tour", "Yellow") fight_PassTurn() goto continue end -- Se rapprocher si hors de portée local dist = fight_GetDistanceToEnemy(target) if dist > SORT_STASE_RANGE and pm > 0 then fight_SendLogs("Distance " .. dist .. " > portée, approche...", "Yellow") fight_MoveToClosestEnemy() fight_Delay(600) pa = fight_GetPA() dist = fight_GetDistanceToEnemy(target) end -- Lancer Stase jusqu'à épuisement des PA ou du quota de 3 local castCount = 0 while pa >= SORT_STASE_PA and castCount < 3 do if fight_CanCastSpell(SORT_STASE, target) then fight_SendLogs("Lancement de Stase sur cellule " .. target, "Green") fight_CastSpell(SORT_STASE, target) fight_Delay(800) pa = fight_GetPA() castCount = castCount + 1 local newTarget = fight_GetWeakestEnemyCell() if newTarget ~= 0 and newTarget ~= target then fight_SendLogs("Nouvelle cible : " .. newTarget, "Orange") target = newTarget end else fight_SendLogs("Sort impossible sur la cible, arrêt", "Red") break end end fight_SendLogs("Fin du tour (PA restants: " .. fight_GetPA() .. ")", "Cyan") fight_PassTurn() ::continue:: end fight_SendLogs("Combat terminé", "Green") end

Exemple 2 — Cra fuyard (distance maximale)

Ce script illustre une IA de kiter qui maintient ses distances et tire depuis loin. Elle utilise Flèche Cinglante et Flèche Optique pour infliger des dégâts à longue portée, et se repositionne si un ennemi se rapproche trop.

Sort ID PA Portée Portée modifiable Ligne de vue Max/tour
Flèche Cinglante 13069 3 1–6 Non Non requise 3
Flèche Optique 13047 3 0–8 Oui Non requise 3
Flèche d'Immobilisation 13056 2 1–6 Non Non requise 4
-- ============================================================ -- IA Cra — Fuyard longue distance -- Stratégie : maintenir la distance, tirer depuis loin -- Se replier si un ennemi est à moins de 3 cases -- ============================================================ local SORT_FLECHE_CINGLANTE = "Flèche Cinglante" local SORT_FLECHE_OPTIQUE = "Flèche Optique" local SORT_FLECHE_IMMOBILISATION = "Flèche d'Immobilisation" local PA_CINGLANTE = 3 local PA_IMMOBILISATION = 2 local DIST_DANGER = 3 local function isInDanger() local closest = fight_GetClosestEnemyCell() if closest == 0 then return false end return fight_GetDistanceToEnemy(closest) <= DIST_DANGER end -- Placement optionnel function fight_placement(possiblePos, availablePos) -- Prendre la dernière cellule (souvent la plus éloignée de l'ennemi) local last = 0 for i, cell in pairs(availablePos) do last = cell end return last > 0 and last or availablePos[0] end -- Obligatoire : logique de combat function main() while fight_GetFightStatus() == "ACTIVE" do while not fight_IsMyTurn() do fight_Delay(200) end if fight_GetFightStatus() ~= "ACTIVE" then break end local pa = fight_GetPA() local pm = fight_GetPM() local target = fight_GetWeakestEnemyCell() fight_SendLogs("Tour | PA:" .. pa .. " PM:" .. pm, "Cyan") if target == 0 then fight_PassTurn() goto continue end -- Priorité 1 : Immobiliser l'ennemi trop proche if isInDanger() and pa >= PA_IMMOBILISATION then local closest = fight_GetClosestEnemyCell() if fight_CanCastSpell(SORT_FLECHE_IMMOBILISATION, closest) then fight_SendLogs("DANGER — Immobilisation cellule " .. closest, "Red") fight_CastSpell(SORT_FLECHE_IMMOBILISATION, closest) fight_Delay(700) pa = fight_GetPA() end end -- Priorité 2 : Fuir si toujours en danger if isInDanger() and pm > 0 then fight_SendLogs("Fuite — recherche cellule sûre", "Yellow") local safeCell = fight_FindSafeCell("far") if safeCell and safeCell > 0 then fight_MoveToCell(safeCell) fight_Delay(700) pm = fight_GetPM() end end -- Priorité 3 : Se repositionner pour avoir la cible en portée de Flèche Optique if pm > 0 and not fight_CanCastSpell(SORT_FLECHE_OPTIQUE, target) then local result = fight_FindBestCellToHit(target, SORT_FLECHE_OPTIQUE) if result and result.bestCell and result.bestCell > 0 and result.pmCost <= pm then fight_MoveToCell(result.bestCell) fight_Delay(700) pa = fight_GetPA() end end -- Priorité 4 : Tirer (Flèche Optique en priorité, Cinglante en fallback) local castCount = 0 while pa >= PA_CINGLANTE and castCount < 3 do local spell = nil if fight_CanCastSpell(SORT_FLECHE_OPTIQUE, target) then spell = SORT_FLECHE_OPTIQUE elseif fight_CanCastSpell(SORT_FLECHE_CINGLANTE, target) then spell = SORT_FLECHE_CINGLANTE end if spell then fight_SendLogs("Tir : " .. spell .. " -> cellule " .. target, "Green") fight_CastSpell(spell, target) fight_Delay(800) pa = fight_GetPA() castCount = castCount + 1 local newTarget = fight_GetWeakestEnemyCell() if newTarget ~= 0 and newTarget ~= target then fight_SendLogs("Nouvelle cible -> cellule " .. newTarget, "Orange") target = newTarget end else fight_SendLogs("Aucun sort possible, arrêt des tirs", "Yellow") break end end fight_SendLogs("Fin du tour (PA restants: " .. fight_GetPA() .. ")", "Cyan") fight_PassTurn() ::continue:: end fight_SendLogs("Combat terminé", "Green") end

Bonnes pratiques

Pratique Pourquoi
Nommer la fonction de combat exactement main Le moteur C# recherche cette fonction par son nom exact. Toute autre orthographe est ignorée et l'IA ne s'exécute pas.
Toujours appeler fight_PassTurn() en fin de tour Sans cela, le bot reste bloqué indéfiniment en attente de fin de tour.
Utiliser fight_CanCastSpell() avant de lancer Évite les tentatives de lancer hors portée ou sans PA suffisants, qui génèrent des erreurs.
Insérer des fight_Delay() entre les actions Le serveur a besoin de temps pour traiter chaque action. Un délai de 600–800ms entre chaque sort est recommandé.
Recalculer les PA après chaque action Les PA peuvent changer pour des raisons externes (buff, débuff). Toujours relire fight_GetPA() après un sort ou déplacement.
Vérifier fight_GetFightStatus() en début de boucle Le combat peut se terminer entre deux tours (victoire, défaite). Ne pas vérifier peut provoquer des erreurs sur les appels de fonctions hors combat.
Mettre à jour la cible après chaque KO La cible peut mourir pendant votre tour. Recalculer avec fight_GetWeakestEnemyCell() après chaque sort.

Erreurs communes

SymptĂ´me Cause probable Solution
L'IA ne démarre jamais, aucune action en combat La fonction main est absente ou mal nommée dans le script S'assurer que le script contient exactement function main() ... end
Le bot ne passe jamais son tour fight_PassTurn() n'est pas appelé Vérifier que tous les chemins d'exécution appellent fight_PassTurn()
Le bot tente de lancer un sort mais rien ne se passe Sort hors portée ou PA insuffisants Utiliser fight_CanCastSpell() avant chaque lancer
Le bot reste bloqué après un déplacement Pas de délai après fight_MoveToCell() Ajouter fight_Delay(700) après chaque déplacement
Erreur Lua : accès à une valeur nil fight_GetWeakestEnemyCell() retourne 0 (pas d'ennemi) Toujours vérifier que target ~= 0 avant d'utiliser la cellule
Le script plante après la fin du combat La boucle continue même hors combat Vérifier fight_GetFightStatus() == "ACTIVE" en début de boucle
Le bot essaie d'utiliser require ou io Fonctions système interdites dans le sandbox IA Utiliser uniquement les fonctions fight_* disponibles