
**System Tags** are per-tile markers that tell the PSDK map engine how a tile behaves: which tiles start wild battles, let you surf, slide on ice, jump a ledge or climb stairs. This guide explains how the engine stores and reads them, the API to query the tile under or in front of a character, and the full catalogue of tags grouped by role.

## The problem: making the terrain react

When a player walks into tall grass, a wild battle can start. When they step onto a ledge, the character hops down and cannot climb back up. When they reach a patch of ice, they keep sliding until a wall stops them. Surf only works on water, the bike refuses to climb certain ledges, and counters count steps differently in a swamp.

None of this is hardcoded per map. Every one of those behaviours is the engine reading a **single marker on the tile** and acting on it. That marker is a System Tag: an integer attached to a tileset tile, looked up by the map engine on every move. Understanding System Tags is understanding how PSDK turns a painted map into terrain that reacts.

System Tags are distinct from RPG Maker XP's historical `terrain_tag`. The old terrain tag still exists (`Game_Character#terrain_tag`), but PSDK's own map logic is driven by System Tags, which are richer and tied to gameplay symbols.

## Where the tags live

A System Tag is stored **per tileset, not per map**. The engine keeps a global table `$data_system_tags` indexed by tileset id, and each map loads the row for its own tileset when it is set up:

```ruby
# Game_Map#load_systemtags, called from Game_Map#setup
$data_system_tags[@map.tileset_id] ||= Array.new($data_tilesets[@map.tileset_id].priorities.xsize, 0)
@system_tags = $data_system_tags[@map.tileset_id]
```

So `@system_tags[tile_id]` gives the tag of a given tile in the tileset. A value of `0` means "no tag". Because a map cell stacks up to three tile layers, the engine reads them from the **top layer down** and returns the first non-zero tag it finds.

You do not set System Tags in code: they are painted in **Tiled**, the map editor PSDK maps are built with. Each map is a `.tmx` file under `Data/Tiled/Maps`, and the System Tags live on dedicated tile layers: `systemtags` for the terrain tags, plus `systemtags_bridge1` and `systemtags_bridge2` for bridges. Painting a tag means placing a tile from the System Tags tileset, whose tiles map to the constants listed further down. Pokémon Studio is the project hub that opens a map in Tiled for you; it does not paint the tags itself.

Each tag constant is generated by a helper:

```ruby
# GameData::SystemTags
def gen(x, y)
  return 384 + x + (y * 8)
end

TGrass = gen 5, 0
TIce   = gen 1, 0
```

Here `(x, y)` is the tag's position in the System Tags tileset image (8 columns wide). You never need the raw number in your own code: a tag is always referred to by its **constant name**, like `GameData::SystemTags::TGrass`.

## Reading a tag from a script

All the reading methods live on `Game_Character` (so every event, NPC and the player share them) and on `Game_Map`. This is the part you actually call from an event's script command.

The two most common queries are the tile the character stands on and the tile right in front of them:

```ruby
# The tag under the player
$game_player.system_tag                 # => Integer id, 0 if none

# The tag of the tile the player is facing (uses the character's direction)
$game_player.front_system_tag           # => Integer id, 0 if none
```

Comparing a raw id is fine, but the engine also exposes a **readable symbol** for the gameplay-relevant tags, which is easier to branch on:

```ruby
$game_player.system_tag_db_symbol       # => :grass, :cave, :sea, :ice, :headbutt...
$game_player.front_system_tag_db_symbol # => same, for the tile ahead
```

The symbol comes from `GameData::SystemTags.system_tag_db_symbol`, which maps the encounter and particle tags to a symbol and returns `:regular_ground` for everything else.

To compare against a precise tag, use the constant rather than a literal number:

```ruby
if $game_player.front_system_tag == GameData::SystemTags::HeadButt
  # the player is facing a Headbutt tile
end
```

When you need to inspect an arbitrary cell instead of the character's position, query the map directly:

```ruby
# Tag at an absolute tile coordinate
$game_map.system_tag(x, y)              # => Integer id, 0 if none

# Is this exact tag present on that cell?
$game_map.system_tag_here?(x, y, GameData::SystemTags::TGrass)  # => true / false
```

`Game_Map#system_tag` accepts a `skip_bridge:` keyword. With `skip_bridge: true` it ignores bridge tags (`BridgeRL`, `BridgeUD`) and keeps looking at the layers below, which is how the engine checks the ground *under* a bridge rather than the bridge itself.

| Method | Receives | Returns |
| --- | --- | --- |
| `Game_Character#system_tag` | nothing | tag id of the tile the character stands on |
| `Game_Character#front_system_tag` | nothing | tag id of the tile the character faces |
| `Game_Character#system_tag_db_symbol` | nothing | symbol for the current tile |
| `Game_Character#front_system_tag_db_symbol` | nothing | symbol for the faced tile |
| `Game_Map#system_tag` | `x`, `y`, `skip_bridge:` | tag id at that cell, `0` if none |
| `Game_Map#system_tag_here?` | `x`, `y`, `tag` | `true` if that exact tag is on the cell |
| `GameData::SystemTags.system_tag_db_symbol` | a tag id | the matching symbol, or `:regular_ground` |

## The families of tags

Every tag is a constant in the `GameData::SystemTags` module. The engine groups several of them into arrays it reuses across the movement logic (`SurfTag`, `SlideTags`, `BRIDGE_TILES`, `ZTag`...). The tables below list every tag by role; the effect column reflects what the engine actually does with each one.

### Terrain and encounters

These tags drive wild battles, the location type and the ground particles.

| Tag | Effect |
| --- | --- |
| `Empty` | Neutral tile; cancels the effect of water tags such as `TSea` or `TPond`. |
| `TGrass` | Grass; shows grass particles and starts grass wild battles. |
| `TTallGrass` | Taller grass; same role as `TGrass` with a distinct animation. |
| `TCave` | Cave; starts cave wild battles. |
| `TMount` | Mountain; starts mountain wild battles. |
| `TSand` | Sand; starts sand wild battles. |
| `TWetSand` | Wet sand; shows a particle when walking, otherwise behaves like sand. |
| `TPond` | Pond or river; starts pond wild battles (also surfable). |
| `TSea` | Sea or ocean; starts sea wild battles (also surfable). |
| `TUnderWater` | Underwater terrain; starts underwater wild battles. |
| `TSnow` | Snow; starts snow wild battles. |
| `Puddle` | Puddle; shows a water particle when walking. |
| `HeadButt` | A tile you can use Headbutt on; counts as grass for the location type. |

### Water and surfing

The engine keeps two arrays here: `SurfTag` (the water tiles you can only enter while surfing) and `SurfLTag` (every tile you are allowed to stand on while surfing, which adds bridges, jumps and the acro-bike bridges).

| Tag | Effect |
| --- | --- |
| `TSea`, `TPond` | Surfable water; part of `SurfTag`. |
| `RapidsL`, `RapidsD`, `RapidsU`, `RapidsR` | Water current forcing the character left / down / up / right. |
| `WaterFall` | Waterfall tile; an aid for waterfall events. |
| `Whirlpool` | Whirlpool tile. |

### Sliding

These tags take control of the character and move them automatically. They are collected in `SlideTags`.

| Tag | Effect |
| --- | --- |
| `TIce` | Ice; the character keeps sliding forward. |
| `RapidsL`, `RapidsD`, `RapidsU`, `RapidsR` | Force the character to slide in that direction. |
| `RocketL`, `RocketD`, `RocketU`, `RocketR` | Force the character to move that way until they hit a wall. |
| `RocketRL`, `RocketRD`, `RocketRU`, `RocketRR` | Same as the `Rocket*` tags but the character also rotates. |
| `StopSlide` | Stops a sliding character. |

### Ledges and jumps

| Tag | Effect |
| --- | --- |
| `JumpR`, `JumpL`, `JumpD`, `JumpU` | Ledge; the character jumps across it to the right / left / down / up. |

### Stairs and slopes

| Tag | Effect |
| --- | --- |
| `StairsL`, `StairsD`, `StairsU`, `StairsR` | Stairs; adjust the character's elevation while moving. |
| `SlopesL`, `SlopesR` | Left and right slopes; shift the character's on-screen position as they climb or descend. |

### Bridges and elevation

Bridges are grouped in `BRIDGE_TILES`; `ZTag` is an array of tiles that change the character's z (priority layer).

| Tag | Effect |
| --- | --- |
| `BridgeUD` | Bridge crossed up and down (the layer below stays reachable). |
| `BridgeRL` | Bridge crossed right and left. |
| `ZTag` | Seven tiles that set the character's z layer, so they pass above or below other tiles. |

### Bikes

| Tag | Effect |
| --- | --- |
| `AcroBike` | Ledge passable only by the Acro bike bunny hop. |
| `AcroBikeRL` | Bike bridge allowing only right and left movement (plus up/down jumps). |
| `AcroBikeUD` | Bike bridge allowing only up and down movement (plus left/right jumps). |
| `MachBike` | Requires high speed; otherwise the character falls down. |
| `CrackedSoil` | Requires high speed; otherwise the character falls into a hole. |
| `Hole` | Hole tile. |

### Swamp and pathfinding

| Tag | Effect |
| --- | --- |
| `SwampBorder` | Shallow swamp; slows the character down. |
| `DeepSwamp` | Deep swamp; the character can get stuck. |
| `Road` | Marks a road, used as a preferred route by the pathfinding system. |
| `RClimb` | Rock Climb tile. |

The canonical, always-current list is the engine file that declares them. If a future PSDK version adds a tag, it appears there first.

## Usage examples

The reading API is plain Ruby, so you can branch on a System Tag anywhere game code runs: an event's script command, a move route, a scheduled task or a field-move handler. Here are the patterns you are most likely to need, each mirroring how the engine itself uses tags.

### Gate an event on the tile ahead

The simplest case: only let an interaction happen on the right kind of tile. Drop a guard straight into an event's script command, reading the tile the player faces:

```ruby
# In an event's script command
if $game_player.front_system_tag_db_symbol == :grass
  # the player faces grass: run the interaction
  $game_temp.common_event_id = MY_GRASS_EVENT_ID
end
```

To branch on a tag that has no dedicated symbol, compare the constant instead:

```ruby
# Only react when the player stands on a bridge crossed right and left
if $game_player.system_tag == GameData::SystemTags::BridgeRL
  # ...
end
```

The same reads work from a script command in the [interpreter](/rpg-maker-xp/using-the-interpreter-in-an-event).

### Read a tag from a Move Route

A move route's **Script** command is evaluated in the moving character's own context, so every `Game_Character` method is in scope with no receiver. The engine ships a move-route helper built on exactly this: `move_random_within_systemtag` makes a character wander at random but only across tiles carrying a given tag, so an NPC or a roaming Pokémon stays confined to, say, grass or water:

```ruby
# Script command inside the event's Move Route
move_random_within_systemtag(GameData::SystemTags::TGrass)
```

Each time the move route runs it tries one random step and takes it only if the neighbouring tile shares the tag, so the character never leaves the patch.

### React to a step with the Scheduler

To run code *when* a character steps onto a tile rather than poll it, register a map event task. `Scheduler::EventTasks` fires on `:begin_step` / `:end_step`, `:begin_jump` / `:end_jump` and `:begin_slide` / `:end_slide`; the task receives the `Game_Character` that moved, and the event id `-1` targets the player. The engine itself uses this to pick the landing particle after a jump:

```ruby
# How PSDK chooses water dust vs normal dust after a jump
Scheduler::EventTasks.on(:end_jump, 'Dust after jumping') do |event|
  next if event.particles_disabled

  particle = Game_Character::SurfTag.include?(event.system_tag) ? :water_dust : :dust
  Yuki::Particles.add_particle(event, particle)
end
```

Your own task follows the same shape, here reacting only when the player lands on ice:

```ruby
Scheduler::EventTasks.on(:end_step, 'React to ice', -1) do |event|
  next unless event.system_tag == GameData::SystemTags::TIce
  # the player just stepped onto an ice tile
end
```

This is the cleanest way to add step-driven behaviour; see the [Scheduler](/psdk/core-systems/scheduler) guide for the full task model.

### Add a field move that checks the terrain

Out-of-battle moves (Surf, Fly, Sweet Scent...) are registered in the `PFM::SKILL_PROCESS` hash, keyed by the move's `db_symbol`. Each entry is a proc receiving the Pokémon, the move and a `test` flag (the dry run the menu uses to know whether the move is usable). This is exactly where Surf reads a System Tag to decide it may be used: it looks at the tile in front of the player and refuses unless it is water.

```ruby
# PFM::SKILL_PROCESS[:surf], abridged
surf: proc do |_pkmn, _skill, test = false|
  new_x, new_y = $game_player.front_tile
  sys_tag = $game_map.system_tag(new_x, new_y)
  # refuse unless the faced tile is surfable water
  next :block unless $game_player.z <= 1 && !$game_player.surfing? &&
                     Game_Character::SurfTag.include?(sys_tag)
  next false if test

  $game_temp.common_event_id = Game_CommonEvent::SURF_ENTER
  next true
end
```

You can register your own field move the same way: add an entry keyed by your move's `db_symbol`, return `:block` to refuse with the default message, and act when the terrain is right.

```ruby
# A custom field move usable only when facing a Rock Climb tile
PFM::SKILL_PROCESS[:my_move] = proc do |_pkmn, _skill, test = false|
  next :block unless $game_player.front_system_tag == GameData::SystemTags::RClimb
  next false if test

  # perform the move's overworld effect here
end
```

When you instead need to change what an existing engine method does around a tag, patch it with a `prepend` module calling `super` rather than editing the engine, as described in [monkey-patching](/getting-started/monkey-patching-in-psdk).

To go one step further and register a brand-new tag of your own, with its own wild encounters, battle background and particles, see [Create a custom System Tag](/psdk/core-systems/create-a-custom-system-tag).

## Conclusion

- A **System Tag** is a per-tile marker, stored per tileset, that tells the map engine how a tile behaves; `0` means no tag.
- The engine reads a cell's three layers from the top down and returns the first non-zero tag.
- Read the tile under a character with `system_tag`, the tile ahead with `front_system_tag`, and get a readable symbol with `system_tag_db_symbol`; query any cell with `Game_Map#system_tag` and `system_tag_here?`.
- Always compare against a **constant name** (`GameData::SystemTags::TGrass`), never a raw number.
- The tags fall into clear families: terrain and encounters, water and surfing, sliding, ledges, stairs and slopes, bridges and elevation, bikes, swamp and pathfinding.
- Tags are painted in Tiled on a dedicated `systemtags` layer; the engine file that declares the constants is the source of truth for the full list.
