diff --git a/content/psdk/data/02-item-descriptor.md b/content/psdk/data/02-item-descriptor.md new file mode 100644 index 0000000..33bc25a --- /dev/null +++ b/content/psdk/data/02-item-descriptor.md @@ -0,0 +1,259 @@ +--- +title: "How to define an item's behavior in PSDK?" +slug: item-descriptor +sidebar_position: 2 +description: "This guide explains how PSDK turns item data into in-game behavior with the ItemDescriptor module: the wrapper read by every UI, and the definition functions telling what an item does from the bag, on a creature or on a move." +--- + +This guide explains how PSDK turns item data into in-game behavior with the **ItemDescriptor** module: the wrapper read by every UI, and the definition functions telling what an item does from the bag, on a creature or on a move. + +## From data to behavior + +A `Studio::Item` only holds **data**: price, flags, descriptions (see [How to access game data in PSDK?](/psdk/data/access-game-data)). It says nothing about what should happen when the player uses the item. That part lives in `PFM::ItemDescriptor` (`scripts/3 Studio/2 Data/0 Item/000 ItemDescriptor.rb` in the PSDK sources). + +When the player uses an item, the UI calls `PFM::ItemDescriptor.actions(item_id)` and receives a **wrapper** describing what to do. Many interfaces call this wrapper `extend_data`. Your job, when creating a custom item, is to register the right definition so the wrapper comes out correctly: you almost never build a wrapper yourself. + +## What happens when the player uses an item + +The complete flow, from the bag to the effect: + +1. The bag calls `PFM::ItemDescriptor.actions(item_id)` and gets the wrapper. +2. If `no_effect` or `chen` is set, the matching message shows and everything stops there. +3. If `open_party` is set, the party opens; for each creature, the UI calls `on_creature_choice` (your usability block) to know whether the item can be used on it. +4. If `open_skill` is set, the move selection opens on the chosen creature, filtered the same way by `on_skill_choice`. +5. The use function matching the context finally runs: `on_use` from the bag, `on_creature_use` or `on_skill_use` on the map. In battle, the UI calls `bind` and the battle engine triggers `execute_battle_action` during the turn. + +Each definition function below fills one or several of these steps. You only write the blocks; PSDK drives the flow. + +## What the wrapper tells the UIs + +The wrapper carries flags that the bag, party and battle UIs read: + +| Property | Meaning when set | +| -------------------- | --------------------------------------------------------------------------- | +| `no_effect` | The UI shows the "this item has no effect" message | +| `chen` | The UI shows the "it's not time to use this item" message | +| `open_party` | The UI opens the party so the player picks a creature | +| `open_skill` | The UI opens the move selection on the chosen creature | +| `open_skill_learn` | The party UI opens the move teaching UI for the move carried by the item | +| `stone_evolve` | The UI shows whether the chosen creature can evolve with this item | +| `use_before_telling` | The item acts before the "item is used" message, so it can still refuse | +| `skill_message_id` | ID of the message shown in the Summary UI during move selection | + +It also carries the behavior functions filled by your definitions: `on_creature_choice` and `on_creature_use` (can the item be used on this creature, and what it does), `on_skill_choice` and `on_skill_use` (same for a move), `on_use` (action from the bag), plus `bind(scene, creature, skill)` and `execute_battle_action` used by the battle engine. + +One thing to keep in mind: your blocks receive **live instances**, not data records. `creature` is a `PFM::Pokemon` (with its current HP, status and moves) and `skill` is a `PFM::Skill` (a learned move with its remaining PP). Their `data` method bridges back to the Studio records: in the examples below, `skill.data.pp` reads the PP defined in Pokémon Studio while `skill.pp` reads the current PP in game. + +## Where and how to register a definition + +Definitions are plain calls to `PFM::ItemDescriptor.define_*` functions, written in your own script in your project's `scripts/` folder. Each definition targets either: + +- a **db_symbol** (`:sacred_ash`) for one specific item, or +- a **`Studio::Item` subclass** (`Studio::PPIncreaseItem`) for a whole family of items. + +When both exist, the db_symbol definition wins over the class one. PSDK also enforces a definition order: you cannot register a use action before its usability check (the engine raises an explicit error if you try). + +## Items used from the bag + +For an item that simply does something from the bag, register `define_bag_use`. The block receives the item and the calling scene. The vanilla Sacred Ash is a complete example, together with its "it's not time" condition registered through `define_chen_prevention`: + +```ruby +PFM::ItemDescriptor.define_chen_prevention(:sacred_ash) do + next $actors.none? { |creature| creature.dead? && !creature.egg? } +end + +PFM::ItemDescriptor.define_bag_use(:sacred_ash) do + $actors.compact.each do |pkmn| + next unless pkmn.hp <= 0 + + pkmn.cure + pkmn.hp = pkmn.max_hp + pkmn.skills_set.compact.each { |j| j.pp = j.ppmax } + $scene.display_message(parse_text(22, 115, PFM::Text::PKNICK[0] => pkmn.given_name)) + end +end +``` + +By default, the bag announces "the item is used", then runs your block, then consumes the item. That order is wrong for items that can turn out to be useless at the exact moment of use: think of a Repel while another repel is still active. Announcing and consuming first would waste the item. + +For those items, set the second parameter of `define_bag_use`, `use_before_telling`, to `true`: your block runs **first** and acts as a last-second check. If the item cannot serve, show your own explanation message and return `:unused`. The bag then skips the "item is used" message and does not consume the item: from the player's point of view, nothing happened. + +The vanilla Repel is the canonical example. It also targets a **class** this time (the whole `Studio::RepelItem` family, where Sacred Ash targeted one db_symbol). With no active repel it applies; otherwise it explains why and refuses: + +```ruby +PFM::ItemDescriptor.define_bag_use(Studio::RepelItem, true) do |item, scene| + if PFM.game_state.get_repel_count <= 0 + $game_temp.last_repel_used_id = item.id + next PFM.game_state.set_repel_count(Studio::RepelItem.from(item).repel_count) + end + + scene.display_message_and_wait(parse_text(22, 47)) + next :unused +end +``` + +You may wonder why the Repel does not simply use a chen prevention, since both mechanisms refuse a usage. The difference is the feedback: `define_chen_prevention` always shows the **generic** "it's not time to use this item" message, while the `:unused` path lets the item display its **own** explanation first (here, the dedicated "a repel is already active" text) and run logic before deciding. Pick chen when a generic refusal is enough, like the Sacred Ash with no fainted creature; pick `use_before_telling` and `:unused` when the player deserves a specific explanation. + +## Items used on a creature + +This family needs up to three definitions: whether the item can be used on the chosen creature, what it does on the map, and what it does in battle. + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(klass_or_db_symbol) do |item, creature| + # Return true if the item can be used on this creature +end + +PFM::ItemDescriptor.define_on_creature_use(klass_or_db_symbol) do |item, creature, scene| + # Apply the item on the creature (map context) +end + +PFM::ItemDescriptor.define_on_creature_battler_use(klass_or_db_symbol) do |item, creature, scene| + # Apply the item on the creature (battle context) +end +``` + +Registering the usability automatically sets `open_party` on the wrapper, so the bag knows it must open the party. The vanilla Gracidea is a compact real example: + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(:gracidea) do |_item, creature| + next false if creature.egg? + next false unless creature.db_symbol == :shaymin + next false if creature.form == creature.shaymin_form(:gracidea) + + next true +end + +PFM::ItemDescriptor.define_on_creature_use(:gracidea) do |_item, _creature, scene| + scene.update_pokemon_form(:gracidea) +end +``` + +For the battle side, the stat boost items (X Attack and friends, `Studio::StatBoostItem`) show the complete pattern. Note the inverted chen prevention: these items are battle-only, so "it's not time" is raised on the map. In battle, the creature is a `PFM::PokemonBattler` (get its battler API with `.from`) and the scene is the battle scene, whose `logic` exposes the handlers your action should go through: + +```ruby +PFM::ItemDescriptor.define_chen_prevention(Studio::StatBoostItem) do + next !$game_temp.in_battle +end + +PFM::ItemDescriptor.define_on_creature_usability(Studio::StatBoostItem) do |item, creature| + next false if creature.egg? || !PFM::PokemonBattler.from(creature).can_fight? + + next creature.send(:"#{item.stat}_stage") < 6 +end + +PFM::ItemDescriptor.define_on_creature_battler_use(Studio::StatBoostItem) do |item, creature, scene| + boost_item = Studio::StatBoostItem.from(item) + creature.loyalty -= boost_item.loyalty_malus + + scene.logic.stat_change_handler.stat_change(boost_item.stat, boost_item.count, creature) +end +``` + +## Items used on a move of a creature + +One more layer: after the creature usability, you define whether the item can act on the chosen move, then what it does. Registering the move usability sets `open_skill` on the wrapper; the optional second parameter is the `skill_message_id` shown in the Summary UI. + +The vanilla PP Up shows the full chain with current property names: + +```ruby +PFM::ItemDescriptor.define_chen_prevention(Studio::PPIncreaseItem) do + next $game_temp.in_battle +end + +PFM::ItemDescriptor.define_on_creature_usability(Studio::PPIncreaseItem) do |_, creature| + next false if creature.egg? + + moves = $game_temp.in_battle ? PFM::PokemonBattler.from(creature).moveset : creature.skills_set + next moves.any? { |move| (move.data.pp * 8 / 5) > move.ppmax } +end + +PFM::ItemDescriptor.define_on_move_usability(Studio::PPIncreaseItem, 35) do |_, skill| + next (skill.data.pp * 8 / 5) > skill.ppmax +end + +PFM::ItemDescriptor.define_on_move_use(Studio::PPIncreaseItem) do |item, creature, skill, scene| + creature.loyalty -= Studio::HealingItem.from(item).loyalty_malus + if Studio::PPIncreaseItem.from(item).is_max + skill.ppmax = skill.data.pp * 8 / 5 + else + skill.ppmax += skill.data.pp * 1 / 5 + end + skill.pp += 99 + scene.display_message_and_wait(parse_text(22, 117, PFM::Text::MOVE[0] => skill.name)) +end +``` + +For the battle context, `define_on_battle_move_use` follows the same shape with the same block parameters (`item, creature, skill, scene`). + +:::note + +Both battle definitions fill the same slot of the wrapper (`action_to_push`). The vanilla PP-restoring items (`Studio::PPHealItem`) actually register their battle action through `define_on_creature_battler_use` with the same four-parameter block; the result is identical. Use `define_on_battle_move_use` for readability when your item targets a move. + +::: + +## Items calling a common event + +A `Studio::EventItem` calls a common event when used; the event to call is **data** (its `event_id` property, set in Pokémon Studio). By default the call is unconditional; `define_event_condition` adds a usability condition for a given event. Note that it targets the **event ID**, not the item. The vanilla Bicycle condition: + +```ruby +PFM::ItemDescriptor.define_event_condition(11) do + next false if $game_player.surfing? + + next $game_switches[Yuki::Sw::EV_Bicycle] || + $game_switches[Yuki::Sw::Env_CanFly] || + $game_switches[Yuki::Sw::Env_CanDig] +end +``` + +Worth reading in the sources: the whole EventItem support is itself built with the public API. It is a `define_bag_use(Studio::EventItem, true)` that checks the registered condition and returns `:unused` when it refuses (`001 EventItem.rb`). Everything in this guide rests on the same few bricks. + +## A custom item from A to Z + +Say you want a Golden Apple that fully heals one creature, usable from the bag on the map. + +1. In **Pokémon Studio**, create the item with the db_symbol `golden_apple`, enable its **usable on map** flag (`is_map_usable`) and its **consumable** flag (`is_limited`): consumption is data, not code. +2. In your own script, register the usability and the action: + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(:golden_apple) do |_item, creature| + next false if creature.egg? + + next creature.hp < creature.max_hp +end + +PFM::ItemDescriptor.define_on_creature_use(:golden_apple) do |_item, creature, scene| + creature.hp = creature.max_hp + scene.display_message_and_wait("#{creature.given_name} is fully healed!") +end +``` + +That is all. The bag sees `open_party` (set automatically by the usability definition) and opens the team, eggs and full-HP creatures are not valid targets, and using the item heals then shows the message. The `is_map_usable` flag you set in Studio already raises `chen` in battle, and the bag handles the consumption because of `is_limited`. + +To go further, read the engine's own definitions: each file under `scripts/3 Studio/2 Data/0 Item/` pairs a `Studio::Item` subclass with its descriptor definitions (`003 HealingItem.rb`, `300 PPIncreaseItem.rb`...), and `000 ItemDescriptor.rb` holds the per-db_symbol special cases. They are ready-made recipes for almost every classic item behavior. + +## What PSDK handles automatically + +You do not have to define everything yourself. When building the wrapper, `actions` fills several things on its own: + +- `chen` is raised automatically when the item's `is_map_usable` or `is_battle_usable` flag (edited in Pokémon Studio) does not match the current context. +- `no_effect` is raised for unknown items (the `:__undef__` placeholder). +- `stone_evolve` is set for any `Studio::StoneItem`, and `open_skill_learn` for any `Studio::TechItem`. +- In battle, the map-only actions are voided so an item never runs its map behavior mid-fight. + +:::note + +Older PSDK resources name these functions `define_on_pokemon_usability`, `define_on_pokemon_use` and `define_on_pokemon_battler_use`. They were renamed with `creature` in current PSDK versions, as in all the examples above. + +::: + +## Conclusion + +- `Studio::Item` holds the data; `PFM::ItemDescriptor` holds the behavior. The UIs get both through the wrapper returned by `PFM::ItemDescriptor.actions`. +- Register definitions in your own script, targeting a db_symbol (one item) or a `Studio::Item` subclass (a family); the db_symbol wins. +- From the bag: `define_bag_use`; when the item can fail at the moment of use, `use_before_telling` makes the block a last-second check and `:unused` refuses without consuming. +- On a creature: `define_on_creature_usability`, then `define_on_creature_use` (map) and `define_on_creature_battler_use` (battle). +- On a move: add `define_on_move_usability`, then `define_on_move_use` (map) and `define_on_battle_move_use` (battle). +- "It's not time" conditions go through `define_chen_prevention`; common event items through `define_event_condition`. +- The `is_map_usable` and `is_battle_usable` flags from Pokémon Studio are enforced automatically, before any of your code runs; `is_limited` controls the consumption. +- The blocks receive live `PFM::Pokemon` and `PFM::Skill` instances; their `data` method bridges back to the Studio records. +- The vanilla definitions under `scripts/3 Studio/2 Data/0 Item/` are ready-made recipes to copy from. diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/psdk/data/02-item-descriptor.md b/i18n/fr/docusaurus-plugin-content-docs/current/psdk/data/02-item-descriptor.md new file mode 100644 index 0000000..f13270b --- /dev/null +++ b/i18n/fr/docusaurus-plugin-content-docs/current/psdk/data/02-item-descriptor.md @@ -0,0 +1,259 @@ +--- +title: "Comment définir le comportement d'un objet dans PSDK ?" +slug: definir-le-comportement-d-un-objet +sidebar_position: 2 +description: "Ce guide explique comment PSDK transforme les données d'un objet en comportement en jeu avec le module ItemDescriptor : le wrapper lu par toutes les UI, et les fonctions de définition qui disent ce qu'un objet fait depuis le sac, sur une créature ou sur une capacité." +--- + +Ce guide explique comment PSDK transforme les données d'un objet en comportement en jeu avec le module **ItemDescriptor** : le wrapper lu par toutes les UI, et les fonctions de définition qui disent ce qu'un objet fait depuis le sac, sur une créature ou sur une capacité. + +## Des données au comportement + +Un `Studio::Item` ne contient que des **données** : prix, drapeaux, descriptions (voir [Comment accéder aux données du jeu dans PSDK ?](/psdk/data/acceder-aux-donnees-du-jeu)). Il ne dit rien de ce qui doit se passer quand le joueur utilise l'objet. Cette partie vit dans `PFM::ItemDescriptor` (`scripts/3 Studio/2 Data/0 Item/000 ItemDescriptor.rb` dans les sources de PSDK). + +Quand le joueur utilise un objet, l'UI appelle `PFM::ItemDescriptor.actions(item_id)` et reçoit un **wrapper** décrivant quoi faire. Beaucoup d'interfaces appellent ce wrapper `extend_data`. Votre travail, quand vous créez un objet personnalisé, est d'enregistrer la bonne définition pour que le wrapper sorte correctement : on ne construit presque jamais un wrapper soi-même. + +## Que se passe-t-il quand le joueur utilise un objet + +Le déroulé complet, du sac jusqu'à l'effet : + +1. Le sac appelle `PFM::ItemDescriptor.actions(item_id)` et obtient le wrapper. +2. Si `no_effect` ou `chen` est levé, le message correspondant s'affiche et tout s'arrête là. +3. Si `open_party` est levé, l'équipe s'ouvre ; pour chaque créature, l'UI appelle `on_creature_choice` (votre bloc d'utilisabilité) pour savoir si l'objet peut être utilisé sur elle. +4. Si `open_skill` est levé, la sélection de capacité s'ouvre sur la créature choisie, filtrée de la même façon par `on_skill_choice`. +5. La fonction d'utilisation correspondant au contexte s'exécute enfin : `on_use` depuis le sac, `on_creature_use` ou `on_skill_use` sur la carte. En combat, l'UI appelle `bind` et le moteur de combat déclenche `execute_battle_action` pendant le tour. + +Chaque fonction de définition ci-dessous remplit une ou plusieurs de ces étapes. Vous n'écrivez que les blocs ; PSDK pilote le déroulé. + +## Ce que le wrapper dit aux UI + +Le wrapper porte des drapeaux que le sac, l'équipe et les UI de combat lisent : + +| Propriété | Signification quand elle est levée | +| -------------------- | ---------------------------------------------------------------------------- | +| `no_effect` | L'UI affiche le message « cet objet n'a aucun effet » | +| `chen` | L'UI affiche le message « ce n'est pas le moment d'utiliser cet objet » | +| `open_party` | L'UI ouvre l'équipe pour que le joueur choisisse une créature | +| `open_skill` | L'UI ouvre la sélection de capacité sur la créature choisie | +| `open_skill_learn` | L'UI d'équipe ouvre l'apprentissage de la capacité portée par l'objet | +| `stone_evolve` | L'UI indique si la créature choisie peut évoluer avec cet objet | +| `use_before_telling` | L'objet agit avant le message « l'objet est utilisé », donc peut encore refuser | +| `skill_message_id` | ID du message affiché dans l'UI Résumé pendant la sélection de capacité | + +Il porte aussi les fonctions de comportement remplies par vos définitions : `on_creature_choice` et `on_creature_use` (l'objet est-il utilisable sur cette créature, et ce qu'il fait), `on_skill_choice` et `on_skill_use` (pareil pour une capacité), `on_use` (action depuis le sac), plus `bind(scene, creature, skill)` et `execute_battle_action` utilisées par le moteur de combat. + +Une chose à garder en tête : vos blocs reçoivent des **instances vivantes**, pas des enregistrements de données. `creature` est un `PFM::Pokemon` (avec ses PV, son statut et ses capacités du moment) et `skill` est un `PFM::Skill` (une capacité apprise avec ses PP restants). Leur méthode `data` fait le pont vers les enregistrements Studio : dans les exemples plus bas, `skill.data.pp` lit les PP définis dans Pokémon Studio tandis que `skill.pp` lit les PP courants en jeu. + +## Où et comment enregistrer une définition + +Les définitions sont de simples appels aux fonctions `PFM::ItemDescriptor.define_*`, écrits dans votre propre script dans le dossier `scripts/` de votre projet. Chaque définition cible soit : + +- un **db_symbol** (`:sacred_ash`) pour un objet précis, soit +- une **sous-classe de `Studio::Item`** (`Studio::PPIncreaseItem`) pour toute une famille d'objets. + +Quand les deux existent, la définition par db_symbol gagne sur celle par classe. PSDK impose aussi un ordre de définition : on ne peut pas enregistrer une action d'utilisation avant son test d'utilisabilité (le moteur lève une erreur explicite si on essaie). + +## Les objets utilisés depuis le sac + +Pour un objet qui fait simplement quelque chose depuis le sac, enregistrez `define_bag_use`. Le bloc reçoit l'objet et la scène appelante. La Cendre Sacrée vanilla est un exemple complet, avec sa condition « ce n'est pas le moment » enregistrée via `define_chen_prevention` : + +```ruby +PFM::ItemDescriptor.define_chen_prevention(:sacred_ash) do + next $actors.none? { |creature| creature.dead? && !creature.egg? } +end + +PFM::ItemDescriptor.define_bag_use(:sacred_ash) do + $actors.compact.each do |pkmn| + next unless pkmn.hp <= 0 + + pkmn.cure + pkmn.hp = pkmn.max_hp + pkmn.skills_set.compact.each { |j| j.pp = j.ppmax } + $scene.display_message(parse_text(22, 115, PFM::Text::PKNICK[0] => pkmn.given_name)) + end +end +``` + +Par défaut, le sac annonce « l'objet est utilisé », puis exécute votre bloc, puis consomme l'objet. Cet ordre est mauvais pour les objets qui peuvent se révéler inutiles au moment précis de l'usage : pensez à un Repousse alors qu'un autre repousse est encore actif. Annoncer et consommer d'abord gaspillerait l'objet. + +Pour ces objets, passez le second paramètre de `define_bag_use`, `use_before_telling`, à `true` : votre bloc s'exécute **en premier** et joue le rôle de contrôle de dernière seconde. Si l'objet ne peut pas servir, affichez votre propre message d'explication et renvoyez `:unused`. Le sac saute alors le message « l'objet est utilisé » et ne consomme pas l'objet : du point de vue du joueur, il ne s'est rien passé. + +Le Repousse vanilla est l'exemple canonique. Il cible aussi une **classe** cette fois (toute la famille `Studio::RepelItem`, là où la Cendre Sacrée ciblait un db_symbol). Sans repousse actif il s'applique ; sinon il explique pourquoi et refuse : + +```ruby +PFM::ItemDescriptor.define_bag_use(Studio::RepelItem, true) do |item, scene| + if PFM.game_state.get_repel_count <= 0 + $game_temp.last_repel_used_id = item.id + next PFM.game_state.set_repel_count(Studio::RepelItem.from(item).repel_count) + end + + scene.display_message_and_wait(parse_text(22, 47)) + next :unused +end +``` + +On peut se demander pourquoi le Repousse n'utilise pas simplement une prévention chen, puisque les deux mécanismes refusent une utilisation. La différence, c'est le retour au joueur : `define_chen_prevention` affiche toujours le message **générique** « ce n'est pas le moment d'utiliser cet objet », tandis que la voie `:unused` laisse l'objet afficher d'abord sa **propre** explication (ici, le texte dédié « un repousse est déjà actif ») et exécuter de la logique avant de trancher. Choisissez chen quand un refus générique suffit, comme la Cendre Sacrée sans créature K.O. ; choisissez `use_before_telling` et `:unused` quand le joueur mérite une explication précise. + +## Les objets utilisés sur une créature + +Cette famille demande jusqu'à trois définitions : si l'objet peut être utilisé sur la créature choisie, ce qu'il fait sur la carte, et ce qu'il fait en combat. + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(klass_ou_db_symbol) do |item, creature| + # Return true if the item can be used on this creature +end + +PFM::ItemDescriptor.define_on_creature_use(klass_ou_db_symbol) do |item, creature, scene| + # Apply the item on the creature (map context) +end + +PFM::ItemDescriptor.define_on_creature_battler_use(klass_ou_db_symbol) do |item, creature, scene| + # Apply the item on the creature (battle context) +end +``` + +Enregistrer l'utilisabilité lève automatiquement `open_party` sur le wrapper, donc le sac sait qu'il doit ouvrir l'équipe. La Gracidée vanilla est un exemple réel et compact : + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(:gracidea) do |_item, creature| + next false if creature.egg? + next false unless creature.db_symbol == :shaymin + next false if creature.form == creature.shaymin_form(:gracidea) + + next true +end + +PFM::ItemDescriptor.define_on_creature_use(:gracidea) do |_item, _creature, scene| + scene.update_pokemon_form(:gracidea) +end +``` + +Pour le versant combat, les objets de boost de statistiques (Attaque + et compagnie, `Studio::StatBoostItem`) montrent le motif complet. Notez la prévention chen inversée : ces objets sont réservés au combat, donc « ce n'est pas le moment » est levé sur la carte. En combat, la créature est un `PFM::PokemonBattler` (son API de combattant s'obtient avec `.from`) et la scène est la scène de combat, dont la `logic` expose les handlers par lesquels votre action doit passer : + +```ruby +PFM::ItemDescriptor.define_chen_prevention(Studio::StatBoostItem) do + next !$game_temp.in_battle +end + +PFM::ItemDescriptor.define_on_creature_usability(Studio::StatBoostItem) do |item, creature| + next false if creature.egg? || !PFM::PokemonBattler.from(creature).can_fight? + + next creature.send(:"#{item.stat}_stage") < 6 +end + +PFM::ItemDescriptor.define_on_creature_battler_use(Studio::StatBoostItem) do |item, creature, scene| + boost_item = Studio::StatBoostItem.from(item) + creature.loyalty -= boost_item.loyalty_malus + + scene.logic.stat_change_handler.stat_change(boost_item.stat, boost_item.count, creature) +end +``` + +## Les objets utilisés sur une capacité d'une créature + +Une couche de plus : après l'utilisabilité sur la créature, on définit si l'objet peut agir sur la capacité choisie, puis ce qu'il fait. Enregistrer l'utilisabilité sur la capacité lève `open_skill` sur le wrapper ; le second paramètre optionnel est le `skill_message_id` affiché dans l'UI Résumé. + +Le PP Plus vanilla montre la chaîne complète avec les noms de propriétés actuels : + +```ruby +PFM::ItemDescriptor.define_chen_prevention(Studio::PPIncreaseItem) do + next $game_temp.in_battle +end + +PFM::ItemDescriptor.define_on_creature_usability(Studio::PPIncreaseItem) do |_, creature| + next false if creature.egg? + + moves = $game_temp.in_battle ? PFM::PokemonBattler.from(creature).moveset : creature.skills_set + next moves.any? { |move| (move.data.pp * 8 / 5) > move.ppmax } +end + +PFM::ItemDescriptor.define_on_move_usability(Studio::PPIncreaseItem, 35) do |_, skill| + next (skill.data.pp * 8 / 5) > skill.ppmax +end + +PFM::ItemDescriptor.define_on_move_use(Studio::PPIncreaseItem) do |item, creature, skill, scene| + creature.loyalty -= Studio::HealingItem.from(item).loyalty_malus + if Studio::PPIncreaseItem.from(item).is_max + skill.ppmax = skill.data.pp * 8 / 5 + else + skill.ppmax += skill.data.pp * 1 / 5 + end + skill.pp += 99 + scene.display_message_and_wait(parse_text(22, 117, PFM::Text::MOVE[0] => skill.name)) +end +``` + +Pour le contexte de combat, `define_on_battle_move_use` suit la même forme avec les mêmes paramètres de bloc (`item, creature, skill, scene`). + +:::note + +Les deux définitions de combat remplissent le même emplacement du wrapper (`action_to_push`). Les objets vanilla de restauration de PP (`Studio::PPHealItem`) enregistrent d'ailleurs leur action de combat via `define_on_creature_battler_use` avec le même bloc à quatre paramètres ; le résultat est identique. Utilisez `define_on_battle_move_use` pour la lisibilité quand votre objet cible une capacité. + +::: + +## Les objets qui appellent un événement commun + +Un `Studio::EventItem` appelle un événement commun à l'utilisation ; l'événement à appeler est de la **donnée** (sa propriété `event_id`, définie dans Pokémon Studio). Par défaut l'appel est inconditionnel ; `define_event_condition` ajoute une condition d'utilisabilité pour un événement donné. Notez qu'elle cible l'**ID de l'événement**, pas l'objet. La condition vanilla de la Bicyclette : + +```ruby +PFM::ItemDescriptor.define_event_condition(11) do + next false if $game_player.surfing? + + next $game_switches[Yuki::Sw::EV_Bicycle] || + $game_switches[Yuki::Sw::Env_CanFly] || + $game_switches[Yuki::Sw::Env_CanDig] +end +``` + +À lire dans les sources : tout le support des EventItem est lui-même construit avec l'API publique. C'est un `define_bag_use(Studio::EventItem, true)` qui vérifie la condition enregistrée et renvoie `:unused` quand elle refuse (`001 EventItem.rb`). Tout ce guide repose sur ces quelques briques. + +## Un objet personnalisé de A à Z + +Disons que vous voulez une Pomme Dorée qui soigne entièrement une créature, utilisable depuis le sac sur la carte. + +1. Dans **Pokémon Studio**, créez l'objet avec le db_symbol `golden_apple`, activez son drapeau **utilisable sur la carte** (`is_map_usable`) et son drapeau **consommable** (`is_limited`) : la consommation, c'est de la donnée, pas du code. +2. Dans votre propre script, enregistrez l'utilisabilité et l'action : + +```ruby +PFM::ItemDescriptor.define_on_creature_usability(:golden_apple) do |_item, creature| + next false if creature.egg? + + next creature.hp < creature.max_hp +end + +PFM::ItemDescriptor.define_on_creature_use(:golden_apple) do |_item, creature, scene| + creature.hp = creature.max_hp + scene.display_message_and_wait("#{creature.given_name} est entièrement soigné !") +end +``` + +C'est tout. Le sac voit `open_party` (levé automatiquement par la définition d'utilisabilité) et ouvre l'équipe, les œufs et les créatures aux PV pleins ne sont pas des cibles valides, et utiliser l'objet soigne puis affiche le message. Le drapeau `is_map_usable` posé dans Studio lève déjà `chen` en combat, et le sac gère la consommation grâce à `is_limited`. + +Pour aller plus loin, lisez les définitions du moteur lui-même : chaque fichier sous `scripts/3 Studio/2 Data/0 Item/` associe une sous-classe de `Studio::Item` à ses définitions de descripteur (`003 HealingItem.rb`, `300 PPIncreaseItem.rb`...), et `000 ItemDescriptor.rb` contient les cas particuliers par db_symbol. Ce sont des recettes prêtes à l'emploi pour presque tous les comportements d'objets classiques. + +## Ce que PSDK gère automatiquement + +Vous n'avez pas tout à définir vous-même. En construisant le wrapper, `actions` remplit plusieurs choses toute seule : + +- `chen` est levé automatiquement quand le drapeau `is_map_usable` ou `is_battle_usable` de l'objet (édité dans Pokémon Studio) ne correspond pas au contexte courant. +- `no_effect` est levé pour les objets inconnus (l'entité de remplacement `:__undef__`). +- `stone_evolve` est levé pour tout `Studio::StoneItem`, et `open_skill_learn` pour tout `Studio::TechItem`. +- En combat, les actions réservées à la carte sont neutralisées : un objet n'exécute jamais son comportement carte en plein combat. + +:::note + +Les ressources PSDK plus anciennes nomment ces fonctions `define_on_pokemon_usability`, `define_on_pokemon_use` et `define_on_pokemon_battler_use`. Elles ont été renommées avec `creature` dans les versions actuelles de PSDK, comme dans tous les exemples ci-dessus. + +::: + +## Conclusion + +- `Studio::Item` porte les données ; `PFM::ItemDescriptor` porte le comportement. Les UI obtiennent les deux via le wrapper renvoyé par `PFM::ItemDescriptor.actions`. +- On enregistre ses définitions dans son propre script, en ciblant un db_symbol (un objet) ou une sous-classe de `Studio::Item` (une famille) ; le db_symbol gagne. +- Depuis le sac : `define_bag_use` ; quand l'objet peut échouer au moment de l'usage, `use_before_telling` fait du bloc un contrôle de dernière seconde et `:unused` refuse sans consommer. +- Sur une créature : `define_on_creature_usability`, puis `define_on_creature_use` (carte) et `define_on_creature_battler_use` (combat). +- Sur une capacité : ajouter `define_on_move_usability`, puis `define_on_move_use` (carte) et `define_on_battle_move_use` (combat). +- Les conditions « ce n'est pas le moment » passent par `define_chen_prevention` ; les objets à événement commun par `define_event_condition`. +- Les drapeaux `is_map_usable` et `is_battle_usable` de Pokémon Studio sont appliqués automatiquement, avant tout code à vous ; `is_limited` contrôle la consommation. +- Les blocs reçoivent des instances vivantes `PFM::Pokemon` et `PFM::Skill` ; leur méthode `data` fait le pont vers les enregistrements Studio. +- Les définitions vanilla sous `scripts/3 Studio/2 Data/0 Item/` sont des recettes prêtes à copier.