mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +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\ini.c" />
|
||||||
<ClCompile Include="..\src\pc\mixer.c" />
|
<ClCompile Include="..\src\pc\mixer.c" />
|
||||||
<ClCompile Include="..\src\pc\network\network.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_inside_painting.c" />
|
||||||
<ClCompile Include="..\src\pc\network\packets\packet_level_warp.c" />
|
<ClCompile Include="..\src\pc\network\packets\packet_level_warp.c" />
|
||||||
<ClCompile Include="..\src\pc\network\packets\packet_object.c" />
|
<ClCompile Include="..\src\pc\network\packets\packet_object.c" />
|
||||||
|
|
|
||||||
|
|
@ -14961,6 +14961,12 @@
|
||||||
<ClCompile Include="..\src\pc\network\packets\packet_inside_painting.c">
|
<ClCompile Include="..\src\pc\network\packets\packet_inside_painting.c">
|
||||||
<Filter>Source Files\src\pc\network\packets</Filter>
|
<Filter>Source Files\src\pc\network\packets</Filter>
|
||||||
</ClCompile>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\actors\common0.h">
|
<ClCompile Include="..\actors\common0.h">
|
||||||
|
|
|
||||||
|
|
@ -376,5 +376,15 @@ struct MarioState
|
||||||
// HOWEVER, simply increasing this to 3 will not magically work
|
// HOWEVER, simply increasing this to 3 will not magically work
|
||||||
// many things will have to be overhauled!
|
// many things will have to be overhauled!
|
||||||
#define MAX_PLAYERS 2
|
#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_
|
#endif // _SM64_TYPES_H_
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
#define BHV_CMD_GET_ADDR_OF_CMD(index) (uintptr_t)(&gCurBhvCommand[index])
|
#define BHV_CMD_GET_ADDR_OF_CMD(index) (uintptr_t)(&gCurBhvCommand[index])
|
||||||
|
|
||||||
static u16 gRandomSeed16;
|
static u16 gRandomSeed16;
|
||||||
static u16 gSyncRandom;
|
|
||||||
|
|
||||||
// Unused function that directly jumps to a behavior command and resets the object's stack index.
|
// Unused function that directly jumps to a behavior command and resets the object's stack index.
|
||||||
static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
||||||
|
|
@ -37,52 +36,34 @@ static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
|
||||||
gCurrentObject->bhvStackIndex = 0;
|
gCurrentObject->bhvStackIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void random_sync_reset(void) {
|
void force_replicable_seed(u8 always) {
|
||||||
// seed the sync'd random seed with enough synchronzied information to be "unique enough"
|
// force the seed to consistent values
|
||||||
gSyncRandom = (u16)gCurrentObject->oPosX
|
extern u16 gRandomSeed16;
|
||||||
^ (u16)gCurrentObject->oPosY
|
extern u32 gGlobalTimer;
|
||||||
^ (u16)gCurrentObject->oPosZ
|
static u32 lastTimer = 0;
|
||||||
^ (u16)gCurrentObject->oVelX
|
static f32 lastPos[3] = { 0 };
|
||||||
^ (u16)gCurrentObject->oVelY
|
if (gGlobalTimer == lastTimer
|
||||||
^ (u16)gCurrentObject->oVelZ
|
&& lastPos[0] == gCurrentObject->oPosX / 10
|
||||||
^ (u16)gCurrentObject->oAction;
|
&& lastPos[1] == gCurrentObject->oPosY / 10
|
||||||
}
|
&& lastPos[2] == gCurrentObject->oPosZ / 10
|
||||||
|
&& !always) {
|
||||||
// Generate a pseudorandom integer from 0 to 65535 from the synchronized seed, and update the seed.
|
return;
|
||||||
u16 random_sync_u16(void) {
|
|
||||||
u16 temp1, temp2;
|
|
||||||
|
|
||||||
if (gSyncRandom == 22026) {
|
|
||||||
gSyncRandom = 0;
|
|
||||||
}
|
}
|
||||||
|
gRandomSeed16 = (u16)(gCurrentObject->oPosX / 1000.0f)
|
||||||
temp1 = (gSyncRandom & 0x00FF) << 8;
|
^ (u16)(gCurrentObject->oPosY / 1000.0f)
|
||||||
temp1 = temp1 ^ gSyncRandom;
|
^ (u16)(gCurrentObject->oPosZ / 1000.0f);
|
||||||
|
if (!always) {
|
||||||
gSyncRandom = ((temp1 & 0x00FF) << 8) + ((temp1 & 0xFF00) >> 8);
|
lastPos[0] = gCurrentObject->oPosX / 10;
|
||||||
|
lastPos[1] = gCurrentObject->oPosY / 10;
|
||||||
temp1 = ((temp1 & 0x00FF) << 1) ^ gSyncRandom;
|
lastPos[2] = gCurrentObject->oPosZ / 10;
|
||||||
temp2 = (temp1 >> 1) ^ 0xFF80;
|
lastTimer = gGlobalTimer;
|
||||||
|
|
||||||
if ((temp1 & 1) == 0) {
|
|
||||||
if (temp2 == 43605) {
|
|
||||||
gSyncRandom = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gSyncRandom = temp2 ^ 0x1FF4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
gSyncRandom = temp2 ^ 0x8180;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gSyncRandom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a pseudorandom integer from 0 to 65535 from the random seed, and update the seed.
|
// Generate a pseudorandom integer from 0 to 65535 from the random seed, and update the seed.
|
||||||
u16 random_u16(void) {
|
u16 random_u16(void) {
|
||||||
// override this function for synchronized entities
|
// 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;
|
u16 temp1, temp2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#define obj_and_int(object, offset, value) object->OBJECT_FIELD_S32(offset) &= (s32)(value)
|
#define obj_and_int(object, offset, value) object->OBJECT_FIELD_S32(offset) &= (s32)(value)
|
||||||
|
|
||||||
|
void force_replicable_seed(u8 always);
|
||||||
u16 random_u16(void);
|
u16 random_u16(void);
|
||||||
float random_float(void);
|
float random_float(void);
|
||||||
s32 random_sign(void);
|
s32 random_sign(void);
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ void bully_step(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void bully_spawn_coin(void) {
|
void bully_spawn_coin(void) {
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
struct Object *coin = spawn_object(o, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
struct Object *coin = spawn_object(o, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||||
#ifdef VERSION_JP //TODO: maybe move this ifdef logic to the header?
|
#ifdef VERSION_JP //TODO: maybe move this ifdef logic to the header?
|
||||||
cur_obj_play_sound_2(SOUND_GENERAL_COIN_SPURT);
|
cur_obj_play_sound_2(SOUND_GENERAL_COIN_SPURT);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ void bhv_temp_coin_loop(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void bhv_coin_init(void) {
|
void bhv_coin_init(void) {
|
||||||
|
force_replicable_seed(FALSE);
|
||||||
o->oVelY = random_float() * 10.0f + 30 + o->oCoinUnk110;
|
o->oVelY = random_float() * 10.0f + 30 + o->oCoinUnk110;
|
||||||
o->oForwardVel = random_float() * 10.0f;
|
o->oForwardVel = random_float() * 10.0f;
|
||||||
o->oMoveAngleYaw = random_u16();
|
o->oMoveAngleYaw = random_u16();
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,6 @@ void huge_goomba_weakly_attacked(void) {
|
||||||
*/
|
*/
|
||||||
void bhv_goomba_update(void) {
|
void bhv_goomba_update(void) {
|
||||||
// PARTIAL_UPDATE
|
// PARTIAL_UPDATE
|
||||||
random_sync_reset();
|
|
||||||
|
|
||||||
f32 animSpeed;
|
f32 animSpeed;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ extern OSMesg D_80339CD4;
|
||||||
extern struct VblankHandler gGameVblankHandler;
|
extern struct VblankHandler gGameVblankHandler;
|
||||||
extern uintptr_t gPhysicalFrameBuffers[3];
|
extern uintptr_t gPhysicalFrameBuffers[3];
|
||||||
extern uintptr_t gPhysicalZBuffer;
|
extern uintptr_t gPhysicalZBuffer;
|
||||||
extern void *D_80339CF0[2];
|
extern void *D_80339CF0[MAX_PLAYERS];
|
||||||
extern void *D_80339CF4;
|
extern void *D_80339CF4;
|
||||||
extern struct SPTask *gGfxSPTask;
|
extern struct SPTask *gGfxSPTask;
|
||||||
extern Gfx *gDisplayListHead;
|
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
|
// 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.
|
// 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 struct MarioAnimation gDemo;
|
||||||
|
|
||||||
extern u8 gMarioAnims[];
|
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) {
|
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->numCoins += o->oDamageOrCoinValue;
|
||||||
m->healCounter += 4 * 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);
|
queue_rumble_data(5, 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network_send_collect_coin(o);
|
||||||
|
|
||||||
return FALSE;
|
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) {
|
void obj_spawn_yellow_coins(struct Object *obj, s8 nCoins) {
|
||||||
struct Object *coin;
|
struct Object *coin;
|
||||||
s8 count;
|
s8 count;
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
|
|
||||||
for (count = 0; count < nCoins; count++) {
|
for (count = 0; count < nCoins; count++) {
|
||||||
coin = spawn_object(obj, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
coin = spawn_object(obj, MODEL_YELLOW_COIN, bhvMovingYellowCoin);
|
||||||
|
|
|
||||||
|
|
@ -743,6 +743,7 @@ static s32 obj_handle_attacks(struct ObjectHitbox *hitbox, s32 attackedMarioActi
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN:
|
case ATTACK_HANDLER_SQUISHED_WITH_BLUE_COIN:
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
o->oNumLootCoins = -1;
|
o->oNumLootCoins = -1;
|
||||||
obj_set_squished_action();
|
obj_set_squished_action();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1634,6 +1634,7 @@ static void obj_spawn_loot_coins(struct Object *obj, s32 numCoins, f32 sp30,
|
||||||
spawnHeight = obj->oPosY;
|
spawnHeight = obj->oPosY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
for (i = 0; i < numCoins; i++) {
|
for (i = 0; i < numCoins; i++) {
|
||||||
if (obj->oNumLootCoins <= 0) {
|
if (obj->oNumLootCoins <= 0) {
|
||||||
break;
|
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) {
|
void cur_obj_spawn_loot_coin_at_mario_pos(void) {
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
struct Object *coin;
|
struct Object *coin;
|
||||||
if (o->oNumLootCoins <= 0) {
|
if (o->oNumLootCoins <= 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2940,6 +2942,7 @@ s32 cur_obj_check_interacted(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void cur_obj_spawn_loot_blue_coin(void) {
|
void cur_obj_spawn_loot_blue_coin(void) {
|
||||||
|
force_replicable_seed(TRUE);
|
||||||
if (o->oNumLootCoins >= 5) {
|
if (o->oNumLootCoins >= 5) {
|
||||||
spawn_object(o, MODEL_BLUE_COIN, bhvMrIBlueCoin);
|
spawn_object(o, MODEL_BLUE_COIN, bhvMrIBlueCoin);
|
||||||
o->oNumLootCoins -= 5;
|
o->oNumLootCoins -= 5;
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ void network_update(void) {
|
||||||
case PACKET_LEVEL_WARP: network_receive_level_warp(&p); break;
|
case PACKET_LEVEL_WARP: network_receive_level_warp(&p); break;
|
||||||
case PACKET_INSIDE_PAINTING: network_receive_inside_painting(&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_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]);
|
default: printf("%s received unknown packet: %d\n", NETWORKTYPESTR, p.buffer[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ enum PacketType {
|
||||||
PACKET_LEVEL_WARP,
|
PACKET_LEVEL_WARP,
|
||||||
PACKET_INSIDE_PAINTING,
|
PACKET_INSIDE_PAINTING,
|
||||||
PACKET_COLLECT_STAR,
|
PACKET_COLLECT_STAR,
|
||||||
|
PACKET_COLLECT_COIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Packet {
|
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_send_collect_star(s16 coinScore, s16 starIndex);
|
||||||
void network_receive_collect_star(struct Packet* p);
|
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
|
#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…
Add table
Reference in a new issue