diff --git a/src/game/area.c b/src/game/area.c index c07974ca6..cb3d4c6ee 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -56,6 +56,8 @@ s16 gCurrLevelNum = LEVEL_MIN; u32 gSpawnedStarDefault[8] = { 0 }; u8 gSpawnedStarDefaultCount = 0; +u32 gSpawnedStarNLE[8] = { 0 }; +u8 gSpawnedStarNLECount = 0; u8 gSpawnedStarRedCoin = 0; u8 gSpawnedStarHidden = 0; diff --git a/src/game/area.h b/src/game/area.h index 2789bdc36..4e5aeefb7 100644 --- a/src/game/area.h +++ b/src/game/area.h @@ -140,6 +140,8 @@ extern s16 gCurrLevelNum; extern u32 gSpawnedStarDefault[]; extern u8 gSpawnedStarDefaultCount; +extern u32 gSpawnedStarNLE[]; +extern u8 gSpawnedStarNLECount; extern u8 gSpawnedStarRedCoin; extern u8 gSpawnedStarHidden; diff --git a/src/game/behavior_actions.c b/src/game/behavior_actions.c index 7e0f55203..8a622a106 100644 --- a/src/game/behavior_actions.c +++ b/src/game/behavior_actions.c @@ -44,6 +44,7 @@ #include "spawn_object.h" #include "spawn_sound.h" #include "thread6.h" +#include "area.h" #define o gCurrentObject diff --git a/src/game/behavior_actions.h b/src/game/behavior_actions.h index b05783272..3df83938c 100644 --- a/src/game/behavior_actions.h +++ b/src/game/behavior_actions.h @@ -2,7 +2,7 @@ #define BEHAVIOR_ACTIONS_H void spawn_mist_particles_variable(s32 count, s32 offsetY, f32 size); -void bhv_spawn_star_no_level_exit(struct Object*,u32); +void bhv_spawn_star_no_level_exit(struct Object* object, u32 params, u8 networkSendEvent); void bhv_star_door_loop_2(void); void spawn_triangle_break_particles(s16 numTris, s16 triModel, f32 triSize, s16 triAnimState); diff --git a/src/game/behaviors/mips.inc.c b/src/game/behaviors/mips.inc.c index c760ba442..7ed0af871 100644 --- a/src/game/behaviors/mips.inc.c +++ b/src/game/behaviors/mips.inc.c @@ -211,7 +211,7 @@ void bhv_mips_act_idle(void) { // Spawn a star if he was just picked up for the first time. if (o->oMipsStarStatus == MIPS_STAR_STATUS_SHOULD_SPAWN_STAR) { - bhv_spawn_star_no_level_exit(o, o->oBehParams2ndByte + 3); + bhv_spawn_star_no_level_exit(o, o->oBehParams2ndByte + 3, TRUE); o->oMipsStarStatus = MIPS_STAR_STATUS_ALREADY_SPAWNED_STAR; } } diff --git a/src/game/behaviors/sparkle_spawn_star.inc.c b/src/game/behaviors/sparkle_spawn_star.inc.c index b66572671..300e50cf8 100644 --- a/src/game/behaviors/sparkle_spawn_star.inc.c +++ b/src/game/behaviors/sparkle_spawn_star.inc.c @@ -140,9 +140,20 @@ void bhv_spawned_star_loop(void) { o->oInteractStatus = 0; } -void bhv_spawn_star_no_level_exit(struct Object* object, u32 sp20) { +void bhv_spawn_star_no_level_exit(struct Object* object, u32 sp20, u8 networkSendEvent) { + // de-duplication checking + for (int i = 0; i < gSpawnedStarNLECount; i++) { + if (gSpawnedStarNLE[i] == sp20) { return; } + } + if (gSpawnedStarNLECount < 8) { + gSpawnedStarNLE[gSpawnedStarNLECount++] = sp20; + } + struct Object *sp1C = spawn_object(object, MODEL_STAR, bhvSpawnedStarNoLevelExit); sp1C->oBehParams = sp20 << 24; sp1C->oInteractionSubtype = INT_SUBTYPE_NO_EXIT; obj_set_angle(sp1C, 0, 0, 0); + if (networkSendEvent) { + network_send_spawn_star_nle(object, sp20); + } } diff --git a/src/game/interaction.c b/src/game/interaction.c index b134c453b..768247023 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -803,7 +803,7 @@ u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object * if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - o->oDamageOrCoinValue < 100 && m->numCoins >= 100) { - bhv_spawn_star_no_level_exit(m->marioObj, 6); + bhv_spawn_star_no_level_exit(m->marioObj, 6, FALSE); } if (o->oDamageOrCoinValue >= 2) { diff --git a/src/game/level_update.c b/src/game/level_update.c index 54452b6e9..23808b75c 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -1312,8 +1312,10 @@ s32 init_level(void) { for (int i = 0; i < 8; i++) { gSpawnedStarDefault[i] = 0; + gSpawnedStarNLE[i] = 0; } gSpawnedStarDefaultCount = 0; + gSpawnedStarNLECount = 0; gSpawnedStarRedCoin = 0; gSpawnedStarHidden = 0; diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index 91f6b5f6c..3d67b2f94 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -131,15 +131,15 @@ static void toad_message_talking(void) { switch (gCurrentObject->oToadMessageDialogId) { case TOAD_STAR_1_DIALOG: gCurrentObject->oToadMessageDialogId = TOAD_STAR_1_DIALOG_AFTER; - bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 0); + bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 0, TRUE); break; case TOAD_STAR_2_DIALOG: gCurrentObject->oToadMessageDialogId = TOAD_STAR_2_DIALOG_AFTER; - bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 1); + bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 1, TRUE); break; case TOAD_STAR_3_DIALOG: gCurrentObject->oToadMessageDialogId = TOAD_STAR_3_DIALOG_AFTER; - bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 2); + bhv_spawn_star_no_level_exit(gMarioStates[0].marioObj, 2, TRUE); break; } } diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index effd03d63..4dc444252 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -22,6 +22,16 @@ u8 network_player_connected_count(void) { return count; } +struct NetworkPlayer* network_player_from_global_index(u8 globalIndex) { + for (int i = 1; i < MAX_PLAYERS; i++) { + if (!gNetworkPlayers[i].connected) { continue; } + if (gNetworkPlayers[i].globalIndex == globalIndex) { + return &gNetworkPlayers[i]; + } + } + return NULL; +} + void network_player_update(void) { float elapsed = (clock() - gLastNetworkSend) / (float)CLOCKS_PER_SEC; if (elapsed > NETWORK_PLAYER_TIMEOUT / 3.0f) { diff --git a/src/pc/network/network_player.h b/src/pc/network/network_player.h index 5842c657d..0eca6d601 100644 --- a/src/pc/network/network_player.h +++ b/src/pc/network/network_player.h @@ -33,6 +33,7 @@ extern struct NetworkPlayer* gNetworkPlayerServer; bool network_player_any_connected(void); u8 network_player_connected_count(void); +struct NetworkPlayer* network_player_from_global_index(u8 globalIndex); void network_player_update(void); u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex); u8 network_player_disconnected(u8 globalIndex); diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 6e634a915..1dc9b0b18 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -23,6 +23,7 @@ void packet_receive(struct Packet* p) { case PACKET_OBJECT: network_receive_object(p); break; case PACKET_SPAWN_OBJECTS: network_receive_spawn_objects(p); break; case PACKET_SPAWN_STAR: network_receive_spawn_star(p); break; + case PACKET_SPAWN_STAR_NLE: network_receive_spawn_star_nle(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/packets/packet.h b/src/pc/network/packets/packet.h index b04e4dce3..5d91805ad 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -15,6 +15,7 @@ enum PacketType { PACKET_OBJECT, PACKET_SPAWN_OBJECTS, PACKET_SPAWN_STAR, + PACKET_SPAWN_STAR_NLE, PACKET_LEVEL_WARP, PACKET_INSIDE_PAINTING, PACKET_COLLECT_STAR, @@ -93,6 +94,8 @@ void network_receive_spawn_objects(struct Packet* p); // packet_spawn_star.c void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams); void network_receive_spawn_star(struct Packet* p); +void network_send_spawn_star_nle(struct Object* o, u32 params); +void network_receive_spawn_star_nle(struct Packet* p); // packet_level_warp.c void network_send_level_warp_begin(void); diff --git a/src/pc/network/packets/packet_collect_coin.c b/src/pc/network/packets/packet_collect_coin.c index 49e11f2a5..bb4cb94f3 100644 --- a/src/pc/network/packets/packet_collect_coin.c +++ b/src/pc/network/packets/packet_collect_coin.c @@ -10,7 +10,7 @@ #include "src/game/object_helpers.h" // defined in sparkle_spawn_star.inc.c -void bhv_spawn_star_no_level_exit(struct Object* object, u32 sp20); +void bhv_spawn_star_no_level_exit(struct Object* object, u32 sp20, u8 networkSendEvent); static u8 localCoinId = 1; @@ -111,7 +111,7 @@ void network_receive_collect_coin(struct Packet* p) { if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && gMarioStates[0].numCoins - coin->oDamageOrCoinValue < 100 && gMarioStates[0].numCoins >= 100) { - bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6); + bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6, FALSE); } return; @@ -125,6 +125,6 @@ SANITY_CHECK_COINS:; if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && oldCoinCount < 100 && gMarioStates[0].numCoins >= 100) { - bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6); + bhv_spawn_star_no_level_exit(gMarioStates[1].marioObj, 6, FALSE); } } diff --git a/src/pc/network/packets/packet_spawn_star.c b/src/pc/network/packets/packet_spawn_star.c index 71697f677..c6586d997 100644 --- a/src/pc/network/packets/packet_spawn_star.c +++ b/src/pc/network/packets/packet_spawn_star.c @@ -1,10 +1,15 @@ #include #include "../network.h" #include "object_fields.h" +#include "behavior_data.h" #include "src/game/behavior_actions.h" +#include "pc/debuglog.h" extern struct Object* gCurrentObject; +static u8 txSeqId = 0; +static u8 rxLastSeqId = (u8)-1; + void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams) { struct Packet p; packet_init(&p, PACKET_SPAWN_STAR, true, true); @@ -38,7 +43,7 @@ void network_receive_spawn_star(struct Packet* p) { case 0: o = spawn_default_star(x, y, z); break; case 1: o = spawn_red_coin_cutscene_star(x, y, z); break; case 2: o = spawn_no_exit_star(x, y, z); break; - default: printf("UNKNOWN SPAWN STAR %d\n", starType); + default: LOG_ERROR("UNKNOWN SPAWN STAR %d", starType); } gCurrentObject->oBehParams = oldBehParams; @@ -47,3 +52,62 @@ void network_receive_spawn_star(struct Packet* p) { packet_read(p, &o->oHomeX, sizeof(u32) * 3); } } + +void network_send_spawn_star_nle(struct Object* o, u32 params) { + u8 globalIndex = UNKNOWN_GLOBAL_INDEX; + if (o->behavior == bhvMario) { + u8 localIndex = o->oBehParams - 1; + globalIndex = gNetworkPlayers[localIndex].globalIndex; + } + + struct Packet p; + packet_init(&p, PACKET_SPAWN_STAR_NLE, true, true); + packet_write(&p, &txSeqId, sizeof(u8)); + packet_write(&p, &globalIndex, sizeof(u8)); + packet_write(&p, &o->oSyncID, sizeof(u32)); + packet_write(&p, ¶ms, sizeof(u32)); + + network_send(&p); + txSeqId++; +} + +void network_receive_spawn_star_nle(struct Packet* p) { + u8 seqId = 0; + u8 globalIndex = UNKNOWN_GLOBAL_INDEX; + u32 syncId = 0; + u32 params = 0; + + packet_read(p, &seqId, sizeof(u8)); + packet_read(p, &globalIndex, sizeof(u8)); + packet_read(p, &syncId, sizeof(u32)); + packet_read(p, ¶ms, sizeof(u32)); + + // de-dup + if (seqId == rxLastSeqId) { + LOG_INFO("Already seen seqId %d", seqId); + return; + } + + // grab network player first + struct Object* object = NULL; + if (globalIndex != UNKNOWN_GLOBAL_INDEX) { + struct NetworkPlayer* np = network_player_from_global_index(globalIndex); + if (np != NULL) { + object = gMarioStates[np->localIndex].marioObj; + } + } + + // check for sync id + if (object == NULL) { + object = gSyncObjects[syncId].o; + } + + // sanity check object + if (object == NULL) { + LOG_ERROR("Could not find object to attach to. %d %d", globalIndex, syncId); + return; + } + + // spawn + bhv_spawn_star_no_level_exit(object, params, FALSE); +}