mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
342 lines
12 KiB
C
342 lines
12 KiB
C
#include <stdio.h>
|
|
#include "../network.h"
|
|
#include "object_fields.h"
|
|
#include "object_constants.h"
|
|
#include "sm64.h"
|
|
#include "game/interaction.h"
|
|
#include "game/mario.h"
|
|
#include "game/area.h"
|
|
#include "audio/external.h"
|
|
#include "engine/surface_collision.h"
|
|
#include "game/object_list_processor.h"
|
|
#include "game/chat.h"
|
|
|
|
#pragma pack(1)
|
|
struct PacketPlayerData {
|
|
u32 rawData[80];
|
|
|
|
s16 cRawStickX;
|
|
s16 cRawStickY;
|
|
f32 cStickX;
|
|
f32 cStickY;
|
|
f32 cStickMag;
|
|
u16 cButtonDown;
|
|
u16 cButtonPressed;
|
|
s16 cExtStickX;
|
|
s16 cExtStickY;
|
|
|
|
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;
|
|
u8 heldSyncID;
|
|
u8 heldBySyncID;
|
|
u8 interactSyncID;
|
|
u8 usedSyncID;
|
|
u8 platformSyncID;
|
|
|
|
s16 currLevelNum;
|
|
s16 currAreaIndex;
|
|
};
|
|
|
|
static void read_packet_data(struct PacketPlayerData* data, struct MarioState* m) {
|
|
u8 heldSyncID = (m->heldObj != NULL) ? m->heldObj->oSyncID : 0;
|
|
u8 heldBySyncID = (m->heldByObj != NULL) ? m->heldByObj->oSyncID : 0;
|
|
u8 interactSyncID = (m->interactObj != NULL) ? m->interactObj->oSyncID : 0;
|
|
u8 usedSyncID = (m->usedObj != NULL) ? m->usedObj->oSyncID : 0;
|
|
u8 platformSyncID = (m->marioObj->platform != NULL) ? m->marioObj->platform->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->cRawStickX = m->controller->rawStickX;
|
|
data->cRawStickY = m->controller->rawStickY;
|
|
data->cStickX = m->controller->stickX;
|
|
data->cStickY = m->controller->stickY;
|
|
data->cStickMag = m->controller->stickMag;
|
|
data->cButtonDown = m->controller->buttonDown;
|
|
data->cButtonPressed = m->controller->buttonPressed;
|
|
data->cExtStickX = m->controller->extStickX;
|
|
data->cExtStickY = m->controller->extStickY;
|
|
|
|
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;
|
|
data->interactSyncID = interactSyncID;
|
|
data->usedSyncID = usedSyncID;
|
|
data->platformSyncID = platformSyncID;
|
|
|
|
data->currLevelNum = gCurrLevelNum;
|
|
data->currAreaIndex = gCurrAreaIndex;
|
|
}
|
|
|
|
static void write_packet_data(struct PacketPlayerData* data, struct MarioState* m,
|
|
u8* customFlags, u8* heldSyncID, u8* heldBySyncID,
|
|
u8* interactSyncID, u8* usedSyncID, u8* platformSyncID) {
|
|
memcpy(m->marioObj->rawData.asU32, data->rawData, sizeof(u32) * 80);
|
|
m->marioObj->header.gfx.node.flags = data->nodeFlags;
|
|
|
|
m->controller->rawStickX = data->cRawStickX;
|
|
m->controller->rawStickY = data->cRawStickY;
|
|
m->controller->stickX = data->cStickX;
|
|
m->controller->stickY = data->cStickY;
|
|
m->controller->stickMag = data->cStickMag;
|
|
m->controller->buttonDown = data->cButtonDown;
|
|
m->controller->buttonPressed = data->cButtonPressed;
|
|
m->controller->extStickX = data->cExtStickX;
|
|
m->controller->extStickY = data->cExtStickY;
|
|
|
|
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;
|
|
*interactSyncID = data->interactSyncID;
|
|
*usedSyncID = data->usedSyncID;
|
|
*platformSyncID = data->platformSyncID;
|
|
}
|
|
|
|
void network_send_player(u8 localIndex) {
|
|
if (gMarioStates[localIndex].marioObj == NULL) { return; }
|
|
|
|
struct PacketPlayerData data = { 0 };
|
|
read_packet_data(&data, &gMarioStates[localIndex]);
|
|
|
|
struct Packet p;
|
|
packet_init(&p, PACKET_PLAYER, false, false);
|
|
packet_write(&p, &gNetworkPlayers[localIndex].globalIndex, sizeof(u8));
|
|
packet_write(&p, &data, sizeof(struct PacketPlayerData));
|
|
// two-player hack: should be network_send_to_all_except()
|
|
network_send(&p);
|
|
}
|
|
|
|
void network_receive_player(struct Packet* p) {
|
|
u8 globalIndex = 0;
|
|
packet_read(p, &globalIndex, sizeof(u8));
|
|
struct NetworkPlayer* np = network_player_from_global_index(globalIndex);
|
|
if (np == NULL || np->localIndex == UNKNOWN_LOCAL_INDEX || !np->connected) { return; }
|
|
|
|
struct MarioState* m = &gMarioStates[np->localIndex];
|
|
if (m == NULL || m->marioObj == NULL) { return; }
|
|
|
|
// save previous state
|
|
struct PacketPlayerData oldData = { 0 };
|
|
read_packet_data(&oldData, m);
|
|
u16 playerIndex = np->localIndex;
|
|
u32 oldBehParams = m->marioObj->oBehParams;
|
|
|
|
// load mario information from packet
|
|
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;
|
|
}
|
|
|
|
// check player level/area
|
|
u8 levelAreaMismatch = TRUE;
|
|
np->currLevelNum = data.currLevelNum;
|
|
np->currAreaIndex = data.currAreaIndex;
|
|
levelAreaMismatch = (data.currLevelNum != gCurrLevelNum || data.currAreaIndex != gCurrAreaIndex);
|
|
if (levelAreaMismatch) { np->fadeOpacity = 0; return; }
|
|
|
|
// apply data from packet to mario state
|
|
u8 heldSyncID = 0;
|
|
u8 heldBySyncID = 0;
|
|
u8 interactSyncID = 0;
|
|
u8 usedSyncID = 0;
|
|
u8 platformSyncID = 0;
|
|
u8 customFlags = 0;
|
|
write_packet_data(&data, m, &customFlags,
|
|
&heldSyncID, &heldBySyncID,
|
|
&interactSyncID, &usedSyncID,
|
|
&platformSyncID);
|
|
|
|
// read custom flags
|
|
m->freeze = GET_BIT(customFlags, 0);
|
|
|
|
// reset player index
|
|
m->playerIndex = playerIndex;
|
|
m->marioObj->oBehParams = oldBehParams;
|
|
|
|
// reset mario sound play flag so that their jump sounds work
|
|
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
|
|
mario_drop_held_object(&gMarioStates[0]);
|
|
force_idle_state(&gMarioStates[0]);
|
|
}
|
|
m->heldObj = heldObj;
|
|
heldObj->oHeldState = HELD_HELD;
|
|
heldObj->heldByPlayerIndex = 1;
|
|
} else {
|
|
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?
|
|
m->heldByObj = gSyncObjects[heldBySyncID].o;
|
|
} else {
|
|
m->heldByObj = NULL;
|
|
}
|
|
|
|
// find and set their interact object
|
|
if (interactSyncID != 0 && gSyncObjects[interactSyncID].o != NULL) {
|
|
m->interactObj = gSyncObjects[interactSyncID].o;
|
|
}
|
|
|
|
// find and set their used object
|
|
if (usedSyncID != 0 && gSyncObjects[usedSyncID].o != NULL) {
|
|
m->usedObj = gSyncObjects[usedSyncID].o;
|
|
}
|
|
|
|
// place on top of platform
|
|
if (platformSyncID != 0 && gSyncObjects[platformSyncID].o != NULL) {
|
|
struct Surface* floor = NULL;
|
|
// search up to 500 units for the platform
|
|
f32 maxDifference = 500;
|
|
m->pos[1] += maxDifference;
|
|
|
|
// find the platform
|
|
gCheckingSurfaceCollisionsForObject = gSyncObjects[platformSyncID].o;
|
|
f32 height = find_floor(m->pos[0], m->pos[1], m->pos[2], &floor);
|
|
gCheckingSurfaceCollisionsForObject = NULL;
|
|
|
|
f32 difference = ABS((m->pos[1] - maxDifference) - height);
|
|
if (floor != NULL && difference <= maxDifference) {
|
|
// place on top of platform
|
|
m->pos[1] = height;
|
|
} else {
|
|
// search failed, reset position
|
|
m->pos[1] -= maxDifference;
|
|
}
|
|
}
|
|
|
|
// jump kicking: restore action state, otherwise it won't play
|
|
if (m->action == ACT_JUMP_KICK) {
|
|
m->actionState = oldData.actionState;
|
|
}
|
|
|
|
// punching:
|
|
if ((m->action == ACT_PUNCHING || m->action == ACT_MOVE_PUNCHING)) {
|
|
// play first punching sound, otherwise it will be missed
|
|
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 (m->actionArg == 2 && oldData.actionArg == 1) {
|
|
m->marioBodyState->punchState = (0 << 6) | 4;
|
|
}
|
|
}
|
|
|
|
// inform of player death
|
|
if (oldData.action != ACT_BUBBLED && data.action == ACT_BUBBLED) {
|
|
chat_add_message("player died", CMT_SYSTEM);
|
|
}
|
|
|
|
// action changed, reset timer
|
|
if (m->action != oldData.action) {
|
|
m->actionTimer = 0;
|
|
}
|
|
|
|
// set model
|
|
m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[(np->globalIndex == 1) ? MODEL_LUIGI : MODEL_MARIO];
|
|
}
|
|
|
|
void network_update_player(void) {
|
|
network_send_player(0);
|
|
}
|