# Zones intelligentes et achats en Lua

Vos trajets peuvent rechercher des lieux de récolte ou de combat et acheter
les ressources manquantes dans un HDV choisi. Ces fonctions sont réutilisables
pour préparer un craft, compléter un stock ou alterner plusieurs activités.

!!!info Version nécessaire
Installez une version de Jitsuri exposant `getSmartGatheringPlan`,
`getSmartCombatPlan` et `buyMissingResource`. Si une fonction vaut `nil`,
mettez Jitsuri à jour avant d'utiliser l'exemple correspondant.
!!!

!!!warning Démonstration fictive
Les exemples concernent une **matière première imaginaire destinée à un objet
décoratif**, sans rapport avec un parcours de capture. Les identifiants sont
remplacés par `0`, une valeur volontairement invalide. Renseignez vos propres
GID, MapID, métier et niveau requis avant de les utiliser.
!!!

## Découvrir les zones

| Fonction | Identifiant attendu | Effet |
|---|---|---|
| `getSmartGatheringPlan(gid)` | GID de la ressource produite, pas le type d'interactif | Recherche les cartes dans l'index local de récolte `maps.bin` v3. |
| `getSmartCombatPlan(monsterIds)` | Chaîne de 1 à 50 GID de monstres séparés par des virgules | Interroge le service de zones utilisé par le mode intelligent. |

Les recherches ne déplacent pas le personnage, ne récoltent pas et ne lancent
pas de combat. Conservez le plan dans une variable pour éviter de refaire
la même recherche à chaque passage sur une carte.

### Résultat d'une recherche

| Champ | Description |
|---|---|
| `success` | `true` si des lieux ont été trouvés. |
| `code`, `message` | Résultat ou explication de l'échec. |
| `mapIds` | Tableau Lua de MapID candidats. |
| `elementIds` | Types d'interactifs de récolte ; tableau vide pour un plan de combat. |
| `maps` | Les mêmes MapID, séparés par des virgules, pour les sélecteurs intelligents. |
| `mapScores` | Scores du catalogue au format `mapId:score,mapId:score`. |

Un index absent ou ancien, une ressource sans lieu connu ou un service
indisponible produit un résultat d'échec. Vérifiez `success` avant de poursuivre.
Les lieux du catalogue ne garantissent pas la présence immédiate d'une
ressource ou d'un groupe de monstres.

### Choisir une destination

Extrait à appeler depuis une action de votre trajet :

```lua
local plan = getSmartGatheringPlan(0) -- GID fictif : renseignez votre ressource
if not plan.success then
    printMessage(plan.code .. " : " .. plan.message, "red")
    return
end

local destination = selectSmartGatheringTargetWithHistory(
    plan.maps, "", 20, plan.mapScores, ""
)
if destination == nil or destination <= 0 then
    printMessage("Aucune destination accessible retenue.", "yellow")
    return
end

ELEMENTS_TO_GATHER = plan.elementIds
setGatheringGoal(0, 25, "total")
-- Utilisez ensuite destination dans le chemin de votre trajet et gather=true
-- sur les cartes où vous souhaitez récolter.
```

Pour le combat, utilisez `selectSmartCombatTargetWithHistory(plan.maps,
cartesRecentes, exploration, plan.mapScores)` puis vos actions de déplacement
et de combat habituelles. Définissez également `FORCE_MONSTERS` avec les
monstres souhaités : chercher leurs zones ne pose pas ce filtre à votre place.

`exploration` va de 0 à 100. `cartesRecentes` est une chaîne de MapID séparés
par des virgules. Le dernier argument du sélecteur de récolte permet de
fournir les scores personnels ; `""` n'en fournit aucun.

Les fonctions ne recopient pas les paramètres, les stockages ou l'équipe
sélectionnés dans l'interface du mode intelligent. Le trajet reste responsable
des niveaux requis, des accès et des actions à exécuter.

## Acheter uniquement le manque

```lua
local result = buyMissingResource(0, 25, 0, 100000) -- GID et MapID à renseigner
```

| Paramètre | Signification |
|---|---|
| `gid` | Identifiant de l'objet à acheter. |
| `targetQuantity` | Stock total souhaité dans l'inventaire, entre 1 et 100 000. |
| `hdvMapId` | MapID de l'HDV compatible à rejoindre et ouvrir. |
| `maximumKamas` | Plafond strictement positif pour les achats de cet appel. |

Avec 10 unités déjà présentes et une cible de 25, la fonction achète au plus
les **15 unités manquantes**. Si la cible est déjà atteinte, elle réussit
sans déplacement ni achat. Elle ne consulte pas la banque automatiquement :
faites les retraits nécessaires avant de l'appeler.

### Choix des prix et limites

- Recherche de la combinaison de lots de 1, 10, 100 ou 1 000 au coût minimal
  pour atteindre la quantité exacte, sans surplus.
- Relecture des offres et recalcul après chaque achat : les prix affichés
  ne garantissent pas que plusieurs lots restent disponibles au même tarif.
- Achat dans **l'HDV configuré uniquement**, sans comparaison entre villes.
- Respect du budget et des kamas disponibles ; contrôle des pods avant
  l'achat et prise en compte des refus pendant l'opération.
- Vérification des achats et de l'objectif dans l'inventaire avant de
  renvoyer une réussite, puis sortie de l'HDV.

Le budget exclut les frais de déplacement et de banque. Il est propre à
chaque appel : pour un plafond commun à plusieurs ressources, soustrayez
`result.spent` du budget restant dans votre Lua.

### Résultat de l'achat

| Champ | Description |
|---|---|
| `success` | Objectif atteint et opération terminée avec succès. |
| `code`, `message` | Résultat ou explication de l'arrêt. |
| `quantity` | Quantité achetée avec confirmation pendant cet appel, pas le stock total. |
| `spent` | Coût en kamas des achats confirmés pendant cet appel. |

Les codes d'échec comprennent notamment `INVALID_ARGUMENT`, `BUSY`,
`UNKNOWN_ITEM`, `UNKNOWN_WEIGHT`, `HDV_UNAVAILABLE`, `INSUFFICIENT_PODS`,
`PURCHASE_INCOMPLETE`, `CLOSE_UNCONFIRMED` et `PURCHASE_FAILED`.

!!!warning Achat incomplet
`success = false` ne signifie pas qu'aucun achat n'a eu lieu. Une partie
peut avoir été achetée ou une confirmation peut manquer. Arrêtez cette phase
et vérifiez l'inventaire avant de relancer ; ne rappelez pas la fonction dans
une boucle sans contrôle. La fonction ne dépose pas automatiquement tous vos
objets en banque pour libérer des pods.
!!!

## Exemple : acheter si le niveau métier manque

Cette démonstration fictive illustre l'achat d'une matière première manquante si le personnage n'a pas le niveau
requis, puis s'arrête. Avec le niveau suffisant, il indique de poursuivre
votre branche de récolte. Il n'implémente pas cette branche à votre place.

```lua
local CONFIG = {
    BUY_IF_NOT_JOB_LEVEL = true,
    RESOURCE_GID = 0, -- GID de votre matière première, à renseigner
    JOB_ID = 0, -- ID du métier, à renseigner
    REQUIRED_LEVEL = 20, -- niveau fictif, à adapter
    TARGET_QUANTITY = 25,
    HDV_MAP = 0, -- MapID de votre HDV, à renseigner
    MAX_KAMAS = 100000,
}
local started = false

local function prepareResource()
    if started then return end
    started = true
    if CONFIG.RESOURCE_GID <= 0 or CONFIG.JOB_ID <= 0 or CONFIG.HDV_MAP <= 0 then
        printMessage("Exemple fictif : renseignez les identifiants avant de lancer.", "red")
        stopScript()
        return
    end

    if getInventoryItemCount(CONFIG.RESOURCE_GID) >= CONFIG.TARGET_QUANTITY then
        printMessage("Stock déjà suffisant.", "green")
    elseif getJobLevel(CONFIG.JOB_ID) >= CONFIG.REQUIRED_LEVEL then
        printMessage("Niveau suffisant : poursuivez votre branche de récolte.", "yellow")
    elseif not CONFIG.BUY_IF_NOT_JOB_LEVEL then
        printMessage("Niveau insuffisant et achat désactivé.", "red")
    elseif buyMissingResource == nil then
        printMessage("Mettez Jitsuri à jour : fonction d'achat absente.", "red")
    else
        local result = buyMissingResource(
            CONFIG.RESOURCE_GID, CONFIG.TARGET_QUANTITY,
            CONFIG.HDV_MAP, CONFIG.MAX_KAMAS
        )
        printMessage(result.message, result.success and "green" or "red")
        printMessage("Achats confirmés : " .. result.quantity ..
            " / coût : " .. result.spent .. " kamas.", "yellow")
    end
    stopScript()
end

function move()
    if started then return {} end
    return { { map = tostring(getMapId()), custom = prepareResource } }
end
```

`BUY_IF_NOT_JOB_LEVEL` est une **variable de cet exemple**, pas une option
native qui modifie tous les trajets. Votre Lua décide du niveau attendu et
de l'activité de remplacement. Une viande, par exemple, ne se récolte pas
sur un interactif : choisissez explicitement sa source plutôt que d'utiliser
un plan de récolte inadapté.

## Combiner banque, récolte, combat et craft

Les [fonctions de recette et de stockage](craft-et-recolte.md) déjà disponibles
permettent de calculer les besoins, retirer ce qui existe en banque, puis
choisir une activité pour chaque ingrédient encore manquant. Un plan de zones
ne détermine pas automatiquement quels monstres donnent les ingrédients :
votre script fournit les associations GID de ressource → GID de monstres.

Pour afficher la progression, réutilisez `getInventoryItemCount(gid)` et
les callbacks existants `onItemGathered`, `onGatheringGoalReached` ou
`onFightEnded`. Les besoins, les journaux et les changements de phase sont
pilotés par votre Lua.
