From 69be70c0b2b75c34d832f8d3d520a14c75928dcd Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 10 May 2025 00:15:13 -0500 Subject: [PATCH 1/5] MovePoint: Update headers to support zNPCSpawner implementations --- src/SB/Core/x/xMovePoint.h | 3 +-- src/SB/Game/zMovePoint.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/SB/Core/x/xMovePoint.h b/src/SB/Core/x/xMovePoint.h index 4e0af7c4a..55dcd9beb 100644 --- a/src/SB/Core/x/xMovePoint.h +++ b/src/SB/Core/x/xMovePoint.h @@ -43,8 +43,7 @@ struct xMovePoint : xBase }; xVec3* xMovePointGetPos(xMovePoint* m); -F32 xMovePointGetNext(const xMovePoint* m, const xMovePoint* prev, xMovePoint** next, - xVec3* hdng); +F32 xMovePointGetNext(const xMovePoint* m, const xMovePoint* prev, xMovePoint** next, xVec3* hdng); void xMovePointSplineDestroy(xMovePoint* m); void xMovePointSplineSetup(xMovePoint* m); void xMovePointSetup(xMovePoint* m, xScene* sc); diff --git a/src/SB/Game/zMovePoint.h b/src/SB/Game/zMovePoint.h index fb5832996..3b45bd91e 100644 --- a/src/SB/Game/zMovePoint.h +++ b/src/SB/Game/zMovePoint.h @@ -22,7 +22,7 @@ struct zMovePoint : xMovePoint return pos; } U32 NumNodes(); - U8 IsOn(); + S32 IsOn(); }; zMovePoint* zMovePoint_GetMemPool(S32 cnt); @@ -35,7 +35,7 @@ void zMovePointLoad(zMovePoint* ent, xSerial* s); void zMovePointReset(zMovePoint* m); S32 zMovePointEventCB(xBase* from, xBase* to, U32 toEvent, const F32* toParam, xBase* b3); F32 zMovePointGetNext(const zMovePoint* current, const zMovePoint* prev, zMovePoint** next, - xVec3* hdng); + xVec3* hdng); xVec3* zMovePointGetPos(const zMovePoint* m); F32 zMovePointGetDelay(const zMovePoint* m); F32 xMovePointGetDelay(const xMovePoint* m); From 88e94e2284a25717ec3915fd873c14f7cb29e06e Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 10 May 2025 00:15:50 -0500 Subject: [PATCH 2/5] zNPCSupport: Add functions used by zNPCSpawner to header file --- src/SB/Game/zNPCSupport.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SB/Game/zNPCSupport.h b/src/SB/Game/zNPCSupport.h index 96b6761b9..b69007b43 100644 --- a/src/SB/Game/zNPCSupport.h +++ b/src/SB/Game/zNPCSupport.h @@ -153,6 +153,8 @@ void NPCC_ang_toXZDir(F32 angle, xVec3* dir); F32 NPCC_aimVary(xVec3* dir_aim, xVec3* pos_src, xVec3* pos_tgt, F32 dst_vary, S32 flg_vary, xVec3* pos_aimPoint); void NPCC_aimMiss(xVec3*, xVec3*, xVec3*, float, xVec3*); +S32 NPCC_chk_hitPlyr(xBound* bnd, xCollis* collide); +S32 NPCC_pos_ofBase(xBase* tgt, xVec3* pos); F32 NPCC_ds2_toCam(const xVec3* pos_from, xVec3* delta); void zNPC_SNDStop(_tageNPCSnd snd); void zNPC_SNDPlay3D(_tageNPCSnd snd, xEnt*); From 1e485f6ba8fee1d8ed3c98cfd5ef856d6232ffc6 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 10 May 2025 00:16:12 -0500 Subject: [PATCH 3/5] xutil: Update xUtil_select to have static definition --- src/SB/Core/x/xutil.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SB/Core/x/xutil.h b/src/SB/Core/x/xutil.h index 2b6311124..313356b01 100644 --- a/src/SB/Core/x/xutil.h +++ b/src/SB/Core/x/xutil.h @@ -11,7 +11,6 @@ U32 xUtil_crc_update(U32 crc_accum, char* data, S32 datasize); S32 xUtil_yesno(F32 wt_yes); void xUtil_wtadjust(F32* wts, S32 cnt, F32 arbref); -template -T* xUtil_select(T** arg0, S32 arg1, const F32* arg3); +template static T* xUtil_select(T** arg0, S32 arg1, const F32* arg3); #endif From 6c3ce0660b1af939b5359727b51c84b68f877221 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 10 May 2025 00:16:34 -0500 Subject: [PATCH 4/5] zNPCSpawner: Add declarations for additional function impls --- src/SB/Game/zNPCSpawner.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/SB/Game/zNPCSpawner.h b/src/SB/Game/zNPCSpawner.h index d6e9033c6..6eb318af1 100644 --- a/src/SB/Game/zNPCSpawner.h +++ b/src/SB/Game/zNPCSpawner.h @@ -84,8 +84,8 @@ struct zNPCSpawner : RyzMemData void UpdateDiscreet(F32 dt); void UpdateContinuous(F32 dt); void Notify(en_SM_NOTICES note, void* data); - S32 Owned(zNPCCommon* npc); - U8 Receivable(en_SM_NOTICES note, void* data); + U8 Owned(zNPCCommon* npc) const; + U8 Receivable(en_SM_NOTICES note, void* data) const; SMSPStatus* SelectSP(const SMNPCStatus* npcstat); // NextPendingNPC. void ClearActive(); @@ -97,7 +97,9 @@ struct zNPCSpawner : RyzMemData void SetNPCStatus(zNPCCommon* npc, en_SM_NPC_STATUS status); SMNPCStatus* StatForNPC(zNPCCommon* npc); - SMSPStatus* StatForSP(zMovePoint* mp, S32 arg0); + SMSPStatus* StatForSP(zMovePoint* mp, S32 arg1); + S32 IsSPLZClear(zMovePoint* sp); + S32 IsNearbyMover(xBound* bnd, S32 usecyl, xCollis* caller_colrec); S32 SpawnBeastie(SMNPCStatus* npcstat, SMSPStatus* spstat); SMNPCStatus* ToastedBeastie(zNPCCommon* npc); void ChildHeartbeat(F32 dt); From 7dbdb948cb0ca547e6ca84ccfb52d46b8f3bb1cf Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 10 May 2025 00:17:01 -0500 Subject: [PATCH 5/5] zNPCSpawner: Implement and reorder functions to best match attempt --- src/SB/Game/zNPCSpawner.cpp | 465 ++++++++++++++++++++++++++++++++---- 1 file changed, 418 insertions(+), 47 deletions(-) diff --git a/src/SB/Game/zNPCSpawner.cpp b/src/SB/Game/zNPCSpawner.cpp index c7349973b..7ac848e40 100644 --- a/src/SB/Game/zNPCSpawner.cpp +++ b/src/SB/Game/zNPCSpawner.cpp @@ -3,8 +3,15 @@ #include #include "zEvent.h" +#include "xDraw.h" +#include "zNPCSupport.h" +#include "zGlobals.h" +#include "xMathInlines.h" +#include "zNPCTypeRobot.h" + +static SMDepot g_smdepot = {}; +static S32 g_drawSpawnBounds; -extern SMDepot g_smdepot; extern F32 _805_Spawner; // 5.0f void zNPCSpawner_Startup() @@ -414,30 +421,171 @@ void zNPCSpawner::UpdateContinuous(F32 dt) } } -void zNPCSpawner::SetNPCStatus(zNPCCommon* npc, en_SM_NPC_STATUS status) +void zNPCSpawner::Notify(en_SM_NOTICES note, void* data) { - SMNPCStatus* stat = this->StatForNPC(npc); - if (stat != NULL) + zNPCCommon* npcdata = (zNPCCommon*)data; + switch (note) { - stat->status = status; + case SM_NOTE_NPCDIED: + ToastedBeastie(npcdata); + SetNPCStatus(npcdata, SM_NPC_DEAD); + break; + case SM_NOTE_NPCSTANDBY: + SetNPCStatus(npcdata, SM_NPC_READY); + break; + case SM_NOTE_NPCALIVE: + SetNPCStatus(npcdata, SM_NPC_ACTIVE); + break; + case SM_NOTE_DUPPAUSE: + flg_spawner |= 0x4; + break; + case SM_NOTE_DUPRESUME: + if ((flg_spawner & 0x2) == 0) + { + flg_spawner &= ~0x4; + } + break; + case SM_NOTE_DUPSETDELAY: + tym_delay = *(F32*)npcdata; + tym_delay = tym_delay > -1.0f ? tym_delay : -1.0f; + + break; + case SM_NOTE_DUPDEAD: + if ((flg_spawner & 0x2) == 0) + { + ClearPending(); + flg_spawner |= 0x2; + if ((wavestat == SM_STAT_DONE) && (flg_spawner & 0x20) == 0) + { + zEntEvent(npc_owner, eEventDuploDuperIsDoner); + } + } + break; + case SM_NOTE_KILLKIDS: + ClearPending(); + flg_spawner |= 0x2; + flg_spawner |= 0x10; } } -SMNPCStatus* zNPCSpawner::ToastedBeastie(zNPCCommon* npc) +U8 zNPCSpawner::Owned(zNPCCommon* npc) const { - SMNPCStatus* ret = this->StatForNPC(npc); - XOrdRemove(&this->actvlist, ret, -1); - zEntEvent((xBase*)this->npc_owner, eEventDuploNPCKilled); - return ret; + S32 i; + for (i = 0; i < 16; i++) + { + if (npcpool[i].npc == npc) + { + return 1; + } + } + + return 0; } -void zNPCSpawner::ChildHeartbeat(F32 dt) +U8 zNPCSpawner::Receivable(en_SM_NOTICES note, void* data) const +{ + switch (note) + { + case SM_NOTE_NPCDIED: + case SM_NOTE_NPCSTANDBY: + case SM_NOTE_NPCALIVE: + return Owned((zNPCCommon*)data); + case SM_NOTE_DUPPAUSE: + case SM_NOTE_DUPRESUME: + case SM_NOTE_DUPSETDELAY: + case SM_NOTE_DUPDEAD: + return 1; + default: + return 0; + } +} + +SMSPStatus* zNPCSpawner::SelectSP(const SMNPCStatus* npcstat) +{ + SMSPStatus* sp_stat; + + if (npcstat->sp_prefer != NULL) + { + sp_stat = StatForSP(npcstat->sp_prefer, 1); + if (!sp_stat->sp->IsOn()) + { + return NULL; + } + + if (!IsSPLZClear(sp_stat->sp)) + { + return NULL; + } + + return sp_stat; + } + else + { + S32 rc = 0; + S32 cnt = 0; + + SMSPStatus* splist[16] = {}; + + for (S32 i = 0; i < 16; i++) + { + SMSPStatus* tmp_stat = &sppool[i]; + if (tmp_stat->sp != NULL && tmp_stat->sp->on && splist[i] == NULL && + IsSPLZClear(sppool[i].sp)) + { + splist[i] = tmp_stat; + cnt += 1; + } + + rc++; + } + + if (!cnt) + { + sp_stat = NULL; + } + else + { + sp_stat = xUtil_select(splist, rc, NULL); + } + } + + return sp_stat; +} + +SMNPCStatus* zNPCSpawner::NextPendingNPC(S32 arg0) { + S32 temp_r4; + const F32* temp_ptr = NULL; + + temp_r4 = this->pendlist.cnt; + if (temp_r4 < 1) + { + return NULL; + } + return xUtil_select((SMNPCStatus**)this->pendlist.list, temp_r4, temp_ptr); } -U8 zMovePoint::IsOn() +void zNPCSpawner::ClearActive() { - return this->on; + for (S32 i = 0; i < actvlist.cnt; i++) + { + if (pendlist.list[i] != NULL) + { + ((st_XORDEREDARRAY*)pendlist.list[i])->cnt = 1; + } + } + + XOrdReset(&actvlist); +} + +void zNPCSpawner::ClearPending() +{ + for (S32 i = 0; i < pendlist.cnt; i++) + { + ((st_XORDEREDARRAY*)pendlist.list[i])->cnt = 1; + } + + XOrdReset(&pendlist); } st_XORDEREDARRAY* zNPCSpawner::FillPending() @@ -469,38 +617,116 @@ st_XORDEREDARRAY* zNPCSpawner::ReFillPending() return &this->actvlist; } -// void zNPCSpawner::ClearActive() -// { -// s32 var_r6; -// s32 var_r7; -// void *temp_r5; - -// var_r7 = 0; -// var_r6 = 0; -// loop_4: -// if (var_r7 < (s32) this->cnt_cleanup) { -// temp_r5 = *(this->pendlist->list + var_r6); -// if (temp_r5 != NULL) { -// (u32) temp_r5[1] = 1; -// } -// var_r6 += 4; -// var_r7 += 1; -// goto loop_4; -// } -// XOrdReset__FP16st_XORDEREDARRAY(&this->unk1B0); -// } +S32 zNPCSpawner::IsSPLZClear(zMovePoint* sp) +{ + xVec3 pos_sp; + S32 rc; + xBound bnd; + xVec3 delt; + + memset(&bnd, FALSE, sizeof(xBound)); -SMNPCStatus* zNPCSpawner::NextPendingNPC(S32 arg0) + bnd.type = 0; + xVec3Copy(&pos_sp, zMovePointGetPos(sp)); + + bnd.type = 1; + bnd.sph.r = 3.5f; + xVec3Copy(&bnd.box.center, &pos_sp); + + xQuickCullForBound(&bnd.qcd, &bnd); + + if (g_drawSpawnBounds) + { + xDrawSetColor(g_CYAN); + xBoundDraw(&bnd); + } + + if (NPCC_chk_hitPlyr(&bnd, NULL)) + { + return 0; + } + + xVec3Sub(&delt, xEntGetPos(&globals.player.ent), &pos_sp); + + if (SQ(delt.x) + SQ(delt.z) < SQ(3.5f)) + { + return 0; + } + + return IsNearbyMover(&bnd, TRUE, NULL); +} + +S32 zNPCSpawner::IsNearbyMover(xBound* bnd, S32 usecyl, xCollis* caller_colrec) { - S32 temp_r4; - const F32* temp_ptr = NULL; + S32 hitthing = 0; + zNPCCommon* npc; + S32 i; + xCollis local_colrec; + xCollis* colrec = caller_colrec; + xVec3 delt = { 0.0f, 0.0f, 0.0f }; + + for (i = 10; i > 0; i--) + { + // ??? + } - temp_r4 = this->pendlist.cnt; - if (temp_r4 < 1) + if (caller_colrec == NULL) { - return NULL; + colrec = &local_colrec; } - return xUtil_select((SMNPCStatus**)this->pendlist.list, temp_r4, temp_ptr); + + for (i = 0; i < globals.sceneCur->num_npcs; i++) + { + npc = (zNPCCommon*)globals.sceneCur->npcs[i]; + if (npc->chkby & 0x8 && npc->SelfType() != 'NTD0' && (npc->SelfType() & ~0xFF) != 'NTT\0') + { + xBoundHitsBound(bnd, &npc->bound, colrec); + + if (!(colrec->flags & 0x1) && !usecyl) + { + NPCC_pos_ofBase(npc, &delt); + + xVec3SubFrom(&delt, &bnd->sph.center); + + if (SQ(delt.x) + SQ(delt.z) < SQ(bnd->cyl.r)) + { + hitthing++; + } + } + + if (hitthing) + { + break; + } + } + } + + return hitthing; +} + +void zNPCSpawner::SetNPCStatus(zNPCCommon* npc, en_SM_NPC_STATUS status) +{ + SMNPCStatus* stat = this->StatForNPC(npc); + if (stat != NULL) + { + stat->status = status; + } +} + +SMSPStatus* zNPCSpawner::StatForSP(zMovePoint* sp, S32 arg1) +{ + SMSPStatus* spstat = NULL; + + S32 i; + for (i = 0; i < 16; i++) + { + if (sppool[i].sp != NULL && sppool[i].sp == sp) + { + spstat = &sppool[i]; + } + } + + return spstat; } /* zNPCSpawner::StatForNPC (zNPCCommon *) */ @@ -509,13 +735,6 @@ SMNPCStatus* zNPCSpawner::StatForNPC(zNPCCommon* npc) s32 var_ctr; SMNPCStatus* var_r6; zNPCCommon* temp_r0; - zNPCCommon* temp_r0_2; - zNPCCommon* temp_r0_3; - zNPCCommon* temp_r0_4; - zNPCCommon* temp_r0_5; - zNPCCommon* temp_r0_6; - zNPCCommon* temp_r0_7; - zNPCCommon* temp_r0_8; var_r6 = NULL; var_ctr = 2; @@ -531,3 +750,155 @@ SMNPCStatus* zNPCSpawner::StatForNPC(zNPCCommon* npc) return var_r6; } + +S32 zNPCSpawner::SpawnBeastie(SMNPCStatus* npcstat, SMSPStatus* spstat) +{ + zNPCCommon* npc; + zMovePoint* sp; + xVec3 pos_sp = { 0, 0, 0 }; + zMovePoint* nav_dest = NULL; + + npc = npcstat->npc; + sp = spstat->sp; + zMovePointGetNext(sp, sp, &nav_dest, NULL); + + if (nav_dest == NULL) + { + nav_dest = sp; + } + + xVec3Copy(&pos_sp, sp->PosGet()); + + npcstat->status = SM_NPC_SPAWNED; + XOrdRemove(&pendlist, npcstat, -1); + XOrdAppend(&actvlist, npcstat); + + npc->Respawn(&pos_sp, nav_dest, sp); + + cnt_spawn++; + + zEntEvent(npc_owner, eEventDuploNPCBorn); + + return 1; +} + +SMNPCStatus* zNPCSpawner::ToastedBeastie(zNPCCommon* npc) +{ + SMNPCStatus* ret = this->StatForNPC(npc); + XOrdRemove(&this->actvlist, ret, -1); + zEntEvent((xBase*)this->npc_owner, eEventDuploNPCKilled); + return ret; +} + +void zNPCSpawner::ChildHeartbeat(F32 dt) +{ +} + +void zNPCSpawner::ChildCleanup(F32 dt) +{ + S32 i; + SMNPCStatus* npc_stat; + S32 cnt_know; + S32 cnt_dead; + + if (wavestat == SM_STAT_DONE || wavestat == SM_STAT_ABORT) + { + for (i = actvlist.cnt - 1; i >= 0; i--) + { + zNPCCommon* npc = *(zNPCCommon**)actvlist.list[i]; + npc->Damage(DMGTYP_INSTAKILL, NULL, NULL); + } + + if (actvlist.cnt == 0 && pendlist.cnt == 0) + { + cnt_know = 0; + cnt_dead = 0; + for (i = 0; i < 16; i++) + { + npc_stat = &npcpool[i]; + if (npc_stat->npc != NULL) + { + cnt_know = 1; + if (npc_stat->status == SM_NPC_READY) + { + cnt_dead = 1; + } + break; + } + } + + if (cnt_dead == cnt_know) + { + flg_spawner &= ~0x10; + } + } + + cnt_cleanup++; + } +} + +S32 zMovePoint::IsOn() +{ + // Cast is required by calling functions to occur in here + // even though a single byte is moved into the return register + return (S32)this->on; +} + +template T* xUtil_select(T** data, S32 size, const F32* arg2) +{ + T* selected = NULL; + S32 selectIdx = 0; + + F32 randOffset; + + if (data == NULL) + { + return NULL; + } + else if (size < 1) + { + return NULL; + } + + randOffset = xurand(); + if (arg2 == NULL) + { + selectIdx = (S32)(randOffset * (F32)size); + } + else + { + F32* roundingData = (F32*)arg2; + F32 threshold = 0.0f; + S32 counter = 0; + + // TODO: Fix float arithmetic and symbols + for (S32 i = size; i > 0; i--) + { + F32 tempValue = threshold; + threshold += *roundingData; + + if (randOffset >= threshold) + { + if (randOffset <= tempValue) + { + selectIdx = counter; + break; + } + } + roundingData++; + counter++; + } + } + + if (selectIdx >= size) + { + selectIdx = size - 1; + } + + if (selectIdx < 0) + { + selectIdx = 0; + } + + return data[selectIdx]; +}