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 |