sa1: reference water_effects from sa2

This commit is contained in:
Oliver Bell committed 2026-04-23 21:14:18 +01:00
1 parent 15e38aff6c
commit 67ecd33583
11 files changed
+148 -562

No files matched your search

@@ -4,7 +4,7 @@
#include "core.h"
#include "game/shared/stage/player.h"
extern void sub_804C40C(void);
extern void InitWaterPalettes(void);
struct Task *SpawnDrowningCountdownNum(Player *p, s32 countdown);
struct Task *SpawnAirBubbles(s32 p0, s32 p1, s32 p2, s32 p3);
+1
View File
@@ -272,6 +272,7 @@ SA2_C_FILES := \
src/game/shared/stage/collision_1.c \
src/game/shared/stage/collision_2.c \
src/game/shared/stage/collision_3.c \
src/game/shared/stage/water_effects.c \
src/game/sa1/ui/game_2_0.c \
src/game/sa1/ui/game_2_1.c \
src/game/sa1/ui/stage_results.c \
@@ -269,7 +269,7 @@ _08055622:
cmp r0, #1
bhi _08055672
bl LoadPalette423Anim
bl sub_804C40C
bl InitWaterPalettes
_08055672:
ldr r0, [r4, #0x20]
bl TaskDestroy
@@ -332,7 +332,7 @@ _08055708:
lsrs r0, r0, #0x18
cmp r0, #1
bhi _0805571A
bl sub_804C40C
bl InitWaterPalettes
_0805571A:
add sp, #4
pop {r3, r4, r5}
+1 -1
View File
@@ -144,7 +144,7 @@ SECTIONS {
sa2/src/game/sa1/stage/player_super_sonic.o(.text);
sa2/src/game/shared/stage/item_tasks.o(.text);
sa2/src/game/shared/stage/underwater_effects.o(.text);
src/game/shared/stage/water_effects.o(.text);
sa2/src/game/shared/stage/water_effects.o(.text);
sa2/src/game/shared/stage/screen_shake.o(.text);
/* NOTE: input_recorder was not part of the engine in this version */
+1 -1
View File
@@ -898,7 +898,7 @@ thumb_func 0x0804c12c sub_804C12C
thumb_func 0x0804c1c0 sub_804C1C0
thumb_func 0x0804c2e8 sub_804C2E8
thumb_func 0x0804c3cc sub_804C3CC
thumb_func 0x0804c40c sub_804C40C
thumb_func 0x0804c40c InitWaterPalettes
thumb_func 0x0804c598 sub_804C598
thumb_func 0x0804c5d8 sub_804C5D8
thumb_func 0x0804c668 sub_804C668
-501
View File
@@ -1,501 +0,0 @@
#include "global.h"
#include "core.h"
#include "flags.h"
#include "malloc_vram.h"
#include "task.h"
#include "trig.h"
#include "bg_triangles.h"
#if (GAME == GAME_SA2)
#include "game/boost_effect.h"
#endif
#include "game/globals.h"
#include "game/shared/stage/player.h"
#include "game/shared/stage/camera.h"
#include "game/shared/stage/mp_sprite_task.h"
#include "data/sa1/sprite_data.h"
#include "game/shared/stage/water_effects.h"
#include "constants/sa1/animations.h"
#include "constants/sa1/zones.h"
#include "constants/sa1/vram_hardcoded.h"
typedef struct {
/* 0x00 */ s32 x;
/* 0x04 */ s32 y;
/* 0x08 */ u8 filler0[0x14];
/* 0x1C */ Sprite s;
/* 0x4C */ u8 filler4C[0xC];
} RunOnWaterEffect; /* size: 0x58 */
// TODO: Make static again
void Task_StageWaterTask(void);
static void Task_RunOnWaterEffect(void);
void TaskDestructor_WaterSurface(struct Task *);
void SA2_LABEL(sub_8011A4C)(void);
void SA2_LABEL(VCountIntr_8011ACC)(void);
static void TaskDestructor_RunOnWaterEffect(struct Task *);
#if (GAME == GAME_SA1)
// SA2 allocates these in the Task, to save global memory.
// Water is only used in one stage, after all.
u16 sPaletteBuffer[(16 * 2) * 16] = { 0 };
#endif
Water gWater = { 0 };
#define WATER_MASK_COLOR_A 0x7BDE
#define WATER_MASK_COLOR_B 0x739C
#define WATER_MASK_A ((WATER_MASK_COLOR_A << 16) | WATER_MASK_COLOR_A)
#define WATER_MASK_B ((WATER_MASK_COLOR_B << 16) | WATER_MASK_COLOR_B)
#define WATER_SURFACE_SPRITE_COUNT ((DISPLAY_WIDTH + 16) / 16)
#define WATER_MASK_PALETTE_CHUNK(in, waterMask) \
({ \
u32 temp1, temp2; \
temp1 = (in); \
temp2 = temp1; \
temp2 = ((temp2 & WATER_MASK_A) + (((temp1 & WATER_MASK_B) + (WATER_MASK_B & (waterMask))) >> 1)); \
temp2 >>= 1; \
})
#if (GAME == GAME_SA2)
static const u16 gUnknown_080D550C[NUM_CHARACTERS] = {
SA2_ANIM_UNDERWATER_1UP_SONIC, SA2_ANIM_UNDERWATER_1UP_CREAM, SA2_ANIM_UNDERWATER_1UP_TAILS,
SA2_ANIM_UNDERWATER_1UP_KNUCKLES, SA2_ANIM_UNDERWATER_1UP_AMY,
};
#endif
#if (GAME == GAME_SA2)
static void inline CopyPalette(u32 *dst, u32 *src, s32 length)
{
u32 r2 = length >> 4;
while (r2-- > 0) {
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
}
}
#endif
static inline void MaskPaletteWithUnderwaterColor_inline(u32 *dst, u32 *src, u32 mask, s32 size)
{
u32 k = (size >> 4);
while (k-- > 0) {
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
}
}
#if (GAME == GAME_SA1)
void sub_804C40C(void)
{
Water *water = &gWater;
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[0], (u32 *)&gObjPalette[0], water->mask, 16 * 16);
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[16 * 16], (u32 *)&gBgPalette[0], water->mask, 16 * 16);
}
#endif
#if (GAME == GAME_SA2)
void InitWaterPalettes(void)
{
u16 animId, character;
const ACmd **animation;
u32 pal;
Water *water = &gWater;
WaterData *wd = TASK_DATA(water->t);
if (IS_MULTI_PLAYER) {
u8 i = 0, j = 0;
for (; j < 4; j++) {
if ((gMultiplayerConnections >> j) & 0x1) {
i++;
}
}
for (j = 0; j < i; j++) {
character = gMultiplayerCharacters[j];
animId = gUnknown_080D550C[character];
animation = gAnimations[animId];
pal = animation[0]->pal.palId;
#ifndef NON_MATCHING
{
const u16 *src = gSpritePalettes[pal];
CopyPalette((u32 *)wd->pal[j], (u32 *)src, 16);
};
#else
CopyPalette((u32 *)wd->pal[j], (u32 *)gSpritePalettes[pal], 16);
#endif
}
} else {
character = gPlayer.character;
animId = gUnknown_080D550C[character];
animation = gAnimations[animId];
pal = animation[0]->pal.palId;
CopyPalette((u32 *)wd->pal[0], (u32 *)gSpritePalettes[pal], 16);
character = gPlayer.character;
animId = sCharacterPalettesBoostEffect[character];
animation = gAnimations[animId];
pal = animation[0]->pal.palId;
CopyPalette((u32 *)wd->pal[1], (u32 *)gSpritePalettes[pal], 16);
}
animId = SA2_ANIM_PALETTE_554;
animation = gAnimations[animId];
pal = (animation[0]->pal.palId + 4);
CopyPalette((u32 *)wd->pal[4], (u32 *)gSpritePalettes[pal], 12 * 16);
MaskPaletteWithUnderwaterColor_inline((u32 *)wd->pal[16], (u32 *)gBgPalette, water->mask, 16 * 16);
}
#endif
#if (GAME == GAME_SA1)
void LoadPalette423Anim(void)
{
Sprite s;
s.graphics.dest = NULL;
s.graphics.anim = SA1_ANIM_LOAD_PALETTE_423;
s.variant = 0;
s.oamFlags = 0;
s.frameFlags = 0;
s.graphics.size = 0;
s.prevVariant = -1;
s.qAnimDelay = Q(0);
s.animSpeed = SPRITE_ANIM_SPEED(1.0);
s.palId = 0;
UpdateSpriteAnimation(&s);
}
#endif
void CreateStageWaterTask(s32 waterLevel, u32 p1, u32 mask)
{
gWater.currentWaterLevel = waterLevel;
gWater.targetWaterLevel = waterLevel;
gWater.SA2_LABEL(unk2) = 0xFF;
gWater.SA2_LABEL(unk1) = -1;
gWater.unk8 = mask & 0x100;
gWater.mask = p1;
if (waterLevel >= 0) {
Sprite *s = &gWater.s;
s->graphics.dest = VRAM_RESERVED_WATER_SURFACE;
s->graphics.size = 0;
s->graphics.anim = SA1_ANIM_WATER_SURFACE;
s->variant = 0;
s->prevVariant |= -1;
s->oamFlags = SPRITE_OAM_ORDER(0);
s->qAnimDelay = 0;
s->animSpeed = SPRITE_ANIM_SPEED(1.0);
s->palId = 0;
s->frameFlags = SPRITE_FLAG(PRIORITY, 0);
UpdateSpriteAnimation(s);
gWater.t = TaskCreate(Task_StageWaterTask, sizeof(WaterData), 0xFFFE, 0, TaskDestructor_WaterSurface);
}
}
void Task_StageWaterTask(void)
{
Water *water = &gWater;
struct Camera *cam = &gCamera;
bool32 active;
Sprite *s;
#if (HAS_RUN_ON_WATER)
if ((gCurrentLevel == LEVEL_INDEX(ZONE_1, ACT_1)) && (I(gPlayer.qWorldX) > 6665) && (I(gPlayer.qWorldX) <= 10650)) {
water->isActive = TRUE;
} else {
water->isActive = FALSE;
}
if (water->isActive != TRUE) {
gFlags &= ~MOVESTATE_IN_WATER;
return;
}
#endif
if (gStageTime & 1) {
if (water->currentWaterLevel != water->targetWaterLevel) {
if (water->currentWaterLevel < water->targetWaterLevel)
water->currentWaterLevel++;
else if (water->currentWaterLevel > water->targetWaterLevel)
water->currentWaterLevel--;
}
}
if (water->currentWaterLevel <= cam->y) {
water->SA2_LABEL(unk2) = 0;
} else if (water->currentWaterLevel < cam->y + DISPLAY_HEIGHT) {
water->SA2_LABEL(unk2) = water->currentWaterLevel - cam->y;
} else {
water->SA2_LABEL(unk2) = 0xFF;
}
gVBlankCallbacks[gNumVBlankCallbacks++] = SA2_LABEL(sub_8011A4C);
gFlags |= FLAGS_EXECUTE_VBLANK_CALLBACKS;
if (water->SA2_LABEL(unk1) > 0 && water->SA2_LABEL(unk1) < DISPLAY_HEIGHT) {
s = &water->s;
s->x = -((cam->x + ((gStageTime + 1) >> 2)) & 15);
s->y = water->SA2_LABEL(unk2) + 1;
s->frameFlags |= (SPRITE_FLAG_MASK_19 | SPRITE_FLAG_MASK_18);
UpdateSpriteAnimation(s);
if (gStageTime & 0x2) {
u16 y = s->y - 1;
if (y < DISPLAY_WIDTH - 1) {
s32 count = WATER_SURFACE_SPRITE_COUNT;
do {
DisplaySprite(s);
s->x += 16;
} while (--count >= 0);
}
}
}
if (water->SA2_LABEL(unk2) > 0 && water->SA2_LABEL(unk2) < DISPLAY_HEIGHT) {
gIntrTable[INTR_INDEX_VCOUNT] = SA2_LABEL(VCountIntr_8011ACC);
gVCountSetting = water->SA2_LABEL(unk2) - 1;
gFlags |= FLAGS_40;
} else {
gIntrTable[INTR_INDEX_VCOUNT] = gIntrTableTemplate[INTR_INDEX_VCOUNT];
gFlags &= ~FLAGS_40;
}
#if (GAME == GAME_SA1)
if (water->SA2_LABEL(unk2) >= DISPLAY_HEIGHT) {
gFlags &= ~FLAGS_EXECUTE_HBLANK_COPY;
} else {
#ifndef NON_MATCHING
UNUSED u32 _;
#endif
u32 r5, i;
s32 r3 = CLAMP_SIN_PERIOD(((gStageTime * 4) + (((gBgScrollRegs[3][1] + water->SA2_LABEL(unk2)) % 32u) << 6)));
SA2_LABEL(sub_8007738)
(3, 0, 32, r3, 2, 64, (((gStageTime * 2 + (((gBgScrollRegs[3][1] + water->SA2_LABEL(unk2)) % 32u) << 5))) + 0x100) & ONE_CYCLE, 10,
32, gBgScrollRegs[3][0], gBgScrollRegs[3][1]);
for (r5 = 32, i = 1; r5 < DISPLAY_HEIGHT; i++, r5 += 32) {
DmaCopy32(3, gBgOffsetsHBlankPrimary, gBgOffsetsHBlankPrimary + ((gHBlankCopySize << 5) * i), (gHBlankCopySize << 5));
}
if (water->SA2_LABEL(unk2)) {
gFlags &= ~FLAGS_EXECUTE_HBLANK_COPY;
}
}
#endif
}
#if (HAS_RUN_ON_WATER)
void CreateRunOnWaterEffect(void)
{
struct Task *t = TaskCreate(Task_RunOnWaterEffect, sizeof(RunOnWaterEffect), 0x4001, 0, TaskDestructor_RunOnWaterEffect);
RunOnWaterEffect *effect = TASK_DATA(t);
Sprite *s = &effect->s;
s->graphics.dest = VramMalloc(12);
s->graphics.size = 0;
s->graphics.anim = SA2_ANIM_WATER_RUNNING_PARTICLES;
s->variant = 0;
s->prevVariant = -1;
s->oamFlags = SPRITE_OAM_ORDER(7);
s->qAnimDelay = 0;
s->animSpeed = SPRITE_ANIM_SPEED(1.0);
s->palId = 0;
s->frameFlags = SPRITE_FLAG(PRIORITY, 2);
}
static void Task_RunOnWaterEffect(void)
{
RunOnWaterEffect *effect = TASK_DATA(gCurTask);
Sprite *s = &effect->s;
Player *p = &gPlayer;
u32 newMovestate;
if ((p->moveState & (MOVESTATE_20000 | MOVESTATE_DEAD)) != MOVESTATE_20000) {
TaskDestroy(gCurTask);
return;
}
effect->x = I(p->qWorldX);
effect->y = gWater.currentWaterLevel;
s->x = effect->x - gCamera.x;
s->y = effect->y - gCamera.y;
if (!(p->moveState & MOVESTATE_FACING_LEFT)) {
s->frameFlags &= ~SPRITE_FLAG_MASK_X_FLIP;
} else {
s->frameFlags |= SPRITE_FLAG_MASK_X_FLIP;
}
UpdateSpriteAnimation(s);
DisplaySprite(s);
}
#endif
#if (GAME == GAME_SA1)
void unused_DestroygWaterTask(void)
{
TaskDestroy(gWater.t);
gWater.t = NULL;
}
#endif
struct Task *CreateWaterfallSurfaceHitEffect(s32 x, s32 y)
{
struct Task *t = CreateMultiplayerSpriteTask(x, y, 0x10, 0, Task_UpdateMpSpriteTaskSprite, TaskDestructor_MultiplayerSpriteTask);
MultiplayerSpriteTask *ts = TASK_DATA(t);
Sprite *s = &ts->s;
s->graphics.dest = VramMalloc(12);
s->graphics.anim = SA1_ANIM_WATER_FALL_HIT_SURFACE;
s->variant = 0;
s->oamFlags = SPRITE_OAM_ORDER(7);
s->frameFlags = SPRITE_FLAG(PRIORITY, 2);
return t;
}
// At the bottom of the file in SA1!
#if (GAME == GAME_SA2)
UNUSED void MaskPaletteWithUnderwaterColor(u32 *dst, u32 *src, u32 mask, s32 size)
{
#ifndef NON_MATCHING
u32 k = (size >> 4);
while (k-- > 0) {
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
}
#else
MaskPaletteWithUnderwaterColor_inline(dst, src, mask, size);
#endif
}
#endif
void TaskDestructor_WaterSurface(struct Task *t)
{
Water *water = &gWater;
gFlags &= ~FLAGS_40;
gIntrTable[INTR_INDEX_VCOUNT] = gIntrTableTemplate[INTR_INDEX_VCOUNT];
water->t = NULL;
#if (GAME == GAME_SA1)
DmaFill32(3, 0, sPaletteBuffer, 60 * 16);
#endif
}
void SA2_LABEL(sub_8011A4C)(void)
{
Water *water = &gWater;
#ifdef BUG_FIX
if (water->t)
#endif
{
#if (GAME == GAME_SA1)
WaterData *wd = TASK_DATA(water->t);
#endif
u32 unk2;
unk2 = water->SA2_LABEL(unk2);
water->SA2_LABEL(unk1) = unk2;
water->unk8 &= ~0x1;
// TODO: This surely can be matched differently!
unk2 <<= 24;
if (!unk2) {
#if (GAME == GAME_SA1)
DmaCopy32(3, &sPaletteBuffer[16 * 16], BG_PLTT, 29 * 16);
DmaCopy32(3, &sPaletteBuffer, OBJ_PLTT, OBJ_PLTT_SIZE);
#elif (GAME == GAME_SA2)
DmaCopy32(3, &wd->pal[16], PLTT, 29 * 16);
DmaCopy32(3, &wd->pal[0], OBJ_PLTT, OBJ_PLTT_SIZE);
#endif
REG_DISPCNT &= ~DISPCNT_BG0_ON;
gFlags |= (FLAGS_UPDATE_SPRITE_PALETTES | FLAGS_UPDATE_BACKGROUND_PALETTES);
}
}
}
void SA2_LABEL(VCountIntr_8011ACC)(void)
{
Water *water = &gWater;
#ifdef BUG_FIX
if (water->t)
#endif
{
#if (GAME == GAME_SA1)
DmaCopy32(0, &sPaletteBuffer[16 * 16], BG_PLTT, 29 * 16);
DmaCopy32(0, &sPaletteBuffer, OBJ_PLTT, OBJ_PLTT_SIZE);
#elif (GAME == GAME_SA2)
WaterData *wd = TASK_DATA(water->t);
DmaCopy32(3, &wd->pal[16], PLTT, 29 * 16);
DmaCopy32(3, &wd->pal[0], OBJ_PLTT, OBJ_PLTT_SIZE);
#endif
REG_DISPCNT &= ~DISPCNT_BG0_ON;
gFlags |= (FLAGS_UPDATE_SPRITE_PALETTES | FLAGS_UPDATE_BACKGROUND_PALETTES);
#if (GAME == GAME_SA1)
{
DmaSet(0, gBgOffsetsHBlankSecondary, (void *)&REG_BG3HOFS, //
((DMA_ENABLE | DMA_START_HBLANK | DMA_REPEAT | DMA_DEST_RELOAD) << 16) | (gHBlankCopySize >> 1));
}
#endif
REG_IF = INTR_FLAG_VCOUNT;
}
}
// Further up in SA2!
#if (GAME == GAME_SA1)
UNUSED void MaskPaletteWithUnderwaterColor(u32 *dst, u32 *src, u32 mask, s32 size)
{
u32 k = (size >> 4);
while (k-- > 0) {
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
}
}
#endif
#if (HAS_RUN_ON_WATER)
static void TaskDestructor_RunOnWaterEffect(struct Task *t)
{
RunOnWaterEffect *effect = TASK_DATA(t);
Sprite *s = &effect->s;
VramFree(s->graphics.dest);
}
static void SA2_LABEL(sub_8011B54)(u32 *dst, u32 *src, s32 size) { CopyPalette(dst, src, size); }
#endif
+1 -1
View File
@@ -66,7 +66,7 @@ sa2/src/game/sa1/stage/player_super_sonic.o(.data);
sa2/src/game/shared/stage/underwater_effects.o(.data);
. = 0x00005C80;
src/game/shared/stage/water_effects.o(.data);
sa2/src/game/shared/stage/water_effects.o(.data);
. = 0x000060D0;
sa2/src/input_recorder.o(.data);
@@ -185,7 +185,7 @@ void CreateEntity_EggSpider(MapEntity *me, u16 regionX, u16 regionY, u8 id)
}
t = TaskCreate(Task_EggSpiderInit, sizeof(EggSpider), 0x2000U, 0U, TaskDestructor_8031CB4);
TaskCreate(sub_804C40C, 0U, 0xFFFDU, 0U, NULL);
TaskCreate(InitWaterPalettes, 0U, 0xFFFDU, 0U, NULL);
boss = TASK_DATA(t);
s = &boss->s;
+1 -1
View File
@@ -17,7 +17,7 @@
#include "constants/sa1/vram_hardcoded.h"
#include "constants/sa1/zones.h"
extern void sub_804C40C(void); // TODO: Move to correct Header!
extern void InitWaterPalettes(void); // TODO: Move to correct Header!
void StageUI_DrawTimer(u32 courseTime);
void sub_8053BAC(void);
+2 -2
View File
@@ -537,7 +537,7 @@ NONMATCH("asm/non_matching/game/sa1/stage/stage_ui__Task_8055458.inc", void Task
if (gCurrentLevel == LEVEL_INDEX(ZONE_4, ACT_1) || gCurrentLevel == LEVEL_INDEX(ZONE_4, ACT_2)) {
LoadPalette423Anim();
sub_804C40C();
InitWaterPalettes();
}
TaskDestroy(strcMain->task20);
@@ -559,7 +559,7 @@ NONMATCH("asm/non_matching/game/sa1/stage/stage_ui__Task_8055458.inc", void Task
}
} else if (gCurrentLevel == LEVEL_INDEX(ZONE_4, ACT_1) || gCurrentLevel == LEVEL_INDEX(ZONE_4, ACT_2)) {
//_08055708
sub_804C40C();
InitWaterPalettes();
}
}
END_NONMATCH
+137 -51
View File
@@ -4,16 +4,32 @@
#include "malloc_vram.h"
#include "task.h"
#if (GAME == GAME_SA1)
#include "trig.h"
#include "bg_triangles.h"
#endif
#include "game/globals.h"
#include "game/sa2/stage/boost_effect.h"
#include "game/shared/stage/player.h"
#include "game/shared/stage/camera.h"
#include "game/shared/stage/mp_sprite_task.h"
#include "data/sa2/sprite_data.h"
#include "game/shared/stage/water_effects.h"
#if (GAME == GAME_SA1)
#include "data/sa1/sprite_data.h"
#include "constants/sa1/animations.h"
#include "constants/sa1/zones.h"
#include "constants/sa1/vram_hardcoded.h"
#elif (GAME == GAME_SA2)
#include "game/sa2/stage/boost_effect.h"
#include "data/sa2/sprite_data.h"
#include "constants/sa2/animations.h"
#include "constants/sa2/zones.h"
#include "constants/sa2/vram_hardcoded.h"
#endif
typedef struct {
/* 0x00 */ s32 x;
@@ -26,10 +42,15 @@ typedef struct {
static void Task_StageWaterTask(void);
static void Task_RunOnWaterEffect(void);
static void TaskDestructor_WaterSurface(struct Task *);
static void sub_8011A4C(void);
static void VCountIntr_8011ACC(void);
static void TaskDestructor_8011B3C(struct Task *);
static void SA2_LABEL(sub_8011A4C)(void);
static void SA2_LABEL(VCountIntr_8011ACC)(void);
static void TaskDestructor_RunOnWaterEffect(struct Task *);
#if (GAME == GAME_SA1)
// SA2 allocates these in the Task, to save global memory.
// Water is only used in one stage, after all.
u16 sPaletteBuffer[(16 * 2) * 16] = { 0 };
#endif
Water gWater = {};
#define WATER_MASK_COLOR_A 0x7BDE
@@ -48,6 +69,7 @@ Water gWater = {};
temp2 >>= 1; \
})
#if (GAME == GAME_SA2)
static const u16 gUnknown_080D550C[NUM_CHARACTERS] = {
SA2_ANIM_UNDERWATER_1UP_SONIC, SA2_ANIM_UNDERWATER_1UP_CREAM, SA2_ANIM_UNDERWATER_1UP_TAILS,
SA2_ANIM_UNDERWATER_1UP_KNUCKLES, SA2_ANIM_UNDERWATER_1UP_AMY,
@@ -68,6 +90,7 @@ static void inline CopyPalette(u32 *dst, u32 *src, s32 length)
*dst++ = *src++;
}
}
#endif
static inline void MaskPaletteWithUnderwaterColor_inline(u32 *dst, u32 *src, u32 mask, s32 size)
{
@@ -84,17 +107,6 @@ static inline void MaskPaletteWithUnderwaterColor_inline(u32 *dst, u32 *src, u32
}
}
#if (GAME == GAME_SA1)
void sub_804C40C(void)
{
Water *water = &gWater;
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[0], (u32 *)&gObjPalette[0], water->mask, 16 * 16);
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[16 * 16], (u32 *)&gBgPalette[0], water->mask, 16 * 16);
}
#endif
#if (GAME == GAME_SA2)
void InitWaterPalettes(void)
{
u16 animId, character;
@@ -103,7 +115,10 @@ void InitWaterPalettes(void)
Water *water = &gWater;
WaterData *wd = TASK_DATA(water->t);
#if (GAME == GAME_SA1)
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[0], (u32 *)&gObjPalette[0], water->mask, 16 * 16);
MaskPaletteWithUnderwaterColor_inline((u32 *)&sPaletteBuffer[16 * 16], (u32 *)&gBgPalette[0], water->mask, 16 * 16);
#elif (GAME == GAME_SA2)
if (IS_MULTI_PLAYER) {
u8 i = 0, j = 0;
for (; j < 4; j++) {
@@ -146,9 +161,9 @@ void InitWaterPalettes(void)
pal = (animation[0]->pal.palId + 4);
CopyPalette((u32 *)wd->pal[4], (u32 *)gSpritePalettes[pal], 12 * PALETTE_LEN_4BPP);
MaskPaletteWithUnderwaterColor_inline((u32 *)wd->pal[16], (u32 *)&GET_PALETTE_COLOR_BG(0, 0), water->mask, 16 * PALETTE_LEN_4BPP);
}
MaskPaletteWithUnderwaterColor_inline((u32 *)wd->pal[16], (u32 *)gBgPalette, water->mask, 16 * 16);
#endif
}
#if (GAME == GAME_SA1)
void LoadPalette423Anim(void)
@@ -180,9 +195,13 @@ void CreateStageWaterTask(s32 waterLevel, u32 p1, u32 mask)
if (waterLevel >= 0) {
Sprite *s = &gWater.s;
s->graphics.dest = (void *)(OBJ_VRAM0 + 0x2980);
s->graphics.dest = VRAM_RESERVED_WATER_SURFACE;
s->graphics.size = 0;
#if (GAME == GAME_SA1)
s->graphics.anim = SA1_ANIM_WATER_SURFACE;
#elif (GAME == GAME_SA2)
s->graphics.anim = SA2_ANIM_WATER_SURFACE;
#endif
s->variant = 0;
s->prevVariant |= -1;
s->oamFlags = SPRITE_OAM_ORDER(0);
@@ -202,15 +221,8 @@ static void Task_StageWaterTask(void)
struct Camera *cam = &gCamera;
bool32 active;
Sprite *s;
u8 unk1;
#ifndef NON_MATCHING
register u8 unk2_0 asm("r0");
register u8 unk2_2 asm("r2");
#else
u8 unk2_0;
u8 unk2_2;
#endif
#if (HAS_RUN_ON_WATER)
if ((gCurrentLevel == LEVEL_INDEX(ZONE_1, ACT_1)) && (I(gPlayer.qWorldX) > 6665) && (I(gPlayer.qWorldX) <= 10650)) {
water->isActive = TRUE;
} else {
@@ -221,6 +233,7 @@ static void Task_StageWaterTask(void)
gFlags &= ~MOVESTATE_IN_WATER;
return;
}
#endif
if (gStageTime & 1) {
if (water->currentWaterLevel != water->targetWaterLevel) {
@@ -232,27 +245,25 @@ static void Task_StageWaterTask(void)
}
if (water->currentWaterLevel <= cam->y) {
water->unk2 = 0;
water->SA2_LABEL(unk2) = 0;
} else if (water->currentWaterLevel < cam->y + DISPLAY_HEIGHT) {
water->unk2 = water->currentWaterLevel - cam->y;
water->SA2_LABEL(unk2) = water->currentWaterLevel - cam->y;
} else {
water->unk2 = 0xFF;
water->SA2_LABEL(unk2) = -1;
}
gVBlankCallbacks[gNumVBlankCallbacks++] = sub_8011A4C;
gVBlankCallbacks[gNumVBlankCallbacks++] = SA2_LABEL(sub_8011A4C);
gFlags |= FLAGS_EXECUTE_VBLANK_CALLBACKS;
unk1 = water->unk1 - 1;
if (unk1 < DISPLAY_HEIGHT - 1) {
if (water->SA2_LABEL(unk1) > 0 && water->SA2_LABEL(unk1) < DISPLAY_HEIGHT) {
s = &water->s;
s->x = -((cam->x + ((gStageTime + 1) >> 2)) & 15);
s->y = water->unk2 + 1;
s->y = water->SA2_LABEL(unk2) + 1;
s->frameFlags |= (SPRITE_FLAG_MASK_19 | SPRITE_FLAG_MASK_18);
UpdateSpriteAnimation(s);
if (gStageTime & 0x2) {
u16 y = s->y - 1;
if (y < DISPLAY_WIDTH - 1) {
if (gStageTime & 2) {
if (s->y > 0 && s->y < DISPLAY_WIDTH) {
s32 count = WATER_SURFACE_SPRITE_COUNT;
do {
DisplaySprite(s);
@@ -262,20 +273,44 @@ static void Task_StageWaterTask(void)
}
}
unk2_0 = (water->unk2);
if ((unk2_2 = unk2_0 - 1) < DISPLAY_HEIGHT - 1) {
gIntrTable[INTR_INDEX_VCOUNT] = VCountIntr_8011ACC;
gVCountSetting = unk2_2;
if (water->SA2_LABEL(unk2) > 0 && water->SA2_LABEL(unk2) < DISPLAY_HEIGHT) {
gIntrTable[INTR_INDEX_VCOUNT] = SA2_LABEL(VCountIntr_8011ACC);
gVCountSetting = water->SA2_LABEL(unk2) - 1;
gFlags |= FLAGS_40;
} else {
gIntrTable[INTR_INDEX_VCOUNT] = gIntrTableTemplate[INTR_INDEX_VCOUNT];
gFlags &= ~FLAGS_40;
}
#if (GAME == GAME_SA1)
if (water->SA2_LABEL(unk2) >= DISPLAY_HEIGHT) {
gFlags &= ~FLAGS_EXECUTE_HBLANK_COPY;
} else {
#ifndef NON_MATCHING
UNUSED u32 _;
#endif
u32 r5, i;
s32 r3 = CLAMP_SIN_PERIOD(((gStageTime * 4) + (((gBgScrollRegs[3][1] + water->SA2_LABEL(unk2)) % 32u) << 6)));
SA2_LABEL(sub_8007738)
(3, 0, 32, r3, 2, 64, (((gStageTime * 2 + (((gBgScrollRegs[3][1] + water->SA2_LABEL(unk2)) % 32u) << 5))) + 0x100) & ONE_CYCLE, 10,
32, gBgScrollRegs[3][0], gBgScrollRegs[3][1]);
for (r5 = 32, i = 1; r5 < DISPLAY_HEIGHT; i++, r5 += 32) {
DmaCopy32(3, gBgOffsetsHBlankPrimary, gBgOffsetsHBlankPrimary + ((gHBlankCopySize << 5) * i), (gHBlankCopySize << 5));
}
if (water->SA2_LABEL(unk2)) {
gFlags &= ~FLAGS_EXECUTE_HBLANK_COPY;
}
}
#endif
}
#if (HAS_RUN_ON_WATER)
void CreateRunOnWaterEffect(void)
{
struct Task *t = TaskCreate(Task_RunOnWaterEffect, sizeof(RunOnWaterEffect), 0x4001, 0, TaskDestructor_8011B3C);
struct Task *t = TaskCreate(Task_RunOnWaterEffect, sizeof(RunOnWaterEffect), 0x4001, 0, TaskDestructor_RunOnWaterEffect);
RunOnWaterEffect *effect = TASK_DATA(t);
Sprite *s = &effect->s;
s->graphics.dest = VramMalloc(12);
@@ -317,6 +352,15 @@ static void Task_RunOnWaterEffect(void)
UpdateSpriteAnimation(s);
DisplaySprite(s);
}
#endif
#if (GAME == GAME_SA1)
void unused_DestroygWaterTask(void)
{
TaskDestroy(gWater.t);
gWater.t = NULL;
}
#endif
struct Task *CreateWaterfallSurfaceHitEffect(s32 x, s32 y)
{
@@ -325,7 +369,11 @@ struct Task *CreateWaterfallSurfaceHitEffect(s32 x, s32 y)
Sprite *s = &ts->s;
s->graphics.dest = VramMalloc(12);
#if (GAME == GAME_SA1)
s->graphics.anim = SA1_ANIM_WATER_FALL_HIT_SURFACE;
#elif (GAME == GAME_SA2)
s->graphics.anim = SA2_ANIM_WATER_FALL_HIT_SURFACE;
#endif
s->variant = 0;
s->oamFlags = SPRITE_OAM_ORDER(7);
s->frameFlags = SPRITE_FLAG(PRIORITY, 2);
@@ -333,6 +381,8 @@ struct Task *CreateWaterfallSurfaceHitEffect(s32 x, s32 y)
return t;
}
// At the bottom of the file in SA1!
#if (GAME == GAME_SA2)
UNUSED void MaskPaletteWithUnderwaterColor(u32 *dst, u32 *src, u32 mask, s32 size)
{
#ifndef NON_MATCHING
@@ -351,6 +401,7 @@ UNUSED void MaskPaletteWithUnderwaterColor(u32 *dst, u32 *src, u32 mask, s32 siz
MaskPaletteWithUnderwaterColor_inline(dst, src, mask, size);
#endif
}
#endif
static void TaskDestructor_WaterSurface(struct Task *t)
{
@@ -359,9 +410,12 @@ static void TaskDestructor_WaterSurface(struct Task *t)
gFlags &= ~FLAGS_40;
gIntrTable[INTR_INDEX_VCOUNT] = gIntrTableTemplate[INTR_INDEX_VCOUNT];
water->t = NULL;
#if (GAME == GAME_SA1)
DmaFill32(3, 0, sPaletteBuffer, 60 * 16);
#endif
}
static void sub_8011A4C(void)
static void SA2_LABEL(sub_8011A4C)(void)
{
Water *water = &gWater;
#ifdef BUG_FIX
@@ -369,24 +423,28 @@ static void sub_8011A4C(void)
#endif
{
WaterData *wd = TASK_DATA(water->t);
u32 unk2;
unk2 = water->unk2;
water->unk1 = unk2;
u32 unk2 = water->SA2_LABEL(unk2);
water->SA2_LABEL(unk1) = unk2;
water->unk8 &= ~0x1;
// TODO: This surely can be matched differently!
unk2 <<= 24;
if (!unk2) {
#if (GAME == GAME_SA1)
DmaCopy32(3, &sPaletteBuffer[16 * 16], BG_PLTT, 29 * 16);
DmaCopy32(3, &sPaletteBuffer, OBJ_PLTT, OBJ_PLTT_SIZE);
#elif (GAME == GAME_SA2)
DmaCopy32(3, &wd->pal[16], PLTT, 29 * 16);
DmaCopy32(3, &wd->pal[0], OBJ_PLTT, OBJ_PLTT_SIZE);
#endif
REG_DISPCNT &= ~DISPCNT_BG0_ON;
gFlags |= (FLAGS_UPDATE_SPRITE_PALETTES | FLAGS_UPDATE_BACKGROUND_PALETTES);
}
}
}
static void VCountIntr_8011ACC(void)
static void SA2_LABEL(VCountIntr_8011ACC)(void)
{
Water *water = &gWater;
#ifdef BUG_FIX
@@ -394,21 +452,49 @@ static void VCountIntr_8011ACC(void)
#endif
{
WaterData *wd = TASK_DATA(water->t);
#if (GAME == GAME_SA1)
DmaCopy32(0, &sPaletteBuffer[16 * 16], BG_PLTT, 29 * 16);
DmaCopy32(0, &sPaletteBuffer, OBJ_PLTT, OBJ_PLTT_SIZE);
#elif (GAME == GAME_SA2)
DmaCopy32(3, &wd->pal[16], PLTT, 29 * 16);
DmaCopy32(3, &wd->pal[0], OBJ_PLTT, OBJ_PLTT_SIZE);
#endif
REG_DISPCNT &= ~DISPCNT_BG0_ON;
gFlags |= (FLAGS_UPDATE_SPRITE_PALETTES | FLAGS_UPDATE_BACKGROUND_PALETTES);
#if (GAME == GAME_SA1)
DmaSet(0, gBgOffsetsHBlankSecondary, (void *)&REG_BG3HOFS, //
((DMA_ENABLE | DMA_START_HBLANK | DMA_REPEAT | DMA_DEST_RELOAD) << 16) | (gHBlankCopySize >> 1));
#endif
REG_IF = INTR_FLAG_VCOUNT;
}
}
static void TaskDestructor_8011B3C(struct Task *t)
// Further up in SA2!
#if (GAME == GAME_SA1)
UNUSED void MaskPaletteWithUnderwaterColor(u32 *dst, u32 *src, u32 mask, s32 size)
{
u32 k = (size >> 4);
while (k-- > 0) {
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
*dst++ = WATER_MASK_PALETTE_CHUNK(*src++, mask);
}
}
#endif
#if (HAS_RUN_ON_WATER)
static void TaskDestructor_RunOnWaterEffect(struct Task *t)
{
RunOnWaterEffect *effect = TASK_DATA(t);
Sprite *s = &effect->s;
VramFree(s->graphics.dest);
}
static void sub_8011B54(u32 *dst, u32 *src, s32 size) { CopyPalette(dst, src, size); }
static void SA2_LABEL(sub_8011B54)(u32 *dst, u32 *src, s32 size) { CopyPalette(dst, src, size); }
#endif