3 Tutorial 08 Quest
bryanthaboi edited this page 2026-10-20 17:01:40 -04:00

Tutorial 08: Quest

Make: Ask for a Nugget, collect it on Tutorial Route, return it once, and remember completion in the save.

Before you start: Intermediate. Complete Tutorial 07. Use its same tutorial_world mod and a practice save. Do not enable Tutorial 07 as a separate mod.

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 07'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
  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

  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 } },
    objects = {
      {
        index = 91, x = 4, y = 2, sprite = "SPRITE_POKE_BALL",
        movement = "STAY", range = "NONE", item = "NUGGET",
        text = "TEXT_TUTORIAL_NUGGET", name = "TUTORIAL_ROUTE_NUGGET",
      },
    },
  })
  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",
      },
      {
        index = 92, x = 12, y = 8, sprite = "SPRITE_SCIENTIST",
        movement = "STAY", range = "NONE", text = "TEXT_TUTORIAL_QUEST",
        name = "TUTORIAL_QUEST_GIVER",
      },
    } },
  })
  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 },
    } },
  })
  mod.content.map_scripts:register("PALLET_TOWN", {
    talk = {
      TEXT_TUTORIAL_WORLD = {
        { "face_player" }, { "show_text", "Welcome to the\nTutorial World!" },
      },
      TEXT_TUTORIAL_QUEST = {
        { "check_flag", "mod:done" }, { "jump_if_true", "thanks" },
        { "check_flag", "mod:stage" }, { "jump_if_true", "pending" },
        { "set_field", "mod:stage", true },
        { "show_text", "Please bring me\na NUGGET." }, { "jump", "end" },
        { "label", "pending" }, { "check_item", "NUGGET" },
        { "jump_if_false", "wait" }, { "take_item", "NUGGET", 1 },
        { "set_field", "mod:done", true }, { "show_text", "Quest complete!" },
        { "jump", "end" }, { "label", "wait" },
        { "show_text", "The NUGGET is on\nTUTORIAL ROUTE." }, { "jump", "end" },
        { "label", "thanks" }, { "show_text", "Thanks again!" },
        { "label", "end" },
      },
    },
  })
end

Understand what you wrote

This snapshot keeps the route and greeting, adds a Nugget item-ball on the route, and adds a scientist in Pallet Town. The scientist's TEXT_TUTORIAL_QUEST entry is a command list with branches: a check records a true/false result, and a jump selects which labeled part runs next.

Two private values record progress. mod:stage is a boolean, not a numeric stage counter: true means you accepted the request. mod:done is true after handing over a Nugget. The mod: prefix places them in this mod's part of the current save, rather than using a global story flag.

not asked                  request active                 complete
stage absent/false  -->    stage = true           -->      done = true
                           done absent/false              stage stays true
       first talk                return with Nugget

Trace the dialogue from the top. A completed quest jumps to thanks. An accepted quest jumps to pending, which checks the bag. Without a Nugget it jumps to wait; with one it removes one item, writes done, and shows completion. A first conversation sets stage and asks for the Nugget, even if you already carry one; talk again to turn it in.

label is a destination name, not dialogue. jump skips to that destination so one branch does not fall into the next. See Script Commands for a smaller traced branch example.

Progress belongs to the mod ID tutorial_world in the current save slot. Keep that ID when upgrading to Tutorial 11 so its quest screen reads these same values. Do not reset progress by renaming the ID or editing a global event flag; test fresh progress in a separate practice slot.

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. Talk to the scientist at Pallet Town player cell (12, 8). Expect Please bring me / a NUGGET. Save the game now.
  2. Close and reload the same slot with the mod enabled. Talk again without a Nugget. Expect the hint pointing to Tutorial Route: the accepted state survived the reload.
  3. Use the lower-right east exit at Pallet (19, 13) to enter the route at (0, 9). Walk northeast to stand above the item-ball at (4, 1), face down, and press A to collect the Nugget at (4, 2). Confirm it is in your bag. Return from route (0, 9) by stepping west.
  4. Return to the scientist and talk. Expect Quest complete!, with one Nugget removed. Talk again: expect Thanks again!, with no second item taken.
  5. Save, close, and reload again. The scientist should still thank you. Keep this completed practice save for Tutorial 11, and another pre-quest slot if you want to inspect all three screen states.

Common pitfalls

What you see What to check
Quest completes immediately on my return Any NUGGET satisfies the request, including one you already owned. The example checks an item type, not where that individual item came from.
Progress appears reset in the next lesson Use the same save slot and tutorial_world manifest ID, and keep only one snapshot installed.
Scientist's old script still runs Replace the whole main.lua with this snapshot; do not add a second competing talk registration.

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.

Save Model explains private data and migrations for published mods. This tutorial keeps its two boolean fields stable; a migration is needed only when a later release changes the saved shape.

Return to the tutorial index to choose the next lesson.