summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRangi <remy.oukaour+rangi42@gmail.com>2020-03-15 19:01:55 -0400
committerRangi <remy.oukaour+rangi42@gmail.com>2020-03-15 19:01:55 -0400
commitda356938fadf1569aa1b74ed4db61beda8147e3a (patch)
tree2baf5fd5840a9c34cda545ac1a3012901631915b
parentcb55369ac9a5b80ae87b293181b2e790aa59c601 (diff)
Improve the event initialization system
-rw-r--r--Improve-the-event-initialization-system.md210
-rw-r--r--Tutorials.md1
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)