Skip to content

Modding Basics

bryanthaboi edited this page Sep 9, 2026 · 1 revision

Modding basics

For: someone new to programming. Read this alongside Your First Change; you do not need to memorize it first. You will learn what the two mod files mean and how to edit them.

Which folder is which?

A folder contains files and other folders. A path tells you how to find one. In mods/my_first_mod/main.lua, enter mods, then my_first_mod, then open main.lua.

Engine source checkout                 App's save directory
├── main.lua       runs the game        ├── mods/
├── src/           engine code          │   └── my_first_mod/
├── tools/         authoring commands   │       ├── manifest.json
└── mods/                              │       └── main.lua
    └── my_first_mod/                   └── saved progress and settings
        ├── manifest.json
        └── main.lua

Wiki checkout
├── Home.md        documentation
└── Tutorial-00-Your-First-Change.md

The two mod locations are alternatives. Choose one for your mod. The main.lua inside your mod is different from the main.lua that runs the engine. The wiki contains instructions, not the installed game. Getting Started gives the real folder paths.

The game also keeps generated files made from your imported ROM. These are working game data, not the place to edit your mod. Never distribute them as mod assets.

Make a plain-text file

Use an editor that saves plain text, not a Word document or rich text. On macOS TextEdit, choose Format → Make Plain Text before saving. Keep smart quotes off for code: " is a code quote; curly quotation marks are different characters. Windows Notepad can save plain text; use All files when necessary to keep the exact extension.

Save files as manifest.json and main.lua. Show file extensions in your file manager and check that .txt was not added. Save after each change; the game cannot see text that exists only in an unsaved editor tab.

Two languages, two jobs

File Format Job
manifest.json JSON: structured information Identify the mod, choose its code file and supported games
main.lua Lua: programming instructions Tell the game what to change

Complete manifest:

{
  "id": "my_first_mod",
  "name": "My First Mod",
  "version": "1.0.0",
  "entry": "main.lua",
  "api": 2,
  "games": ["red"]
}

A JSON field is a named value, such as "name": "My First Mod". Double quotes surround names and text; commas separate fields. There is no comma after the last field. The outer braces { } hold the group together. JSON does not allow the Lua comments shown below.

id is a stable internal name; name is what the player sees. Avoid changing the ID after you have saved progress owned by the mod: a different ID means a different private save area. api chooses the mod API rules; games limits the example to Red. The Manifest reference explains additional choices.

Read your first Lua file

Complete main.lua:

return function(mod)
  -- Replace the words on the Pallet Town sign.
  mod.content.text:override("_PalletTownSignText", "HELLO FROM MY MOD!")
end
  1. function(mod) creates a set of instructions the game will call when it loads your mod. The name mod receives the tools for this particular mod.
  2. return gives that function to the loader. Keep this outer structure.
  3. A line beginning with -- is a comment for people; Lua ignores it.
  4. mod.content.text chooses the text registry, a named collection of text.
  5. :override(...) calls an operation on that collection. The values in parentheses are its arguments: the internal text ID, then your new text.
  6. end closes the function. New mod instructions belong above this line, inside the function, unless a lesson explicitly says otherwise.

Only edit the second quoted string at first. _PalletTownSignText is an internal name, not a phrase you invent. Code names are case-sensitive. The same name with different capital letters can mean something else.

Values and tables

These are example shapes to read, not an additional mod file:

local greeting = "Hello"          -- string: text
local speed = 90                  -- number
local enabled = true              -- boolean: true or false
local missing = nil               -- no value
local stats = { hp = 60, speed = 90 }  -- table with named fields
local moves = { "TACKLE", "GROWL" }   -- table used as an ordered list

local creates a name used by the surrounding code. = assigns a value. A Lua table can group named fields or hold a list. A record is a table describing one thing, such as a creature. A registry holds many records, found by their IDs.

baseStats is a field name. GROWLITHE is a creature ID. The display name “Growlithe” is text for the player. When you write code, copy the internal field/ID spelling from the reference; do not guess it from the display name.

Change just one thing

After the first exercise, try Tutorial 02. For example, this is an insertion inside your existing entry function:

mod.content.pokemon:patch("GROWLITHE", {
  baseStats = { speed = 90 },
})

patch changes the fields you name. Here the other base stats stay as they were. override replaces an entire record. To change one stat, choose a patch so you do not accidentally omit the other fields.

Lists need extra care. In a record registry, a plain replacement list replaces the whole list; __append adds to its end. Some deep registries append plain lists instead. Follow the registry's merge rules before editing a list.

Code that runs later

A callback is a function the game calls later, for example when the player enters a map. This is an insertion inside an entry function:

mod.events:on("map.entered", function(event)
  mod.log:info("Entered %s", event.mapId)
end)

event is a table containing information about that map entry. event.mapId reads one field. %s is a placeholder in the log message; the next argument fills it in. The message appears in log output, not in a dialogue box.

An event lets you respond to something that happened. A hook lets you participate in a calculation or decision. Hooks have return-value rules and can affect other code; learn Events and Hooks before using one. A return value is the result a function gives its caller.

A reliable edit loop

  1. Make one small edit and save the file.
  2. Validate it with Modkit if you use a source checkout.
  3. Restart, confirm the correct game/mod is enabled, and perform the lesson's in-game check.
  4. If it fails, read the first error and compare with the complete example.
  5. Turn the mod off and restart to compare the active changes.

Validation catches many file/data problems. It cannot prove that a screen draws correctly or that a quest is reachable. You need the in-game check too.

Next: Your First Change, Glossary, or Tutorials.

Clone this wiki locally