diff --git a/CMakeLists.txt b/CMakeLists.txt index 14280eff..72c6e18f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,11 @@ set(FF7_MINI set(FF7_MAGIC src/magic/barrier.c src/magic/brizad.c + src/magic/gas.c + src/magic/hakugek1.c + src/magic/hakugek2.c + src/magic/kaen.c + src/magic/kaen2.c src/magic/lv5deth.c src/magic/mabaria.c src/magic/sennkoku.c diff --git a/config/symbols.magic-gas.us.txt b/config/symbols.magic-gas.us.txt new file mode 100644 index 00000000..a4323cd4 --- /dev/null +++ b/config/symbols.magic-gas.us.txt @@ -0,0 +1,13 @@ +MAGIC_Gas = 0x801B0010; +GasTargetCenterCalc = 0x801B0030; +GasSprayVectorCalc = 0x801B01DC; +GasSpriteMatrixSetup = 0x801B030C; +GasDoubleBufferFlip = 0x801B046C; +GasFlameSpriteTick = 0x801B04D8; +GasEmberTick = 0x801B05DC; +GasFireballTick = 0x801B080C; +GasTargetTick = 0x801B0AEC; +GasTargetSetup = 0x801B0EAC; +GasStart = 0x801B0F84; +g_GasPrimBuffer = 0x801D3FF8; +g_GasBufferPtr = 0x801EBFF8; diff --git a/config/symbols.magic-hakugek1.us.txt b/config/symbols.magic-hakugek1.us.txt new file mode 100644 index 00000000..f5347e0b --- /dev/null +++ b/config/symbols.magic-hakugek1.us.txt @@ -0,0 +1,14 @@ +MAGIC_Hakugek1 = 0x801B0000; +Hakugek1TargetCenterCalc = 0x801B0020; +Hakugek1SprayVectorCalc = 0x801B01CC; +Hakugek1SpriteMatrixSetup = 0x801B02FC; +Hakugek1DoubleBufferFlip = 0x801B045C; +Hakugek1FirePuffTick = 0x801B04C8; +Hakugek1DebrisTick = 0x801B062C; +Hakugek1PillarRiseTick = 0x801B0878; +Hakugek1ShockwaveTick = 0x801B09F0; +Hakugek1TargetTick = 0x801B0D84; +Hakugek1TargetSetup = 0x801B11B4; +Hakugek1Start = 0x801B1290; +g_Hakugek1PrimBuffer = 0x801D2A80; +g_Hakugek1BufferPtr = 0x801EAA80; diff --git a/config/symbols.magic-hakugek2.us.txt b/config/symbols.magic-hakugek2.us.txt new file mode 100644 index 00000000..d003865b --- /dev/null +++ b/config/symbols.magic-hakugek2.us.txt @@ -0,0 +1,14 @@ +MAGIC_Hakugek2 = 0x801B0000; +Hakugek2TargetCenterCalc = 0x801B0020; +Hakugek2SprayVectorCalc = 0x801B01CC; +Hakugek2SpriteMatrixSetup = 0x801B02FC; +Hakugek2DoubleBufferFlip = 0x801B045C; +Hakugek2FirePuffTick = 0x801B04C8; +Hakugek2DebrisTick = 0x801B062C; +Hakugek2PillarRiseTick = 0x801B0878; +Hakugek2ShockwaveTick = 0x801B09E0; +Hakugek2TargetTick = 0x801B0D64; +Hakugek2TargetSetup = 0x801B10A0; +Hakugek2Start = 0x801B117C; +g_Hakugek2PrimBuffer = 0x801D296C; +g_Hakugek2BufferPtr = 0x801EA96C; diff --git a/config/symbols.magic-kaen.us.txt b/config/symbols.magic-kaen.us.txt new file mode 100644 index 00000000..86fe3b7e --- /dev/null +++ b/config/symbols.magic-kaen.us.txt @@ -0,0 +1,13 @@ +MAGIC_Kaen = 0x801B0010; +KaenTargetCenterCalc = 0x801B0030; +KaenSprayVectorCalc = 0x801B01DC; +KaenSpriteMatrixSetup = 0x801B030C; +KaenDoubleBufferFlip = 0x801B046C; +KaenFlameSpriteTick = 0x801B04D8; +KaenEmberTick = 0x801B05DC; +KaenFireballTick = 0x801B080C; +KaenTargetTick = 0x801B0AEC; +KaenTargetSetup = 0x801B0EAC; +KaenStart = 0x801B0F6C; +g_KaenPrimBuffer = 0x801D3F30; +g_KaenBufferPtr = 0x801EBF30; diff --git a/config/symbols.magic-kaen2.us.txt b/config/symbols.magic-kaen2.us.txt new file mode 100644 index 00000000..aa1207be --- /dev/null +++ b/config/symbols.magic-kaen2.us.txt @@ -0,0 +1,13 @@ +MAGIC_Kaen2 = 0x801B0000; +Kaen2TargetCenterCalc = 0x801B0020; +Kaen2SprayVectorCalc = 0x801B01CC; +Kaen2SpriteMatrixSetup = 0x801B02FC; +Kaen2DoubleBufferFlip = 0x801B045C; +Kaen2FlameSpriteTick = 0x801B04C8; +Kaen2EmberTick = 0x801B0658; +Kaen2FireballTick = 0x801B0888; +Kaen2TargetTick = 0x801B0B40; +Kaen2TargetSetup = 0x801B0FF0; +Kaen2Start = 0x801B10BC; +g_Kaen2PrimBuffer = 0x801D4140; +g_Kaen2BufferPtr = 0x801EC140; diff --git a/config/us.yaml b/config/us.yaml index 69eaa150..418b81c6 100644 --- a/config/us.yaml +++ b/config/us.yaml @@ -807,3 +807,72 @@ overlays: - [0, c, sennkoku] - [0x930, data, sennkoku_model] - [0x559C, data, sennkoku_buffers] + # KAEN.BIN -- effect overlay + - name: kaen + disk_path: disks/us/MAGIC/KAEN.BIN + sha1: 2dc7702de646e5288feeae5f349b58ce88135b5e + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-kaen.us.txt + segments: + # REVIEW (split): the 0x10 bytes before the code are the effect's sprite descriptor; the linker script puts rodata first, so it is a rodata segment; cost: none, data only + - [0, rodata, kaen] + - [0x10, c, kaen] + - [0xFE8, data, kaen_model] + - [0x23F30, data, kaen_buffers] + # KAEN2.BIN -- effect overlay + - name: kaen2 + disk_path: disks/us/MAGIC/KAEN2.BIN + sha1: 0bb5bf470e3b04dd658b7c28ff374464a1401ca6 + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-kaen2.us.txt + segments: + - [0, c, kaen2] + - [0x1128, data, kaen2_model] + - [0x24140, data, kaen2_buffers] + # GAS.BIN -- effect overlay + - name: gas + disk_path: disks/us/MAGIC/GAS.BIN + sha1: 5fe66342aeed0b053440d8c66bc144a49445a016 + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-gas.us.txt + segments: + # REVIEW (split): the 0x10 bytes before the code are the effect's sprite descriptor; the linker script puts rodata first, so it is a rodata segment; cost: none, data only + - [0, rodata, gas] + - [0x10, c, gas] + - [0xFF0, data, gas_model] + - [0x23FF8, data, gas_buffers] + # HAKUGEK1.BIN -- effect overlay + - name: hakugek1 + disk_path: disks/us/MAGIC/HAKUGEK1.BIN + sha1: 5cf866b06953286fa308f84b04fc327c50c56669 + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-hakugek1.us.txt + segments: + - [0, c, hakugek1] + - [0x12FC, data, hakugek1_model] + - [0x22A80, data, hakugek1_buffers] + # HAKUGEK2.BIN -- effect overlay + - name: hakugek2 + disk_path: disks/us/MAGIC/HAKUGEK2.BIN + sha1: a7385d17eb1439af1cb9b1282abd9e163e563b5f + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-hakugek2.us.txt + segments: + - [0, c, hakugek2] + - [0x11E8, data, hakugek2_model] + - [0x2296C, data, hakugek2_buffers] diff --git a/src/magic/gas.c b/src/magic/gas.c new file mode 100644 index 00000000..80dbc267 --- /dev/null +++ b/src/magic/gas.c @@ -0,0 +1,306 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "gas.h" +#include "../battle/battle.h" + +// Effect shared by St. Elmos Fire (セントエルモスファイアー), Cold (冷気) and Diamond Fire (ダイヤファイア). + +void GasStart(s32 targetMask, s32 callbackArg); + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 g_GasPrimBuffer[2][0xC000]; +extern void* g_GasBufferPtr; + +// Only StartFrame/AnimationFrame are common to every magic overlay; the +// rest is payload this effect lays out for itself. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR PartPos; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} GasData; // size:0x20 + +extern ModelRenderDesc D_801D3FBC; +extern SVECTOR D_801D3FB4; +extern SpriteAnim* D_801D3F9C[]; +extern SpriteRenderDesc D_801D3F90; +extern SpriteRenderDesc D_801D3FA8; +extern SpriteRenderDesc D_801D3FCC; +extern VECTOR D_801B0000; +extern GasData g_BattleEffectSlots[]; +extern GasData g_BattleDetachedSlots[]; +extern s16 g_BattleDetachedCursor; +extern s32* D_801D3FD8[]; +extern u_long D_801B0FF0[]; +extern void* D_80163C74; +s32 BattleDetachedRegister(void (*func)(void)); +void func_800BBA84(u16 arg0, s32 arg1, s32 arg2); +void GasTargetTick(void); + +void MAGIC_Gas(s32 targetMask, s32 callbackArg) { GasStart(targetMask, callbackArg); } + +void GasTargetCenterCalc(s16 targetMask, SVECTOR* out) { + VECTOR sum; + SVECTOR pos; + s16 i; + s16 count; + + count = 0; + sum.vx = sum.vy = sum.vz = 0; + for (i = 0; i < 10; i++) { + if ((targetMask >> i) & 1) { + BattleGetPartPosition(i, g_BattleModels[i].boneIndices[0], &pos); + count++; + sum.vx += pos.vx; + sum.vy += pos.vy; + sum.vz += pos.vz; + } + } + out->vx = sum.vx / count; + out->vy = sum.vy / count; + out->vz = sum.vz / count; +} + +void GasSprayVectorCalc(s32 offset, s16 spread, SVECTOR* out) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + + in.vz = 0; + in.vx = 0; + in.vy = offset + (rand() % spread * 2 - spread); + rot.vy = 0; + rot.vx = rand() % 800 - 400; + rot.vz = rand() % 800 - 400; + RotMatrix(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, out); +} + +void GasSpriteMatrixSetup(SVECTOR* pos, SVECTOR* rot, s16 scale, s32 offset) { + MATRIX matrix; + MATRIX rotated; + VECTOR dir; + s32 flag; + s16 i; + + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(pos, (VECTOR*)matrix.t, &flag); + if (offset != 0) { + VectorNormal((VECTOR*)matrix.t, &dir); + matrix.t[0] += offset * dir.vx >> 12; + matrix.t[1] += offset * dir.vy >> 12; + matrix.t[2] += offset * dir.vz >> 12; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); +} + +// Same as BarrierDoubleBufferFlip. +static void GasDoubleBufferFlip(void) { + GasData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + g_GasBufferPtr = g_GasPrimBuffer[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void GasFlameSpriteTick(void) { + GasData* effect; + + effect = &g_BattleDetachedSlots[g_BattleDetachedCursor]; + BattleSetBillboardMatrix(&effect->Pos, effect->unk16 == 2 ? 0x2000 : 0x3000, 0); + D_801D3F90.frames = D_801D3F9C[effect->unk16]; + D_801D3F90.frameIndex = effect->AnimationFrame; + D_80163C74 = func_800D4D90(&D_801D3F90, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801D3F9C[effect->unk16]->frameCount) { + effect->StartFrame = -1; + } + } +} + +void GasEmberTick(void) { + MATRIX rotated; + MATRIX matrix; + SVECTOR rot; + s32 flag; + GasData* effect; + s16 fade; + s16 scale; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + fade = 8; + if (effect->AnimationFrame >= 12) { + fade = 20 - effect->AnimationFrame; + } + rot.vy = 0; + rot.vx = 0; + rot.vz = effect->unk14; + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + scale = 0x2000; + if (effect->unk16 < 4) { + scale = 0x1400; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(&rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D3FA8.frameIndex = effect->unk16; + D_801D3FA8.color.cd = fade == 8 ? 0x2C : 0x2E; + D_801D3FA8.color.r = D_801D3FA8.color.g = D_801D3FA8.color.b = fade << 4; + D_80163C74 = func_800D4D90(&D_801D3FA8, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + if (effect->Pos.vy > 0) { + effect->Pos.vy = 0; + effect->PartPos.vy *= -1; + } + effect->PartPos.vy += 0x14; + effect->unk14 += 0x200; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void GasFireballTick(void) { + MATRIX dirMatrix; + MATRIX matrix; + MATRIX composed; + s32 pad[2]; // match: unused, gives the original stack frame + VECTOR scale; + GasData* effect; + GasData* child; + + scale = D_801B0000; + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleMatrixFromDirection(&effect->PartPos, &dirMatrix); + RotMatrixYXZ(&D_801D3FB4, &matrix); + MulMatrix0(&dirMatrix, &matrix, &matrix); + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = effect->Pos.vy; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D3FBC.model = D_801D3FD8[effect->AnimationFrame % 8]; + g_GasBufferPtr = func_800D29D4(&D_801D3FBC, g_cDb->unk70, 12, g_GasBufferPtr); + BattleSetBillboardMatrix(&effect->Pos, 0x1000, 0); + D_80163C74 = func_800D4D90(&D_801D3FCC, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->AnimationFrame++; + if (effect->AnimationFrame == 8) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(GasFlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx + rand() % 800 - 400; + child->Pos.vy = effect->Pos.vy + rand() % 800 - 400; + child->Pos.vz = effect->Pos.vz + rand() % 800 - 400; + child->unk16 = 0; + effect->StartFrame = -1; + } + } +} + +void GasTargetTick(void) { + s32 pad[12]; // match: unused, gives the original stack frame + GasData* effect; + GasData* child; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (D_80062D98 == 0) { + if (effect->AnimationFrame == 0) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(GasFlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->unk16 = 2; + child = &g_BattleEffectSlots[BattleEffectRegister(GasFireballTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->PartPos.vx = (effect->PartPos.vx - effect->Pos.vx) / 8; + child->PartPos.vy = (effect->PartPos.vy - effect->Pos.vy) / 8; + child->PartPos.vz = (effect->PartPos.vz - effect->Pos.vz) / 8; + } + if (effect->AnimationFrame == 8) { + i = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(GasEmberTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 400 - 200; + child->Pos.vz = effect->PartPos.vz + rand() % 400 - 200; + child->Pos.vy = 0; + GasSprayVectorCalc(-250, 100, &child->PartPos); + child->unk16 = rand() % 20; + i++; + } while (i < 12); + func_800BBA84(0xFA, effect->unk1C, 0); + } + if (effect->AnimationFrame >= 12 && effect->AnimationFrame < 19) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(GasFlameSpriteTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 1200 - 600; + child->Pos.vy = effect->PartPos.vy + rand() % 1200 - 600; + child->Pos.vz = effect->PartPos.vz + rand() % 1200 - 600; + child->unk16 = 1; + } + if (effect->AnimationFrame == 15) { + func_800D5774(effect->unk1C); + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 45) { + effect->StartFrame = -1; + } + } +} + +void GasTargetSetup(s32 target, s32 callbackArg) { + GasData* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(GasTargetTick)]; + BattleGetPartPosition(callbackArg, g_BattleModels[callbackArg].boneIndices[8], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->PartPos); + effect->unk1C = target; + func_800BBA84(0x163, callbackArg, 0); +} + +void GasStart(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(GasDoubleBufferFlip); + BattleSetLoadTimToVram(D_801B0FF0, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, GasTargetSetup); +} diff --git a/src/magic/gas.h b/src/magic/gas.h new file mode 100644 index 00000000..cc6b0b87 --- /dev/null +++ b/src/magic/gas.h @@ -0,0 +1,6 @@ +#ifndef GAS_H +#define GAS_H + +#include "magic.h" + +#endif diff --git a/src/magic/hakugek1.c b/src/magic/hakugek1.c new file mode 100644 index 00000000..4559c7f3 --- /dev/null +++ b/src/magic/hakugek1.c @@ -0,0 +1,391 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "hakugek1.h" +#include "../battle/battle.h" + +// Big Bomber (ビッグボンバー / Biggu Bonba). + +void Hakugek1Start(s32 targetMask, s32 callbackArg); + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 g_Hakugek1PrimBuffer[2][0xC000]; +extern void* g_Hakugek1BufferPtr; + +// Only StartFrame/AnimationFrame are common to every magic overlay; the +// rest is payload this effect lays out for itself. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR PartPos; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} Hakugek1Data; // size:0x20 + +extern ModelRenderDesc D_801D2A54; +extern ModelRenderDesc D_801D2A64; +extern SpriteAnim* D_801D2A48[]; +extern SpriteRenderDesc D_801D2A30; +extern SpriteRenderDesc D_801D2A3C; +extern Hakugek1Data g_BattleEffectSlots[]; +extern Hakugek1Data g_BattleDetachedSlots[]; +extern s16 g_BattleDetachedCursor; +extern s32 D_801D296C; +extern s32 D_801D139C[]; +extern s32 D_801D1708[]; +extern s32 D_801D1B14[]; +extern s32 D_801D20E4[]; +extern u16 D_801D2A74[]; +extern u_long D_801B12FC[]; +extern void* D_80163C74; +void Hakugek1TargetSetup(s32 target, s32 callbackArg); +void func_800BBA84(u16 arg0, s32 arg1, s32 arg2); +void Hakugek1TargetTick(void); + +void MAGIC_Hakugek1(s32 targetMask, s32 callbackArg) { Hakugek1Start(targetMask, callbackArg); } + +void Hakugek1TargetCenterCalc(s16 targetMask, SVECTOR* out) { + VECTOR sum; + SVECTOR pos; + s16 i; + s16 count; + + count = 0; + sum.vx = sum.vy = sum.vz = 0; + for (i = 0; i < 10; i++) { + if ((targetMask >> i) & 1) { + BattleGetPartPosition(i, g_BattleModels[i].boneIndices[0], &pos); + count++; + sum.vx += pos.vx; + sum.vy += pos.vy; + sum.vz += pos.vz; + } + } + out->vx = sum.vx / count; + out->vy = sum.vy / count; + out->vz = sum.vz / count; +} + +void Hakugek1SprayVectorCalc(s32 offset, s16 spread, SVECTOR* out) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + + in.vz = 0; + in.vx = 0; + in.vy = offset + (rand() % spread * 2 - spread); + rot.vy = 0; + rot.vx = rand() % 800 - 400; + rot.vz = rand() % 800 - 400; + RotMatrix(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, out); +} + +void Hakugek1SpriteMatrixSetup(SVECTOR* pos, SVECTOR* rot, s16 scale, s32 offset) { + MATRIX matrix; + MATRIX rotated; + VECTOR dir; + s32 flag; + s16 i; + + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(pos, (VECTOR*)matrix.t, &flag); + if (offset != 0) { + VectorNormal((VECTOR*)matrix.t, &dir); + matrix.t[0] += offset * dir.vx >> 12; + matrix.t[1] += offset * dir.vy >> 12; + matrix.t[2] += offset * dir.vz >> 12; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); +} + +// Same as BarrierDoubleBufferFlip. +static void Hakugek1DoubleBufferFlip(void) { + Hakugek1Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + g_Hakugek1BufferPtr = g_Hakugek1PrimBuffer[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void Hakugek1FirePuffTick(void) { + MATRIX matrix; + MATRIX rotated; + s32 flag; + Hakugek1Data* effect; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[i][2] = 0; + matrix.m[i][1] = 0; + matrix.m[i][0] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = 0x3000; + MulMatrix(&matrix, &g_BattleWorldView.m); + RotMatrixYXZ(&effect->PartPos, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D2A30.frameIndex = effect->AnimationFrame; + g_Hakugek1BufferPtr = func_800D4D90(&D_801D2A30, (u_long**)&g_cDb->unk70, 12, g_Hakugek1BufferPtr); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801D296C) { + effect->StartFrame = -1; + } + } +} + +void Hakugek1DebrisTick(void) { + MATRIX rotated; + MATRIX matrix; + SVECTOR rot; + s32 flag; + Hakugek1Data* effect; + s16 fade; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + fade = 8; + if (effect->AnimationFrame >= 12) { + fade = 20 - effect->AnimationFrame; + } + rot.vy = 0; + rot.vx = 0; + rot.vz = effect->unk14; + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = 0x2000; + RotMatrixYXZ(&rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D2A3C.frameIndex = effect->unk16 % 3; + D_801D2A3C.frames = D_801D2A48[effect->unk16 / 3]; + D_801D2A3C.color.r = D_801D2A3C.color.g = D_801D2A3C.color.b = fade << 4; + D_80163C74 = func_800D4D90(&D_801D2A3C, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->PartPos.vy += 0x14; + effect->unk14 += 0x200; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} +void Hakugek1PillarRiseTick(void) { + SVECTOR rot; + MATRIX matrix; + MATRIX composed; + VECTOR scale; + Hakugek1Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->unk14; + RotMatrixYXZ(&rot, &matrix); + scale.vy = 0x1000; + scale.vx = scale.vz = effect->unk18; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = effect->Pos.vy; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + SetFarColor(0, 0, 0); + D_801D2A54.color = effect->AnimationFrame * 200; + g_Hakugek1BufferPtr = func_800D29D4(&D_801D2A54, g_cDb->unk70, 12, g_Hakugek1BufferPtr); + if (D_80062D98 == 0) { + effect->unk18 += 0x200; + effect->unk14 += 0x80; + effect->Pos.vy += effect->PartPos.vy; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void Hakugek1ShockwaveTick(void) { + SVECTOR rot; + MATRIX matrix; + MATRIX composed; + VECTOR scale; + Hakugek1Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->AnimationFrame * 64; + RotMatrixYXZ(&rot, &matrix); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 240 + 0x600; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + SetFarColor(0, 0, 0); + D_801D2A64.flags = MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2A64.model = D_801D139C; + D_801D2A64.color = 0x80 - effect->AnimationFrame * 6; + g_Hakugek1BufferPtr = func_800D29D4(&D_801D2A64, g_cDb->unk70, 12, g_Hakugek1BufferPtr); + RotMatrixYXZ(&rot, &matrix); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 320 + 0x800; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D2A64.flags = MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2A64.model = D_801D1B14; + D_801D2A64.color = 0x80 - effect->AnimationFrame * 6; + g_Hakugek1BufferPtr = func_800D29D4(&D_801D2A64, g_cDb->unk70, 12, g_Hakugek1BufferPtr); + RotMatrixYXZ(&rot, &matrix); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 240 + 0x600; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D2A64.flags = MODEL_DEPTH_CUE | MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2A64.model = D_801D1708; + D_801D2A64.color = effect->AnimationFrame * 48 + 0xC00; + g_Hakugek1BufferPtr = func_800D29D4(&D_801D2A64, g_cDb->unk70, 12, g_Hakugek1BufferPtr); + D_801D2A64.flags = MODEL_DEPTH_CUE | MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2A64.model = D_801D20E4; + D_801D2A64.color = effect->AnimationFrame * 96; + g_Hakugek1BufferPtr = func_800D29D4(&D_801D2A64, g_cDb->unk70, 12, g_Hakugek1BufferPtr); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == 20) { + effect->StartFrame = -1; + } + } +} + +void Hakugek1TargetTick(void) { + SVECTOR pos; + s32 pad[10]; // unused; gives the function its 0x60 byte stack frame (match) + Hakugek1Data* effect; + Hakugek1Data* child; + s32 ang; + s16 i; + s16 j; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (D_80062D98 == 0) { + if (effect->AnimationFrame == 0) { + ang = ratan2(effect->PartPos.vx - effect->Pos.vx, effect->PartPos.vz - effect->Pos.vz); + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1FirePuffTick)]; + BattleGetPartPosition(effect->unk1E, g_BattleModels[effect->unk1E].boneIndices[11], &child->Pos); + child->PartPos.vx = 0; + child->PartPos.vy = ang + 0x400; + child->PartPos.vz = -0x200; + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1FirePuffTick)]; + BattleGetPartPosition(effect->unk1E, g_BattleModels[effect->unk1E].boneIndices[12], &child->Pos); + child->PartPos.vx = 0; + child->PartPos.vy = ang + 0x400; + child->PartPos.vz = -0x200; + } + if (effect->AnimationFrame == 20) { + i = 0; + do { + pos.vx = effect->PartPos.vx + (rcos((i << 16 >> 5) + 0x200) * 800 >> 12); + pos.vy = effect->PartPos.vy; + pos.vz = effect->PartPos.vz + (rsin((i << 16 >> 5) + 0x200) * 800 >> 12); + j = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1DebrisTick)]; + child->Pos.vx = pos.vx + rand() % 400 - 200; + child->Pos.vz = pos.vz + rand() % 400 - 200; + child->Pos.vy = 0; + Hakugek1SprayVectorCalc(-300, 50, &child->PartPos); + child->unk16 = rand() % 9; + j++; + } while (j < 24); + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1ShockwaveTick)]; + child->Pos.vx = pos.vx; + child->Pos.vy = 0; + child->Pos.vz = pos.vz; + j = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1PillarRiseTick)]; + child->Pos.vx = pos.vx; + child->Pos.vy = pos.vy; + child->Pos.vz = pos.vz; + child->Pos.vy = (j - 4) * 80; + child->unk18 = D_801D2A74[j]; + child->PartPos.vy = (j - 4) * 10; + child->unk14 = rand() % 4096; + j++; + } while (j < 5); + i++; + } while (i < 2); + func_800BBA84(0xFA, effect->unk1C, 0); + } + if (effect->AnimationFrame == 35) { + func_800D5774(effect->unk1C); + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 45) { + effect->StartFrame = -1; + } + } +} + +void Hakugek1TargetSetup(s32 target, s32 callbackArg) { + Hakugek1Data* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(Hakugek1TargetTick)]; + BattleGetPartPosition(callbackArg, g_BattleModels[callbackArg].boneIndices[0], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->PartPos); + effect->unk1C = target; + effect->unk1E = callbackArg; + func_800BBA84(0x14, callbackArg, 0); +} + +void Hakugek1Start(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(Hakugek1DoubleBufferFlip); + BattleSetLoadTimToVram(D_801B12FC, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, Hakugek1TargetSetup); +} diff --git a/src/magic/hakugek1.h b/src/magic/hakugek1.h new file mode 100644 index 00000000..5c489bd5 --- /dev/null +++ b/src/magic/hakugek1.h @@ -0,0 +1,6 @@ +#ifndef HAKUGEK1_H +#define HAKUGEK1_H + +#include "magic.h" + +#endif diff --git a/src/magic/hakugek2.c b/src/magic/hakugek2.c new file mode 100644 index 00000000..f492b3be --- /dev/null +++ b/src/magic/hakugek2.c @@ -0,0 +1,376 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "hakugek2.h" +#include "../battle/battle.h" + +// Effect shared by Seed Bullet (シード弾) and Throw (投げる). + +void Hakugek2Start(s32 targetMask, s32 callbackArg); + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 g_Hakugek2PrimBuffer[2][0xC000]; +extern void* g_Hakugek2BufferPtr; + +// Only StartFrame/AnimationFrame are common to every magic overlay; the +// rest is payload this effect lays out for itself. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR PartPos; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} Hakugek2Data; // size:0x20 + +extern ModelRenderDesc D_801D2940; +extern ModelRenderDesc D_801D2950; +extern SpriteAnim* D_801D2934[]; +extern SpriteRenderDesc D_801D291C; +extern SpriteRenderDesc D_801D2928; +extern Hakugek2Data g_BattleEffectSlots[]; +extern Hakugek2Data g_BattleDetachedSlots[]; +extern s16 g_BattleDetachedCursor; +extern s32 D_801D2858; +extern s32 D_801D1288[]; +extern s32 D_801D15F4[]; +extern s32 D_801D1A00[]; +extern s32 D_801D1FD0[]; +extern u16 D_801D2960[]; +extern u_long D_801B11E8[]; +extern void* D_80163C74; +void Hakugek2TargetSetup(s32 target, s32 callbackArg); +void Hakugek2TargetTick(void); + +void MAGIC_Hakugek2(s32 targetMask, s32 callbackArg) { Hakugek2Start(targetMask, callbackArg); } + +void Hakugek2TargetCenterCalc(s16 targetMask, SVECTOR* out) { + VECTOR sum; + SVECTOR pos; + s16 i; + s16 count; + + count = 0; + sum.vx = sum.vy = sum.vz = 0; + for (i = 0; i < 10; i++) { + if ((targetMask >> i) & 1) { + BattleGetPartPosition(i, g_BattleModels[i].boneIndices[0], &pos); + count++; + sum.vx += pos.vx; + sum.vy += pos.vy; + sum.vz += pos.vz; + } + } + out->vx = sum.vx / count; + out->vy = sum.vy / count; + out->vz = sum.vz / count; +} + +void Hakugek2SprayVectorCalc(s32 offset, s16 spread, SVECTOR* out) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + + in.vz = 0; + in.vx = 0; + in.vy = offset + (rand() % spread * 2 - spread); + rot.vy = 0; + rot.vx = rand() % 800 - 400; + rot.vz = rand() % 800 - 400; + RotMatrix(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, out); +} + +void Hakugek2SpriteMatrixSetup(SVECTOR* pos, SVECTOR* rot, s16 scale, s32 offset) { + MATRIX matrix; + MATRIX rotated; + VECTOR dir; + s32 flag; + s16 i; + + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(pos, (VECTOR*)matrix.t, &flag); + if (offset != 0) { + VectorNormal((VECTOR*)matrix.t, &dir); + matrix.t[0] += offset * dir.vx >> 12; + matrix.t[1] += offset * dir.vy >> 12; + matrix.t[2] += offset * dir.vz >> 12; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); +} + +// Same as BarrierDoubleBufferFlip. +static void Hakugek2DoubleBufferFlip(void) { + Hakugek2Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + g_Hakugek2BufferPtr = g_Hakugek2PrimBuffer[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void Hakugek2FirePuffTick(void) { + MATRIX matrix; + MATRIX rotated; + s32 flag; + Hakugek2Data* effect; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[i][2] = 0; + matrix.m[i][1] = 0; + matrix.m[i][0] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = 0x3000; + MulMatrix(&matrix, &g_BattleWorldView.m); + RotMatrixYXZ(&effect->PartPos, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D291C.frameIndex = effect->AnimationFrame; + g_Hakugek2BufferPtr = func_800D4D90(&D_801D291C, (u_long**)&g_cDb->unk70, 12, g_Hakugek2BufferPtr); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801D2858) { + effect->StartFrame = -1; + } + } +} + +void Hakugek2DebrisTick(void) { + MATRIX rotated; + MATRIX matrix; + SVECTOR rot; + s32 flag; + Hakugek2Data* effect; + s16 fade; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + fade = 8; + if (effect->AnimationFrame >= 12) { + fade = 20 - effect->AnimationFrame; + } + rot.vy = 0; + rot.vx = 0; + rot.vz = effect->unk14; + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = 0x2000; + RotMatrixYXZ(&rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D2928.frameIndex = effect->unk16 % 3; + D_801D2928.frames = D_801D2934[effect->unk16 / 3]; + D_801D2928.color.r = D_801D2928.color.g = D_801D2928.color.b = fade << 4; + D_80163C74 = func_800D4D90(&D_801D2928, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->PartPos.vy += 0x14; + effect->unk14 += 0x200; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void Hakugek2PillarRiseTick(void) { + SVECTOR rot; + MATRIX matrix; + MATRIX composed; + VECTOR scale; + Hakugek2Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->unk14; + RotMatrixYXZ(&rot, &matrix); + scale.vy = 0x1000; + scale.vx = scale.vz = effect->unk18; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = effect->Pos.vy; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D2940.color = effect->AnimationFrame * 200; + g_Hakugek2BufferPtr = func_800D29D4(&D_801D2940, g_cDb->unk70, 12, g_Hakugek2BufferPtr); + if (D_80062D98 == 0) { + effect->unk18 += 0x200; + effect->unk14 += 0x80; + effect->Pos.vy += effect->PartPos.vy; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void Hakugek2ShockwaveTick(void) { + SVECTOR rot; + MATRIX matrix; + MATRIX composed; + VECTOR scale; + Hakugek2Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->AnimationFrame * 64; + RotMatrixYXZ(&rot, &matrix); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 240 + 0x600; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D2950.flags = MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2950.model = D_801D1288; + D_801D2950.color = 0x80 - effect->AnimationFrame * 6; + g_Hakugek2BufferPtr = func_800D29D4(&D_801D2950, g_cDb->unk70, 12, g_Hakugek2BufferPtr); + RotMatrixYXZ(&rot, &matrix); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 320 + 0x800; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + RotMatrixYXZ(&rot, &matrix); + D_801D2950.flags = MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2950.model = D_801D1A00; + D_801D2950.color = 0x80 - effect->AnimationFrame * 6; + g_Hakugek2BufferPtr = func_800D29D4(&D_801D2950, g_cDb->unk70, 12, g_Hakugek2BufferPtr); + scale.vx = scale.vy = scale.vz = effect->AnimationFrame * 240 + 0x600; + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = 0; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D2950.flags = MODEL_DEPTH_CUE | MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2950.model = D_801D15F4; + D_801D2950.color = effect->AnimationFrame * 48 + 0xC00; + g_Hakugek2BufferPtr = func_800D29D4(&D_801D2950, g_cDb->unk70, 12, g_Hakugek2BufferPtr); + D_801D2950.flags = MODEL_DEPTH_CUE | MODEL_NO_CULL | MODEL_SEMI_TRANS; + D_801D2950.model = D_801D1FD0; + D_801D2950.color = effect->AnimationFrame * 96; + g_Hakugek2BufferPtr = func_800D29D4(&D_801D2950, g_cDb->unk70, 12, g_Hakugek2BufferPtr); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == 20) { + effect->StartFrame = -1; + } + } +} +void Hakugek2TargetTick(void) { + s32 pad[12]; // unused; gives the function its 0x58 byte stack frame (match) + Hakugek2Data* effect; + Hakugek2Data* child; + s32 ang; + s16 j; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (D_80062D98 == 0) { + if (effect->AnimationFrame == 0) { + ang = ratan2(effect->PartPos.vx - effect->Pos.vx, effect->PartPos.vz - effect->Pos.vz); + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek2FirePuffTick)]; + BattleGetPartPosition(effect->unk1E, g_BattleModels[effect->unk1E].boneIndices[8], &child->Pos); + child->PartPos.vx = 0; + child->PartPos.vy = ang + 0x400; + child->PartPos.vz = -0x200; + } + if (effect->AnimationFrame == 20) { + { + j = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek2DebrisTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 400 - 200; + child->Pos.vz = effect->PartPos.vz + rand() % 400 - 200; + child->Pos.vy = 0; + Hakugek2SprayVectorCalc(-300, 50, &child->PartPos); + child->unk16 = rand() % 9; + j++; + } while (j < 24); + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek2ShockwaveTick)]; + child->Pos.vx = effect->PartPos.vx; + child->Pos.vy = 0; + child->Pos.vz = effect->PartPos.vz; + j = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(Hakugek2PillarRiseTick)]; + child->Pos.vx = effect->PartPos.vx; + child->Pos.vy = effect->PartPos.vy; + child->Pos.vz = effect->PartPos.vz; + child->Pos.vy = (j - 4) * 80; + child->unk18 = D_801D2960[j]; + child->PartPos.vy = (j - 4) * 10; + child->unk14 = rand() % 4096; + j++; + } while (j < 5); + } + func_800BBA84(0xFA, effect->unk1C, 0); + } + if (effect->AnimationFrame == 35) { + func_800D5774(effect->unk1C); + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 45) { + effect->StartFrame = -1; + } + } +} + +void Hakugek2TargetSetup(s32 target, s32 callbackArg) { + Hakugek2Data* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(Hakugek2TargetTick)]; + BattleGetPartPosition(callbackArg, g_BattleModels[callbackArg].boneIndices[0], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->PartPos); + effect->unk1C = target; + effect->unk1E = callbackArg; + func_800BBA84(0x14, callbackArg, 0); +} + +void Hakugek2Start(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(Hakugek2DoubleBufferFlip); + BattleSetLoadTimToVram(D_801B11E8, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, Hakugek2TargetSetup); +} diff --git a/src/magic/hakugek2.h b/src/magic/hakugek2.h new file mode 100644 index 00000000..cfc31810 --- /dev/null +++ b/src/magic/hakugek2.h @@ -0,0 +1,6 @@ +#ifndef HAKUGEK2_H +#define HAKUGEK2_H + +#include "magic.h" + +#endif diff --git a/src/magic/kaen.c b/src/magic/kaen.c new file mode 100644 index 00000000..34a4a2be --- /dev/null +++ b/src/magic/kaen.c @@ -0,0 +1,307 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "kaen.h" +#include "../battle/battle.h" + +// Effect shared by Fire Veil (火とん), Fireball (ファイアボール), Flame (火炎), Ghenghana (ゲヘナガナー) +// and Turk Light (タークス光線). + +void KaenStart(s32 targetMask, s32 callbackArg); + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 g_KaenPrimBuffer[2][0xC000]; +extern void* g_KaenBufferPtr; + +// Only StartFrame/AnimationFrame are common to every magic overlay; the +// rest is payload this effect lays out for itself. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR PartPos; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} KaenData; // size:0x20 + +extern ModelRenderDesc D_801D3EF4; +extern SVECTOR D_801D3EEC; +extern SpriteAnim* D_801D3ED4[]; +extern SpriteRenderDesc D_801D3EC8; +extern SpriteRenderDesc D_801D3EE0; +extern SpriteRenderDesc D_801D3F04; +extern VECTOR D_801B0000; +extern KaenData g_BattleEffectSlots[]; +extern KaenData g_BattleDetachedSlots[]; +extern s16 g_BattleDetachedCursor; +extern s32* D_801D3F10[]; +extern u_long D_801B0FE8[]; +extern void* D_80163C74; +s32 BattleDetachedRegister(void (*func)(void)); +void func_800BBA84(u16 arg0, s32 arg1, s32 arg2); +void KaenTargetTick(void); + +void MAGIC_Kaen(s32 targetMask, s32 callbackArg) { KaenStart(targetMask, callbackArg); } + +void KaenTargetCenterCalc(s16 targetMask, SVECTOR* out) { + VECTOR sum; + SVECTOR pos; + s16 i; + s16 count; + + count = 0; + sum.vx = sum.vy = sum.vz = 0; + for (i = 0; i < 10; i++) { + if ((targetMask >> i) & 1) { + BattleGetPartPosition(i, g_BattleModels[i].boneIndices[0], &pos); + count++; + sum.vx += pos.vx; + sum.vy += pos.vy; + sum.vz += pos.vz; + } + } + out->vx = sum.vx / count; + out->vy = sum.vy / count; + out->vz = sum.vz / count; +} + +void KaenSprayVectorCalc(s32 offset, s16 spread, SVECTOR* out) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + + in.vz = 0; + in.vx = 0; + in.vy = offset + (rand() % spread * 2 - spread); + rot.vy = 0; + rot.vx = rand() % 800 - 400; + rot.vz = rand() % 800 - 400; + RotMatrix(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, out); +} + +void KaenSpriteMatrixSetup(SVECTOR* pos, SVECTOR* rot, s16 scale, s32 offset) { + MATRIX matrix; + MATRIX rotated; + VECTOR dir; + s32 flag; + s16 i; + + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(pos, (VECTOR*)matrix.t, &flag); + if (offset != 0) { + VectorNormal((VECTOR*)matrix.t, &dir); + matrix.t[0] += offset * dir.vx >> 12; + matrix.t[1] += offset * dir.vy >> 12; + matrix.t[2] += offset * dir.vz >> 12; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); +} + +// Same as BarrierDoubleBufferFlip. +static void KaenDoubleBufferFlip(void) { + KaenData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + g_KaenBufferPtr = g_KaenPrimBuffer[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void KaenFlameSpriteTick(void) { + KaenData* effect; + + effect = &g_BattleDetachedSlots[g_BattleDetachedCursor]; + BattleSetBillboardMatrix(&effect->Pos, effect->unk16 == 2 ? 0x2000 : 0x3000, 0); + D_801D3EC8.frames = D_801D3ED4[effect->unk16]; + D_801D3EC8.frameIndex = effect->AnimationFrame; + D_80163C74 = func_800D4D90(&D_801D3EC8, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801D3ED4[effect->unk16]->frameCount) { + effect->StartFrame = -1; + } + } +} + +void KaenEmberTick(void) { + MATRIX rotated; + MATRIX matrix; + SVECTOR rot; + s32 flag; + KaenData* effect; + s16 fade; + s16 scale; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + fade = 8; + if (effect->AnimationFrame >= 12) { + fade = 20 - effect->AnimationFrame; + } + rot.vy = 0; + rot.vx = 0; + rot.vz = effect->unk14; + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + scale = 0x2000; + if (effect->unk16 < 4) { + scale = 0x1400; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(&rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D3EE0.frameIndex = effect->unk16; + D_801D3EE0.color.cd = fade == 8 ? 0x2C : 0x2E; + D_801D3EE0.color.r = D_801D3EE0.color.g = D_801D3EE0.color.b = fade << 4; + D_80163C74 = func_800D4D90(&D_801D3EE0, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + if (effect->Pos.vy > 0) { + effect->Pos.vy = 0; + effect->PartPos.vy *= -1; + } + effect->PartPos.vy += 0x14; + effect->unk14 += 0x200; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void KaenFireballTick(void) { + MATRIX dirMatrix; + MATRIX matrix; + MATRIX composed; + s32 pad[2]; // match: unused, gives the original stack frame + VECTOR scale; + KaenData* effect; + KaenData* child; + + scale = D_801B0000; + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleMatrixFromDirection(&effect->PartPos, &dirMatrix); + RotMatrixYXZ(&D_801D3EEC, &matrix); + MulMatrix0(&dirMatrix, &matrix, &matrix); + ScaleMatrix(&matrix, &scale); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = effect->Pos.vy; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D3EF4.model = D_801D3F10[effect->AnimationFrame % 8]; + g_KaenBufferPtr = func_800D29D4(&D_801D3EF4, g_cDb->unk70, 12, g_KaenBufferPtr); + BattleSetBillboardMatrix(&effect->Pos, 0x1000, 0); + D_80163C74 = func_800D4D90(&D_801D3F04, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->AnimationFrame++; + if (effect->AnimationFrame == 8) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(KaenFlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx + rand() % 800 - 400; + child->Pos.vy = effect->Pos.vy + rand() % 800 - 400; + child->Pos.vz = effect->Pos.vz + rand() % 800 - 400; + child->unk16 = 0; + effect->StartFrame = -1; + } + } +} + +void KaenTargetTick(void) { + s32 pad[12]; // match: unused, gives the original stack frame + KaenData* effect; + KaenData* child; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (D_80062D98 == 0) { + if (effect->AnimationFrame == 0) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(KaenFlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->unk16 = 2; + child = &g_BattleEffectSlots[BattleEffectRegister(KaenFireballTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->PartPos.vx = (effect->PartPos.vx - effect->Pos.vx) / 8; + child->PartPos.vy = (effect->PartPos.vy - effect->Pos.vy) / 8; + child->PartPos.vz = (effect->PartPos.vz - effect->Pos.vz) / 8; + } + if (effect->AnimationFrame == 8) { + i = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(KaenEmberTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 400 - 200; + child->Pos.vz = effect->PartPos.vz + rand() % 400 - 200; + child->Pos.vy = 0; + KaenSprayVectorCalc(-250, 100, &child->PartPos); + child->unk16 = rand() % 20; + i++; + } while (i < 8); + func_800BBA84(0xFA, effect->unk1C, 0); + } + if (effect->AnimationFrame >= 12 && effect->AnimationFrame < 19) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(KaenFlameSpriteTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 1200 - 600; + child->Pos.vy = effect->PartPos.vy + rand() % 1200 - 600; + child->Pos.vz = effect->PartPos.vz + rand() % 1200 - 600; + child->unk16 = 1; + } + if (effect->AnimationFrame == 15) { + func_800D5774(effect->unk1C); + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 30) { + effect->StartFrame = -1; + } + } +} + +void KaenTargetSetup(s32 target, s32 callbackArg) { + KaenData* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(KaenTargetTick)]; + BattleGetPartPosition(callbackArg, g_BattleModels[callbackArg].boneIndices[8], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->PartPos); + effect->unk1C = target; +} + +void KaenStart(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(KaenDoubleBufferFlip); + BattleSetLoadTimToVram(D_801B0FE8, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, KaenTargetSetup); + func_800BBA84(0x163, callbackArg, 0); +} diff --git a/src/magic/kaen.h b/src/magic/kaen.h new file mode 100644 index 00000000..1ce076fd --- /dev/null +++ b/src/magic/kaen.h @@ -0,0 +1,6 @@ +#ifndef KAEN_H +#define KAEN_H + +#include "magic.h" + +#endif diff --git a/src/magic/kaen2.c b/src/magic/kaen2.c new file mode 100644 index 00000000..50f0268d --- /dev/null +++ b/src/magic/kaen2.c @@ -0,0 +1,330 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "kaen2.h" +#include "../battle/battle.h" + +// Effect shared by Fire Shell (火炎弾), Gush (噴火) and S-Mine (Sマイン). + +void Kaen2Start(s32 targetMask, s32 callbackArg); + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 g_Kaen2PrimBuffer[2][0xC000]; +extern void* g_Kaen2BufferPtr; + +// Only StartFrame/AnimationFrame are common to every magic overlay; the +// rest is payload this effect lays out for itself. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR PartPos; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} Kaen2Data; // size:0x20 + +extern ModelRenderDesc D_801D4104; +extern SVECTOR D_801D40EC; +extern SpriteAnim* D_801D40D4[]; +extern SpriteRenderDesc D_801D40C8; +extern SpriteRenderDesc D_801D40E0; +extern SpriteRenderDesc D_801D4114; +extern VECTOR D_801D40F4; +extern Kaen2Data g_BattleEffectSlots[]; +extern Kaen2Data g_BattleDetachedSlots[]; +extern s16 g_BattleDetachedCursor; +extern s32* D_801D4120[]; +extern u_long D_801B1128[]; +extern void* D_80163C74; +s32 BattleDetachedRegister(void (*func)(void)); +void Kaen2TargetSetup(s32 target, s32 callbackArg); +void Kaen2FlameSpriteTick(void); +void Kaen2TargetTick(void); + +void MAGIC_Kaen2(s32 targetMask, s32 callbackArg) { Kaen2Start(targetMask, callbackArg); } + +void Kaen2TargetCenterCalc(s16 targetMask, SVECTOR* out) { + VECTOR sum; + SVECTOR pos; + s16 i; + s16 count; + + count = 0; + sum.vx = sum.vy = sum.vz = 0; + for (i = 0; i < 10; i++) { + if ((targetMask >> i) & 1) { + BattleGetPartPosition(i, g_BattleModels[i].boneIndices[0], &pos); + count++; + sum.vx += pos.vx; + sum.vy += pos.vy; + sum.vz += pos.vz; + } + } + out->vx = sum.vx / count; + out->vy = sum.vy / count; + out->vz = sum.vz / count; +} + +void Kaen2SprayVectorCalc(s32 offset, s16 spread, SVECTOR* out) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + + in.vz = 0; + in.vx = 0; + in.vy = offset + (rand() % spread * 2 - spread); + rot.vy = 0; + rot.vx = rand() % 800 - 400; + rot.vz = rand() % 800 - 400; + RotMatrix(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, out); +} + +void Kaen2SpriteMatrixSetup(SVECTOR* pos, SVECTOR* rot, s16 scale, s32 offset) { + MATRIX matrix; + MATRIX rotated; + VECTOR dir; + s32 flag; + s16 i; + + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(pos, (VECTOR*)matrix.t, &flag); + if (offset != 0) { + VectorNormal((VECTOR*)matrix.t, &dir); + matrix.t[0] += offset * dir.vx >> 12; + matrix.t[1] += offset * dir.vy >> 12; + matrix.t[2] += offset * dir.vz >> 12; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); +} + +// Same as BarrierDoubleBufferFlip. +static void Kaen2DoubleBufferFlip(void) { + Kaen2Data* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + g_Kaen2BufferPtr = g_Kaen2PrimBuffer[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void Kaen2FlameSpriteTick(void) { + Kaen2Data* effect; + + effect = &g_BattleDetachedSlots[g_BattleDetachedCursor]; + BattleSetBillboardMatrix(&effect->Pos, effect->unk16 == 2 ? 0x2000 : 0x3000, 0); + D_801D40C8.frames = D_801D40D4[effect->unk16]; + D_801D40C8.frameIndex = effect->AnimationFrame; + D_80163C74 = func_800D4D90(&D_801D40C8, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + if (effect->unk16 == 1) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->PartPos.vx -= effect->PartPos.vx / 8; + effect->PartPos.vy -= effect->PartPos.vy / 8; + effect->PartPos.vz -= effect->PartPos.vz / 8; + } + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801D40D4[effect->unk16]->frameCount) { + effect->StartFrame = -1; + } + } +} + +void Kaen2EmberTick(void) { + MATRIX rotated; + MATRIX matrix; + SVECTOR rot; + s32 flag; + Kaen2Data* effect; + s16 fade; + s16 scale; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + for (i = 0; i < 3; i++) { + matrix.m[2][i] = 0; + matrix.m[1][i] = 0; + matrix.m[0][i] = 0; + } + fade = 8; + if (effect->AnimationFrame >= 12) { + fade = 20 - effect->AnimationFrame; + } + rot.vy = 0; + rot.vx = 0; + rot.vz = effect->unk14; + SetRotMatrix(&g_BattleWorldView.m); + SetTransMatrix(&g_BattleWorldView.m); + RotTrans(&effect->Pos, (VECTOR*)matrix.t, &flag); + scale = 0x2000; + if (effect->unk16 < 4) { + scale = 0x1400; + } + matrix.m[0][0] = matrix.m[1][1] = matrix.m[2][2] = scale; + RotMatrixYXZ(&rot, &rotated); + MulMatrix(&matrix, &rotated); + SetRotMatrix(&matrix); + SetTransMatrix(&matrix); + D_801D40E0.frameIndex = effect->unk16; + D_801D40E0.color.cd = fade == 8 ? 0x2C : 0x2E; + D_801D40E0.color.r = D_801D40E0.color.g = D_801D40E0.color.b = fade << 4; + D_80163C74 = func_800D4D90(&D_801D40E0, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + if (effect->Pos.vy > 0) { + effect->Pos.vy = 0; + effect->PartPos.vy *= -1; + } + effect->PartPos.vy += 0x14; + effect->unk14 += 0x200; + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x14) { + effect->StartFrame = -1; + } + } +} + +void Kaen2FireballTick(void) { + MATRIX dirMatrix; + MATRIX matrix; + MATRIX composed; + s32 pad[2]; // match: unused, gives the original stack frame + Kaen2Data* effect; + Kaen2Data* child; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleMatrixFromDirection(&effect->PartPos, &dirMatrix); + RotMatrixYXZ(&D_801D40EC, &matrix); + MulMatrix0(&dirMatrix, &matrix, &matrix); + ScaleMatrix(&matrix, &D_801D40F4); + matrix.t[0] = effect->Pos.vx; + matrix.t[1] = effect->Pos.vy; + matrix.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView.m, &matrix, &composed); + SetRotMatrix(&composed); + SetTransMatrix(&composed); + D_801D4104.model = D_801D4120[effect->AnimationFrame % 8]; + g_Kaen2BufferPtr = func_800D29D4(&D_801D4104, g_cDb->unk70, 12, g_Kaen2BufferPtr); + BattleSetBillboardMatrix(&effect->Pos, 0x1000, 0); + D_80163C74 = func_800D4D90(&D_801D4114, (u_long**)&g_cDb->unk70, 12, D_80163C74); + if (D_80062D98 == 0) { + effect->Pos.vx += effect->PartPos.vx; + effect->Pos.vy += effect->PartPos.vy; + effect->Pos.vz += effect->PartPos.vz; + effect->AnimationFrame++; + if (effect->AnimationFrame == 8) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(Kaen2FlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx + rand() % 800 - 400; + child->Pos.vy = effect->Pos.vy + rand() % 800 - 400; + child->Pos.vz = effect->Pos.vz + rand() % 800 - 400; + child->unk16 = 0; + effect->StartFrame = -1; + } + } +} + +void Kaen2TargetTick(void) { + SVECTOR in; + SVECTOR rot; + MATRIX matrix; + Kaen2Data* effect; + Kaen2Data* child; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (D_80062D98 == 0) { + if (effect->AnimationFrame < 12) { + if ((effect->AnimationFrame % 4) == 0) { + child = &g_BattleDetachedSlots[BattleDetachedRegister(Kaen2FlameSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->unk16 = 2; + child = &g_BattleEffectSlots[BattleEffectRegister(Kaen2FireballTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->PartPos.vx = (effect->PartPos.vx - effect->Pos.vx) / 8; + child->PartPos.vy = (effect->PartPos.vy - effect->Pos.vy) / 8; + child->PartPos.vz = (effect->PartPos.vz - effect->Pos.vz) / 8; + func_800BBA84(0x163, effect->unk1E, 0); + } + } + if (effect->AnimationFrame == 14) { + i = 0; + do { + child = &g_BattleEffectSlots[BattleEffectRegister(Kaen2EmberTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 400 - 200; + child->Pos.vz = effect->PartPos.vz + rand() % 400 - 200; + child->Pos.vy = 0; + Kaen2SprayVectorCalc(-250, 100, &child->PartPos); + child->unk16 = rand() % 20; + i++; + } while (i < 8); + func_800BBA84(0x7C, effect->unk1C, 0); + } + if (effect->AnimationFrame >= 12 && effect->AnimationFrame < 19) { + i = 0; + do { + child = &g_BattleDetachedSlots[BattleDetachedRegister(Kaen2FlameSpriteTick)]; + child->Pos.vx = effect->PartPos.vx + rand() % 1200 - 600; + child->Pos.vy = effect->PartPos.vy + rand() % 1200 - 600; + child->Pos.vz = effect->PartPos.vz + rand() % 1200 - 600; + rot.vx = rand() % 1200 - 600; + rot.vy = 0; + rot.vz = rand() % 1200 - 600; + in.vx = 0; + in.vy = -160; + in.vz = 0; + RotMatrixYXZ(&rot, &matrix); + ApplyMatrixSV(&matrix, &in, &child->PartPos); + i++; + child->unk16 = 1; + } while (i < 2); + } + if (effect->AnimationFrame == 15) { + func_800D5774(effect->unk1C); + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 40) { + effect->StartFrame = -1; + } + } +} + +void Kaen2TargetSetup(s32 target, s32 callbackArg) { + Kaen2Data* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(Kaen2TargetTick)]; + BattleGetPartPosition(callbackArg, g_BattleModels[callbackArg].boneIndices[8], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->PartPos); + effect->unk1C = target; + effect->unk1E = callbackArg; +} + +void Kaen2Start(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(Kaen2DoubleBufferFlip); + BattleSetLoadTimToVram(D_801B1128, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, Kaen2TargetSetup); +} diff --git a/src/magic/kaen2.h b/src/magic/kaen2.h new file mode 100644 index 00000000..17e761d8 --- /dev/null +++ b/src/magic/kaen2.h @@ -0,0 +1,6 @@ +#ifndef KAEN2_H +#define KAEN2_H + +#include "magic.h" + +#endif diff --git a/tools/builder/builder/objdiff.go b/tools/builder/builder/objdiff.go index 23967455..2c334913 100644 --- a/tools/builder/builder/objdiff.go +++ b/tools/builder/builder/objdiff.go @@ -113,6 +113,11 @@ var metaOverlays = map[string]metaOverlay{ "lv5deth": {"lv5deth", "magic", "LV.5 Death"}, "choco0": {"choco0", "magic", "choco0"}, "sennkoku": {"sennkoku", "magic", "Death Sentence / Roulette"}, + "kaen": {"kaen", "magic", "KAEN"}, + "kaen2": {"kaen2", "magic", "KAEN2"}, + "gas": {"gas", "magic", "GAS"}, + "hakugek1": {"hakugek1", "magic", "HAKUGEK1"}, + "hakugek2": {"hakugek2", "magic", "HAKUGEK2"}, } func makeObjdiffConfig(b BuildConfig) objdiffConfig {