From 38ffe06a2cc2ebcfa8d9725172643256a380f26d Mon Sep 17 00:00:00 2001 From: xCrystal Date: Thu, 18 Jan 2024 19:22:21 +0100 Subject: [PATCH] Implement clearing level in RAM according to End Space effect, supporting up to 4 clearable stages per level; Implement unlocking levels [Commit 1] (#35) --- constants/landmark_constants.asm | 3 +- constants/level_constants.asm | 16 +++- constants/space_constants.asm | 7 ++ data/{ => levels}/level_selection_menu.asm | 0 data/levels/levels.asm | 41 +++++++++ docs/develop/index.md | 4 +- engine/menus/cleared_level_screen.asm | 97 ++++++++++++++++++++++ engine/menus/level_selection_menu.asm | 11 ++- maps/DebugLevel2_Map1.asm | 4 +- maps/DebugLevel2_Map2.asm | 2 +- maps/DebugLevel5_Map1.asm | 2 +- ram/wram.asm | 7 +- 12 files changed, 183 insertions(+), 11 deletions(-) rename data/{ => levels}/level_selection_menu.asm (100%) create mode 100755 data/levels/levels.asm diff --git a/constants/landmark_constants.asm b/constants/landmark_constants.asm index cdb960ce8..c52907099 100644 --- a/constants/landmark_constants.asm +++ b/constants/landmark_constants.asm @@ -1,4 +1,4 @@ -; Landmarks indexes (see data/level_selection_menu.asm) +; Landmarks indexes (see data/levels/level_selection_menu.asm) const_def const LANDMARK_LEVEL_1 ; 00 if DEF(_DEBUG) @@ -9,6 +9,7 @@ if DEF(_DEBUG) const LANDMARK_DEBUGLEVEL_5 ; 05 endc DEF NUM_LANDMARKS EQU const_value +assert NUM_LANDMARKS <= 254 ; used in CaughtData (legacy) const_def $7f, -1 diff --git a/constants/level_constants.asm b/constants/level_constants.asm index 14aa803b8..cc4576bd2 100755 --- a/constants/level_constants.asm +++ b/constants/level_constants.asm @@ -1,4 +1,4 @@ -; Level indexes (see data/level_selection_menu.asm) +; Level constants (see data/levels/level_selection_menu.asm) const_def const LEVEL_1 ; 00 if DEF(_DEBUG) @@ -9,3 +9,17 @@ if DEF(_DEBUG) const DEBUGLEVEL_5 ; 05 endc DEF NUM_LEVELS EQU const_value +assert NUM_LEVELS <= 254 + +; Level stages + const_def + const STAGE_1 ; 00 + const STAGE_2 ; 01 + const STAGE_3 ; 02 + const STAGE_4 ; 03 + +; requirement types to unlock a given level + const_def + const UNLOCK_WHEN_LEVELS_CLEARED ; 00 + const UNLOCK_WHEN_NUMBER_OF_LEVELS_CLEARED ; 01 + const UNLOCK_WHEN_TECHNIQUES_CLEARED ; 02 diff --git a/constants/space_constants.asm b/constants/space_constants.asm index 73f45c407..98b260391 100755 --- a/constants/space_constants.asm +++ b/constants/space_constants.asm @@ -3,6 +3,13 @@ DEF FIRST_SPACE_METATILE EQU $80 DEF FIRST_GREY_SPACE_METATILE EQU $e0 DEF UNIQUE_SPACE_METATILES_MASK EQU %11111 +; End Space effect constants (denotes which stage of the level is cleared by this space; equivalent to STAGE_* constants) + const_def + const ES1 ; 0 + const ES2 ; 1 + const ES3 ; 2 + const ES4 ; 3 + ; Branch Space special direction values const_def 255, -1 const BRANCH_DIRECTION_INVALID ; -1 diff --git a/data/level_selection_menu.asm b/data/levels/level_selection_menu.asm similarity index 100% rename from data/level_selection_menu.asm rename to data/levels/level_selection_menu.asm diff --git a/data/levels/levels.asm b/data/levels/levels.asm new file mode 100755 index 000000000..490487252 --- /dev/null +++ b/data/levels/levels.asm @@ -0,0 +1,41 @@ +MACRO level_unlock_req + +; a list of levels that must be cleared (each must be in the form of: , STAGE_*) +if !STRCMP("\1", "LEVELS_CLEARED") + db UNLOCK_WHEN_\1 + rept (_NARG - 1) / 2 + db \2, \3 + shift + shift + endr + +; an amount of levels that must be cleared (regardless of stage) +elif !STRCMP("\1", "NUMBER_OF_LEVELS_CLEARED") + db UNLOCK_WHEN_\1 + db \2 + +; a list of techniques that must be cleared +elif !STRCMP("\1", "TECHNIQUES_CLEARED") + db UNLOCK_WHEN_\1 + rept _NARG - 1 + db \2 + shift + endr + +; else no requirements +endc + db $ff + REDEF x += 1 +ENDM + +LevelUnlockRequirements: +DEF x = 0 + level_unlock_req NONE ; LEVEL_1 (irrelevant) +if DEF(_DEBUG) + level_unlock_req NONE ; DEBUGLEVEL_1 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_1, STAGE_1 ; DEBUGLEVEL_2 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_2, STAGE_1 ; DEBUGLEVEL_3 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_3, STAGE_1 ; DEBUGLEVEL_4 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_4, STAGE_1 ; DEBUGLEVEL_5 +endc + assert x == NUM_LEVELS diff --git a/docs/develop/index.md b/docs/develop/index.md index 003be97ef..61ccc6e2d 100755 --- a/docs/develop/index.md +++ b/docs/develop/index.md @@ -82,7 +82,8 @@ - **wDieRoll** - **wSpacesLeft** -- Addresses within *wCurMapData* ~ *wCurMapDataEnd*: preserved on save. Initialized when entering a level, and updated accordingly throughout the level. Includes: +- Addresses within *wCurMapData* ~ *wCurMapDataEnd*: preserved on save. Initialized when entering a level (in StartMap), and updated accordingly throughout the level. Includes: + - **wCurLevel**: this one is not initialized in StartMap, but in LevelSelectionMenu, and stays static during the level. - **wCurTurn** - **wCurSpace** - **wCurLevelCoins** @@ -95,6 +96,7 @@ - Addresses within *wPlayerData* ~ *wPlayerDataEnd*: preserved on save. Includes: - **wUnlockedLevels**: flag array that tracks progression regarding which levels have been unlocked. + - **wClearedLevelsStage**: flag array that tracks progression regarding which levels have been cleared. Each level can have up to four clearable endings (N). - **wUnlockedTechniques**: flag array that tracks progression regarding which techniques have been unlocked. - **wCurOverworldMiscPal** diff --git a/engine/menus/cleared_level_screen.asm b/engine/menus/cleared_level_screen.asm index b5fecad5b..2d31289d8 100755 --- a/engine/menus/cleared_level_screen.asm +++ b/engine/menus/cleared_level_screen.asm @@ -30,6 +30,8 @@ ClearedLevelScreen: jr z, .loop .exit call AddLevelCoinsToBalance + call ClearLevel + call UnlockLevels ld c, 30 jp DelayFrames @@ -58,3 +60,98 @@ AddLevelCoinsToBalance: pop bc farcall GiveCoins ret + +ClearLevel: + ld a, [wCurSpaceEffect] ; End Space effect byte contains STAGE_* + call GetClearedLevelsStageAddress + ld b, SET_FLAG + ld d, 0 + ld a, [wCurLevel] + ld e, a + call FlagAction + ret + +UnlockLevels: + ld hl, LevelUnlockRequirements + ld de, wUnlockedLevels - 1 + ld b, 0 +.next_byte + ld c, 8 + inc de + ld a, [de] +.next_bit + srl a + push af + call nc, .CheckUnlockLevel ; skip if level is already unlocked + inc b + ld a, b + cp NUM_LEVELS + jr z, .done +; advance hl to next level in LevelUnlockRequirements +.loop + ld a, [hli] + inc a ; cp $ff + jr nz, .loop + pop af + dec c + jr z, .next_byte + jr .next_bit + +.done + pop af + ret + +; check if the LevelUnlockRequirements[b] at hl for unlocking level b are met. +; return hl pointing to up to the $ff byte of this LevelUnlockRequirements entry. +.CheckUnlockLevel: + push de + push bc + ld a, [hl] + cp $ff + jr z, .reqs_met ; jump if no specific reqs to unlock this level + inc hl + cp UNLOCK_WHEN_LEVELS_CLEARED + jr z, .check_levels_cleared_loop + cp UNLOCK_WHEN_NUMBER_OF_LEVELS_CLEARED + cp UNLOCK_WHEN_TECHNIQUES_CLEARED + +.check_levels_cleared_loop + ld a, [hli] ; which level + ld e, a + inc a ; cp $ff + jr z, .reqs_met ; jump when no more required levels and all passed so far + ld a, [hli] ; which stage + push hl + call GetClearedLevelsStageAddress + ld b, CHECK_FLAG + ld d, 0 + call FlagAction + pop hl + jr z, .reqs_not_met ; if this level is not cleared, requirements aren't met + jr .check_levels_cleared_loop ; otherwise check next level in list + +.reqs_met + pop bc + pop de + ret + +.reqs_not_met + pop bc + pop de + ret + +; return hl = wClearedLevelsStage* given STAGE_ constant in a +GetClearedLevelsStageAddress: + ld hl, wClearedLevelsStage1 + cp ES1 + ret z + ld hl, wClearedLevelsStage2 + cp ES2 + ret z + ld hl, wClearedLevelsStage3 + cp ES3 + ret z + ld hl, wClearedLevelsStage4 + ret + +INCLUDE "data/levels/levels.asm" diff --git a/engine/menus/level_selection_menu.asm b/engine/menus/level_selection_menu.asm index 123e6a9b8..046e692bb 100755 --- a/engine/menus/level_selection_menu.asm +++ b/engine/menus/level_selection_menu.asm @@ -118,6 +118,13 @@ LevelSelectionMenu:: jr .main_loop .enter_level + ld a, [wLevelSelectionMenuCurrentLandmark] + ld e, a + ld d, 0 + ld hl, LandmarkToLevelTable + add hl, de + ld a, [hl] + ld [wCurLevel], a ld a, [wLevelSelectionMenuCurrentLandmark] call LevelSelectionMenu_GetLandmarkSpawnPoint ld [wDefaultSpawnpoint], a @@ -521,12 +528,10 @@ LevelSelectionMenu_GetLandmarkName:: LevelSelectionMenu_GetLandmarkSpawnPoint: ; Return SPAWN_* (a) of landmark a. - push hl ld hl, LevelSelectionMenu_Landmarks + $5 ld bc, LevelSelectionMenu_Landmarks.landmark2 - LevelSelectionMenu_Landmarks.landmark1 call AddNTimes ld a, [hl] - pop hl ret LevelSelectionMenu_GetValidKeys: @@ -744,7 +749,7 @@ _LevelSelectionMenuHandleTransition: xor a ret -INCLUDE "data/level_selection_menu.asm" +INCLUDE "data/levels/level_selection_menu.asm" LevelSelectionMenuGFX: INCBIN "gfx/level_selection_menu/background.2bpp.lz" diff --git a/maps/DebugLevel2_Map1.asm b/maps/DebugLevel2_Map1.asm index 55a73a0ee..cdf7fe046 100755 --- a/maps/DebugLevel2_Map1.asm +++ b/maps/DebugLevel2_Map1.asm @@ -37,7 +37,7 @@ DebugLevel2_Map1_MapEvents: DebugLevel2_Map1_MapSpaces: space 6, 16, $0, 1 ; 0 space 6, 14, $0, 2 ; 1 - space 6, 12, .BS1 ; 2 .BS1 + space 6, 12, .BS1 ; 2 space 6, 10, $0, GO_UP ; 3 space 20, 2, $0, 5 ; 4 space 22, 2, $0, 6 ; 5 @@ -47,7 +47,7 @@ DebugLevel2_Map1_MapSpaces: space 22, 14, $0, 10 ; 9 space 20, 14, $0, 11 ; 10 space 20, 16, $0, 12 ; 11 - space 20, 18, $0, 12 ; 12 + space 20, 18, ES1, 12 ; 12 space 4, 12, $0, 14 ; 13 space 2, 12, $0, 15 ; 14 diff --git a/maps/DebugLevel2_Map2.asm b/maps/DebugLevel2_Map2.asm index 1965e73f5..a215f58db 100755 --- a/maps/DebugLevel2_Map2.asm +++ b/maps/DebugLevel2_Map2.asm @@ -30,4 +30,4 @@ DebugLevel2_Map2_MapSpaces: space 4, 2, $0, 8 ; 7 space 6, 2, $0, 9 ; 8 space 6, 6, $0, 10 ; 9 - space 6, 8, $0, 10 ; 10 + space 6, 8, ES2, 10 ; 10 diff --git a/maps/DebugLevel5_Map1.asm b/maps/DebugLevel5_Map1.asm index 891cb3b54..66c328f6f 100755 --- a/maps/DebugLevel5_Map1.asm +++ b/maps/DebugLevel5_Map1.asm @@ -105,7 +105,7 @@ DebugLevel5_Map1_MapSpaces: space 8, 8, $0, 32 ; 31 space 10, 8, $0, 33 ; 32 space 12, 8, $0, 34 ; 33 - space 14, 8, $0, 34 ; 34 + space 14, 8, ES1, 34 ; 34 space 6, 2, $0, 36 ; 35 space 6, 0, .BS36 ; 36 diff --git a/ram/wram.asm b/ram/wram.asm index 7f6f68311..65f54b65d 100644 --- a/ram/wram.asm +++ b/ram/wram.asm @@ -2441,7 +2441,11 @@ wMooMooBerries:: db wUndergroundSwitchPositions:: db wFarfetchdPosition:: db -wUnlockedLevels:: flag_array NUM_LANDMARKS +wUnlockedLevels:: flag_array NUM_LEVELS +wClearedLevelsStage1:: flag_array NUM_LEVELS +wClearedLevelsStage2:: flag_array NUM_LEVELS +wClearedLevelsStage3:: flag_array NUM_LEVELS +wClearedLevelsStage4:: flag_array NUM_LEVELS wUnlockedTechniques:: flag_array NUM_TECHNIQUES @@ -2560,6 +2564,7 @@ wYCoord:: db wXCoord:: db wScreenSave:: ds SCREEN_META_WIDTH * SCREEN_META_HEIGHT +wCurLevel:: db wCurTurn:: dw wCurSpace:: db wCurLevelCoins:: ds 3