Create a custom System Tag
PSDK lets you teach the map engine a brand-new System Tag, a tile of your own with its own wild encounters, battle background, terrain-based move behaviour and walking particles. This guide walks through the five engine hooks a new tag plugs into, writes every method override as a prepend, and shows where to paint the finished tag in Tiled.
The problem: a terrain the engine doesn't know yet
The built-in System Tags cover the classic terrains: grass, cave, sea, ice, sand and so on. Each one already knows how to start its own wild battles, pick a battle background and spray the right walking particle. But the moment you want something the base set does not have, say a glowing puddle that triggers its own encounter table and shows a custom battleback, no existing tag fits.
A custom System Tag is how you add that terrain without forking the engine. You declare one new constant, then plug it into the handful of engine methods that already branch on a tag, each time adding your case and falling back to the original behaviour with super. The result behaves like a first-class tag: you paint it in Tiled and the engine reacts to it on every step.
This guide assumes you already know how a tag is read and stored. If not, start with Understanding System Tags. Every method override below follows the monkey-patching convention, a prepend module calling super, never an alias.
What a custom tag plugs into
A tag is not a single switch: each behaviour lives in a different part of the engine. Adding a tag means touching up to five of them, the last one optional.
| Step | Engine hook | What it controls |
|---|---|---|
| 1 | GameData::SystemTags.system_tag_db_symbol | The readable symbol returned for the tile. |
| 2 | PFM::Environment#get_zone_type / #convert_zone_type | The zone type, which drives wild encounters. |
| 3 | Battle::Logic::BattleInfo::BACKGROUND_NAMES | The battle background shown when a fight starts on the tile. |
| 4 | Game_Map::TERRAIN_TAGS_TABLE | The location used by LocationBased moves. |
| 5 | Game_Character::PARTICLES_METHODS (optional) | The particle spawned when a character walks on the tile. |
The five snippets can live in separate script files or be merged into one, as long as the constant from step 1 is defined before the later steps reference it. Where these scripts go in a PSDK project is covered in monkey-patching.
Throughout this guide the example tag is called CustomPuddle, with the symbol :custom_puddle.
1. Declare the constant and its symbol
A tag constant is just an id produced by the gen(x, y) helper, where (x, y) is the tile's position on the System Tags tileset. Pick a cell that no existing tag uses (the built-in tags stop at row 7, so any cell below is free):
module GameData
module SystemTags
# Column 0, row 12 of the System Tags tileset, a free cell
CustomPuddle = gen 0, 12
end
end
Next, give the tag a readable symbol so the rest of the engine, and Pokémon Studio, can refer to it by name. The engine resolves a tag to its symbol in GameData::SystemTags.system_tag_db_symbol. Override it to return your symbol for the new tag and defer to the original for everything else. By convention a custom symbol starts with custom_:
module GameData
module SystemTags
# Adds the symbol of the custom tag without touching the engine method
module CustomPuddleSymbol
def system_tag_db_symbol(system_tag)
return :custom_puddle if system_tag == CustomPuddle
super
end
end
# system_tag_db_symbol is a module-level method (module_function),
# so the override is prepended onto the module's singleton class.
singleton_class.prepend(CustomPuddleSymbol)
end
end
The super call keeps every existing tag working: only CustomPuddle is handled here, anything else falls through to the engine's own case.
2. Give the tag a zone type
A zone type is the integer the engine uses to classify the ground the player stands on. It is what decides which wild encounter list applies and, in the next step, which battle background to load. Two methods produce it: get_zone_type, which reads the player's current tile, and convert_zone_type, which maps an arbitrary tag to the same value. Both live on PFM::Environment.
The built-in zone types run from 0 (building) to 10 (ice), so the next free value is 11. Override both methods to return it for the custom tag:
module PFM
class Environment
module CustomPuddleZone
def get_zone_type(ice_prio = false)
return 11 if custom_puddle?
super
end
def convert_zone_type(system_tag)
return 11 if system_tag == GameData::SystemTags::CustomPuddle
super
end
private
# Is the player standing on the custom puddle tag?
# @return [Boolean]
def custom_puddle?
@game_state.game_player.system_tag == GameData::SystemTags::CustomPuddle
end
end
prepend CustomPuddleZone
end
end
Two details matter here. Calling super with no arguments forwards ice_prio untouched, so the original priority logic keeps working for the built-in tags. And 11 is simply the next free value in this version of PSDK: if a future update adds new terrains, this number, and the background index in the next step, must move up accordingly.
3. Choose the battle background
Without this step a battle started on the tile renders on a black background. The engine picks the image from BACKGROUND_NAMES, an array of file names indexed by zone type, in Battle::Logic::BattleInfo. Because the engine shifts the index by one for any non-building zone (zone_type + 1), zone type 11 reads index 12. The array currently holds twelve entries (indices 0 to 11), so a single push appends your background at exactly index 12:
module Battle
class Logic
class BattleInfo
# zone type 11 reads index 12 after the engine's +1 shift,
# so this must be the entry pushed after the last built-in one
BACKGROUND_NAMES.push('back_custom_puddle')
end
end
end
The pushed string is the name of an image in your project's graphics/battlebacks folder, here back_custom_puddle. The ordering is load-bearing: the new name has to land at the index your zone type points to, so push it once, after the engine has built the default array, and do not insert it elsewhere.
4. Map the tag for LocationBased moves
Some moves change their behaviour with the ground they are used on: their LocationBased logic reads a location symbol rather than the raw tag. That mapping lives in Game_Map::TERRAIN_TAGS_TABLE. Add an entry so the engine treats your tile like a known location, here shallow water:
class Game_Map
# LocationBased moves treat the custom puddle like shallow water
TERRAIN_TAGS_TABLE[GameData::SystemTags::CustomPuddle] = :shallow_water
end
Reuse whichever existing location symbol fits the terrain you are modelling; :shallow_water is one of several the engine already understands.
5. Add walking particles (optional)
This step is purely cosmetic: it spawns a particle when a character steps on the tile. Skip it entirely if you do not want one. The engine looks up PARTICLES_METHODS, a hash keyed by tag, to know which method to call, so add your tag and define the matching method. The method asks Yuki::Particles to spawn a particle identified by your symbol:
class Game_Character
PARTICLES_METHODS[GameData::SystemTags::CustomPuddle] = :particle_push_custom_puddle
# Spawn the custom puddle particle when a character steps on the tag
def particle_push_custom_puddle
Yuki::Particles.add_particle(self, :custom_puddle)
end
end
The particle itself, the :custom_puddle animation, is defined in Data/Animations/Particles.rb. Open that file, scroll to the bottom just above the save, line, and add your animation next to the many existing examples. After editing it, regenerate the compiled particle data from a terminal opened in Data/Animations:
ruby Particles.rb
Paint the tag in Tiled
The constant only becomes useful once the tag is painted on a map. System Tags are painted in Tiled on the systemtags layer, from the System Tags tileset image at Data/Tiled/Assets/prio_w.png. Add your tile on the first free cell of that image, matching the (x, y) position you passed to gen, then paint it wherever the terrain should apply. How the systemtags layer works is detailed in Understanding System Tags.
Conclusion
- A custom System Tag is a constant produced by
gen(x, y)that you plug into the engine methods already branching on a tag. - Every method override is a
prependmodule that handles your tag and callssuperfor the rest, never analias. - The five hooks are: the symbol (
system_tag_db_symbol), the zone type (get_zone_type/convert_zone_type), the battle background (BACKGROUND_NAMES), theLocationBasedlocation (TERRAIN_TAGS_TABLE) and, optionally, the particle (PARTICLES_METHODS). - The zone type and the background index are version-specific numbers:
11and index12are the next free slots today, and shift up if PSDK adds terrains. - Reuse an existing location symbol such as
:shallow_waterforLocationBasedmoves rather than inventing one. - Finish by painting the tile on the
systemtagslayer fromData/Tiled/Assets/prio_w.png.