papermario/src/7E9D0.c
HailSanta c1d9bbbd2a
more world partners (#976)
* wip

* hm

* update

* bow

* parakarry

---------

Co-authored-by: HailSanta <Hail2Santa@gmail.com>
2023-03-14 17:55:49 +09:00

685 lines
22 KiB
C

// this file and world/actions.c are candidates for merging
#include "common.h"
#include "world/partners.h"
#include "world/actions.h"
#include "npc.h"
#include "effects.h"
#include "ld_addrs.h"
#ifdef SHIFT
#define PLAYER_ACTION_VRAM_DEF texture_memory_VRAM_END
#else
#define PLAYER_ACTION_VRAM_DEF (void*) 0x802B6000
#endif
extern void* D_8010C924;
extern s32 D_8010C964;
extern s32 gSpinHistoryBufferPos;
extern s16 D_8010C9B0;
extern s32 gSpinHistoryPosY[5];
extern s32 gSpinHistoryPosX[5];
extern s32 gSpinHistoryPosZ[5];
extern s16 gSpinHistoryPosAngle[5];
void func_800E5520(void) {
D_8010C9B0 = 0;
}
s32 phys_adjust_cam_on_landing(void) {
PartnerActionStatus* partnerActionStatus = &gPartnerActionStatus;
GameStatus* gameStatus = gGameStatusPtr;
PlayerStatus* playerStatus = &gPlayerStatus;
s32 ret = 1;
switch (gameStatus->areaID) {
case AREA_OBK:
ret = gameStatus->mapID != 4;
break;
case AREA_ISK:
switch (gameStatus->mapID) {
case 0:
ret = 2;
break;
case 1:
if (D_8010C9B0 == 0) {
if (playerStatus->position.y <= 0.0f) {
D_8010C9B0 = 1;
}
ret = 2;
} else if (playerStatus->position.y > 0.0f) {
ret = 0;
}
break;
case 3:
if (playerStatus->position.y > 25.0f) {
ret = 0;
}
break;
case 4:
if (playerStatus->position.y > 50.0f) {
ret = 0;
}
break;
case 7:
if (playerStatus->position.y > -390.0f) {
ret = 0;
} else if (playerStatus->position.y < -495.0f) {
ret = 0;
}
break;
case 8:
if (playerStatus->position.y > -90.0f) {
ret = 0;
} else if (playerStatus->position.y < -370.0f) {
ret = 0;
}
break;
case 2:
if (gGameStatusPtr->entryID == 0) {
if (D_8010C9B0 == 0) {
if (!(playerStatus->position.y > 0.0f)) {
D_8010C9B0 = 1;
} else {
ret = 2;
break;
}
}
if (playerStatus->position.y > 0.0f) {
ret = 0;
}
} else {
ret = 2;
}
break;
case 5:
if (gGameStatusPtr->entryID == 0) {
if (D_8010C9B0 == 0) {
if (!(playerStatus->position.y > -130.0f)) {
D_8010C9B0 = 1;
} else {
ret = 2;
break;
}
}
if (playerStatus->position.y > -130.0f) {
ret = 0;
}
} else {
ret = 2;
}
break;
case 10:
if (D_8010C9B0 == 0) {
if (!(playerStatus->position.y > -520.0f)) {
D_8010C9B0 = 1;
} else {
ret = 2;
break;
}
}
if (playerStatus->position.y > -520.0f) {
ret = 0;
}
break;
case 11:
if (gGameStatusPtr->entryID == 0) {
if (D_8010C9B0 == 0) {
if (!(playerStatus->position.y > -520.0f)) {
D_8010C9B0 = 1;
} else {
ret = 2;
break;
}
}
if (playerStatus->position.y > -520.0f) {
ret = 0;
}
}
if (evt_get_variable(NULL, GB_StoryProgress) >= STORY_CH2_DRAINED_THIRD_SAND_ROOM) {
ret = 2;
}
break;
}
break;
}
if (ret == 1) {
s32 surfaceType = get_collider_flags(gCollisionStatus.currentFloor) & COLLIDER_FLAGS_SURFACE_TYPE_MASK;
if (surfaceType == SURFACE_TYPE_LAVA) {
gCameras[CAM_DEFAULT].moveFlags |= CAMERA_MOVE_IGNORE_PLAYER_Y;
ret = 0;
} else {
gCameras[CAM_DEFAULT].moveFlags &= ~CAMERA_MOVE_IGNORE_PLAYER_Y;
}
} else if (partnerActionStatus->partnerActionState != PARTNER_ACTION_NONE && partnerActionStatus->actingPartner == PARTNER_PARAKARRY) {
gCameras[CAM_DEFAULT].moveFlags |= CAMERA_MOVE_FLAG_2;
} else {
gCameras[CAM_DEFAULT].moveFlags &= ~CAMERA_MOVE_FLAG_2;
}
return ret;
}
void phys_clear_spin_history(void) {
s32 i;
gSpinHistoryBufferPos = 0;
for (i = 0; i < ARRAY_COUNT(gSpinHistoryPosY); i++) {
gSpinHistoryPosAngle[i] = 180;
gSpinHistoryPosY[i] = 0x80000000;
}
}
f32 phys_get_spin_history(s32 lag, s32* x, s32* y, s32* z) {
s32 idx = gSpinHistoryBufferPos;
// Can't get this if/else to match otherwise...
if (idx - lag >= 0) {
idx -= lag;
} else {
idx = gSpinHistoryBufferPos - lag + 5;
}
*x = gSpinHistoryPosX[idx];
*y = gSpinHistoryPosY[idx];
*z = gSpinHistoryPosZ[idx];
return gSpinHistoryPosAngle[idx];
}
void phys_reset_spin_history(void) {
s32 i;
mem_clear(&gPlayerSpinState, sizeof(PlayerSpinState));
gSpinHistoryBufferPos = 0;
for (i = 0; i < ARRAY_COUNT(gSpinHistoryPosAngle); i++) {
gSpinHistoryPosAngle[i] = 0;
gSpinHistoryPosX[i] = 0;
gSpinHistoryPosY[i] = 0;
gSpinHistoryPosZ[i] = 0;
}
D_8010C964 = 0;
D_8010C924 = NULL;
}
void phys_update_action_state(void) {
Camera* cameras = gCameras;
PartnerActionStatus* partnerActionStatus = &gPartnerActionStatus;
PlayerStatus* playerStatus = &gPlayerStatus;
PlayerSpinState* playerSpinState = &gPlayerSpinState;
if (!(playerStatus->flags & PS_FLAG_ENTERING_BATTLE)) {
playerStatus->flags &= ~PS_FLAG_TIME_STOPPED;
}
if (playerStatus->animFlags & PA_FLAG_USING_PEACH_PHYSICS) {
phys_peach_update();
return;
}
if (playerStatus->camResetDelay != 0) {
playerStatus->camResetDelay--;
if (playerStatus->camResetDelay == 0) {
gCameras[CAM_DEFAULT].moveFlags |= CAMERA_MOVE_FLAG_4;
}
}
// if midair, look for velocity inflection
if (playerStatus->timeInAir != 0) {
if (playerStatus->gravityIntegrator[0] <= 0.0f && LastMidairPlayerVelY > 0.0f) {
playerStatus->peakJumpTime = playerStatus->timeInAir;
}
LastMidairPlayerVelY = playerStatus->gravityIntegrator[0];
}
func_800E24F8();
if (playerSpinState->stopSoundTimer != 0) {
playerSpinState->stopSoundTimer--;
if (playerSpinState->stopSoundTimer == 0) {
playerStatus->animFlags &= ~PA_FLAG_SPINNING;
if (playerSpinState->spinSoundID != 0) {
sfx_stop_sound(playerSpinState->spinSoundID);
}
}
}
do {
s32 actionState = playerStatus->actionState;
if (actionState == ACTION_STATE_IDLE || actionState == ACTION_STATE_WALK || actionState == ACTION_STATE_RUN) {
s32 cond;
if (!(playerStatus->flags & PS_FLAG_INPUT_DISABLED)) {
cond = check_conversation_trigger();
} else {
cond = FALSE;
}
if ((partnerActionStatus->partnerActionState == PARTNER_ACTION_NONE) && !(playerStatus->flags & PS_FLAG_PAUSED) && cond) {
set_action_state(ACTION_STATE_TALK);
}
check_input_spin();
}
if (playerStatus->flags & PS_FLAG_ACTION_STATE_CHANGED) {
void* dmaStart = PlayerActionsTable[playerStatus->actionState].dmaStart;
if (dmaStart != NULL && dmaStart != D_8010C924) {
D_8010C924 = dmaStart;
dma_copy(dmaStart, PlayerActionsTable[playerStatus->actionState].dmaEnd, PLAYER_ACTION_VRAM_DEF);
}
}
PlayerActionsTable[playerStatus->actionState].update();
} while (playerStatus->flags & PS_FLAG_ACTION_STATE_CHANGED);
}
void phys_peach_update(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
func_800E24F8();
do {
if (!(playerStatus->flags & PS_FLAG_PAUSED) && check_conversation_trigger()) {
set_action_state(ACTION_STATE_TALK);
}
if (playerStatus->flags & PS_FLAG_ACTION_STATE_CHANGED) {
Action* action = &PlayerActionsTable[playerStatus->actionState];
if (action->flag) {
if (action->dmaStart != NULL && action->dmaStart != D_8010C924) {
D_8010C924 = action->dmaStart;
dma_copy(D_8010C924, PlayerActionsTable[playerStatus->actionState].dmaEnd, PLAYER_ACTION_VRAM_DEF);
}
if (PlayerActionsTable[playerStatus->actionState].flag) {
PlayerActionsTable[playerStatus->actionState].update();
}
}
} else {
if (PlayerActionsTable[playerStatus->actionState].flag) {
PlayerActionsTable[playerStatus->actionState].update();
}
}
} while (playerStatus->flags & PS_FLAG_ACTION_STATE_CHANGED);
peach_check_for_parasol_input();
if (playerStatus->animFlags & PA_FLAG_INVISIBLE) {
peach_sync_disguise_npc();
}
}
void set_action_state(s32 actionState) {
PlayerStatus* playerStatus = &gPlayerStatus;
PlayerData* playerData = &gPlayerData;
PlayerSpinState* spinState = &gPlayerSpinState;
if (playerStatus->flags & PS_FLAG_SPECIAL_JUMP) {
playerStatus->flags &= ~PS_FLAG_SPECIAL_JUMP;
enable_player_input();
}
if (playerStatus->animFlags & PA_FLAG_8BIT_MARIO) {
// TODO figure this out
#ifdef NON_EQUIVALENT
if (
actionState == ACTION_STATE_IDLE || actionState == ACTION_STATE_WALK ||
actionState == ACTION_STATE_RUN || actionState == ACTION_STATE_JUMP ||
actionState == ACTION_STATE_BOUNCE || actionState == ACTION_STATE_HOP ||
actionState == ACTION_STATE_LAUNCH || actionState == ACTION_STATE_LANDING_ON_SWITCH ||
actionState == ACTION_STATE_FALLING || actionState == ACTION_STATE_STEP_DOWN ||
actionState == ACTION_STATE_LAND || actionState == ACTION_STATE_STEP_DOWN_LAND
) {
#else
if (actionState < ACTION_STATE_TALK)
if (actionState >= 0) {
#endif
playerStatus->prevActionState = playerStatus->actionState;
playerStatus->actionState = actionState;
playerStatus->flags |= PS_FLAG_ACTION_STATE_CHANGED;
}
return;
}
if (actionState == ACTION_STATE_HIT_FIRE || actionState == ACTION_STATE_HIT_LAVA) {
s8 partner;
if (playerStatus->hazardType == HAZARD_TYPE_FIRE_BAR) {
actionState = ACTION_STATE_HIT_FIRE;
}
// Whilst Sushie, Lakilester, or Parakarry's ability is active, hazards have no effect.
partner = playerData->currentPartner;
if (partner == PARTNER_SUSHIE || partner == PARTNER_LAKILESTER || partner == PARTNER_PARAKARRY) {
if (gPartnerActionStatus.partnerActionState != PARTNER_ACTION_NONE) {
playerStatus->animFlags |= PA_FLAG_INTERRUPT_USE_PARTNER;
playerStatus->flags |= PS_FLAG_HIT_FIRE;
return;
}
}
}
if (actionState == ACTION_STATE_SLIDING) {
playerStatus->flags |= PS_FLAG_SLIDING;
playerStatus->moveFrames = 0;
playerStatus->flags &= ~PS_FLAG_CUTSCENE_MOVEMENT;
}
playerStatus->prevActionState = playerStatus->actionState;
if (actionState == ACTION_STATE_USE_TWEESTER) {
playerStatus->prevActionState = ACTION_STATE_IDLE;
}
if (actionState == ACTION_STATE_ENEMY_FIRST_STRIKE) {
playerStatus->animFlags |= PA_FLAG_INTERRUPT_USE_PARTNER;
}
playerStatus->actionState = actionState;
playerStatus->flags |= PS_FLAG_ACTION_STATE_CHANGED;
if (playerStatus->actionState == ACTION_STATE_SPIN) {
return;
}
playerStatus->flags &= ~PS_FLAG_SPINNING;
playerStatus->animFlags &= ~PA_FLAG_SPINNING;
if (spinState->spinSoundID != 0) {
sfx_stop_sound(spinState->spinSoundID);
}
if (playerStatus->specialDecorationEffect != NULL) {
playerStatus->specialDecorationEffect->data.unk_46->unk_24 = 10;
playerStatus->specialDecorationEffect = NULL;
}
}
void update_locomotion_state(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
if (!is_ability_active(ABILITY_SLOW_GO) &&
SQ(playerStatus->stickAxis[0]) + SQ(playerStatus->stickAxis[1]) > SQ(55))
{
set_action_state(ACTION_STATE_RUN);
} else {
set_action_state(ACTION_STATE_WALK);
}
}
void start_falling(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
set_action_state(ACTION_STATE_FALLING);
LOAD_INTEGRATOR_FALL(playerStatus->gravityIntegrator);
}
void start_bounce_a(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
set_action_state(ACTION_STATE_BOUNCE);
playerStatus->gravityIntegrator[0] = 10.0f;
playerStatus->gravityIntegrator[1] = -2.0f;
playerStatus->gravityIntegrator[2] = 0.8f;
playerStatus->gravityIntegrator[3] = -0.75f;
}
void start_bounce_b(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
set_action_state(ACTION_STATE_BOUNCE);
playerStatus->gravityIntegrator[0] = 8.0f;
playerStatus->gravityIntegrator[1] = -1.0f;
playerStatus->gravityIntegrator[2] = 0;
playerStatus->gravityIntegrator[3] = 0;
playerStatus->flags |= PS_FLAG_SCRIPTED_FALL;
}
s32 check_input_hammer(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
PlayerData* playerData = &gPlayerData;
if (playerStatus->pressedButtons & BUTTON_B) {
if (playerStatus->flags & PS_FLAG_FALLING) {
return FALSE;
}
if (gPartnerActionStatus.partnerActionState == PARTNER_ACTION_USE && playerData->currentPartner == PARTNER_WATT) {
return FALSE;
}
if (playerData->hammerLevel == -1) {
return FALSE;
}
set_action_state(ACTION_STATE_HAMMER);
return TRUE;
}
return FALSE;
}
s32 check_input_jump(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
CollisionStatus* collisionStatus = &gCollisionStatus;
s32 surfaceType;
if (!(playerStatus->pressedButtons & BUTTON_A)) {
return FALSE;
}
// @bug? collider flags not properly masked with COLLIDER_FLAG_SURFACE_TYPE
surfaceType = get_collider_flags((u16)gCollisionStatus.currentFloor);
if ((surfaceType == SURFACE_TYPE_SLIDE) && phys_should_player_be_sliding()) {
return FALSE;
}
if (collisionStatus->touchingWallTrigger != 0 ||
(playerStatus->animFlags & (PA_FLAG_SPEECH_PROMPT_AVAILABLE |
PA_FLAG_INTERACT_PROMPT_AVAILABLE)))
{
return FALSE;
}
if ((collisionStatus->currentInspect != -1) && (collisionStatus->currentInspect & COLLISION_WITH_ENTITY_BIT)) {
Entity* entity = get_entity_by_index(collisionStatus->currentInspect);
if (entity->flags & ENTITY_FLAG_SHOWS_INSPECT_PROMPT) {
if ((entity->boundScriptBytecode == 0) || (entity->flags & ENTITY_FLAG_4000)) {
if (entity->type == ENTITY_TYPE_PINK_FLOWER ||
entity->type == ENTITY_TYPE_BELLBELL_PLANT ||
entity->type == ENTITY_TYPE_TRUMPET_PLANT)
{
return FALSE;
}
} else {
return FALSE;
}
}
}
set_action_state(ACTION_STATE_JUMP);
return TRUE;
}
void check_input_spin(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
PlayerSpinState* spinState = &gPlayerSpinState;
PlayerSpinState* temp2 = spinState;
if (!((playerStatus->flags & (PS_FLAG_NO_STATIC_COLLISION | PS_FLAG_CUTSCENE_MOVEMENT)) ||
(playerStatus->animFlags & PA_FLAG_USING_WATT) ||
(playerStatus->currentButtons & BUTTON_C_DOWN) ||
is_ability_active(ABILITY_SLOW_GO))) {
s32 actionState = playerStatus->actionState;
s32 btnPressed = playerStatus->pressedButtons & Z_TRIG;
// TODO
if (actionState != ACTION_STATE_RIDE) {
if (actionState < ACTION_STATE_STEP_UP) {
if (actionState < ACTION_STATE_JUMP) {
if (actionState >= 0 && !(playerStatus->animFlags & PA_FLAG_SPINNING)) {
if (btnPressed || spinState->hasBufferedSpin) {
set_action_state(ACTION_STATE_SPIN);
if (spinState->hasBufferedSpin != FALSE) {
if (spinState->bufferedStickAxis.x != 0 || spinState->bufferedStickAxis.y != 0) {
playerStatus->prevActionState = temp2->prevActionState;
} else {
playerStatus->prevActionState = ACTION_STATE_IDLE;
}
}
}
}
}
}
}
}
}
void peach_set_disguise_anim(AnimID anim) {
s32 listIndex = PeachDisguiseNpcIndex;
if (listIndex >= 0) {
get_npc_by_index(listIndex)->currentAnim = anim;
}
}
void peach_force_disguise_action(s32 useParasol) {
PlayerStatus* playerStatus = &gPlayerStatus;
if (useParasol) {
set_action_state(ACTION_STATE_USE_SNEAKY_PARASOL);
} else {
playerStatus->animFlags &= ~PA_FLAG_INVISIBLE;
gGameStatusPtr->peachFlags &= ~PEACH_STATUS_FLAG_DISGUISED;
playerStatus->peachDisguise = PEACH_DISGUISE_NONE;
free_npc_by_index(PeachDisguiseNpcIndex);
set_action_state(ACTION_STATE_IDLE);
playerStatus->colliderHeight = 55;
playerStatus->colliderDiameter = 38;
}
}
void peach_check_for_parasol_input(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
s32 actionState = playerStatus->actionState;
Npc* disguiseNpc;
if (actionState == ACTION_STATE_IDLE || actionState == ACTION_STATE_WALK || actionState == ACTION_STATE_RUN) {
if (D_8010C92C != 0) {
D_8010C92C--;
if (D_8010C92C == 0) {
if (gGameStatusPtr->peachFlags & PEACH_STATUS_FLAG_DISGUISED) {
playerStatus->animFlags |= PA_FLAG_INVISIBLE;
gGameStatusPtr->peachFlags |= PEACH_STATUS_FLAG_DISGUISED;
disguiseNpc = peach_make_disguise_npc(gGameStatusPtr->peachDisguise);
if (disguiseNpc != NULL) {
disguiseNpc->flags &= ~NPC_FLAG_IGNORE_CAMERA_FOR_YAW;
}
}
}
} else if (gGameStatusPtr->peachFlags & PEACH_STATUS_FLAG_HAS_PARASOL && playerStatus->pressedButtons & BUTTON_B) {
set_action_state(ACTION_STATE_USE_SNEAKY_PARASOL);
}
}
}
void peach_sync_disguise_npc(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
if (PeachDisguiseNpcIndex >= 0) {
Npc* npc = get_npc_by_index(PeachDisguiseNpcIndex);
if (npc->flags & NPC_FLAG_IGNORE_CAMERA_FOR_YAW) {
npc->renderYaw = playerStatus->spriteFacingAngle;
} else {
npc->yaw = playerStatus->targetYaw;
}
npc->pos.x = playerStatus->position.x;
npc->pos.y = playerStatus->position.y;
npc->pos.z = playerStatus->position.z;
}
}
Npc* peach_make_disguise_npc(s32 peachDisguise) {
PlayerStatus* playerStatus = &gPlayerStatus;
NpcBlueprint bp;
NpcBlueprint* bpPtr = &bp;
Npc* npc;
f32 yaw;
if (peachDisguise == PEACH_DISGUISE_NONE) {
return NULL;
}
playerStatus->colliderHeight = 37;
playerStatus->colliderDiameter = 38;
playerStatus->peachDisguise = peachDisguise;
gGameStatusPtr->peachDisguise = peachDisguise;
bpPtr->flags = NPC_FLAG_8 | NPC_FLAG_IGNORE_WORLD_COLLISION | NPC_FLAG_IGNORE_PLAYER_COLLISION | NPC_FLAG_IGNORE_CAMERA_FOR_YAW;
bpPtr->initialAnim = BasicPeachDisguiseAnims[playerStatus->peachDisguise].idle;
bpPtr->onUpdate = NULL;
bpPtr->onRender = NULL;
PeachDisguiseNpcIndex = create_standard_npc(bpPtr, PeachDisguiseExtraAnims[playerStatus->peachDisguise]);
npc = get_npc_by_index(PeachDisguiseNpcIndex);
disable_npc_shadow(npc);
if (!(playerStatus->spriteFacingAngle >= 90.0f) || !(playerStatus->spriteFacingAngle < 270.0f)) {
yaw = 180.0f;
} else {
yaw = 0.0f;
}
set_npc_yaw(npc, yaw);
npc->pos.x = playerStatus->position.x;
npc->pos.y = playerStatus->position.y;
npc->pos.z = playerStatus->position.z;
return npc;
}
s32 peach_disguise_check_overlaps(void) {
PlayerStatus* playerStatus = &gPlayerStatus;
Camera* camera = &gCameras[gCurrentCameraID];
f32 yaw, dx, dy;
s32 radius;
s32 hitID;
s32 i;
if (playerStatus->spriteFacingAngle >= 90.0f && playerStatus->spriteFacingAngle < 270.0f) {
yaw = camera->currentYaw - 270.0f;
} else {
yaw = camera->currentYaw - 90.0f;
}
sin_cos_rad(DEG_TO_RAD(clamp_angle(yaw)), &dx, &dy);
for (radius = 2, i = 2; i > 0; radius += 18, i--) {
f32 x = playerStatus->position.x + (dx * radius);
f32 y = playerStatus->position.y + 4.0f;
f32 z = playerStatus->position.z - (dy * radius);
hitID = player_test_lateral_overlap(3, playerStatus, &x, &y, &z, 4.0f, yaw);
if (hitID >= 0) {
break;
}
}
return hitID;
}