From fa42b14e293d45331b487bf61826604524ea7fa4 Mon Sep 17 00:00:00 2001 From: xCrystal Date: Mon, 22 Jan 2024 19:32:19 +0100 Subject: [PATCH] Level selection menu: draw level cleared indicators [Commit 1] (#12) --- constants/landmark_constants.asm | 6 + constants/level_constants.asm | 14 +- constants/space_constants.asm | 2 +- data/levels/level_selection_menu.asm | 17 +- data/levels/levels.asm | 10 +- docs/develop/index.md | 2 +- engine/menus/cleared_level_screen.asm | 16 +- engine/menus/level_selection_menu.asm | 196 ++++++++++++++++++++++-- gfx/level_selection_menu/background.png | Bin 1167 -> 1208 bytes home/flag.asm | 14 ++ 10 files changed, 228 insertions(+), 49 deletions(-) diff --git a/constants/landmark_constants.asm b/constants/landmark_constants.asm index 5637c3084..5c9ae9d2a 100644 --- a/constants/landmark_constants.asm +++ b/constants/landmark_constants.asm @@ -20,6 +20,12 @@ DEF LSMTEXTBOX_MAX_TEXT_ROW_LENGTH EQU LSMTEXTBOX_WIDTH - 5 DEF LSMTEXTBOX_BLACK_TILE EQU "" DEF LSMTEXTBOX_LEVEL_INDICATOR_TILE EQU $30 DEF LSMTEXTBOX_LEVEL_NUMBERS_FIRST_TILE EQU $31 + const_def $3b ; $31 + 10 + ; these must be consecutive + const LSMTEXTBOX_STAGE_1_INDICATOR_TILE ; $3b + const LSMTEXTBOX_STAGE_2_INDICATOR_TILE ; $3c + const LSMTEXTBOX_STAGE_3_INDICATOR_TILE ; $3d + const LSMTEXTBOX_STAGE_4_INDICATOR_TILE ; $3e ; used in CaughtData (legacy) const_def $7f, -1 diff --git a/constants/level_constants.asm b/constants/level_constants.asm index cc4576bd2..7beb3896b 100755 --- a/constants/level_constants.asm +++ b/constants/level_constants.asm @@ -13,10 +13,16 @@ assert NUM_LEVELS <= 254 ; Level stages const_def - const STAGE_1 ; 00 - const STAGE_2 ; 01 - const STAGE_3 ; 02 - const STAGE_4 ; 03 + const STAGE_1_F ; 00 + const STAGE_2_F ; 01 + const STAGE_3_F ; 02 + const STAGE_4_F ; 03 +DEF NUM_LEVEL_STAGES EQU const_value + +DEF STAGE_1 EQU 1 << STAGE_1_F +DEF STAGE_2 EQU 1 << STAGE_2_F +DEF STAGE_3 EQU 1 << STAGE_3_F +DEF STAGE_4 EQU 1 << STAGE_4_F ; requirement types to unlock a given level const_def diff --git a/constants/space_constants.asm b/constants/space_constants.asm index 98b260391..fb632d97c 100755 --- a/constants/space_constants.asm +++ b/constants/space_constants.asm @@ -3,7 +3,7 @@ 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) +; End Space effect constants (denotes which stage of the level is cleared by this space; equivalent to STAGE_*_F constants) const_def const ES1 ; 0 const ES2 ; 1 diff --git a/data/levels/level_selection_menu.asm b/data/levels/level_selection_menu.asm index e66c09e1e..2d169de16 100755 --- a/data/levels/level_selection_menu.asm +++ b/data/levels/level_selection_menu.asm @@ -4,7 +4,7 @@ DEF LEVELSELECTIONMENU_LANDMARK_OFFSET_X EQU TILE_WIDTH + $8 + 4 DEF LEVELSELECTIONMENU_LANDMARK_OFFSET_Y EQU TILE_WIDTH + $10 + 4 MACRO level_selection_menu_landmark -; page number, xcoord (in tiles), ycoord (in tiles), ptr to name, spawn point (SPAWN_*) +; page number, xcoord (in tiles), ycoord (in tiles), ptr to name, spawn point (SPAWN_*), level stages ; xcoord ranges between 0 and 17 (SCREEN_WIDTH points minus two) ; when crossing pages, x=17 and x=0 are adjacent (one tile apart) ; ycoord ranges between 0 and 13 (SCREEN_HEIGH points minus four) @@ -14,6 +14,7 @@ MACRO level_selection_menu_landmark db LEVELSELECTIONMENU_LANDMARK_OFFSET_Y + \3 * TILE_WIDTH dw \4 db \5 + db \6 ; note: level stages are inherent to level rather than landmark, but they are shown as part of a landmark. ENDM MACRO level_selection_menu_landmark_name @@ -23,16 +24,16 @@ MACRO level_selection_menu_landmark_name ENDM LevelSelectionMenu_Landmarks: - table_width 6, LevelSelectionMenu_Landmarks + table_width 7, LevelSelectionMenu_Landmarks .landmark1 - level_selection_menu_landmark 0, 16, 11, .Level1LandmarkName, SPAWN_LEVEL_1 ; LANDMARK_LEVEL_1 + level_selection_menu_landmark 0, 16, 11, .Level1LandmarkName, SPAWN_LEVEL_1, STAGE_1 ; LANDMARK_LEVEL_1 .landmark2 if DEF(_DEBUG) - level_selection_menu_landmark 0, 16, 10, .DebugLevel1LandmarkName, SPAWN_DEBUGLEVEL_1 ; LANDMARK_DEBUGLEVEL_1 - level_selection_menu_landmark 0, 11, 8, .DebugLevel2LandmarkName, SPAWN_DEBUGLEVEL_2 ; LANDMARK_DEBUGLEVEL_2 - level_selection_menu_landmark 0, 9, 10, .DebugLevel3LandmarkName, SPAWN_DEBUGLEVEL_3 ; LANDMARK_DEBUGLEVEL_3 - level_selection_menu_landmark 1, 16, 10, .DebugLevel4LandmarkName, SPAWN_DEBUGLEVEL_4 ; LANDMARK_DEBUGLEVEL_4 - level_selection_menu_landmark 2, 9, 4, .DebugLevel5LandmarkName, SPAWN_DEBUGLEVEL_5 ; LANDMARK_DEBUGLEVEL_5 + level_selection_menu_landmark 0, 16, 10, .DebugLevel1LandmarkName, SPAWN_DEBUGLEVEL_1, STAGE_1 | STAGE_3 | STAGE_4 ; LANDMARK_DEBUGLEVEL_1 + level_selection_menu_landmark 0, 11, 8, .DebugLevel2LandmarkName, SPAWN_DEBUGLEVEL_2, STAGE_1 | STAGE_2 ; LANDMARK_DEBUGLEVEL_2 + level_selection_menu_landmark 0, 9, 10, .DebugLevel3LandmarkName, SPAWN_DEBUGLEVEL_3, STAGE_1 | STAGE_2 | STAGE_3 | STAGE_4 ; LANDMARK_DEBUGLEVEL_3 + level_selection_menu_landmark 1, 16, 10, .DebugLevel4LandmarkName, SPAWN_DEBUGLEVEL_4, STAGE_1 ; LANDMARK_DEBUGLEVEL_4 + level_selection_menu_landmark 2, 9, 4, .DebugLevel5LandmarkName, SPAWN_DEBUGLEVEL_5, STAGE_1 ; LANDMARK_DEBUGLEVEL_5 endc assert_table_length NUM_LANDMARKS diff --git a/data/levels/levels.asm b/data/levels/levels.asm index 84e73aa4d..121463cc6 100755 --- a/data/levels/levels.asm +++ b/data/levels/levels.asm @@ -1,6 +1,6 @@ MACRO level_unlock_req -; a list of levels that must be cleared (each must be in the form of: , STAGE_*) +; a list of levels that must be cleared (each must be in the form of: , STAGE_*_F) if !STRCMP("\1", "LEVELS_CLEARED") db UNLOCK_WHEN_\1 rept (_NARG - 1) / 2 @@ -34,10 +34,10 @@ 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 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_1, STAGE_1_F ; DEBUGLEVEL_2 + level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_2, STAGE_1_F ; DEBUGLEVEL_3 +; level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_3, STAGE_1_F ; DEBUGLEVEL_4 +; level_unlock_req LEVELS_CLEARED, DEBUGLEVEL_4, STAGE_1_F ; DEBUGLEVEL_5 level_unlock_req NUMBER_OF_LEVELS_CLEARED, 3 ; DEBUGLEVEL_4 level_unlock_req TECHNIQUES_CLEARED, TECHNIQUE_FLASH | TECHNIQUE_WATERFALL ; DEBUGLEVEL_5 endc diff --git a/docs/develop/index.md b/docs/develop/index.md index 61ccc6e2d..f3857f535 100755 --- a/docs/develop/index.md +++ b/docs/develop/index.md @@ -83,7 +83,7 @@ - **wSpacesLeft** - 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. + - **wCurLevel**: this one is not initialized in StartMap, but in LevelSelectionMenu (where it is also used), and stays static during the level. - **wCurTurn** - **wCurSpace** - **wCurLevelCoins** diff --git a/engine/menus/cleared_level_screen.asm b/engine/menus/cleared_level_screen.asm index 3fb72871c..a2ed4473e 100755 --- a/engine/menus/cleared_level_screen.asm +++ b/engine/menus/cleared_level_screen.asm @@ -66,7 +66,7 @@ AddLevelCoinsToBalance: ret ClearLevel: - ld a, [wCurSpaceEffect] ; End Space effect byte contains STAGE_* + ld a, [wCurSpaceEffect] ; End Space effect byte contains STAGE_*_F call GetClearedLevelsStageAddress ld b, SET_FLAG ld d, 0 @@ -193,20 +193,6 @@ ComputeLevelsToUnlock: 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 - SaveUnlockedLevels: ld hl, wTempUnlockedLevels .loop diff --git a/engine/menus/level_selection_menu.asm b/engine/menus/level_selection_menu.asm index 89e1df98e..c5d9865d8 100755 --- a/engine/menus/level_selection_menu.asm +++ b/engine/menus/level_selection_menu.asm @@ -40,8 +40,9 @@ LevelSelectionMenu:: ld a, [wLevelSelectionMenuCurrentLandmark] call LevelSelectionMenu_InitPlayerSprite call LevelSelectionMenu_InitLandmark - call LevelSelectionMenu_PrintLevelAndLandmarkName + call LevelSelectionMenu_PrintLevelAndLandmarkNameAndStageIndicators call LevelSelectionMenu_DrawDirectionalArrows + call LevelSelectionMenu_DrawStageTrophies .main_loop farcall PlaySpriteAnimations @@ -94,16 +95,16 @@ LevelSelectionMenu:: ld a, h ld [bc], a -; clear textbox as we are about to move out of current landmark +; clear textbox and non-player sprites, as we are about to move out of current landmark call LevelSelectionMenu_Delay10Frames call LevelSelectionMenu_ClearTextbox ; preserves e + call LevelSelectionMenu_ClearNonPlayerSpriteOAM ; preserves e ; begin transition xor a ; FALSE ld [wLevelSelectionMenuStandingStill], a ld a, 1 << 7 ; "first step of movement" flag ld [wLevelSelectionMenuMovementStepsLeft], a call LevelSelectionMenu_SetAnimSeqAndFrameset - call LevelSelectionMenu_ClearNonPlayerSpriteOAM ; perform all movements to transition to the new landmark .wait_transition_loop @@ -117,18 +118,12 @@ LevelSelectionMenu:: jr z, .wait_transition_loop call LevelSelectionMenu_InitLandmark - call LevelSelectionMenu_PrintLevelAndLandmarkName + call LevelSelectionMenu_PrintLevelAndLandmarkNameAndStageIndicators call LevelSelectionMenu_DrawDirectionalArrows + call LevelSelectionMenu_DrawStageTrophies jp .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 @@ -170,7 +165,11 @@ LevelSelectionMenu_LoadGFX: call FarCopyBytes ld hl, LevelSelectionMenuDirectionalArrowsGFX ; ld de, vTiles0 + 24 tiles - ld bc, 4 tiles + ld bc, NUM_DIRECTIONS tiles + call FarCopyBytes + ld hl, LevelSelectionMenuStageTrophiesGFX +; ld de, vTiles0 + (24 + NUM_DIRECTIONS) tiles + ld bc, NUM_LEVEL_STAGES * 2 tiles call FarCopyBytes ret @@ -268,7 +267,7 @@ endr ld [de], a ret -LevelSelectionMenu_PrintLevelAndLandmarkName: +LevelSelectionMenu_PrintLevelAndLandmarkNameAndStageIndicators: ; level indicator and level numbers are 8x16. ; botton half of their graphics are $10 tiles after the top half. hlcoord LSMTEXTBOX_X_COORD, LSMTEXTBOX_Y_COORD @@ -278,12 +277,14 @@ LevelSelectionMenu_PrintLevelAndLandmarkName: ld bc, SCREEN_WIDTH add hl, bc ld [hl], a +; get level from landmark and copy it to wCurLevel ld a, [wLevelSelectionMenuCurrentLandmark] ld e, a ld d, 0 ld hl, LandmarkToLevelTable add hl, de ld a, [hl] + ld [wCurLevel], a ld c, 0 .loop1 ld e, a @@ -308,6 +309,7 @@ LevelSelectionMenu_PrintLevelAndLandmarkName: add e ld [hl], a + ld a, [wLevelSelectionMenuCurrentLandmark] call LevelSelectionMenu_GetLandmarkName ld hl, wStringBuffer1 decoord LSMTEXTBOX_X_COORD + 4, LSMTEXTBOX_Y_COORD @@ -318,11 +320,44 @@ LevelSelectionMenu_PrintLevelAndLandmarkName: ld bc, LSMTEXTBOX_MAX_TEXT_ROW_LENGTH call CopyBytes + ld de, 0 ; e tracks number of already printed stages, to know where to print current one (in descending order) + ld a, [wLevelSelectionMenuCurrentLandmark] + call LevelSelectionMenu_GetLandmarkLevelStages + bit STAGE_4_F, a + push af + ld a, LSMTEXTBOX_STAGE_4_INDICATOR_TILE + call nz, .PrintStageTile + pop af + bit STAGE_3_F, a + push af + ld a, LSMTEXTBOX_STAGE_3_INDICATOR_TILE + call nz, .PrintStageTile + pop af + bit STAGE_2_F, a + push af + ld a, LSMTEXTBOX_STAGE_2_INDICATOR_TILE + call nz, .PrintStageTile + pop af + bit STAGE_1_F, a + ld a, LSMTEXTBOX_STAGE_1_INDICATOR_TILE + call nz, .PrintStageTile + call WaitBGMap xor a ld [hBGMapMode], a ret +.PrintStageTile: + hlcoord LSMTEXTBOX_X_COORD + (LSMTEXTBOX_WIDTH - 1), LSMTEXTBOX_Y_COORD + add hl, de + ld [hl], a + ld bc, SCREEN_WIDTH + add hl, bc + add $10 + ld [hl], a + dec de + ret + LevelSelectionMenu_ClearTextbox: hlcoord LSMTEXTBOX_X_COORD, LSMTEXTBOX_Y_COORD ld a, LSMTEXTBOX_BLACK_TILE @@ -386,6 +421,127 @@ LevelSelectionMenu_DrawDirectionalArrows: db -4, -16, 24 + LEFT db -4, 8, 24 + RIGHT +LevelSelectionMenu_DrawStageTrophies: +; Draw stage trophies OAM of cleared level stages. +; These objects go after player sprite and arrows in OAM. + ld de, wShadowOAM + ($4 + NUM_DIRECTIONS + $0) * SPRITEOAMSTRUCT_LENGTH + bccoord LSMTEXTBOX_X_COORD + (LSMTEXTBOX_WIDTH - 1), LSMTEXTBOX_Y_COORD + ld a, 6 + call .draw_stage_trophy + ret c + ld de, wShadowOAM + ($4 + NUM_DIRECTIONS + $2) * SPRITEOAMSTRUCT_LENGTH + bccoord LSMTEXTBOX_X_COORD + (LSMTEXTBOX_WIDTH - 2), LSMTEXTBOX_Y_COORD + ld a, 4 + call .draw_stage_trophy + ret c + ld de, wShadowOAM + ($4 + NUM_DIRECTIONS + $4) * SPRITEOAMSTRUCT_LENGTH + bccoord LSMTEXTBOX_X_COORD + (LSMTEXTBOX_WIDTH - 3), LSMTEXTBOX_Y_COORD + ld a, 2 + call .draw_stage_trophy + ret c + ld de, wShadowOAM + ($4 + NUM_DIRECTIONS + $6) * SPRITEOAMSTRUCT_LENGTH + bccoord LSMTEXTBOX_X_COORD + (LSMTEXTBOX_WIDTH - 4), LSMTEXTBOX_Y_COORD + xor a + call .draw_stage_trophy + ret + +.draw_stage_trophy: +; input: +; - de: wShadowOAM address +; - bc: current tile address in wTilemap +; - a: .BaseOAMCoords entry to use +; if current tile is not a stage indicator tile, return carry to signal to not keep going + push af + ld a, [bc] + sub LSMTEXTBOX_STAGE_1_INDICATOR_TILE + jr c, .ret_c + cp STAGE_4_F + 1 + jr nc, .ret_c + call .IsLevelStageCleared + jr z, .ret_nc ; this level has not been cleared, but there are more levels yet to check, so return nc + add a + add a + ld c, a + ld b, 0 + ld hl, .BaseOAMTilesAttrs + add hl, bc + pop af + push hl + add a + ld c, a + ld b, 0 + ld hl, .BaseOAMCoords + add hl, bc + pop bc + call .CopyOAM + call .CopyOAM + xor a + ret ; nc + +.ret_c: + pop af + scf + ret + +.ret_nc + pop af + xor a + ret + +.IsLevelStageCleared: +; return nz if [wCurLevel]'s stage in a has been cleared, z otherwise. +; preserve a and de. + ld c, a + push bc + push de + call GetClearedLevelsStageAddress + ld b, CHECK_FLAG + ld d, 0 + ld a, [wCurLevel] + ld e, a + call FlagAction + pop de + pop bc + ld a, c + ret + +.CopyOAM: + ld a, [hli] + ld [de], a + inc de + ld a, [hli] + ld [de], a + inc de + ld a, [bc] + ld [de], a + inc bc + inc de + ld a, [bc] + ld [de], a + inc bc + inc de + ret + +.BaseOAMCoords: + db 17 * TILE_WIDTH, 16 * TILE_WIDTH + db 18 * TILE_WIDTH, 16 * TILE_WIDTH + db 17 * TILE_WIDTH, 17 * TILE_WIDTH + db 18 * TILE_WIDTH, 17 * TILE_WIDTH + db 17 * TILE_WIDTH, 18 * TILE_WIDTH + db 18 * TILE_WIDTH, 18 * TILE_WIDTH + db 17 * TILE_WIDTH, 19 * TILE_WIDTH + db 18 * TILE_WIDTH, 19 * TILE_WIDTH + +.BaseOAMTilesAttrs: + db 24 + NUM_DIRECTIONS + 0, 2 + db 24 + NUM_DIRECTIONS + 1, 2 + db 24 + NUM_DIRECTIONS + 2, 3 + db 24 + NUM_DIRECTIONS + 3, 3 + db 24 + NUM_DIRECTIONS + 4, 4 + db 24 + NUM_DIRECTIONS + 5, 4 + db 24 + NUM_DIRECTIONS + 6, 5 + db 24 + NUM_DIRECTIONS + 7, 5 + LevelSelectionMenu_ClearNonPlayerSpriteOAM: ld hl, wShadowOAM + $4 * SPRITEOAMSTRUCT_LENGTH ld bc, wShadowOAMEnd - (wShadowOAM + $4 * SPRITEOAMSTRUCT_LENGTH) @@ -570,14 +726,13 @@ LevelSelectionMenu_GetLandmarkCoords:: ret LevelSelectionMenu_GetLandmarkName:: -; Copy the name of landmark e to wStringBuffer1 (tow row) and wStringBuffer2 (bottom row). +; Copy the name of landmark a to wStringBuffer1 (tow row) and wStringBuffer2 (bottom row). push hl push de push bc ld hl, LevelSelectionMenu_Landmarks + $3 ld bc, LevelSelectionMenu_Landmarks.landmark2 - LevelSelectionMenu_Landmarks.landmark1 - ld a, e call AddNTimes ld a, [hli] ld h, [hl] @@ -611,6 +766,14 @@ LevelSelectionMenu_GetLandmarkSpawnPoint: ld a, [hl] ret +LevelSelectionMenu_GetLandmarkLevelStages: +; Return STAGE_* flags (a) of landmark a. + ld hl, LevelSelectionMenu_Landmarks + $6 + ld bc, LevelSelectionMenu_Landmarks.landmark2 - LevelSelectionMenu_Landmarks.landmark1 + call AddNTimes + ld a, [hl] + ret + LevelSelectionMenu_GetValidKeys: call LevelSelectionMenu_GetValidDirections ld a, c @@ -848,3 +1011,6 @@ INCLUDE "gfx/level_selection_menu/attrmap.asm" LevelSelectionMenuDirectionalArrowsGFX: INCBIN "gfx/level_selection_menu/directional_arrows.2bpp" + +LevelSelectionMenuStageTrophiesGFX: +;INCBIN "gfx/level_selection_menu/stage_trophies.2bpp" diff --git a/gfx/level_selection_menu/background.png b/gfx/level_selection_menu/background.png index d78327d9fd406cf64e3dbaa3c45f0b2633d6b532..252fa60c9acdca7a7d5b1a7a1407fe1037f84584 100755 GIT binary patch delta 1137 zcmV-%1djWU3AhQ6R)0-NL_t(|UhSF-ZsRZvM4Mu-m>jY_WOm2fF_SH-SKOnGd ziWEhWik$XzKA*220pH)>%k^KM<(l1FzP`SeEBh$+d1fDYV}Q%$^3&?$dcC?f=szgQ zPp8vzEn#gb(Y(m${4)EWKi6L{_;4O=e9+c=z`jbY^R!3wet#wU{cQxVu5YzR4+Al*r(=WDb2elCM`s9BeUdd0%$Ns z*wR)jJ!7M4Q6a2suaZIZoOnrv{@!k3!Pts@7WqOlxtF|rw!8(v>8&%Ift(EkPXFx! z=wf)-Kzj>iN7})?jS;If@`-k$xO#HiwtwRijJS*zO`;bL7CE~W8eHD< zn!(DCfZdWj8^_LH7YO%W^;=3~*O0}3Nr0^vpTPNNO#mB{OkN^dK-I79*GhIa_{0K5 zgO324ust&Ywy<5AJ7iXlq_Td?tTZ-s%jHB|%o%I8bGW zDvzGT*(=%JVh1RZRdyzScHZI)koC(dpYqu|+C6x=AA*Q3y81!?vUz98Q=5bwpI1$| zUjGU8sTb+%_{$k!7Bg*U*s<^F$GxbX|N0+6%zv2Coj@y}*V;;{g?4KzS?SeNvUyRi z@rWvzL={XT{mdU7e?0{103&Q^>!6KN>^<4K12BX5n$ufRI*8tn?gVPDw~~AHTG6=h z%I8G==-mNoq5jQlARg*KEsW@nq5&z<()ZOlUCrMkZD5#z$Sgi*mf3r@qs5P&x9$Xy zoqy5A%ql0dpCvu#$$q@5UG-y&9YDvnP=DD0aH9sTqiEy9e)b-)Z!fe*^!}h7n?R*v zTYm>Qcu!vbr{IG!M*_S#>sJ8Jg3({Q%vo_E>!DSuNpuV*OYJcsk9?A4;?At@FZIjvZh|RtNynr73$6FBl z+BREWVzUIW*{_sjAd_#6j<@&tkHqeWz|u|uncYrXp?)+*CI^&hf&i=gDhab&0%+j9 z0OFYqwql>DE&BnUekPA#-$>s+Yq9$X?2-T)EC#F=>R7#28#UO4ZpHKQ8QAqJAZiNE zXyuIRk8%z^XICM>-Ac+!T3QW zzg#ZMIl|gfym^t)`DOOsf6l*P@)10)ao}3-0mmw}!P6em`+qHoVuUP{1Lj2!fCZ?{ z1Bxjss4Op;yfmLbKj#}@gcAVv8kA%(jLGU)B?>TtdQnAZzDsC4nGH{_cCyY>u<+Q8 zSOmDXz&_=;R{NP8>q9xNIgejEfKH?Z>Xs^PYkC%+AZ|NvE`owFOkin#NP1~uve;mg zi$m-+Y^dDEn}5j>tI8SwO#v|2o@7$bIjL4v)cuyocMVzomjal*_yoZ}YXR7tWbz1a0ad?tTr1hT!8;bn zb3OuW!uFO0FvE5`pJy$& z-uMaispsjd|K%NEE4H+qVSV2-j(br%|MfqDSbs62JArmS&$XRW3+>i+veK)kWb>k2 z^Wk+c@j94z`dK{ce|-s52P4e1b<#$Z`;u(k0a!u&n$z1+>O}8HcLKH7+sVCpt!Q3& z=5wNP^zHz)Q2*v@ARelt7DjYO(S$^_jD2-3SM&Esn;2FgDvQrqW%i!e-r{@DTXzD_ z&VT4)W}TDS&x)S&MuJ0Zq%R+6xTR7&fWu#?S=M;-k-E%3n*%~ z^>=`iWAgGp1)s#61#ocIj{v?4dVlRQ?}`iAIF)Dmk=)uW*;ic>jiGErmt^usrfLV^ z1TnEFab7Yr{aX7O)VFp(jeXT2nQo2kM}KD8N5C^p`s6>;3cz-N63IX&pN;jmx9>+| z_m{wEr-00EsjX1o^HC!Q);F)M-YXT%ZUvx;FC`JrY{C`$Ol{c%l=L%s36Ar0?Xy~& zkHjtopb2BrYN3wRYqgPK7rK>Fe9ywJ9|2y1M!RQJe}sGbp24pI;DnxeQYZDGB|+Df z)