From 659dfb085a932c40566b9b77df63202cc210c675 Mon Sep 17 00:00:00 2001 From: xCrystal Date: Wed, 13 Mar 2024 18:17:13 +0100 Subject: [PATCH] docs --- README.md | 2 +- docs/usage/img/level_selection_menu.bmp | Bin 69174 -> 69174 bytes docs/usage/img/lsm_unlock.bmp | Bin 0 -> 69174 bytes docs/usage/index.md | 24 ++++++++++++++++-------- gfx/level_selection_menu/background.pal | 16 ++++++++-------- 5 files changed, 25 insertions(+), 17 deletions(-) create mode 100755 docs/usage/img/lsm_unlock.bmp diff --git a/README.md b/README.md index 0c296f622..948241b6d 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,6 @@ Compared to pokecrystal and the Pokemon Crystal ROM, the ROM built by pokecrysta pokecrystal-board requires RGBDS 0.7.0 to build. It has two build targets: *crystal*, and *crystal_debug*. The former builds a ROM with the *_DEBUG* symbol undefined, and the latter builds a ROM with the *_DEBUG* symbol defined. *crystal_debug* is meant to include additional content and configurations to facilitate testing during development, while *crystal* builds the ROM meant to be hypothetically released to the public. Other than that, refer to the [install docs from pokecrystal](INSTALL.md) for detailed instructions on how to setup and build pokecrystal-board. -If you have specific questions about the usage of pokecrystal-board or how to contribute to it, or to report a bug, feel free to open an issue or to contact me on Discord. But please, do not do this for questions that are rather in the domain of pokecrystal. +To suggest a feature or to report a bug, feel free to open an issue (using the "Feature suggestion" or "Bug report" labels) If you have specific questions about the usage of pokecrystal-board or how to contribute to it, feel free to reach me on Discord in the pret channel. But please, do not do this for questions that are rather in the domain of pokecrystal. If you are interested on developing on top of pokecrystal-board, [docs/usage/index.md](docs/usage/index.md) details the different features. For generic changes made in pokecrystal-board (adaptations, cleaning up, etc.) refer to issues #1, #2, #7, #8. You can also navigate issues tagged with a "Feature" label to see commits pertaining specific features. Additionally, a rough list of new RAM addresses can be found in [docs/develop/ram_addresses.md](docs/develop/ram_addresses.md). \ No newline at end of file diff --git a/docs/usage/img/level_selection_menu.bmp b/docs/usage/img/level_selection_menu.bmp index d649cf99d4eed6a3437e987776ee881a99fae149..7204a5a38ed614c5f81e29999759970f0fb505ae 100755 GIT binary patch delta 1896 zcmb_dO=uHA6wYE8hz5g^#b6*9C<%zA8wH^dV=S~Jh^bOk^3#&qlD455fAC@v>OnyW zf_(=CW3^sttR>Qs9z0kNHMWP+dhj43cvnRHfr22u-H9*T?XCuDm<+R-@8^9pkIih! zK3lRsDR+V5mihCp1xbGUZPjM8eSdTf2QI{i-#NY@!LB^pgwN2Kl=+pDbJ`COPX=K+ z-U|1VshLzjVhBJcxVmMy9(O`@$a6F5fHUcLa!2WhVW?uGSC2s@lYsXKO2t0hXD_a_k`hDR%!Zr>x8J2auNd<>rG8| zb*64%iZd<1n|%OxPqn%9KN^wDxfq5nR;0J`4d+&m@c}mu3h>JkS!|2Q1g;Mz}Dc1&ri;7T4WqGsK`c@D!Ui z2rq_w*o6GX;B5&$XBaq#Wqv3>D5)1`7!W#o7@|oApH9o%KP)hmOhi{PyjloIV+jYe zbUMLbh?(_(Dm9*0@EhZivs*Mfp@61!DZ=>96NVfSsg*PuXS{gfvSrC1GY($3xOuO6 zBrke6pfZ`VIMRu-!V?dz+*LM%V!2yoQ)ME6cF}9u;zTK*7jjd5HKu;+4|D5d^3`u+ zYA74NAuQn4_lxyEkfw8s35&&}-IZgr?>Ke(!GR=Z`z`h6FAslcy! z?UhN%bmyJ+p)iBxMFt~N`)e|0K=LrzTytRDCc%Gf!C delta 1944 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 literal 0 HcmV?d00001 diff --git a/docs/usage/index.md b/docs/usage/index.md index e2d898bc8..e1a68b2dd 100755 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -89,7 +89,7 @@ The effects of each implemented type of board space are defined as scripts in [e Board space effects are triggered from *PlayerEvents.CheckBoardEvent* during *BOARDEVENT_HANDLE_BOARD*. Each space tile uses a specific collision value (*COLL_\*_SPACE*, e.g. *COLL_BLUE_SPACE*), and the appropriate script is queued via *CallScript* for its later execution in *ScriptEvents*. Most space scripts have a check for whether the player has already landed on the space. But others (e.g. branch space, end space) trigger even if the player has not completed the movement. A branch space additionally does not count as an actual space in the movement (so it can't be landed on either). -When a player lands on a space, it turns into a "grey space" or "disabled space" with no effect should the player land on it in a later turn. This grey space is also used by default as the starting space of a level (which matches the spawn point of the map). +When a player lands on a space, it turns into a "grey space" or "disabled space" with no effect should the player land on it in a later turn. This is so that potential paths in a closed loop don't allow the player to "go infinite", though this is more of an aesthetic thing, since technically there is no built-in mechanism that prevents the player from re-entering a level indefinitely to rack up coins, for example. This grey space is also used by default as the starting space of a level (which should be made to match the spawn point of the map). ### Regular spaces @@ -115,7 +115,7 @@ In a branch space, the last two bytes of the *space* macro are repurposed as a p endbranch ``` -Each *branchdir* entry includes: direction, next space id, required techniques. The order of entries is irrelevant, but do not put the same direction more than once in the same branch struct (all but the last entry using that direction will be ignored). The number of arguments occupied by required techniques in each *branchdir* entry is equal to the number of techniques you have defined divided by eight. +Each *branchdir* entry includes: direction, next space id, required techniques. The order of entries is irrelevant, but do not put the same direction more than once in the same branch struct (in that case, all but the last entry using that direction will be ignored). The number of arguments occupied by required techniques in each *branchdir* entry is equal to the number of techniques you have defined divided by eight, and the techniques in each argument must be aligned to their byte as per the technique constants (more on this in [Unlocked techniques](#technique-events)). ### End space @@ -271,6 +271,12 @@ How levels are unlocked along the way is dictated by the *LevelUnlockRequirement Means that: *LEVEL_3* becomes unlocked when stage 2 of *LEVEL_1* and stage 1 of *LEVEL_2* are both cleared; *LEVEL_4* becomes unlocked when you clear your third level stage; *LEVEL_5* becomes unlocked when Flash and Waterfall have both been unlocked. As with [*branchdir*](#branch-and-union-spaces), the number of arguments occupied by techniques after *TECHNIQUES_CLEARED* is equal to the number of techniques you have defined divided by eight (in the above case, there are 9-16 and both Flash and Waterfall are among the first eight). +As mentioned before, when a level stage is cleared, the transition is overworld -> post-level screen -> level selection menu. When the player unlocks one or more levels as a result of this, the entry to the level selection menu displays a small animation that indicates the levels that have been unlocked: + +![Unlock](img/lsm_unlock.bmp) + +On the other hand, an exit from the overworld directly to the level selection menu can occur if the player whites out or leaves the level through the "exit level" board menu option. Exiting the overworld is done through the *exitoverworld* script and its only argument is a constant that denotes the exit overworld reason. Currently, *CLEARED_LEVEL*, *ABANDONED_LEVEL*, and *WHITED_OUT_IN_LEVEL* are defined. + # Other features ## Window HUD @@ -279,6 +285,8 @@ Means that: *LEVEL_3* becomes unlocked when stage 2 of *LEVEL_1* and stage 1 of The amount of scanlines occupied by the HUD is controlled by *hWindowHUDLY*. Writing any non-0 value to this address enables the window-based HUD. Enabling the window-based HUD means that the LCD interrupt is configured in LYC=LY mode in the corresponding scanline; the vblank interrupt enables the window, and the LCD interrupt disables it (through the LCDC register) (note: the window-based HUD can't be enabled simultaneously with pokecrystal-native LCD interrupt usage through *hLCDCPointer*, used during battle transition, battle animations, and movies). +The content of the overworld HUD in pokecrystal-board shows: turn number, die number rolled this turn, coins accumulated in the level, and an unused metric ("experience"). Overworld HUD design is up to your implementation. Several states cause the overworld HUD to be hidden, such as a Pokemon battle, the "view map" mode, the party menu, or the bag menu. + The available functions to use the HUD engine are: - *EnableWindowHUD*: Configure LCD interrupt in LYC=LY mode with corresponding LYC @@ -286,7 +294,7 @@ The available functions to use the HUD engine are: - *LoadWindowHUD*: Load the HUD at *wWhichHUD* to the top of *wTilemap* and *wAttrmap*. Only does anything if *hWindowHUDLY* is non-0 - *LoadHUD*: Load the HUD at *wWhichHUD* to the top of *wTilemap* and *wAttrmap* (without using the Game Boy's window) -The window-based HUD is currently only used in the overworld. Individual HUD types with their own *hWindowHUDLY* (if window-based) and their own content are supported through the *LoadHUD*/*LoadWindowHUD* pointer table. Each entry points to a handler for a specific HUD type (e.g. *HUD_OVERWORLD*, as defined in [constants/gfx_constants.asm](constants/gfx_constants.asm)) meant to implement the loading of the HUD's content to *wTilemap* and *wAttrmap* whilst enabling the HUD. But the overworld normally doesn't use *wTilemap* and *wAttrmap* directly (only when a textbox is open), so the overworld HUD engine requires additional functions beyond the above four generic functions: +The window-based HUD is currently only used in the overworld. Individual HUD types with their own *hWindowHUDLY* (if window-based) and their own content are supported through the *LoadHUD*/*LoadWindowHUD* pointer table. Each entry points to a handler for a specific HUD type (e.g. *HUD_OVERWORLD*, as defined in [constants/gfx_constants.asm](constants/gfx_constants.asm)) meant to implement the loading of the HUD's content to *wTilemap* and *wAttrmap* whilst enabling the HUD. But, in pokecrystal and thus also pokecrystal-board, the overworld normally doesn't use *wTilemap* and *wAttrmap* directly (only when a textbox is open), so the overworld HUD engine requires additional functions beyond the above four generic functions: - *ConstructOverworldHUDTilemap*: Draw the overworld HUD's tilemap into *wOverworldHUDTiles* - *TransferOverworldHUDToBGMap*: Transfer overworld HUD to *vBGMap1*/*vBGMap3* during v/hblank(s). Tilemap is read from *wOverworldHUDTiles*, attrmap is all *PAL_BG_TEXT | PRIORITY*. @@ -296,8 +304,6 @@ The window-based HUD is currently only used in the overworld. Individual HUD typ For example, *ConstructAndEnableOverworldHUD* and *EnableOverworldHUD* are used in map setup scripts as appropriate, and *RefreshOverworldHUD* is called between turns or any other time that the HUD's content needs refreshing. *DisableOverworldHUD* is used for example when leaving the overworld or to transitions to screens that don't display the HUD (battle, party menu, view map mode, etc.). -The content of the overworld HUD in pokecrystal-board shows: turn number, die number rolled this turn, coins accumulated in the level, and an unused metric ("experience"). Overworld HUD design is up to your implementation. - ## Overworld textbox and font The overworld uses a 2bpp font by default to display text, menus, etc. The font type (1bpp or 2bpp) is managed by *wText2bpp*, which is *TRUE* by default in the overworld. Actions like leaving the overworld, opening a submenu like the party menu, starting a battle, etc. turn *wText2bpp* to *FALSE*. This address dictates whether menu-drawing and text-printing functions act as 2bpp or 1bpp when drawing the textbox layout. Notably, note that 1bpp text is not compatible with the overworld HUD enabled, because the latter uses 2bpp font tiles. @@ -366,7 +372,7 @@ For drawing small complementary screen elements in the overworld or the level se In pokecrystal-board, the sprite animation engine supports a mode of not wiping up Shadow OAM (in *DoNextFrameForAllSprites*) in order to work alongside other OAM in the overworld. This is turned on by setting bit *DONT_CLEAR_SHADOW_OAM_IN_SPRITE_ANIMS_F* of *wStateFlags*. -In the overworld, these complementary sprites are referred to as secondary sprites and are managed by the same logic that manages the displaying NPCs: *InitSprites*. In *InitSprites*, *InitSecondarySprites*, checks for requested secondary sprites in the form of *wDisplaySecondarySprites* bits set and processes each requested sprite collection sequentially. It's your responsibility to ensure that you are not exceeding the limit of more than 40 objects at once or more than 10 objects per row considering both NPCs and secondary sprites. +In the overworld, these complementary sprites are referred to as secondary sprites and are managed by the same logic that manages the displaying NPCs: *InitSprites*. In *InitSprites*, *InitSecondarySprites*, checks for requested secondary sprites in the form of *wDisplaySecondarySprites* bits set and processes each requested sprite collection sequentially. It's your responsibility to ensure that you are not exceeding the hardware limit of more than 40 objects at once or more than 10 objects per row considering both NPCs and secondary sprites. ![OAM](img/oam.png) @@ -374,6 +380,8 @@ To update just secondary sprites without processing NPC sprites, you can use *Up The tiles reserved for secondary sprites in the overworld are 0x20 through 0x7e in VRAM bank 0. Tiles from 0x00 through 0x1f are reserved for NPC sprites. You may want to adjust the separation point according to your needs. [charmap.asm](charmap.asm) defines the placement of secondary sprite tiles for different use cases. The start tile of 0x20 is denoted by *SECONDARY_SPRITES_FIRST_TILE*. From there, it's a matter of managing which sprites may or may not overlap to place their tiles in VRAM in the way that most optimizes the available space. +A bunch of these secondary sprites, like the directional arrows in the level selection menu, a branch space, or the "view map" mode, share the palette of the player character. Others use a specific palette. Again, this is your design and graphical aspects are considered placeholder. + ## Player characters Player data has been centralized into the *Players* table from [data/players/players.asm](data/players/players.asm), including overworld sprites and their states, player pictures, and their palettes. Player constants are defined in [constants/player_constants.asm](constants/player_constants.asm). When you define pointers in a *player* entry in the *Players* table, you must ensure the equivalent argument in all entries points to something in the same bank. @@ -388,9 +396,9 @@ Players:: ``` *ChrisSpriteGFX*, *KrisSpriteGFX*, and *RivalSpriteGFX* must point to graphics located in the same bank as each other, and so on. -Note that for many pokecrystal features that are considered placeholder or discontinued in pokecrystal-board (e.g. player selection/naming screen, Pokegear, pack, trainer card, Magnet Train, etc.), pokecrystal-board still uses legacy *PLAYERGENDER_FEMALE_F* flag. The concept of gender is otherwise deprecated. +Note that for many pokecrystal features that are considered placeholder or discontinued in pokecrystal-board (e.g. player selection/naming screen, Pokegear, pack, trainer card, Magnet Train, etc.), pokecrystal-board still uses legacy *PLAYERGENDER_FEMALE_F* flag. The concept of player gender is otherwise deprecated. -![Player](img/player.bmp) +![Player](img/players.bmp) # Gameplay design aspects diff --git a/gfx/level_selection_menu/background.pal b/gfx/level_selection_menu/background.pal index 8d7efd792..2a1ca1794 100755 --- a/gfx/level_selection_menu/background.pal +++ b/gfx/level_selection_menu/background.pal @@ -1,6 +1,6 @@ ; morn - rgbpals_fade_src "lsm_m_morn" + rgbpals_fade_src "lsm_morn" ; border RGB 28, 31, 20 @@ -37,7 +37,7 @@ ; day (original) - rgbpals_fade_src "lsm_m_day" + rgbpals_fade_src "lsm_day" ; border RGB 28, 31, 20 @@ -74,7 +74,7 @@ ; nite - rgbpals_fade_src "lsm_m_nite" + rgbpals_fade_src "lsm_nite" ; border RGB 28, 31, 20 @@ -111,7 +111,7 @@ ; eve - rgbpals_fade_src "lsm_m_eve" + rgbpals_fade_src "lsm_eve" ; border RGB 28, 31, 20 @@ -146,7 +146,7 @@ rgbpals_fade_src_end - rgbpals_fade_apply "lsm_m_morn", "lsm_m_day", 2 - rgbpals_fade_apply "lsm_m_day", "lsm_m_eve", 2 - rgbpals_fade_apply "lsm_m_eve", "lsm_m_nite", 2 - rgbpals_fade_apply "lsm_m_nite", "lsm_m_morn", 2 + rgbpals_fade_apply "lsm_morn", "lsm_day", 2 + rgbpals_fade_apply "lsm_day", "lsm_eve", 2 + rgbpals_fade_apply "lsm_eve", "lsm_nite", 2 + rgbpals_fade_apply "lsm_nite", "lsm_morn", 2