From 199bd07bb341bcea47202a84673a770d1ee44335 Mon Sep 17 00:00:00 2001 From: MysterD Date: Fri, 7 Aug 2020 20:34:12 -0700 Subject: [PATCH] Synchronized water bombs Added packet system to spawn objects on remote --- src/game/behaviors/water_bomb.inc.c | 57 +++++++--- src/game/behaviors/water_bomb_cannon.inc.c | 8 +- src/pc/network/network.c | 1 + src/pc/network/network.h | 5 + src/pc/network/packets/packet_collect_coin.c | 1 - src/pc/network/packets/packet_spawn_objects.c | 103 ++++++++++++++++++ 6 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 src/pc/network/packets/packet_spawn_objects.c diff --git a/src/game/behaviors/water_bomb.inc.c b/src/game/behaviors/water_bomb.inc.c index 41333803..5254b9c7 100644 --- a/src/game/behaviors/water_bomb.inc.c +++ b/src/game/behaviors/water_bomb.inc.c @@ -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; } diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c index dc36b869..00690cf1 100644 --- a/src/game/behaviors/water_bomb_cannon.inc.c +++ b/src/game/behaviors/water_bomb_cannon.inc.c @@ -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 diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 99a7b5ef..5198926f 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -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; diff --git a/src/pc/network/network.h b/src/pc/network/network.h index b49ce109..2af8db79 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -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); diff --git a/src/pc/network/packets/packet_collect_coin.c b/src/pc/network/packets/packet_collect_coin.c index 25bab034..4c7dba38 100644 --- a/src/pc/network/packets/packet_collect_coin.c +++ b/src/pc/network/packets/packet_collect_coin.c @@ -117,5 +117,4 @@ SANITY_CHECK_COINS:; && gMarioStates[0].numCoins >= 100) { bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6); } - } diff --git a/src/pc/network/packets/packet_spawn_objects.c b/src/pc/network/packets/packet_spawn_objects.c new file mode 100644 index 00000000..a7ac9e4f --- /dev/null +++ b/src/pc/network/packets/packet_spawn_objects.c @@ -0,0 +1,103 @@ +#include +#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; + } +}