diff --git a/.github/workflows/validate-and-report.yml b/.github/workflows/validate-and-report.yml index 7aee2db4b5..0d3432a241 100644 --- a/.github/workflows/validate-and-report.yml +++ b/.github/workflows/validate-and-report.yml @@ -97,6 +97,46 @@ jobs: if: github.ref == 'refs/heads/master' && github.event_name == 'push' run: .venv/bin/python3 tools/progress.py --version ${{ matrix.version }} + # unlike the matrix build above, this task builds all versions of the + # game from the same build graph + build-all: + # Building and testing cannot work if the repository owner is not Xeeynamo + # due to the missing secrets to clone the CI dependencies + if: github.repository == 'Xeeynamo/sotn-decomp' + runs-on: ubuntu-latest + env: + DISCORD_PROGRESS_WEBHOOK: ${{ secrets.DISCORD_PROGRESS_WEBHOOK }} + steps: + - name: Install requirements + run: sudo apt-get install gcc-mipsel-linux-gnu + - name: Clone main repo (PR) + if: github.event_name == 'pull_request_target' + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + submodules: false + - name: Clone main repo + if: github.event_name != 'pull_request_target' + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + submodules: false + - name: Install tools requirements + run: make update-dependencies + - name: Get PSX dependencies + uses: actions/cache@v4 + id: get-dependencies + with: + path: 'disks/dependencies' + key: sotn-pspeu-deps + - name: Setting up dependencies + working-directory: disks + run: cat dependencies/* | tar -zxf - + - name: Extract dependencies + run: make extract_disk + - name: Build binaries and check if they match + run: make + generate-duplicates-report: strategy: matrix: diff --git a/Makefile b/Makefile index 7dbd32c619..b3b9b48c3a 100644 --- a/Makefile +++ b/Makefile @@ -126,7 +126,7 @@ endif .PHONY: all all: ##@ (Default) build and check -all: build check +all: build_all check .PHONY: extract_assets extract_assets: @@ -157,9 +157,8 @@ build_pspeu: $(SOTNSTR_APP) $(SOTNASSETS) $(ALLEGREX) $(MWCCPSP) $(MWCCGAP_APP) ninja .PHONY: build_all build_all: - $(MAKE) VERSION=us - $(MAKE) VERSION=pspeu - $(MAKE) VERSION=hd + $(PYTHON) tools/builds/gen.py + ninja .PHONY: clean clean_asm clean_asm: @@ -167,9 +166,9 @@ clean_asm: clean: ##@ clean extracted files, assets, and build artifacts clean: clean_asm git clean -fdx assets/ - git clean -fdx build/$(VERSION)/ + git clean -fdx build/ git clean -fdx src/**/gen/ - git clean -fdx config/*$(VERSION)* + git clean -fdx config/ git clean -fdx function_calls/ git clean -fdx sotn_calltree.txt diff --git a/config/assets.pspeu.yaml b/config/assets.pspeu.yaml index a01d6269f2..a3334cd363 100644 --- a/config/assets.pspeu.yaml +++ b/config/assets.pspeu.yaml @@ -333,7 +333,7 @@ files: - [0x2B328, layers, layers] - [0x2B608, palette, pal_stained_glass, 16] # 0x204 - [0x2B628, paldef, palette_def] - - [0x2B6F8, gfx_banks, graphics_banks] + - [0x2B6F8, gfx_banks, psp/graphics_banks] - [0x2B8E0, skip] - [0x2BC20, rooms, rooms] - [0x2BCF8, cutscene, cutscene_script_it] @@ -350,7 +350,7 @@ files: # These gfx palette assignments are unconfirmed. # They match psx, but the .png does not render correctly for psp. - [0x2C588, cmpgfx, gfx_stage_name_jp_sm, 128, 128, 4] - - [0x2C880, cmpgfx, gfx_stage_name_jp_lg, 128, 128, 4] + - [0x2C880, cmpgfx, psp/gfx_stage_name_jp_lg, 128, 128, 4] - [0x2CD10, cmpgfx, gfx_confessional, 128, 128, 4] - [0x2DF38, cmpgfx, gfx_chair, 128, 128, 4, 0x3ADF0] - [0x2EB18, cmpgfx, gfx_corner_guard, 128, 128, 4, 0x3AE50] diff --git a/config/assets.us.yaml b/config/assets.us.yaml index a67e633f99..8d411d0a23 100644 --- a/config/assets.us.yaml +++ b/config/assets.us.yaml @@ -257,14 +257,14 @@ files: - [0x8C, palette, pal_stained_glass, 16] # 0x204 - [0xAC, paldef, palette_def] - [0x178, layers, layers] - - [0x468, gfx_banks, graphics_banks] + - [0x468, gfx_banks, us/graphics_banks] - [0x670, layout, entity_layouts] - [0x818, skip] - [0x2358, rooms, rooms] - [0x2F44, skip] - [0x3A94, cutscene, cutscene_script_psx] - [0x3C18, binary, cutscene_data_psx] # May be gfx data, unsure - - [0x3F20, cmpgfx, gfx_stage_name_jp_lg, 128, 128, 4] + - [0x3F20, cmpgfx, us/gfx_stage_name_jp_lg, 128, 128, 4] - [0x8498, cmpgfx, gfx_confessional, 128, 128, 4] - [0x96BC, cmpgfx, gfx_chair, 128, 128, 4, 0x1692C] - [0xA298, cmpgfx, gfx_corner_guard, 128, 128, 4, 0x16BAC] @@ -874,6 +874,10 @@ files: - start: 0 vram: 0x80180000 assets: + - [0xDC, layers, layers] + - [0x104, gfx_banks, graphics_banks] + - [0x180, layout, entity_layouts] + - [0x328, skip] - [0x1204, rooms, rooms] - [0x1210, skip] - target: disks/us/BOSS/RBO3/RBO3.BIN diff --git a/config/splat.us.bobo6.yaml b/config/splat.us.bobo6.yaml index 2a7e33868e..1290216f73 100644 --- a/config/splat.us.bobo6.yaml +++ b/config/splat.us.bobo6.yaml @@ -38,7 +38,8 @@ segments: align: 4 subalign: 4 subsegments: - - [0x0, data] + - [0x0, .data, header] + - [0x180, .data, gen/e_laydef] - [0x328, .data, e_init] - [0x4D8, data, background_block_init] - [0x510, .data, e_red_door_tiles] @@ -58,7 +59,7 @@ segments: - [0x1210, data, richter] - [0x1284, data, us_39144] - [0x2F70, .data, e_life_up] - - [0x2FC4, data, gen/e_layout] + - [0x2FC4, .data, gen/e_layout] - [0x303C, data] - [0x1F9A0, data, tile_data] - [0x239B0, data] @@ -69,6 +70,7 @@ segments: - [0x264BC, .rodata, e_stage_name] - [0x264F0, .rodata, richter] - [0x26C18, .rodata, us_39144] + - [0x26E20, .rodata, us_3E79C] - [0x27044, .rodata, prim_helpers] - [0x2704C, .rodata, e_life_up] - [0x27068, c, e_room_bg] @@ -90,6 +92,7 @@ segments: - [0x34BD0, c, richter] - [0x39134, c, unused] - [0x39144, c, us_39144] + - [0x3E79C, c, us_3E79C] - [0x4D674, c, prim_helpers] - [0x4DEA4, c, e_life_up] - [0x4E758, bss] # unused diff --git a/config/symbols.pspeu.bobo6.txt b/config/symbols.pspeu.bobo6.txt new file mode 100644 index 0000000000..1412d77921 --- /dev/null +++ b/config/symbols.pspeu.bobo6.txt @@ -0,0 +1,66 @@ +CreateEntityFromLayout = 0x09237700; // source=match +CreateEntityWhenInVerticalRange = 0x09237820; // source=match +CreateEntityWhenInHorizontalRange = 0x092379B8; // source=match +FindFirstEntityToTheRight = 0x09237B50; // source=match +FindFirstEntityToTheLeft = 0x09237BC0; // source=match +CreateEntitiesToTheRight = 0x09237C48; // source=match +CreateEntitiesToTheLeft = 0x09237D88; // source=match +FindFirstEntityAbove = 0x09237EE0; // source=match +FindFirstEntityBelow = 0x09237F58; // source=match +CreateEntitiesAbove = 0x09237FE0; // source=match +CreateEntitiesBelow = 0x09238128; // source=match +InitRoomEntities = 0x09238288; // source=match +UpdateRoomPosition = 0x092384B8; // source=match +CreateEntityFromCurrentEntity = 0x09238598; // source=match +CreateEntityFromEntity = 0x09238620; // source=match +EntityStageNamePopup = 0x092386A8; // source=match +EntityIsNearPlayer = 0x09239528; // source=match +BO6_EntityRedDoor = 0x09239610; // source=match +Random = 0x0923A830; // source=match +Update = 0x0923A870; // source=match +UpdateStageEntities = 0x0923AC50; // source=match +func_psp_0923AD68 = 0x0923AD68; // source=match +func_psp_0923B2F0 = 0x0923B2F0; // source=match +EntitySoulStealOrb = 0x0923B780; // source=match +EntityEnemyBlood = 0x0923BCE0; // source=match +EntityUnkId13 = 0x09240540; // source=match +EntityExplosionVariants = 0x09240688; // source=match +EntityGreyPuff = 0x09240800; // source=match +EntityIntenseExplosion = 0x09240980; // source=match +HitDetection = 0x09240AF0; // source=match +EntityDamageDisplay = 0x092422C0; // source=match +BottomCornerText = 0x09242CC8; // source=match +EntityRoomForeground = 0x092432C0; // source=match +DestroyEntity = 0x092433D8; // source=match +PreventEntityFromRespawning = 0x09243470; // source=match +AnimateEntity = 0x092434F8; // source=match +GetSideToPlayer = 0x09243668; // source=match +MoveEntity = 0x092436F0; // source=match +FallEntity = 0x09243740; // source=match +AllocEntity = 0x09243778; // source=match +UnkEntityFunc0 = 0x092437E8; // source=match +GetAngleBetweenEntities = 0x09243890; // source=match +GetNormalizedAngle = 0x09243910; // source=match +SetStep = 0x09243A08; // source=match +InitializeEntity = 0x09243A58; // source=match +EntityDummy = 0x09243C00; // source=match +CheckFieldCollision = 0x09243C38; // source=match +ReplaceBreakableWithItemDrop = 0x09243DC8; // source=match +UnkPrimHelper = 0x09243EC0; // source=match +PrimResetNext = 0x092443C0; // source=match +UnkPolyFunc2 = 0x092444C8; // source=match +PrimDecreaseBrightness = 0x09244530; // source=match +EntityBreakable = 0x09244A00; // source=match +BO6_EntityBackgroundBlock = 0x092479C0; // source=match +BO6_EntityLockCamera = 0x092488C8; // source=match +func_8015C4AC = 0x0925ABB8; // source=match +func_8015C6D4 = 0x0925AE20; // source=match +RicSetStep = 0x0925B248; // source=match +RicSetAnimation = 0x0925B270; // source=match +RicDecelerateX = 0x0925B2B0; // source=match +RicCheckFacing = 0x0925B350; // source=match +RicSetSpeedX = 0x0925B448; // source=match +func_8015CAAC = 0x0925B498; // source=match +RicSetInvincibilityFrames = 0x0925B4E0; // source=match +DisableAfterImage = 0x0925B588; // source=match +func_8015CC28 = 0x0925B648; // source=match diff --git a/config/symbols.us.bobo6.txt b/config/symbols.us.bobo6.txt index 04b4f30651..5608ba9e05 100644 --- a/config/symbols.us.bobo6.txt +++ b/config/symbols.us.bobo6.txt @@ -101,8 +101,6 @@ EntityEnemyBlood = 0x801B4304; EntityRoomForeground = 0x801B47B4; BO6_CheckBladeDashInput = 0x801B4FA4; BO6_CheckHighJumpInput = 0x801B502C; -BO6_RicMain = 0x801B50BC; -RichterThinking = 0x801B5A2C; EntityRichter = 0x801B6AC4; BO6_RicStepStand = 0x801B6BD8; BO6_RicStepWalk = 0x801B6E3C; @@ -115,7 +113,6 @@ BO6_RicStepHit = 0x801B7818; BO6_RicStepDead = 0x801B7E80; BO6_RicStepStandInAir = 0x801B8540; BO6_RicStepEnableFlameWhip = 0x801B85DC; -BO6_RicStepHydrostorm = 0x801B8684; BO6_RicStepGenericSubwpnCrash = 0x801B86B8; BO6_RicStepThrowDaggers = 0x801B86EC; BO6_RicStepSlide = 0x801B87B4; @@ -125,7 +122,6 @@ BO6_RicStepHighJump = 0x801B8F78; BO6_RicSetStep = 0x801B9928; BO6_RicSetAnimation = 0x801B9940; DecelerateX = 0x801B995C; -DecelerateY = 0x801B99A4; BO6_RicCheckFacing = 0x801B99EC; BO6_RicSetSpeedX = 0x801B9AA4; BO6_RicSetInvincibilityFrames = 0x801B9AF4; @@ -133,7 +129,6 @@ BO6_DisableAfterImage = 0x801B9B78; BO6_RicSetCrouch = 0x801B9C5C; BO6_RicSetStand = 0x801B9D2C; BO6_RicSetFall = 0x801B9F38; -BO6_RicCheckSubwpnChainLimit = 0x801BA0E8; BO6_RicDoSubweapon = 0x801BA168; BO6_RicDoAttack = 0x801BA2A0; BO6_RicDoCrash = 0x801BA598; @@ -199,12 +194,21 @@ g_Ric_padHeld = 0x801D15E8; g_Ric_padSim = 0x801D15EC; g_Ric_demo_timer = 0x801D15F4; g_Ric_timers_0 = 0x801D15F8; +g_Ric_timers_1 = 0x801D15FA; +g_Ric_timers_2 = 0x801D15FC; +g_Ric_timers_3 = 0x801D15FE; g_Ric_timers_4 = 0x801D1600; +g_Ric_timers_5 = 0x801D1602; +g_Ric_timers_6 = 0x801D1604; +g_Ric_timers_7 = 0x801D1606; +g_Ric_timers_8 = 0x801D1608; g_Ric_timers_9 = 0x801D160A; g_Ric_timers_10 = 0x801D160C; +g_Ric_timers_11 = 0x801D160E; g_Ric_timers_12 = 0x801D1610; g_Ric_timers_13 = 0x801D1612; g_Ric_timers_14 = 0x801D1614; +g_Ric_timers_15 = 0x801D1616; g_Ric_vram_flag = 0x801D1618; g_Ric_unk04 = 0x801D161C; g_Ric_unk08 = 0x801D1620; @@ -220,3 +224,20 @@ g_Ric_unk6C = 0x801D1684; g_Ric_unk70 = 0x801D1688; g_IsCutsceneDone = 0x801D1698; g_ItemIconSlots = 0x801D16A8; + +BO6_RicEntityCrashAxe = 0x801C560C; +anim_cross_boomerang = 0x80182970; +BO6_RicEntitySubwpnCross = 0x801C4200; +BO6_spriteBanks = 0x80180034; +BO6_cluts = 0x801800D8; +BO6_gfxBanks = 0x80180130; +richter_sprites = 0x8018303C; +BO6_rooms_layers = 0x801800FC; +BO6_Overlay = 0x80180000; +bo6_tilemap_0 = 0x8019F7A0; +bo6_tiledef_0 = 0x801A39A0; +D_8019DDA8 = 0x8019DDA8; +D_8019A4F4 = 0x8019A4F4; +D_8019ACC4 = 0x8019ACC4; +BO6_RicEntitySubwpnHolyWater = 0x801C32C4; +BO6_RicCheckHolyWaterCollision = 0x801C310C; diff --git a/config/symbols.us.txt b/config/symbols.us.txt index fb51ce252f..340160d9ea 100644 --- a/config/symbols.us.txt +++ b/config/symbols.us.txt @@ -846,7 +846,6 @@ g_Servant = 0x8006CBC4; _svm_tn = 0x8006CBC8; g_Clut = 0x8006CBCC; // size:0x6000 g_Player = 0x80072BD0; // size:0x3CE -g_Player_demo_timer = 0x80072EFC; g_GfxLoad = 0x80072FA0; g_GameStep = 0x80073060; g_ServantLoaded = 0x80073064; @@ -899,15 +898,15 @@ PLAYER_animCurFrame = 0x8007342E; PLAYER_unk5A = 0x80073432; PLAYER_unkA4 = 0x8007347C; PLAYER_ext_player_anim = 0x80073484; -g_Entities_1 = 0x80073494; -g_Entities_19 = 0x800741CC; -g_Entities_32 = 0x80074B58; -g_Entities_64 = 0x800762D8; -g_Entities_96 = 0x80077A58; -g_Entities_128 = 0x800791D8; -g_Entities_160 = 0x8007A958; -g_Entities_192 = 0x8007C0D8; -g_Entities_224 = 0x8007D858; +g_Entities_1 = 0x80073494; // size:0xBC +g_Entities_19 = 0x800741CC; // size:0xBC +g_Entities_32 = 0x80074B58; // size:0xBC +g_Entities_64 = 0x800762D8; // size:0xBC +g_Entities_96 = 0x80077A58; // size:0xBC +g_Entities_128 = 0x800791D8; // size:0xBC +g_Entities_160 = 0x8007A958; // size:0xBC +g_Entities_192 = 0x8007C0D8; // size:0xBC +g_Entities_224 = 0x8007D858; // size:0xBC g_EvHwCardEnd = 0x8007EFD8; g_EvHwCardErr = 0x8007EFDC; g_EvHwCardTmo = 0x8007EFE0; diff --git a/include/common.h b/include/common.h index 511fc99f7e..c020f2d600 100644 --- a/include/common.h +++ b/include/common.h @@ -52,7 +52,6 @@ #define UNUSED #endif #define ASSERT(x) assert(x) - #elif defined(VERSION_PSP) #define ASSERT(x) #define STATIC_ASSERT(x, y) diff --git a/include/entity.h b/include/entity.h index a905d27fea..f7a5b89fa3 100644 --- a/include/entity.h +++ b/include/entity.h @@ -3556,6 +3556,16 @@ typedef struct { /* 0x84 */ s16 castTimer; } ET_HellfireBeastFlamePillar; +typedef struct { + /* 0x7C */ s16 timer; + /* 0x7E */ s16 velocityAngle; + /* 0x80 */ s32 : 32; + /* 0x84 */ s32 : 32; + /* 0x88 */ s32 : 32; + /* 0x8C */ struct Entity* parent; + /* 0x90 */ s16 unkTimer; +} ET_ShaftOrb; + typedef union { // offset=0x7C struct Primitive* prim; ET_Placeholder ILLEGAL; @@ -3863,6 +3873,7 @@ typedef union { // offset=0x7C ET_HellfireBeast hellfireBeast; ET_HellfireBeastThorsHammer hellfireBeastThorsHammer; ET_HellfireBeastFlamePillar hellfireBeastFlamePillar; + ET_ShaftOrb shaftOrb; } Ext; #define SYNC_FIELD(struct1, struct2, field) \ diff --git a/include/game.h b/include/game.h index fcea49c83b..731f13d713 100644 --- a/include/game.h +++ b/include/game.h @@ -1926,7 +1926,7 @@ typedef struct { /* 80072BD0 */ Collider colFloor[NUM_HORIZONTAL_SENSORS]; /* 80072C60 */ Collider colCeiling[NUM_HORIZONTAL_SENSORS]; /* 80072CF0 */ Collider colWall[NUM_VERTICAL_SENSORS * 2]; - /* 80072EE8 */ u32 padPressed; + /* 0x318 80072EE8 */ u32 padPressed; /* 80072EEC */ u32 padTapped; /* 80072EF0 */ u32 padHeld; /* 80072EF4 */ u32 padSim; // simulate input to force player actions diff --git a/include/input.h b/include/input.h new file mode 100644 index 0000000000..5c6f98e579 --- /dev/null +++ b/include/input.h @@ -0,0 +1,23 @@ +#ifndef INPUT_H +#define INPUT_H + +enum InputChecks { + CHECK_GROUND = 1, + CHECK_FALL = 4, + CHECK_FACING = 8, + CHECK_JUMP = 0x10, + CHECK_DOUBLEJUMP = 0x20, + CHECK_CRASH = 0x40, + CHECK_80 = 0x80, + CHECK_GRAVITY_HIT = 0x200, + CHECK_400 = 0x400, + CHECK_800 = 0x800, + CHECK_ATTACK = 0x1000, + CHECK_CROUCH = 0x2000, + CHECK_GRAVITY_FALL = 0x8000, + CHECK_GRAVITY_JUMP = 0x10000, + CHECK_GROUND_AFTER_HIT = 0x20000, + CHECK_SLIDE = 0x40000, +}; + +#endif // INPUT_H diff --git a/src/boss/bo4/collision.c b/src/boss/bo4/collision.c index c27e1b6268..e1c8c8d874 100644 --- a/src/boss/bo4/collision.c +++ b/src/boss/bo4/collision.c @@ -1,4 +1,2 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -#include "bo4.h" - #include "../../st/collision.h" diff --git a/src/boss/bo4/e_init.c b/src/boss/bo4/e_init.c index 3ed33cab3a..7d9a201fb1 100644 --- a/src/boss/bo4/e_init.c +++ b/src/boss/bo4/e_init.c @@ -16,7 +16,6 @@ void EntityHeartDrop(Entity*); void EntityEnemyBlood(Entity*); void EntityMessageBox(Entity*); void EntityDummy(Entity*); -void EntityDummy(Entity*); void OVL_EXPORT(EntityBackgroundBlock)(Entity*); void OVL_EXPORT(EntityLockCamera)(Entity*); void EntityUnkId13(Entity*); diff --git a/src/boss/bo4/e_red_door.c b/src/boss/bo4/e_red_door.c index fce258d085..1cec5faaba 100644 --- a/src/boss/bo4/e_red_door.c +++ b/src/boss/bo4/e_red_door.c @@ -1,4 +1,3 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #include "bo4.h" - #include "../../st/e_red_door.h" diff --git a/src/boss/bo4/st_update.c b/src/boss/bo4/st_update.c index 6f3cc3e5a0..d7b647ada4 100644 --- a/src/boss/bo4/st_update.c +++ b/src/boss/bo4/st_update.c @@ -1,4 +1,2 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -#include "bo4.h" - #include "../../st/st_update.h" diff --git a/src/boss/bo6/bo6.h b/src/boss/bo6/bo6.h index 8a8815d3dd..dedd9c8000 100644 --- a/src/boss/bo6/bo6.h +++ b/src/boss/bo6/bo6.h @@ -25,6 +25,11 @@ typedef enum EntityIDs { /* 0x13 */ E_ID_13, /* 0x14 */ E_EXPLOSION_VARIANTS = 0x14, /* 0x15 */ E_GREY_PUFF, + /* 0x17 */ E_ID_17 = 0x17, + + /* 0x21 */ E_ID_21 = 0x21, + /* 0x23 */ E_ID_23 = 0x23, + /* 0x24 */ E_ID_24 = 0x24, /* 0x40 */ E_RICHTER = STAGE_ENTITY_START, /* 0x41 */ E_ID_41, @@ -69,10 +74,11 @@ typedef enum { /* 0x1F */ PL_S_30, // unused /* 0x20 */ PL_S_31, // unused /* 0x21 */ PL_S_INIT, - /* 0x40 */ PL_S_64 = 0x40, - /* 0x50 */ PL_S_80 = 0x50, - /* 0x60 */ PL_S_96 = 0x60, - /* 0x70 */ PL_S_112 = 0x70, + // various endings + /* 0x40 */ PL_S_ENDING_1 = 0x40, + /* 0x50 */ PL_S_ENDING2 = 0x50, + /* 0x60 */ PL_S_ENDING_3 = 0x60, + /* 0x70 */ PL_S_ENDING_4 = 0x70, /* 0xF0 */ PL_S_DEBUG = 0xF0, } OVL_EXPORT(RicSteps); @@ -96,6 +102,129 @@ enum RicTimers { PL_T_AFTERIMAGE_DISABLE, }; +// TODO: share with richter +enum RicBlueprints { + BP_SKID_SMOKE, + BP_SMOKE_PUFF, + BP_SUBWPN_CROSS, + BP_SUBWPN_CROSS_PARTICLES, + BP_EMBERS, + BP_5, + BP_SUBWPN_HOLYWATER, + BP_HOLYWATER_FIRE, + BP_HIT_BY_FIRE, + BP_HOLYWATER_FLAMES, + BP_WHIP, + BP_MULTIPLE_EMBERS, + BP_HYDROSTORM, + BP_CRASH_CROSS, + BP_CRASH_CROSSES_ONLY, + BP_NOT_IMPLEMENTED_1, + // 0x10 + BP_NOT_IMPLEMENTED_2, + BP_ARM_BRANDISH_WHIP, + BP_18, + BP_AXE, + BP_20, + BP_NOT_IMPLEMENTED_3, + BP_REVIVAL_COLUMN, + BP_MARIA_POWERS_APPLIED, + BP_SLIDE, + BP_25, + BP_BLADE_DASH, + BP_BLUE_CIRCLE, + BP_BLUE_SPHERE, + BP_MARIA, + BP_MARIA_POWERS_INVOKED, + BP_31, + // 0x20 + BP_NOT_IMPLEMENTED_4, + BP_RIC_BLINK, + BP_CRASH_CROSS_PARTICLES, + BP_35, + BP_36, + BP_37, + BP_38, + BP_39, + BP_HOLYWATER_GLASS, + BP_CRASH_AXE, + BP_42, + BP_SUBWPN_DAGGER, + BP_CRASH_DAGGER, + BP_HIGH_JUMP, + BP_HIT_BY_CUT, + BP_HIT_BY_ICE, + // 0x30 + BP_HIT_BY_THUNDER, + BP_VIBHUTI, + BP_REBOUND_STONE, + BP_AGUNEA, + BP_AGUNEA_HIT_ENEMY, + BP_DEATH_BY_FIRE, + BP_CRASH_VITHUBI, + BP_VITHUBI_CRASH_CLOUD, + BP_CRASH_REBOUND_STONE, + BP_57, + BP_CRASH_REBOUND_STONE_EXPLOSION, + BP_CRASH_BIBLE, + BP_CRASH_BIBLE_BEAM, + BP_BIBLE, + BP_BIBLE_TRAIL, + BP_SUBWPN_STOPWATCH, + // 0x40 + BP_STOPWATCH_CIRCLE, + BP_CRASH_STOPWATCH, + BP_66, + BP_CRASH_AGUNEA, + BP_CRASH_AGUNEA_THUNDER, + BP_CRASH_REBOUND_STONE_PARTICLES, + BP_HIT_BY_DARK, + BP_HIT_BY_HOLY, + BP_AGUNEA_THUNDER, + BP_CRASH_STOPWATCH_LIGHTNING, + BP_SMOKE_PUFF_2, + BP_SKID_SMOKE_2, + BP_SKID_SMOKE_3, + BP_TELEPORT, + NUM_BLUEPRINTS, +}; + +// TODO: share with richter +enum RicSubweapons { + PL_W_NONE, + PL_W_DAGGER, + PL_W_AXE, + PL_W_HOLYWATER, + PL_W_CROSS, + PL_W_BIBLE, + PL_W_STOPWATCH, + PL_W_REBNDSTONE, + PL_W_VIBHUTI, + PL_W_AGUNEA, + PL_W_10, + PL_W_HOLYWATER_FLAMES, + PL_W_CRASH_CROSS, + PL_W_CRASH_CROSS_BEAM, + PL_W_WHIP, + PL_W_15, + PL_W_HYDROSTORM, + PL_W_BIBLE_BEAM, + PL_W_KICK, + PL_W_19, + PL_W_20, + PL_W_21, + PL_W_HIGHJUMP, + PL_W_23, + PL_W_CRASH_VIBHUTI, + PL_W_CRASH_REBOUND_STONE, + PL_W_CRASH_AGUNEA, + PL_W_27, + PL_W_28, + PL_W_CRASH_REBOUND_EXPLOSION, + PL_W_30, + NUM_WEAPONS, +}; + #define RIC g_Entities[STAGE_ENTITY_START] extern PlayerState g_Ric; diff --git a/src/boss/bo6/header.c b/src/boss/bo6/header.c new file mode 100644 index 0000000000..e45353cc74 --- /dev/null +++ b/src/boss/bo6/header.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +#include "bo6.h" + +extern RoomHeader OVL_EXPORT(rooms)[]; +extern s16** OVL_EXPORT(spriteBanks)[]; +extern u_long* OVL_EXPORT(cluts)[]; +extern LayoutEntity* OVL_EXPORT(pStObjLayoutHorizontal)[]; +extern RoomDef OVL_EXPORT(rooms_layers)[]; +extern u_long** OVL_EXPORT(gfxBanks)[]; +extern u8 richter_sprites[]; + +AbbreviatedOverlay2 OVL_EXPORT(Overlay) = { + .Update = Update, + .HitDetection = HitDetection, + .UpdateRoomPosition = UpdateRoomPosition, + .InitRoomEntities = InitRoomEntities, + .rooms = OVL_EXPORT(rooms), + .spriteBanks = OVL_EXPORT(spriteBanks), + .cluts = OVL_EXPORT(cluts), + .objLayoutHorizontal = OVL_EXPORT(pStObjLayoutHorizontal), + .tileLayers = OVL_EXPORT(rooms_layers), + .gfxBanks = OVL_EXPORT(gfxBanks), + .UpdateStageEntities = UpdateStageEntities, + .unk2C = richter_sprites, + .unk30 = richter_sprites, +}; + +extern s16* D_us_801A39B0[]; +extern s16* D_us_801A4628[]; +extern s16* D_us_801A43A4[]; +extern s16* D_us_801A4AAC[]; + +s16** OVL_EXPORT(spriteBanks)[] = { +NULL, +D_us_801A39B0, +NULL, +D_us_801A4628, +D_us_801A43A4, +D_us_801A4AAC, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +}; + + +extern s16* D_us_8019E9A0[0x300]; +extern s16* D_us_8019EFA0[0x100]; +extern s16* D_us_8019F3A0[0x80]; +extern s16* D_us_8019F1A0[0x80]; +extern s16* D_us_8019F5A0[0x80]; + +static u_long* D_us_80180094[] = { + MAKE_PAL_OP(PAL_BULK_COPY, 0), + PAL_BULK(0x2200, D_us_8019E9A0), + PAL_BULK(0x2500, D_us_8019EFA0), + PAL_BULK(0x2600, D_us_8019F3A0), + PAL_BULK(0x2680, D_us_8019F1A0), + PAL_BULK(0x2700, D_us_8019F5A0), + PAL_TERMINATE(), +}; + +u_long* OVL_EXPORT(cluts)[] = { + D_us_80180094, +}; + +#include "gen/layers.h" +#include "gen/graphics_banks.h" diff --git a/src/boss/bo6/richter.c b/src/boss/bo6/richter.c index 17bb49830d..2318cabe0a 100644 --- a/src/boss/bo6/richter.c +++ b/src/boss/bo6/richter.c @@ -1,23 +1,1085 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #include "bo6.h" -INCLUDE_ASM("boss/bo6/nonmatchings/richter", func_us_801B4BD0); +extern AnimationFrame D_us_80182008[]; +extern s32 D_us_801D11C8[]; +extern s32 D_us_801D1248[]; -INCLUDE_ASM("boss/bo6/nonmatchings/richter", func_us_801B4EAC); +void func_us_801B4BD0(void) { + s32 i; + Entity* var_s1; + Entity* e; + PlayerState* var_a0; + Primitive* prim; + s32* memset_ptr; + s32 memset_len; + s16 temp_v0; + s16 primIndex; + s32 radius; + s32 intensity; + s32 temp_v1; + s32 var_s2; + s32 var_s2_2; + s32 var_s2_3; + s32* var_s0; + s32* var_s3; + + RIC.animSet = ANIMSET_OVL(1); + RIC.zPriority = g_unkGraphicsStruct.g_zEntityCenter + 8; + RIC.flags = FLAG_UNK_10000000 | FLAG_POS_CAMERA_LOCKED | FLAG_UNK_400000 | + FLAG_UNK_2000; + RIC.facingLeft = 0; + RIC.unk5A = 8; + RIC.palette = 0x8220; + RIC.scaleX = RIC.scaleY = 0x100; + RIC.rotPivotY = 0x18; + RIC.drawMode = DRAW_DEFAULT; + + g_PlayerDraw[8].r0 = g_PlayerDraw[8].r1 = g_PlayerDraw[8].r2 = + g_PlayerDraw[8].r3 = g_PlayerDraw[8].g0 = g_PlayerDraw[8].g1 = + g_PlayerDraw[8].g2 = g_PlayerDraw[8].g3 = g_PlayerDraw[8].b0 = + g_PlayerDraw[8].b1 = g_PlayerDraw[8].b2 = g_PlayerDraw[8].b3 = + 0x80; + + g_PlayerDraw[8].enableColorBlend = 0; + + memset_len = sizeof(PlayerState) / sizeof(s32); + memset_ptr = (s32*)&g_Ric; + for (i = 0; i < memset_len; i++) { + *memset_ptr++ = 0; + } + + g_Ric.vram_flag = g_Ric.unk04 = 1; + + BO6_RicSetStand(0); + RIC.anim = D_us_80182008; + + for (i = 0; i < 32; i++) { + radius = (rand() & 0x3FF) + FLT(1.0 / 16.0); + intensity = (rand() & 0xFF) + FLT(1.0 / 16.0); + D_us_801D11C8[i] = ((rcos(radius) << 4) * intensity) >> 8; + D_us_801D1248[i] = -(((rsin(radius) << 4) * intensity) >> 7); + } + + for (e = &g_Entities[STAGE_ENTITY_START + 1], i = 0; i < 3; i++, e++) { + DestroyEntity(e); + e->animSet = ANIMSET_OVL(1); + e->unk5A = i + 9; + e->palette = PAL_OVL(0x220); + e->flags = FLAG_POS_CAMERA_LOCKED; + } + + primIndex = g_api.AllocPrimitives(PRIM_TILE, 6); + prim = &g_PrimBuf[primIndex]; + g_Entities[65].primIndex = primIndex; + g_Entities[65].flags |= FLAG_HAS_PRIMS; + while (prim != NULL) { + prim->drawMode = DRAW_UNK_100 | DRAW_HIDE | DRAW_UNK02; + prim = prim->next; + } + g_api.TimeAttackController( + TIMEATTACK_EVENT_SAVE_RICHTER, TIMEATTACK_SET_VISITED); +} + +void func_us_801B4EAC(void) { + g_Ric.unk04 = g_Ric.vram_flag; + g_Ric.vram_flag = 0; + RIC.posY.val += RIC.velocityY; + RIC.posX.val += RIC.velocityX; + + if (RIC.posY.val >= 0xB30000) { + RIC.posY.val = 0xB30000; + g_Ric.vram_flag |= 1; + } + if (RIC.posY.val <= 0x280000) { + RIC.posY.val = 0x280000; + g_Ric.vram_flag |= 2; + } + if (RIC.posX.val >= 0xF80000) { + RIC.posX.val = 0xF80000; + g_Ric.vram_flag |= 4; + } + if (RIC.posX.val <= 0x80000) { + RIC.posX.val = 0x80000; + g_Ric.vram_flag |= 8; + } +} INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_CheckBladeDashInput); INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_CheckHighJumpInput); -INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicMain); +extern s32 D_us_80181210[]; +extern u16 D_us_80181250[]; +extern u16 D_us_80181270[]; +extern AnimationFrame* D_us_80181F14[]; +extern AnimationFrame D_us_801820BC[]; +extern AnimationFrame D_us_801821E0[]; +extern AnimationFrame D_us_8018224C[]; +extern FrameProperty D_us_801824A4[]; +extern s32 D_us_801D169C; +extern u32 D_us_801D16A4; + +void OVL_EXPORT(RicMain)(void) { + s16 angle; + s32 timer; + s32 i; + s32 status; + s16 step; + s16 step_s; + PlayerDraw* playerDraw; + DamageParam damage; + s32 posX, posY; + + // status = saved_reg_s3; + // step = saved_reg_s5; + // step_s = saved_reg_s6; +#ifdef VERSION_PSP + status = 0; +#endif + g_CurrentEntity = &RIC; + playerDraw = &g_PlayerDraw[8]; + g_Ric.unk4C = 0; + for (i = 0; i < LEN(g_Ric.timers); i++) { + if (g_Ric.timers[i]) { + switch (i) { /* switch 1 */ + case PL_T_POISON: /* switch 1 */ + g_Ric.timers[PL_T_POISON] = 0x800; + break; + case PL_T_CURSE: + break; + case PL_T_2: /* switch 1 */ + RIC.palette = g_Ric.unk40; + break; + case PL_T_3: + break; + case PL_T_4: /* switch 1 */ + angle = (g_GameTimer & 0xF) * 256; + playerDraw->r0 = playerDraw->g0 = playerDraw->b0 = + (rsin(angle) + FLT(1)) / 64 + 0x60; + + angle += FLT(1.0 / 8.0); + playerDraw->r1 = playerDraw->g1 = playerDraw->b1 = + (rsin(angle) + FLT(1)) / 64 + 0x60; + + angle += FLT(1.0 / 8.0); + playerDraw->r3 = playerDraw->g3 = playerDraw->b3 = + (rsin(angle) + FLT(1)) / 64 + 0x60; + + angle += FLT(1.0 / 8.0); + playerDraw->r2 = playerDraw->g2 = playerDraw->b2 = + (rsin(angle) + FLT(1)) / 64 + 0x60; + + playerDraw->enableColorBlend = true; + break; + case PL_T_5: + case PL_T_6: + case PL_T_7: + case PL_T_8: + case PL_T_ATTACK: + case PL_T_10: + case PL_T_RUN: + case PL_T_12: + break; + case PL_T_INVINCIBLE_SCENE: /* switch 1 */ + g_Ric.timers[PL_T_INVINCIBLE_SCENE] = 4; + break; + case PL_T_INVINCIBLE: + break; + case PL_T_AFTERIMAGE_DISABLE: /* switch 1 */ + OVL_EXPORT(DisableAfterImage)(0, 0); + break; + } + + if (--g_Ric.timers[i] == 0) { + switch (i) { /* switch 2 */ + case PL_T_POISON: + case PL_T_CURSE: + break; + case PL_T_2: /* switch 2 */ + RIC.palette = PAL_OVL(0x220); + break; + case PL_T_3: + break; + case PL_T_4: /* switch 2 */ + playerDraw->enableColorBlend = false; + break; + case PL_T_INVINCIBLE_SCENE: /* switch 2 */ + OVL_EXPORT(RicSetInvincibilityFrames)(1, 0x10); + break; + case PL_T_6: /* switch 2 */ + if ((RIC.step == 4) && (RIC.anim != D_us_801820BC)) { + BO6_RicSetAnimation(D_us_801820BC); + g_Ric.unk44 &= ~0x10; + } + break; + case PL_T_INVINCIBLE: + break; + case PL_T_AFTERIMAGE_DISABLE: /* switch 2 */ + func_us_801B9C14(); + break; + case PL_T_5: + case PL_T_7: + case PL_T_8: + case PL_T_ATTACK: + case PL_T_10: + case PL_T_RUN: + case PL_T_12: + default: + break; + } + } + } + } + + g_Ric.padHeld = g_Ric.padPressed; + if (g_Ric.demo_timer) { + g_Ric.demo_timer--; + g_Ric.padPressed = g_Ric.padSim; + } else { + g_Ric.padPressed = g_pads[1].pressed; + } + g_Ric.padTapped = (g_Ric.padHeld ^ g_Ric.padPressed) & g_Ric.padPressed; + + if ((RIC.step != PL_S_ENDING_4) && (RIC.step != PL_S_DEAD)) { + if (RIC.step != PL_S_DEAD && RIC.flags & FLAG_DEAD) { + D_us_801D16A4 |= 2; + step = RIC.step; + step_s = RIC.step_s; + damage.effects = + D_us_80181210[g_CurrentEntity->nFramesInvincibility]; + OVL_EXPORT(RicSetStep)(0x11); +#ifdef VERSION_PSP + g_api.TimeAttackController( + TIMEATTACK_EVENT_SAVE_RICHTER, TIMEATTACK_SET_RECORD); +#endif + } else if (D_us_801D169C != 0) { + OVL_EXPORT(RicSetStep)(0x70); + } else { + if ((g_Ric.timers[PL_T_INVINCIBLE_SCENE] | + g_Ric.timers[PL_T_INVINCIBLE]) == 0) { + if ((g_CurrentEntity->hitFlags > 0) && + (g_CurrentEntity->hitFlags < 4)) { + step = RIC.step; + step_s = RIC.step_s; + damage.effects = + D_us_80181210[g_CurrentEntity->nFramesInvincibility]; + if ((g_Ric.unk6C - g_Ric.unk6A) >= 10) { + damage.damageKind = 3; + } else { + damage.damageKind = 2; + } + OVL_EXPORT(RicSetStep)(0xB); + } + } + goto check_input_combo; + } + } else { + check_input_combo: + BO6_CheckBladeDashInput(); + BO6_CheckHighJumpInput(); + } + g_Ric.prev_step = RIC.step; + g_Ric.prev_step_s = RIC.step_s; + + switch (RIC.step) { /* switch 3 */ + case PL_S_STAND: /* switch 3 */ + BO6_RicStepStand(); + break; + case PL_S_WALK: /* switch 3 */ + BO6_RicStepWalk(); + break; + case PL_S_CROUCH: /* switch 3 */ + BO6_RicStepCrouch(); + break; + case PL_S_FALL: /* switch 3 */ + BO6_RicStepFall(); + break; + case PL_S_JUMP: /* switch 3 */ + BO6_RicStepJump(); + break; + case PL_S_HIGHJUMP: /* switch 3 */ + BO6_RicStepHighJump(); + break; + case PL_S_HIT: /* switch 3 */ + BO6_RicStepHit(damage.effects, damage.damageKind, step, step_s); + break; + case PL_S_DEAD: /* switch 3 */ + BO6_RicStepDead(damage.effects, damage.damageKind, step, step_s); + break; + case PL_S_STAND_IN_AIR: /* switch 3 */ + BO6_RicStepStandInAir(); + break; + case PL_S_FLAME_WHIP: /* switch 3 */ + BO6_RicStepEnableFlameWhip(); + break; + case PL_S_HYDROSTORM: /* switch 3 */ + BO6_RicStepHydrostorm(); + break; + case PL_S_THROW_DAGGERS: /* switch 3 */ + BO6_RicStepThrowDaggers(); + break; + case PL_S_SUBWPN_CRASH: /* switch 3 */ + BO6_RicStepGenericSubwpnCrash(); + break; + case PL_S_SLIDE: /* switch 3 */ + BO6_RicStepSlide(); + break; + case PL_S_RUN: /* switch 3 */ + BO6_RicStepRun(); + break; + case PL_S_SLIDE_KICK: /* switch 3 */ + BO6_RicStepSlideKick(); + break; + case PL_S_BLADEDASH: /* switch 3 */ + BO6_RicStepBladeDash(); + break; + case PL_S_ENDING_1: /* switch 3 */ + func_us_801B913C(); + break; + case PL_S_ENDING2: /* switch 3 */ + func_us_801B9144(); + break; + case PL_S_ENDING_3: /* switch 3 */ + func_us_801B9338(); + break; + case PL_S_ENDING_4: /* switch 3 */ + func_us_801B9340(); + break; + } + + g_Ric.unk08 = g_Ric.status; + + switch (RIC.step) { /* switch 4 */ + case PL_S_STAND: /* switch 4 */ + status = 0x08000000; /* switch 4 */ + break; + case PL_S_WALK: + status = 0x08000000; + break; + case PL_S_CROUCH: /* switch 4 */ + status = 0x08000000; + if (RIC.step_s != 2) { + status = 0x08000020; + } + break; + case PL_S_FALL: /* switch 4 */ + status = 0x08002000; + break; + case PL_S_JUMP: /* switch 4 */ + status = 0x08002000; + break; + case PL_S_HIGHJUMP: /* switch 4 */ + BO6_RicSetInvincibilityFrames(1, 4); + break; + case PL_S_HIT: /* switch 4 */ + status = 0x08010000; + BO6_RicSetInvincibilityFrames(1, 8); + break; + case PL_S_DEAD: /* switch 4 */ +#ifdef VERSION_PSP + g_api.TimeAttackController( + TIMEATTACK_EVENT_SAVE_RICHTER, TIMEATTACK_SET_RECORD); +#endif + status = 0x08050000; + if (RIC.step_s == 0x80) { + status |= 0x80000; + } + BO6_RicSetInvincibilityFrames(1, 8); + break; + + case PL_S_FLAME_WHIP: /* switch 4 */ + case PL_S_HYDROSTORM: /* switch 4 */ + case PL_S_THROW_DAGGERS: /* switch 4 */ + case PL_S_SUBWPN_CRASH: /* switch 4 */ + status = 0x08000000; + // fallthrough + // possibly PSP specific + case PL_S_STAND_IN_AIR: + BO6_RicSetInvincibilityFrames(1, 8); + break; + case PL_S_SLIDE: /* switch 4 */ + case PL_S_SLIDE_KICK: /* switch 4 */ + status = 0x20; + break; + case PL_S_RUN: + case PL_S_BLADEDASH: + break; + case PL_S_ENDING_1: /* switch 4 */ + case PL_S_ENDING2: /* switch 4 */ + BO6_RicSetInvincibilityFrames(1, 8); + status |= 0x08000000; + break; + case PL_S_ENDING_3: /* switch 4 */ +#ifdef VERSION_PSP + g_api.TimeAttackController( + TIMEATTACK_EVENT_SAVE_RICHTER, TIMEATTACK_SET_RECORD); +#endif + BO6_RicSetInvincibilityFrames(1, 8); + status |= 0x08000000; + break; + case PL_S_ENDING_4: /* switch 4 */ + g_api.TimeAttackController( + TIMEATTACK_EVENT_SAVE_RICHTER, TIMEATTACK_SET_RECORD); + BO6_RicSetInvincibilityFrames(1, 8); + status |= 0x08000000; + break; + } + + if (g_Ric.timers[PL_T_ATTACK]) { + status |= 0x400; + } + if (g_Ric.timers[PL_T_10]) { + status |= 0x800; + } + if (g_Ric.timers[PL_T_12]) { + status |= 0x1000; + } + status |= 0x10000000; + g_Ric.status = status; + if (g_Ric.unk08 & 0x10000) { + if (!(g_Ric.status & PLAYER_STATUS_UNK10000)) { + if (g_Ric.unk5C) { + if (g_Status.hp < 2) { + OVL_EXPORT(RicSetDeadPrologue)(); + BO6_RicSetInvincibilityFrames(1, 8); + } + } else { + BO6_RicSetInvincibilityFrames(1, 8); + g_Ric.timers[PL_T_4] = 8; + RIC.palette = PAL_OVL(0x220); + } + } + } + if (status & 0x08000000) { + BO6_DisableAfterImage(1, 4); + } + g_api.UpdateAnim(D_us_801824A4, D_us_80181F14); + if (RIC.anim == &D_us_8018224C[0]) { + RIC.palette = D_us_80181250[RIC.pose]; + } + if ((RIC.anim == &D_us_801821E0[0]) && (RIC.pose == 4)) { + RIC.palette = D_us_80181270[RIC.poseTimer & 3]; + } + if (g_Ric.status & PLAYER_STATUS_DEAD) { + if (RIC.poseTimer < 0) { + RIC.animCurFrame |= 0x8000; + } + RIC.hitboxState = 0; + } else { + RIC.hitboxState = g_Ric.unk70; + if ((g_Ric.timers[PL_T_INVINCIBLE_SCENE] | + g_Ric.timers[PL_T_INVINCIBLE]) != 0) { + RIC.hitboxState = 0; + } + } + func_us_801B94CC(); + // g_Entities_64 = 0x091E4580; + // 0x091E4584; + // 0x091E4596; + posX = RIC.posX.val; + posY = RIC.posY.val; + func_us_801B4EAC(); + func_us_801B96F4(); +#ifdef VERSION_PSP + FntPrint("pl_color:%04x\n", RIC.palette); +#endif +} + +extern s32 D_us_801CF3C8; // Richter Think Step +extern s32 D_us_801CF3CC; // Richter Think Step_s +void func_us_801B5A14(s32 step) { + D_us_801CF3C8 = step; + D_us_801CF3CC = 0; +} + +typedef enum { THINK_STEP_INIT } ThinkStep; + +// RIC base = 0x800762D8 +extern s32 D_us_80181278; +extern s32 D_us_801CF3C8; // Richter Think Step +extern s32 D_us_801CF3CC; // Richter Think Step_s +extern s32 D_us_801CF3D0; +extern s32 D_us_801CF3D8; +extern s32 D_us_801CF3E0; +extern s32 D_us_801CF3E4; +extern s32 g_CutsceneFlags; + +extern s32 D_us_801D169C; // padSim + 0xB0 +static void func_us_801B5A14(s32 step); + +// not matching on PSP +void RichterThinking(void) { + s32 globalPosX; + s32 playerDistanceX; + bool facingLeft; + + if (D_us_801CF3D8) { + D_us_801CF3D8--; + } + + globalPosX = g_Tilemap.scrollX.i.hi + RIC.posX.i.hi; + g_Ric.demo_timer = 2; + g_Ric.padSim = 0; + + facingLeft = false; + if ((RIC.posX.i.hi - PLAYER.posX.i.hi) >= 0) { + facingLeft = true; + } + + playerDistanceX = abs(RIC.posX.i.hi - PLAYER.posX.i.hi); + + if (D_us_801CF3E4 < g_Ric.unk6C && D_us_801CF3E4 >= g_Ric.unk6A) { + D_us_801CF3C8 = 0x1E; + } + + if (D_us_801D169C != 0) { + func_us_801B5A14(0x28); + } + + if (g_Player.status & PLAYER_STATUS_DEAD) { + D_us_801CF3C8 = 0x32; + } + + if (D_us_801CF3C8 < 0x12) { + if (g_Ric.status & PLAYER_STATUS_UNK10000) { + func_us_801B5A14(0); + } else if (g_Player.timers[ALU_T_12] && D_us_801CF3C8 != 0xE) { + func_us_801B5A14(0xE); + } + } + + FntPrint("think_step:%02x\n", D_us_801CF3C8); + + switch (D_us_801CF3C8) { /* switch 1 */ + // following item crash at start of fight + case THINK_STEP_INIT: /* switch 1 */ + if (!(g_Ric.status & PLAYER_STATUS_UNK10000)) { + if (g_Player.timers[ALU_T_10]) { + if (rand() & 1) { + func_us_801B5A14(7); + } else { + func_us_801B5A14(5); + } + } else if (abs(RIC.posY.i.hi - PLAYER.posY.i.hi) > 0x20) { + func_us_801B5A14(7); + } else { + if (playerDistanceX > 0x58) { + if (facingLeft) { + g_Ric.padSim = 0x8000; + } else { + g_Ric.padSim = 0x2000; + } + } else { + func_us_801B5A14(1); + } + } + } + break; + // decicing on attack? + case 1: /* switch 1 */ + if (RIC.facingLeft != facingLeft) { + if (facingLeft) { + g_Ric.padSim = 0x8000; + } else { + g_Ric.padSim = 0x2000; + } + } + + if (D_us_801CF3CC == 0) { + D_us_801CF3D0 = 8; + D_us_801CF3CC = 1; + return; + } + + if (playerDistanceX > 0x58) { + func_us_801B5A14(0); + } else { + if (((globalPosX < 0x10) && (RIC.facingLeft == 0)) || + ((globalPosX > 0xF0) && (RIC.facingLeft))) { + func_us_801B5A14(4); + if (D_us_801CF3E0 != 0) { + func_us_801B5A14(0xD); + return; + } + } else if (g_Player.status & PLAYER_STATUS_CROUCH) { + if (rand() & 1) { + func_us_801B5A14(6); + } else { + func_us_801B5A14(5); + } + } else { + if (g_Player.timers[ALU_T_9]) { + switch (rand() & 7) { /* switch 2 */ + case 0: /* switch 2 */ + case 6: /* switch 2 */ + func_us_801B5A14(6); + break; + case 7: /* switch 2 */ + func_us_801B5A14(7); + break; + case 1: /* switch 2 */ + func_us_801B5A14(5); + break; + case 2: /* switch 2 */ + case 3: /* switch 2 */ + case 4: /* switch 2 */ + case 5: /* switch 2 */ + default: + func_us_801B5A14(2); + break; + } + } else { + if (g_Player.timers[ALU_T_10]) { + if (rand() & 1) { + func_us_801B5A14(7); + } else { + func_us_801B5A14(5); + } + } else if (D_us_801CF3D0) { + D_us_801CF3D0--; + } else if (playerDistanceX < 0x40) { + if ((RIC.posY.i.hi - PLAYER.posY.i.hi) < 0x18) { + func_us_801B5A14(3); + } else { + func_us_801B5A14(8); + } + } else { + func_us_801B5A14(8); + } + } + } + } + break; + case 2: /* switch 1 */ + switch (D_us_801CF3CC) { /* switch 3; irregular */ + case 0: /* switch 3 */ + if (RIC.step == 5) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x40; + } + break; + case 1: /* switch 3 */ + if (g_Ric.unk44 & 8) { + D_us_801CF3CC = 2; + } else { + if (g_Timer & 1) { + g_Ric.padSim = 0x40; + } + if (g_Ric.vram_flag & 1) { + func_us_801B5A14(0); + } + } + break; + case 2: + default: /* switch 3 */ + if (D_us_801CF3E0 == 0) { + func_us_801B5A14(9); + } + if (g_Ric.vram_flag & 1) { + if (D_us_801CF3E0 != 0) { + func_us_801B5A14(0xC); + } else { + func_us_801B5A14(0); + } + } + break; + } + break; + // whip attack + case 3: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (!g_Ric.unk46) { + if (g_Timer & 1) { + g_Ric.padSim = 0x80; + } + D_us_801CF3CC = 1; + } + } else { + if (!g_Ric.unk46) { + func_us_801B5A14(0); + } + } + break; + // dash + case 4: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (g_Timer & 1) { + g_Ric.padSim = PAD_R1; + } + if (RIC.step == 0x19) { + D_us_801CF3C8 = 1; + } + } else if (RIC.step != 0x19) { + func_us_801B5A14(0); + } + break; + case 5: /* switch 1 */ + g_Ric.padSim = 0x4000; + switch (D_us_801CF3CC) { + case 0: + if (RIC.facingLeft != facingLeft) { + if (facingLeft) { + g_Ric.padSim |= 0x8000; + } else { + g_Ric.padSim |= 0x2000; + } + } + if (RIC.step == 3) { + D_us_801CF3CC = 1; + D_us_801CF3D0 = 8; + } + break; + case 1: + default: + if (!--D_us_801CF3D0) { + func_us_801B5A14(17); + } else if (playerDistanceX < 0x40) { + func_us_801B5A14(10); + } + break; + } + break; + case 6: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (RIC.step == 5) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x40; + } + } else if (g_Ric.vram_flag & 1) { + func_us_801B5A14(0); + } else if (RIC.velocityY > 0x4000) { + func_us_801B5A14(9); + } + break; + case 7: /* switch 1 */ + if (RIC.facingLeft) { + g_Ric.padSim = PAD_LEFT; + } else { + g_Ric.padSim = PAD_RIGHT; + } + if (D_us_801CF3CC == 0) { + if (RIC.step == 5) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim |= PAD_CROSS; + } + } else if (g_Ric.vram_flag & 1) { + func_us_801B5A14(0); + } else if (RIC.velocityY > 0x4000) { + if (rand() & 1) { + func_us_801B5A14(0xB); + } else { + func_us_801B5A14(9); + } + } + break; + // subweapon attack? + case 8: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (!g_Ric.unk46) { + if (g_Timer & 1) { + g_Ric.padSim = PAD_UP | PAD_SQUARE; + } + D_us_801CF3CC = 1; + } + } else if (!g_Ric.unk46) { + func_us_801B5A14(0); + } + break; + case 9: /* switch 1 */ + if (g_Ric.vram_flag & 1) { + func_us_801B5A14(0); + } else if (D_us_801CF3CC == 0) { + if (!g_Ric.unk46) { + if (g_Timer & 1) { + g_Ric.padSim = PAD_UP | PAD_SQUARE; + } + D_us_801CF3CC = 1; + } + } else if (!g_Ric.unk46) { + func_us_801B5A14(0); + } + break; + case 10: /* switch 1 */ + g_Ric.padSim = PAD_DOWN; + if (D_us_801CF3CC == 0) { + if (!g_Ric.unk46) { + if (g_Timer & 1) { + g_Ric.padSim |= PAD_SQUARE; + } + D_us_801CF3CC = 1; + } + } else if (!g_Ric.unk46) { + func_us_801B5A14(0); + } + break; + case 11: /* switch 1 */ + if (g_Ric.vram_flag & 1) { + func_us_801B5A14(0); + } else if (D_us_801CF3CC == 0) { + if (!g_Ric.unk46) { + if (g_Timer & 1) { + g_Ric.padSim = 0x80; + } + D_us_801CF3CC = 1; + } + } else if (!g_Ric.unk46) { + func_us_801B5A14(0); + } + break; + case 12: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (RIC.step == 0x1C) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x110; + } + } else if (RIC.step != 0x1C) { + func_us_801B5A14(0x11); + return; + } + break; + case 13: /* switch 1 */ + // STEP: cutscene + if (D_us_801CF3CC == 0) { + if (RIC.step == 0x1C) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x810; + } + } else if (RIC.step != 0x1C) { + func_us_801B5A14(0); + } + break; + + case 14: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (RIC.step == 0x13) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x14; + } + } else if (RIC.step != 0x13) { + func_us_801B5A14(0); + } + break; + + case 15: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (RIC.step == 0x15) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x11; + } + } else if (RIC.step != 0x15) { + func_us_801B5A14(0); + } + break; + case 16: /* switch 1 */ + if (D_us_801CF3CC == 0) { + if (RIC.step == 0x13) { + D_us_801CF3CC = 1; + } else if (g_Timer & 1) { + g_Ric.padSim = 0x10; + } + } else if (RIC.step != 0x13) { + func_us_801B5A14(0); + } + break; + case 17: /* switch 1 */ + switch (D_us_801CF3CC) { /* switch 4; irregular */ + case 0: /* switch 4 */ + g_Ric.padSim = 0x4000; + if (RIC.step == 3) { + D_us_801CF3CC = 1; + } + break; + case 1: /* switch 4 */ + if (RIC.step == 0x18) { + D_us_801CF3CC = 4; + if (D_us_801CF3E0 != 0) { + if (RIC.facingLeft && RIC.posX.i.hi > 0x80) { + D_us_801CF3CC = 2; + } + if (!RIC.facingLeft && RIC.posX.i.hi < 0x80) { + D_us_801CF3CC = 2; + } + } + } else { + g_Ric.padSim = 0x4000; + if (g_Timer & 1) { + g_Ric.padSim |= PAD_CROSS; + } + } + break; + case 2: /* switch 4 */ + if (RIC.step == 0x1B) { + D_us_801CF3CC = 3; + } else { + g_Ric.padSim = 0x4000; + if (g_Timer & 1) { + g_Ric.padSim |= 0x40; + } + } + break; + case 3: /* switch 4 */ + g_Ric.padSim = 0x80; + if (RIC.step != 0x1B) { + func_us_801B5A14(0); + } + // fallthrough + + default: + func_us_801B5A14(0); + break; + } + break; + case 18: /* switch 1 */ + OVL_EXPORT(RicSetInvincibilityFrames)(1, 4); + if (RIC.step == 1) { + func_us_801B5A14(0x13); + } + break; + + case 19: /* switch 1 */ + OVL_EXPORT(RicSetInvincibilityFrames)(1, 4); + if (D_us_801CF3CC == 0) { + D_us_801CF3D0 = 0x40; + D_us_801CF3CC = 1; + } else { + if ((g_CutsceneFlags & 2) || (g_CastleFlags[SHAFT_ORB_DEFEATED]) || + (g_DemoMode != Demo_None)) { + if (!--D_us_801CF3D0) { + OVL_EXPORT(RicCreateEntFactoryFromEntity) + (g_CurrentEntity, 0x48, 0); + func_us_801B5A14(0x10); + } + } + } + break; + case 30: /* switch 1 */ + g_Player.timers[ALU_T_INVINCIBLE_CONSUMABLES] = 3; + OVL_EXPORT(RicSetInvincibilityFrames)(1, 8); + g_Ric.padSim = 0x1000; + if (RIC.step == 1 && RIC.step_s == 1) { + RIC.step = 0x50; + RIC.step_s = 0; + D_us_801CF3C8 = 0x1F; + } + break; + case 31: /* switch 1 */ + g_Player.timers[ALU_T_INVINCIBLE_CONSUMABLES] = 3; + OVL_EXPORT(RicSetInvincibilityFrames)(1, 8); + if (RIC.step != 0x50) { + D_us_801CF3E0 = 1; + func_us_801B5A14(0x20); + } + break; + case 32: /* switch 1 */ + g_Player.timers[ALU_T_INVINCIBLE_CONSUMABLES] = 3; + OVL_EXPORT(RicSetInvincibilityFrames)(1, 8); + if (D_us_801CF3CC == 0) { + D_us_801CF3D0 = 0x10; + D_us_801CF3CC = 1; + } else { + if (D_us_801CF3D0 != 0) { + D_us_801CF3D0--; + } + func_us_801B5A14(0xF); + } + break; + + case 40: /* switch 1 */ + g_Player.timers[ALU_T_INVINCIBLE_CONSUMABLES] = 3; + D_us_80181278 = 0x32; + D_us_801CF3C8++; + break; + + case 41: /* switch 1 */ + g_Player.timers[ALU_T_INVINCIBLE_CONSUMABLES] = 3; + break; + + case 50: /* switch 1 */ + if (!(g_Player.status & PLAYER_STATUS_DEAD)) { + func_us_801B5A14(0); + } + g_Ric.padSim = 0x1000; + break; + } +} + +extern s32 D_us_801D11C0; + +void func_us_801B6998(void) { + switch (D_us_80181278) { + case 0: + break; + case 10: + if ((g_CastleFlags[SHAFT_ORB_DEFEATED] == 0) && + (g_DemoMode == Demo_None)) { + g_unkGraphicsStruct.pauseEnemies = true; + D_us_801D11C0 = 0; + } + D_us_80181278++; + break; + case 11: + if (++D_us_801D11C0 > 1) { + if ((g_CastleFlags[SHAFT_ORB_DEFEATED] == 0) && + (g_DemoMode == Demo_None)) { + g_unkGraphicsStruct.unk20 = 0xFF; + } + D_us_80181278++; + } + break; + case 20: + if (g_CutsceneFlags & 4) { + D_us_80181278 = 0x1E; + } + break; + case 40: + g_CutsceneFlags |= 8; + break; + case 50: + default: + break; + } +} -INCLUDE_ASM("boss/bo6/nonmatchings/richter", func_us_801B5A14); +extern EInit D_us_80180400; // +extern s32 D_us_801CF3E0; +extern s32 D_us_801CF3E4; -INCLUDE_ASM("boss/bo6/nonmatchings/richter", RichterThinking); +void EntityRichter(Entity* self) { + Entity* entity; + s32 i; -INCLUDE_ASM("boss/bo6/nonmatchings/richter", func_us_801B6998); + g_Ric.unk6A = RIC.hitPoints; + if (self->step == 0) { + InitializeEntity(D_us_80180400); + func_us_801B4BD0(); // + entity = &g_Entities[STAGE_ENTITY_START + 4]; + for (i = STAGE_ENTITY_START + 4; i < 144; i++, entity++) { + DestroyEntity(entity); + } + g_Ric.unk6E = g_Ric.unk6A = g_Ric.unk6C = RIC.hitPoints; + D_us_801CF3E4 = g_Ric.unk6E / 2; + D_us_801CF3E0 = 0; + g_Ric.unk70 = RIC.hitboxState; + func_us_801B5A14(18); + OVL_EXPORT(DisableAfterImage)(1, 48); + } else { + RichterThinking(); + OVL_EXPORT(RicMain)(); // equivalent to EntityDoppleganger{10,40} + func_us_801BBBD0(); + func_us_801B6998(); + } + g_Ric.unk6C = g_Ric.unk6A; +} -INCLUDE_ASM("boss/bo6/nonmatchings/richter", EntityRichter); +// possible file split - pl_setstep INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepStand); @@ -33,7 +1095,13 @@ INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepCrouch); INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicResetPose); -INCLUDE_ASM("boss/bo6/nonmatchings/richter", func_us_801B77D8); +void func_us_801B77D8(void) { + if ((RIC.posX.i.hi - PLAYER.posX.i.hi) <= 0) { + RIC.entityRoomIndex = 0; + } else { + RIC.entityRoomIndex = 1; + } +} INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepHit); @@ -43,7 +1111,12 @@ INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepStandInAir); INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepEnableFlameWhip); -INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepHydrostorm); +void OVL_EXPORT(RicStepHydrostorm)(void) { + if (RIC.poseTimer < 0) { + OVL_EXPORT(RicSetStand)(0); + g_Ric.unk46 = 0; + } +} INCLUDE_ASM("boss/bo6/nonmatchings/richter", BO6_RicStepGenericSubwpnCrash); diff --git a/src/boss/bo6/us_39144.c b/src/boss/bo6/us_39144.c index e236bf5e5e..e8092755c1 100644 --- a/src/boss/bo6/us_39144.c +++ b/src/boss/bo6/us_39144.c @@ -1,61 +1,387 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +#include #include "bo6.h" -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9144); +extern s32 D_us_80181278; +extern AnimationFrame D_us_80181F1C[]; +extern AnimationFrame D_us_801823C8[]; + +// ending 2 function +void func_us_801B9144(void) { + Entity* entity; + switch (RIC.step_s) { + case 0: + BO6_RicSetAnimation(D_us_80181F1C); + g_api.PlaySfx(0x82B); + if (RIC.posX.i.hi < 0x80) { + RIC.facingLeft = 0; + } else { + RIC.facingLeft = 1; + } + RIC.step_s++; + // fallthrough + + case 1: + D_us_80181278 = 0x14; + entity = &g_Entities[200]; + CreateEntityFromCurrentEntity(E_ID_17, entity); + entity->params = 1; + RIC.step_s++; + // fallthrough + + case 2: + if (D_us_80181278 == 0x1E) { + BO6_RicSetAnimation(D_us_801823C8); + BO6_RicCreateEntFactoryFromEntity( + g_CurrentEntity, FACTORY(E_ID_24, 0x1), 0); + RIC.step_s++; + } + break; + case 3: + if (RIC.animCurFrame == 0xB5) { + if (RIC.poseTimer == 1) { + BO6_RicCreateEntFactoryFromEntity( + g_CurrentEntity, FACTORY(E_ID_23, 0), 0); + g_api.PlaySfx(SFX_WEAPON_APPEAR); + } + } + if (RIC.poseTimer < 0) { + D_us_80181278 = 0x28; + BO6_RicSetStand(0); + BO6_RicCreateEntFactoryFromEntity( + g_CurrentEntity, FACTORY(E_ID_21, 0x45), 0); + g_Ric.timers[ALU_T_POISON] = 0x800; + } + break; + } +} void func_us_801B9338(void) {} -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9340); - +extern s16 D_us_8018221C[]; + +void func_us_801B9340(void) { + switch (RIC.step_s) { + case 0: + BO6_RicResetPose(); + RIC.velocityY = FIX(-5); + func_us_801B9ACC(0xFFFF1000); + RIC.anim = D_us_8018221C; + g_api.PlaySfx(0x83E); + g_Ric.unk40 = 0x8166; + g_Ric.timers[2] = 8; + BO6_RicCreateEntFactoryFromEntity( + g_CurrentEntity, FACTORY(E_ID_21, 0x58), 0); + RIC.step_s += 1; + return; + case 1: + if ((g_Ric.vram_flag & 2) && (FIX(-1) > RIC.velocityY)) { + RIC.velocityY = FIX(-1); + } + if (BO6_RicCheckInput(0x20280) != 0) { + RIC.step = 0x70; + RIC.step_s = 2; + return; + } + return; + case 2: + DecelerateX(FIX(0.125)); + if ((PLAYER.posX.i.hi - RIC.posX.i.hi) > 0) { + RIC.facingLeft = 0; + return; + } + RIC.facingLeft = 1; + break; + } +} + +// split pl_utils + +// maybe func_8015C4AC INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B94CC); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B96F4); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetStep); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetAnimation); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", DecelerateX); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", DecelerateY); +// maybe func_8015C6D4 +extern u8 D_us_80181298[]; +extern u8 D_us_801812A8[]; + +void func_us_801B96F4(void) { + byte pad[0x28]; + Primitive* prim; + PlayerDraw* draw; + s32 i; + u8 var_s3; + u8 var_s5; + u8 resetAnim; + + resetAnim = g_Entities[65].ext.entSlot1.unk1; + prim = &g_PrimBuf[g_Entities[65].primIndex]; + i = 0; + draw = &g_PlayerDraw[9]; + var_s5 = D_us_80181298[g_Entities[65].ext.entSlot1.unk2]; + var_s3 = D_us_801812A8[g_Entities[65].ext.entSlot1.unk2]; + while (prim != NULL) { + if (prim->r0 > var_s3) { + prim->r0 -= var_s5; + } + if (prim->r0 < 112 && prim->b0 < 240) { + prim->b0 += 6; + } + if (prim->r0 < 88) { + prim->y1 = 16; + } else { + prim->y1 = 0; + } + if (prim->r0 <= var_s3) { + prim->x1 = 0; + } + if ((i ^ g_Timer) & 1) { + g_Entities[i / 2 + 65].posX.i.hi = prim->x0; + g_Entities[i / 2 + 65].posY.i.hi = prim->y0; + g_Entities[i / 2 + 65].animCurFrame = prim->x1; + g_Entities[i / 2 + 65].drawMode = prim->y1; + g_Entities[i / 2 + 65].facingLeft = prim->x2; + g_Entities[i / 2 + 65].palette = prim->y2; + g_Entities[i / 2 + 65].zPriority = RIC.zPriority - 2; + if (resetAnim) { + g_Entities[i / 2 + 65].animCurFrame = 0; + prim->x1 = 0; + } + + draw->r0 = draw->r1 = draw->r2 = draw->r3 = draw->g0 = draw->g1 = + draw->g2 = draw->g3 = prim->r0; + draw->b0 = draw->b1 = draw->b2 = draw->b3 = prim->b0; + draw->enableColorBlend = true; + draw++; + } + i++; + prim = prim->next; + } +} + + + +// BO6_RicSetStep +void OVL_EXPORT(RicSetStep)(s16 step) { + RIC.step = step; + RIC.step_s = 0; +} + +void OVL_EXPORT(RicSetAnimation)(AnimationFrame* anim) { + g_CurrentEntity->anim = anim; + g_CurrentEntity->poseTimer = 0; + g_CurrentEntity->pose = 0; +} + +#include "../../decelerate.h" INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicCheckFacing); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetSpeedX); +void BO6_RicSetSpeedX(s32 speed) { + if (g_CurrentEntity->facingLeft == 1) + speed = -speed; + g_CurrentEntity->velocityX = speed; +} -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9ACC); +// set velocity +void func_us_801B9ACC(s32 speed) { + if (RIC.entityRoomIndex == 1) + speed = -speed; + RIC.velocityX = speed; +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetInvincibilityFrames); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_DisableAfterImage); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9C14); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9C3C); +// similar to func_8010DFF0 in DRA or func_us_801C5354 in BO4 +void OVL_EXPORT(DisableAfterImage)(s32 resetAnims, s32 time) { + Primitive* prim; + + if (resetAnims) { + g_Entities[E_ID_41].ext.disableAfterImage.unk7D = 1; + g_Entities[E_ID_41].animCurFrame = g_Entities[E_ID_42].animCurFrame = + g_Entities[E_ID_43].animCurFrame = 0; + prim = &g_PrimBuf[g_Entities[E_ID_41].primIndex]; + while (prim != NULL) { + prim->x1 = 0; + prim = prim->next; + } + } + g_Entities[E_ID_41].ext.disableAfterImage.unk7C = 1; + g_Entities[E_ID_41].ext.disableAfterImage.unk7E = 0xA; + if (time) { + g_Ric.timers[ALU_T_15] = 4; + } +} + +void func_us_801B9C14() { + g_Entities[65].ext.entSlot1.unk3 = 0; + g_Entities[65].ext.entSlot1.unk2 = 0; + g_Entities[65].ext.entSlot1.unk1 = 0; + g_Entities[65].ext.entSlot1.unk0 = 0; +} + +void BO6_RicSetDebug() { OVL_EXPORT(RicSetStep)(PL_S_DEBUG); } INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetCrouch); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetStand); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9D74); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9DE4); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801B9E70); +extern AnimationFrame ric_anim_stand[]; + +void BO6_RicSetStand(s32 velocityX) { + RIC.velocityX = velocityX; + RIC.velocityY = 0; + g_Ric.unk44 = 0; + OVL_EXPORT(RicSetStep)(PL_S_STAND); + OVL_EXPORT(RicSetAnimation)(ric_anim_stand); +} + +extern s16 D_us_801821F8[]; + +void func_us_801B9D74(void) { + g_Ric.unk44 = 0; + BO6_RicSetStep(0x1A); + BO6_RicSetAnimation(D_us_801821F8); + BO6_RicSetSpeedX(FIX(2.25)); + g_Ric.timers[11] = 0x28; + RIC.velocityY = 0; + BO6_RicCreateEntFactoryFromEntity(g_CurrentEntity, 0x50001, 0); +} + +extern s16 D_us_80182010[]; + +void func_us_801B9DE4(void) { + if (g_Ric.timers[8] != 0) { + func_us_801B9D74(); + } else { + g_Ric.timers[1] = 8; + g_Ric.timers[1] = g_Ric.timers[8] = 12; + g_Ric.unk44 = 0; + BO6_RicSetStep(2); + BO6_RicSetAnimation(D_us_80182010); + BO6_RicSetSpeedX(FIX(1.25)); + RIC.velocityY = 0; + } +} + +extern s16 D_us_80182078[]; +extern s16 D_us_80182094[]; + +void func_us_801B9E70(void) { + if ((BO6_RicCheckFacing() != 0) || (RIC.step == 0x18)) { + BO6_RicSetAnimation(D_us_80182094); + if (RIC.step == 0x1A) { + BO6_RicSetSpeedX(FIX(2.25)); + g_Ric.unk44 = 0x10; + } else { + BO6_RicSetSpeedX(0x14000); + g_Ric.unk44 = 0; + } + } else { + BO6_RicSetAnimation(D_us_80182078); + RIC.velocityX = 0; + g_Ric.unk44 = 4; + } + BO6_RicSetStep(5); + RIC.velocityY = FIX(-4.6875); +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetFall); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BA050); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicCheckSubwpnChainLimit); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicDoSubweapon); +extern s16 D_us_80182324[]; + +void func_us_801BA050(void) { + BO6_RicSetStep(9); + RIC.velocityX = 0; + BO6_RicSetSpeedX(FIX(1.25)); + RIC.velocityY = FIX(-7.5); + g_Ric.high_jump_timer = 0; + BO6_RicSetAnimation(D_us_80182324); + func_us_801B9C14(); + BO6_RicCreateEntFactoryFromEntity(g_CurrentEntity, FACTORY(0x2D, 0), 0); + g_api.PlaySfx(0x82D); + g_Ric.timers[12] = 4; +} + +static s32 OVL_EXPORT(RicCheckSubwpnChainLimit)(s16 subwpnId, s16 limit) { + Entity* entity; + s32 i; + u32 nEmpty; + s32 nFound; + + entity = &g_Entities[0x60]; + for (i = 0, nFound = 0, nEmpty = 0; i < 32; i++, entity++) { + if (!entity->entityId) { + nEmpty++; + } + + if (entity->ext.subweapon.subweaponId != 0 && + entity->ext.subweapon.subweaponId == subwpnId) { + nFound++; + } + + if (nFound >= limit) { + return -1; + } + } + + if (nEmpty < 1) { + return -1; + } else { + return 0; + } +} + +extern u16 D_us_80182170[][2]; +extern u16 D_us_801821C0[][2]; + +s32 OVL_EXPORT(RicDoSubweapon)(void) { + SubweaponDef subweapon; + s16 subweaponId; + s16 chainLimit; + + if (!(g_Ric.padPressed & PAD_UP)) { + return 1; + } + + subweaponId = BO6_RicCheckSubweapon(&subweapon, 0, 0); + chainLimit = subweapon.chainLimit; + if (OVL_EXPORT(RicCheckSubwpnChainLimit)(subweaponId, chainLimit) < 0) { + return 2; + } + + OVL_EXPORT(RicCreateEntFactoryFromEntity) + (g_CurrentEntity, subweapon.blueprintNum, 0); + g_Ric.timers[PL_T_10] = 4; + switch (RIC.step) { + case PL_S_RUN: + RIC.step = PL_S_STAND; + OVL_EXPORT(RicCreateEntFactoryFromEntity)(g_CurrentEntity, 0U, 0); + BO6_RicSetAnimation(D_us_80182170); + break; + case PL_S_STAND: + case PL_S_WALK: + case PL_S_CROUCH: + RIC.step = PL_S_STAND; + BO6_RicSetAnimation(D_us_80182170); + break; + case PL_S_FALL: + case PL_S_JUMP: + RIC.step = PL_S_JUMP; + BO6_RicSetAnimation(D_us_801821C0); + break; + } + g_Ric.unk46 = 3; + RIC.step_s = 0x42; + // n.b.! this was just set before the switch + g_Ric.timers[PL_T_10] = 4; + return 0; +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicDoAttack); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicDoCrash); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetDeadPrologue); +void OVL_EXPORT(RicSetDeadPrologue)() { + OVL_EXPORT(RicSetStep)(PL_S_DEAD_PROLOGUE); +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicSetSlide); @@ -65,7 +391,19 @@ INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BA9D0); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicCheckInput); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicGetFreeEntity); +Entity* OVL_EXPORT(RicGetFreeEntity)(s16 start, s16 end) { + Entity* entity = &g_Entities[start]; + s16 i; + + for (i = start; i < end; i++, entity++) { + if (entity->entityId == E_NONE) { + return entity; + } + } + return NULL; +} + +// pl_blueprints? INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicGetFreeEntityReverse); @@ -73,7 +411,56 @@ INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BB314); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BB370); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicCheckSubweapon); +extern SubweaponDef subweapons_def[]; + +s32 BO6_RicCheckSubweapon( + SubweaponDef* actualSubwpn, s32 isItemCrash, s32 useHearts) { + SubweaponDef* subwpn; + SubweaponDef* wpn; + s32 i; + s32 subweaponId; + s32 distanceX; + + distanceX = abs(RIC.posX.i.hi - PLAYER.posX.i.hi); + + if (isItemCrash == 0) { + if (distanceX < 0x50) { + subweaponId = 3; + } else { + subweaponId = 4; + } + if ((RIC.posY.i.hi - PLAYER.posY.i.hi) > 0x18) { + subweaponId = 2; + } + if ((RIC.posY.i.hi - PLAYER.posY.i.hi) < -0x18) { + subweaponId = 3; + } + if (g_Player.status & PLAYER_STATUS_BAT_FORM) { + subweaponId = 2; + } + + *actualSubwpn = subweapons_def[subweaponId]; + } else { + subweaponId = 4; + if (g_Ric.padPressed & 4) { + subweaponId = 2; + } else { + if (g_Ric.padPressed & 1) { + subweaponId = 3; + } + if (g_Ric.padPressed & 0x800) { + subweaponId = 9; + } + if (g_Ric.padPressed & 0x100) { + subweaponId = 5; + } + } + + subwpn = &subweapons_def[subweaponId]; + *actualSubwpn = subweapons_def[subwpn->crashId]; + } + return subweaponId; +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BB5BC); @@ -85,15 +472,113 @@ void func_us_801BBBC0(void) {} void func_us_801BBBC8(void) {} -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BBBD0); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicCreateEntFactoryFromEntity); +extern AnimationFrame* D_us_801812B8[]; +extern PfnEntityUpdate D_us_8018158C[]; +extern u8 D_us_801D0800; +extern u8 D_us_801D0804; +extern u8 D_us_801D0808; +extern u8 D_us_801D07FC; + +void func_us_801BBBD0(void) { + Entity* entity; + PfnEntityUpdate entityUpdate; + s32 i; + + entity = g_CurrentEntity = &g_Entities[E_ID_44]; + + for (i = E_ID_44; i < E_ID_90; i++, g_CurrentEntity++, entity++) { + if (entity->entityId) { + entityUpdate = D_us_8018158C[entity->entityId]; + entityUpdate(entity); + entity = g_CurrentEntity; + + if (entity->entityId) { + if (!(entity->flags & FLAG_UNK_10000000) && + (entity->posX.i.hi > 288 || entity->posX.i.hi < -32 || + entity->posY.i.hi > 256 || entity->posY.i.hi < -16)) { + DestroyEntity(entity); + } else { + if (entity->flags & FLAG_UNK_20000000) { + g_api.UpdateAnim(NULL, D_us_801812B8); + } + entity->flags |= FLAG_NOT_AN_ENEMY; + } + } + } + } + + if (D_us_801D07FC) { + D_us_801D07FC--; + if (D_us_801D07FC & 1) { + g_api.func_800EA5AC(1, D_us_801D0800, D_us_801D0804, D_us_801D0808); + } + } + if ((RIC.step == 0x11) || (RIC.step == 0x60) || (RIC.step == 0x70)) { + FntPrint("dead boss\n"); + entity = &g_Entities[E_ID_44]; + for (i = E_ID_44; i < E_ID_90; i++, entity++) { + entity->hitboxState = 0; + }; + } +} + +Entity* OVL_EXPORT(RicCreateEntFactoryFromEntity)( + Entity* source, u32 factoryParams, s32 arg2) { + Entity* entity = OVL_EXPORT(RicGetFreeEntity)(68, 80); + if (!entity) { + return NULL; + } + DestroyEntity(entity); + entity->entityId = E_FACTORY; + // the parent pointer must align for anything the factory creates + entity->ext.factory.parent = source; + entity->posX.val = source->posX.val; + entity->posY.val = source->posY.val; + entity->facingLeft = source->facingLeft; + entity->zPriority = source->zPriority; + entity->params = factoryParams & 0xFFF; + entity->ext.factory.paramsBase = (factoryParams & 0xFF0000) >> 8; + return entity; +} INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityFactory); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BC2F0); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BC3E0); +extern EInit D_us_80180448; + +void func_us_801BC3E0(Entity* self) { + if (RIC.step != PL_S_SLIDE_KICK) { + DestroyEntity(self); + return; + } + self->posX.i.hi = RIC.posX.i.hi; + self->posY.i.hi = RIC.posY.i.hi; + self->facingLeft = RIC.facingLeft; + if (self->step == 0) { + InitializeEntity(D_us_80180448); + self->flags = 0x18000000; + self->hitboxOffX = 0x14; + self->hitboxWidth = self->hitboxHeight = 9; + self->step = 1; + } + + if (RIC.animCurFrame == 140) { + self->hitboxOffY = 0; + } + + if (RIC.animCurFrame == 141) { + self->hitboxOffY = 12; + } + + if (self->hitFlags) { + g_Ric.unk44 |= 0x80; + } else { + g_Ric.unk44 &= ~0x80; + } + self->hitFlags = 0; +} + INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BC4F8); @@ -103,124 +588,446 @@ INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BC678); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityHitByCutBlood); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BD0B8); +extern AnimationFrame D_us_801818A8[]; +extern AnimationFrame D_us_801819D0[]; +extern AnimationFrame D_us_80181A0C[]; + +void func_us_801BD0B8(Entity* self) { + s16 paramsLo = self->params & 0xFF; + s16 paramsHi = (self->params >> 8) & 0xFF; + + switch (self->step) { + case 0: + if (paramsHi == 1) { + self->scaleX = 0xC0; + self->scaleY = 0xC0; + self->drawFlags = FLAG_DRAW_SCALEX | FLAG_DRAW_SCALEY; + self->animSet = ANIMSET_DRA(2); + self->anim = D_us_80181A0C; + } + + if ((paramsHi == 0) || (paramsHi == 2)) { + if (paramsLo & 3) { + self->anim = D_us_801819D0; + self->scaleX = 0x120; + self->scaleY = 0x120; + self->drawFlags = FLAG_DRAW_SCALEX | FLAG_DRAW_SCALEY; + self->animSet = ANIMSET_DRA(2); + } else { + self->animSet = ANIMSET_DRA(5); + self->anim = D_us_801818A8; + self->palette = PAL_OVL(0x170); + } + } + self->flags = FLAG_UNK_20000000 | FLAG_POS_CAMERA_LOCKED; + + if (rand() & 3) { + self->zPriority = RIC.zPriority + 2; + } else { + self->zPriority = RIC.zPriority - 2; + } + + if (paramsHi == 2) { + self->posX.i.hi = RIC.posX.i.hi + (rand() % 44) - 22; + } else { + self->posX.i.hi = RIC.posX.i.hi + (rand() & 15) - 8; + } + + self->posY.i.hi = RIC.posY.i.hi + RIC.hitboxOffY + + (rand() & 31) - 16; + self->velocityY = FIX(-0.5); + self->velocityX = RIC.velocityX >> 2; + self->step++; + break; + + case 1: + self->scaleX -= 4; + self->scaleY -= 4; + self->posY.val += self->velocityY; + self->posX.val += self->velocityX; + if ((self->pose == 8) && (self->anim != D_us_801818A8)) { + self->drawMode = DRAW_TPAGE; + if (!(paramsLo & 1) && (self->poseTimer == 1)) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, FACTORY(4, 4), 0); + } + } + + if ((self->pose == 16) && (self->anim == D_us_801818A8)) { + self->drawMode = DRAW_TPAGE; + } + + if (self->poseTimer < 0) { + DestroyEntity(self); + } + break; + } +} + INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BD384); INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BD47C); -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityPlayerBlinkWhite); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801BE79C); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityShrinkingPowerUpRing); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityHitByIce); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityHitByLightning); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C03E8); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", EntityShaft); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C0FE8); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C13A8); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityWhip); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityArmBrandishWhip); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C2688); - -void func_us_801C277C(void) {} - -void func_us_801C2784(void) {} - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnHolyWaterBreakGlass); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashHydroStorm); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_DebugShowWaitInfo); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_DebugInputWait); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C310C); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", WarpBackgroundBrightness); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C32C4); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnHolyWaterFlame); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnCrashCross); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C4200); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C488C); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnCrossTrail); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnCrashCrossParticles); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnThrownAxe); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C560C); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnKnife); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_ReboundStoneBounce1); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_ReboundStoneBounce2); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnReboundStone); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnThrownVibhuti); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_PrimDecreaseBrightness); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnAgunea); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityAguneaHitEnemy); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityVibhutiCrashCloud); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashVibhuti); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C8590); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C8618); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashReboundStoneExplosion); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashReboundStone); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashBibleBeam); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashBible); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801C9DE8); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", func_us_801CA340); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_GetAguneaLightningAngle); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_AguneaShuffleParams); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityAguneaLightning); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityAguneaCircle); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnStopwatchCircle); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_EntityStopWatch); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnBibleTrail); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntitySubwpnBible); - -INCLUDE_RODATA("boss/bo6/nonmatchings/us_39144", D_us_801A7028); - -INCLUDE_RODATA("boss/bo6/nonmatchings/us_39144", D_us_801A7030); - -INCLUDE_ASM("boss/bo6/nonmatchings/us_39144", BO6_RicEntityCrashCrossBeam); +extern s32 D_us_80181A64[]; +extern s16 D_us_80181AA4[][10]; +extern s16* D_us_801A39B0[]; +extern u8* richter_sprites[]; + +void BO6_RicEntityPlayerBlinkWhite(Entity* self) { + u8 xMargin; + u8 yMargin; + s16 angle; + u8 wSprite; + u8 hSprite; + s16 width; + s16 height; + s16 selfX; + s16 selfY; + s32 i; + Primitive* prim; + s16 xPivot; + s16 yPivot; + s16 plSpriteIndex; + s16 upperParams; + s16 angleRedIndex; + s16* sp44; + u8* plSprite; + s16* dataPtr; + s16 angleGreenIndex; + s16 angleBlueIndex; + s16 redDivide; + s16 blueDivide; + s16 greenDivide; + void* dummy48; + + if (!RIC.animSet || !(RIC.animCurFrame & 0x7FFF)) { + DestroyEntity(self); + return; + } + self->posY.i.hi = RIC.posY.i.hi; + self->posX.i.hi = RIC.posX.i.hi; + self->facingLeft = RIC.facingLeft; +#if defined(VERSION_PSP) + sp44 = D_92641C8[RIC.animCurFrame & 0x7FFF]; +#endif + sp44 = D_us_801A39B0[RIC.animCurFrame & 0x7FFF]; + plSpriteIndex = *sp44++; + plSpriteIndex &= 0x7FFF; + selfX = self->posX.i.hi; + selfY = self->posY.i.hi; + plSprite = richter_sprites[plSpriteIndex]; + xMargin = 4; + yMargin = 1; + wSprite = xMargin + plSprite[0]; + hSprite = yMargin + plSprite[1]; + width = wSprite - xMargin; + height = hSprite - yMargin; + xPivot = sp44[0] + plSprite[2]; + yPivot = sp44[1] + plSprite[3]; + + self->rotate = RIC.rotate; + self->drawFlags = RIC.drawFlags; + self->scaleX = RIC.scaleX; + self->scaleY = RIC.scaleY; + self->rotPivotY = RIC.rotPivotY; + self->rotPivotX = RIC.rotPivotX; + upperParams = (self->params & 0x7F00) >> 8; + dataPtr = D_us_80181AA4[upperParams & 0x3F]; + switch (self->step) { + case 0: + if (func_us_801BD47C(self) != 0) { + DestroyEntity(self); + return; + } + self->primIndex = g_api.AllocPrimitives(PRIM_GT4, 8); + if (self->primIndex == -1) { + DestroyEntity(self); + return; + } + + self->flags = FLAG_UNK_10000000 | FLAG_POS_CAMERA_LOCKED | FLAG_HAS_PRIMS; + + prim = &g_PrimBuf[self->primIndex]; + for (i = 0; i < 8; i++) { + D_us_80181A64[i] = i << 9; + prim->tpage = 0x10; + prim->clut = dataPtr[3]; + prim->r0 = prim->b0 = prim->g0 = prim->r1 = prim->b1 = prim->g1 = + prim->r2 = prim->b2 = prim->g2 = prim->r3 = prim->b3 = + prim->g3 = 0x80; + prim->priority = RIC.zPriority + 2; + prim->drawMode = + dataPtr[8] + DRAW_UNK_100 + DRAW_COLORS; + prim = prim->next; + } + self->ext.playerBlink.unk8A = dataPtr[9]; + self->ext.playerBlink.unk90 = 0; + self->step += 1; + break; + case 1: + self->ext.playerBlink.unk90 += 0xA; + if (self->ext.playerBlink.unk90 > 0x100) { + self->ext.playerBlink.unk90 = 0x100; + self->ext.playerBlink.unk80 = dataPtr[7]; + self->step += 1; + } + break; + case 2: + if (dataPtr[7] >= 0x7000) { + switch ((u32)dataPtr[7]) { + case 0x7000: + if (g_Ric.timers[PL_T_POISON] == 0 || + (RIC.step == PL_S_DEAD || RIC.step == PL_S_ENDING_3 || RIC.step == PL_S_ENDING_4)) { + self->step++; + } + break; + case 0x7001: + if (g_Ric.timers[PL_T_INVINCIBLE_SCENE] == 0) { + self->step++; + } + break; + case 0x7007: + case 0x7002: + if (RIC.step != PL_S_HIT) { + self->step++; + } + break; + case 0x7005: + case 0x7006: + if (RIC.step_s == 3) { + self->step++; + } + break; + case 0x7003: + case 0x7004: + case 0x7008: + + break; + } + self->ext.playerBlink.unk80 = 8; + } + if (--self->ext.playerBlink.unk80 == 0) { + self->step++; + } + break; + case 3: + self->ext.playerBlink.unk90 -= 10; + if (self->ext.playerBlink.unk90 < 0) { + DestroyEntity(self); + return; + } + break; + } + self->ext.playerBlink.unk82 += self->ext.playerBlink.unk8A; + if (self->facingLeft) { + selfX = selfX - xPivot; + } else { + selfX = selfX + xPivot; + } + selfY = selfY + yPivot; + prim = &g_PrimBuf[self->primIndex]; + for (i = 0; i < 8; i++) { + if (upperParams & 0x40) { + switch (i) { + case 0: + if (self->facingLeft) { + prim->x0 = selfX; + prim->x1 = selfX - width / 2; + prim->u0 = xMargin; + prim->u1 = xMargin + width / 2; + } else { + prim->x0 = selfX; + prim->x1 = selfX + width / 2; + prim->u0 = xMargin; + prim->u1 = xMargin + width / 2; + } + prim->y0 = prim->y1 = selfY; + prim->v0 = prim->v1 = yMargin; + break; + case 1: + if (self->facingLeft) { + prim->x0 = selfX - width / 2; + prim->x1 = selfX - width; + prim->u0 = xMargin + width / 2; + prim->u1 = xMargin + width; + } else { + prim->x0 = selfX + width / 2; + prim->x1 = selfX + width; + prim->u0 = xMargin + width / 2; + prim->u1 = xMargin + width; + } + prim->y0 = prim->y1 = selfY; + prim->v0 = prim->v1 = yMargin; + break; + case 2: + if (self->facingLeft) { + prim->x0 = prim->x1 = selfX - width; + prim->u0 = prim->u1 = xMargin + width; + } else { + prim->x0 = prim->x1 = selfX + width; + prim->u0 = prim->u1 = xMargin + width; + } + prim->y0 = selfY; + prim->y1 = selfY + height / 2; + prim->v0 = yMargin; + prim->v1 = yMargin + height / 2; + break; + case 3: + if (self->facingLeft) { + prim->x0 = prim->x1 = selfX - width; + prim->u0 = prim->u1 = xMargin + width; + } else { + prim->x0 = prim->x1 = selfX + width; + prim->u0 = prim->u1 = xMargin + width; + } + prim->y0 = selfY + height / 2; + prim->y1 = selfY + height; + prim->v0 = yMargin + height / 2; + prim->v1 = yMargin + height; + break; + case 4: + if (self->facingLeft) { + prim->x0 = selfX - width; + prim->x1 = selfX - width / 2; + prim->u0 = xMargin + width; + prim->u1 = xMargin + width / 2; + } else { + prim->x0 = selfX + width; + prim->x1 = selfX + width / 2; + prim->u0 = xMargin + width; + prim->u1 = xMargin + width / 2; + } + prim->y0 = prim->y1 = selfY + height; + prim->v0 = prim->v1 = yMargin + height; + break; + case 5: + if (self->facingLeft) { + prim->x0 = selfX - width / 2; + prim->x1 = selfX; + prim->u0 = xMargin + width / 2; + prim->u1 = xMargin; + } else { + prim->x0 = selfX + width / 2; + prim->x1 = selfX; + prim->u0 = xMargin + width / 2; + prim->u1 = xMargin; + } + prim->y0 = prim->y1 = selfY + height; + prim->v0 = prim->v1 = yMargin + height; + break; + case 6: + if (self->facingLeft) { + prim->x0 = prim->x1 = selfX; + prim->u0 = prim->u1 = xMargin; + } else { + prim->x0 = prim->x1 = selfX; + prim->u0 = prim->u1 = xMargin; + } + prim->y0 = selfY + height; + prim->y1 = selfY + height / 2; + prim->v0 = yMargin + height; + prim->v1 = yMargin + height / 2; + break; + case 7: + if (self->facingLeft) { + prim->x0 = prim->x1 = selfX; + prim->u0 = prim->u1 = xMargin; + } else { + prim->x0 = prim->x1 = selfX; + prim->u0 = prim->u1 = xMargin; + } + prim->y0 = selfY + height / 2; + prim->y1 = selfY; + prim->v0 = yMargin + height / 2; + prim->v1 = yMargin; + break; + } + if (self->facingLeft) { + prim->x2 = prim->x3 = + selfX - width / 2 + + (((rcos(self->ext.playerBlink.unk82) >> 4) * 3) >> 0xC); + } else { + prim->x2 = prim->x3 = + selfX + width / 2 + + (((rcos(self->ext.playerBlink.unk82) >> 4) * 3) >> 0xC); + } + prim->y2 = prim->y3 = + (selfY + height / 2) - + ((rsin(self->ext.playerBlink.unk82) >> 4) * 3 << 1 >> 8); + prim->u2 = prim->u3 = xMargin + width / 2; + prim->v2 = prim->v3 = yMargin + height / 2; + } else { + if (self->facingLeft) { + prim->x0 = prim->x2 = (selfX - width) + 1; + prim->x1 = prim->x3 = selfX + 1; + } else { + prim->x0 = prim->x2 = selfX; + prim->x1 = prim->x3 = selfX + width; + } + prim->y0 = prim->y1 = selfY + height * i / 8; + prim->y2 = prim->y3 = selfY + height * (i + 1) / 8; + if (self->facingLeft) { + prim->u0 = prim->u2 = wSprite - 1; + prim->u1 = prim->u3 = xMargin - 1; + } else { + prim->u0 = prim->u2 = xMargin; + prim->u1 = prim->u3 = wSprite; + } + prim->v0 = prim->v1 = yMargin + height * i / 8; + prim->v2 = prim->v3 = yMargin + height * (i + 1) / 8; + } + angleRedIndex = dataPtr[0]; + angleGreenIndex = dataPtr[2]; + angleBlueIndex = dataPtr[1]; + redDivide = dataPtr[4]; + greenDivide = dataPtr[6]; + blueDivide = dataPtr[5]; + if (upperParams & 0x40) { + angle = D_us_80181A64[(i + angleRedIndex) % 8]; + prim->r0 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / redDivide; + angle = D_us_80181A64[(i + angleGreenIndex) % 8]; + prim->g0 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / greenDivide; + angle = D_us_80181A64[(i + angleBlueIndex) % 8]; + prim->b0 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / blueDivide; + angle = D_us_80181A64[(i + angleRedIndex + 1) % 8]; + prim->r1 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / redDivide; + angle = D_us_80181A64[(i + angleGreenIndex + 1) % 8]; + prim->g1 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / greenDivide; + angle = D_us_80181A64[(i + angleBlueIndex + 1) % 8]; + prim->b1 = ((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / blueDivide; + prim->r2 = prim->g2 = prim->b2 = prim->r3 = prim->g3 = prim->b3 = 0; + D_us_80181A64[i] += self->ext.playerBlink.unk8A; + } else { + angle = D_us_80181A64[(i + angleRedIndex) % 8]; + prim->r0 = prim->r1 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / redDivide); + angle = D_us_80181A64[(i + angleGreenIndex) % 8]; + prim->g0 = prim->g1 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / greenDivide); + angle = D_us_80181A64[(i + angleBlueIndex) % 8]; + prim->b0 = prim->b1 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / blueDivide); + angle = D_us_80181A64[(i + angleRedIndex + 1) % 8]; + prim->r2 = prim->r3 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / redDivide); + angle = D_us_80181A64[(i + angleGreenIndex + 1) % 8]; + prim->g2 = prim->g3 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / greenDivide); + angle = D_us_80181A64[(i + angleBlueIndex + 1) % 8]; + prim->b2 = prim->b3 = (((rsin(angle) + 0x1000) >> 6) * + self->ext.playerBlink.unk90 / blueDivide); + D_us_80181A64[i] += self->ext.playerBlink.unk8A; + } + prim->priority = RIC.zPriority + 2; + prim = prim->next; + } + if ((upperParams & 0x3F) == 0 || (upperParams & 0x3F) == 7) { + BO6_RicSetInvincibilityFrames(1, 10); + } +} diff --git a/src/boss/bo6/us_3E79C.c b/src/boss/bo6/us_3E79C.c new file mode 100644 index 0000000000..a9ca972737 --- /dev/null +++ b/src/boss/bo6/us_3E79C.c @@ -0,0 +1,1527 @@ +#include "bo6.h" + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", func_us_801BE79C); + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityShrinkingPowerUpRing); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityHitByIce); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityHitByLightning); + +extern EInit D_us_801804B4; +extern AnimationFrame D_us_80181E78[]; +extern s32 D_us_801D0850; +extern s32 D_us_801D0854[]; +extern s32 D_us_801D169C; + +// possibly ShaftOrb? +void func_us_801C03E8(Entity* self) { + Primitive* prim; // s0 + s32 i; // s1 + + u16 palette; // 0x6E(sp) + s32 temp_s0_5; // 0x68(sp) + s32 temp_v0_7; // 0x64(sp) + s32 var_v1_2; // 0x60(sp) + s32 var_a0_2; // 0x5C(sp) + + s32 distanceX; // 0x58(sp) + s32 distanceY; // 0x54(sp) + + s32 scale; // 0x50(sp) + s32 posX; // 0x4C(sp) + s32 posY; // 0x48(sp) + s32 ricPosX; // 0x44(sp) + s32 ricPosY; // s8 + + s32 anotherX; + s32 anotherY; + + s32 var_s4; // 0x38(sp) + s32 sp30; // 0x34(sp) + s32 j; // 0x30(sp) + s32 angle; // s7 + s32 distance; // s6 + s32 direction; // s5 + s32 primX; // s4 + s32 primY; // s3 + s32 temp_s0_2; // s2 + + scale = 4; + D_us_801D169C = 0; + var_s4 = 0; + sp30 = 0; + + if (self->flags & FLAG_DEAD) { + if (self->step < 0x14) { + D_us_801D169C = 1; + self->step = 0x14; + } + } else { +#ifdef VERSION_PSP + if ((self->hitFlags) && (self->step != 10)) { +#else + if ((self->hitFlags) && (self->step == 2)) { +#endif + self->ext.shaftOrb.unkTimer = 10; + self->step = 0xA; + } + self->hitFlags = 0; + } + + switch (self->step) { + case 0: + InitializeEntity(D_us_801804B4); + self->primIndex = g_api.AllocPrimitives(PRIM_GT4, 0x20); + + if (self->primIndex == -1) { + self->step = 0; + return; + } + prim = &g_PrimBuf[self->primIndex]; + + for (i = 0; i < 8; i++) { + prim->clut = 0x252; + prim->tpage = 0x12; + prim->u0 = prim->u2 = 0; + prim->u1 = prim->u3 = 0x1F; + prim->v0 = prim->v1 = 0; + prim->v2 = prim->v3 = 0x1F; + prim->priority = RIC.zPriority + 4; + prim->drawMode = DRAW_UNK_40 | DRAW_TPAGE2 | DRAW_TPAGE | + DRAW_HIDE | DRAW_COLORS | DRAW_UNK02 | DRAW_TRANSP; + D_us_801D0854[i] = 0; + prim = prim->next; + } + + for (i = 0; i < 24; i++) { + prim->priority = RIC.zPriority - 2; + prim->r0 = prim->g0 = prim->r1 = prim->g1 = 0x3F; + prim->b0 = prim->b1 = 0x7F; + prim->drawMode = + DRAW_TPAGE2 | DRAW_TPAGE | DRAW_HIDE | DRAW_UNK02 | DRAW_TRANSP; + prim->type = PRIM_LINE_G2; + prim = prim->next; + } + + self->flags |= FLAG_UNK_20000000 | FLAG_UNK_10000000 | FLAG_HAS_PRIMS; + self->posX.i.hi = 0x80; + self->posY.i.hi = 0x30; + self->ext.ILLEGAL.s16[4] = 0x400; + self->ext.ILLEGAL.s16[5] = 0x10; + self->ext.ILLEGAL.s16[6] = 0x30; + self->ext.ILLEGAL.s16[7] = 0xC00; + self->animSet = ANIMSET_OVL(5); + self->animCurFrame = 0; + self->unk5A = 0x48; + self->palette = 0x8252; + self->ext.ILLEGAL.s16[0] = self->hitboxState; + self->anim = D_us_80181E78; + self->zPriority = RIC.zPriority + 4; + self->step = 1; + self->opacity = 0; + self->drawFlags = FLAG_DRAW_OPACITY; + self->drawMode = DRAW_TPAGE2 | DRAW_TPAGE; + break; + + case 1: + self->opacity++; + if (self->opacity >= 0x80) { + self->drawFlags = FLAG_DRAW_DEFAULT; + self->drawMode = DRAW_DEFAULT; + self->step++; + } + break; + + case 2: + distanceX = RIC.posX.i.hi + RIC.hitboxOffX; + distanceY = (RIC.posY.i.hi + RIC.hitboxOffY) - 0x40; + + angle = self->ext.ILLEGAL.s16[4]; + + distanceX += (((rcos(angle) >> 4) * scale) >> 8); + distanceY -= (((rsin(angle) >> 4) * scale) >> 8); + self->ext.ILLEGAL.s16[4] += self->ext.ILLEGAL.s16[5]; + + posX = distanceX - self->posX.i.hi; + posY = distanceY - self->posY.i.hi; + angle = ratan2(-posY, posX) & 0xFFF; + + temp_s0_2 = (self->ext.ILLEGAL.s16[7] & 0xFFF); + var_v1_2 = abs(temp_s0_2 - angle); + + var_a0_2 = self->ext.ILLEGAL.s16[6]; + if (self->ext.ILLEGAL.s16[6] > var_v1_2) { + var_a0_2 = var_v1_2; + } + + if (temp_s0_2 < angle) { + if (var_v1_2 < 0x800) { + temp_s0_2 += var_a0_2; + } else { + temp_s0_2 -= var_a0_2; + } + } else { + if (var_v1_2 < 0x800) { + temp_s0_2 -= var_a0_2; + } else { + distance = var_a0_2; + temp_s0_2 += distance; + } + } + self->ext.ILLEGAL.s16[7] = temp_s0_2 & 0xFFF; + temp_s0_5 = rcos(temp_s0_2) * 0x10; + temp_v0_7 = rsin(temp_s0_2) * 0x10; + self->posX.val = temp_s0_5 + self->posX.val; + self->posY.val -= temp_v0_7; + break; + case 10: + if (g_Timer & 1) { + self->palette = 0x815F; + } else { + self->palette = 0x8168; + } + + self->poseTimer++; + if (--self->ext.shaftOrb.unkTimer == 0) { + self->step = 2; + } + + break; + case 20: + BO6_RicCreateEntFactoryFromEntity(self, 0x49, 0); + BO6_RicCreateEntFactoryFromEntity(self, 0x4B, 0); + self->step++; + break; + case 21: + DestroyEntity(self); + return; + } + + if (g_api.CheckEquipmentItemCount(0x22U, 1U) != 0) { + palette = 0x8252; + self->hitboxState = self->ext.ILLEGAL.s16[0]; + self->ext.ILLEGAL.s16[1] = 1; + } else { + palette = 0x810D; + self->hitboxState = 0; + self->ext.ILLEGAL.s16[1] = 0; + } + if (RIC.step == PL_S_DEAD || RIC.step == PL_S_ENDING_1) { + self->hitboxState = 0; + } + if (self->step != PL_S_9) { + self->palette = palette; + } + if (!(g_Timer % 4) && (self->step == 2)) { + D_us_801D0850++; + D_us_801D0850 %= 8; + var_s4 = 1; + } + + if (g_Timer % 0x100 == 0) { + if (self->step == 2) { + if ((abs(self->posX.i.hi - RIC.posX.i.hi) < 0x20) && + (RIC.step != PL_S_DEAD)) { + sp30 = 1; + if (self->ext.ILLEGAL.s16[1]) { + BO6_RicCreateEntFactoryFromEntity(self, 0x590021, 0); + } + } + } + } + + posX = self->posX.i.hi; + posY = self->posY.i.hi; + prim = &g_PrimBuf[self->primIndex]; + + for (i = 0; i < 8; i++) { + if (D_us_801D0854[i] == 0) { + if ((var_s4 != 0) && (D_us_801D0850 == i)) { + prim->x0 = prim->x2 = posX - 0x10; + prim->x1 = prim->x3 = posX + 0xF; + prim->y0 = prim->y1 = posY - 0x10; + prim->y2 = prim->y3 = posY + 0xF; + prim->drawMode &= ~DRAW_HIDE; + prim->r0 = prim->r1 = prim->r2 = prim->r3 = prim->g0 = + prim->g1 = prim->g2 = prim->g3 = prim->b0 = prim->b1 = + prim->b2 = prim->b3 = 0x80; + D_us_801D0854[i] += 1; + } + } else { + prim->b3 -= 4; + if (prim->b3 < 0x10) { + D_us_801D0854[i] = 0; + } + prim->r0 = prim->r1 = prim->r2 = prim->r3 = prim->g0 = prim->g1 = + prim->g2 = prim->g3 = prim->b0 = prim->b1 = prim->b2 = prim->b3; + } + if (!self->ext.ILLEGAL.s16[1]) { + prim->drawMode |= DRAW_HIDE; + } + prim = prim->next; + } + + ricPosX = RIC.posX.i.hi; + ricPosY = RIC.posY.i.hi; + + if (g_Timer & 1) { + direction = -1; + } else { + direction = 1; + } + posX -= 3; + + for (j = 0; j < 3; j++, posX += 3) { + primX = posX; + primY = posY; + distance = 3; + // > lw a1, 0x48(sp) + anotherY = ricPosY - posY; + anotherX = ricPosX - posX; + angle = ratan2(-anotherY, anotherX); + distance = (SquareRoot12(I_TO_FLT( + (anotherX * anotherX) + (anotherY * anotherY))) / + 7); + distance = FLT_TO_I(distance); + + for (i = 0; i < 8; i++) { + direction = -direction; + if (prim->r2 == 0) { + if (sp30 != 0) { + prim->r2++; + prim->b2 = 0xC; + prim->drawMode &= ~DRAW_HIDE; + } + } else if (--prim->b2 == 0) { + prim->drawMode |= DRAW_HIDE; + prim->r2 = 0; + } + prim->x0 = primX; + prim->y0 = primY; + temp_s0_2 = angle + (rand() & 0x1FF) * direction; + prim->x1 = (((rcos(temp_s0_2) >> 4) * distance) >> 8) + primX; + prim->y1 = -(((rsin(temp_s0_2) >> 4) * distance) >> 8) + primY; + primX = prim->x1; + primY = prim->y1; + + if (i == 7) { + prim->x1 = ricPosX; + prim->y1 = ricPosY; + } + if (!self->ext.ILLEGAL.s16[1]) { + prim->drawMode |= DRAW_HIDE; + } + prim = prim->next; + } + } +#ifndef VERSION_PSP + FntPrint("tama_step:%02x\n", self->step); +#endif +} + +#ifdef VERSION_PSP +extern s32 D_pspeu_0927BAF8; +#define E_ID_17 D_pspeu_0927BAF8 +#else +#endif + +extern s32 g_CutsceneFlags; + +// TODO: I AM SHAFT! +void EntityShaft(Entity* self) { + Entity* entity; + FntPrint("I AM SHAFT\n"); + switch (self->step) { + case 0: +#ifdef VERSION_PSP + self->flags = FLAG_UNK_10000000 | FLAG_HAS_PRIMS; +#else + self->flags = FLAG_UNK_10000000; +#endif + self->animSet = -0x7FFB; + self->animCurFrame = 0x8B; + self->unk5A = 0x48; + self->palette = PAL_OVL(0x250); + self->zPriority = RIC.zPriority + 2; + self->opacity = 0; + self->drawFlags = FLAG_DRAW_OPACITY; + self->drawMode = DRAW_TPAGE2 | DRAW_TPAGE; + self->step++; + break; + + case 1: + self->opacity += 4; + if (self->opacity > 48) { + self->step++; + entity = &g_Entities[0xC8]; + CreateEntityFromCurrentEntity(E_ID_17, entity); + entity->params = 3; + self->ext.ILLEGAL.s16[0] = 0x100; + } + self->posY.val += rsin(self->ext.ILLEGAL.s16[1]) * 4; + self->ext.ILLEGAL.s16[1] += 0x20; + break; + + case 2: + if (!self->ext.ILLEGAL.s16[0]) { + if ((g_CutsceneFlags & 0x40) || (g_DemoMode != Demo_None)) { + self->drawFlags |= FLAG_DRAW_SCALEY | FLAG_DRAW_SCALEX; + self->scaleX = self->scaleY = 0x100; + self->step++; + } + } else { + self->ext.ILLEGAL.s16[0]--; + } + self->posY.val += rsin(self->ext.ILLEGAL.s16[1]) * 4; + self->ext.ILLEGAL.s16[1] += 0x20; + break; + + case 3: + self->scaleX -= 0x20; + if (self->scaleX < 0x10) { + self->scaleX = 0x10; + } + + self->scaleY += 64; + if (self->scaleY > 0x800) { + self->scaleY = 0x800; + } + self->opacity += 6; + if (self->opacity > 0xF0) { + self->step++; + } + break; + + case 4: + self->opacity -= 3; + if (self->opacity < 4) { + DestroyEntity(self); + } + break; + } +} + +// TODO: rename ShaftOrb +Entity* BO6_RicGetFreeEntity(s16, s16); +extern u8 D_us_80181E9C[]; + +void func_us_801C0FE8(Entity* self) { + Entity* entity; + Primitive* prim; + s32 posX; + s32 posY; + s32 accelX; + s32 accelY; + s16 primIndex; + s16 params; + s32 velocity; + + params = self->params & 0xFF; + switch (self->step) { + case 0: + self->primIndex = g_api.AllocPrimitives(PRIM_GT4, 1); + if (self->primIndex == -1) { + DestroyEntity(self); + return; + } + prim = &g_PrimBuf[self->primIndex]; + prim->clut = 0x252; + prim->tpage = 0x12; + + // temp_a1 = &D_us_80181E9C[temp_a0]; + prim->u0 = prim->u2 = D_us_80181E9C[params * 2] - 2; + prim->u1 = prim->u3 = D_us_80181E9C[params * 2] + 2; + + prim->v0 = prim->v1 = D_us_80181E9C[params * 2 + 1] - 2; + prim->v2 = prim->v3 = D_us_80181E9C[params * 2 + 1] + 2; + + prim->priority = RIC.zPriority + 4; + prim->drawMode = DRAW_UNK02; + + accelX = D_us_80181E9C[params * 2] - 16; + accelY = D_us_80181E9C[params * 2 + 1] - 16; + self->posX.i.hi += accelX; + self->posY.i.hi += accelY; + + velocity = ratan2(-accelY, accelX); + velocity += ((rand() & 0x7F) - 0x40); + self->ext.shaftOrb.velocityAngle = velocity; + self->flags = FLAG_UNK_10000000 | FLAG_HAS_PRIMS; + self->ext.shaftOrb.timer = 8; + self->step++; + break; + + case 1: + if (--self->ext.shaftOrb.timer == 0) { + self->ext.shaftOrb.timer = 16; + velocity = self->ext.shaftOrb.velocityAngle; + self->velocityX = (rcos(velocity) * 32) + (rand() & 0xF); + self->velocityY = -((rsin(velocity) * 32) + (rand() & 0xF)); + self->step++; + } + break; + + case 2: + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + if (--self->ext.shaftOrb.timer == 0) { + BO6_RicCreateEntFactoryFromEntity(self, 0x4A, 0); + self->velocityY = (rand() & 0x7FFF) + 0xFFFF0000; + self->velocityX = self->velocityX >> 2; + self->ext.shaftOrb.timer = 1; + self->step++; + } + break; + case 3: + if ((self->ext.shaftOrb.timer % 4) == 0) { + entity = BO6_RicGetFreeEntity(0x50, 0x8F); + if (entity != NULL) { + DestroyEntity(entity); + entity->entityId = 0x43; + entity->params = 0x100; + // not shaft orb + entity->ext.shaftOrb.parent = self->ext.shaftOrb.parent; + entity->posX.val = self->posX.val; + entity->posY.val = self->posY.val; + } + } + self->ext.shaftOrb.timer += 1; + self->velocityY += 0xC00; + self->posY.val += self->velocityY; + self->posX.val += self->velocityX; + self->flags &= ~FLAG_UNK_10000000; + break; + } + + posX = self->posX.i.hi; + posY = self->posY.i.hi; + prim = &g_PrimBuf[self->primIndex]; + prim->x0 = prim->x2 = posX - 2; + prim->x1 = prim->x3 = posX + 2; + prim->y0 = prim->y1 = posY - 2; + prim->y2 = prim->y3 = posY + 2; +} + +extern AnimationFrame D_us_80181EDC[]; + +void func_us_801C13A8(Entity* self) { + s16 params = self->params & 0x7F00; + switch (self->step) { + case 0: + self->flags = FLAG_UNK_20000000 | FLAG_POS_CAMERA_LOCKED; + self->unk5A = 0x79; + self->animSet = 0xE; + self->zPriority = RIC.zPriority + 6; + self->palette = PAL_OVL(0x25E); + self->drawMode = DRAW_UNK_40 | DRAW_TPAGE2 | DRAW_TPAGE; + self->drawFlags = FLAG_DRAW_SCALEY | FLAG_DRAW_SCALEX; + self->scaleX = self->scaleY = 0xC0; + self->anim = D_us_80181EDC; + if (params) { + self->scaleX = self->scaleY = 0x80; + self->anim = D_us_80181EDC; + } + self->velocityY = -FIX(0.25); + self->step++; + break; + + case 1: + self->posY.val += self->velocityY; + if (self->poseTimer < 0) { + DestroyEntity(self); + } + break; + } +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityWhip); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityArmBrandishWhip); + +extern s16 D_us_80182870[]; +// same as `ric` `func_80167964` except `g_Ric`/`g_Player` reference and lookup table +void func_us_801C2688(Entity* entity) { + if (g_Ric.unk46 == 0) { + DestroyEntity(entity); + return; + } + if (entity->step == 0) { + entity->flags = 0x18000000; + } + if (!(entity->params & 0xFF00)) { + g_Entities[D_us_80182870[entity->poseTimer]].palette = PAL_OVL(0x240); + } + g_Entities[D_us_80182870[entity->poseTimer]].ext.player.unkA4 = 4; + entity->poseTimer++; + if (entity->poseTimer == 15) { + DestroyEntity(entity); + } +} + +void func_us_801C277C(void) {} + +void func_us_801C2784(void) {} + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnHolyWaterBreakGlass); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashHydroStorm); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_DebugShowWaitInfo); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_DebugInputWait); + +s32 OVL_EXPORT(RicCheckHolyWaterCollision)(s16 height, s16 width) { + Collider collider; + Collider collider2; + s16 maskedEffects; + s16 maskedEffects2; + s16 posX; + s16 posY; + s16 newPosY; + + if (((g_CurrentEntity->posX.val + width) < 0) || + (((s16) g_CurrentEntity->posX.i.hi + width) > 0x100)) { + if ((g_CurrentEntity->posY.i.hi + height) >= 0xD4) { + g_CurrentEntity->posY.i.hi = 0xD4 - height; + return 1; + } + return 0; + } + posX = g_CurrentEntity->posX.i.hi + width; + posY = g_CurrentEntity->posY.i.hi + height; + + g_api.CheckCollision(posX, posY, &collider, 0); + maskedEffects = collider.effects & 0xF801; + posY = posY - 1 + collider.unk18; + g_api.CheckCollision(posX, posY, &collider2, 0); + + newPosY = height + ((s16) g_CurrentEntity->posY.i.hi + collider.unk18); + if ((maskedEffects & 0x8801) == 1 || + (maskedEffects & 0x8801) == 0x801) { + maskedEffects = collider2.effects & 0xF001; + if (!(maskedEffects & 1)) { + g_CurrentEntity->posY.i.hi = newPosY; + return 1; + } + if (((s32) collider2.effects & 0x8001) == 0x8001) { + g_CurrentEntity->posY.i.hi = newPosY - 1 + collider2.unk18; + return maskedEffects; + } + return 0; + } + if ((maskedEffects & 0x8001) == 0x8001) { + g_CurrentEntity->posY.i.hi = newPosY; + return maskedEffects & 0xF001; + } + return 0; +} + + +int WarpBackgroundBrightness() { return 0; } + +extern EInit D_us_80180460; + +void OVL_EXPORT(RicEntitySubwpnHolyWater)(Entity* self) { + s16 xMod; + s32 colRes; + + if (self->step > 2) { + self->posY.i.hi += 5; + } + switch (self->step) { + case 0: + self->ext.holywater.subweaponId = PL_W_HOLYWATER; + InitializeEntity(D_us_80180460); + self->flags = 0x08000000; + self->animSet = ANIMSET_OVL(3); + self->animCurFrame = 0x23; + self->zPriority = RIC.zPriority + 2; + self->unk5A = 0x24; + self->palette = PAL_OVL(0x22F); + xMod = 0; + if (self->facingLeft) { + xMod = -xMod; + } + self->posX.i.hi += xMod; + self->posY.i.hi += -16; + self->ext.holywater.angle = (rand() & 0x7F) + 0xDC0; + if (RIC.facingLeft == 1) { + self->ext.holywater.angle = (rand() & 0x7F) + 0x9C0; + } + self->velocityX = (FLT_TO_FIX(rcos(self->ext.holywater.angle)) * 0x600) >> 8; + self->velocityY = + -(FLT_TO_FIX(rsin(self->ext.holywater.angle)) * 0x600) >> 8; + self->hitboxWidth = 4; + self->hitboxHeight = 4; + self->ext.holywater.unk80 = 0x200; + self->step = 1; + break; + + case 1: + self->posY.val += self->velocityY; + colRes = BO6_RicCheckHolyWaterCollision(0, 0); + self->posX.val += self->velocityX; + + if ((colRes & 1) || (self->hitFlags != 0)) { + BO6_RicCreateEntFactoryFromEntity(self, 0x28, 0); + g_api.PlaySfx(SFX_GLASS_BREAK_WHOOSH); + self->ext.ILLEGAL.u16[0] = 0x50; + self->animSet = 0; + self->step = 3; + self->velocityX = (s32) self->velocityX >> 2; + } else if (self->flags & 0x100) { + BO6_RicCreateEntFactoryFromEntity(self, 0x28, 0); + g_api.PlaySfx(SFX_GLASS_BREAK_WHOOSH); + self->ext.ILLEGAL.u16[0] = 0x50; + self->animSet = 0; + self->step = 3; + self->velocityX = -((s32) self->velocityX >> 2); + } + break; + case 2: + if (self->flags & 0x100) { + DestroyEntity(self); + return; + } + if (--self->ext.holywater.timer == 0) { + self->velocityX = self->velocityX >> 2; + self->ext.holywater.timer = 0x50; + self->step++; + } + break; + case 3: + if (self->flags & 0x100) { + self->velocityX = 0; + } + if (!(self->ext.holywater.timer & 3)) { + BO6_RicCreateEntFactoryFromEntity( + self, FACTORY(BP_HOLYWATER_FIRE, self->ext.holywater.unk82), 0); + self->ext.holywater.unk82 += 1; + self->velocityX -= (self->velocityX / 32); + } + + self->posX.val += self->velocityX; + colRes = OVL_EXPORT(RicCheckHolyWaterCollision)(6, 0); + if (!(colRes & 1)) { + self->velocityX = self->velocityX >> 1; + self->step++; + } + break; + case 4: + if (self->flags & 0x100) { + self->velocityX = 0; + } + + if (!(self->ext.holywater.timer & 3)) { + BO6_RicCreateEntFactoryFromEntity( + self, FACTORY(BP_HOLYWATER_FIRE, self->ext.holywater.unk82), 0); + self->ext.holywater.unk82 += 1; + } + self->velocityY += FIX(12.0 / 128); + if (self->velocityY > FIX(4)) { + self->velocityY = FIX(4); + } + self->posY.val += self->velocityY; + colRes = BO6_RicCheckHolyWaterCollision(0, 0); + self->posX.val += self->velocityX; + xMod = 4; + if (self->velocityX < 0) { + xMod = -xMod; + } + colRes |= WarpBackgroundBrightness(-7, xMod); + if (colRes & 1) { + self->velocityX <<= 1; + self->step--; + } + break; + case 5: + break; + } + + if (self->step > 2) { + if (--self->ext.holywater.timer < 0) { + DestroyEntity(self); + return; + } + if (self->ext.holywater.timer == 2) { + self->step = 5; + } + self->posY.i.hi -= 5; + self->animCurFrame = 0; + } + g_Ric.timers[PL_T_3] = 2; + self->hitFlags = 0; + self->flags &= ~0x100; + FntPrint("judge:%02x\n", self->hitboxState); +} + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnHolyWaterFlame); + +// split pl_subweapon_cross + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnCrashCross); + +extern EInit D_us_80180454; +extern s16 D_us_801D10C8; + +extern AnimationFrame anim_cross_boomerang[]; +#if defined(VERSION_PSP) +extern Point16 D_us_801D08C4[4][128]; +extern s32 D_us_801D10C4; +#else +extern Point16 D_us_801D08C4[4][128]; +extern s32 D_us_801D10C4; +#endif + +void OVL_EXPORT(RicEntitySubwpnCross)(Entity* self) { + s16 playerHitboxX; + s16 playerHitboxY; + s16 rotate; + s16* psp_s1; + s32 xAccel; + + rotate = self->rotate; + switch (self->step) { + case 0: + self->ext.crossBoomerang.subweaponId = PL_W_CROSS; + InitializeEntity(D_us_80180454); + self->flags = 0x38000000; + D_us_801D10C8 = self->hitboxState; + // gets used by shadow, must align with that entity + self->ext.crossBoomerang.unk84 = D_us_801D08C4[D_us_801D10C4]; + D_us_801D10C4++; + D_us_801D10C4 &= 3; + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, BP_5, 0); + self->animSet = ANIMSET_OVL(4); + self->unk5A = 0x44; + self->anim = anim_cross_boomerang; + self->facingLeft = RIC.facingLeft; + self->zPriority = RIC.zPriority; + OVL_EXPORT(RicSetSpeedX)(FIX(3.5625)); + self->drawFlags = FLAG_DRAW_ROTATE; + self->rotate = 0xC00; + self->hitboxWidth = 8; + self->hitboxHeight = 8; + self->posY.i.hi -= 8; + g_api.PlaySfx(SFX_THROW_WEAPON_MAGIC); + self->step = 1; + break; + case 1: + if (RIC.pose == 1) { + self->step++; + } + case 2: + // First phase. We spin at 0x80 angle units per frame. + // Velocity gets decremented by 1/16 per frame until we slow + // down to less than 0.75. + self->rotate -= 0x80; + self->posX.val += self->velocityX; + if (self->facingLeft) { + xAccel = FIX(-1.0 / 16); + } else { + xAccel = FIX(1.0 / 16); + } + self->velocityX -= xAccel; + + if (abs(self->velocityX) < FIX(0.75)) { + self->step = 3; + } + + if ((self->hitFlags == 2) || (self->flags & 0x100)) { + if (self->velocityX < 0) { + self->velocityX = -0x800; + } else { + self->velocityX = 0x800; + } + self->ext.crossBoomerang.timer = 0x1E; + self->step = 3; + self->ext.crossBoomerang.timer = 0x10; + self->hitboxState = 0; + } + + break; + case 3: + // Second phase. Once we are slow, we spin twice as fast, and then + // wait until our speed gets higher once again (turned around). + self->rotate -= 0x100; + self->posX.val += self->velocityX; + if (self->facingLeft) { + xAccel = FIX(-1.0 / 16); + } else { + xAccel = FIX(1.0 / 16); + } + if (self->hitFlags == 2 || (self->flags & 0x100)) { + if (self->facingLeft) { + xAccel = FIX(-1.0 / 16); + } else { + xAccel = FIX(1.0 / 16); + } + } + self->velocityX -= xAccel; + if (abs(self->velocityX) > FIX(0.75)) { + self->step++; + } + break; + case 4: + // Third phase. We've now sped up and we're coming back. + // Increase speed until a terminal velocity of 2.5. + if (self->facingLeft) { + xAccel = FIX(-1.0 / 16); + } else { + xAccel = FIX(1.0 / 16); + } + self->velocityX -= xAccel; + if (abs(self->velocityX) > FIX(2.5)) { + self->hitboxState = D_us_801D10C8; + self->step++; + } + case 5: + if (--self->ext.crossBoomerang.timer < 0 && ((self->hitFlags == 2) || (self->flags & 0x100))) { + self->velocityY = -0x30000; + self->ext.ILLEGAL.u16[0] = 0x32; + self->hitboxState = 0; + self->step = 6; + self->velocityX = -((s32) self->velocityX / 2); + } + + // Now we check 2 conditions. If we're within the player's hitbox... + playerHitboxX = (RIC.posX.i.hi + RIC.hitboxOffX); + playerHitboxY = (RIC.posY.i.hi + RIC.hitboxOffY); + if (abs(self->posX.i.hi - playerHitboxX) < + RIC.hitboxWidth + self->hitboxWidth && + abs(self->posY.i.hi - playerHitboxY) < + RIC.hitboxHeight + self->hitboxHeight) { + // ... Then we go to step 7 to be destroyed. + self->step = 7; + self->ext.crossBoomerang.timer = 0x20; + return; + } + // Alternatively, if we're offscreen, we will also be destroyed. + if ((self->facingLeft == 0 && self->posX.i.hi < -0x20) || + (self->facingLeft && self->posX.i.hi > 0x120)) { + self->step = 7; + self->ext.crossBoomerang.timer = 0x20; + return; + } + // Otherwise, we keep trucking. spin at the slower rate again. + self->rotate -= 0x80; + self->posX.val += self->velocityX; + break; + case 6: + if (--self->ext.crossBoomerang.timer == 0) { + DestroyEntity(self); + return; + } + self->velocityY += 0x2800; + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + self->rotate += 0x180; + break; + case 7: + if (--self->ext.crossBoomerang.timer == 0) { + DestroyEntity(self); + return; + } + self->hitboxState = 0; + self->animSet = 0; + self->posX.val += self->velocityX; + break; + } + // We will increment through these states, creating trails. + // Factory 3 is entity #4, func_80169C10. Appears to make tiny sparkles. + // Factory 4 is entity #5, RicEntityHitByCutBlood. Appears to make a + // "shadow" of the cross boomerang. + self->ext.crossBoomerang.unk7E++; + if (1 < self->step && self->step < 6) { + if ((self->ext.crossBoomerang.unk7E & 0xF) == 1) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, BP_SUBWPN_CROSS_PARTICLES, 0); + } + if ((self->ext.crossBoomerang.unk7E & 0xF) == 4) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, FACTORY(BP_EMBERS, 6), 0); + } + if ((self->ext.crossBoomerang.unk7E & 0xF) == 6) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, BP_SUBWPN_CROSS_PARTICLES, 0); + } + if ((self->ext.crossBoomerang.unk7E & 0xF) == 8) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, FACTORY(BP_EMBERS, 6), 0); + } + if ((self->ext.crossBoomerang.unk7E & 0xF) == 12) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, FACTORY(BP_EMBERS, 6), 0); + } + if ((self->ext.crossBoomerang.unk7E & 0xF) == 11) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, BP_SUBWPN_CROSS_PARTICLES, 0); + } + } + // Applies a flickering effect + if ((g_GameTimer >> 1) & 1) { + self->palette = PAL_OVL(0x1B0); + } else { + self->palette = PAL_OVL(0x1B1); + } + psp_s1 = (s16*)self->ext.crossBoomerang.unk84; + psp_s1 = &psp_s1[self->ext.crossBoomerang.unk80 * 2]; + *psp_s1 = self->posX.i.hi + g_Tilemap.scrollX.i.hi; + psp_s1++; + *psp_s1 = self->posY.i.hi + g_Tilemap.scrollY.i.hi; + self->ext.crossBoomerang.unk80++; + self->ext.crossBoomerang.unk80 &= 0x3F; + rotate ^= self->rotate; + g_Ric.timers[PL_T_3] = 2; + self->hitFlags = 0; + self->flags &= ~0x100; +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", func_us_801C488C); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnCrossTrail); + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnCrashCrossParticles); + +// split pl_subweapons + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnThrownAxe); + +extern EInit D_us_80180490; +extern u8 D_us_8018299C[]; + +void OVL_EXPORT(RicEntityCrashAxe)(Entity* self) { + Primitive* primFirst; + Primitive* prim; + s16 angle1; + s16 angle2; + s16 angle3; + s16 angle4; + s32 mod; + s32 i; + u8 r; + u8 g; + u8 b; + s16 angleMod; + s16 x; + s16 y; + s16 angle; + s32 pose; + s32 velocity; + s32 colorRef; + + mod = 21; + switch (self->step) { + case 0: + self->ext.subwpnAxe.subweaponId = 2; + InitializeEntity(D_us_80180490); + self->primIndex = g_api.AllocPrimitives(PRIM_GT4, 5); + if (self->primIndex == -1) { + DestroyEntity(self); + return; + } + self->flags = 0x18800000; + self->facingLeft = 0; + self->ext.subwpnAxe.unk7C = ((self->params & 0xFF) << 9) + 0xC00; + self->posY.i.hi -= 12; + prim = &g_PrimBuf[self->primIndex]; + i = 0; + while (prim) { + prim->tpage = 0x1C; + prim->u0 = prim->v0 = prim->v1 = prim->u2 = 0; + prim->u1 = prim->u3 = 0x18; + prim->v2 = prim->v3 = 0x28; + prim->priority = RIC.zPriority + 4; + if (i != 0) { + prim->drawMode = DRAW_UNK_100 | DRAW_TPAGE2 | DRAW_TPAGE | + DRAW_HIDE | DRAW_COLORS | DRAW_TRANSP; + self->ext.subwpnAxe.unk8C[i - 1] = 0; + self->ext.subwpnAxe.unk90[i - 1] = 0; + self->ext.subwpnAxe.unk94[i - 1] = 0; + } else { + prim->drawMode = DRAW_UNK_100 | DRAW_HIDE; + } + i++; + prim = prim->next; + } + self->hitboxWidth = 12; + self->hitboxHeight = 12; + self->ext.subwpnAxe.angle = (self->params & 0xFF) << 9; + self->ext.subwpnAxe.velocity = 16; + self->step = 1; + break; + case 1: + velocity = self->ext.subwpnAxe.velocity; + self->ext.subwpnAxe.velocity++; + if (self->ext.subwpnAxe.velocity > 0x28) { + self->ext.subwpnAxe.unkA2 = 16; + self->step++; + } + angle = self->ext.subwpnAxe.angle; + self->ext.subwpnAxe.angle += 0xC0; + self->ext.subwpnAxe.unk7C += 0x80; + self->velocityX = velocity * rcos(angle); + self->velocityY = velocity * -rsin(angle); + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + break; + case 2: + if (--self->ext.subwpnAxe.unkA2 == 0) { + self->ext.subwpnAxe.unkA2 = 8; + self->step++; + } + velocity = self->ext.subwpnAxe.velocity; + angle = self->ext.subwpnAxe.angle; + self->ext.subwpnAxe.angle += 0xC0; + self->ext.subwpnAxe.unk7C += 0x80; + self->velocityX = rcos(angle) * velocity; + self->velocityY = -rsin(angle) * velocity; + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + break; + case 3: + if (--self->ext.subwpnAxe.unkA2 == 0) { + g_Ric.unk4E = 1; + self->flags &= 0xEFFFFFFF; + } + velocity = self->ext.subwpnAxe.velocity; + self->ext.subwpnAxe.velocity += 2; + angle = self->ext.subwpnAxe.angle; + self->ext.subwpnAxe.angle += 0x28; + self->ext.subwpnAxe.unk7C += 0x80; + self->velocityX = rcos(angle) * velocity; + self->velocityY = -rsin(angle) * velocity; + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + if (self->poseTimer == 0) { + pose = self->pose; + self->ext.subwpnAxe.unk8C[pose] = 0; + self->ext.subwpnAxe.unk90[pose] = 1; + self->ext.subwpnAxe.unk94[pose] = 1; + pose++; + pose &= 3; + self->pose = pose; + self->poseTimer = 2; + } else { + self->poseTimer--; + } + if ((self->hitFlags == 2) || (self->flags & 0x100)) { + self->velocityY = -0x30000; + self->hitboxState = 0; + self->step = 4; + self->velocityX = -((s32) self->velocityX / 2); + } + + break; + case 4: + if (self->facingLeft) { + angleMod = 0xC0; + } else { + angleMod = -0xC0; + + } + self->ext.subwpnAxe.unk7C += angleMod; + self->velocityY += 0x2400; + if (self->velocityY > 0x80000) { + self->velocityY = 0x80000; + } + self->posY.val += self->velocityY; + self->posX.val += self->velocityX; + if (self->posY.i.hi > 256) { + DestroyEntity(self); + return; + } + break; + } + + prim = &g_PrimBuf[self->primIndex]; + primFirst = prim; + pose = ((g_GameTimer >> 1) & 1) + 0x1AB; + i = 0; + while (prim != NULL) { + prim->clut = pose; + if (i == 0) { + if (self->facingLeft) { + angle1 = 0x800 - 0x2A0; + angle2 = 0x2A0; + angle3 = 0x800 + 0x2A0; + angle4 = 0x800 + 0x800 - 0x2A0; + } else { + angle2 = 0x800 - 0x2A0; + angle1 = 0x2A0; + angle4 = 0x800 + 0x2A0; + angle3 = 0x800 + 0x800 - 0x2A0; + } + x = self->posX.i.hi; + y = self->posY.i.hi; + angleMod = self->ext.subwpnAxe.unk7C; + angle1 += angleMod; + angle2 += angleMod; + angle3 += angleMod; + angle4 += angleMod; + + prim->x0 = x + +(((rcos(angle1) << 4) * mod) >> 0x10); + prim->y0 = y + -(((rsin(angle1) << 4) * mod) >> 0x10); + prim->x1 = x + +(((rcos(angle2) << 4) * mod) >> 0x10); + prim->y1 = y + -(((rsin(angle2) << 4) * mod) >> 0x10); + prim->x2 = x + +(((rcos(angle3) << 4) * mod) >> 0x10); + prim->y2 = y + -(((rsin(angle3) << 4) * mod) >> 0x10); + prim->x3 = x + +(((rcos(angle4) << 4) * mod) >> 0x10); + prim->y3 = y + -(((rsin(angle4) << 4) * mod) >> 0x10); + prim->drawMode &= ~DRAW_HIDE; + } else if (self->ext.subwpnAxe.unk90[i - 1]) { + if (self->ext.subwpnAxe.unk94[i - 1]) { + self->ext.subwpnAxe.unk94[i - 1] = 0; + prim->x0 = primFirst->x0; + prim->y0 = primFirst->y0; + prim->x1 = primFirst->x1; + prim->y1 = primFirst->y1; + prim->x2 = primFirst->x2; + prim->y2 = primFirst->y2; + prim->x3 = primFirst->x3; + prim->y3 = primFirst->y3; + } + colorRef = (self->ext.subwpnAxe.unk8C[i - 1]++); + if (colorRef < 10) { + r = D_us_8018299C[colorRef * 4 + 0]; + g = D_us_8018299C[colorRef * 4 + 1]; + b = D_us_8018299C[colorRef * 4 + 2]; + prim->r0 = r; + prim->g0 = g; + prim->b0 = b; + prim->r1 = r; + prim->g1 = g; + prim->b1 = b; + prim->r2 = r; + prim->g2 = g; + prim->b2 = b; + prim->r3 = r; + prim->g3 = g; + prim->b3 = b; + prim->drawMode &= ~DRAW_HIDE; + } else { + self->ext.subwpnAxe.unk90[i - 1] = 0; + prim->drawMode |= DRAW_HIDE; + } + } + i++; + prim = prim->next; + } +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnKnife); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_ReboundStoneBounce1); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_ReboundStoneBounce2); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnReboundStone); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnThrownVibhuti); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_PrimDecreaseBrightness); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnAgunea); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityAguneaHitEnemy); + +extern AnimationFrame D_us_801829D4[]; + +void BO6_RicEntityVibhutiCrashCloud(Entity* self) { + s32 angle; + + switch (self->step) { + case 0: + self->primIndex = g_api.AllocPrimitives(PRIM_GT4, 1); + if (self->primIndex == -1) { + DestroyEntity(self); + return; + } + + self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_HAS_PRIMS; + self->posX.val = self->ext.vibCrashCloud.parent->ext.vibhutiCrash.x; + self->posY.val = self->ext.vibCrashCloud.parent->ext.vibhutiCrash.y; + self->facingLeft = + self->ext.vibCrashCloud.parent->ext.vibhutiCrash.facing; + self->flags |= FLAG_UNK_20000000; + self->unk5A = 0x64; + self->animSet = 0xE; + self->palette = PAL_OVL(0x19E); + self->anim = D_us_801829D4; + self->drawMode = DRAW_TPAGE2 | DRAW_TPAGE; + self->drawFlags = FLAG_DRAW_OPACITY; + self->opacity = 0x60; + self->hitboxWidth = 8; + self->hitboxHeight = 8; + + angle = (rand() % 512) + 0x300; + self->velocityX = rcos(angle) << 5; + self->velocityY = -(rsin(angle) << 5); + self->step++; + break; + + case 1: + self->ext.vibCrashCloud.unk7C++; + if (self->ext.vibCrashCloud.unk7C > 38) { + DestroyEntity(self); + } else { + self->posX.val += self->velocityX; + self->posY.val += self->velocityY; + } + break; + } +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashVibhuti); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", func_us_801C8590); + +extern s32 D_us_80182A0C[]; + +// same as RIC func_8016D9C4 +void func_us_801C8618(Entity* self) { + PrimLineG2* prim; + Primitive* prim2; + s32 i; + long angle; + s32 var_s6; + s32 var_s5; + s32 var_s7; + s32 brightness; + + switch (self->step) { + case 0: + self->primIndex = g_api.AllocPrimitives(PRIM_LINE_G2, 20); + if (self->primIndex == -1) { + DestroyEntity(self); + return; + } + self->flags = 0x10800000; + prim = (PrimLineG2*)&g_PrimBuf[self->primIndex]; + for (i = 0; i < 4; i++) { + prim->preciseX.val = RIC.posX.val; + prim->preciseY.val = RIC.posY.val - FIX(40); + prim->priority = 194; + prim->drawMode = DRAW_HIDE; + prim->x0 = prim->x1 = RIC.posX.i.hi; + prim->y0 = prim->y1 = RIC.posY.i.hi - 0x1C; + prim->r0 = prim->g0 = prim->b0 = 0x80; + prim->r1 = prim->g1 = prim->b1 = 0x70; + prim->angle = D_us_80182A0C[i]; + prim->delay = 1; + prim = (PrimLineG2*)prim->next; + } + for (brightness = 0x80; i < 20; i++) { + if (!(i % 4)) { + brightness -= 0x10; + switch (i / 4) { + case 1: + self->ext.et_8016D9C4.lines[0] = prim; + break; + case 2: + self->ext.et_8016D9C4.lines[1] = prim; + break; + case 3: + self->ext.et_8016D9C4.lines[2] = prim; + break; + case 4: + self->ext.et_8016D9C4.lines[3] = prim; + break; + } + } + prim->priority = 0xC2; + prim->drawMode = DRAW_HIDE; + prim->x0 = prim->x1 = RIC.posX.i.hi; + prim->y0 = prim->y1 = RIC.posY.i.hi - 0x1C; + prim->r0 = prim->g0 = prim->b0 = brightness; + prim->r1 = prim->g1 = prim->b1 = brightness - 0x10; + prim = (PrimLineG2*)prim->next; + } + self->ext.et_8016D9C4.unk90 = 4; + self->ext.et_8016D9C4.unk8C = self->ext.et_8016D9C4.unk8E = 0; + self->step++; + break; + case 1: + self->ext.et_8016D9C4.unk8E = 1; + switch (self->ext.et_8016D9C4.unk8C) { + case 0: + prim = (PrimLineG2*)&g_PrimBuf[self->primIndex]; + break; + case 1: + prim = self->ext.et_8016D9C4.lines[0]; + break; + case 2: + prim = self->ext.et_8016D9C4.lines[1]; + break; + case 3: + prim = self->ext.et_8016D9C4.lines[2]; + break; + case 4: + prim = self->ext.et_8016D9C4.lines[3]; + break; + } + for (i = 0; i < 4; i++) { + prim->drawMode &= ~DRAW_HIDE; + prim = (PrimLineG2*)prim->next; + } + self->ext.et_8016D9C4.unk8C++; + if (self->ext.et_8016D9C4.unk8C > 4) { + self->step++; + } + break; + case 2: + if (!self->ext.et_8016D9C4.unk90) { + self->step++; + break; + } + break; + case 3: + self->ext.et_8016D9C4.unk90++; + if (self->ext.et_8016D9C4.unk90 > 4) { + DestroyEntity(self); + return; + } + break; + } + if (!self->ext.et_8016D9C4.unk8E) { + return; + } + prim = (PrimLineG2*)&g_PrimBuf[self->primIndex]; + for (i = 0; i < 4; i++) { + if (prim->delay) { + prim->x1 = prim->x0; + prim->y1 = prim->y0; + prim->x0 = prim->preciseX.i.hi; + prim->y0 = prim->preciseY.i.hi; + var_s7 = ratan2(prim->preciseY.val, FIX(128) - prim->preciseX.val) & + 0xFFF; + angle = prim->angle - var_s7; + if (labs(angle) > 0x800) { + if (angle < 0) { + angle += 0x1000; + } else { + angle -= 0x1000; + } + } + if (angle >= 0) { + if (angle > 0x80) { + var_s6 = 0x80; + } else { + var_s6 = angle; + } + angle = var_s6; + } else { + if (angle < -0x80) { + var_s5 = -0x80; + } else { + var_s5 = angle; + } + angle = var_s5; + } + prim->angle = prim->angle - angle; + prim->angle &= 0xFFF; + prim->velocityX.val = (rcos(prim->angle) << 4 << 4); + prim->velocityY.val = -(rsin(prim->angle) << 4 << 4); + prim->preciseX.val += prim->velocityX.val; + prim->preciseY.val += prim->velocityY.val; + self->posX.i.hi = prim->preciseX.i.hi; + self->posY.i.hi = prim->preciseY.i.hi; + OVL_EXPORT(RicCreateEntFactoryFromEntity)( + self, BP_CRASH_REBOUND_STONE_PARTICLES, 0); + if (prim->preciseY.val < 0) { + prim->delay = 0; + prim->drawMode |= DRAW_HIDE; + self->ext.et_8016D9C4.unk90--; + } + } + prim = (PrimLineG2*)prim->next; + } + prim = self->ext.et_8016D9C4.lines[0]; + prim2 = &g_PrimBuf[self->primIndex]; + for (i = 0; i < 16; i++) { + prim->x1 = prim->x0; + prim->y1 = prim->y0; + prim->x0 = prim2->x1; + prim->y0 = prim2->y1; + prim = (PrimLineG2*)prim->next; + prim2 = prim2->next; + } +} + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashReboundStoneExplosion); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashReboundStone); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashBibleBeam); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashBible); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", func_us_801C9DE8); + +void func_us_801CA340(Entity* self) { + OVL_EXPORT(RicCreateEntFactoryFromEntity)(self, FACTORY(0x3F, 1), 0); + DestroyEntity(self); +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_GetAguneaLightningAngle); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_AguneaShuffleParams); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityAguneaLightning); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityAguneaCircle); + +INCLUDE_ASM( + "boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnStopwatchCircle); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_EntityStopWatch); + +void OVL_EXPORT(RicEntitySubwpnBibleTrail)(Entity* entity) { + Primitive* prim; + + switch (entity->step) { + case 0: + entity->primIndex = g_api.AllocPrimitives(PRIM_GT4, 1); + if (entity->primIndex == -1) { + DestroyEntity(entity); + return; + } + entity->flags = FLAG_UNK_10000000 | FLAG_HAS_PRIMS; + prim = &g_PrimBuf[entity->primIndex]; + prim->tpage = 0x1C; + prim->clut = 0x19D; + prim->u0 = prim->u2 = 0x20; + prim->v0 = prim->v1 = 0; + prim->u1 = prim->u3 = 0x30; + prim->v2 = prim->v3 = 0x10; + prim->x0 = prim->x2 = entity->posX.i.hi - 8; + prim->x1 = prim->x3 = entity->posX.i.hi + 8; + prim->y0 = prim->y1 = entity->posY.i.hi - 8; + prim->y2 = prim->y3 = entity->posY.i.hi + 8; + prim->priority = entity->zPriority; + prim->drawMode = DRAW_TPAGE | DRAW_COLORS | DRAW_TRANSP; + entity->ext.et_BibleSubwpn.unk7E = 0x60; + entity->step++; + break; + case 1: + entity->ext.et_BibleSubwpn.unk7C++; + if (entity->ext.et_BibleSubwpn.unk7C > 5) { + entity->step++; + } + entity->ext.et_BibleSubwpn.unk7E -= 8; + break; + case 2: + DestroyEntity(entity); + return; + } + prim = &g_PrimBuf[entity->primIndex]; + PCOL(prim) = entity->ext.et_BibleSubwpn.unk7E; +} + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntitySubwpnBible); + +INCLUDE_RODATA("boss/bo6/nonmatchings/us_3E79C", D_us_801A7028); + +INCLUDE_RODATA("boss/bo6/nonmatchings/us_3E79C", D_us_801A7030); + +INCLUDE_ASM("boss/bo6/nonmatchings/us_3E79C", BO6_RicEntityCrashCrossBeam); diff --git a/src/maria/maria.h b/src/maria/maria.h index 1673405d0b..0183245d7f 100644 --- a/src/maria/maria.h +++ b/src/maria/maria.h @@ -1,28 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #include #include +#include #define OVL_EXPORT(x) MAR_##x -enum MarInputChecks { - CHECK_GROUND = 1, - CHECK_FALL = 4, - CHECK_FACING = 8, - CHECK_JUMP = 0x10, - CHECK_DOUBLEJUMP = 0x20, - CHECK_CRASH = 0x40, - CHECK_80 = 0x80, - CHECK_GRAVITY_HIT = 0x200, - CHECK_400 = 0x400, - CHECK_800 = 0x800, - CHECK_ATTACK = 0x1000, - CHECK_CROUCH = 0x2000, - CHECK_GRAVITY_FALL = 0x8000, - CHECK_GRAVITY_JUMP = 0x10000, - CHECK_GROUND_AFTER_HIT = 0x20000, - CHECK_SLIDE = 0x40000, -}; - enum MarPalette { /* 0x8114 */ PAL_MARIA = 0x8114, /* 0x8115 */ PAL_WPN_OWL, diff --git a/src/ric/pl_main.c b/src/ric/pl_main.c index 8a5344e037..f7c04b6630 100644 --- a/src/ric/pl_main.c +++ b/src/ric/pl_main.c @@ -446,13 +446,13 @@ void RicMain(void) { break; case PL_T_4: { angle = (g_GameTimer & 0xF) * 256; - draw->r0 = draw->g0 = draw->b0 = (rsin(angle) + 0x1000) / 64 + 0x60; - angle += 0x200; - draw->r1 = draw->g1 = draw->b1 = (rsin(angle) + 0x1000) / 64 + 0x60; - angle += 0x200; - draw->r3 = draw->g3 = draw->b3 = (rsin(angle) + 0x1000) / 64 + 0x60; - angle += 0x200; - draw->r2 = draw->g2 = draw->b2 = (rsin(angle) + 0x1000) / 64 + 0x60; + draw->r0 = draw->g0 = draw->b0 = (rsin(angle) + FLT(1)) / 64 + 0x60; + angle += FLT(1.0 / 8.0); + draw->r1 = draw->g1 = draw->b1 = (rsin(angle) + FLT(1)) / 64 + 0x60; + angle += FLT(1.0 / 8.0); + draw->r3 = draw->g3 = draw->b3 = (rsin(angle) + FLT(1)) / 64 + 0x60; + angle += FLT(1.0 / 8.0); + draw->r2 = draw->g2 = draw->b2 = (rsin(angle) + FLT(1)) / 64 + 0x60; draw->enableColorBlend = 1; break; } @@ -477,7 +477,7 @@ void RicMain(void) { case PL_T_INVINCIBLE: break; case PL_T_2: - PLAYER.palette = 0x8120; + PLAYER.palette = PAL_OVL(0x120); break; case PL_T_4: draw->enableColorBlend = 0; diff --git a/src/ric/ric.h b/src/ric/ric.h index ecfffa0786..5ea9e55821 100644 --- a/src/ric/ric.h +++ b/src/ric/ric.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #include #include +#include #define OVL_EXPORT(x) RIC_##x @@ -8,22 +9,6 @@ #define GAME_OVER 0x80000 -enum RicInputChecks { - CHECK_GROUND = 1, - CHECK_FALL = 4, - CHECK_FACING = 8, - CHECK_JUMP = 0x10, - CHECK_CRASH = 0x40, - CHECK_80 = 0x80, - CHECK_GRAVITY_HIT = 0x200, - CHECK_ATTACK = 0x1000, - CHECK_CROUCH = 0x2000, - CHECK_GRAVITY_FALL = 0x8000, - CHECK_GRAVITY_JUMP = 0x10000, - CHECK_GROUND_AFTER_HIT = 0x20000, - CHECK_SLIDE = 0x40000, -}; - // Richter mostly uses the same steps as Alucard, or uses unused Alucard // steps. // There are a couple steps that mean one thing for Alucard, and another for diff --git a/src/st/create_entity.h b/src/st/create_entity.h index cf0760c8fa..1dfdcaeaaf 100644 --- a/src/st/create_entity.h +++ b/src/st/create_entity.h @@ -17,8 +17,6 @@ STATIC_PAD_BSS(3); static u8 g_LayoutObjPosVertical; STATIC_PAD_BSS(3); -#include - #define LAYOUT_OBJ_START 0xfffe #define LAYOUT_OBJ_END 0xffff diff --git a/src/st/dai/gfx_data.c b/src/st/dai/gfx_data.c index fc87c9ca55..92deab7727 100644 --- a/src/st/dai/gfx_data.c +++ b/src/st/dai/gfx_data.c @@ -8,7 +8,7 @@ u8 gfx_stage_name_jp_sm[] = { #endif u8 gfx_stage_name_jp_lg[] = { -#include "gen/gfx_stage_name_jp_lg.h" +#include GEN_VERSION(gfx_stage_name_jp_lg.h) }; u8 gfx_confessional[] = { diff --git a/src/st/dai/graphics_banks.c b/src/st/dai/graphics_banks.c index 26ab147231..e4b27d074e 100644 --- a/src/st/dai/graphics_banks.c +++ b/src/st/dai/graphics_banks.c @@ -1,4 +1,4 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #include "dai.h" -#include "gen/graphics_banks.h" +#include GEN_VERSION(graphics_banks.h) diff --git a/src/st/entity_lock_camera.h b/src/st/entity_lock_camera.h index ad4a258155..c25bd03673 100644 --- a/src/st/entity_lock_camera.h +++ b/src/st/entity_lock_camera.h @@ -6,9 +6,9 @@ static u8 entityLockCameraHitbox[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x20, }; -static u8 entityLockCameraData[8] = {0}; +u8 entityLockCameraData[8] = {0}; -static u16 entityLockCameraTilemapProps[] = { +u16 entityLockCameraTilemapProps[] = { #if defined(STAGE_IS_NO1) 0x01A0, 0x0000, 0x02A0, 0x0200, 0x01A0, 0x0000, 0x02A0, 0x0200, #else diff --git a/src/st/st_update.h b/src/st/st_update.h index 6b48db022a..e112b7d19d 100644 --- a/src/st/st_update.h +++ b/src/st/st_update.h @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -#include +#include #if !defined(STAGE_IS_CAT) && !defined(STAGE_IS_NZ1) static u16 unused[] = { @@ -24,8 +24,6 @@ s32 Random(void) { return (g_randomNext >> 0x18) & 0xFF; } -#include - void Update(void) { s16 x; Entity* e; diff --git a/tools/builds/gen.py b/tools/builds/gen.py index 7bf93d9ab7..6cc39c4722 100644 --- a/tools/builds/gen.py +++ b/tools/builds/gen.py @@ -108,11 +108,19 @@ def add_c_psx( "cc_flags": get_cc_flags_for_exceptional_files(version, file_name), }, ) + + # this file may be generated by a different + # version of the game, but the content will + # be identical. + if file_name in entries: + return output + entries[file_name] = {"reason": "generated"} nw.build( rule="phony", outputs=file_name, implicit=[f"src/.assets_build_done_{ver}"], ) + return output @@ -150,6 +158,9 @@ def add_copy_psx( outputs=[output], inputs=[in_file_name], ) + if in_file_name in entries: + return output + entries[in_file_name] = {"reason": "copied"} nw.build( rule="phony", outputs=[in_file_name], @@ -230,6 +241,13 @@ def add_c_psp( "src_dir": os.path.dirname(file_name), }, ) + + # this file may be generated by a different + # version of the game, but the content will + # be identical. + if file_name in entries: + return output + entries[file_name] = {"reason": "generated"} nw.build( rule="phony", outputs=file_name, @@ -268,18 +286,21 @@ def add_assets_config(nw: ninja_syntax.Writer, version: str): def add_gfx_stage( nw: ninja_syntax.Writer, target_path: str, asset_path: str, output_name: str ): - nw.build( - rule="gfxstage-decode", - outputs=f"{asset_path}_0.png", - inputs=target_path, - variables={ - "path": asset_path, - }, - ) + input_name = f"{asset_path}_0.png" + if input_name not in entries: + entries[input_name] = {"reason": "gfxtage decoding"} + nw.build( + rule="gfxstage-decode", + outputs=input_name, + inputs=target_path, + variables={ + "path": asset_path, + }, + ) nw.build( rule="gfxstage-encode", outputs=[output_name], - inputs=[f"{asset_path}_0.png"], + inputs=[input_name], variables={ "path": asset_path, }, @@ -627,7 +648,7 @@ def add_checksum(nw: ninja_syntax.Writer, version: str, file_name: str): ) nw.build( rule="check", - outputs=["🆗"], + outputs=[f"{version} 🆗"], inputs=file_name, implicit=binaries, ) @@ -712,12 +733,15 @@ def add_checksum(nw: ninja_syntax.Writer, version: str, file_name: str): command=( "VERSION=$version" " tools/sotn_str/target/release/sotn_str process -p -f $in" - " | .venv/bin/python3 tools/mwccgap/mwccgap.py $out --src-dir $src_dir" + " | .venv/bin/python3 ../mwccgap/mwccgap.py $out --src-dir $src_dir" " --mwcc-path bin/mwccpsp.exe --use-wibo --wibo-path bin/wibo --as-path bin/allegrex-as" " --asm-dir-prefix asm/pspeu --target-encoding sjis --macro-inc-path include/macro.inc" " -gccinc -Iinclude -D_internal_version_$version -DSOTN_STR -c -lang c -sdatathreshold 0 -char unsigned -fl divbyzerocheck" + " -gccdep -MD" " $opt_level -opt nointrinsics" ), + depfile="$out.d", + deps="gcc", description="psp cc $in", ) nw.rule( @@ -783,8 +807,8 @@ def add_checksum(nw: ninja_syntax.Writer, version: str, file_name: str): actual_version = os.getenv("VERSION") if not actual_version: - actual_version = "us" - for version in [actual_version]: + actual_version = "us,hd,pspeu" + for version in actual_version.split(","): for entry in os.scandir("config/"): if not entry.name.startswith(f"splat.{version}."): continue diff --git a/tools/sotn-assets/assets/cmpgfx/handler.go b/tools/sotn-assets/assets/cmpgfx/handler.go index c29c03d819..7570e97469 100644 --- a/tools/sotn-assets/assets/cmpgfx/handler.go +++ b/tools/sotn-assets/assets/cmpgfx/handler.go @@ -65,7 +65,7 @@ func (h *handler) Extract(e assets.ExtractArgs) error { if err := util.WriteFile(assetPathAsRAW(e.AssetDir, e.Name), cmp); err != nil { return fmt.Errorf("error writing file: %v", err) } - fout, err := os.Create(assetPathAsPNG(e.AssetDir, e.Name)) + fout, err := util.CreateAtomicWriter(assetPathAsPNG(e.AssetDir, e.Name)) if err != nil { return fmt.Errorf("error creating file: %v", err) } diff --git a/tools/sotn-assets/assets/headergfx/handler.go b/tools/sotn-assets/assets/headergfx/handler.go index 3100567377..cb19b48204 100644 --- a/tools/sotn-assets/assets/headergfx/handler.go +++ b/tools/sotn-assets/assets/headergfx/handler.go @@ -49,7 +49,7 @@ func (h *handler) Extract(e assets.ExtractArgs) error { if err := os.MkdirAll(e.AssetDir, 0755); err != nil { return fmt.Errorf("error creating directory: %v", err) } - fout, err := os.Create(assetPath(e.AssetDir, e.Name)) + fout, err := util.CreateAtomicWriter(assetPath(e.AssetDir, e.Name)) if err != nil { return fmt.Errorf("error creating file: %v", err) } diff --git a/tools/sotn-assets/assets/palette/palette.go b/tools/sotn-assets/assets/palette/palette.go index 9ddd9de821..c88c068507 100644 --- a/tools/sotn-assets/assets/palette/palette.go +++ b/tools/sotn-assets/assets/palette/palette.go @@ -81,7 +81,7 @@ func (h *handler) Extract(e assets.ExtractArgs) error { for i, paletteFileName := range paletteNames { i, paletteFileName := i, paletteFileName eg.Go(func() error { - f, err := os.Create(filepath.Join(e.AssetDir, paletteFileName)) + f, err := util.CreateAtomicWriter(filepath.Join(e.AssetDir, paletteFileName)) if err != nil { return err } diff --git a/tools/sotn-assets/assets/rawgfx/handler.go b/tools/sotn-assets/assets/rawgfx/handler.go index e4440bb83f..af24fe25c8 100644 --- a/tools/sotn-assets/assets/rawgfx/handler.go +++ b/tools/sotn-assets/assets/rawgfx/handler.go @@ -61,7 +61,7 @@ func (h *handler) Extract(e assets.ExtractArgs) error { if err := os.MkdirAll(filepath.Dir(assetPath(e.AssetDir, e.Name)), 0755); err != nil { return fmt.Errorf("error creating directory: %v", err) } - fout, err := os.Create(assetPath(e.AssetDir, e.Name)) + fout, err := util.CreateAtomicWriter(assetPath(e.AssetDir, e.Name)) if err != nil { return fmt.Errorf("error creating file: %v", err) } diff --git a/tools/sotn-assets/assets/spritesheet/handler.go b/tools/sotn-assets/assets/spritesheet/handler.go index 73dea024c4..52622d9fc4 100644 --- a/tools/sotn-assets/assets/spritesheet/handler.go +++ b/tools/sotn-assets/assets/spritesheet/handler.go @@ -118,10 +118,11 @@ func (h *handler) Extract(e assets.ExtractArgs) error { if err != nil { return fmt.Errorf("error generating image: %v", err) } - f, err := os.Create(filepath.Join(e.AssetDir, entry.Name)) + f, err := util.CreateAtomicWriter(filepath.Join(e.AssetDir, entry.Name)) if err != nil { return err } + defer f.Close() if err := util.PngEncode(f, bitmap, w, h, palette); err != nil { return fmt.Errorf("failed to encode %s: %w", entry.Name, err) } @@ -219,7 +220,7 @@ func (h *handler) Build(e assets.BuildArgs) error { if err := os.MkdirAll(filepath.Dir(dstFile), 0755); err != nil { return fmt.Errorf("failed to create directory: %w", err) } - f, err := os.Create(dstFile) + f, err := util.CreateAtomicWriter(dstFile) if err != nil { return err } diff --git a/tools/sotn-assets/assets/tiledef/handler.go b/tools/sotn-assets/assets/tiledef/handler.go index a2af55ae5c..9e5a75d575 100644 --- a/tools/sotn-assets/assets/tiledef/handler.go +++ b/tools/sotn-assets/assets/tiledef/handler.go @@ -159,7 +159,7 @@ func Build(inFile, symbol, outDir string) error { if err := os.MkdirAll(filepath.Dir(outFileName), 0755); err != nil { return err } - f, err := os.Create(outFileName) + f, err := util.CreateAtomicWriter(outFileName) if err != nil { return err } diff --git a/tools/sotn-assets/util/utils.go b/tools/sotn-assets/util/utils.go index 56843a5028..f24de41fd2 100644 --- a/tools/sotn-assets/util/utils.go +++ b/tools/sotn-assets/util/utils.go @@ -6,6 +6,7 @@ import ( "github.com/xeeynamo/sotn-decomp/tools/sotn-assets/psx" "golang.org/x/exp/constraints" "image/color" + "io" "os" "path/filepath" "slices" @@ -141,7 +142,19 @@ func WriteFile(name string, content []byte) error { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create directory %q: %v\n", dir, err) } - return os.WriteFile(name, content, 0644) + return WriteFileAtomic(name, content) +} + +func WriteFileAtomic(name string, content []byte) error { + w, err := CreateAtomicWriter(name) + if err != nil { + return fmt.Errorf("could not create writer for %q: %v\n", name, err) + } + defer w.Close() + if _, err := w.Write(content); err != nil { + return fmt.Errorf("could not write %q: %v\n", name, err) + } + return nil } // WriteJsonFile converts the passed object as a JSON and internally calls WriteFile @@ -258,3 +271,52 @@ func Make4bppFromBitmap(data []byte) []byte { } return out } + +type StringWriteCloser interface { + io.WriteCloser + io.StringWriter + + Name() string +} + +type AtomicWriter struct { + name string + f *os.File +} + +func CreateAtomicWriter(name string) (StringWriteCloser, error) { + base := filepath.Base(name) + dir := filepath.Dir(name) + f, err := os.CreateTemp(dir, base) + if err != nil { + return nil, fmt.Errorf("failed to create temp file for %q: %v\n", name, err) + } + + a := AtomicWriter { name: name, f: f } + + return a, nil +} + +func (a AtomicWriter) Close() error { + a.f.Close() + if err := os.Rename(a.f.Name(), a.name); err != nil { + // in the common case, a.f will already be renamed + // in case it isn't remove the temp file + os.Remove(a.f.Name()) + return fmt.Errorf("failed to move temp file to destination %q: %v\n", a.name, err) + } + + return nil +} + +func (a AtomicWriter) Write(p []byte) (int, error) { + return a.f.Write(p) +} + +func (a AtomicWriter) WriteString(s string) (int, error) { + return a.f.WriteString(s) +} + +func (a AtomicWriter) Name() string { + return a.f.Name() +}