diff --git a/CMakeLists.txt b/CMakeLists.txt index 14280eff..d7172a18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,8 @@ set(FF7_MAGIC src/magic/brizad.c src/magic/lv5deth.c src/magic/mabaria.c + src/magic/q_ketsu.c + src/magic/q_shu.c src/magic/sennkoku.c src/magic/thunder.c ) diff --git a/config/symbols.magic-q_ketsu.us.txt b/config/symbols.magic-q_ketsu.us.txt new file mode 100644 index 00000000..82e86150 --- /dev/null +++ b/config/symbols.magic-q_ketsu.us.txt @@ -0,0 +1,10 @@ +QKetsuDoubleBufferFlip = 0x801B0010; +QKetsuHitSparkleSpriteTick = 0x801B007C; +QKetsuRisingSparkleSpriteTick = 0x801B0138; +QKetsuSpiralSparkleSpriteTick = 0x801B0264; +QKetsuTrailSparkleSpriteTick = 0x801B03FC; +QKetsuOrbSpriteTick = 0x801B04B4; +QKetsuModelTick = 0x801B08E0; +QKetsuTargetTick = 0x801B0C5C; +QKetsuTargetSetup = 0x801B0F5C; +MAGIC_QKetsu = 0x801B1028; diff --git a/config/symbols.magic-q_shu.us.txt b/config/symbols.magic-q_shu.us.txt new file mode 100644 index 00000000..f7abd24e --- /dev/null +++ b/config/symbols.magic-q_shu.us.txt @@ -0,0 +1,10 @@ +QShuDoubleBufferFlip = 0x801B0010; +QShuHitSparkleSpriteTick = 0x801B007C; +QShuRisingSparkleSpriteTick = 0x801B0138; +QShuSpiralSparkleSpriteTick = 0x801B0264; +QShuTrailSparkleSpriteTick = 0x801B03FC; +QShuOrbSpriteTick = 0x801B04B4; +QShuModelTick = 0x801B08E0; +QShuTargetTick = 0x801B0C5C; +QShuTargetSetup = 0x801B0F5C; +MAGIC_QShu = 0x801B1028; diff --git a/config/us.yaml b/config/us.yaml index 69eaa150..04d0f36c 100644 --- a/config/us.yaml +++ b/config/us.yaml @@ -807,3 +807,31 @@ overlays: - [0, c, sennkoku] - [0x930, data, sennkoku_model] - [0x559C, data, sennkoku_buffers] + # Q_KETSU.BIN -- effect overlay + - name: q_ketsu + disk_path: disks/us/MAGIC/Q_KETSU.BIN + sha1: 66cda108823bda268f71222de2e8aedb1f55faa7 + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-q_ketsu.us.txt + segments: + - [0, rodata, q_ketsu] + - [0x10, c, q_ketsu] + - [0x10A4, data, q_ketsu_model] + - [0xDAE4, data, q_ketsu_buffers] # REVIEW (split): first aligned word after the last non-zero byte; a cut inside the zero fill is byte-neutral + # Q_SHU.BIN -- effect overlay + - name: q_shu + disk_path: disks/us/MAGIC/Q_SHU.BIN + sha1: e180f519db423f6eb419203f59abe619f88b5c11 + base_path: magic + imports: [main, battle] + vram_start: 0x801B0000 + symbol_addrs_path: + - config/symbols.magic-q_shu.us.txt + segments: + - [0, rodata, q_shu] + - [0x10, c, q_shu] + - [0x10A4, data, q_shu_model] + - [0xDAE4, data, q_shu_buffers] # REVIEW (split): first aligned word after the last non-zero byte; a cut inside the zero fill is byte-neutral diff --git a/src/magic/q_ketsu.c b/src/magic/q_ketsu.c new file mode 100644 index 00000000..a8df96df --- /dev/null +++ b/src/magic/q_ketsu.c @@ -0,0 +1,289 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "q_ketsu.h" +#include "../battle/battle.h" + +// Effect shared by Drain (ドレイン), Dance (おどり) and Eyesight (眼力). + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 D_801BDAE8[2][0xC000]; +extern void* D_801D5AE8; +extern SpriteRenderDesc D_801BDA90; +extern SpriteRenderDesc D_801BDA9C; +extern SpriteRenderDesc D_801BDABC; +extern SpriteAnim* D_801BDAA8[]; +extern s32 D_801BD9CC; +extern s32 D_801BD84C; +extern SpriteRenderDesc D_801BDAB0; +extern s32 D_801BD8E4; +extern u8 D_801B10A4[]; +extern ModelRenderDesc D_801B0000; +extern s16 D_801BDAD4; +extern VECTOR D_801BDAD8; +extern SpriteRenderDesc D_801BDAC8; +extern s32 D_801BD9AC; + +void func_800BBA84(u16 arg0, s32 arg1, s32 arg2); +void QKetsuOrbSpriteTick(void); + +// Only StartFrame/AnimationFrame are common to every magic overlay. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR Pos2; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} QKetsuData; // size:0x20 + +extern QKetsuData g_BattleEffectSlots[]; + +// Same as BarrierDoubleBufferFlip. +void QKetsuDoubleBufferFlip(void) { + QKetsuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + D_801D5AE8 = D_801BDAE8[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void QKetsuHitSparkleSpriteTick(void) { + QKetsuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDA90.frameIndex = effect->AnimationFrame; + D_801D5AE8 = func_800D4D90(&D_801BDA90, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->AnimationFrame++; + if (effect->AnimationFrame >= D_801BD9CC) { + effect->StartFrame = -1; + } + } +} + +void QKetsuRisingSparkleSpriteTick(void) { + QKetsuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDA9C.frameIndex = effect->AnimationFrame % D_801BDAA8[effect->unk14]->frameCount; + D_801BDA9C.frames = D_801BDAA8[effect->unk14]; + D_801D5AE8 = func_800D4D90(&D_801BDA9C, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->Pos.vy += 0x64; + effect->AnimationFrame++; + if (effect->AnimationFrame == 10) { + effect->StartFrame = -1; + } + } +} + +void QKetsuSpiralSparkleSpriteTick(void) { + QKetsuData* effect; + SVECTOR vec; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + vec.vx = effect->Pos.vx + (rsin(effect->unk14) * effect->unk18 >> 12); + vec.vz = effect->Pos.vz + (rcos(effect->unk14) * effect->unk18 >> 12); + vec.vy = effect->Pos.vy + effect->unk1A; + BattleSetBillboardMatrix(&vec, 0x2000, 0); + D_801BDAB0.frameIndex = effect->AnimationFrame % D_801BD8E4; + D_801D5AE8 = func_800D4D90(&D_801BDAB0, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->unk1A -= 0x32; + effect->unk14 += 0x80; + effect->unk18 -= effect->unk18 / 10; + effect->AnimationFrame++; + if (effect->AnimationFrame == 20) { + effect->StartFrame = -1; + } + } +} + +void QKetsuTrailSparkleSpriteTick(void) { + QKetsuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDABC.frameIndex = effect->AnimationFrame; + D_801D5AE8 = func_800D4D90(&D_801BDABC, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801BD84C) { + effect->StartFrame = -1; + } + } +} + +void QKetsuOrbSpriteTick(void) { + QKetsuData* effect; + QKetsuData* child; + SVECTOR vec; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + vec.vx = effect->Pos.vx + (rsin(effect->unk14) * effect->unk18 >> 12); + vec.vy = effect->Pos.vy + (rcos(effect->unk14) * effect->unk18 >> 12); + vec.vz = effect->Pos.vz; + BattleSetBillboardMatrix(&vec, (s16)(effect->unk16 * 5 / effect->unk1C), 0); + D_801BDAC8.frameIndex = effect->AnimationFrame % D_801BD9AC; + D_801D5AE8 = func_800D4D90(&D_801BDAC8, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + if (effect->AnimationFrame < 0x1E) { + effect->unk16 += 0xC0; + } + if ((u16)(effect->AnimationFrame - 0x1E) < 10) { + effect->Pos.vx += effect->Pos2.vx; + effect->Pos.vy += effect->Pos2.vy; + effect->Pos.vz += effect->Pos2.vz; + effect->unk18 = (u32)(rsin(effect->unk1A += 0xCC) * 25) >> 8; + effect->unk14 += 0x200; + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuTrailSparkleSpriteTick)]; + 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; + } + if (effect->AnimationFrame >= 0x29) { + effect->unk16 -= 0x100; + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuRisingSparkleSpriteTick)]; + child->Pos.vx = effect->Pos.vx + rand() % 600 - 300; + child->Pos.vy = effect->Pos.vy + rand() % 600 - 300; + child->Pos.vz = effect->Pos.vz + rand() % 600 - 300; + child->unk14 = rand() & 1; + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x3C) { + effect->StartFrame = -1; + } + } +} + +void QKetsuModelTick(void) { + QKetsuData* effect; + QKetsuData* child; + MATRIX mat; + MATRIX view; + ModelRenderDesc desc = D_801B0000; + SVECTOR rot; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->AnimationFrame << 7; + RotMatrixYXZ(&rot, &mat); + ScaleMatrix(&mat, &D_801BDAD8); + mat.t[0] = effect->Pos.vx; + mat.t[1] = effect->Pos.vy; + mat.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView, &mat, &view); + SetRotMatrix(&view); + SetTransMatrix(&view); + desc.color = D_801BDAD4; + desc.clut = effect->AnimationFrame % 5 * 0xC0; + D_801D5AE8 = func_800D29D4(&desc, (u_long**)&g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + if (effect->AnimationFrame < 0x14) { + effect->Pos.vx += effect->Pos2.vx; + effect->Pos.vy += effect->Pos2.vy; + effect->Pos.vz += effect->Pos2.vz; + D_801BDAD4 += 6; + } + if ((u16)(effect->AnimationFrame - 0x14) < 0x1E) { + for (i = 0; i < 1; i++) { + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuSpiralSparkleSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->unk14 = rand() & 0xFFF; + child->unk18 = 1000; + child->unk1A = 1000; + } + } + if ((u16)(effect->AnimationFrame - 0x26) < 6) { + if (!(effect->AnimationFrame % 2)) { + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuOrbSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy - 0x1F4; + child->Pos.vz = effect->Pos.vz; + child->Pos2.vx = -(u16)effect->Pos2.vx * 2; + child->Pos2.vy = -(u16)effect->Pos2.vy * 2; + child->Pos2.vz = -(u16)effect->Pos2.vz * 2; + child->unk16 = 0x100; + child->unk1A = 0; + child->unk18 = 0; + child->unk1C = (effect->AnimationFrame - 0x26) / 2 + 5; + } + } + if (effect->AnimationFrame >= 0x33) { + if (!(D_801BDAD4 -= 6)) { + effect->StartFrame = -1; + } + } + effect->AnimationFrame++; + } +} + +void QKetsuTargetTick(void) { + QKetsuData* effect; + QKetsuData* child; + s32 y; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (!D_80062D98) { + if (!effect->AnimationFrame) { + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuModelTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy - 0x190; + child->Pos.vz = effect->Pos.vz; + child->Pos2.vx = (effect->Pos2.vx - child->Pos.vx) / 20; + y = child->Pos.vy + 0x320; + child->Pos2.vy = (effect->Pos2.vy - y) / 20; + child->Pos2.vz = (effect->Pos2.vz - child->Pos.vz) / 20; + } + if (effect->AnimationFrame == 0x5A) { + func_800D5774(effect->unk1C); + func_800BBA84(7, effect->unk1E, 0x40); + } + if (effect->AnimationFrame >= 0x57 && effect->AnimationFrame < 0x66) { + for (i = 0; i < 4; i++) { + child = &g_BattleEffectSlots[BattleEffectRegister(QKetsuHitSparkleSpriteTick)]; + BattleGetPartPosition(effect->unk1E, rand() % g_BattleModels[effect->unk1E].numBones, &child->Pos); + child->Pos.vx += rand() % 600 - 300; + child->Pos.vy += rand() % 600 - 300; + child->Pos.vz += rand() % 600 - 300; + } + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x6E) { + effect->StartFrame = -1; + } + } +} + +void QKetsuTargetSetup(s32 target, s32 target2) { + QKetsuData* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(QKetsuTargetTick)]; + BattleGetPartPosition(target2, g_BattleModels[target2].boneIndices[0], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->Pos2); + effect->unk1C = target; + effect->unk1E = target2; +} + +void MAGIC_QKetsu(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(QKetsuDoubleBufferFlip); + BattleSetLoadTimToVram(D_801B10A4, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, QKetsuTargetSetup); + func_800BBA84(0x7E, callbackArg, 0x40); +} diff --git a/src/magic/q_ketsu.h b/src/magic/q_ketsu.h new file mode 100644 index 00000000..b1f14f02 --- /dev/null +++ b/src/magic/q_ketsu.h @@ -0,0 +1,6 @@ +#ifndef Q_KETSU_H +#define Q_KETSU_H + +#include "magic.h" + +#endif diff --git a/src/magic/q_shu.c b/src/magic/q_shu.c new file mode 100644 index 00000000..86f5dda2 --- /dev/null +++ b/src/magic/q_shu.c @@ -0,0 +1,290 @@ +//! PSYQ=3.3 CC1=2.6.3 + +#include "common.h" +#include "magic.h" +#include "q_shu.h" +#include "../battle/battle.h" + +// Effect shared by Aspil (アスピル), Energy Siphon (エネルギー吸収), Magic Cutter (魔吸軟骨剣), +// Dance (おどり) and Eyesight (眼力). + +// Primitive buffer pages, one 0xC000 page per double-buffered frame. +extern u8 D_801BDAE8[2][0xC000]; +extern void* D_801D5AE8; +extern SpriteRenderDesc D_801BDA90; +extern SpriteRenderDesc D_801BDA9C; +extern SpriteRenderDesc D_801BDABC; +extern SpriteAnim* D_801BDAA8[]; +extern s32 D_801BD9CC; +extern s32 D_801BD84C; +extern SpriteRenderDesc D_801BDAB0; +extern s32 D_801BD8E4; +extern u8 D_801B10A4[]; +extern ModelRenderDesc D_801B0000; +extern s16 D_801BDAD4; +extern VECTOR D_801BDAD8; +extern SpriteRenderDesc D_801BDAC8; +extern s32 D_801BD9AC; + +void func_800BBA84(u16 arg0, s32 arg1, s32 arg2); +void QShuOrbSpriteTick(void); + +// Only StartFrame/AnimationFrame are common to every magic overlay. +typedef struct { + /* 0x00 */ s16 StartFrame; + /* 0x02 */ s16 AnimationFrame; + /* 0x04 */ SVECTOR Pos; + /* 0x0C */ SVECTOR Pos2; + /* 0x14 */ s16 unk14; + /* 0x16 */ s16 unk16; + /* 0x18 */ s16 unk18; + /* 0x1A */ s16 unk1A; + /* 0x1C */ s16 unk1C; + /* 0x1E */ s16 unk1E; +} QShuData; // size:0x20 + +extern QShuData g_BattleEffectSlots[]; + +// Same as BarrierDoubleBufferFlip. +void QShuDoubleBufferFlip(void) { + QShuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + D_801D5AE8 = D_801BDAE8[effect->AnimationFrame]; + effect->AnimationFrame ^= 1; + if (g_BattleEffectCount < 2) { + effect->StartFrame = -1; + } +} + +void QShuHitSparkleSpriteTick(void) { + QShuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDA90.frameIndex = effect->AnimationFrame; + D_801D5AE8 = func_800D4D90(&D_801BDA90, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->AnimationFrame++; + if (effect->AnimationFrame >= D_801BD9CC) { + effect->StartFrame = -1; + } + } +} + +void QShuRisingSparkleSpriteTick(void) { + QShuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDA9C.frameIndex = effect->AnimationFrame % D_801BDAA8[effect->unk14]->frameCount; + D_801BDA9C.frames = D_801BDAA8[effect->unk14]; + D_801D5AE8 = func_800D4D90(&D_801BDA9C, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->Pos.vy += 0x64; + effect->AnimationFrame++; + if (effect->AnimationFrame == 10) { + effect->StartFrame = -1; + } + } +} + +void QShuSpiralSparkleSpriteTick(void) { + QShuData* effect; + SVECTOR vec; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + vec.vx = effect->Pos.vx + (rsin(effect->unk14) * effect->unk18 >> 12); + vec.vz = effect->Pos.vz + (rcos(effect->unk14) * effect->unk18 >> 12); + vec.vy = effect->Pos.vy + effect->unk1A; + BattleSetBillboardMatrix(&vec, 0x2000, 0); + D_801BDAB0.frameIndex = effect->AnimationFrame % D_801BD8E4; + D_801D5AE8 = func_800D4D90(&D_801BDAB0, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->unk1A -= 0x32; + effect->unk14 += 0x80; + effect->unk18 -= effect->unk18 / 10; + effect->AnimationFrame++; + if (effect->AnimationFrame == 20) { + effect->StartFrame = -1; + } + } +} + +void QShuTrailSparkleSpriteTick(void) { + QShuData* effect; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + BattleSetBillboardMatrix(&effect->Pos, 0x2000, 0); + D_801BDABC.frameIndex = effect->AnimationFrame; + D_801D5AE8 = func_800D4D90(&D_801BDABC, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + effect->AnimationFrame++; + if (effect->AnimationFrame == D_801BD84C) { + effect->StartFrame = -1; + } + } +} + +void QShuOrbSpriteTick(void) { + QShuData* effect; + QShuData* child; + SVECTOR vec; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + vec.vx = effect->Pos.vx + (rsin(effect->unk14) * effect->unk18 >> 12); + vec.vy = effect->Pos.vy + (rcos(effect->unk14) * effect->unk18 >> 12); + vec.vz = effect->Pos.vz; + BattleSetBillboardMatrix(&vec, (s16)(effect->unk16 * 5 / effect->unk1C), 0); + D_801BDAC8.frameIndex = effect->AnimationFrame % D_801BD9AC; + D_801D5AE8 = func_800D4D90(&D_801BDAC8, g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + if (effect->AnimationFrame < 0x1E) { + effect->unk16 += 0xC0; + } + if ((u16)(effect->AnimationFrame - 0x1E) < 10) { + effect->Pos.vx += effect->Pos2.vx; + effect->Pos.vy += effect->Pos2.vy; + effect->Pos.vz += effect->Pos2.vz; + effect->unk18 = (u32)(rsin(effect->unk1A += 0xCC) * 25) >> 8; + effect->unk14 += 0x200; + child = &g_BattleEffectSlots[BattleEffectRegister(QShuTrailSparkleSpriteTick)]; + 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; + } + if (effect->AnimationFrame >= 0x29) { + effect->unk16 -= 0x100; + child = &g_BattleEffectSlots[BattleEffectRegister(QShuRisingSparkleSpriteTick)]; + child->Pos.vx = effect->Pos.vx + rand() % 600 - 300; + child->Pos.vy = effect->Pos.vy + rand() % 600 - 300; + child->Pos.vz = effect->Pos.vz + rand() % 600 - 300; + child->unk14 = rand() & 1; + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x3C) { + effect->StartFrame = -1; + } + } +} + +void QShuModelTick(void) { + QShuData* effect; + QShuData* child; + MATRIX mat; + MATRIX view; + ModelRenderDesc desc = D_801B0000; + SVECTOR rot; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + rot.vz = 0; + rot.vx = 0; + rot.vy = effect->AnimationFrame << 7; + RotMatrixYXZ(&rot, &mat); + ScaleMatrix(&mat, &D_801BDAD8); + mat.t[0] = effect->Pos.vx; + mat.t[1] = effect->Pos.vy; + mat.t[2] = effect->Pos.vz; + CompMatrix(&g_BattleWorldView, &mat, &view); + SetRotMatrix(&view); + SetTransMatrix(&view); + desc.color = D_801BDAD4; + desc.clut = effect->AnimationFrame % 5 * 0xC0; + D_801D5AE8 = func_800D29D4(&desc, (u_long**)&g_cDb->unk70, 12, D_801D5AE8); + if (!D_80062D98) { + if (effect->AnimationFrame < 0x14) { + effect->Pos.vx += effect->Pos2.vx; + effect->Pos.vy += effect->Pos2.vy; + effect->Pos.vz += effect->Pos2.vz; + D_801BDAD4 += 6; + } + if ((u16)(effect->AnimationFrame - 0x14) < 0x1E) { + for (i = 0; i < 1; i++) { + child = &g_BattleEffectSlots[BattleEffectRegister(QShuSpiralSparkleSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy; + child->Pos.vz = effect->Pos.vz; + child->unk14 = rand() & 0xFFF; + child->unk18 = 1000; + child->unk1A = 1000; + } + } + if ((u16)(effect->AnimationFrame - 0x26) < 6) { + if (!(effect->AnimationFrame % 2)) { + child = &g_BattleEffectSlots[BattleEffectRegister(QShuOrbSpriteTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy - 0x1F4; + child->Pos.vz = effect->Pos.vz; + child->Pos2.vx = -(u16)effect->Pos2.vx * 2; + child->Pos2.vy = -(u16)effect->Pos2.vy * 2; + child->Pos2.vz = -(u16)effect->Pos2.vz * 2; + child->unk16 = 0x100; + child->unk1A = 0; + child->unk18 = 0; + child->unk1C = (effect->AnimationFrame - 0x26) / 2 + 5; + } + } + if (effect->AnimationFrame >= 0x33) { + if (!(D_801BDAD4 -= 6)) { + effect->StartFrame = -1; + } + } + effect->AnimationFrame++; + } +} + +void QShuTargetTick(void) { + QShuData* effect; + QShuData* child; + s32 y; + s16 i; + + effect = &g_BattleEffectSlots[g_BattleEffectCursor]; + if (!D_80062D98) { + if (!effect->AnimationFrame) { + child = &g_BattleEffectSlots[BattleEffectRegister(QShuModelTick)]; + child->Pos.vx = effect->Pos.vx; + child->Pos.vy = effect->Pos.vy - 0x190; + child->Pos.vz = effect->Pos.vz; + child->Pos2.vx = (effect->Pos2.vx - child->Pos.vx) / 20; + y = child->Pos.vy + 0x320; + child->Pos2.vy = (effect->Pos2.vy - y) / 20; + child->Pos2.vz = (effect->Pos2.vz - child->Pos.vz) / 20; + } + if (effect->AnimationFrame == 0x5A) { + func_800D5774(effect->unk1C); + func_800BBA84(0x1F, effect->unk1E, 0x40); + } + if (effect->AnimationFrame >= 0x57 && effect->AnimationFrame < 0x66) { + for (i = 0; i < 4; i++) { + child = &g_BattleEffectSlots[BattleEffectRegister(QShuHitSparkleSpriteTick)]; + BattleGetPartPosition(effect->unk1E, rand() % g_BattleModels[effect->unk1E].numBones, &child->Pos); + child->Pos.vx += rand() % 600 - 300; + child->Pos.vy += rand() % 600 - 300; + child->Pos.vz += rand() % 600 - 300; + } + } + effect->AnimationFrame++; + if (effect->AnimationFrame == 0x6E) { + effect->StartFrame = -1; + } + } +} + +void QShuTargetSetup(s32 target, s32 target2) { + QShuData* effect; + + effect = &g_BattleEffectSlots[BattleEffectRegister(QShuTargetTick)]; + BattleGetPartPosition(target2, g_BattleModels[target2].boneIndices[0], &effect->Pos); + BattleGetPartPosition(target, g_BattleModels[target].boneIndices[0], &effect->Pos2); + effect->unk1C = target; + effect->unk1E = target2; +} + +void MAGIC_QShu(s32 targetMask, s32 callbackArg) { + BattleEffectRegister(QShuDoubleBufferFlip); + BattleSetLoadTimToVram(D_801B10A4, 0, 0, 0); + MagicAnimationRegister(targetMask, callbackArg, 4, QShuTargetSetup); + func_800BBA84(0x7E, callbackArg, 0x40); +} diff --git a/src/magic/q_shu.h b/src/magic/q_shu.h new file mode 100644 index 00000000..dfadd9ee --- /dev/null +++ b/src/magic/q_shu.h @@ -0,0 +1,6 @@ +#ifndef Q_SHU_H +#define Q_SHU_H + +#include "magic.h" + +#endif diff --git a/tools/builder/builder/objdiff.go b/tools/builder/builder/objdiff.go index 23967455..f02b0b06 100644 --- a/tools/builder/builder/objdiff.go +++ b/tools/builder/builder/objdiff.go @@ -113,6 +113,8 @@ var metaOverlays = map[string]metaOverlay{ "lv5deth": {"lv5deth", "magic", "LV.5 Death"}, "choco0": {"choco0", "magic", "choco0"}, "sennkoku": {"sennkoku", "magic", "Death Sentence / Roulette"}, + "q_ketsu": {"q_ketsu", "magic", "Q_KETSU"}, + "q_shu": {"q_shu", "magic", "Q_SHU"}, } func makeObjdiffConfig(b BuildConfig) objdiffConfig {