diff --git a/asm/code_0.s b/asm/code_0.s index 23dc98c1..49e2e920 100644 --- a/asm/code_0.s +++ b/asm/code_0.s @@ -21655,8 +21655,8 @@ _0800ABC8: .4byte 0xFFFFFF00 _0800ABCC: .4byte 0xDFFFFFFF _0800ABD0: .4byte sub_800E67C - thumb_func_start sub_800ABD4 -sub_800ABD4: @ 0x0800ABD4 + thumb_func_start Player_UseSpecialSpringWithKey +Player_UseSpecialSpringWithKey: @ 0x0800ABD4 push {r4, r5, lr} adds r5, r0, #0 ldr r0, _0800AC94 @ =gStageData @@ -21820,6 +21820,7 @@ _0800AD14: _0800AD1C: .4byte gCamera _0800AD20: .4byte sub_800EB4C +@ Used by Special Spring, Act Ring and Final Zone Ring. thumb_func_start Player_800AD24 Player_800AD24: @ 0x0800AD24 push {r4, r5, lr} diff --git a/asm/ia_special_spring.s b/asm/ia_special_spring.s deleted file mode 100644 index b1582f13..00000000 --- a/asm/ia_special_spring.s +++ /dev/null @@ -1,493 +0,0 @@ -.include "asm/macros.inc" -.include "constants/constants.inc" - -.text -.syntax unified -.arm - - thumb_func_start CreateEntity_SpecialSpring -CreateEntity_SpecialSpring: @ 0x08040CDC - push {r4, r5, r6, r7, lr} - mov r7, r8 - push {r7} - sub sp, #4 - mov r8, r0 - adds r4, r1, #0 - adds r5, r2, #0 - adds r6, r3, #0 - lsls r4, r4, #0x10 - lsrs r4, r4, #0x10 - lsls r5, r5, #0x10 - lsrs r5, r5, #0x10 - lsls r6, r6, #0x18 - lsrs r6, r6, #0x18 - ldr r0, _08040D78 @ =Task_SpecialSpringMain - movs r2, #0x84 - lsls r2, r2, #6 - ldr r1, _08040D7C @ =TaskDestructor_SpecialSpring - str r1, [sp] - movs r1, #0xb4 - movs r3, #0 - bl TaskCreate - ldrh r3, [r0, #6] - movs r0, #0xc0 - lsls r0, r0, #0x12 - adds r0, r3, r0 - movs r2, #0 - strh r4, [r0, #4] - strh r5, [r0, #6] - mov r1, r8 - str r1, [r0] - ldrb r1, [r1] - strb r1, [r0, #0xa] - strb r6, [r0, #0xb] - mov r6, r8 - ldrb r1, [r6] - lsls r1, r1, #3 - lsls r4, r4, #8 - adds r1, r1, r4 - ldr r7, _08040D80 @ =0x030000AC - adds r6, r3, r7 - strh r1, [r6] - mov r4, r8 - ldrb r1, [r4, #1] - lsls r1, r1, #3 - lsls r5, r5, #8 - adds r1, r1, r5 - adds r7, #2 - adds r5, r3, r7 - strh r1, [r5] - ldr r4, _08040D84 @ =0x030000B0 - adds r1, r3, r4 - strb r2, [r1] - subs r7, #0xa2 - adds r3, r3, r7 - ldr r4, _08040D88 @ =gCamera - ldr r2, [r4] - ldrh r1, [r6] - subs r1, r1, r2 - strh r1, [r3, #0x10] - ldr r2, [r4, #4] - ldrh r1, [r5] - subs r1, r1, r2 - strh r1, [r3, #0x12] - movs r2, #2 - rsbs r2, r2, #0 - adds r1, r2, #0 - mov r4, r8 - strb r1, [r4] - bl sub_8040F10 - add sp, #4 - pop {r3} - mov r8, r3 - pop {r4, r5, r6, r7} - pop {r0} - bx r0 - .align 2, 0 -_08040D78: .4byte Task_SpecialSpringMain -_08040D7C: .4byte TaskDestructor_SpecialSpring -_08040D80: .4byte 0x030000AC -_08040D84: .4byte 0x030000B0 -_08040D88: .4byte gCamera - - thumb_func_start Task_SpecialSpringMain -Task_SpecialSpringMain: @ 0x08040D8C - push {r4, r5, r6, r7, lr} - mov r7, sb - mov r6, r8 - push {r6, r7} - sub sp, #4 - ldr r0, _08040E6C @ =gCurTask - ldr r0, [r0] - ldrh r0, [r0, #6] - movs r2, #0xc0 - lsls r2, r2, #0x12 - adds r2, r0, r2 - ldr r1, _08040E70 @ =0x0300000C - adds r1, r1, r0 - mov r8, r1 - ldr r3, [r2] - ldrb r0, [r2, #0xa] - lsls r0, r0, #3 - ldrh r1, [r2, #4] - lsls r1, r1, #8 - adds r0, r0, r1 - lsls r0, r0, #0x10 - lsrs r6, r0, #0x10 - ldrb r0, [r3, #1] - lsls r0, r0, #3 - ldrh r1, [r2, #6] - lsls r1, r1, #8 - adds r0, r0, r1 - lsls r0, r0, #0x10 - lsrs r5, r0, #0x10 - ldr r7, _08040E74 @ =gStageData - ldrb r1, [r7, #6] - lsls r0, r1, #2 - adds r0, r0, r1 - lsls r0, r0, #2 - adds r0, r0, r1 - lsls r0, r0, #4 - ldr r1, _08040E78 @ =gPlayers - mov sb, r1 - adds r4, r0, r1 - adds r0, r4, #0 - bl sub_802C080 - cmp r0, #0 - beq _08040DE6 - b _08040EFE -_08040DE6: - lsls r1, r6, #0x10 - asrs r1, r1, #0x10 - lsls r2, r5, #0x10 - asrs r2, r2, #0x10 - str r0, [sp] - mov r0, r8 - adds r3, r4, #0 - bl sub_8020950 - adds r2, r0, #0 - cmp r2, #0 - bne _08040E00 - b _08040EFE -_08040E00: - movs r1, #0x80 - lsls r1, r1, #9 - ands r1, r2 - cmp r1, #0 - beq _08040E8C - ldr r0, _08040E7C @ =gSaveGame - adds r0, #0x22 - ldrb r1, [r7, #9] - adds r3, r0, r1 - ldrb r1, [r3] - cmp r1, #0 - beq _08040EF4 - ldrb r0, [r7, #3] - cmp r0, #0 - bne _08040EF4 - subs r0, r1, #1 - strb r0, [r3] - ldr r1, _08040E80 @ =sub_800ABD4 - adds r0, r4, #0 - bl SetPlayerCallback - adds r0, r4, #0 - adds r0, #0x2b - ldrb r1, [r0] - lsls r1, r1, #0x1e - lsrs r1, r1, #0x1e - lsls r0, r1, #2 - adds r0, r0, r1 - lsls r0, r0, #2 - adds r0, r0, r1 - lsls r0, r0, #4 - mov r1, sb - adds r3, r0, r1 - adds r0, r3, #0 - adds r0, #0x2b - ldrb r1, [r0] - movs r0, #0x1c - ands r0, r1 - cmp r0, #8 - bne _08040E58 - ldr r1, _08040E84 @ =Player_800AD24 - adds r0, r3, #0 - bl SetPlayerCallback -_08040E58: - movs r0, #2 - mov r1, r8 - strb r0, [r1, #0x1a] - ldr r0, _08040E88 @ =0x00000206 - bl sub_8003DF0 - movs r0, #4 - strb r0, [r7, #4] - b _08040EFE - .align 2, 0 -_08040E6C: .4byte gCurTask -_08040E70: .4byte 0x0300000C -_08040E74: .4byte gStageData -_08040E78: .4byte gPlayers -_08040E7C: .4byte gSaveGame -_08040E80: .4byte sub_800ABD4 -_08040E84: .4byte Player_800AD24 -_08040E88: .4byte 0x00000206 -_08040E8C: - movs r0, #0xc0 - lsls r0, r0, #0xc - ands r0, r2 - cmp r0, #0 - beq _08040EF4 - strh r1, [r4, #0x18] - strh r1, [r4, #0x1c] - movs r0, #0xff - lsls r0, r0, #8 - adds r1, r0, #0 - adds r0, r2, #0 - ands r0, r1 - lsls r0, r0, #0x10 - asrs r0, r0, #0x10 - ldr r1, [r4, #0x10] - adds r3, r1, r0 - str r3, [r4, #0x10] - movs r0, #0x80 - lsls r0, r0, #0xb - ands r0, r2 - cmp r0, #0 - beq _08040ECC - ldrh r1, [r4, #0x1e] - movs r0, #0x20 - ands r0, r1 - cmp r0, #0 - beq _08040ECC - ldr r1, _08040EC8 @ =0xFFFFFF00 - adds r0, r3, r1 - b _08040EE8 - .align 2, 0 -_08040EC8: .4byte 0xFFFFFF00 -_08040ECC: - movs r0, #0x80 - lsls r0, r0, #0xc - ands r0, r2 - cmp r0, #0 - beq _08040EFE - ldrh r1, [r4, #0x1e] - movs r0, #0x10 - ands r0, r1 - cmp r0, #0 - beq _08040EFE - ldr r0, [r4, #0x10] - movs r1, #0x80 - lsls r1, r1, #1 - adds r0, r0, r1 -_08040EE8: - str r0, [r4, #0x10] - ldr r0, [r4, #4] - movs r1, #0x40 - orrs r0, r1 - str r0, [r4, #4] - b _08040EFE -_08040EF4: - lsls r1, r2, #0x18 - asrs r1, r1, #0x10 - ldr r0, [r4, #0x14] - adds r0, r0, r1 - str r0, [r4, #0x14] -_08040EFE: - bl sub_8040FD8 - add sp, #4 - pop {r3, r4} - mov r8, r3 - mov sb, r4 - pop {r4, r5, r6, r7} - pop {r0} - bx r0 - - thumb_func_start sub_8040F10 -sub_8040F10: @ 0x08040F10 - push {r4, r5, r6, r7, lr} - mov r7, sl - mov r6, sb - mov r5, r8 - push {r5, r6, r7} - adds r5, r0, #0 - adds r4, r5, #0 - adds r4, #0xc - movs r0, #0x18 - bl VramMalloc - str r0, [r5, #0xc] - movs r7, #0 - movs r6, #0 - movs r0, #0xe4 - lsls r0, r0, #2 - strh r0, [r4, #0xc] - strb r7, [r4, #0x1a] - movs r0, #0xc0 - lsls r0, r0, #3 - strh r0, [r4, #0x14] - strh r6, [r4, #0xe] - strh r6, [r4, #0x16] - movs r1, #1 - rsbs r1, r1, #0 - mov r8, r1 - movs r0, #0xff - strb r0, [r4, #0x1b] - movs r0, #0x10 - strb r0, [r4, #0x1c] - strb r7, [r4, #0x1f] - str r1, [r4, #0x20] - movs r1, #0x80 - lsls r1, r1, #5 - mov sl, r1 - str r1, [r4, #8] - adds r0, r4, #0 - bl UpdateSpriteAnimation - ldr r0, _08040FB8 @ =gSaveGame - adds r0, #0x32 - ldrb r0, [r0] - ldr r1, _08040FBC @ =gStageData - ldrb r1, [r1, #9] - asrs r0, r1 - movs r1, #1 - mov sb, r1 - ands r0, r1 - cmp r0, #0 - beq _08040FC4 - adds r4, #0x28 - movs r0, #6 - bl VramMalloc - str r0, [r5, #0x34] - ldr r0, _08040FC0 @ =0x000003D7 - strh r0, [r4, #0xc] - movs r0, #3 - strb r0, [r4, #0x1a] - movs r0, #0xc0 - lsls r0, r0, #3 - strh r0, [r4, #0x14] - strh r6, [r4, #0xe] - strh r6, [r4, #0x16] - ldrb r0, [r4, #0x1b] - mov r1, r8 - orrs r0, r1 - strb r0, [r4, #0x1b] - movs r0, #0x10 - strb r0, [r4, #0x1c] - strb r7, [r4, #0x1f] - movs r1, #1 - rsbs r1, r1, #0 - str r1, [r4, #0x20] - mov r0, sl - str r0, [r4, #8] - adds r0, r4, #0 - bl UpdateSpriteAnimation - adds r0, r5, #0 - adds r0, #0xb0 - mov r1, sb - strb r1, [r0] - b _08040FCA - .align 2, 0 -_08040FB8: .4byte gSaveGame -_08040FBC: .4byte gStageData -_08040FC0: .4byte 0x000003D7 -_08040FC4: - adds r0, r5, #0 - adds r0, #0xb0 - strb r7, [r0] -_08040FCA: - pop {r3, r4, r5} - mov r8, r3 - mov sb, r4 - mov sl, r5 - pop {r4, r5, r6, r7} - pop {r0} - bx r0 - - thumb_func_start sub_8040FD8 -sub_8040FD8: @ 0x08040FD8 - push {r4, r5, r6, r7, lr} - mov r7, sl - mov r6, sb - mov r5, r8 - push {r5, r6, r7} - ldr r0, _08041028 @ =gCurTask - mov sl, r0 - ldr r0, [r0] - ldrh r5, [r0, #6] - movs r0, #0xc0 - lsls r0, r0, #0x12 - adds r0, r0, r5 - mov r8, r0 - ldr r1, _0804102C @ =0x0300000C - adds r4, r5, r1 - ldr r2, [r0] - mov sb, r2 - adds r1, #0xa0 - adds r0, r5, r1 - ldr r2, _08041030 @ =0x030000AE - adds r1, r5, r2 - movs r2, #0 - ldrsh r6, [r0, r2] - movs r0, #0 - ldrsh r7, [r1, r0] - adds r0, r6, #0 - adds r1, r7, #0 - bl IsPointInScreenRect - cmp r0, #0 - bne _08041034 - mov r1, r8 - ldrb r0, [r1, #0xa] - mov r2, sb - strb r0, [r2] - mov r1, sl - ldr r0, [r1] - bl TaskDestroy - b _0804107A - .align 2, 0 -_08041028: .4byte gCurTask -_0804102C: .4byte 0x0300000C -_08041030: .4byte 0x030000AE -_08041034: - ldr r2, _08041088 @ =gCamera - mov r8, r2 - ldr r0, [r2] - subs r0, r6, r0 - strh r0, [r4, #0x10] - ldr r0, [r2, #4] - subs r0, r7, r0 - strh r0, [r4, #0x12] - adds r0, r4, #0 - bl UpdateSpriteAnimation - adds r0, r4, #0 - bl DisplaySprite - ldr r1, _0804108C @ =0x030000B0 - adds r0, r5, r1 - ldrb r0, [r0] - cmp r0, #0 - beq _0804107A - ldr r2, _08041090 @ =0x03000034 - adds r4, r5, r2 - mov r1, r8 - ldr r0, [r1] - subs r0, r6, r0 - strh r0, [r4, #0x10] - ldr r0, [r1, #4] - subs r0, r7, r0 - subs r0, #0x18 - strh r0, [r4, #0x12] - adds r0, r4, #0 - bl UpdateSpriteAnimation - adds r0, r4, #0 - bl DisplaySprite -_0804107A: - pop {r3, r4, r5} - mov r8, r3 - mov sb, r4 - mov sl, r5 - pop {r4, r5, r6, r7} - pop {r0} - bx r0 - .align 2, 0 -_08041088: .4byte gCamera -_0804108C: .4byte 0x030000B0 -_08041090: .4byte 0x03000034 - - thumb_func_start TaskDestructor_SpecialSpring -TaskDestructor_SpecialSpring: @ 0x08041094 - push {r4, r5, lr} - ldrh r4, [r0, #6] - movs r0, #0xc0 - lsls r0, r0, #0x12 - adds r5, r4, r0 - ldr r0, [r5, #0xc] - bl VramFree - ldr r0, _080410BC @ =0x030000B0 - adds r4, r4, r0 - ldrb r0, [r4] - cmp r0, #0 - beq _080410B4 - ldr r0, [r5, #0x34] - bl VramFree -_080410B4: - pop {r4, r5} - pop {r0} - bx r0 - .align 2, 0 -_080410BC: .4byte 0x030000B0 diff --git a/include/constants/anim_sizes.h b/include/constants/anim_sizes.h index 237a859e..1cc13392 100644 --- a/include/constants/anim_sizes.h +++ b/include/constants/anim_sizes.h @@ -50,6 +50,25 @@ #define ANIM_FLAT_SPRING_WIDTH 32 #define ANIM_FLAT_SPRING_HEIGHT 32 +#define ANIM_SPECIAL_SPRING_WIDTH 32 +#define ANIM_SPECIAL_SPRING_HEIGHT 48 + +#define ANIM_ACT_RING_WIDTH 32 +#define ANIM_ACT_RING_HEIGHT 32 + +#define ANIM_ACT_RING_VAR0_WIDTH 32 +#define ANIM_ACT_RING_VAR0_HEIGHT 32 + +#define ANIM_ACT_RING_VAR1_WIDTH 32 +#define ANIM_ACT_RING_VAR1_HEIGHT 32 + +#define ANIM_ACT_RING_VAR2_WIDTH 32 +#define ANIM_ACT_RING_VAR2_HEIGHT 32 + +// "Crown" signalling completion +#define ANIM_ACT_RING_VAR3_WIDTH 24 +#define ANIM_ACT_RING_VAR3_HEIGHT 16 + // Enemies #define ANIM_SPINNER_WIDTH 48 diff --git a/include/constants/animations.h b/include/constants/animations.h index 5b22391f..7db90163 100644 --- a/include/constants/animations.h +++ b/include/constants/animations.h @@ -127,7 +127,7 @@ #define ANIM_OMOCHAO3 980 #define ANIM_OMOCHAO4 981 #define ANIM_OMOCHAO5 982 -#define ANIM_ACT_RINGS 983 +#define ANIM_ACT_RING 983 /* ANIM_ACT_RINGS: 0=Act 1 1=Act 2 diff --git a/include/constants/songs.h b/include/constants/songs.h index ef902f9f..32970258 100644 --- a/include/constants/songs.h +++ b/include/constants/songs.h @@ -235,7 +235,7 @@ #define SE_515 0x203 #define VOICE__CHAO__COLLECTED 0x204 #define SE_SPECIAL_KEY 0x205 -#define SE_518 0x206 +#define SE_SPECIAL_SPRING 0x206 #define SE_GOAL_MEDAL 0x207 #define SE_GOAL_MEDAL_RESULT 0x208 #define SE_BIG_WARP_RING 0x209 diff --git a/include/game/player_callbacks.h b/include/game/player_callbacks.h index fdf96e3e..60d91cee 100644 --- a/include/game/player_callbacks.h +++ b/include/game/player_callbacks.h @@ -20,6 +20,7 @@ extern void Player_800A7CC(Player *p); extern void Player_800A860(Player *p); extern void Player_800A90C(Player *p); extern void Player_800A98C(Player *p); +extern void Player_UseSpecialSpringWithKey(Player *p); extern void Player_800AD24(Player *p); extern void Player_800AE14(Player *p); extern void Player_800B1B8(Player *p); diff --git a/ldscript.txt b/ldscript.txt index b20f784f..ec112577 100644 --- a/ldscript.txt +++ b/ldscript.txt @@ -63,7 +63,7 @@ SECTIONS { build/sa3/src/game/interactables/pole.o(.text); build/sa3/src/game/interactables/mud_fx.o(.text); build/sa3/asm/ia_act_ring.o(.text); - build/sa3/asm/ia_special_spring.o(.text); + build/sa3/src/game/interactables/special_spring.o(.text); build/sa3/src/game/interactables/factory_ring.o(.text); build/sa3/asm/ia_bonus_ufo.o(.text); build/sa3/asm/ia_100.o(.text); diff --git a/src/game/interactables/platform_raising_wheel.c b/src/game/interactables/platform_raising_wheel.c index bf696631..a46b518e 100644 --- a/src/game/interactables/platform_raising_wheel.c +++ b/src/game/interactables/platform_raising_wheel.c @@ -28,7 +28,7 @@ typedef struct { void Task_PlatformRaisingWheel(void); void TaskDestructor_PlatformRaisingWheel(struct Task *t); void sub_8035F8C(void); -void sub_8036068(Sprite *s); +static void InitSprite(Sprite *s); void CreateEntity_PlatformRaisingWheel(MapEntity *me, u16 regionX, u16 regionY, u8 id) { @@ -61,7 +61,7 @@ void CreateEntity_PlatformRaisingWheel(MapEntity *me, u16 regionX, u16 regionY, SET_MAP_ENTITY_INITIALIZED(me); - sub_8036068(s); + InitSprite(s); } void Task_PlatformRaisingWheel(void) @@ -175,7 +175,7 @@ void TaskDestructor_PlatformRaisingWheel(struct Task *t) VramFree(wheel->s.tiles); } -void sub_8036068(Sprite *s) +static void InitSprite(Sprite *s) { s->tiles = ALLOC_TILES(ANIM_PLATFORM_RAISING_WHEEL); s->anim = ANIM_PLATFORM_RAISING_WHEEL; diff --git a/src/game/interactables/special_spring.c b/src/game/interactables/special_spring.c new file mode 100644 index 00000000..829f3eb3 --- /dev/null +++ b/src/game/interactables/special_spring.c @@ -0,0 +1,200 @@ +#include "global.h" +#include "malloc_vram.h" +#include "module_unclear.h" +#include "sprite.h" +#include "task.h" +#include "game/camera.h" +#include "game/entity.h" +#include "game/player.h" +#include "game/player_callbacks.h" +#include "game/save.h" +#include "game/stage.h" + +#include "constants/animations.h" +#include "constants/anim_sizes.h" +#include "constants/move_states.h" +#include "constants/songs.h" + +typedef struct { + /* 0x00 */ SpriteBase base; + /* 0x0C */ Sprite s0; + /* 0x34 */ Sprite sprCompletionCrown; + /* 0x5C */ u8 filler5C[0x50]; + /* 0xAC */ s16 worldX; + /* 0xAE */ s16 worldY; + /* 0xB0 */ bool8 wasCompletedBefore; +} SpecialSpring; /* size: 0xB4 */ + +void Task_SpecialSpring(void); +void TaskDestructor_SpecialSpring(struct Task *); +static void InitSprites(SpecialSpring *); +void sub_8040FD8(void); + +void CreateEntity_SpecialSpring(MapEntity *me, u16 regionX, u16 regionY, u8 id) +{ + struct Task *t = TaskCreate(Task_SpecialSpring, sizeof(SpecialSpring), 0x2100, 0, TaskDestructor_SpecialSpring); + SpecialSpring *spring = TASK_DATA(t); + Sprite *s; + s16 worldX, worldY; + + spring->base.regionX = regionX; + spring->base.regionY = regionY; + spring->base.me = me; + spring->base.spriteX = me->x; + spring->base.id = id; + + spring->worldX = TO_WORLD_POS(me->x, regionX); + spring->worldY = TO_WORLD_POS(me->y, regionY); + spring->wasCompletedBefore = 0; + + s = &spring->s0; + s->x = spring->worldX - gCamera.x; + s->y = spring->worldY - gCamera.y; + + SET_MAP_ENTITY_INITIALIZED(me); + + InitSprites(spring); +} + +void Task_SpecialSpring(void) +{ + SpecialSpring *spring = TASK_DATA(gCurTask); + Sprite *s = &spring->s0; + MapEntity *me = spring->base.me; + Player *p; + Player *partner; + s16 worldX, worldY; + + worldX = TO_WORLD_POS(spring->base.spriteX, spring->base.regionX); + worldY = TO_WORLD_POS(me->y, spring->base.regionY); + + p = &gPlayers[gStageData.charId]; + + if (!sub_802C080(p)) { + + u32 mask = sub_8020950(s, worldX, worldY, p, NULL); + + if (mask) { + if (mask & 0x10000) { + u8 spKeys = gSaveGame.specialKeys[gStageData.zone]; + if ((spKeys > 0) && (gStageData.gameMode == GAME_MODE_SINGLE_PLAYER)) { + gSaveGame.specialKeys[gStageData.zone] = spKeys - 1; + SetPlayerCallback(p, (void *)Player_UseSpecialSpringWithKey); + + partner = &gPlayers[p->charFlags.partnerIndex]; + + if (partner->charFlags.someIndex == 2) { + SetPlayerCallback(partner, (void *)Player_800AD24); + } + + s->variant = 2; + sub_8003DF0(SE_SPECIAL_SPRING); + gStageData.unk4 = 4; + } else { + s16 qDY = Q(mask & 0x00FF); + p->qWorldY += qDY; + } + } else if (mask & 0xC0000) { + s16 qDX; + + p->qSpeedAirX = 0; + p->qSpeedGround = 0; + + qDX = mask & 0xFF00; + p->qWorldX += qDX; + + // TODO: Maybe a macro? + // Used in platform_raising_wheel as well. + if ((mask & 0x40000) && (p->keyInput & DPAD_LEFT)) { + p->qWorldX -= Q(1.0); + p->moveState |= MOVESTATE_40; + } else if ((mask & 0x80000) && (p->keyInput & DPAD_RIGHT)) { + p->qWorldX += Q(1.0); + p->moveState |= MOVESTATE_40; + } + } else { + s16 qDY = Q(mask & 0x00FF); + p->qWorldY += qDY; + } + } + } + + sub_8040FD8(); +} + +static void InitSprites(SpecialSpring *spring) +{ + Sprite *s = &spring->s0; + s->tiles = ALLOC_TILES(ANIM_SPECIAL_SPRING); + s->anim = ANIM_SPECIAL_SPRING; + s->variant = 0; + s->oamFlags = SPRITE_OAM_ORDER(24); + s->animCursor = 0; + s->timeUntilNextFrame = 0; + s->prevVariant = -1; + s->animSpeed = SPRITE_ANIM_SPEED(1.0); + s->palId = 0; + s->hitboxes[0].index = HITBOX_STATE_INACTIVE; + s->frameFlags = SPRITE_FLAG(PRIORITY, 1); + UpdateSpriteAnimation(s); + + if ((gSaveGame.collectedEmeralds >> gStageData.zone) & 0x1) { + s = &spring->sprCompletionCrown; + s->tiles = ALLOC_TILES_VARIANT(ANIM_ACT_RING, 3); + s->anim = ANIM_ACT_RING; + s->variant = 3; + s->oamFlags = SPRITE_OAM_ORDER(24); + s->animCursor = 0; + s->timeUntilNextFrame = 0; + s->prevVariant = -1; + s->animSpeed = SPRITE_ANIM_SPEED(1.0); + s->palId = 0; + s->hitboxes[0].index = HITBOX_STATE_INACTIVE; + s->frameFlags = SPRITE_FLAG(PRIORITY, 1); + UpdateSpriteAnimation(s); + + spring->wasCompletedBefore = 1; + } else { + spring->wasCompletedBefore = 0; + } +} + +void sub_8040FD8(void) +{ + SpecialSpring *spring = TASK_DATA(gCurTask); + Sprite *s = &spring->s0; + MapEntity *me = spring->base.me; + s16 worldX, worldY; + + worldX = spring->worldX; + worldY = spring->worldY; + + if (!IsPointInScreenRect(worldX, worldY)) { + SET_MAP_ENTITY_NOT_INITIALIZED(me, spring->base.spriteX); + TaskDestroy(gCurTask); + return; + } else { + s->x = worldX - gCamera.x; + s->y = worldY - gCamera.y; + UpdateSpriteAnimation(s); + DisplaySprite(s); + + if (spring->wasCompletedBefore) { + s = &spring->sprCompletionCrown; + s->x = worldX - gCamera.x; + s->y = worldY - gCamera.y - 24; + UpdateSpriteAnimation(s); + DisplaySprite(s); + } + } +} + +void TaskDestructor_SpecialSpring(struct Task *t) +{ + SpecialSpring *spring = TASK_DATA(t); + VramFree(spring->s0.tiles); + + if (spring->wasCompletedBefore) { + VramFree(spring->sprCompletionCrown.tiles); + } +} \ No newline at end of file