diff options
author | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-03-15 19:01:55 -0400 |
---|---|---|
committer | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-03-15 19:01:55 -0400 |
commit | da356938fadf1569aa1b74ed4db61beda8147e3a (patch) | |
tree | 2baf5fd5840a9c34cda545ac1a3012901631915b | |
parent | cb55369ac9a5b80ae87b293181b2e790aa59c601 (diff) |
Improve the event initialization system
-rw-r--r-- | Improve-the-event-initialization-system.md | 210 | ||||
-rw-r--r-- | Tutorials.md | 1 |
2 files changed, 211 insertions, 0 deletions
diff --git a/Improve-the-event-initialization-system.md b/Improve-the-event-initialization-system.md new file mode 100644 index 0000000..78f42dd --- /dev/null +++ b/Improve-the-event-initialization-system.md @@ -0,0 +1,210 @@ +When you start a new game, the memory is zeroed out. But some things need to be initialized to nonzero values. + +This is done with a callback script in [maps/PlayersHouse2F.asm](../blob/master/maps/PlayersHouse2F.asm), the map you first start in: if the `EVENT_INITIALIZED_EVENTS` event is not set yet, it does `jumpstd initializeevents`. That command actually calls the `InitializeEventsScript` script, which is defined in [engine/events/std_scripts.asm](../blob/master/engine/events/std_scripts.asm). It sets a lot of events, a couple of engine flags, initializes variable sprites, and finally sets the `EVENT_INITIALIZED_EVENTS` event so it won't repeat every time you enter PlayersHouse2F. + +There are a few problems with this approach. One, it's tied to a particular map: if you change which map the player starts in, you have to remember to move the `initializeevents` callback. Two, it's hard to find: every time you need to add another initially-set event, you have to scroll halfway through an engine file to the right script. Three, it wastes ROM space: every single `setevent`, `setflag`, or `variablesprite` line spends one byte to identify the command. + +This tutorial (using code originally by [ISSOtm](https://github.com/ISSOtm)) will solve all of those problems. It's recommended for if you want your project to be a base for other hacks, since they'll all need their own initializations. + + +## Contents + +1. [Create data tables for what to initialize](#1-create-data-tables-for-what-to-initialize) +2. [Process the data tables to initialize everything](#2-process-the-data-tables-to-initialize-everything) +3. [Remove the scripts that initialized everything](#3-remove-the-scripts-that-initialized-everything) + + +## 1. Create data tables for what to initialize + +Create **data/events/init_events.asm**: + +```diff ++InitialEvents: ++ dw EVENT_EARLS_ACADEMY_EARL ++ ... ++ dw EVENT_INDIGO_PLATEAU_POKECENTER_RIVAL ++ dw EVENT_INITIALIZED_EVENTS ++ dw -1 ; end ++ ++InitialEngineFlags: ++ dw ENGINE_ROCKET_SIGNAL_ON_CH20 ++ dw ENGINE_ROCKETS_IN_MAHOGANY ++ dw -1 ; end ++ ++InitialVariableSprites: ++initvarsprite: MACRO ++; variable sprite, appearance sprite ++ db \1 - SPRITE_VARS, \2 ++ENDM ++ initvarsprite SPRITE_WEIRD_TREE, SPRITE_SUDOWOODO ++ initvarsprite SPRITE_OLIVINE_RIVAL, SPRITE_SILVER ++ initvarsprite SPRITE_AZALEA_ROCKET, SPRITE_ROCKET ++ initvarsprite SPRITE_FUCHSIA_GYM_1, SPRITE_JANINE ++ initvarsprite SPRITE_FUCHSIA_GYM_2, SPRITE_JANINE ++ initvarsprite SPRITE_FUCHSIA_GYM_3, SPRITE_JANINE ++ initvarsprite SPRITE_FUCHSIA_GYM_4, SPRITE_JANINE ++ initvarsprite SPRITE_COPYCAT, SPRITE_LASS ++ initvarsprite SPRITE_JANINE_IMPERSONATOR, SPRITE_LASS ++ db -1 ; end +``` + +This file defines three tables: `InitialEvents`, `InitialEngineFlags`, and `InitialVariableSprites`. They're all based on the commands from `InitializeEventsScript`: + +- Every `setevent` command becomes a `dw` in `InitialEvents` +- Every `setflag` command becomes a `dw` in `InitialEngineFlags` +- Every `variablesprite` command becomes an `initvarsprite` in `InitialVariableSprites` + +(The `initvarsprite` macro is so you don't have to subtract `SPRITE_VARS` in every single line; it's based on the definition of `variablesprite` in [macros/scripts/events.asm](../blob/master/macros/scripts/events.asm).) + +There are 136 entries in these three tables, so that saves 136 bytes because they don't all start with script commands. We spend five bytes on the `-1`s to mark the end of each table (`dw -1` is two bytes), so if the code to process these tables takes fewer than 131 bytes, we'll have saved space overall. + + +## 2. Process the data tables to initialize everything + +Create **engine/events/init_events.asm**: + +```diff ++InitializeEvents: ++; initialize events ++ ld hl, InitialEvents ++.events_loop ++ ld a, [hli] ++ ld e, a ++ ld a, [hli] ++ ld d, a ++ and e ++ cp -1 ++ jr z, .events_done ++ ld b, SET_FLAG ++ push hl ++ call EventFlagAction ++ pop hl ++ jr .events_loop ++.events_done ++ ++; initialize engine flags ++ ld hl, InitialEngineFlags ++.flags_loop ++ ld a, [hli] ++ ld e, a ++ ld a, [hli] ++ ld d, a ++ and e ++ cp -1 ++ jr z, .flags_done ++ ld b, SET_FLAG ++ push hl ++ farcall EngineFlagAction ++ pop hl ++ jr .flags_loop ++.flags_done ++ ++; initialize variable sprites ++ ld hl, InitialVariableSprites ++.sprites_loop ++ ld a, [hli] ++ ld e, a ++ ld d, 0 ++ cp -1 ++ jr z, .sprites_done ++ ld a, [hli] ++ push hl ++ ld hl, wVariableSprites ++ add hl, de ++ ld [hl], a ++ pop hl ++ jr .sprites_loop ++.sprites_done ++ ++ ret ++ ++INCLUDE "data/events/init_events.asm" +``` + +This defines the `InitializeEvents` assembly function. It has three main loops, one for each table. They're all based on the definitions of the original script commands (`setevent`, `setflag`, or `variablesprite`) from [engine/overworld/scripting.asm](../blob/master/engine/overworld/scripting.asm). + +Then edit [engine/menus/intro_menu.asm](../blob/master/engine/menus/intro_menu.asm): + +```diff + InitializeWorld: + call ShrinkPlayer + farcall SpawnPlayer + farcall _InitializeStartDay ++ farcall InitializeEvents + ret +``` + +And edit [main.asm](../blob/master/main.asm): + +```diff + SECTION "Phone Scripts 2", ROMX + + INCLUDE "engine/events/std_scripts.asm" ++INCLUDE "engine/events/init_events.asm" + ... +``` + +The code uses 73 bytes—67 to define `InitializeEvents` and 6 to `farcall` it—which means we've saved 58 bytes so far. But we're about to save more, since the old system of calling `InitializeEventsScript` needs to go. + + +## 3. Remove the scripts that initialized everything + +Edit [constants/std_constants.asm](../blob/master/constants/std_constants.asm): + +```diff + ; StdScripts indexes (see engine/events/std_scripts.asm) + ; also used in TileCollisionStdScripts (see data/events/collision_stdscripts.asm) + enum_start + ... +- enum initializeevents + ... +``` + +Edit [engine/events/std_scripts.asm](../blob/master/engine/events/std_scripts.asm): + +```diff + StdScripts:: + ; entries correspond to constants/std_constants.asm + ... +- dba InitializeEventsScript + ... + +-InitializeEventsScript: +- setevent EVENT_EARLS_ACADEMY_EARL +- ... +- setevent EVENT_INITIALIZED_EVENTS +- return +``` + +And edit [maps/PlayersHouse2F.asm](../blob/master/maps/PlayersHouse2F.asm): + +```diff + .InitializeRoom: + special ToggleDecorationsVisibility + setevent EVENT_TEMPORARY_UNTIL_MAP_RELOAD_8 +- checkevent EVENT_INITIALIZED_EVENTS +- iftrue .SkipInitialization +- jumpstd initializeevents +- return +- +-.SkipInitialization: + return +``` + +Now we have a system for initializing the game that's independent of the starting map, easy to find with the rest of the game data, and uses less space. + +Let's see how much less. The [tools/free_space.awk](../blob/master/tools/free_space.awk) script exists to measure that, based on the .map file produced when you `make` the ROM. So run it before this tutorial: + +``` +$ tools/free_space.awk pokecrystal.map +Free space: 455223/2097152 (21.71%) +``` + +And again after the tutorial: + +``` +$ tools/free_space.awk pokecrystal.map +Free space: 455295/2097152 (21.71%) +``` + +That's 72 more ROM bytes than before. It's not a whole lot, but every bit helps. You can eke out a few more by applying the tricks from the [assembly optimization tutorial](Optimizing-assembly-code) to `InitializeEvents`: it was written more for clarity than to save space. (For example, all three `cp -1` can become `inc a` to save three bytes *and* run a little faster.) diff --git a/Tutorials.md b/Tutorials.md index 8fb29e7..a4b3494 100644 --- a/Tutorials.md +++ b/Tutorials.md @@ -71,6 +71,7 @@ Tutorials may use diff syntax to show edits: - [Allow tiles to have different attributes in different blocks (including X and Y flip)](Allow-tiles-to-have-different-attributes-in-different-blocks-\(including-X-and-Y-flip\)) - [Allow more than 15 `object_event`s per map](Allow-more-than-15-object_events-per-map) - [Improve the outdoor sprite system](Improve-the-outdoor-sprite-system) +- [Improve the event initialization system](Improve-the-event-initialization-system) - [Expand the Town Map tileset](Expand-the-Town-Map-tileset) - [Increase Pokémon sprite animation size](Increase-Pokémon-sprite-animation-size) - [Allow more trainer parties, with individual DVs, stat experience, and nicknames](Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames) |