3 Tutorial 07 New Map
bryanthaboi edited this page 2026-10-20 17:01:40 -04:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Tutorial 07: New map

Make: Connect a small route to Pallet Town, then walk through its grass to meet wild Pokémon.

Before you start: Intermediate. Complete Tutorial 06. This is the next version of that same tutorial_world mod, including its original NPC.

Game: Red, local single-player. Use a practice save for experiments. If file creation is new to you, start with Tutorial 00; unfamiliar words are in the Glossary.

1. Set up the files

Tutorials 06, 07, 08, and 11 are versions of one mod, tutorial_world. Install exactly one snapshot. Begin with Tutorial 06's finished folder. Copy your current files somewhere outside the mods directory before replacing them. Keep the same mod ID and save slot as you progress.

For a fresh start, these complete starting files match this lesson's prerequisite: manifest.json, main.lua. Replace them with the finished files below as you complete the lesson.

This is a mod folder, not the engine root. Do not replace the engine's own main.lua. Preserve filenames and avoid an extra .txt extension.

Finished folder layout:

mods/tutorial_world/
  main.lua
  manifest.json

Each link opens a complete file you can save at the matching path. The JSON and Lua are also printed in full below; do not append them to another lesson's entry function.

File in this mod Complete example
main.lua Download main.lua
manifest.json Download manifest.json

2. Describe the mod

Complete file: replace tutorial_world/manifest.json:

{
  "id": "tutorial_world",
  "name": "Tutorial World",
  "version": "1.0.0",
  "entry": "main.lua",
  "api": 2,
  "games": [
    "red"
  ]
}

id is the stable internal name, name is the label in MODS, and entry tells the loader which Lua file to open. games limits these examples to Red. Keep commas between JSON fields and no comma after the last field.

3. Write the change

Complete file: replace tutorial_world/main.lua:

return function(mod)
  local blocks = {}
  for i = 1, 30 do blocks[i] = 10 end
  blocks[8], blocks[9] = 11, 11 -- the two grass blocks
  local pallet = mod.content.maps:get("PALLET_TOWN")
  local palletBlocks = {}
  for i, block in ipairs(pallet.blocks) do palletBlocks[i] = block end
  palletBlocks[7 * pallet.width] = 10 -- block (9, 6), lower-right exit

  mod.content.maps:register("TUTORIAL_ROUTE", {
    id = "TUTORIAL_ROUTE", label = "TutorialRoute", index = 1000,
    tileset = "OVERWORLD", width = 6, height = 5, blocks = blocks,
    borderBlock = 10, connections = { west = { map = "PALLET_TOWN", offset = -2 } },
  })
  mod.content.maps:patch("PALLET_TOWN", {
    connections = { east = { map = "TUTORIAL_ROUTE", offset = 2 } },
    blocks = palletBlocks,
    objects = { __append = {
      {
        index = 90, x = 8, y = 7, sprite = "SPRITE_BEAUTY",
        movement = "STAY", range = "NONE", text = "TEXT_TUTORIAL_WORLD",
        name = "TUTORIAL_WORLD_NPC",
      },
    } },
  })
  mod.content.map_scripts:register("PALLET_TOWN", {
    talk = { TEXT_TUTORIAL_WORLD = {
      { "face_player" }, { "show_text", "Welcome to the\nTutorial World!" },
    } },
  })
  mod.content.encounters:register("TUTORIAL_ROUTE", {
    grass = { rate = 25, slots = {
      { species = "PIDGEY", level = 3 }, { species = "RATTATA", level = 3 },
      { species = "PIDGEY", level = 4 }, { species = "RATTATA", level = 4 },
      { species = "PIDGEY", level = 5 }, { species = "RATTATA", level = 5 },
      { species = "PIDGEY", level = 3 }, { species = "RATTATA", level = 3 },
      { species = "PIDGEY", level = 6 }, { species = "RATTATA", level = 6 },
    } },
  })
end

Understand what you wrote

A map has both a drawing grid and a movement grid. One tile is an 8×8 image square; a block is a 4×4 group of those tiles (32×32 pixels); a player cell is a 2×2 group (16×16 pixels). Thus one block covers 2×2 player cells.

one 32×32 block       the same block's four player cells
[t][t][t][t]          [ cell ][ cell ]
[t][t][t][t]          [      ][      ]
[t][t][t][t]          [ cell ][ cell ]
[t][t][t][t]          [      ][      ]

width = 6 and height = 5 describe blocks: 30 entries in row order, making a 12×10-player-cell map. The Lua loop fills that list; blocks[8] and blocks[9] replace selected positions. Lua list positions start at 1 even though map x/y coordinates start at 0.

A block number chooses an arrangement from the OVERWORLD tileset, which also determines collision and grass behavior. Numbers from another tileset may mean something entirely different. Use the map recipes and the loaded tileset records in the developer console to inspect IDs before replacing them. The included layout is your starting comparison; do not choose numbers just because their names sound plausible.

The two connections entries join the east side of Pallet Town to the west side of the route. Both directions matter: registering a map alone does not give the player a way to enter or leave it. offset is measured in 32-pixel blocks. The east offset of 2 places the route two blocks (four player cells) lower than Pallet Town; the return connection uses -2. Thus Pallet cell y=13 connects to route cell y=9 in both directions.

Pallet Town originally has a blocked east edge. The example reads its loaded map with get, copies the block list, and changes only block (9, 6) to floor. This opens the entrance while keeping the rest of the imported layout. The copy matters: make edits through patch instead of mutating a record returned by get.

The encounter record names the same route ID and supplies a grass encounter rate plus ten slots. Grass appearance, walkability, and encounter registration are separate pieces; a loaded encounter table cannot turn a wall into walkable grass.

4. Load and check it

Save every file, restart the app, enable the mod for Red in the launcher's MODS panel, and launch. Resolve any load error before checking gameplay. Troubleshooting shows where errors appear. With default desktop bindings, A is Z/Enter/Space, B is X/Backspace, and Start is Escape/keypad Enter; use your configured controls if different.

Optional source-checkout validation

If you have Python 3, LuaJIT, and the engine source with Red imported, open a terminal in the engine root containing tools/. For a mod installed in that checkout's mods directory, run:

python3 tools/modkit.py validate tutorial_world --base imported

If you saved it elsewhere, replace the mod ID with the quoted full path to its folder. A validation error identifies a file or content rule to fix; a pass means you can continue to the in-game checks. It does not prove rendering, sound, or the entire playthrough works. Packaged-app users can use MODS errors and the checkpoints without setting up these command-line tools.

Checkpoint

  1. Start in Pallet Town with a usable party and confirm the Tutorial 06 greeting still works.
  2. From the greeting NPC, walk southeast to the opening in the lower-right edge of Pallet Town, at player cell (19, 13). Step east onto the route at (0, 9). Step west again from that same cell to return to Pallet (19, 13), then follow the open path back to the NPC.
  3. Step through the route's grass near the upper part of the small map until a battle occurs. Expect Pidgey or Rattata from the listed slots; an encounter is random, not guaranteed on the first step.
  4. Before moving an NPC, translate the desired block position to player cells. Keep the tested entry path clear while changing one block at a time.

Common pitfalls

What you see What to check
Map registers but I cannot enter it Check both connection IDs and offsets, then collision at the entry cells. Map registration alone creates no door or path.
No battle on the first grass step Grass encounters are random. Check that the party is usable, the tiles are encounter grass, and the encounters ID matches TUTORIAL_ROUTE.

For syntax, missing-file, and restart problems, use Troubleshooting. Compare the whole finished file before changing several things at once.

Undo and continue

Keep the practice save and its matching mod files together. Saves can contain this example's custom content or progress. To compare with vanilla Red, disable the mod, restart, and open a separate unmodified practice slot. Do not overwrite the only copy of a modded save while its content is disabled. Save recovery explains backups.

Return to the tutorial index to choose the next lesson.