Aller au contenu

pets — Familiers et nourrissage

API Lua des familiers Rétro : inventaire, PV, corpulence, repas, soins, équipement et réponses serveur.

8 fonctions, avec leurs structures, exemples et règles de confirmation.

Index


Référence

Fonctionnement et portée

pets.list() / pets.get(uid) / pets.equipped()

returns table / table | nil / table | nil

API des familiers Rétro portée par le moteur Lua. Appels avec un point (pets.feed), pas avec deux-points. Un familier est un objet de type 18, équipé à la position 8. Son UID identifie cet exemplaire ; templateId identifie son modèle. Les fantômes (type 90), certificats de chanil (type 77), dragodindes et objets vivants sont distincts. Les apparats peuvent aussi être de type 18 : la liste ne garantit donc pas qu’un objet mange. Nourrir exige des PV connus et strictement positifs. Les fonctions lisent les données serveur, sans calculer ni simuler les gains. Les ajouts nécessitent un moteur et une documentation mis à jour ; leur présence dans les sources ne met pas à jour les workers déjà démarrés.


pets.list

pets.list()

returns table of Pet

Tous les objets type 18 de l’inventaire, équipés ou dans le sac ; aucune lecture automatique du coffre. Tableau Lua indexé à partir de 1, vide si aucun familier connu. L’ordre est celui de l’inventaire, sans promesse de tri. Inclut les apparats de ce type. Les tables sont des instantanés : rappeler la fonction après une action.


pets.get

pets.get(petUid: number)

returns Pet | nil

Recherche cet exemplaire par UID dans l’inventaire. Retourne nil si absent ou d’un autre type ; un templateId ne sélectionne pas ses exemplaires. Plusieurs familiers identiques doivent être gérés par leurs UID distincts.


pets.equipped

pets.equipped()

returns Pet | nil

Familier type 18 présent à l’emplacement 8 ; nil si aucun n’est connu. Les autres emplacements et la monture ne sont pas concernés.


Structure Pet

local pet = pets.equipped()
if pet then
  print(pet.uid, pet.templateId, pet.life, pet.corpulence)
  if pet.lastMeal then
    print(pet.lastMeal.year, pet.lastMeal.month, pet.lastMeal.day,
          pet.lastMeal.hour, pet.lastMeal.minute)
  end
end

returns Pet

Champs d’identité : uid, templateId, name, quantity, position, type (=18), superType, equipped (position==8). Champs communs avec decodeEffects : effects (chaîne brute), jets ({id,value,max,text}), effectData ({id,val1?,val2?,val3?,param4?,raw}), life?, corpulence?, overfeeding?, underfeeding?, lastFoodTemplateId?, receivedAt?, lastMeal?. Les champs suffixés ? sont absents/nil si l’effet ou sa valeur n’est pas connu ; une absence ne vaut jamais 0 PV ni autorisation de nourrir. corpulence = normal, obese ou lean. overfeeding et underfeeding sont les paramètres bruts p2/p3 de l’effet 806, pas un nombre de repas à donner. Les dates contiennent year,month,day,hour,minute ; aucune conversion automatique vers l’heure système.


pets.decodeEffects

pets.decodeEffects(effects: string)

returns table

Décode une chaîne d’effets sans envoyer de paquet et sans exiger que l’objet soit dans l’inventaire. Même données d’effets que Pet, sans champs d’identité. Pratique pour exchange.storageItems()[i].effects ou des effets conservés par le script. Chaîne vide : effects="", jets={}, effectData={}, champs familiers absents. effectData préserve chaque effet à ID hex valide, y compris ceux non affichés dans jets ; id et val1/val2/val3 sont convertis en nombres décimaux, param4 reste une chaîne (par exemple un jet de dés), raw conserve l’entrée originale. Un paramètre numérique absent ou malformé reste nil.


Effets des familiers

local data = pets.decodeEffects("320#5#48#6,326#0#0#0,327#0#0#76e,328#290#326#4b0")
print(data.life)                 -- 6, et non 5
print(data.lastFoodTemplateId)   -- 1902
print(data.lastMeal.year)        -- 656 : année telle que reçue
for _, effect in ipairs(data.effectData) do
  print(effect.id, effect.val1, effect.val2, effect.val3)
end

returns table

Format : id#p1#p2#p3[#param4], valeurs hex dans les paquets, effets séparés par virgule. 800 (0x320) : PV dans p3 exclusivement ; ne pas utiliser p1/p2 comme PV ou déduire leur unité. 805 (0x325) : reçu le. 806 (0x326) : p2>6 donne obese en priorité, sinon p3>6 donne lean, sinon normal. 807 (0x327) : template du dernier aliment dans p3. 808 (0x328) : date du dernier repas, p1=année, p2=mois indexé depuis zéro×100+jour, p3=heure×100+minute ; heure absente=00:00, année -1=date inconnue. 814 affiche le nom du modèle désigné par p1 ; 717 affiche un monstre et un compteur p3 ; 962 est un libellé de niveau. Ces effets ne sont pas les dates/PV du familier ; les effets 970–975 concernent les objets vivants. Les autres effets restent accessibles dans effectData et effects.


pets.equip

pets.equip(petUid: number, timeoutMs: number = 5000)

blocking · returns boolean, string

Équipe ce familier depuis le sac et attend de le voir à la position 8. Retour true,equipped après constat serveur, true,already_equipped s’il l’était déjà. Refus si absent, mauvais type, pile vide ou autre position que le sac (-1). Peut remplacer le familier équipé : conserver son UID si une restauration est souhaitée. Le niveau et les conditions d’équipement restent contrôlés par le serveur/moteur. Aucun repas n’est donné par cette fonction.


pets.unequip

pets.unequip(timeoutMs: number = 5000)

blocking · returns boolean, string

Déséquipe le familier actuellement à la position 8 et attend que ce même UID soit dans le sac (-1). Retour true,unequipped ou true,already_unequipped s’il n’y avait aucun familier équipé. Ne dépose pas en banque et ne transforme pas en certificat.


pets.feed

pets.feed(petUid: number, foodUid: number, timeoutMs: number = 5000)

blocking · returns boolean, string

Demande UN repas ou soin au familier UID déjà équipé. foodUid est l’UID d’une pile du sac, jamais le template de la nourriture. La commande est OM|8 sans quantité ; elle est envoyée une seule fois. Refus local si cible absente/non équipée, PV absents ou <=0, nourriture absente/vide/équipée, UID identiques ou nourriture elle-même familier. Un emplacement 8 occupé par plusieurs objets dans l’état local est refusé (pet_slot_ambiguous). Les UID et le contexte sont revérifiés dans le moteur juste avant émission. Aucun équipement automatique ni recherche de nourriture implicite. true,fed signifie que la quantité de cette pile a diminué (ou pile supprimée) ET que les effets de ce familier ont changé. Cela ne garantit ni gain de caractéristique, ni repas à la bonne heure, ni absence de pénalité ; les soins et les repas avec indigestion peuvent aussi satisfaire ces critères. Relire life/corpulence et consulter les messages serveur.


pets.feedEquipped

pets.feedEquipped(foodUid: number, timeoutMs: number = 5000)

blocking · returns boolean, string

Raccourci : capture l’UID du familier équipé puis applique les mêmes contrôles et la même confirmation que pets.feed. false,pet_not_equipped si aucun familier n’est équipé. Un changement de familier avant traitement empêche l’envoi à une autre cible.


Retours, erreurs et attente

local ok, reason = pets.feed(petUid, foodUid, 5000)
if not ok then
  error("Opération interrompue : " .. reason)
end

returns boolean, string

Les quatre mutations attendent au maximum timeoutMs, 5000 ms par défaut, sans relance. 0 envoie la demande puis vérifie immédiatement l’état : ne pas l’utiliser pour un enchaînement confirmé. Raisons de succès : fed, equipped, already_equipped, unequipped, already_unequipped. Refus/interruptions : not_in_game, in_fight, busy, dialog_open, exchange_open ; pet_not_found, pet_not_equipped, pet_slot_ambiguous, pet_not_in_bag, pet_empty, pet_life_unknown, pet_dead, pet_removed ; food_not_found, food_not_in_bag, food_empty, food_is_pet, same_item ; timeout. timeout ou interruption après émission peut cacher une opération déjà appliquée : relire l’état et les logs avant de décider d’un nouveau repas. Une erreur de type/argument invalide, l’annulation du script ou la disparition du canal moteur lève une erreur Lua. Une attente ne constitue pas un ACK corrélé : éviter une manipulation simultanée du même inventaire par le client ou un autre script.


Régime, échéance et progression

-- Planifier seulement après avoir vérifié le régime et l’échéance.
-- Ne pas appeler pets.feed en boucle à partir des seuls PV.

returns information

Le client transmet le repas ; le serveur décide si l’aliment convient, si le délai est respecté, du soin, des PV perdus et du gain éventuel. Les sources locales ne fournissent pas de table exhaustive structurée aliments/intervalles par familier. Aucune fonction canFeed/nextMeal/foods ni règle universelle « X heures / Y repas » n’est annoncée. p1/p2 de l’effet 800 sont exposés bruts sans attribution d’unité. L’année de jeu peut être 656 ; lastMeal ne doit pas être passé directement à os.time comme une date civile. Une date absente ou ancienne ne prouve pas qu’un repas est dû. Les dévoreurs d’âmes progressent par les combats concernés ; leur régime n’est pas une boucle OM. Les bornes des jets de modèle ne prouvent pas le gain du prochain repas.


Soigner une fois avec une poudre

local pet = pets.equipped()
local powder = nil
for _, item in ipairs(inventory.list()) do
  if item.templateId == 2239 and item.position == -1 and item.quantity > 0 then
    powder = item
    break
  end
end
if pet and pet.life and pet.life > 0 and powder then
  local ok, reason = pets.feed(pet.uid, powder.uid, 5000)
  local after = pets.get(pet.uid)
  print(ok, reason, after and after.life)
end

returns exemple : un seul soin

2239 = Poudre d’Eniripsa dans les données consultées. Les captures montrent trois demandes séparées, PV 5→6→7→8, OCO puis Im032 et OQ/OR. Cet exemple effectue au plus une demande ; il ne remonte pas automatiquement à un maximum supposé et ne prouve pas que tous les familiers acceptent cette poudre. Le soin observé ne modifiait ni date ni dernier aliment : il ne faut pas enregistrer automatiquement un nouveau repas à chaque soin. La poudre de résurrection et un fantôme passent par un échange PNJ, pas par ce soin.


Banque et lots de familiers

for _, item in ipairs(exchange.storageItems()) do
  if item.type == 18 then
    local data = pets.decodeEffects(item.effects)
    print(item.uid, item.id, data.life, data.corpulence)
  end
end

returns exemple lecture coffre

Le coffre doit déjà être ouvert et chargé. storageItems retourne id=template et uid=exemplaire ; pets.decodeEffects analyse ses effets sans nourrir dans le coffre. Retirer les objets par l’API exchange existante, confirmer leur présence dans inventory, fermer l’échange, puis équiper avant de nourrir. Pour un lot : préparer des UID distincts avec leur aliment et leur échéance validés ; confirmer chaque équipement, donner un seul repas, relire PV/corpulence, arrêter sur erreur ou résultat ambigu. Ne pas ajouter une boucle de reconnexion ou un intervalle global supposé.


Paquets et messages observables

OM<foodUid décimal>|8
OCO<uid hex>~<template hex>~<quantité hex>~<position hex>~<effets>
OQ<foodUid décimal>|<quantité décimale>
OR<foodUid décimal>

returns référence protocole — ne pas exécuter comme Lua

Le moteur applique OCO/OCK comme remplacement complet de l’objet par UID, en préservant le reste de l’inventaire. OM entrant actualise la position ; OQ actualise la quantité ; OR entrant supprime l’objet. Une capture de soin présente OCO, Im032 puis OQ/OR, sans garantie universelle sur l’ordre. Im026 et Im027 peuvent accompagner une nourriture consommée avec pénalité. Of (f minuscule) nourrit les objets vivants ; OF entrant anime la découverte d’un objet : aucun des deux n’est un ACK de repas familier. OR sortant appartient à une réinitialisation prévue dans le code client mais non activée pour les modèles locaux ; ne pas confondre avec OR entrant. Préférer les fonctions pets au paquet brut.


Messages serveur des familiers

-- Lire les messages du journal avec les nouveaux effets du familier.

returns information

Im025 : le familier fait la fête ; Im026 : mange sans faim ; Im027 : obésité/indigestion ; Im028 : très maigre, mange avec douleur et reprend du poids ; Im029 : mange et semblait affamé ; Im030 : trop gros, faim et douleur mais amaigrissement ; Im031 : famélique, mange avec douleur ; Im032 : apprécie, également observé lors d’un soin. Im150–152 : oubli et dégradation suivant la corpulence ; Im153 : ressource refusée/rendue ; Im154 : transformation en fantôme ; Im188 : incompatibilité avec le personnage sur sa monture. Ces textes ne donnent pas une formule de perte de PV. Le retour fed n’interprète pas ces Im comme un bénéfice. Un refus sans modification des deux instantanés finit en timeout ; la cause exacte est à consulter dans les logs.


Modèles, apparats et données de jeu

local pet = pets.equipped()
if pet then
  local model = gameData.getData("items", "/I/u/" .. pet.templateId)
  print(model.n, model.ce)
end

returns exemple lecture gamedata

Le catalogue local consulté (items 1388) contient 170 modèles type 18, dont 85 portent ce (apparat) ; ces nombres dépendent de la version, à découvrir avec gameData.getSources(). Lire le modèle via /I/u/ pour ses descriptions et conditions. Les effets théoriques de itemstats et les champs ef ne sont pas les effets de l’exemplaire ni la promesse de son prochain gain. getData lève une erreur si source/chemin/base indisponible ; cette lecture est indépendante de pets. Aucun aliment n’est choisi automatiquement à partir du nom ou du dernier repas.


Chanil, fantômes, résurrection et amélioration

-- Parcours distincts : npc et exchange pour les échanges PNJ.
-- inventory.useItem(uid) pour une utilisation d’objet prévue par le client.

returns information

Oshimo est le modèle de PNJ 297 au chanil [9,21] selon les données consultées ; retrouver son identifiant d’entité sur la carte pour les fonctions npc. Le dépôt/retrait passe par familier, certificat type77 et kamas en échange ; aucun tarif ni parcours complet n’est automatisé par pets. Un fantôme type90 et une Poudre de résurrection (modèle8012) relèvent d’un échange PNJ décrit dans les dialogues, distinct du soin poudre2239. Les potions Eupéoh relèvent de l’utilisation d’objet et de ses conditions : ne pas les traiter comme des ressources à envoyer aveuglément au slot8, ni répéter une amélioration à dose unique. La réinitialisation OR sortante reste un mécanisme conditionnel non exposé : aucun modèle local ne porte son flag prp. Ces opérations annexes n’ont pas été validées par les captures de repas ; ne pas annoncer un soin, une résurrection ou une conservation de bonus à partir d’un simple envoi de commande.