Synchronized water bombs

Added packet system to spawn objects on remote
This commit is contained in:
MysterD 2020-08-07 20:34:12 -07:00
parent 8b24d364bb
commit 199bd07bb3
6 changed files with 159 additions and 16 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -117,5 +117,4 @@ SANITY_CHECK_COINS:;
&& gMarioStates[0].numCoins >= 100) {
bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6);
}
}

View 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;
}
}