# Récolter puis fabriquer en Lua

Jitsuri permet de construire un atelier automatisé sans imposer un parcours
unique. Votre script choisit où récupérer les ingrédients, quelles ressources
récolter et dans quel atelier fabriquer l'objet.

Les anciens trajets utilisant `craftItem(...)` restent compatibles. Les
nouvelles fonctions sont surtout utiles lorsque la banque et l'atelier sont
éloignés, lorsque plusieurs coffres sont utilisés ou lorsque le script doit
récolter uniquement ce qui manque.

## Lire une recette

```lua
local recipe = getCraftRecipe(32521)

if recipe == nil then
    printMessage("Recette inconnue.", "red")
    stopScript()
    return
end

for _, ingredient in ipairs(recipe.ingredients) do
    printMessage(
        "GID " .. ingredient.gid .. " x" .. ingredient.quantity,
        "yellow"
    )
end
```

La recette indique notamment le métier, la compétence d'atelier et les
ingrédients nécessaires.

## Savoir ce qu'il manque

```lua
local quantity = getCraftableQuantity(32521)
local missing = getMissingCraftIngredients(32521, 10, "inventory")

for _, item in ipairs(missing) do
    printMessage(
        "Il manque " .. item.missing .. " unité(s) du GID " .. item.gid,
        "yellow"
    )
end
```

Utilisez `"inventory"` pour le sac du personnage et `"storage"` pour la
banque ou le coffre actuellement ouvert.

## Créer un objectif de récolte

Pour demander dix unités supplémentaires d'une ressource :

```lua
setGatheringGoal(421, 10, "additional")
```

Deux modes sont disponibles :

- `total` atteint une quantité totale dans l'inventaire ;
- `additional` récolte une quantité en plus de celle présente au démarrage de
  l'objectif.

Jitsuri suit la quantité réelle reçue dans l'inventaire. Si le GID correspond à
un récoltable connu, la ressource est ajoutée automatiquement à la sélection du
trajet, puis retirée lorsque l'objectif est atteint.

```lua
function onGatheringGoalProgress(goal)
    printMessage(
        goal.collectedQuantity .. "/" .. goal.requestedQuantity,
        "yellow"
    )
end

function onGatheringGoalReached(goal)
    printMessage("Objectif de récolte terminé.", "green")
    requestRouteRefresh()
end
```

Pour préparer automatiquement les objectifs d'une recette :

```lua
local result = createGatheringGoalsForCraft(32521, 10)

if not result.success then
    printMessage("Recette inconnue ou quantité invalide.", "red")
    stopScript()
    return
end

for _, item in ipairs(result.nonHarvestable) do
    printMessage(
        "À récupérer dans un stockage : GID " .. item.gid ..
        " x" .. item.missing,
        "yellow"
    )
end
```

Les ingrédients récoltables deviennent des objectifs. Les autres restent dans
`nonHarvestable` afin que votre script choisisse une banque, un coffre de
maison, un coffre de guilde ou une autre source.

`result.ready` vaut `true` lorsqu'aucun ingrédient ne manque. `result.code`
précise si les objectifs ont été créés ou si les éléments manquants ne sont pas
récoltables.

## Utiliser une banque ou un coffre

Une fois sur la bonne carte :

```lua
if not openBank() then
    printMessage("Impossible d'ouvrir la banque.", "red")
    stopScript()
    return
end

local result = withdrawCraftIngredients(32521, 10)
closeStorage()

if not result.success then
    printMessage(result.code .. " : " .. result.message, "red")
    stopScript()
end
```

Fonctions utiles :

| Fonction | Description |
|---|---|
| `isStorageOpen()` | Confirme qu'une banque ou un coffre est réellement ouvert. |
| `getStorageItemCount(gid)` | Quantité du GID dans le stockage courant. |
| `getStorageItems()` | Liste les piles du stockage courant. |
| `withdrawStorageItem(gid, quantity)` | Retire une quantité précise. |
| `withdrawCraftIngredients(itemGid, quantity)` | Retire uniquement ce qui manque pour le craft. |
| `depositStorageItem(gid, quantity)` | Dépose une quantité ; `0` dépose tout le GID. |
| `closeStorage()` | Ferme proprement le stockage. |

`openGuildChest(number)`, `goToAndEnterHouse(...)` et
`goAndOpenChest(...)` permettent d'utiliser les autres stockages déjà
configurés dans vos trajets.

!!!warning
Les fonctions de transfert exigent un stockage confirmé comme ouvert. Cela
évite de réutiliser accidentellement le contenu d'une ancienne banque après un
changement de carte.
!!!

## Fabriquer depuis l'inventaire

Sur la carte de l'atelier :

```lua
local opened = openCraftWorkshop(32521)

if not opened.success then
    printMessage(opened.message, "red")
    stopScript()
    return
end

local result = craftFromInventory(32521, 10)
closeCraftWorkshop()

if result.success then
    printMessage("Fabrication terminée.", "green")
else
    printMessage(result.code .. " : " .. result.message, "red")
end
```

Pour contrôler chaque étape séparément, utilisez `setCraftRecipe`,
`setCraftQuantity`, `executeCraft` et `executeCraftStep`.

## Exemples complets

Les trois exemples suivants utilisent le GID `32521`, la banque `84935175` et
l'atelier `73533440`. Remplacez ces trois valeurs par celles de votre recette
et de votre trajet. Les parcours manuels demandent dix fabrications ; la
fonction automatique historique fabrique la quantité maximale possible.

### 1. Laisser Jitsuri tout gérer

Cet exemple utilise la fonction historique tout-en-un. Jitsuri rejoint la
banque, calcule la quantité fabricable, retire les ingrédients, rejoint
l'atelier et lance la fabrication.

```lua
local ITEM_GID = 32521
local BANK_MAP_ID = 84935175
local WORKSHOP_MAP_ID = 73533440
local started = false

local function runAutomaticCraft()
    if started then
        return
    end

    started = true
    local success = craftItem(
        ITEM_GID,
        BANK_MAP_ID,
        WORKSHOP_MAP_ID,
        true,  -- atelier spécial si nécessaire
        true   -- retour en banque à la fin
    )

    if success then
        printMessage("Craft automatique terminé.", "green")
    else
        printMessage("Le craft automatique a échoué.", "red")
    end

    stopScript()
end

function move()
    return {
        {
            map = tostring(getMapId()),
            custom = runAutomaticCraft,
        },
    }
end
```

Cette forme est la plus courte et reste adaptée aux anciens trajets. Utilisez
les fonctions séparées des exemples suivants pour contrôler chaque étape.

### 2. Prendre tout le nécessaire en banque puis fabriquer

Ici, le Lua pilote lui-même les deux phases. Tous les ingrédients nécessaires
aux dix crafts sont pris en banque. Seules les quantités manquantes sont
retirées : le reste du coffre n'est pas déplacé et les pods ne sont pas remplis
avec des objets inutiles au craft.

```lua
local ITEM_GID = 32521
local CRAFT_QUANTITY = 10
local BANK_MAP_ID = 84935175
local WORKSHOP_MAP_ID = 73533440
local phase = "bank"

local function fail(result)
    printMessage(result.code .. " : " .. result.message, "red")
    stopScript()
end

local function takeIngredientsFromBank()
    if not openBank() then
        printMessage("Impossible d'ouvrir la banque.", "red")
        stopScript()
        return
    end

    local result = withdrawCraftIngredients(
        ITEM_GID,
        CRAFT_QUANTITY
    )
    closeStorage()

    if not result.success then
        fail(result)
        return
    end

    phase = "workshop"
    requestRouteRefresh()
end

local function craftAtWorkshop()
    local opened = openCraftWorkshop(ITEM_GID)
    if not opened.success then
        fail(opened)
        return
    end

    local result = craftFromInventory(ITEM_GID, CRAFT_QUANTITY)
    closeCraftWorkshop()

    if not result.success then
        fail(result)
        return
    end

    printMessage(
        result.craftedQuantity .. " fabrication(s) confirmée(s).",
        "green"
    )
    stopScript()
end

function move()
    if phase == "bank" then
        return {
            {
                map = tostring(BANK_MAP_ID),
                custom = takeIngredientsFromBank,
            },
        }
    end

    return {
        {
            map = tostring(WORKSHOP_MAP_ID),
            custom = craftAtWorkshop,
        },
    }
end
```

Pour un coffre de guilde ou de maison, conservez la même logique et remplacez
`openBank()` par l'ouverture du stockage concerné.

### 3. Récolter, compléter en banque puis fabriquer

Ce dernier exemple crée automatiquement les objectifs correspondant aux
ingrédients récoltables. Une fois ces objectifs terminés, il complète la
recette en banque puis rejoint l'atelier.

```lua
MAX_PODS = 90
ELEMENTS_TO_GATHER = {}

local ITEM_GID = 32521
local CRAFT_QUANTITY = 10
local BANK_MAP_ID = 84935175
local WORKSHOP_MAP_ID = 73533440
local phase = "prepare"

local function stopWithMessage(message)
    printMessage(message, "red")
    stopScript()
end

local function prepareGatheringGoals()
    local result = createGatheringGoalsForCraft(
        ITEM_GID,
        CRAFT_QUANTITY
    )

    if not result.success then
        stopWithMessage("Recette inconnue ou quantité invalide.")
        return
    end

    for _, item in ipairs(result.nonHarvestable) do
        printMessage(
            "Banque requise : GID " .. item.gid ..
            " x" .. item.missing,
            "yellow"
        )
    end

    if #result.goals > 0 then
        phase = "gather"
    else
        phase = "bank"
    end
end

function onGatheringGoalProgress(goal)
    printMessage(
        "Récolte GID " .. goal.gid .. " : " ..
        goal.collectedQuantity .. "/" .. goal.requestedQuantity,
        "yellow"
    )
end

function onGatheringGoalReached(goal)
    printMessage("Objectif GID " .. goal.gid .. " terminé.", "green")

    if areAllGatheringGoalsReached() then
        phase = "bank"
        requestRouteRefresh()
    end
end

local function completeRecipeFromBank()
    if not openBank() then
        stopWithMessage("Impossible d'ouvrir la banque.")
        return
    end

    local result = withdrawCraftIngredients(
        ITEM_GID,
        CRAFT_QUANTITY
    )
    closeStorage()

    if not result.success then
        stopWithMessage(result.code .. " : " .. result.message)
        return
    end

    phase = "workshop"
    requestRouteRefresh()
end

local function craftResult()
    local opened = openCraftWorkshop(ITEM_GID)
    if not opened.success then
        stopWithMessage(opened.code .. " : " .. opened.message)
        return
    end

    local result = craftFromInventory(ITEM_GID, CRAFT_QUANTITY)
    closeCraftWorkshop()

    if result.success then
        printMessage("Récolte et fabrication terminées.", "green")
    else
        printMessage(result.code .. " : " .. result.message, "red")
    end
    stopScript()
end

function move()
    if phase == "prepare" then
        prepareGatheringGoals()
    end

    if phase == "gather" then
        return {
            -- Remplacez ces cartes par un trajet contenant les ressources
            -- nécessaires à votre recette.
            { map = "5,7", path = "bottom", gather = true },
            { map = "5,8", path = "left", gather = true },
            { map = "4,8", path = "5,7", gather = true },
        }
    end

    if phase == "bank" then
        return {
            {
                map = tostring(BANK_MAP_ID),
                custom = completeRecipeFromBank,
            },
        }
    end

    return {
        {
            map = tostring(WORKSHOP_MAP_ID),
            custom = craftResult,
        },
    }
end
```

!!!warning
Le trajet de récolte doit traverser des cartes contenant les ingrédients de la
recette. Les objectifs choisissent automatiquement les récoltables à utiliser,
mais ils ne peuvent pas inventer un parcours vers une ressource absente des
cartes fournies dans `move()`.
!!!

## Écouter l'inventaire

Les callbacks sont facultatifs :

```lua
function onInventoryItemChanged(event)
    printMessage(
        "GID " .. event.gid .. " : " .. event.quantity ..
        " (" .. event.delta .. ")",
        "yellow"
    )
end

function onItemGathered(event)
    printMessage(
        "Récolté : GID " .. event.gid .. " x" .. event.quantity,
        "green"
    )
end
```

`onInventoryItemChanged` couvre aussi les achats, retraits, dépôts, crafts et
autres modifications d'inventaire. `onItemGathered` est réservé à la récolte.

## Résultats et erreurs

Les actions retournent une table avec `success`, `code`, `message`, les
quantités demandées/fabricables/fabriquées et la liste des ingrédients
manquants.

Les codes les plus courants sont :

- `RECIPE_NOT_FOUND` : recette inconnue ;
- `STORAGE_NOT_OPEN` : banque ou coffre non ouvert ;
- `INGREDIENT_MISSING` : ingrédients insuffisants ;
- `WRONG_WORKSHOP` : mauvais atelier sur la carte ;
- `WORKSHOP_NOT_OPEN` : atelier non ouvert ;
- `SERVER_REJECTED` : action non confirmée ;
- `CRAFT_BUSY` : une autre opération est encore en cours.

Ne lancez pas plusieurs transferts ou crafts en parallèle. Attendez le résultat
de l'action courante avant de poursuivre.

## Rétrocompatibilité

Cette fonction historique reste disponible :

```lua
craftItem(itemGid, bankMapId, workshopMapId, specialCraft, returnBank)
```

Elle convient aux anciens trajets tout-en-un. Pour un nouveau parcours, les
fonctions séparées offrent davantage de contrôle et des erreurs plus faciles à
diagnostiquer.
