diff --git a/include/types.h b/include/types.h index b3afed7f..d81dc460 100644 --- a/include/types.h +++ b/include/types.h @@ -370,6 +370,11 @@ struct MarioState /*????*/ u8 isSnoring; /*????*/ struct Object* bubbleObj; /*????*/ u8 freeze; + + // Variables for a spline curve animation (used for the flight path in the grand star cutscene) + /*????*/ Vec4s* splineKeyframe; + /*????*/ float splineKeyframeFraction; + /*????*/ int splineState; }; #define PLAY_MODE_NORMAL 0 diff --git a/src/engine/math_util.c b/src/engine/math_util.c index ca2a7457..0ce743b6 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -7,11 +7,6 @@ #include "trig_tables.inc.c" -// Variables for a spline curve animation (used for the flight path in the grand star cutscene) -Vec4s *gSplineKeyframe; -float gSplineKeyframeFraction; -int gSplineState; - // These functions have bogus return values. // Disable the compiler warning. #pragma GCC diagnostic push @@ -824,14 +819,14 @@ f32 atan2f(f32 y, f32 x) { * [0, 0, 0, 0, 1, 2, ... n-1, n, n, n, n] * TODO: verify the classification of the spline / figure out how polynomials were computed */ -void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c) { +void spline_get_weights(struct MarioState* m, Vec4f result, f32 t, UNUSED s32 c) { f32 tinv = 1 - t; f32 tinv2 = tinv * tinv; f32 tinv3 = tinv2 * tinv; f32 t2 = t * t; f32 t3 = t2 * t; - switch (gSplineState) { + switch (m->splineState) { case CURVE_BEGIN_1: result[0] = tinv3; result[1] = t3 * 1.75f - t2 * 4.5f + t * 3.0f; @@ -873,10 +868,10 @@ void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c) { * The array should end with three entries with s=0 (infinite keyframe duration). * That's because the spline has a 3rd degree polynomial, so it looks 3 points ahead. */ -void anim_spline_init(Vec4s *keyFrames) { - gSplineKeyframe = keyFrames; - gSplineKeyframeFraction = 0; - gSplineState = 1; +void anim_spline_init(struct MarioState* m, Vec4s *keyFrames) { + m->splineKeyframe = keyFrames; + m->splineKeyframeFraction = 0; + m->splineState = 1; } /** @@ -884,33 +879,33 @@ void anim_spline_init(Vec4s *keyFrames) { * anim_spline_init should be called before polling for vectors. * Returns TRUE when the last point is reached, FALSE otherwise. */ -s32 anim_spline_poll(Vec3f result) { +s32 anim_spline_poll(struct MarioState* m, Vec3f result) { Vec4f weights; s32 i; s32 hasEnded = FALSE; vec3f_copy(result, gVec3fZero); - spline_get_weights(weights, gSplineKeyframeFraction, gSplineState); + spline_get_weights(m, weights, m->splineKeyframeFraction, m->splineState); for (i = 0; i < 4; i++) { - result[0] += weights[i] * gSplineKeyframe[i][1]; - result[1] += weights[i] * gSplineKeyframe[i][2]; - result[2] += weights[i] * gSplineKeyframe[i][3]; + result[0] += weights[i] * m->splineKeyframe[i][1]; + result[1] += weights[i] * m->splineKeyframe[i][2]; + result[2] += weights[i] * m->splineKeyframe[i][3]; } - if ((gSplineKeyframeFraction += gSplineKeyframe[0][0] / 1000.0f) >= 1) { - gSplineKeyframe++; - gSplineKeyframeFraction--; - switch (gSplineState) { + if ((m->splineKeyframeFraction += m->splineKeyframe[0][0] / 1000.0f) >= 1) { + m->splineKeyframe++; + m->splineKeyframeFraction--; + switch (m->splineState) { case CURVE_END_2: hasEnded = TRUE; break; case CURVE_MIDDLE: - if (gSplineKeyframe[2][0] == 0) { - gSplineState = CURVE_END_1; + if (m->splineKeyframe[2][0] == 0) { + m->splineState = CURVE_END_1; } break; default: - gSplineState++; + m->splineState++; break; } } diff --git a/src/engine/math_util.h b/src/engine/math_util.h index 00245064..005b9303 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -76,8 +76,8 @@ s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec); f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec); s16 atan2s(f32 y, f32 x); f32 atan2f(f32 a, f32 b); -void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c); -void anim_spline_init(Vec4s *keyFrames); -s32 anim_spline_poll(Vec3f result); +void spline_get_weights(struct MarioState* m, Vec4f result, f32 t, UNUSED s32 c); +void anim_spline_init(struct MarioState* m, Vec4s *keyFrames); +s32 anim_spline_poll(struct MarioState* m, Vec3f result); #endif // MATH_UTIL_H diff --git a/src/game/behaviors/grand_star.inc.c b/src/game/behaviors/grand_star.inc.c index 92cadcb3..799343af 100644 --- a/src/game/behaviors/grand_star.inc.c +++ b/src/game/behaviors/grand_star.inc.c @@ -68,6 +68,9 @@ void bhv_grand_star_loop(void) { } else { cur_obj_become_tangible(); if (o->oInteractStatus & INT_STATUS_INTERACTED) { + if (gMarioStates[0].action != ACT_JUMBO_STAR_CUTSCENE) { + set_mario_action(&gMarioStates[0], ACT_JUMBO_STAR_CUTSCENE, 0); + } obj_mark_for_deletion(o); o->oInteractStatus = 0; } diff --git a/src/game/interaction.c b/src/game/interaction.c index d94a829e..125cf5b5 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -1228,6 +1228,7 @@ u8 player_is_sliding(struct MarioState* m) { u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object* o) { if (gServerSettings.playerInteractions == PLAYER_INTERACTIONS_NONE) { return FALSE; } + if (m->action == ACT_JUMBO_STAR_CUTSCENE) { return FALSE; } struct MarioState* m2 = NULL; for (int i = 0; i < MAX_PLAYERS; i++) { @@ -1237,6 +1238,7 @@ u32 interact_player(struct MarioState* m, UNUSED u32 interactType, struct Object } } if (m2 == NULL) { return FALSE; } + if (m2->action == ACT_JUMBO_STAR_CUTSCENE) { return FALSE; } // don't do further interactions if we've hopped on top if (resolve_player_collision(m, m2)) { diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index d437d717..e562dc62 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -1940,6 +1940,11 @@ static s32 act_intro_cutscene(struct MarioState *m) { return FALSE; } +static void jumbo_star_offset(struct MarioState* m) { + m->pos[0] += 300.0f * sins(m->faceAngle[1] + 0x4000 * m->playerIndex); + m->pos[2] += 300.0f * coss(m->faceAngle[1] + 0x4000 * m->playerIndex); +} + // jumbo star cutscene: Mario lands after grabbing the jumbo star static void jumbo_star_cutscene_falling(struct MarioState *m) { if (m->actionState == 0) { @@ -1947,7 +1952,7 @@ static void jumbo_star_cutscene_falling(struct MarioState *m) { m->flags |= (MARIO_WING_CAP | MARIO_CAP_ON_HEAD); m->faceAngle[1] = -0x8000; - m->pos[0] = 0.0f; + m->pos[0] = 100.0f * m->playerIndex; m->pos[2] = 0.0f; mario_set_forward_vel(m, 0.0f); @@ -2011,6 +2016,8 @@ static s32 jumbo_star_cutscene_taking_off(struct MarioState *m) { } vec3f_set(m->pos, 0.0f, 307.0, marioObj->rawData.asF32[0x22]); + m->pos[0] += 100.0f * m->playerIndex; + update_mario_pos_for_anim(m); vec3f_copy(marioObj->header.gfx.pos, m->pos); vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); @@ -2033,15 +2040,19 @@ static s32 jumbo_star_cutscene_flying(struct MarioState *m) { switch (m->actionState) { case 0: set_mario_animation(m, MARIO_ANIM_WING_CAP_FLY); - anim_spline_init(sJumboStarKeyframes); + anim_spline_init(m, sJumboStarKeyframes); m->actionState++; // fall through case 1: - if (anim_spline_poll(targetPos)) { + if (anim_spline_poll(m, targetPos)) { // does this twice set_mario_action(m, ACT_FREEFALL, 0); m->actionState++; } else { + targetPos[0] += 100.0f * m->playerIndex; + float heightScalar = min(m->actionTimer / 30.0f, 1.0f); + targetPos[1] -= 100.0f * m->playerIndex * heightScalar; + targetDX = targetPos[0] - m->pos[0]; targetDY = targetPos[1] - m->pos[1]; targetDZ = targetPos[2] - m->pos[2]; @@ -2064,7 +2075,7 @@ static s32 jumbo_star_cutscene_flying(struct MarioState *m) { vec3f_copy(m->marioObj->header.gfx.pos, m->pos); m->particleFlags |= PARTICLE_SPARKLES; - if (m->actionTimer++ == 500) { + if (m->actionTimer++ == 500 && m->playerIndex == 0) { level_trigger_warp(m, WARP_OP_CREDITS_START); } diff --git a/src/pc/controller/controller_keyboard_debug.c b/src/pc/controller/controller_keyboard_debug.c index 94f7b351..3467533b 100644 --- a/src/pc/controller/controller_keyboard_debug.c +++ b/src/pc/controller/controller_keyboard_debug.c @@ -2,15 +2,23 @@ #include "game/level_update.h" #include "game/camera.h" #include "pc/network/network.h" +#include "game/mario.h" +#include "sm64.h" #ifdef DEBUG static u8 warpToLevel = LEVEL_CCM; #define SCANCODE_0 0x0B +#define SCANCODE_1 0x02 +#define SCANCODE_2 0x03 #define SCANCODE_3 0x04 +#define SCANCODE_4 0x05 +#define SCANCODE_5 0x06 #define SCANCODE_6 0x07 #define SCANCODE_7 0x08 +#define SCANCODE_8 0x09 +#define SCANCODE_9 0x0A static void debug_breakpoint_here(void) { // create easy breakpoint position for debugging @@ -92,6 +100,10 @@ static void debug_warp_area() { } } +static void debug_grand_star(void) { + set_mario_action(&gMarioStates[0], ACT_JUMBO_STAR_CUTSCENE, 0); +} + static void debug_suicide(void) { gMarioStates[0].hurtCounter = 31; } @@ -103,6 +115,7 @@ void debug_keyboard_on_key_down(int scancode) { #ifdef DEVELOPMENT case SCANCODE_6: debug_warp_level(warpToLevel); break; case SCANCODE_7: debug_warp_area(); break; + case SCANCODE_9: debug_grand_star(); break; case SCANCODE_0: debug_suicide(); break; #endif } diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index 955b7fe4..411fe45b 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -10,120 +10,221 @@ #define SET_BIT(val, num) ((((u8)(val)) & 0x01) << (num)); #define GET_BIT(val, num) (((val) >> (num)) & 0x01) +#pragma pack(1) +struct PacketPlayerData { + u32 rawData[80]; + struct Controller controller; + s16 nodeFlags; + + u16 input; + u32 flags; + u32 particleFlags; + u32 action; + u32 prevAction; + u16 actionState; + u16 actionTimer; + u32 actionArg; + f32 intendedMag; + s16 intendedYaw; + s16 invincTimer; + u8 framesSinceA; + u8 framesSinceB; + u8 wallKickTimer; + u8 doubleJumpTimer; + Vec3s faceAngle; + Vec3s angleVel; + s16 slideYaw; + s16 twirlYaw; + Vec3f pos; + Vec3f vel; + f32 forwardVel; + f32 slideVelX; + f32 slideVelZ; + s16 health; + u8 squishTimer; + f32 peakHeight; + s16 currentRoom; + + u8 customFlags; + u32 heldSyncID; + u32 heldBySyncID; +}; + +static void read_packet_data(struct PacketPlayerData* data, struct MarioState* m) { + u32 heldSyncID = (m->heldObj != NULL) ? m->heldObj->oSyncID : 0; + u32 heldBySyncID = (m->heldByObj != NULL) ? m->heldByObj->oSyncID : 0; + u8 customFlags = SET_BIT((m->freeze > 0), 0); + + memcpy(data->rawData, m->marioObj->rawData.asU32, sizeof(u32) * 80); + data->nodeFlags = m->marioObj->header.gfx.node.flags; + data->controller = *m->controller; + + data->input = m->input; + data->flags = m->flags; + data->particleFlags = m->particleFlags; + data->action = m->action; + data->prevAction = m->prevAction; + data->actionState = m->actionState; + data->actionTimer = m->actionTimer; + data->actionArg = m->actionArg; + data->intendedMag = m->intendedMag; + data->intendedYaw = m->intendedYaw; + data->invincTimer = m->invincTimer; + data->framesSinceA = m->framesSinceA; + data->framesSinceB = m->framesSinceB; + data->wallKickTimer = m->wallKickTimer; + data->doubleJumpTimer = m->doubleJumpTimer; + memcpy(data->faceAngle, m->faceAngle, sizeof(s16) * 3); + memcpy(data->angleVel, m->angleVel, sizeof(s16) * 3); + data->slideYaw = m->slideYaw; + data->twirlYaw = m->twirlYaw; + memcpy(data->pos, m->pos, sizeof(f32) * 3); + memcpy(data->vel, m->vel, sizeof(f32) * 3); + data->forwardVel = m->forwardVel; + data->slideVelX = m->slideVelX; + data->slideVelZ = m->slideVelZ; + data->health = m->health; + data->squishTimer = m->squishTimer; + data->peakHeight = m->peakHeight; + data->currentRoom = m->currentRoom; + + data->customFlags = customFlags; + data->heldSyncID = heldSyncID; + data->heldBySyncID = heldBySyncID; +} + +static void write_packet_data(struct PacketPlayerData* data, struct MarioState* m, + u8* customFlags, u32* heldSyncID, u32* heldBySyncID) { + memcpy(m->marioObj->rawData.asU32, data->rawData, sizeof(u32) * 80); + m->marioObj->header.gfx.node.flags = data->nodeFlags; + *m->controller = data->controller; + + m->input = data->input; + m->flags = data->flags; + m->particleFlags = data->particleFlags; + m->action = data->action; + m->prevAction = data->prevAction; + m->actionState = data->actionState; + m->actionTimer = data->actionTimer; + m->actionArg = data->actionArg; + m->intendedMag = data->intendedMag; + m->intendedYaw = data->intendedYaw; + m->invincTimer = data->invincTimer; + m->framesSinceA = data->framesSinceA; + m->framesSinceB = data->framesSinceB; + m->wallKickTimer = data->wallKickTimer; + m->doubleJumpTimer = data->doubleJumpTimer; + memcpy(m->faceAngle, data->faceAngle, sizeof(s16) * 3); + memcpy(m->angleVel, data->angleVel, sizeof(s16) * 3); + m->slideYaw = data->slideYaw; + m->twirlYaw = data->twirlYaw; + memcpy(m->pos, data->pos, sizeof(f32) * 3); + memcpy(m->vel, data->vel, sizeof(f32) * 3); + m->forwardVel = data->forwardVel; + m->slideVelX = data->slideVelX; + m->slideVelZ = data->slideVelZ; + m->health = data->health; + m->squishTimer = data->squishTimer; + m->peakHeight = data->peakHeight; + m->currentRoom = data->currentRoom; + + *customFlags = data->customFlags; + *heldSyncID = data->heldSyncID; + *heldBySyncID = data->heldBySyncID; +} + void network_send_player(void) { if (gMarioStates[0].marioObj == NULL) { return; } - u32 heldSyncID = (gMarioStates[0].heldObj != NULL) - ? gMarioStates[0].heldObj->oSyncID - : 0; - u32 heldBySyncID = (gMarioStates[0].heldByObj != NULL) - ? gMarioStates[0].heldByObj->oSyncID - : 0; - u8 customFlags = SET_BIT((gMarioStates[0].freeze > 0), 0); + struct PacketPlayerData data = { 0 }; + read_packet_data(&data, &gMarioStates[0]); struct Packet p; packet_init(&p, PACKET_PLAYER, false); - packet_write(&p, &gMarioStates[0], sizeof(u32) * 24); - packet_write(&p, gMarioStates[0].controller, 20); - packet_write(&p, gMarioStates[0].marioObj->rawData.asU32, sizeof(u32) * 80); - packet_write(&p, &gMarioStates[0].health, sizeof(s16)); - packet_write(&p, &gMarioStates[0].marioObj->header.gfx.node.flags, sizeof(s16)); - packet_write(&p, &gMarioStates[0].actionState, sizeof(u16)); - packet_write(&p, &gMarioStates[0].actionTimer, sizeof(u16)); - packet_write(&p, &gMarioStates[0].actionArg, sizeof(u32)); - packet_write(&p, &gMarioStates[0].currentRoom, sizeof(s16)); - packet_write(&p, &gMarioStates[0].squishTimer, sizeof(u8)); - packet_write(&p, &gMarioStates[0].peakHeight, sizeof(f32)); - packet_write(&p, &customFlags, sizeof(u8)); - packet_write(&p, &heldSyncID, sizeof(u32)); - packet_write(&p, &heldBySyncID, sizeof(u32)); + packet_write(&p, &data, sizeof(struct PacketPlayerData)); network_send(&p); } void network_receive_player(struct Packet* p) { - if (gMarioStates[1].marioObj == NULL) { return; } + struct MarioState* m = &gMarioStates[1]; + if (m == NULL || m->marioObj == NULL) { return; } // save previous state - u32 heldSyncID = 0; - u32 heldBySyncID = 0; - u16 playerIndex = gMarioStates[1].playerIndex; - u8 customFlags = 0; - u32 oldAction = gMarioStates[1].action; - u16 oldActionState = gMarioStates[1].actionState; - u16 oldActionArg = gMarioStates[1].actionArg; - u32 oldBehParams = gMarioStates[1].marioObj->oBehParams; + struct PacketPlayerData oldData = { 0 }; + read_packet_data(&oldData, m); + u16 playerIndex = m->playerIndex; + u32 oldBehParams = m->marioObj->oBehParams; // load mario information from packet - packet_read(p, &gMarioStates[1], sizeof(u32) * 24); - packet_read(p, gMarioStates[1].controller, 20); - packet_read(p, &gMarioStates[1].marioObj->rawData.asU32, sizeof(u32) * 80); - packet_read(p, &gMarioStates[1].health, sizeof(s16)); - packet_read(p, &gMarioStates[1].marioObj->header.gfx.node.flags, sizeof(s16)); - packet_read(p, &gMarioStates[1].actionState, sizeof(u16)); - packet_read(p, &gMarioStates[1].actionTimer, sizeof(u16)); - packet_read(p, &gMarioStates[1].actionArg, sizeof(u32)); - packet_read(p, &gMarioStates[1].currentRoom, sizeof(s16)); - packet_read(p, &gMarioStates[1].squishTimer, sizeof(u8)); - packet_read(p, &gMarioStates[1].peakHeight, sizeof(f32)); - packet_read(p, &customFlags, sizeof(u8)); - packet_read(p, &heldSyncID, sizeof(u32)); - packet_read(p, &heldBySyncID, sizeof(u32)); + struct PacketPlayerData data = { 0 }; + packet_read(p, &data, sizeof(struct PacketPlayerData)); + + // check to see if we should just drop this packet + if (oldData.action == ACT_JUMBO_STAR_CUTSCENE && data.action == ACT_JUMBO_STAR_CUTSCENE) { + return; + } + + // apply data from packet to mario state + u32 heldSyncID = 0; + u32 heldBySyncID = 0; + u8 customFlags = 0; + write_packet_data(&data, m, &customFlags, &heldSyncID, &heldBySyncID); // read custom flags - gMarioStates[1].freeze = GET_BIT(customFlags, 0); + m->freeze = GET_BIT(customFlags, 0); // reset player index - gMarioStates[1].playerIndex = playerIndex; - gMarioStates[1].marioObj->oBehParams = oldBehParams; + m->playerIndex = playerIndex; + m->marioObj->oBehParams = oldBehParams; // reset mario sound play flag so that their jump sounds work - if (gMarioStates[1].action != oldAction) { - gMarioStates[1].flags &= ~(MARIO_ACTION_SOUND_PLAYED | MARIO_MARIO_SOUND_PLAYED); + if (m->action != oldData.action) { + m->flags &= ~(MARIO_ACTION_SOUND_PLAYED | MARIO_MARIO_SOUND_PLAYED); } // find and set their held object if (heldSyncID != 0 && gSyncObjects[heldSyncID].o != NULL) { // TODO: do we have to move graphics nodes around to make this visible? struct Object* heldObj = gSyncObjects[heldSyncID].o; - if (gMarioStates[0].heldObj == heldObj && gNetworkType == NT_CLIENT) { // two-player hack: needs priority + if (m->heldObj == heldObj && gNetworkType == NT_CLIENT) { // two-player hack: needs priority mario_drop_held_object(&gMarioStates[0]); force_idle_state(&gMarioStates[0]); } - gMarioStates[1].heldObj = heldObj; + m->heldObj = heldObj; heldObj->oHeldState = HELD_HELD; heldObj->heldByPlayerIndex = 1; } else { - gMarioStates[1].heldObj = NULL; + m->heldObj = NULL; } // find and set their held-by object if (heldBySyncID != 0 && gSyncObjects[heldBySyncID].o != NULL) { // TODO: do we have to move graphics nodes around to make this visible? - gMarioStates[1].heldByObj = gSyncObjects[heldBySyncID].o; + m->heldByObj = gSyncObjects[heldBySyncID].o; } else { - gMarioStates[1].heldByObj = NULL; + m->heldByObj = NULL; } // jump kicking: restore action state, otherwise it won't play - if (gMarioStates[1].action == ACT_JUMP_KICK) { - gMarioStates[1].actionState = oldActionState; + if (m->action == ACT_JUMP_KICK) { + m->actionState = oldData.actionState; } // punching: - if ((gMarioStates[1].action == ACT_PUNCHING || gMarioStates[1].action == ACT_MOVE_PUNCHING)) { + if ((m->action == ACT_PUNCHING || m->action == ACT_MOVE_PUNCHING)) { // play first punching sound, otherwise it will be missed - if (gMarioStates[1].action != oldAction) { - play_sound(SOUND_MARIO_PUNCH_YAH, gMarioStates[1].marioObj->header.gfx.cameraToObject); + if (m->action != oldData.action) { + play_sound(SOUND_MARIO_PUNCH_YAH, m->marioObj->header.gfx.cameraToObject); } // make the first punch large, otherwise it will be missed - if (gMarioStates[1].actionArg == 2 && oldActionArg == 1) { - gMarioStates[1].marioBodyState->punchState = (0 << 6) | 4; + if (m->actionArg == 2 && oldData.actionArg == 1) { + m->marioBodyState->punchState = (0 << 6) | 4; } } // action changed, reset timer - if (gMarioStates[1].action != oldAction) { - gMarioStates[1].actionTimer = 0; + if (m->action != oldData.action) { + m->actionTimer = 0; } }