Skip to content

Allow more than 15 object_events per map

Rangi edited this page Nov 25, 2021 · 9 revisions

Map scripts in maps/*.asm declare new objects—NPCs, item balls, Berry trees, etc—with the object_event macro. But you can't have more than 15 object_events in one map. (GoldenrodCity.asm reaches this limit, and four other maps nearly do with 14 object_events.) Turns out this is an easy limit to increase.

Edit constants/map_object_constants.asm:

 MAPOBJECT_LENGTH EQU _RS
-NUM_OBJECTS EQU 16
+NUM_OBJECTS EQU 18
 PLAYER_OBJECT EQU 0

And edit wram.asm:

-	ds 40
+	ds 6
 
 wMapObjects::
 wPlayerObject:: map_object wPlayer ; player is map object 0
-; wMap1Object - wMap15Object
+; wMap1Object - wMap17Object
 for n, 1, NUM_OBJECTS
 wMap{d:n}Object:: map_object wMap{d:n}
 endr
 
 wObjectMasks:: ds NUM_OBJECTS

That's it! We just added space for maps to define 17 total object_events. (Plus the player object, making 18 total.) So you could, for example, add two more NPCs to make Goldenrod City feel busier.

Let's look at how it works. wMapObjects is an array of map_object structs, one for each possible object (including the player). You can see what data is stored for each map_object in the macro definition in macros/wram.asm:

 map_object: MACRO
 \1ObjectStructID::  db
 \1ObjectSprite::    db
 \1ObjectYCoord::    db
 \1ObjectXCoord::    db
 \1ObjectMovement::  db
 \1ObjectRadius::    db
 \1ObjectHour::      db
 \1ObjectTimeOfDay:: db
 \1ObjectColor::     db
 \1ObjectRange::     db
 \1ObjectScript::    dw
 \1ObjectEventFlag:: dw
	ds 2
 ENDM

This is all the same data that gets declared by the object_event macro, defined in macros/scripts/maps.asm:

 object_event: MACRO
 ;\1: x: left to right, starts at 0
 ;\2: y: top to bottom, starts at 0
 ;\3: sprite: a SPRITE_* constant
 ;\4: movement function: a SPRITEMOVEDATA_* constant
 ;\5, \6: movement radius: x, y
 ;\7, \8: hour limits: h1, h2 (0-23)
 ;  * if h1 < h2, the object_event will only appear from h1 to h2
 ;  * if h1 > h2, the object_event will not appear from h2 to h1
 ;  * if h1 == h2, the object_event will always appear
 ;  * if h1 == -1, h2 is treated as a time-of-day value:
 ;    a combo of MORN, DAY, and/or NITE, or -1 to always appear
 ;\9: color: a PAL_NPC_* constant, or 0 for sprite default
 ;\<10>: function: a OBJECTTYPE_* constant
 ;\<11>: sight range: applies to OBJECTTYPE_TRAINER
 ;\<12>: script pointer
 ;\<13>: event flag: an EVENT_* constant, or -1 to always appear
	db \3, \2 + 4, \1 + 4, \4
	dn \6, \5
	db \7, \8
	dn \9, \<10>
	db \<11>
	dw \<12>, \<13>
 ; the dummy PlayerObjectTemplate object_event has no def_object_events
 if DEF(_NUM_OBJECT_EVENTS)
 {_NUM_OBJECT_EVENTS} += 1
 endc
 ENDM

wObjectMasks is likewise an array of mask bytes, one for each possible object. If the mask is −1, the object is hidden.

Basically, the object_event structs in the ROM get copied into the map_object structs in RAM, so there needs to be enough space in RAM for all of them. And each new object needs 17 bytes—16 for its map_object (count them), and one for its mask—so we had to use 34 of the unused bytes in that ds 40.

If you wanted to allow an 18th object, you would have to find 17 bytes of unused space here and there in the same SECTION as wMapObjects and wObjectMasks. Which is not difficult; if you scroll down to the "map scene ids" and "fight counts", they both have large chunks of unused space afterwards (ds 49 and ds 100).

Clone this wiki locally