mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 05:25:14 +00:00
Synchronized coin collection
Reimplemented how randomness is synchronized
This commit is contained in:
parent
5ec9ab9ec0
commit
444c1fdd3b
16 changed files with 182 additions and 43 deletions
|
@ -3944,6 +3944,8 @@
|
|||
<ClCompile Include="..\src\pc\ini.c" />
|
||||
<ClCompile Include="..\src\pc\mixer.c" />
|
||||
<ClCompile Include="..\src\pc\network\network.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_collect_coin.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_collect_star.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_inside_painting.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_level_warp.c" />
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_object.c" />
|
||||
|
|
|
@ -14961,6 +14961,12 @@
|
|||
<ClCompile Include="..\src\pc\network\packets\packet_inside_painting.c">
|
||||
<Filter>Source Files\src\pc\network\packets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_collect_star.c">
|
||||
<Filter>Source Files\src\pc\network\packets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\pc\network\packets\packet_collect_coin.c">
|
||||
<Filter>Source Files\src\pc\network\packets</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\actors\common0.h">
|
||||
|
|
|
@ -376,5 +376,15 @@ struct MarioState
|
|||
// HOWEVER, simply increasing this to 3 will not magically work
|
||||
// many things will have to be overhauled!
|
||||
#define MAX_PLAYERS 2
|
||||
// still deciding to increase it?
|
||||
// networking will have to be rewritten to have more than one destination. 'reliable' messages would need to be sent per-player
|
||||
// things that base priority on whether they are the host or not would need priority based on player index instead
|
||||
// player 2's mario2.geo file will need a different one for player 3, 4, 5, etc... and will need values within it adjusted in a similar manner (diff them)
|
||||
// read all of the code surrounding a search through the entire codebase of the following:
|
||||
// gLuigiObject
|
||||
// gMarioObject
|
||||
// gMarioState[0]
|
||||
// gMarioState[1]
|
||||
// luigi
|
||||
|
||||
#endif // _SM64_TYPES_H_
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#define BHV_CMD_GET_ADDR_OF_CMD(index) (uintptr_t)(&gCurBhvCommand[index])
|
||||
|
||||
static u16 gRandomSeed16;
|
||||
static u16 gSyncRandom;
|
||||
|
||||
// Unused function that directly jumps to a behavior command and resets the object's stack index.
|
||||
static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
||||
|
@ -37,52 +36,34 @@ static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
|||
gCurrentObject->bhvStackIndex = 0;
|
||||
}
|
||||
|
||||
void random_sync_reset(void) {
|
||||
// seed the sync'd random seed with enough synchronzied information to be "unique enough"
|
||||
gSyncRandom = (u16)gCurrentObject->oPosX
|
||||
^ (u16)gCurrentObject->oPosY
|
||||
^ (u16)gCurrentObject->oPosZ
|
||||
^ (u16)gCurrentObject->oVelX
|
||||
^ (u16)gCurrentObject->oVelY
|
||||
^ (u16)gCurrentObject->oVelZ
|
||||
^ (u16)gCurrentObject->oAction;
|
||||
}
|
||||
|
||||
// Generate a pseudorandom integer from 0 to 65535 from the synchronized seed, and update the seed.
|
||||
u16 random_sync_u16(void) {
|
||||
u16 temp1, temp2;
|
||||
|
||||
if (gSyncRandom == 22026) {
|
||||
gSyncRandom = 0;
|
||||
void force_replicable_seed(u8 always) {
|
||||
// force the seed to consistent values
|
||||
extern u16 gRandomSeed16;
|
||||
extern u32 gGlobalTimer;
|
||||
static u32 lastTimer = 0;
|
||||
static f32 lastPos[3] = { 0 };
|
||||
if (gGlobalTimer == lastTimer
|
||||
&& lastPos[0] == gCurrentObject->oPosX / 10
|
||||
&& lastPos[1] == gCurrentObject->oPosY / 10
|
||||
&& lastPos[2] == gCurrentObject->oPosZ / 10
|
||||
&& !always) {
|
||||
return;
|
||||
}
|
||||
|
||||
temp1 = (gSyncRandom & 0x00FF) << 8;
|
||||
temp1 = temp1 ^ gSyncRandom;
|
||||
|
||||
gSyncRandom = ((temp1 & 0x00FF) << 8) + ((temp1 & 0xFF00) >> 8);
|
||||
|
||||
temp1 = ((temp1 & 0x00FF) << 1) ^ gSyncRandom;
|
||||
temp2 = (temp1 >> 1) ^ 0xFF80;
|
||||
|
||||
if ((temp1 & 1) == 0) {
|
||||
if (temp2 == 43605) {
|
||||
gSyncRandom = 0;
|
||||
}
|
||||
else {
|
||||
gSyncRandom = temp2 ^ 0x1FF4;
|
||||
}
|
||||
gRandomSeed16 = (u16)(gCurrentObject->oPosX / 1000.0f)
|
||||
^ (u16)(gCurrentObject->oPosY / 1000.0f)
|
||||
^ (u16)(gCurrentObject->oPosZ / 1000.0f);
|
||||
if (!always) {
|
||||
lastPos[0] = gCurrentObject->oPosX / 10;
|
||||
lastPos[1] = gCurrentObject->oPosY / 10;
|
||||
lastPos[2] = gCurrentObject->oPosZ / 10;
|
||||
lastTimer = gGlobalTimer;
|
||||
}
|
||||
else {
|
||||
gSyncRandom = temp2 ^ 0x8180;
|
||||
}
|
||||
|
||||
return gSyncRandom;
|
||||
}
|
||||
|
||||
// Generate a pseudorandom integer from 0 to 65535 from the random seed, and update the seed.
|
||||
u16 random_u16(void) {
|
||||
// override this function for synchronized entities
|
||||
if (gCurrentObject->oSyncID != 0) { return random_sync_u16(); }
|
||||
if (gCurrentObject->oSyncID != 0) { force_replicable_seed(FALSE); }
|
||||
|
||||
u16 temp1, temp2;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#define obj_and_int(object, offset, value) object->OBJECT_FIELD_S32(offset) &= (s32)(value)
|
||||
|
||||
void force_replicable_seed(u8 always);
|
||||
u16 random_u16(void);
|
||||
float random_float(void);
|
||||
s32 random_sign(void);
|
||||
|
|
|
@ -176,6 +176,7 @@ void bully_step(void) {
|
|||
}
|
||||
|
||||
void bully_spawn_coin(void) {
|
||||
force_replicable_seed(TRUE);
|
||||
struct Object *coin = spawn_object(o, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||
#ifdef VERSION_JP //TODO: maybe move this ifdef logic to the header?
|
||||
cur_obj_play_sound_2(SOUND_GENERAL_COIN_SPURT);
|
||||
|
|
|
@ -49,6 +49,7 @@ void bhv_temp_coin_loop(void) {
|
|||
}
|
||||
|
||||
void bhv_coin_init(void) {
|
||||
force_replicable_seed(FALSE);
|
||||
o->oVelY = random_float() * 10.0f + 30 + o->oCoinUnk110;
|
||||
o->oForwardVel = random_float() * 10.0f;
|
||||
o->oMoveAngleYaw = random_u16();
|
||||
|
|
|
@ -285,7 +285,6 @@ void huge_goomba_weakly_attacked(void) {
|
|||
*/
|
||||
void bhv_goomba_update(void) {
|
||||
// PARTIAL_UPDATE
|
||||
random_sync_reset();
|
||||
|
||||
f32 animSpeed;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ extern OSMesg D_80339CD4;
|
|||
extern struct VblankHandler gGameVblankHandler;
|
||||
extern uintptr_t gPhysicalFrameBuffers[3];
|
||||
extern uintptr_t gPhysicalZBuffer;
|
||||
extern void *D_80339CF0[2];
|
||||
extern void *D_80339CF0[MAX_PLAYERS];
|
||||
extern void *D_80339CF4;
|
||||
extern struct SPTask *gGfxSPTask;
|
||||
extern Gfx *gDisplayListHead;
|
||||
|
@ -51,7 +51,7 @@ extern struct DemoInput gRecordedDemoInput;
|
|||
|
||||
// this area is the demo input + the header. when the demo is loaded in, there is a header the size
|
||||
// of a single word next to the input list. this word is the current ID count.
|
||||
extern struct MarioAnimation D_80339D10[2];
|
||||
extern struct MarioAnimation D_80339D10[MAX_PLAYERS];
|
||||
extern struct MarioAnimation gDemo;
|
||||
|
||||
extern u8 gMarioAnims[];
|
||||
|
|
|
@ -748,6 +748,11 @@ void reset_mario_pitch(struct MarioState *m) {
|
|||
}
|
||||
|
||||
u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
|
||||
if (m != &gMarioStates[0]) {
|
||||
// only collect locally
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m->numCoins += o->oDamageOrCoinValue;
|
||||
m->healCounter += 4 * o->oDamageOrCoinValue;
|
||||
|
||||
|
@ -762,6 +767,8 @@ u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *
|
|||
queue_rumble_data(5, 80);
|
||||
}
|
||||
|
||||
network_send_collect_coin(o);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -672,6 +672,7 @@ s32 obj_find_wall_displacement(Vec3f dist, f32 x, f32 y, f32 z, f32 radius) {
|
|||
void obj_spawn_yellow_coins(struct Object *obj, s8 nCoins) {
|
||||
struct Object *coin;
|
||||
s8 count;
|
||||
force_replicable_seed(TRUE);
|
||||
|
||||
for (count = 0; count < nCoins; count++) {
|
||||
coin = spawn_object(obj, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||
|
|
|
@ -743,6 +743,7 @@ static s32 obj_handle_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioActi
|
|||
break;
|
||||
|
||||
case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN:
|
||||
force_replicable_seed(TRUE);
|
||||
o->oNumLootCoins = -1;
|
||||
obj_set_squished_action();
|
||||
break;
|
||||
|
|
|
@ -1634,6 +1634,7 @@ static void obj_spawn_loot_coins(struct Object *obj, s32 numCoins, f32 sp30,
|
|||
spawnHeight = obj->oPosY;
|
||||
}
|
||||
|
||||
force_replicable_seed(TRUE);
|
||||
for (i = 0; i < numCoins; i++) {
|
||||
if (obj->oNumLootCoins <= 0) {
|
||||
break;
|
||||
|
@ -1657,6 +1658,7 @@ void obj_spawn_loot_yellow_coins(struct Object *obj, s32 numCoins, f32 sp28) {
|
|||
}
|
||||
|
||||
void cur_obj_spawn_loot_coin_at_mario_pos(void) {
|
||||
force_replicable_seed(TRUE);
|
||||
struct Object *coin;
|
||||
if (o->oNumLootCoins <= 0) {
|
||||
return;
|
||||
|
@ -2940,6 +2942,7 @@ s32 cur_obj_check_interacted(void) {
|
|||
}
|
||||
|
||||
void cur_obj_spawn_loot_blue_coin(void) {
|
||||
force_replicable_seed(TRUE);
|
||||
if (o->oNumLootCoins >= 5) {
|
||||
spawn_object(o, MODEL_BLUE_COIN, bhvMrIBlueCoin);
|
||||
o->oNumLootCoins -= 5;
|
||||
|
|
|
@ -111,6 +111,7 @@ void network_update(void) {
|
|||
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;
|
||||
case PACKET_COLLECT_COIN: network_receive_collect_coin(&p); break;
|
||||
default: printf("%s received unknown packet: %d\n", NETWORKTYPESTR, p.buffer[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ enum PacketType {
|
|||
PACKET_LEVEL_WARP,
|
||||
PACKET_INSIDE_PAINTING,
|
||||
PACKET_COLLECT_STAR,
|
||||
PACKET_COLLECT_COIN,
|
||||
};
|
||||
|
||||
struct Packet {
|
||||
|
@ -84,4 +85,7 @@ void network_receive_inside_painting(struct Packet* p);
|
|||
|
||||
void network_send_collect_star(s16 coinScore, s16 starIndex);
|
||||
void network_receive_collect_star(struct Packet* p);
|
||||
|
||||
void network_send_collect_coin(struct Object* o);
|
||||
void network_receive_collect_coin(struct Packet* p);
|
||||
#endif
|
||||
|
|
121
src/pc/network/packets/packet_collect_coin.c
Normal file
121
src/pc/network/packets/packet_collect_coin.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "object_fields.h"
|
||||
#include "object_constants.h"
|
||||
#include "course_table.h"
|
||||
#include "src/game/interaction.h"
|
||||
#include "src/engine/math_util.h"
|
||||
|
||||
static u8 localCoinId = 1;
|
||||
|
||||
// the remoteCoinId 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_COIN_IDS 16
|
||||
static u8 remoteCoinIds[MAX_REMOTE_COIN_IDS] = { 0 };
|
||||
static u8 onRemoteCoinId = 0;
|
||||
|
||||
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_coin(const BehaviorScript *behavior, f32* pos, s32 coinValue, 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 && obj->oDamageOrCoinValue == coinValue) {
|
||||
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_coin(struct Object* o) {
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_COLLECT_COIN, true);
|
||||
|
||||
packet_write(&p, &localCoinId, sizeof(u8));
|
||||
packet_write(&p, &o->behavior, sizeof(void*));
|
||||
packet_write(&p, &o->oPosX, sizeof(f32) * 3);
|
||||
packet_write(&p, &gMarioStates[0].numCoins, sizeof(s16));
|
||||
packet_write(&p, &o->oDamageOrCoinValue, sizeof(s32));
|
||||
|
||||
network_send(&p);
|
||||
localCoinId++;
|
||||
}
|
||||
|
||||
void network_receive_collect_coin(struct Packet* p) {
|
||||
u8 remoteCoinId = 0;
|
||||
void* behavior = NULL;
|
||||
f32 pos[3] = { 0 };
|
||||
s16 numCoins = 0;
|
||||
s32 coinValue = 0;
|
||||
|
||||
packet_read(p, &remoteCoinId, sizeof(u8));
|
||||
packet_read(p, &behavior, sizeof(void*));
|
||||
packet_read(p, &pos, sizeof(f32) * 3);
|
||||
packet_read(p, &numCoins, sizeof(s16));
|
||||
packet_read(p, &coinValue, sizeof(s32));
|
||||
|
||||
// check if remote coin id has already been seen
|
||||
for (int i = 0; i < MAX_REMOTE_COIN_IDS; i++) {
|
||||
if (remoteCoinIds[i] == remoteCoinId) {
|
||||
// we already saw this coin!
|
||||
goto SANITY_CHECK_COINS;
|
||||
}
|
||||
}
|
||||
// cache the seen id
|
||||
remoteCoinIds[onRemoteCoinId] = remoteCoinId;
|
||||
onRemoteCoinId = (onRemoteCoinId + 1) % MAX_REMOTE_COIN_IDS;
|
||||
|
||||
// make sure it's valid
|
||||
if (behavior == NULL) { goto SANITY_CHECK_COINS; }
|
||||
|
||||
// find the coin
|
||||
struct Object* coin = find_nearest_coin(behavior, pos, coinValue, 1000);
|
||||
if (coin == NULL) { goto SANITY_CHECK_COINS; }
|
||||
|
||||
// destroy coin
|
||||
coin->oInteractStatus = INT_STATUS_INTERACTED;
|
||||
|
||||
// add to local mario's coin count
|
||||
gMarioStates[0].numCoins += coinValue;
|
||||
|
||||
// check for 100-coin star
|
||||
extern s16 gCurrCourseNum;
|
||||
if (COURSE_IS_MAIN_COURSE(gCurrCourseNum)
|
||||
&& gMarioStates[0].numCoins - coin->oDamageOrCoinValue < 100
|
||||
&& gMarioStates[0].numCoins >= 100) {
|
||||
bhv_spawn_star_no_level_exit(6);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
SANITY_CHECK_COINS:;
|
||||
// make sure we're at least at the same coin count
|
||||
s16 oldCoinCount = gMarioStates[0].numCoins;
|
||||
gMarioStates[0].numCoins = max(numCoins, gMarioStates[0].numCoins);
|
||||
|
||||
// check for 100-coin star
|
||||
if (COURSE_IS_MAIN_COURSE(gCurrCourseNum)
|
||||
&& oldCoinCount < 100
|
||||
&& gMarioStates[0].numCoins >= 100) {
|
||||
bhv_spawn_star_no_level_exit(6);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue