> ## Documentation Index
> Fetch the complete documentation index at: https://docs.humalike.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Actions définies par le serveur

> Déclarez des actions que votre propre script exécute, conditionnées aux faits qu'il signale.

Le fournisseur d'actions déclare des actions à côté des observations qu'il
[signale](/fr/ai-npc/integrations/world-events#observations-serveur). Le modèle
choisit une action déclarée comme n'importe quelle action du catalogue, Humalike
n'accepte le tag qu'une fois les observations requises signalées, et votre
`RunAction` exécute l'action. Le comportement en jeu de cette condition et du
comptoir est décrit sous [Systèmes de gameplay](/fr/ai-npc/gameplay#actions-définies-par-le-serveur).

```lua theme={null}
exports.humalike:RegisterProvider('actions', {
    name = 'my_actions', apiVersion = 1, priority = 100,
    SupportedActions = {},
    Namespace = 'myserver',
    Observations = {
        item_given = {
            fields = { item = 'string', quantity = 'integer' },
            template = { en = 'the character handed you {quantity} x {item}' },
        },
    },
    Actions = {
        give_map = {
            name = 'Give the treasure map',
            description = 'Hand the player the map to the hidden chest.',
            params = { copies = { type = 'integer', enum = { 1, 2 } } },
            fixed = { item = 'treasure_map' },
            requires = {
                { observation = 'item_given', where = { item = 'amulet' }, consume = true },
            },
            locked_hint = { en = 'Only once the amulet is in your hands.' },
        },
    },
    RunAction = function(action, source, npcCoords, params)
        if action ~= 'give_map' then return false end
        return exports.ox_inventory:AddItem(source, params.item, params.copies or 1)
    end,
})
```

Activez `myserver:give_map` sur les PNJ qui doivent en disposer dans le tableau
de bord. Les clés sont préfixées par l'espace de noms sur le réseau
(`myserver:give_map`) et locales dans `RunAction` (`give_map`). `RunAction`
reçoit les `params` du modèle fusionnés avec `fixed` et `player_id` :

```lua theme={null}
-- Tag: [myserver:give_map copies=2]
-- RunAction receives ('give_map', source, npcCoords, { player_id = 12, copies = 2, item = 'treasure_map' })
```

Les actions sont redéclarées à chaque rapport de capacités. Une action modifiée
ou supprimée prend effet au prochain démarrage de la ressource.

## Déclaration

| Champ         | Description                                                                                                                                                                                         |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | Affiché dans le tableau de bord. Jusqu'à 80 caractères.                                                                                                                                             |
| `description` | La ligne que le modèle lit sous ses règles ACTIONS. Jusqu'à 400 caractères, sans crochets.                                                                                                          |
| `params`      | Jusqu'à 4 valeurs que le modèle renseigne. Voir [Params](#params).                                                                                                                                  |
| `fixed`       | Jusqu'à 16 valeurs décidées par votre script. Jamais envoyées à Humalike ; fusionnées sous les valeurs du modèle avant `RunAction` et toujours prioritaires.                                        |
| `requires`    | Jusqu'à 4 conditions sur les observations signalées, qui doivent toutes être satisfaites. Voir [Conditions](#conditions).                                                                           |
| `locked_hint` | Ce qui est dit au PNJ, par code de langue (n'importe laquelle proposée par le tableau de bord ; l'anglais est lu quand la langue du PNJ n'a pas de texte), tant qu'une condition n'est pas remplie. |
| `limit`       | `{ per_player = 1, every_s = 86400, hint = { en = '...' } }`. Actions par joueur dans toute fenêtre de `every_s` secondes (jusqu'à 7 jours). Aucune limite sauf déclaration.                        |
| `auto`        | `true` : Humalike exécute l'action à la prochaine réponse du PNJ dès que ses conditions sont remplies, que le modèle écrive ou non le tag. Requiert une condition `consume = true`.                 |
| `params_from` | `{ amount = 'item_given.quantity' }`. Valeurs tirées des faits déclencheurs, jamais du modèle. Voir [Params issus des faits](#params-issus-des-faits).                                              |
| `uses_stock`  | `{ item = 'map', quantity = 1 }`. L'action prélève dans le stock du PNJ et se verrouille à zéro. Voir [Stock](#stock).                                                                              |

### Params

```lua theme={null}
params = {
    copies = { type = 'integer', enum = { 1, 2 }, required = true, description = 'How many copies.' },
}
```

* Les types sont `string`, `integer` ou `boolean`.
* `enum` contient jusqu'à 16 mots simples ou entiers.
* `required` et `description` sont facultatifs.
* `player_id` est toujours ajouté par Humalike.
* Un tag dont les valeurs ne correspondent pas à la déclaration est écarté avant
  d'être prononcé ou mémorisé.

### Conditions

```lua theme={null}
requires = {
    { observation = 'item_given', where = { item = 'cash', quantity = { gte = 500 } },
      within_s = 600, consume = true },
}
```

| Champ         | Description                                                                         |
| ------------- | ----------------------------------------------------------------------------------- |
| `observation` | Une clé d'observation déclarée, signalée pour ce PNJ et le joueur auquel il répond. |
| `where`       | Conditions sur les champs déclarés. Toutes doivent correspondre.                    |
| `within_s`    | Âge maximal des faits comptés, en secondes. De 5 à 3600, 600 par défaut.            |
| `consume`     | `true` dépense chaque fait compté par la condition une fois l'action effectuée.     |

| Forme de `where`             | Correspond à                                        |
| ---------------------------- | --------------------------------------------------- |
| `item = 'amulet'`            | Égalité scalaire. `1` et `1.0` sont le même nombre. |
| `quantity = { gte = 500 }`   | Borne inférieure sur un champ numérique.            |
| `quantity = { lte = 3 }`     | Borne supérieure. `gte` et `lte` se combinent.      |
| `quantity = { sum_gte = 2 }` | Somme sur tous les faits comptés.                   |

Les faits dépensés disparaissent pour toutes les actions et pour le comptoir.

### Params issus des faits

```lua theme={null}
refund_deposit = {
    name = 'Give the deposit back',
    description = 'Return the deposit the player put on the counter.',
    requires = { { observation = 'item_given', where = { item = 'cash' }, consume = true } },
    params_from = { amount = 'item_given.quantity' },
},
-- RunAction receives { player_id, amount = <what was actually handed over> }
```

* Les valeurs proviennent des faits comptés par la **première** entrée de
  `requires` portant sur cette observation.
* Les nombres sont additionnés sur ces faits. Tout le reste prend la valeur la
  plus récente.

## Stock

```lua theme={null}
local result = exports.humalike:SetNpcStock(npcId, { map = 50, bread = 'unlimited' })
```

Remplace l'intégralité du stock du PNJ. Un objet non listé est un objet dont le
PNJ ne possède aucun exemplaire. Le tableau de bord affiche le stock en lecture
seule sur la page du PNJ.

* Jusqu'à 32 objets par PNJ.
* Les noms d'objets correspondent à `^[a-z][a-z0-9_]*$`, au plus 48 caractères.
* Les quantités sont des nombres entiers de 0 à 1 000 000, ou `'unlimited'`.

Renvoie `{ ok = true, value = { stock } }` ou `{ ok = false, error = code }` :

| Erreur                           | Signification                                                         |
| -------------------------------- | --------------------------------------------------------------------- |
| `invalid_npc`                    | `npcId` n'est pas une chaîne UUID.                                    |
| `npc_not_found`, `npc_not_bound` | Identique à `ReportObservation`.                                      |
| `invalid_stock`                  | `stock` n'est pas une table.                                          |
| `invalid_item:<name>`            | Le nom enfreint le motif ou la longueur.                              |
| `invalid_count:<name>`           | La quantité n'est ni un nombre entier dans la plage ni `'unlimited'`. |
| `too_many_items`                 | Plus de 32 objets.                                                    |
| `runtime_not_ready`              | Pas encore d'identifiants. Rien n'a été envoyé.                       |

## Comptoir

Pour les PNJ qui vendent, déclarez un `Catalog` plutôt qu'une action par
produit. Humalike prend la commande, la chiffre et encaisse le paiement, puis
appelle `deliver` et `refund` sur votre `RunAction`.

```lua theme={null}
Catalog = {
    currency = 'cash',
    payment = 'item_given',
    items = {
        water = { price = 5 },
        bread = { price = 3 },
        pistol = { price = 150, limit = { per_player = 1, every_s = 86400 } },
    },
},
```

| Champ      | Description                                                                                                                                                          |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `currency` | La valeur `item` de l'observation `payment` qui compte comme de l'argent.                                                                                            |
| `payment`  | L'observation déclarée que votre hook d'inventaire signale. Sa `quantity` doit être typée `integer`.                                                                 |
| `items`    | De 1 à 64 produits, nommés selon la règle du stock, chacun avec un `price` entier de 0 à 10 000 000 et une `limit` facultative (même forme qu'une `limit` d'action). |

Un PNJ vend les articles du catalogue que votre script a [mis en stock](#stock).

### `deliver` et `refund`

Les deux sont déclarées pour vous avec le `Catalog`. Ne les listez pas sous
`Actions`.

| Action    | `params`                                                                          | Retour                                                                                                                                                    |
| --------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deliver` | `items` (objet vers quantité), `total`, `paid`, `change`, `currency`, `player_id` | `true` seulement une fois chaque ligne et la monnaie rendue dans l'inventaire du joueur. En cas d'échec, annulez ce qui a été ajouté et renvoyez `false`. |
| `refund`  | `amount`, `currency`, `player_id`                                                 | `true` une fois le montant de retour dans l'inventaire du joueur.                                                                                         |

```lua theme={null}
RunAction = function(action, source, npcCoords, params)
    if action == 'deliver' then
        return giveAllOrNothing(source, params.items, params.currency, params.change)
    elseif action == 'refund' then
        return exports.ox_inventory:AddItem(source, params.currency, params.amount) == true
    end
    return false
end
```

### Commandes depuis un menu

```lua theme={null}
local result = exports.humalike:PlaceOrder(npcId, playerId, { water = 2 })
```

Envoie les lignes choisies depuis votre propre menu de boutique. Jusqu'à 16
lignes, chaque quantité de 1 à 1 000. Renvoie `{ ok = true, value = { lines } }`
ou `{ ok = false, error = code }` :

| Erreur                                                                     | Signification                                        |
| -------------------------------------------------------------------------- | ---------------------------------------------------- |
| `invalid_player`, `character_not_loaded`, `npc_not_found`, `npc_not_bound` | Identique à `ReportObservation`.                     |
| `no_catalog`                                                               | Le fournisseur d'actions ne déclare aucun `Catalog`. |
| `invalid_lines`                                                            | `lines` n'est pas une table non vide.                |
| `unknown_item:<name>`                                                      | Absent du catalogue.                                 |
| `invalid_quantity:<name>`                                                  | N'est pas un nombre entier de 1 à 1 000.             |
| `too_many_lines`                                                           | Plus de 16 lignes.                                   |

### Clés réservées

* `order`, `cancel_order`, `order_placed`, `order_refused` : non déclarables
  comme actions ni comme observations.
* `deliver`, `refund` : non déclarables comme observations.
* Aucune clé ne peut être à la fois une action et une observation.
* `spent` est un champ d'observation réservé.

## Contrat de RunAction

* Renvoyez exactement `true` lorsque l'action est effectuée. `false` rejette
  l'invocation, qui n'est pas réessayée.
* Répondez dans les 5 secondes. Une réponse perdue (délai dépassé, connexion
  coupée, HTTP 5xx) est réessayée immédiatement, jusqu'à trois tentatives, sous
  le même id d'invocation.
* Conservez les ids que vous avez acceptés et répondez à une répétition à partir
  de cet enregistrement, afin qu'une réponse perdue ne provoque jamais deux fois
  la même action.

## Débogage

* Une déclaration refusée est affichée par `RegisterProvider` avec le champ en
  cause. Rien n'est enregistré.
* La page du PNJ dans le tableau de bord liste les actions déclarées avec leurs
  conditions. La vue transcription montre les observations et les actions
  effectuées.
* `humalike_debug 1` journalise chaque push entrant et son résultat.

## Suite

* [Comportement de la condition et du comptoir](/fr/ai-npc/gameplay#actions-définies-par-le-serveur).
* [Signaler les observations dont ces actions dépendent](/fr/ai-npc/integrations/world-events#observations-serveur).
* [Revoir le contrat des fournisseurs](/fr/ai-npc/integrations/provider-api).
