diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index 29e88cc85..3392431b4 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -14967,6 +14967,9 @@ Source Files\src\pc\network\packets + + Source Files\src\pc\network\packets + diff --git a/src/game/area.c b/src/game/area.c index eb96a5c6b..5281c823a 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -53,6 +53,10 @@ u8 gWarpTransBlue = 0; s16 gCurrSaveFileNum = 1; s16 gCurrLevelNum = LEVEL_MIN; +u8 gSpawnedStarDefault = 0; +u8 gSpawnedStarRedCoin = 0; +u8 gSpawnedStarHidden = 0; + /* * The following two tables are used in get_mario_spawn_type() to determine spawn type * from warp behavior. diff --git a/src/game/area.h b/src/game/area.h index d746caa73..0c238b2b1 100644 --- a/src/game/area.h +++ b/src/game/area.h @@ -138,6 +138,9 @@ extern struct Area *gCurrentArea; extern s16 gCurrSaveFileNum; extern s16 gCurrLevelNum; +extern u8 gSpawnedStarDefault; +extern u8 gSpawnedStarRedCoin; +extern u8 gSpawnedStarHidden; void override_viewport_and_clip(Vp *a, Vp *b, u8 c, u8 d, u8 e); void print_intro_text(void); diff --git a/src/game/behavior_actions.h b/src/game/behavior_actions.h index e57974fb7..94d5743e6 100644 --- a/src/game/behavior_actions.h +++ b/src/game/behavior_actions.h @@ -576,4 +576,8 @@ Gfx *geo_scale_bowser_key(s32 run, struct GraphNode *node, UNUSED f32 mtx[4][4]) extern struct WaterDropletParams gShallowWaterSplashDropletParams; extern struct WaterDropletParams gShallowWaterWaveDropletParams; +struct Object* spawn_default_star(f32 x, f32 y, f32 z); +struct Object* spawn_red_coin_cutscene_star(f32 x, f32 y, f32 z); +struct Object* spawn_no_exit_star(f32 x, f32 y, f32 z); + #endif // BEHAVIOR_ACTIONS_H diff --git a/src/game/behaviors/spawn_star.inc.c b/src/game/behaviors/spawn_star.inc.c index 9a194a5ca..3127e101b 100644 --- a/src/game/behaviors/spawn_star.inc.c +++ b/src/game/behaviors/spawn_star.inc.c @@ -121,23 +121,35 @@ struct Object *spawn_star(struct Object *sp30, f32 sp34, f32 sp38, f32 sp3C) { return sp30; } -void spawn_default_star(f32 sp20, f32 sp24, f32 sp28) { - struct Object *sp1C; - sp1C = spawn_star(sp1C, sp20, sp24, sp28); - sp1C->oBehParams2ndByte = 0; +struct Object* spawn_default_star(f32 x, f32 y, f32 z) { + if (gSpawnedStarDefault) { return NULL; } + struct Object *star; + star = spawn_star(star, x, y, z); + star->oBehParams2ndByte = 0; + gSpawnedStarDefault = TRUE; + network_send_spawn_star(star, 0, x, y, z); + return star; } -void spawn_red_coin_cutscene_star(f32 sp20, f32 sp24, f32 sp28) { - struct Object *sp1C; - sp1C = spawn_star(sp1C, sp20, sp24, sp28); - sp1C->oBehParams2ndByte = 1; +struct Object* spawn_red_coin_cutscene_star(f32 x, f32 y, f32 z) { + if (gSpawnedStarRedCoin) { return NULL; } + struct Object * star; + star = spawn_star(star, x, y, z); + star->oBehParams2ndByte = 1; + gSpawnedStarRedCoin = TRUE; + network_send_spawn_star(star, 1, x, y, z); + return star; } -void spawn_no_exit_star(f32 sp20, f32 sp24, f32 sp28) { - struct Object *sp1C; - sp1C = spawn_star(sp1C, sp20, sp24, sp28); - sp1C->oBehParams2ndByte = 1; - sp1C->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; +struct Object* spawn_no_exit_star(f32 x, f32 y, f32 z) { + if (gSpawnedStarHidden) { return NULL; } + struct Object * star; + star = spawn_star(star, x, y, z); + star->oBehParams2ndByte = 1; + star->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; + gSpawnedStarHidden = TRUE; + network_send_spawn_star(star, 2, x, y, z); + return star; } void bhv_hidden_red_coin_star_init(void) { diff --git a/src/game/interaction.c b/src/game/interaction.c index df3698926..daa5d63e6 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -762,7 +762,7 @@ u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object * && m->numCoins >= 100) { bhv_spawn_star_no_level_exit(m->marioObj, 6); } - + if (o->oDamageOrCoinValue >= 2) { queue_rumble_data(5, 80); } @@ -779,6 +779,9 @@ u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Ob } u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { + // only allow for local player + if (m != &gMarioStates[0]) { return; } + u32 starIndex; u32 starGrabAction = ACT_STAR_DANCE_EXIT; u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0; @@ -822,7 +825,7 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O if (m == &gMarioStates[0]) { // sync the star collection - network_send_collect_star(m->numCoins, starIndex); + network_send_collect_star(o, m->numCoins, starIndex); } save_file_collect_star_or_key(m->numCoins, starIndex); @@ -1127,7 +1130,7 @@ u32 interact_tornado(struct MarioState *m, UNUSED u32 interactType, struct Objec play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); queue_rumble_data(30, 60); - + return set_mario_action(m, ACT_TORNADO_TWIRLING, m->action == ACT_TWIRLING); } @@ -1149,7 +1152,7 @@ u32 interact_whirlpool(struct MarioState *m, UNUSED u32 interactType, struct Obj play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); queue_rumble_data(30, 60); - + return set_mario_action(m, ACT_CAUGHT_IN_WHIRLPOOL, 0); } @@ -1184,7 +1187,7 @@ u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object if (!sInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & get_invincibility_flag(m))) { queue_rumble_data(5, 80); - + o->oInteractStatus = INT_STATUS_INTERACTED; m->interactObj = o; @@ -1284,7 +1287,7 @@ u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object push_mario_out_of_object(m, o, 5.0f); drop_and_set_mario_action(m, bully_knock_back_mario(m), 0); queue_rumble_data(5, 80); - + return TRUE; } diff --git a/src/game/level_update.c b/src/game/level_update.c index cd15fee68..b190033af 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -1190,6 +1190,10 @@ s32 init_level(void) { sTransitionTimer = 0; D_80339EE0 = 0; + gSpawnedStarDefault = 0; + gSpawnedStarRedCoin = 0; + gSpawnedStarHidden = 0; + if (gCurrCreditsEntry == NULL) { gHudDisplay.flags = HUD_DISPLAY_DEFAULT; } else { @@ -1237,7 +1241,7 @@ s32 init_level(void) { set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0); } } - + if (gCurrDemoInput == NULL) { cancel_rumble(); } diff --git a/src/game/obj_behaviors.h b/src/game/obj_behaviors.h index 5a2b11680..0fee830d2 100644 --- a/src/game/obj_behaviors.h +++ b/src/game/obj_behaviors.h @@ -160,6 +160,6 @@ void bhv_free_bowling_ball_roll_loop(void); /* likely unused */ void bhv_free_bowling_ball_loop(void); /* likely unused */ void bhv_rr_cruiser_wing_init(void); void bhv_rr_cruiser_wing_loop(void); -void spawn_default_star(f32 sp20, f32 sp24, f32 sp28); +struct Object* spawn_default_star(f32 sp20, f32 sp24, f32 sp28); #endif // OBJ_BEHAVIORS_H diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 5198926f0..b6abec54a 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -109,6 +109,7 @@ void network_update(void) { 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_SPAWN_STAR: network_receive_spawn_star(&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; @@ -131,4 +132,4 @@ void network_shutdown(void) { wprintf(L"%s closesocket failed with error %d\n", NETWORKTYPESTR, WSAGetLastError()); } WSACleanup(); -} \ No newline at end of file +} diff --git a/src/pc/network/network.h b/src/pc/network/network.h index bc02a0523..5c7cbbf21 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -19,6 +19,7 @@ enum PacketType { PACKET_PLAYER, PACKET_OBJECT, PACKET_SPAWN_OBJECTS, + PACKET_SPAWN_STAR, PACKET_LEVEL_WARP, PACKET_INSIDE_PAINTING, PACKET_COLLECT_STAR, @@ -83,13 +84,16 @@ 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_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z); +void network_receive_spawn_star(struct Packet* p); + void network_update_level_warp(void); void network_receive_level_warp(struct Packet* p); void network_update_inside_painting(void); void network_receive_inside_painting(struct Packet* p); -void network_send_collect_star(s16 coinScore, s16 starIndex); +void network_send_collect_star(struct Object* o, s16 coinScore, s16 starIndex); void network_receive_collect_star(struct Packet* p); void network_send_collect_coin(struct Object* o); diff --git a/src/pc/network/packets/packet_collect_star.c b/src/pc/network/packets/packet_collect_star.c index 84a9c6a5d..c467bb51d 100644 --- a/src/pc/network/packets/packet_collect_star.c +++ b/src/pc/network/packets/packet_collect_star.c @@ -1,16 +1,52 @@ #include #include "../network.h" #include "course_table.h" +#include "object_fields.h" +#include "object_constants.h" +#include "game/interaction.h" extern s16 gCurrSaveFileNum; extern s16 gCurrCourseNum; -void network_send_collect_star(s16 coinScore, s16 starIndex) { +static f32 dist_to_pos(struct Object* o, f32* pos) { + f32 x = (f32)o->oPosX - pos[0]; x *= x; + f32 y = (f32)o->oPosY - pos[1]; y *= y; + f32 z = (f32)o->oPosZ - pos[2]; z *= z; + return (f32)sqrt(x + y + z); +} + +static struct Object* find_nearest_star(const BehaviorScript* behavior, f32* pos, float minDist) { + uintptr_t* behaviorAddr = segmented_to_virtual(behavior); + struct Object* closestObj = NULL; + struct Object* obj; + struct ObjectNode* listHead; + + extern struct ObjectNode* gObjectLists; + listHead = &gObjectLists[get_object_list_from_behavior(behaviorAddr)]; + obj = (struct Object*) listHead->next; + + while (obj != (struct Object*) listHead) { + if (obj->behavior == behaviorAddr && obj->activeFlags != ACTIVE_FLAG_DEACTIVATED) { + f32 objDist = dist_to_pos(obj, pos); + if (objDist < minDist) { + closestObj = obj; + minDist = objDist; + } + } + obj = (struct Object*) obj->header.next; + } + + return closestObj; +} + +void network_send_collect_star(struct Object* o, s16 coinScore, s16 starIndex) { struct Packet p; packet_init(&p, PACKET_COLLECT_STAR, true); packet_write(&p, &gCurrSaveFileNum, sizeof(s16)); packet_write(&p, &gCurrCourseNum, sizeof(s16)); + packet_write(&p, &o->oPosX, sizeof(u32) * 3); + packet_write(&p, &o->behavior, sizeof(void*)); packet_write(&p, &coinScore, sizeof(s16)); packet_write(&p, &starIndex, sizeof(s16)); @@ -18,12 +54,16 @@ void network_send_collect_star(s16 coinScore, s16 starIndex) { } void network_receive_collect_star(struct Packet* p) { + u32 pos[3] = { 0 }; + void* behavior = NULL; s16 coinScore, starIndex; s16 lastSaveFileNum = gCurrSaveFileNum; s16 lastCourseNum = gCurrCourseNum; packet_read(p, &gCurrSaveFileNum, sizeof(s16)); packet_read(p, &gCurrCourseNum, sizeof(s16)); + packet_read(p, &pos, sizeof(u32) * 3); + packet_read(p, &behavior, sizeof(void*)); packet_read(p, &coinScore, sizeof(s16)); packet_read(p, &starIndex, sizeof(s16)); @@ -36,4 +76,9 @@ void network_receive_collect_star(struct Packet* p) { gCurrSaveFileNum = lastSaveFileNum; gCurrCourseNum = lastCourseNum; + + struct Object* star = find_nearest_star(behavior, pos, 500); + if (star != NULL) { + star->oInteractStatus = INT_STATUS_INTERACTED; + } } diff --git a/src/pc/network/packets/packet_spawn_star.c b/src/pc/network/packets/packet_spawn_star.c new file mode 100644 index 000000000..ac4dad593 --- /dev/null +++ b/src/pc/network/packets/packet_spawn_star.c @@ -0,0 +1,40 @@ +#include +#include "../network.h" +#include "object_fields.h" + +void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z) { + struct Packet p; + packet_init(&p, PACKET_SPAWN_STAR, true); + packet_write(&p, &starType, sizeof(u8)); + packet_write(&p, &x, sizeof(f32)); + packet_write(&p, &y, sizeof(f32)); + packet_write(&p, &z, sizeof(f32)); + + packet_write(&p, &o->oPosX, sizeof(u32) * 3); + packet_write(&p, &o->oHomeX, sizeof(u32) * 3); + + network_send(&p); +} + +void network_receive_spawn_star(struct Packet* p) { + u8 starType; + f32 x, y, z; + + packet_read(p, &starType, sizeof(u8)); + packet_read(p, &x, sizeof(f32)); + packet_read(p, &y, sizeof(f32)); + packet_read(p, &z, sizeof(f32)); + + struct Object* o = NULL; + switch (starType) { + 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); + } + + if (o != NULL) { + packet_read(p, &o->oPosX, sizeof(u32) * 3); + packet_read(p, &o->oHomeX, sizeof(u32) * 3); + } +}