mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 05:25:14 +00:00
Synchronized water bombs
Added packet system to spawn objects on remote
This commit is contained in:
parent
8b24d364bb
commit
199bd07bb3
6 changed files with 159 additions and 16 deletions
|
@ -29,36 +29,65 @@ static struct ObjectHitbox sWaterBombHitbox = {
|
|||
* Spawn water bombs targeting mario when he comes in range.
|
||||
*/
|
||||
void bhv_water_bomb_spawner_update(void) {
|
||||
f32 latDistToMario;
|
||||
if (o->oSyncID == 0) {
|
||||
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
|
||||
network_init_object_field(o, &o->oWaterBombSpawnerBombActive);
|
||||
network_init_object_field(o, &o->oWaterBombSpawnerTimeToSpawn);
|
||||
}
|
||||
|
||||
f32 latDistToMario = 9999;
|
||||
f32 spawnerRadius;
|
||||
struct MarioState* marioState = NULL;
|
||||
struct Object* player = NULL;
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||
f32 latDist = lateral_dist_between_objects(o, gMarioStates[i].marioObj);
|
||||
if (latDist < latDistToMario) {
|
||||
latDistToMario = latDist;
|
||||
player = gMarioStates[i].marioObj;
|
||||
marioState = &gMarioStates[i];
|
||||
}
|
||||
}
|
||||
|
||||
spawnerRadius = 50 * (u16)(o->oBehParams >> 16) + 200.0f;
|
||||
latDistToMario = lateral_dist_between_objects(o, gMarioObject);
|
||||
|
||||
// When mario is in range and a water bomb isn't already active
|
||||
if (!o->oWaterBombSpawnerBombActive && latDistToMario < spawnerRadius
|
||||
&& gMarioObject->oPosY - o->oPosY < 1000.0f) {
|
||||
&& player->oPosY - o->oPosY < 1000.0f) {
|
||||
if (o->oWaterBombSpawnerTimeToSpawn != 0) {
|
||||
o->oWaterBombSpawnerTimeToSpawn -= 1;
|
||||
} else {
|
||||
struct Object *waterBomb =
|
||||
spawn_object_relative(0, 0, 2000, 0, o, MODEL_WATER_BOMB, bhvWaterBomb);
|
||||
} else if (network_owns_object(o)) {
|
||||
// this branch only runs for one player at a time
|
||||
|
||||
struct Object *waterBomb = spawn_object_relative(0, 0, 2000, 0, o, MODEL_WATER_BOMB, bhvWaterBomb);
|
||||
|
||||
if (waterBomb != NULL) {
|
||||
// Drop farther ahead of mario when he is moving faster
|
||||
f32 waterBombDistToMario = 28.0f * gMarioStates[0].forwardVel + 100.0f;
|
||||
f32 waterBombDistToMario = 28.0f * marioState->forwardVel + 100.0f;
|
||||
|
||||
waterBomb->oAction = WATER_BOMB_ACT_INIT;
|
||||
|
||||
waterBomb->oPosX =
|
||||
gMarioObject->oPosX + waterBombDistToMario * sins(gMarioObject->oMoveAngleYaw);
|
||||
waterBomb->oPosZ =
|
||||
gMarioObject->oPosZ + waterBombDistToMario * coss(gMarioObject->oMoveAngleYaw);
|
||||
waterBomb->oPosX = player->oPosX + waterBombDistToMario * sins(player->oMoveAngleYaw);
|
||||
waterBomb->oPosZ = player->oPosZ + waterBombDistToMario * coss(player->oMoveAngleYaw);
|
||||
|
||||
spawn_object(waterBomb, MODEL_WATER_BOMB_SHADOW, bhvWaterBombShadow);
|
||||
struct Object *waterBombShadow = spawn_object(waterBomb, MODEL_WATER_BOMB_SHADOW, bhvWaterBombShadow);
|
||||
|
||||
o->oWaterBombSpawnerBombActive = TRUE;
|
||||
o->oWaterBombSpawnerTimeToSpawn = random_linear_offset(0, 50);
|
||||
|
||||
// update the spawner
|
||||
network_send_object(o);
|
||||
|
||||
// send out the waterBomb objects
|
||||
struct Object* spawn_objects[] = {
|
||||
waterBomb,
|
||||
waterBombShadow
|
||||
};
|
||||
u32 models[] = {
|
||||
MODEL_WATER_BOMB,
|
||||
MODEL_WATER_BOMB_SHADOW
|
||||
};
|
||||
network_send_spawn_objects(spawn_objects, models, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +157,9 @@ static void water_bomb_act_drop(void) {
|
|||
set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);
|
||||
|
||||
// Move toward mario
|
||||
o->oMoveAngleYaw = o->oAngleToMario;
|
||||
struct Object* player = nearest_player_to_object(o);
|
||||
int angleToPlayer = obj_angle_to_object(o, player);
|
||||
o->oMoveAngleYaw = angleToPlayer;
|
||||
o->oForwardVel = 10.0f;
|
||||
o->oWaterBombStretchSpeed = -40.0f;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ void bhv_bubble_cannon_barrel_loop(void) {
|
|||
|
||||
void water_bomb_cannon_act_0(void) {
|
||||
#ifndef NODRAWINGDISTANCE
|
||||
if (o->oDistanceToMario < 2000.0f) {
|
||||
struct Object* player = nearest_player_to_object(o);
|
||||
int distanceToPlayer = dist_between_objects(o, player);
|
||||
if (distanceToPlayer < 2000.0f) {
|
||||
#endif
|
||||
spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles);
|
||||
cur_obj_unhide();
|
||||
|
@ -53,7 +55,9 @@ void water_bomb_cannon_act_0(void) {
|
|||
|
||||
void water_bomb_cannon_act_1(void) {
|
||||
#ifndef NODRAWINGDISTANCE
|
||||
if (o->oDistanceToMario > 2500.0f) {
|
||||
struct Object* player = nearest_player_to_object(o);
|
||||
int distanceToPlayer = dist_between_objects(o, player);
|
||||
if (distanceToPlayer > 2500.0f) {
|
||||
o->oAction = 2;
|
||||
} else if (o->oBehParams2ndByte == 0) {
|
||||
#else
|
||||
|
|
|
@ -108,6 +108,7 @@ void network_update(void) {
|
|||
case PACKET_ACK: network_receive_ack(&p); break;
|
||||
case PACKET_PLAYER: network_receive_player(&p); break;
|
||||
case PACKET_OBJECT: network_receive_object(&p); break;
|
||||
case PACKET_SPAWN_OBJECTS: network_receive_spawn_objects(&p); break;
|
||||
case PACKET_LEVEL_WARP: network_receive_level_warp(&p); break;
|
||||
case PACKET_INSIDE_PAINTING: network_receive_inside_painting(&p); break;
|
||||
case PACKET_COLLECT_STAR: network_receive_collect_star(&p); break;
|
||||
|
|
|
@ -18,6 +18,7 @@ enum PacketType {
|
|||
PACKET_ACK,
|
||||
PACKET_PLAYER,
|
||||
PACKET_OBJECT,
|
||||
PACKET_SPAWN_OBJECTS,
|
||||
PACKET_LEVEL_WARP,
|
||||
PACKET_INSIDE_PAINTING,
|
||||
PACKET_COLLECT_STAR,
|
||||
|
@ -75,8 +76,12 @@ void network_receive_player(struct Packet* p);
|
|||
|
||||
bool network_owns_object(struct Object* o);
|
||||
void network_update_objects(void);
|
||||
void network_send_object(struct Object* o);
|
||||
void network_receive_object(struct Packet* p);
|
||||
|
||||
void network_send_spawn_objects(struct Object* objects[], u32 models[], u8 objectCount);
|
||||
void network_receive_spawn_objects(struct Packet* p);
|
||||
|
||||
void network_update_level_warp(void);
|
||||
void network_receive_level_warp(struct Packet* p);
|
||||
|
||||
|
|
|
@ -117,5 +117,4 @@ SANITY_CHECK_COINS:;
|
|||
&& gMarioStates[0].numCoins >= 100) {
|
||||
bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
103
src/pc/network/packets/packet_spawn_objects.c
Normal file
103
src/pc/network/packets/packet_spawn_objects.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "object_fields.h"
|
||||
#include "object_constants.h"
|
||||
#include "behavior_data.h"
|
||||
|
||||
static u8 localSpawnId = 1;
|
||||
|
||||
// the remoteSpawnId stuff is only valid for 'luigi' aka the one remote player
|
||||
// will need to be extended if MAX_PLAYERS is ever increased
|
||||
#define MAX_REMOTE_SPAWN_IDS 16
|
||||
static u8 remoteSpawnIds[MAX_REMOTE_SPAWN_IDS] = { 0 };
|
||||
static u8 onRemoteSpawnId = 0;
|
||||
|
||||
#define MAX_SPAWN_OBJECTS_PER_PACKET 8
|
||||
|
||||
struct SpawnObjectData {
|
||||
u8 parentId;
|
||||
u32 model;
|
||||
void* behavior;
|
||||
s16 activeFlags;
|
||||
s32 rawData[80];
|
||||
};
|
||||
|
||||
static u8 generate_parent_id(struct Object* objects[], u8 onIndex) {
|
||||
struct Object* o = objects[onIndex];
|
||||
|
||||
if (onIndex == 0) {
|
||||
assert(o->parentObj->oSyncID != 0);
|
||||
return (u8)o->parentObj->oSyncID;
|
||||
}
|
||||
|
||||
for (u8 i = (onIndex - 1); i >= 0; i--) {
|
||||
if (o->parentObj == objects[i]) { return i; }
|
||||
}
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void network_send_spawn_objects(struct Object* objects[], u32 models[], u8 objectCount) {
|
||||
assert(objectCount < MAX_SPAWN_OBJECTS_PER_PACKET);
|
||||
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_SPAWN_OBJECTS, true);
|
||||
packet_write(&p, &localSpawnId, sizeof(u8));
|
||||
packet_write(&p, &objectCount, sizeof(u8));
|
||||
|
||||
for (u8 i = 0; i < objectCount; i++) {
|
||||
struct Object* o = objects[i];
|
||||
u32 model = models[i];
|
||||
u8 parentId = generate_parent_id(objects, i);
|
||||
packet_write(&p, &parentId, sizeof(u8));
|
||||
packet_write(&p, &model, sizeof(u32));
|
||||
packet_write(&p, &o->behavior, sizeof(void*));
|
||||
packet_write(&p, &o->activeFlags, sizeof(s16));
|
||||
packet_write(&p, o->rawData.asU32, sizeof(s32) * 80);
|
||||
assert(o->oSyncID == 0);
|
||||
}
|
||||
|
||||
network_send(&p);
|
||||
|
||||
localSpawnId++;
|
||||
if (localSpawnId == 0) { localSpawnId++; }
|
||||
}
|
||||
|
||||
void network_receive_spawn_objects(struct Packet* p) {
|
||||
u8 remoteSpawnId = 0;
|
||||
u8 objectCount = 0;
|
||||
|
||||
packet_read(p, &remoteSpawnId, sizeof(u8));
|
||||
packet_read(p, &objectCount, sizeof(u8));
|
||||
|
||||
// check if remote coin id has already been seen
|
||||
for (int i = 0; i < MAX_REMOTE_SPAWN_IDS; i++) {
|
||||
if (remoteSpawnIds[i] == remoteSpawnId) {
|
||||
// we already saw this event!
|
||||
return;
|
||||
}
|
||||
}
|
||||
// cache the seen id
|
||||
remoteSpawnIds[onRemoteSpawnId] = remoteSpawnId;
|
||||
onRemoteSpawnId = (onRemoteSpawnId + 1) % MAX_REMOTE_SPAWN_IDS;
|
||||
|
||||
struct Object* spawned[MAX_SPAWN_OBJECTS_PER_PACKET] = { 0 };
|
||||
for (u8 i = 0; i < objectCount; i++) {
|
||||
struct SpawnObjectData data = { 0 };
|
||||
packet_read(p, &data.parentId, sizeof(u8));
|
||||
packet_read(p, &data.model, sizeof(u32));
|
||||
packet_read(p, &data.behavior, sizeof(void*));
|
||||
packet_read(p, &data.activeFlags, sizeof(s16));
|
||||
packet_read(p, &data.rawData, sizeof(s32) * 80);
|
||||
|
||||
struct Object* parentObj = (i == 0)
|
||||
? syncObjects[data.parentId].o
|
||||
: spawned[data.parentId];
|
||||
if (parentObj == NULL) { continue; }
|
||||
|
||||
struct Object* o = spawn_object(parentObj, data.model, data.behavior);
|
||||
memcpy(o->rawData.asU32, data.rawData, sizeof(u32) * 80);
|
||||
|
||||
spawned[i] = o;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue