From 05655b74985db8c749bf72cf65d3edeacfa8b897 Mon Sep 17 00:00:00 2001 From: Colin Miller Date: Sun, 13 Jul 2025 23:23:56 -0400 Subject: [PATCH 1/3] ported zNPCSupport code, zNPCTypeCommon --- src/SB/Core/gc/iCollide.h | 3 +- src/SB/Core/x/xCollide.h | 14 + src/SB/Core/x/xDraw.h | 3 + src/SB/Core/x/xMath.h | 2 + src/SB/Core/x/xSpline.h | 7 +- src/SB/Game/zMovePoint.h | 5 + src/SB/Game/zNPCFXCinematic.cpp | 19 +- src/SB/Game/zNPCFXCinematic.h | 11 +- src/SB/Game/zNPCMessenger.h | 6 +- src/SB/Game/zNPCSupport.cpp | 814 ++++++++- src/SB/Game/zNPCSupport.h | 9 +- src/SB/Game/zNPCTypeCommon.cpp | 2787 ++++++++++++++++++++++++++++++- src/SB/Game/zNPCTypeCommon.h | 57 +- 13 files changed, 3568 insertions(+), 169 deletions(-) diff --git a/src/SB/Core/gc/iCollide.h b/src/SB/Core/gc/iCollide.h index 41f9af95b..bff1746a9 100644 --- a/src/SB/Core/gc/iCollide.h +++ b/src/SB/Core/gc/iCollide.h @@ -10,8 +10,9 @@ void iBoxForModelLocal(xBox* o, const xModelInstance* m); void iBoxForModel(xBox* o, const xModelInstance* m); S32 iSphereHitsEnv3(const xSphere* b, const xEnv* env, xCollis* colls, U8 ncolls, F32 sth); S32 iSphereHitsModel3(const xSphere* b, const xModelInstance* m, xCollis* colls, U8 ncolls, - F32 sth); + F32 sth); U32 iRayHitsModel(const xRay3* r, const xModelInstance* m, xCollis* coll); +void iSphereForModel(xSphere* o, const xModelInstance* m); void iCollideInit(xScene* sc); #endif diff --git a/src/SB/Core/x/xCollide.h b/src/SB/Core/x/xCollide.h index 331393247..17655cd6c 100644 --- a/src/SB/Core/x/xCollide.h +++ b/src/SB/Core/x/xCollide.h @@ -6,6 +6,20 @@ #include "xQuickCull.h" #include "iMath3.h" +#define k_HIT_IT ((U32)(1 << 0)) +#define k_HIT_0x2 ((U32)(1 << 1)) +#define k_HIT_0x4 ((U32)(1 << 2)) +#define k_HIT_0x8 ((U32)(1 << 3)) +#define k_HIT_0x10 ((U32)(1 << 4)) +#define k_HIT_0x100 ((U32)(1 << 8)) +#define k_HIT_0x200 ((U32)(1 << 9)) +#define k_HIT_0x400 ((U32)(1 << 10)) +#define k_HIT_0x800 ((U32)(1 << 11)) +#define k_HIT_0xF00 (k_HIT_0x100 | k_HIT_0x200 | k_HIT_0x400 | k_HIT_0x800) +#define k_HIT_CALC_HDNG ((U32)(1 << 12)) +#define k_HIT_CALC_TRI ((U32)(1 << 13)) +#define k_HIT_0x20000 ((U32)(1 << 17)) + struct xModelInstance; struct xCollis diff --git a/src/SB/Core/x/xDraw.h b/src/SB/Core/x/xDraw.h index 6e91c87c5..f3c46b9b3 100644 --- a/src/SB/Core/x/xDraw.h +++ b/src/SB/Core/x/xDraw.h @@ -5,6 +5,9 @@ #include "xMath3.h" void xDrawSetColor(iColor_tag); +inline void xDrawLine(const xVec3* a, const xVec3* b) +{ +} void xDrawSphere2(const xSphere*, U32); void xDrawOBB(const xBox*, const xMat4x3*); void xDrawBox(const xBox*); diff --git a/src/SB/Core/x/xMath.h b/src/SB/Core/x/xMath.h index 8f1ca7f21..669c5de79 100644 --- a/src/SB/Core/x/xMath.h +++ b/src/SB/Core/x/xMath.h @@ -9,6 +9,8 @@ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define xabs(x) iabs(x) +#define xeq(a, b, e) (xabs((a) - (b)) <= (e)) + #define CLAMP(x, a, b) (MAX((a), MIN((x), (b)))) #define SQR(x) ((x) * (x)) diff --git a/src/SB/Core/x/xSpline.h b/src/SB/Core/x/xSpline.h index 50a103e4a..3bf649f8d 100644 --- a/src/SB/Core/x/xSpline.h +++ b/src/SB/Core/x/xSpline.h @@ -32,6 +32,11 @@ struct xSpline3 }; void xSpline3_ArcInit(xSpline3* spl, U32 sample); -xSpline3* xSpline3_Bezier(xVec3* points, F32* time, U32 numpoints, U32 numalloc, xVec3* p1, xVec3* p2); +xSpline3* xSpline3_Bezier(xVec3* points, F32* time, U32 numpoints, U32 numalloc, xVec3* p1, + xVec3* p2); +inline F32 xSpline3_ArcTotal(xSpline3* spl) +{ + return spl->arcLength[spl->N * spl->arcSample - 1]; +} #endif diff --git a/src/SB/Game/zMovePoint.h b/src/SB/Game/zMovePoint.h index 3b45bd91e..5cd6b372e 100644 --- a/src/SB/Game/zMovePoint.h +++ b/src/SB/Game/zMovePoint.h @@ -23,6 +23,11 @@ struct zMovePoint : xMovePoint } U32 NumNodes(); S32 IsOn(); + + S32 HasSpline() + { + return spl != NULL; + } }; zMovePoint* zMovePoint_GetMemPool(S32 cnt); diff --git a/src/SB/Game/zNPCFXCinematic.cpp b/src/SB/Game/zNPCFXCinematic.cpp index 55d859e50..a5431480f 100644 --- a/src/SB/Game/zNPCFXCinematic.cpp +++ b/src/SB/Game/zNPCFXCinematic.cpp @@ -438,6 +438,14 @@ void NCIN_Par_CIN_PLATFORM_JETS_Upd(const zCutsceneMgr*, NCINEntry* fxrec, S32 p fxrec->pos_B[0] = 5.0f; } +void NCINBeNosey::CanRenderNow() +{ + zCutsceneMgr* csnmgr; + NCINEntry* fxtab; + NCINEntry* nextrec; + NCINEntry* fxrec; +} + void NCIN_Generic_Upd(const zCutsceneMgr*, NCINEntry* fxrec, S32 param) { if (param != 0) @@ -473,7 +481,8 @@ void NCIN_BubSlam(const zCutsceneMgr*, NCINEntry* fxrec, S32 param) } } -void NCIN_BubTrailBone_AR(const zCutsceneMgr*, NCINEntry* fxrec, RpAtomic*, RwMatrixTag*, U32 num_1, U32 num_2) +void NCIN_BubTrailBone_AR(const zCutsceneMgr*, NCINEntry* fxrec, RpAtomic*, RwMatrixTag*, U32 num_1, + U32 num_2) { S32 ifx = fxrec->pos_A[1].x; S32 ify = fxrec->pos_A[1].y; @@ -690,6 +699,14 @@ void NCIN_HazTTSteam_AR(const zCutsceneMgr* cutsceneMgr, NCINEntry* fxrec, RpAto { } +void NCIN_ArfDogBoom(const zCutsceneMgr*, NCINEntry*, S32) +{ +} + +void NCIN_ShieldPop(const zCutsceneMgr*, NCINEntry*, S32) +{ +} + void clamp_bone_index(NCINEntry*, RpAtomic*) { } diff --git a/src/SB/Game/zNPCFXCinematic.h b/src/SB/Game/zNPCFXCinematic.h index 361788736..8d4172c7c 100644 --- a/src/SB/Game/zNPCFXCinematic.h +++ b/src/SB/Game/zNPCFXCinematic.h @@ -11,14 +11,12 @@ #include "zShrapnel.h" #include "rwcore.h" - void zNPCFXStartup(); void zNPCFXShutdown(); S32 zNPCFXCutscenePrep(const xScene*, F32, const zCutsceneMgr* csnmgr); void zNPCFXCutscene(const xScene*, F32, const zCutsceneMgr* csnmgr); void zNPCFXCutsceneDone(const xScene*, F32, const zCutsceneMgr* csnmgr); - struct NCINLyt { zLightning* lyt_zap; @@ -200,4 +198,13 @@ struct NPCCone void RadiusSet(F32); }; +struct NCINBeNosey : XCSNNosey +{ + zCutsceneMgr* use_csnmgr; + NCINEntry* use_fxtab; + + void UpdatedAnimated(RpAtomic* model, RwMatrixTag* animMat, U32 animIndex, U32 dataIndex); + void CanRenderNow(); +}; + #endif diff --git a/src/SB/Game/zNPCMessenger.h b/src/SB/Game/zNPCMessenger.h index 9f73d480e..0d3d80f36 100644 --- a/src/SB/Game/zNPCMessenger.h +++ b/src/SB/Game/zNPCMessenger.h @@ -8,10 +8,12 @@ void zNPCMsg_SceneReset(); void zNPCMsg_ScenePrepare(); void zNPCMsg_Startup(); void zNPCMsg_Shutdown(); -void zNPCMsg_AreaNotify(zNPCCommon* sender, en_NPC_MSG_ID msgid, F32 rad, S32 filter, - en_NPCTYPES toNPCType); +// void zNPCMsg_AreaNotify(zNPCCommon* sender, en_NPC_MSG_ID msgid, F32 rad, S32 filter, +// en_NPCTYPES toNPCType); void zNPCMsg_Timestep(xScene* xscn, F32 dt); +struct NPCMsg; + struct NPCPSClt { // total size: 0x10 diff --git a/src/SB/Game/zNPCSupport.cpp b/src/SB/Game/zNPCSupport.cpp index 0c04cd95e..29dfb2eec 100644 --- a/src/SB/Game/zNPCSupport.cpp +++ b/src/SB/Game/zNPCSupport.cpp @@ -8,18 +8,33 @@ #include "zNPCHazard.h" #include "zNPCGlyph.h" #include "zNPCSupplement.h" +#include "zNPCMgr.h" +#include "zNPCTypeRobot.h" +#include "zNPCFXCinematic.h" #include "xMathInlines.h" #include "xMath3.h" #include "xUtil.h" #include "xBound.h" +#include "xQuickCull.h" +#include "xCollide.h" + +#define MAX_FIREWORK 32 NPCWidget g_npc_widgets[1] = {}; static U32 g_hash_uiwidgets[1] = { 0 }; static char* g_strz_uiwidgets[1] = { "MNU4 NPCTALK" }; + +static U32 sNPCSndFx[eNPCSnd_Total] = {}; +static U32 sNPCSndID[eNPCSnd_Total] = {}; +static F32 sNPCSndFxVolume[eNPCSnd_Total] = {}; + S32 g_pc_playerInvisible; static Firework g_fireworks[32]; +F32 Firework::acc_thrust = 15.0f; +F32 Firework::acc_gravity = -10.0f; + void NPCSupport_Startup() { zNPCHazard_Startup(); @@ -83,7 +98,10 @@ void NPCSupport_Timestep(F32 dt) void NPCWidget_Startup() { - g_hash_uiwidgets[0] = xStrHash((const char*)g_strz_uiwidgets); + for (S32 i = 0; i < NPC_WIDGE_NOMORE; i++) + { + g_hash_uiwidgets[i] = xStrHash(g_strz_uiwidgets[i]); + } } void NPCWidget_Shutdown() @@ -108,7 +126,7 @@ void NPCWidget::Reset() { } -U32 NPCWidget::On(const zNPCCommon* npc, int theman) +S32 NPCWidget::On(const zNPCCommon* npc, S32 theman) { if ((!theman && !NPCIsTheLocker(npc)) && (((S32)IsLocked()) || (!Lock(npc)))) { @@ -128,6 +146,23 @@ U32 NPCWidget::On(const zNPCCommon* npc, int theman) return 1; } +S32 NPCWidget::Off(const zNPCCommon* npc, S32 theman) +{ + if (!theman && !this->NPCIsTheLocker(npc)) + { + return 0; + } + + if (npc) + { + this->Unlock(npc); + } + + zEntEvent(this->base_widge, eEventInvisible); + zEntEvent(this->base_widge, eEventUIFocusOff_Unselect); + return 1; +} + S32 NPCWidget::Unlock(const zNPCCommon* npc) { if (npc_ownerlock == NULL) @@ -192,7 +227,8 @@ NPCWidget* NPCWidget_Find(en_NPC_UI_WIDGETS which) void NPCWidget::Init(en_NPC_UI_WIDGETS which) { - base_widge = zSceneFindObject(g_hash_uiwidgets[idxID = which]); + idxID = which; + base_widge = zSceneFindObject(g_hash_uiwidgets[idxID]); } void NPCTarget::TargetSet(xEnt* ent, int b) @@ -217,6 +253,322 @@ void NPCTarget::TargetClear() typ_target = NPC_TGT_NONE; } +S32 NPCTarget::FindNearest(S32 flg_consider, xBase* skipme, xVec3* from, F32 dst_max) +//NONMATCH("https://decomp.me/scratch/wWBRW") +{ + S32 found = 0; + st_XORDEREDARRAY* npclist; + F32 ds2_best; + zNPCCommon *npc, *npc_best; + xVec3 vec = {}; + F32 fv; + S32 i, ntyp; + + npc_best = NULL; + ds2_best = (dst_max < 0.0f) ? HUGE : SQ(dst_max); + + if (flg_consider & 0x1) + { + this->TargetSet(&globals.player.ent, 1); + + if (from) + { + xVec3Sub(&vec, xEntGetPos(&globals.player.ent), from); + ds2_best = xVec3Length2(&vec); + } + } + + if (from && (flg_consider & 0x1E)) + { + npclist = zNPCMgr_GetNPCList(); + + for (i = 0; i < npclist->cnt; i++) + { + npc = (zNPCCommon*)npclist->list[i]; + ntyp = npc->SelfType(); + + if (npc == skipme) + continue; + + if (((ntyp & 0xFFFFFF00) != 'NTT\0' || (flg_consider & 0x4)) && + ((ntyp & 0xFFFFFF00) != 'NTR\0' || (flg_consider & 0x2)) && + ((ntyp & 0xFFFFFF00) != 'NTF\0' || (flg_consider & 0x8)) && + ((ntyp & 0xFFFFFF00) != 'NTA\0' || (flg_consider & 0x10))) + { + if (npc->IsAlive()) + { + xVec3Sub(&vec, xEntGetPos(npc), from); + if (flg_consider & 0x80) + { + vec.y = 0.0f; + } + + fv = xVec3Length2(&vec); + if (fv > ds2_best) + continue; + + ds2_best = fv; + npc_best = npc; + found = 1; + } + } + } + + if (found) + { + this->TargetSet(npc_best, 0); + } + } + + return found; +} + +S32 NPCTarget::InCylinder(xVec3* from, F32 rad, F32 hyt, F32 off) +{ + S32 inrange = 1; + + xVec3 vec = {}; + this->PosGet(&vec); + xVec3SubFrom(&vec, from); + + F32 upper = hyt + off; + F32 lower = upper - hyt; + + if (vec.y > upper) + { + inrange = 0; + } + else if (vec.y < lower) + { + inrange = 0; + } + else if (xVec3Length2(&vec) > SQ(rad)) + { + inrange = 0; + } + + return inrange; +} + +S32 NPCTarget::IsDead() +{ + S32 dead = 0; + + switch (this->typ_target) + { + case NPC_TGT_PLYR: + if (globals.player.Health < 1) + { + dead = 1; + } + break; + case NPC_TGT_ENT: + if (this->ent_target->baseType == eBaseTypeNPC) + { + if (!((zNPCCommon*)this->ent_target)->IsAlive()) + { + dead = 1; + } + } + break; + case NPC_TGT_BASE: + break; + } + + return dead; +} + +// void NPCLaser::Render(xVec3* pos_src, xVec3* pos_tgt) +// //NONMATCH("https://decomp.me/scratch/lNgTd") +// { +// xVec3 var_70; +// xVec3Copy(&var_70, pos_src); + +// xVec3 var_7C; +// xVec3Copy(&var_7C, pos_tgt); + +// xVec3 var_88; +// xVec3Sub(&var_88, &var_7C, &var_70); +// xVec3Normalize(&var_88, &var_88); + +// xVec3 var_94; +// xVec3Cross(&var_94, &globals.camera.mat.at, &var_88); + +// F32 f1 = xVec3Length2(&var_94); +// if (f1 < 0.00001f) +// { +// xVec3Copy(&var_94, &g_X3); +// } +// else +// { +// xVec3SMulBy(&var_94, 1.0f / xsqrt(f1)); +// } + +// xVec3 var_A0; +// xVec3Cross(&var_A0, &var_94, &var_88); + +// S32 i; + +// static RwIm3DVertex laser_vtxbuf[2][14]; +// RwIm3DVertex* vtx_horz = laser_vtxbuf[0]; +// RwIm3DVertex* vtx_vert = laser_vtxbuf[1]; + +// for (i = 0; i <= 6; i++) +// { +// F32 rat = (F32)i / 6.0f; +// F32 f29 = LERP(rat, this->radius[0], this->radius[1]); + +// xVec3 var_AC; +// var_AC.x = LERP(rat, var_70.x, var_7C.x); +// var_AC.y = LERP(rat, var_70.y, var_7C.y); +// var_AC.z = LERP(rat, var_70.z, var_7C.z); + +// U8 r22 = LERP(rat, this->rgba[0].red, this->rgba[1].red); +// U8 r23 = LERP(rat, this->rgba[0].green, this->rgba[1].green); +// U8 r24 = LERP(rat, this->rgba[0].blue, this->rgba[1].blue); +// U8 r25 = LERP(rat, this->rgba[0].alpha, this->rgba[1].alpha); + +// F32 u = 1.0f - rat + this->uv_base[0]; +// F32 v = 1.0f - rat + this->uv_base[1]; + +// while (u > 1.0f) +// u -= 1.0f; +// while (v > 1.0f) +// v -= 1.0f; + +// xVec3 var_B8; + +// xVec3SMul(&var_B8, &var_94, f29); +// xVec3AddTo(&var_B8, &var_AC); +// RwIm3DVertexSetPos(&vtx_horz[0], var_B8.x, var_B8.y, var_B8.z); +// RwIm3DVertexSetRGBA(&vtx_horz[0], r22, r23, r24, r25); +// RwIm3DVertexSetU(&vtx_horz[0], 0.0f); +// RwIm3DVertexSetV(&vtx_horz[0], v); + +// xVec3SMul(&var_B8, &var_94, -f29); +// xVec3AddTo(&var_B8, &var_AC); +// RwIm3DVertexSetPos(&vtx_horz[1], var_B8.x, var_B8.y, var_B8.z); +// RwIm3DVertexSetRGBA(&vtx_horz[1], r22, r23, r24, r25); +// RwIm3DVertexSetU(&vtx_horz[1], 1.0f); +// RwIm3DVertexSetV(&vtx_horz[1], v); + +// vtx_horz += 2; + +// xVec3SMul(&var_B8, &var_A0, f29); +// xVec3AddTo(&var_B8, &var_AC); +// RwIm3DVertexSetPos(&vtx_vert[0], var_B8.x, var_B8.y, var_B8.z); +// RwIm3DVertexSetRGBA(&vtx_vert[0], r22, r23, r24, r25); +// RwIm3DVertexSetU(&vtx_vert[0], 0.0f); +// RwIm3DVertexSetV(&vtx_vert[0], v); + +// xVec3SMul(&var_B8, &var_A0, -f29); +// xVec3AddTo(&var_B8, &var_AC); +// RwIm3DVertexSetPos(&vtx_vert[1], var_B8.x, var_B8.y, var_B8.z); +// RwIm3DVertexSetRGBA(&vtx_vert[1], r22, r23, r24, r25); +// RwIm3DVertexSetU(&vtx_vert[1], 1.0f); +// RwIm3DVertexSetV(&vtx_vert[1], v); + +// vtx_vert += 2; +// } + +// SDRenderState old_rendstat = zRenderStateCurrent(); +// if (old_rendstat == SDRS_Unknown) +// { +// old_rendstat = SDRS_Default; +// } + +// zRenderState(SDRS_NPCVisual); + +// RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)this->rast_laser); +// RwIm3DTransform(laser_vtxbuf[0], 14, NULL, +// rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA | rwIM3D_VERTEXUV); +// RwIm3DRenderPrimitive(rwPRIMTYPETRISTRIP); +// RwIm3DEnd(); + +// RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)this->rast_laser); +// RwIm3DTransform(laser_vtxbuf[1], 14, NULL, +// rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA | rwIM3D_VERTEXUV); +// RwIm3DRenderPrimitive(rwPRIMTYPETRISTRIP); +// RwIm3DEnd(); + +// zRenderState(old_rendstat); +// } + +// void NPCCone::RenderCone(xVec3* pos_tiptop, xVec3* pos_botcenter) +// //NONMATCH("https://decomp.me/scratch/G9pbs") +// { +// RwRGBA rgba_top = this->rgba_top; +// RwRGBA rgba_bot = this->rgba_bot; +// xVec3 pos_top = *pos_tiptop; +// xVec3 pos_bot = *pos_botcenter; +// F32 f29 = this->uv_tip[0] + 0.5f * this->uv_slice[0]; +// F32 f28 = this->uv_tip[1]; +// F32 f31 = this->uv_tip[0] + this->uv_slice[0]; +// F32 f30 = this->uv_tip[1] + this->uv_slice[1]; + +// void* mem = xMemPushTemp(10 * sizeof(RwIm3DVertex)); +// if (!mem) +// { +// return; +// } + +// memset(mem, 0, 10 * sizeof(RwIm3DVertex)); + +// RwIm3DVertex* vert_list = (RwIm3DVertex*)mem; +// RwIm3DVertex* vtx = vert_list + 1; + +// RwIm3DVertexSetPos(&vert_list[0], pos_top.x, pos_top.y, pos_top.z); +// RwIm3DVertexSetRGBA(&vert_list[0], rgba_top.red, rgba_top.green, rgba_top.blue, rgba_top.alpha); +// RwIm3DVertexSetU(&vert_list[0], f29); +// RwIm3DVertexSetV(&vert_list[0], f28); + +// for (S32 i = 0; i < 8; i++) +// { +// F32 ang_seg = i * PI / 4; +// F32 f29 = isin(ang_seg); +// F32 f1 = icos(ang_seg); + +// xVec3 var_A0; +// var_A0.x = f29; +// var_A0.y = 0.0f; +// var_A0.z = f1; +// var_A0 *= this->rad_cone; +// var_A0 += pos_bot; + +// RwIm3DVertexSetPos(vtx, var_A0.x, var_A0.y, var_A0.z); +// RwIm3DVertexSetRGBA(vtx, rgba_bot.red, rgba_bot.green, rgba_bot.blue, rgba_bot.alpha); + +// F32 f0 = 1 / 8.0f * i; + +// RwIm3DVertexSetU(vtx, f31 + f0); +// RwIm3DVertexSetV(vtx, f30); + +// vtx++; +// } + +// *vtx = vert_list[1]; +// RwIm3DVertexSetU(vtx, f31 + this->uv_slice[0]); +// RwIm3DVertexSetV(vtx, f30); + +// SDRenderState old_rendstat = zRenderStateCurrent(); +// if (old_rendstat == SDRS_Unknown) +// { +// old_rendstat = SDRS_Default; +// } + +// zRenderState(SDRS_NPCVisual); + +// RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); +// RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)this->rast_cone); +// RwIm3DTransform(vert_list, 10, NULL, rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA | rwIM3D_VERTEXUV); +// RwIm3DRenderPrimitive(rwPRIMTYPETRIFAN); +// RwIm3DEnd(); + +// zRenderState(old_rendstat); + +// xMemPopTemp(mem); +// } + void NPCBlinker::Reset() { tmr_uvcell = -1.0f; @@ -270,7 +622,45 @@ void Firework::Cleanup() { } -void NPAR_EmitFWExhaust(xVec3* pos, xVec3* vel); +void NPAR_EmitFWExhaust(xVec3* pos, const xVec3* vel); + +void Firework::Update(F32 dt) +{ + switch (this->fwstate) + { + case FW_STAT_FLIGHT: + this->FlyFlyFly(dt); + if (this->tmr_remain < 0.0f) + { + this->fwstate = FW_STAT_BOOM; + } + break; + case FW_STAT_BOOM: + this->Detonate(); + this->fwstate = FW_STAT_DONE; + break; + case FW_STAT_DONE: + break; + } + + this->tmr_remain = MAX(-1.0f, this->tmr_remain - dt); +} + +void Firework::FlyFlyFly(F32 dt) //NONMATCH("https://decomp.me/scratch/6hPC3") +{ + F32 pam_life = 1.0f - CLAMP(this->tmr_remain / this->tym_lifespan, 0.0f, 1.0f); + if (pam_life < 0.75f) + { + xVec3 dir_trav = this->vel; + dir_trav.normalize(); + + this->vel += dir_trav * (Firework::acc_thrust * dt); + } + + this->vel += g_NY3 * (Firework::acc_gravity * dt); + + NPAR_EmitFWExhaust(&this->pos, &g_O3); +} void Firework::Detonate() { @@ -291,7 +681,129 @@ void NPCC_dir_toXZAng(const xVec3* vec) void NPCC_aimMiss(xVec3* dir_aim, xVec3* pos_src, xVec3* pos_tgt, F32 dst_miss, xVec3* pos_miss) { - NPCC_aimVary(dir_aim, pos_src, pos_tgt, dst_miss, 8, pos_miss); + NPCC_aimVary(dir_aim, pos_src, pos_tgt, dst_miss, 8, pos_miss); +} + +F32 NPCC_aimVary(xVec3* dir_aim, xVec3* pos_src, xVec3* pos_tgt, F32 dst_vary, S32 flg_vary, + xVec3* pos_aimPoint) //NONMATCH("https://decomp.me/scratch/D1gIj") +{ + F32 dst_toFake = 0.0f; + xVec3 dir_left = {}; + xVec3 dir_toFake = {}; + xVec3 dir_toReal = {}; + xVec3 vec_offset = {}; + xVec3 pos_tgtFake = {}; + + xVec3Sub(&dir_toReal, pos_tgt, pos_src); + + if (flg_vary & 0x10) + { + dir_toReal.y = 0.0f; + } + + F32 mag_vary = xVec3Length(&dir_toReal); + if (mag_vary < 0.001f) + { + if (mag_vary > 0.0f) + { + xVec3SMulBy(&dir_toReal, 100000.0f); + xVec3Normalize(&dir_toReal, &dir_toReal); + } + else + { + xVec3Copy(dir_aim, &g_X3); + } + + if (pos_aimPoint) + { + xVec3Copy(pos_aimPoint, pos_tgt); + } + + return mag_vary; + } + + xVec3SMulBy(&dir_toReal, 1.0f / mag_vary); + xVec3Cross(&dir_left, &g_Y3, &dir_toReal); + + F32 mag_updown; + if (flg_vary & 0x8) + { + mag_updown = dst_vary; + } + else + { + mag_updown = 2.0f * (xurand() - 0.5f) * dst_vary; + + F32 fv; + if ((flg_vary & 0x1) && (flg_vary & 0x2)) + { + fv = 2.0f * (xurand() - 0.5f); + } + else if (flg_vary == 0x1) + { + fv = xurand(); + } + else if (flg_vary == 0x2) + { + fv = -xurand(); + } + else + { + fv = 0.0f; + } + + dst_toFake = fv * dst_vary; + } + + xVec3AddScaled(&vec_offset, &dir_left, mag_updown); + xVec3AddScaled(&vec_offset, &g_Y3, dst_toFake); + xVec3Add(&pos_tgtFake, pos_tgt, &vec_offset); + xVec3Sub(&dir_toFake, &pos_tgtFake, pos_src); + + F32 f31 = xVec3Normalize(&dir_toFake, dir_aim); + + if (pos_aimPoint) + { + xVec3Copy(pos_aimPoint, &pos_tgtFake); + } + + return (flg_vary & 0x4) ? f31 : mag_vary; +} + +S32 NPCC_chk_hitEnt(xEnt* tgt, xBound* bnd, + xCollis* collide) //NONMATCH("https://decomp.me/scratch/Bf1rk") +{ + S32 hittgt = 0; + xCollis* colrec; + xCollis lcl_collide = {}; + + colrec = collide ? collide : &lcl_collide; + colrec->optr = tgt; + colrec->oid = tgt->id; + + if (collide) + { + colrec->flags = ((U32)(1 << 9)) | ((U32)(1 << 12)); + } + else + { + colrec->flags = 0; + } + + xQuickCullForEverything(&bnd->qcd); + xBoundHitsBound(bnd, &tgt->bound, colrec); + + if (colrec->flags & ((U32)(1 << 0))) + { + hittgt = 1; + } + + return hittgt; +} + +S32 NPCC_chk_hitPlyr(xBound* bnd, xCollis* collide) +{ + return NPCC_chk_hitEnt(&globals.player.ent, bnd, collide); } void Firework_SceneReset(int param_1) @@ -370,8 +882,82 @@ RwRaster* NPCC_FindRWRaster(RwTexture* txtr) return NULL; } -void zNPC_SNDInit() +void NPCC_GenSmooth(xVec3** pos_base, + xVec3** pos_mid) //WIP NONMATCH("https://decomp.me/scratch/1MplX") +{ + static F32 prepute[4][4]; + static const F32 yews[4] = { 0.25f, 0.5f, 0.75f, 1.0f }; + static S32 init = 0; + + S32 i; + + if (!init) + { + init = 1; + + for (i = 0; i < 4; i++) + { + F32 u = yews[i]; + F32 u2 = u * u; + F32 u3 = u * u2; + + prepute[i][0] = u2 + -0.5f * u3 + -0.5f * u; + prepute[i][1] = -2.5f * u2 + 1.5f * u3 + 1.0f; + prepute[i][2] = 2.0f * u2 + -1.5f * u3 + 0.5f * u; + prepute[i][3] = -0.5f * u2 + 0.5f * u3; + } + } + + for (i = 0; i < 4; i++) + { + xVec3SMul(pos_mid[i], pos_base[0], prepute[i][0]); + xVec3AddScaled(pos_mid[i], pos_base[1], prepute[i][1]); + xVec3AddScaled(pos_mid[i], pos_base[2], prepute[i][2]); + xVec3AddScaled(pos_mid[i], pos_base[3], prepute[i][3]); + } +} + +void zNPC_SNDInit() //NONMATCH("https://decomp.me/scratch/VlDh8") { + sNPCSndID[eNPCSnd_GloveAttack] = 0; + sNPCSndID[eNPCSnd_SleepyAttack] = 0; + sNPCSndID[eNPCSnd_TubeAttack] = 0; + sNPCSndID[eNPCSnd_FodBzztAttack] = 0; + sNPCSndID[eNPCSnd_JellyfishAttack] = 0; + + sNPCSndFxVolume[eNPCSnd_GloveAttack] = 0.77f; + sNPCSndFxVolume[eNPCSnd_SleepyAttack] = 0.77f; + sNPCSndFxVolume[eNPCSnd_TubeAttack] = 0.77f; + sNPCSndFxVolume[eNPCSnd_FodBzztAttack] = 0.77f; + sNPCSndFxVolume[eNPCSnd_JellyfishAttack] = 0.77f; + + sNPCSndFx[eNPCSnd_GloveAttack] = xStrHash("Glove_hover_loop"); + sNPCSndFx[eNPCSnd_SleepyAttack] = xStrHash("ST_hit2_loop"); + sNPCSndFx[eNPCSnd_TubeAttack] = xStrHash("Tube_attack21_loop"); + sNPCSndFx[eNPCSnd_FodBzztAttack] = xStrHash("FodBzzt_attack_loop"); + sNPCSndFx[eNPCSnd_JellyfishAttack] = xStrHash("Jellyfish_zap_loop"); +} + +void zNPC_SNDPlay3D(_tageNPCSnd snd, xEnt* ent) +{ + if (globals.cmgr) + return; + if (sNPCSndID[snd] != 0) + return; + if (sNPCSndFx[snd] == 0) + return; + + sNPCSndID[snd] = xSndPlay3D(sNPCSndFx[snd], sNPCSndFxVolume[snd], 0.0f, 0x80, 0, ent, 2.0f, + 15.0f, SND_CAT_GAME, 0.0f); +} + +void zNPC_SNDStop(_tageNPCSnd snd) +{ + if (sNPCSndFx[snd] == 0) + return; + + xSndStop(sNPCSndID[snd]); + sNPCSndID[snd] = 0; } U32 NPCC_LineHitsBound(xVec3* param_1, xVec3* param_2, xBound* param_3, xCollis* param_4) @@ -384,7 +970,7 @@ U32 NPCC_LineHitsBound(xVec3* param_1, xVec3* param_2, xBound* param_3, xCollis* if (param_4 != NULL) { - colrec = (xCollis *)param_4; + colrec = (xCollis*)param_4; } xVec3Sub(&vec, param_2, param_1); len = xVec3Length(&vec); @@ -407,74 +993,74 @@ S32 NPCC_bnd_ofBase(xBase* tgt, xBound* bnd) { S32 retval = 1; - switch(tgt->baseType) - { - case eBaseTypeCamera: - case eBaseTypeDoor: - case eBaseTypeVolume: - case eBaseTypeEGenerator: - retval = 0; - break; - case eBaseTypePlayer: - case eBaseTypePickup: - case eBaseTypePlatform: - case eBaseTypeStatic: - case eBaseTypeDynamic: - case eBaseTypeBubble: - case eBaseTypePendulum: - case eBaseTypeHangable: - case eBaseTypeButton: - case eBaseTypeProjectile: - case eBaseTypeDestructObj: - case eBaseTypeNPC: - case eBaseTypeBoulder: - *bnd = *(xBound*)((int)tgt + 0x64); - break; - default: - retval = 0; - break; - case eBaseTypeCruiseBubble: - break; + switch (tgt->baseType) + { + case eBaseTypeCamera: + case eBaseTypeDoor: + case eBaseTypeVolume: + case eBaseTypeEGenerator: + retval = 0; + break; + case eBaseTypePlayer: + case eBaseTypePickup: + case eBaseTypePlatform: + case eBaseTypeStatic: + case eBaseTypeDynamic: + case eBaseTypeBubble: + case eBaseTypePendulum: + case eBaseTypeHangable: + case eBaseTypeButton: + case eBaseTypeProjectile: + case eBaseTypeDestructObj: + case eBaseTypeNPC: + case eBaseTypeBoulder: + *bnd = *(xBound*)((int)tgt + 0x64); + break; + default: + retval = 0; + break; + case eBaseTypeCruiseBubble: + break; } return retval; } S32 NPCC_pos_ofBase(xBase* tgt, xVec3* pos) { - xVec3 *pxVar1; + xVec3* pxVar1; S32 retval = 1; - switch(tgt->baseType) - { - case eBaseTypeCamera: - xVec3Copy(pos, &globals.camera.mat.pos); - break; - case eBaseTypeCruiseBubble: - retval = 0; - break; - case eBaseTypePlayer: - case eBaseTypePickup: - case eBaseTypePlatform: - case eBaseTypeStatic: - case eBaseTypeDynamic: - case eBaseTypeBubble: - case eBaseTypePendulum: - case eBaseTypeHangable: - case eBaseTypeButton: - case eBaseTypeProjectile: - case eBaseTypeDestructObj: - case eBaseTypeNPC: - case eBaseTypeBoulder: - xVec3Copy(pos, xEntGetPos((xEnt *)tgt)); - break; - case eBaseTypeDoor: - case eBaseTypeVolume: - case eBaseTypeEGenerator: - retval = 0; - break; - default: - retval = 0; - break; + switch (tgt->baseType) + { + case eBaseTypeCamera: + xVec3Copy(pos, &globals.camera.mat.pos); + break; + case eBaseTypeCruiseBubble: + retval = 0; + break; + case eBaseTypePlayer: + case eBaseTypePickup: + case eBaseTypePlatform: + case eBaseTypeStatic: + case eBaseTypeDynamic: + case eBaseTypeBubble: + case eBaseTypePendulum: + case eBaseTypeHangable: + case eBaseTypeButton: + case eBaseTypeProjectile: + case eBaseTypeDestructObj: + case eBaseTypeNPC: + case eBaseTypeBoulder: + xVec3Copy(pos, xEntGetPos((xEnt*)tgt)); + break; + case eBaseTypeDoor: + case eBaseTypeVolume: + case eBaseTypeEGenerator: + retval = 0; + break; + default: + retval = 0; + break; } return retval; } @@ -483,19 +1069,19 @@ void NPCTarget::PosGet(xVec3* pos) { switch (typ_target) { - case NPC_TGT_NONE: - break; - case NPC_TGT_PLYR: - case NPC_TGT_ENT: - case NPC_TGT_BASE: - NPCC_pos_ofBase(bas_target, pos); - break; - case NPC_TGT_POS: - xVec3Copy(pos, &pos_target); - break; - case NPC_TGT_MVPT: - xVec3Copy(pos, zMovePointGetPos(nav_target)); - break; + case NPC_TGT_NONE: + break; + case NPC_TGT_PLYR: + case NPC_TGT_ENT: + case NPC_TGT_BASE: + NPCC_pos_ofBase(bas_target, pos); + break; + case NPC_TGT_POS: + xVec3Copy(pos, &pos_target); + break; + case NPC_TGT_MVPT: + xVec3Copy(pos, zMovePointGetPos(nav_target)); + break; } } @@ -525,6 +1111,70 @@ void NPCC_xBoundBack(xBound* bnd) void NPCC_DstSq(const xVec3*, const xVec3*, xVec3*); +S32 NPCC_HaveLOSToPos(xVec3* pos_src, xVec3* pos_tgt, F32 dst_max, xBase* tgt, xCollis* colCallers) +//NONMATCH("https://decomp.me/scratch/LyDtk") +{ + S32 result; + xRay3 ray = {}; + xScene* xscn = globals.sceneCur; + xCollis* colrec; + + if (colCallers) + { + colrec = colCallers; + } + else + { + static xCollis localCollis = { (((U32)(1 << 8)) | ((U32)(1 << 9)) | ((U32)(1 << 10)) | + ((U32)(1 << 11))) | + ((U32)(1 << 12)) }; + + memset(&localCollis, 0, sizeof(xCollis)); + localCollis.flags = + (((U32)(1 << 8)) | ((U32)(1 << 9)) | ((U32)(1 << 10)) | ((U32)(1 << 11))) | + ((U32)(1 << 12)) | ((U32)(1 << 12)); + + colrec = &localCollis; + } + + ray.min_t = 0.0f; + ray.max_t = dst_max; + + xVec3Sub(&ray.dir, pos_tgt, pos_src); + xVec3Normalize(&ray.dir, &ray.dir); + xVec3Copy(&ray.origin, pos_src); + + ray.flags = (1 << 10) | (1 << 11); + + xRayHitsScene(xscn, &ray, colrec); + + if (!(colrec->flags & ((U32)(1 << 0)))) + { + result = 1; + } + else if (colrec->dist > dst_max) + { + result = 1; + } + else if (tgt && colrec->oid != 0) + { + if (tgt->id == colrec->oid) + { + result = 1; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + + return result; +} + void NPCC_DstSqPlyrToPos(const xVec3* pos) { NPCC_DstSq(pos, xEntGetPos(&globals.player.ent), NULL); @@ -535,7 +1185,7 @@ F32 NPCC_ds2_toCam(const xVec3* pos_from, xVec3* delta) xVec3 delt = {}; xVec3Sub(&delt, &globals.camera.mat.pos, pos_from); F32 retval = xVec3Length2(&delt); - if (delta != (xVec3 *)0) + if (delta != (xVec3*)0) { xVec3Copy(delta, &delt); } @@ -606,4 +1256,4 @@ void NPCC_MakeArbPlane(const xVec3* dir_norm, xVec3* at, xVec3* rt) { NPCC_MakePerp(at, dir_norm); xVec3Cross(rt, at, dir_norm); -} \ No newline at end of file +} diff --git a/src/SB/Game/zNPCSupport.h b/src/SB/Game/zNPCSupport.h index b69007b43..65b750a53 100644 --- a/src/SB/Game/zNPCSupport.h +++ b/src/SB/Game/zNPCSupport.h @@ -70,6 +70,8 @@ struct NPCTarget void PosGet(xVec3* pos); void TargetSet(xEnt* ent, int b); S32 IsDead(); + S32 FindNearest(S32 flg_consider, xBase* skipme, xVec3* from, F32 dst_max); + S32 InCylinder(xVec3* from, F32 rad, F32 hyt, F32 off); }; struct NPCBlinker @@ -91,8 +93,8 @@ struct NPCWidget S32 NPCIsTheLocker(const zNPCCommon* npc_lock); U32 IsLocked(); S32 IsVisible(); - U32 Off(zNPCCommon* npc, U32 theman); - U32 On(const zNPCCommon* npc, S32 theman); + S32 Off(const zNPCCommon* npc, S32 theman); + S32 On(const zNPCCommon* npc, S32 theman); void Reset(); void Init(en_NPC_UI_WIDGETS); S32 Lock(const zNPCCommon*); @@ -150,6 +152,7 @@ F32 NPCC_TmrCycle(F32* tmr, F32 dt, F32 interval); xVec3* NPCC_rightDir(xEnt* ent); xVec3* NPCC_faceDir(xEnt* ent); void NPCC_ang_toXZDir(F32 angle, xVec3* dir); +F32 NPCC_dir_toXZAng(const 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*); @@ -159,5 +162,7 @@ F32 NPCC_ds2_toCam(const xVec3* pos_from, xVec3* delta); void zNPC_SNDStop(_tageNPCSnd snd); void zNPC_SNDPlay3D(_tageNPCSnd snd, xEnt*); RwRaster* NPCC_FindRWRaster(char*); +void NPCC_xBoundAway(xBound* bnd); +void NPCC_xBoundBack(xBound* bnd); #endif diff --git a/src/SB/Game/zNPCTypeCommon.cpp b/src/SB/Game/zNPCTypeCommon.cpp index 3eb5e7a37..b898bfa59 100644 --- a/src/SB/Game/zNPCTypeCommon.cpp +++ b/src/SB/Game/zNPCTypeCommon.cpp @@ -16,6 +16,9 @@ #include "xString.h" #include "xDebug.h" +#include "xBound.h" + +// Finish porting code from bfbbpc repo #define Unknown 0 #define LassoGuide_Grab01 1 @@ -26,6 +29,7 @@ static char* g_strz_lassanim[3] = { "Unknown", "LassoGuide_Grab01", "LassoGuide_ extern S32 g_hash_lassanim[3]; extern volatile S32 g_skipDescent; extern NPCConfig* g_ncfghead; +static zNPCSettings* g_dflt_npcsettings; extern NPCSndTrax g_sndTrax_General[]; extern F32 lbl_803CE4C0; extern S32 g_flg_wonder; @@ -33,6 +37,84 @@ extern S32 g_isConversation; extern xBase* g_ownerConversation; extern F32 g_tmr_talkless; +static char* g_strz_params[NPC_PARM_NOMORE] = { + "Empty", + "MoveSpeed", + "TurnSpeed", + "FactorAccel", + "FactorDrift", + "FactorMass", + "FactorGravKnock", + "FactorElasticity", + "BoundMainIsBox", + "BoundMainCenter", + "BoundMainExtent", + "HitPoints", + "ScaleModel", + "DetectRadius", + "DetectHeight", + "DetectOffset", + "AttackRadius", + "AttackFOV", + "SoundRadius", + "DelayFidget", + "AttackPeriod", + "StunTime", + "AlertTime", + "VtxAttackBase", + "VtxAttack", + "VtxAttack1", + "VtxAttack2", + "VtxAttack3", + "VtxAttack4", + "VtxEyeball", + "VtxDmgSmokeA", + "VtxDmgSmokeB", + "VtxDmgSmokeC", + "VtxDmgFlameA", + "VtxDmgFlameB", + "VtxDmgFlameC", + "VtxPropel", + "VtxExhaust", + "VtxGen01", + "VtxGen02", + "VtxGen03", + "VtxGen04", + "VtxGen05", + "AttackSize01", + "AttackFrames01", + "AttackFrames01a", + "AttackFrames01b", + "AttackFrames02", + "AttackFrames02a", + "AttackFrames02b", + "AttackFrames03", + "AttackFrames03a", + "AttackFrames03b", + "EsteemSlotA", + "EsteemSlotB", + "EsteemSlotC", + "EsteemSlotD", + "EsteemSlotE", + "DistShadowCast", + "ShadowCacheRadius", + "ShadowRasterRadius", + "TestCount", + "EndTag_INIOnly", + "FirstMovepoint", + "EndTag_PropsOnly", + "Bogus_Share", + "EndTag_Shared", +}; + +static en_npcparm mdlVertToParm[NPC_MDLVERT_NOMORE] = { + NPC_PARM_VTX_ATTACKBASE, NPC_PARM_VTX_ATTACK, NPC_PARM_VTX_ATTACK1, NPC_PARM_VTX_ATTACK2, + NPC_PARM_VTX_ATTACK3, NPC_PARM_VTX_ATTACK4, NPC_PARM_VTX_EYEBALL, NPC_PARM_VTX_DMGSMOKEA, + NPC_PARM_VTX_DMGSMOKEB, NPC_PARM_VTX_DMGSMOKEC, NPC_PARM_VTX_DMGFLAMEA, NPC_PARM_VTX_DMGFLAMEB, + NPC_PARM_VTX_DMGFLAMEC, NPC_PARM_VTX_PROPEL, NPC_PARM_VTX_EXHAUST, NPC_PARM_VTX_GEN01, + NPC_PARM_VTX_GEN02, NPC_PARM_VTX_GEN03, NPC_PARM_VTX_GEN04, NPC_PARM_VTX_GEN05, +}; + xFactoryInst* ZNPC_Create_Common(S32 who, RyzMemGrow* grow, void*) { zNPCCommon* com = NULL; @@ -116,138 +198,2691 @@ void zNPCCommon_Timestep(xScene* scene, F32 dt) } } -void zNPCCommon::Destroy() +void zNPCCommon::Init(xEntAsset* entass) { - SelfDestroy(); -} + xSceneID2Name(globals.sceneCur, entass->id); + xNPCBasic::Init(entass); -void zNPCCommon::Process(xScene* xscn, F32 dt) -{ - if ((flg_misc & 4) != 0) + this->entass = entass; + this->npcass = (xEntNPCAsset*)(entass + 1); + + xLinkAsset* npclinx = (xLinkAsset*)(this->npcass + 1); + if (linkCount) { - ModelScaleSet(&cfg_npc->scl_model); + this->link = npclinx; } - flags1.flg_upward = flags1.flg_upward & ~0x2; - xNPCBasic::Process(xscn, dt); + else + { + this->link = NULL; + } + + this->parmdata = zEntGetModelParams(this->entass->modelInfoID, &this->pdatsize); + + this->cfg_npc = this->ConfigFind(this->entass->modelInfoID); + if (!this->cfg_npc) + { + this->cfg_npc = this->ConfigCreate(this->entass->modelInfoID); + this->ParseINI(); + } + + if (this->cfg_npc && xVec3Length2(&this->cfg_npc->scl_model) > 0.0f) + { + this->flg_misc |= 0x4; + } + + this->InitBounds(); } -void zNPCCommon::ParseProps() +void zNPCCommon::InitBounds() //NONMATCH("https://decomp.me/scratch/JPhdS") { - for (S32 i = 0x3f; i < 0x42; i++) + NPCConfig* cfg = this->cfg_npc; + xVec3 half = {}; + xSphere* sph = &this->bound.sph; + xBBox* box = &this->bound.box; + + if (cfg->useBoxBound) { - switch (i) + this->bound.type = XBOUND_TYPE_BOX; + } + else + { + this->bound.type = XBOUND_TYPE_SPHERE; + } + + S32 r28; + if (xVec3Length2(&cfg->off_bound) > 0.0f) + { + r28 = 1; + } + else + { + r28 = 0; + } + + if (xVec3Length2(&cfg->dim_bound) > 0.0f) + { + xSceneID2Name(globals.sceneCur, this->id); + this->DBG_Name(); + + if (this->bound.type == XBOUND_TYPE_SPHERE) { - case 0x3f: - MvptReset(NULL); + sph->r = cfg->dim_bound.x; + xVec3Copy(&sph->center, xEntGetPos(this)); + xVec3AddTo(&sph->center, &cfg->off_bound); + } + else + { + xVec3SMul(&half, &cfg->dim_bound, 0.5f); + xVec3Copy(&box->center, xEntGetPos(this)); + xVec3AddTo(&box->center, &cfg->off_bound); + xVec3Add(&box->box.upper, &box->center, &half); + xVec3Sub(&box->box.lower, &box->center, &half); + } + } + else + { + xSceneID2Name(globals.sceneCur, this->id); + this->DBG_Name(); + + switch (this->bound.type) + { + case XBOUND_TYPE_SPHERE: + iSphereForModel(sph, this->model); + cfg->dim_bound.x = sph->r; + cfg->dim_bound.y = 0.0f; + cfg->dim_bound.z = 0.0f; + if (!r28) + { + xVec3Copy(&cfg->off_bound, &sph->center); + cfg->off_bound.y = MAX(cfg->off_bound.y, sph->r); + } + sph->r = cfg->dim_bound.x; + xVec3Copy(&sph->center, xEntGetPos(this)); + xVec3AddTo(&sph->center, &cfg->off_bound); break; - default: + case XBOUND_TYPE_BOX: + case XBOUND_TYPE_OBB: + iBoxForModel(&box->box, this->model); + xVec3Sub(&cfg->dim_bound, &box->box.upper, &box->box.lower); + if (!r28) + { + xVec3SMul(&cfg->off_bound, &cfg->dim_bound, 0.5f); + } + xVec3SMul(&half, &cfg->dim_bound, 0.5f); + xVec3Copy(&box->center, xEntGetPos(this)); + xVec3AddTo(&box->center, &cfg->off_bound); + xVec3Add(&box->box.upper, &box->center, &half); + xVec3Sub(&box->box.lower, &box->center, &half); break; } } } -bool zNPCCommon::IsMountableType(en_ZBASETYPE type) +void zNPCCommon::Setup() { - switch (type) + xSceneID2Name(globals.sceneCur, this->id); + + xNPCBasic::Setup(); + + this->DBG_InstName(); + this->DBG_RptDataSize(); + + this->npcsetass = zNPCSettings_Find(this->npcass->npcProps); + + this->ParseLinks(); + + if (this->LassoInit()) { - case eBaseTypePlatform: - return true; + this->LassoSetup(); + } + + this->SelfSetup(); + this->DBG_AddTweakers(); + + switch (this->SelfType()) + { + case NPC_TYPE_HAMMER: + case NPC_TYPE_HAMSPIN: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("hammer_shrapnel"), NULL); + break; + case NPC_TYPE_TARTAR: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("tartar_shrapnel"), NULL); + break; + case NPC_TYPE_FODDER: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("fodder_shrapnel"), NULL); + break; + case NPC_TYPE_FODBZZT: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_0a_bzzt_shrapnel"), NULL); + break; + case NPC_TYPE_CHOMPER: + this->explosion = + (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_0a_chomper_shrapnel"), NULL); + break; + case NPC_TYPE_GLOVE: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("g-love_shrapnel"), NULL); + break; + case NPC_TYPE_MONSOON: + this->explosion = + (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_4a_monsoon_shrapnel"), NULL); + break; + case NPC_TYPE_SLEEPY: + this->explosion = + (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_sleepy-time_shrapnel"), NULL); + break; + case NPC_TYPE_ARFARF: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_arf_shrapnel"), NULL); + break; + case NPC_TYPE_CHUCK: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_chuck_shrapnel"), NULL); + break; + case NPC_TYPE_SLICK: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("robot_9a_shrapnel"), NULL); + break; + case NPC_TYPE_DUPLOTRON: + this->explosion = + (zShrapnelAsset*)xSTFindAsset(xStrHash("duplicatotron1000_shrapnel"), NULL); + break; + case NPC_TYPE_TIKI_WOOD: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("tiki_wooden_shrapnel"), NULL); + break; + case NPC_TYPE_TIKI_QUIET: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("tiki_shhhh_shrapnel"), NULL); + break; + case NPC_TYPE_TIKI_THUNDER: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("tiki_thunder_shrapnel"), NULL); + break; + case NPC_TYPE_TIKI_LOVEY: + this->explosion = + (zShrapnelAsset*)xSTFindAsset(xStrHash("tiki_lovey_dovey_shrapnel"), NULL); + break; + case NPC_TYPE_TIKI_STONE: + this->explosion = (zShrapnelAsset*)xSTFindAsset(xStrHash("tiki_stone_shrapnel"), NULL); break; default: - return false; + this->explosion = NULL; break; } -} -void zNPCCommon::SelfDestroy() -{ - xBehaveMgr* bmgr = xBehaveMgr_GetSelf(); - if (psy_instinct != NULL) + S32 wason = 0; + xPsyche* psy = this->psy_instinct; + + if (psy) { - bmgr->UnSubscribe(psy_instinct); + if (psy->ImmTranIsOn()) + { + wason = 1; + } + psy->ImmTranOff(); } - psy_instinct = NULL; -} -S32 zNPCCommon::GetVertPos(en_mdlvert vid, xVec3* pos) -{ - NPCConfig* cfg = cfg_npc; - if (!(cfg->flg_vert & 1 << vid)) + this->Reset(); + + if (psy && wason) { - return 0; + psy->ImmTranOn(); } - iModelTagEval(model->Data, &cfg->tag_vert[vid], model->Mat, pos); - return 1; } -void zNPCPlyrSnd_Reset() +void zNPCCommon::Reset() //NONMATCH("https://decomp.me/scratch/cl84A") { - g_tmr_talkless = 10.0f; -} + xSceneID2Name(globals.sceneCur, this->id); -void zNPCPlyrSnd_Update(F32 dt) -{ - g_tmr_talkless = MAX(-1.0f, g_tmr_talkless - dt); -} + xNPCBasic::Reset(); -void zNPCCommon::ConfigSceneDone() -{ - g_ncfghead = 0; + this->entShadow->dst_cast = this->cfg_npc->dst_castShadow; + this->entShadow->radius[0] = this->cfg_npc->rad_shadowCache; + this->entShadow->radius[1] = this->cfg_npc->rad_shadowRaster; + + this->ParseProps(); + + this->npcset = *this->npcsetass; + + if (this->entass->flags & XENT_COLLTYPE_DYN) + { + xEntShow(this); + } + else + { + xEntHide(this); + } + + if (this->flg_move & 0x2) + { + this->pflags |= XENT_COLLTYPE_DYN; + } + else if (this->flg_move & 0x4) + { + this->pflags &= (U8)~XENT_COLLTYPE_DYN; + } + else + { + this->pflags &= (U8)~XENT_COLLTYPE_DYN; + } + + if (this->model->Anim) + { + xAnimPlaySetState(this->model->Anim->Single, &this->model->Anim->Table->StateList[0], 0.0f); + } + + this->drv_data = this->PRIV_GetDriverData(); + if (this->drv_data) + { + xEntDriveInit(this->drv_data, this); + this->drv_data->flags |= 0x1; + } + + if (this->lassdata) + { + this->lassdata->stage = LASS_STAT_PENDING; + } } -void zNPCCommon_WonderReset() +void zNPCCommon::Destroy() { - g_isConversation = 0; - g_flg_wonder = 0; + SelfDestroy(); } -U32 zNPCCommon::CanDoSplines() +void zNPCCommon::Damage(en_NPC_DAMAGE_TYPE damtype, xBase* who, const xVec3* vec_hit) { - bool retval = false; - if ((npcset.useNavSplines) && ((flg_move) & 8)) + static NPCMsg msg; + NPCDamageInfo* dmg = &msg.dmgdata; + + if (!(this->flg_vuln & 0x1)) + return; + if (who && who->baseType == eBaseTypePlayer && !(this->flg_vuln & 0xFFFF0000)) + return; + + switch (damtype) { - retval = true; + case DMGTYP_ABOVE: + if (!(this->flg_vuln & 0x40000)) + return; + break; + case DMGTYP_BELOW: + if (!(this->flg_vuln & 0x20000)) + return; + break; + case DMGTYP_SIDE: + if (gCurrentPlayer == eCurrentPlayerSpongeBob) + { + if (!(this->flg_vuln & 0x10000)) + return; + } + else if (gCurrentPlayer == eCurrentPlayerPatrick) + { + if (!(this->flg_vuln & 0x80000000)) + return; + } + else if (gCurrentPlayer == eCurrentPlayerSandy) + { + if (!(this->flg_vuln & 0x2000000)) + return; + } + else + { + if (!(this->flg_vuln & 0x10000)) + return; + } + break; + case DMGTYP_INSTAKILL: + this->tmr_invuln = -1.0f; + break; + case DMGTYP_HITBYTOSS: + if (!(this->flg_vuln & 0x4)) + return; + break; + case DMGTYP_NPCATTACK: + if (!(this->flg_vuln & 0x8)) + return; + break; + case DMGTYP_ROPE: + if (!(this->flg_vuln & 0x1000000)) + return; + break; + case DMGTYP_CRUISEBUBBLE: + if (!(this->flg_vuln & 0x80000)) + return; + break; + case DMGTYP_PROJECTILE: + if (!(this->flg_vuln & 0x8)) + return; + break; + case DMGTYP_BUBBOWL: + if (!(this->flg_vuln & 0x100000)) + return; + break; + case DMGTYP_BOULDER: + if (!(this->flg_vuln & 0x200000)) + return; + break; + case DMGTYP_THUNDER_TIKI_EXPLOSION: + if (!(this->flg_vuln & 0x2)) + return; + break; + case DMGTYP_DAMAGE_SURFACE: + case DMGTYP_SURFACE: + if (!(this->flg_vuln & 0x10)) + return; + break; + case DMGTYP_BUNGEED: + if (!(this->flg_vuln & 0x400000)) + return; + break; } - return retval; -} -zMovePoint* zNPCCommon::FirstAssigned() -{ - zMovePoint* nav_first = NULL; - zNPCCommon::GetParm(NPC_PARM_FIRSTMVPT, &nav_first); - return nav_first; + if (this->tmr_invuln < 0.0f) + { + this->tmr_invuln = 0.5f; + + memset(&msg, 0, sizeof(msg)); + + msg.from = this->id; + msg.sendto = this->id; + msg.msgid = NPC_MID_DAMAGE; + msg.infotype = NPC_MDAT_DAMAGE; + + dmg->dmg_type = damtype; + dmg->dmg_from = who; + + if (vec_hit) + { + xVec3Copy(&dmg->vec_dmghit, vec_hit); + } + else + { + xVec3Copy(&dmg->vec_dmghit, &g_O3); + } + + zNPCMsg_SendMsg(&msg, this); + } } -U32 zNPCCommon::AnimCurStateID() +S32 zNPCCommon::Respawn(const xVec3* pos, zMovePoint* mvptFirst, zMovePoint* mvptSpawnRef) { - xAnimState* state = AnimCurState(); - if (state != NULL) + static NPCMsg msg; + + memset(&msg, 0, sizeof(msg)); + + msg.msgid = NPC_MID_RESPAWN; + msg.from = this->id; + msg.sendto = this->id; + msg.infotype = NPC_MDAT_SPAWN; + + if (pos) { - return state->ID; + xVec3Copy(&msg.spawning.pos_spawn, pos); } else { - return 0; + xVec3Copy(&msg.spawning.pos_spawn, &this->entass->pos); } -} -F32 zNPCCommon::AnimDuration(xAnimState* ast) -{ - if (ast == 0) + if (mvptFirst) { - ast = AnimCurState(); + msg.spawning.nav_firstMovepoint = mvptFirst; + } + else + { + msg.spawning.nav_firstMovepoint = NULL; } - return (ast == 0) ? 0.0f : ast->Data->Duration; -} -F32 zNPCCommon::AnimTimeRemain(xAnimState* ast) -{ - return (AnimDuration(ast) - AnimTimeCurrent()); + msg.spawning.nav_spawnReference = mvptSpawnRef; + msg.spawning.spawnSuccess = 0; + + zNPCMsg_SendMsg(&msg, this); + + return msg.spawning.spawnSuccess; } -F32 zNPCCommon::AnimTimeCurrent() +S32 zNPCCommon::NPCMessage(NPCMsg* mail) { - return model->Anim->Single->Time; + S32 handled = 1; + + switch (mail->msgid) + { + case NPC_MID_SYSEVENT: + { + xPsyche* psy = this->psy_instinct; + + switch (mail->sysevent.toEvent) + { + case eEventNPCPatrolOn: + this->npcset.allowPatrol = 1; + break; + case eEventNPCPatrolOff: + this->npcset.allowPatrol = 0; + break; + case eEventNPCWanderOn: + this->npcset.allowDetect = 1; + break; + case eEventNPCWanderOff: + this->npcset.allowDetect = 0; + break; + case eEventNPCDetectOn: + this->npcset.allowDetect = 1; + break; + case eEventNPCDetectOff: + this->npcset.allowDetect = 0; + break; + case eEventNPCChaseOn: + this->npcset.allowChase = 1; + break; + case eEventNPCChaseOff: + this->npcset.allowChase = 0; + break; + case eEventNPCSplineOKOn: + this->npcset.useNavSplines = 1; + break; + case eEventNPCSplineOKOff: + this->npcset.useNavSplines = 0; + break; + case eEventNPCSetActiveOff: + if (psy && psy->HasGoal(NPC_GOAL_LIMBO)) + { + psy->GoalSet(NPC_GOAL_LIMBO, 1); + } + break; + case eEventNPCSetActiveOn: + if (psy) + { + psy->GIDOfPending(); + + S32 r4 = psy->GIDOfSafety(); + if (r4) + { + psy->GoalSet(r4, 1); + } + } + break; + default: + handled = 0; + break; + } + break; + } + case NPC_MID_RESPAWN: + { + xVec3Copy(xEntGetPos(this), &mail->spawning.pos_spawn); + + zMovePoint* mvpt = mail->spawning.nav_spawnReference; + if (!mvpt) + mvpt = mail->spawning.nav_firstMovepoint; + + this->nav_past = mvpt; + this->nav_curr = mvpt; + this->nav_dest = mvpt; + this->nav_lead = mvpt; + + mail->spawning.spawnSuccess = 1; + break; + } + case NPC_MID_DAMAGE: + break; + case NPC_MID_DEV_ANIMCYCLE: + if (this->psy_instinct && this->psy_instinct->HasGoal(NPC_GOAL_DEVANIMSPIN)) + { + this->psy_instinct->GoalSet(NPC_GOAL_DEVANIMSPIN, 0); + } + break; + case NPC_MID_DEV_ANIMSPIN: + if (this->psy_instinct && this->psy_instinct->HasGoal(NPC_GOAL_DEVANIMCYCLE)) + { + this->psy_instinct->GoalSet(NPC_GOAL_DEVANIMCYCLE, 0); + } + break; + case NPC_MID_DEV_HEROMODE: + if (this->psy_instinct && this->psy_instinct->HasGoal(NPC_GOAL_DEVANIMHERO)) + { + this->psy_instinct->GoalSet(NPC_GOAL_DEVANIMHERO, 0); + } + break; + case NPC_MID_DEV_DONE: + break; + default: + handled = 1; + break; + } + + return handled; +} + +void zNPCCommon::Move(xScene* xscn, F32 dt, xEntFrame* frm) +{ + bool retval = false; + if ((npcset.useNavSplines) && ((flg_move)&8)) + if (this->drv_data && (this->drv_data->driver || this->drv_data->odriver)) + { + retval = true; + S32 backit = 0; + xVec3 var_28; + + if (this->frame->mode & 0x2) + { + backit = 1; + var_28 = this->frame->dpos; + } + + xEntDriveUpdate(this->drv_data, xscn, dt, NULL); + + if (backit) + { + this->frame->mode |= 0x2; + this->frame->dpos = var_28; + } + } + + xNPCBasic::Move(xscn, dt, frm); +} + +void zNPCCommon::Process(xScene* xscn, F32 dt) +{ + if ((flg_misc & 4) != 0) + { + ModelScaleSet(&cfg_npc->scl_model); + } + flags1.flg_upward = flags1.flg_upward & ~0x2; + xNPCBasic::Process(xscn, dt); +} + +void zNPCCommon::BUpdate(xVec3* pos) //NONMATCH("https://decomp.me/scratch/zpv2r") +{ + NPCConfig* cfg = this->cfg_npc; + + if (cfg->useBoxBound) + { + this->bound.type = XBOUND_TYPE_BOX; + + xBBox* box = &this->bound.box; + xVec3 half = cfg->dim_bound * 0.5f; + + box->center = *pos + cfg->off_bound; + box->box.upper = box->center + half; + box->box.lower = box->center - half; + } + else + { + this->bound.type = XBOUND_TYPE_SPHERE; + + xSphere* sph = &this->bound.sph; + + sph->center = *pos + cfg->off_bound; + sph->r = cfg->dim_bound.x; + } + + if (this->bound.type != XBOUND_TYPE_NA) + { + xQuickCullForBound(&this->bound.qcd, &this->bound); + } + + zGridUpdateEnt(this); +} + +F32 zNPCCommon::BoundAsRadius(S32 useCfg) const //NONMATCH("https://decomp.me/scratch/rCFqE") +{ + F32 rad = 1.0f; + + if (useCfg) + { + NPCConfig* cfg = this->cfg_npc; + + if (cfg->useBoxBound) + { + xVec3 dim = cfg->dim_bound; + rad = (dim.x + dim.y + dim.z) * (1 / 6.f); + } + else + { + rad = cfg->dim_bound.x; + } + } + else + { + if (this->bound.type == XBOUND_TYPE_BOX) + { + const xBBox* box = &this->bound.box; + const xVec3* le = &box->box.lower; + const xVec3* ue = &box->box.upper; + rad = ((ue->x + ue->y + ue->z) - (le->x + le->y - le->z)) * (1 / 6.f); + } + else if (this->bound.type == XBOUND_TYPE_SPHERE) + { + rad = this->bound.sph.r; + } + } + + return rad; +} + +void zNPCCommon::NewTime(xScene* xscn, F32 dt) +{ + if (this->flg_misc & 0x2) + { + this->SndQueUpdate(dt); + } + + this->tmr_invuln = MAX(-1.0f, this->tmr_invuln - dt); + + xNPCBasic::NewTime(xscn, dt); +} + +S32 zNPCCommon::SysEvent(xBase* from, xBase* to, U32 toEvent, const F32* toParam, + xBase* toParamWidget, S32* handled) +{ + static NPCMsg npcmsg; + + S32 doOtherEvents = 1; + zNPCCommon* npc = (zNPCCommon*)to; + xVec3 pos = {}; + xVec3 dir = {}; + + *handled = 1; + + switch (toEvent) + { + case eEventSceneEnd: + { + xPsyche* psy = this->psy_instinct; + if (psy) + { + psy->GoalNone(1); + } + break; + } + case eEventNPCSpecial_PlatformSnap: + case eEventNPCSpecial_PlatformFall: + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + break; + } + case eEventHit: + this->ConvertHitEvent(from, to, toEvent, toParam, toParamWidget, handled); + break; + case eEventReset: + this->Reset(); + break; + case eEventVisible: + xEntShow(npc); + break; + case eEventInvisible: + xEntHide(npc); + break; + case eEventSetUpdateDistance: + if (globals.updateMgr) + { + if (toParam[0] <= 0.0f) + { + xUpdateCull_SetCB(globals.updateMgr, npc, xUpdateCull_AlwaysTrueCB, NULL); + } + else + { + FloatAndVoid dist; + dist.f = SQR(toParam[0]); + xUpdateCull_SetCB(globals.updateMgr, npc, xUpdateCull_DistanceSquaredCB, dist.v); + } + } + break; + case eEventLaunchShrapnel: + { + zShrapnelAsset* shrap = (zShrapnelAsset*)toParamWidget; + if (shrap && shrap->initCB) + { + xVec3 currVel; + xVec3Sub(&currVel, &npc->frame->mat.pos, &npc->frame->oldmat.pos); + xVec3SMulBy(&currVel, 1.0f / globals.update_dt); + + shrap->initCB(shrap, this->model, &currVel, NULL); + } + break; + } + case eEventNPCKillQuietly: + this->Damage(DMGTYP_KILLEVENT, from, NULL); + break; + case eEventKill: + this->Damage(DMGTYP_INSTAKILL, from, NULL); + break; + case eEventNPCRespawn: + this->Respawn(NULL, NULL, NULL); + break; + case eEventNPCPatrolOn: + case eEventNPCPatrolOff: + case eEventNPCWanderOn: + case eEventNPCWanderOff: + case eEventNPCDetectOn: + case eEventNPCDetectOff: + case eEventNPCChaseOn: + case eEventNPCChaseOff: + case eEventNPCFightOn: + case eEventNPCFightOff: + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + break; + } + case eEventNPCForceConverseStart: + if (NPCC_ForceTalkOk()) + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + } + + break; + case eEventNPCCheerForMe: + zNPCMsg_SendMsg(NPC_MID_CELEBRATE, this); + break; + case eEventNPCGoToSleep: + case eEventNPCWakeUp: + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + break; + } + case eEventNPCSetActiveOn: + case eEventNPCSetActiveOff: + this->SelfType(); + + if (this->psy_instinct) + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + } + + break; + case eEventDuploWaveBegin: + case eEventDuploWaveComplete: + case eEventDuploNPCBorn: + case eEventDuploNPCKilled: + case eEventDuploExpiredMaxNPC: + case eEventDuploPause: + case eEventDuploResume: + case eEventDuploKillKids: + { + memset(&npcmsg, 0, sizeof(npcmsg)); + + npcmsg.msgid = NPC_MID_SYSEVENT; + npcmsg.infotype = NPC_MDAT_SYSEVENT; + npcmsg.from = this->id; + npcmsg.sendto = this->id; + + NPCSysEvent* se = &npcmsg.sysevent; + se->doLinkEvents = 1; + se->handled = 0; + se->to = to; + se->from = from; + se->toEvent = toEvent; + se->toParamWidget = toParamWidget; + + if (!toParam) + { + se->toParam[0] = 0.0f; + se->toParam[1] = 0.0f; + se->toParam[2] = 0.0f; + se->toParam[3] = 0.0f; + } + else + { + se->toParam[0] = toParam[0]; + se->toParam[1] = toParam[1]; + se->toParam[2] = toParam[2]; + se->toParam[3] = toParam[3]; + } + + zNPCMsg_SendMsg(&npcmsg, -1.0f, NULL); + + *handled = npcmsg.sysevent.handled; + doOtherEvents = se->doLinkEvents; + break; + } + case eEventNPCScript_ScriptBegin: + zNPCMsg_SendMsg(NPC_MID_SCRIPTBEGIN, this); + break; + case eEventNPCScript_ScriptEnd: + zNPCMsg_SendMsg(NPC_MID_SCRIPTEND, this); + break; + case eEventNPCScript_SetPos: + if (toParamWidget) + { + NPCC_pos_ofBase(toParamWidget, &pos); + this->MatPosSet(&pos); + } + break; + case eEventNPCScript_SetDir: + if (toParamWidget) + { + NPCC_pos_ofBase(toParamWidget, &dir); + xVec3SubFrom(&dir, xEntGetPos(this)); + dir.y = 0.0f; + + if (xVec3Length2(&dir) > 0.001f) + { + xVec3Normalize(&dir, &dir); + xVec3Copy((xVec3*)&this->model->Mat->at, &dir); + xVec3Copy((xVec3*)&this->model->Mat->up, &g_Y3); + xVec3Cross((xVec3*)&this->model->Mat->right, &dir, &g_Y3); + } + } + break; + case eEventNPCScript_LookNormal: + case eEventNPCScript_LookAlert: + case eEventNPCScript_FaceWidget: + case eEventNPCScript_GotoWidget: + case eEventNPCScript_AttackWidget: + case eEventNPCScript_FollowWidget: + case eEventNPCScript_PlayAnim: + case eEventNPCScript_LeadPlayer: + zEventName(toEvent); + break; + case eEventUnknown: + case eEventMount: + case eEventDismount: + case eEventDeath: + case eEventSceneBegin: + case eEventRoomBegin: + case eEventRoomEnd: + case eEventNPCScript_ScriptReady: + case eEventNPCScript_Halt: + case eEventNPCScript_FaceWidgetDone: + case eEventNPCScript_GotoWidgetDone: + case eEventNPCScript_AttackWidgetDone: + case eEventNPCScript_PlayAnimDone: + break; + default: + *handled = 0; + doOtherEvents = xNPCBasic::SysEvent(from, to, toEvent, toParam, toParamWidget, handled); + break; + } + + return doOtherEvents; +} + +void zNPCCommon::ConvertHitEvent(xBase* from, xBase* to, U32 toEvent, const F32* toParam, + xBase* toParamWidget, S32* handled) +{ + xVec3 pos_cruiser = {}; + xVec3* vec_hit = NULL; + F32 mag; + en_NPC_DAMAGE_TYPE what = DMGTYP_UNDECIDED; + + if (from) + { + switch (from->baseType) + { + case eBaseTypePlayer: + { + U32 mvinf = zEntPlayer_MoveInfo(); + if (mvinf & 0x8) + { + what = DMGTYP_BELOW; + } + else if (mvinf & 0x10) + { + what = DMGTYP_ABOVE; + } + else if (mvinf & 0x20) + { + what = DMGTYP_SIDE; + } + else + { + what = DMGTYP_SIDE; + } + + xVec3Sub(&pos_cruiser, xEntGetPos(&globals.player.ent), xEntGetPos(this)); + vec_hit = &pos_cruiser; + break; + } + case eBaseTypeCruiseBubble: + what = DMGTYP_CRUISEBUBBLE; + + if (toParam) + { + pos_cruiser.x = toParam[0]; + pos_cruiser.y = toParam[1]; + pos_cruiser.z = toParam[2]; + + mag = xVec3Length(&pos_cruiser); + if (mag > 0.001f) + { + xVec3SubFrom(&pos_cruiser, xEntGetPos(this)); + xVec3SMulBy(&pos_cruiser, 2.0f / mag); + + vec_hit = &pos_cruiser; + } + } + + break; + case eBaseTypeProjectile: + what = DMGTYP_PROJECTILE; + break; + case eBaseTypeBoulder: + if (globals.player.bubblebowl && globals.player.bubblebowl == (xEntBoulder*)from) + { + what = DMGTYP_BUBBOWL; + } + else + { + what = DMGTYP_BOULDER; + } + break; + } + } + + this->Damage(what, from, vec_hit); +} + +void zNPCCommon::VelStop() //NONMATCH("https://decomp.me/scratch/YizcX") +{ + this->spd_throttle = 0.0f; + this->frame->dvel.x = 0.0f; + this->frame->dvel.y = 0.0f; + this->frame->dvel.z = 0.0f; + this->frame->vel.x = 0.0f; + this->frame->vel.y = 0.0f; + this->frame->vel.z = 0.0f; + this->frame->mode |= 0xC; + + if (!this->drv_data) + { + this->frame->dpos.x = 0.0f; + this->frame->dpos.y = 0.0f; + this->frame->dpos.z = 0.0f; + this->frame->mode |= 0x2; + } + else if (!this->drv_data->driver) + { + this->frame->dpos.x = 0.0f; + this->frame->dpos.y = 0.0f; + this->frame->dpos.z = 0.0f; + this->frame->mode |= 0x2; + } +} + +F32 zNPCCommon::ThrottleAdjust(F32 dt, F32 spd_want, F32 accel) +{ + NPCConfig* cfg = this->cfg_npc; + F32 acc; + S32 speedup = (spd_want > this->spd_throttle); + + acc = (accel < 0.0f) ? (dt * cfg->fac_accelMax) : (dt * accel); + + if (xeq(spd_want, this->spd_throttle, acc)) + { + this->spd_throttle = spd_want; + return this->spd_throttle; + } + + if (speedup) + { + this->spd_throttle += acc; + this->spd_throttle = CLAMP(this->spd_throttle, 0.0f, spd_want); + } + else + { + this->spd_throttle -= acc; + this->spd_throttle = MAX(spd_want, this->spd_throttle); + } + + return this->spd_throttle; +} + +F32 zNPCCommon::ThrottleAccel(F32 dt, S32 speedup, F32 pct_max) +{ + NPCConfig* cfg = this->cfg_npc; + F32 acc = dt * cfg->fac_accelMax; + F32 spd_top = pct_max * cfg->spd_moveMax; + + if (speedup) + { + this->spd_throttle += acc; + } + else + { + this->spd_throttle -= acc; + } + + this->spd_throttle = CLAMP(this->spd_throttle, 0.0f, spd_top); + + return this->spd_throttle; +} + +F32 zNPCCommon::ThrottleApply(F32 dt, const xVec3* dir, S32 force3D) +{ + xVec3 var_30; + if (xVec3Length2(dir) < 0.00001f) + { + var_30 = *NPCC_faceDir(this) * this->spd_throttle; + } + else + { + var_30 = *dir * this->spd_throttle; + } + + var_30 *= dt; + + if ((this->flg_move & 0x2) && !force3D) + { + this->frame->dpos.x = var_30.x; + this->frame->dpos.z = var_30.z; + } + else + { + this->frame->dpos = var_30; + } + + this->frame->mode |= 0x2; + + return this->spd_throttle; +} + +F32 zNPCCommon::TurnToFace(F32 dt, const xVec3* dir_want, F32 useTurnRate) +//NONMATCH("https://decomp.me/scratch/KRonE") +{ + F32 f29 = (useTurnRate < 0.0f) ? (dt * this->cfg_npc->spd_turnMax) : (dt * useTurnRate); + F32 f30 = NPCC_dir_toXZAng(dir_want); + F32 f31 = NPCC_dir_toXZAng(NPCC_faceDir(this)); + + F32 f28 = this->frame->rot.angle; + if (this->frame->mode & 0x20) + { + f28 += this->frame->drot.angle; + } + + F32 f1_ = xDangleClamp(f30 - f28); + F32 f2_ = CLAMP(f1_, -f29, f29); + F32 f2 = CLAMP(f28 + f2_, f31 - f29, f31 + f29); + F32 f1 = f2 - f28; + + this->frame->drot.angle = f1; + this->frame->mode |= 0x20; + + return f1; + + /* + F32 ang_caller; + F32 ang_wouldbe; + F32 rot_lim; + F32 ang_true; + F32 ang_past; + F32 ang_diff; + */ +} + +void zNPCCommon::ParseLinks() +{ + for (S32 i = 0; i < this->linkCount; i++) + { + switch (this->link[i].srcEvent) + { + case eEventNPCScript_ScriptBegin: + case eEventNPCScript_ScriptEnd: + case eEventNPCScript_ScriptReady: + case eEventNPCScript_Halt: + case eEventNPCScript_SetPos: + case eEventNPCScript_SetDir: + case eEventNPCScript_LookNormal: + case eEventNPCScript_LookAlert: + case eEventNPCScript_FaceWidget: + case eEventNPCScript_FaceWidgetDone: + case eEventNPCScript_GotoWidget: + case eEventNPCScript_GotoWidgetDone: + case eEventNPCScript_AttackWidget: + case eEventNPCScript_AttackWidgetDone: + case eEventNPCScript_FollowWidget: + case eEventNPCScript_PlayAnim: + case eEventNPCScript_PlayAnimDone: + case eEventNPCScript_LeadPlayer: + xSceneID2Name(globals.sceneCur, this->id); + this->flg_misc |= 0x1; + break; + } + } +} + +void zNPCCommon::ParseINI() +{ + NPCConfig* cfg = this->cfg_npc; + + this->TagVerts(); + + for (S32 i = NPC_PARM_MOVERATE; i < NPC_PARM_ENDTAG_INI; i++) + { + en_npcparm pid = (en_npcparm)i; + + switch (pid) + { + case NPC_PARM_ACCEL: + this->GetParm(pid, &cfg->fac_accelMax); + break; + case NPC_PARM_DRIFT: + this->GetParm(pid, &cfg->fac_driftMax); + break; + case NPC_PARM_MASS: + this->GetParm(pid, &cfg->npcMass); + if (cfg->npcMass < 0.5f) + { + cfg->npcMass = 0.5f; + } + cfg->npcMassInv = 1.0f / cfg->npcMass; + break; + case NPC_PARM_TOSSELASTIC: + this->GetParm(pid, &cfg->fac_elastic); + break; + case NPC_PARM_TOSSGRAV: + this->GetParm(pid, &cfg->fac_gravKnock); + break; + case NPC_PARM_HITPOINTS: + this->GetParm(pid, &cfg->pts_damage); + break; + case NPC_PARM_BND_ISBOX: + this->GetParm(pid, &cfg->useBoxBound); + break; + case NPC_PARM_BND_CENTER: + this->GetParm(pid, &cfg->off_bound); + break; + case NPC_PARM_BND_EXTENT: + this->GetParm(pid, &cfg->dim_bound); + break; + case NPC_PARM_MOVERATE: + this->GetParm(pid, &cfg->spd_moveMax); + break; + case NPC_PARM_TURNRATE: + { + F32 fv = RAD2DEG(cfg->spd_turnMax); + this->GetParm(pid, &fv); + cfg->spd_turnMax = DEG2RAD(fv); + break; + } + case NPC_PARM_MODELSCALE: + this->GetParm(pid, &cfg->scl_model); + break; + case NPC_PARM_DETECT_RAD: + this->GetParm(pid, &cfg->rad_detect); + break; + case NPC_PARM_DETECT_HYT: + this->GetParm(pid, &cfg->hyt_detect); + break; + case NPC_PARM_DETECT_OFF: + this->GetParm(pid, &cfg->off_detect); + break; + case NPC_PARM_TIMEFIDGET: + this->GetParm(pid, &cfg->tym_fidget); + break; + case NPC_PARM_TIMEATTACK: + this->GetParm(pid, &cfg->tym_attack); + break; + case NPC_PARM_TIMESTUN: + this->GetParm(pid, &cfg->tym_stun); + break; + case NPC_PARM_TIMEALERT: + this->GetParm(pid, &cfg->tym_alert); + break; + case NPC_PARM_ATTACK_RAD: + this->GetParm(pid, &cfg->rad_attack); + break; + case NPC_PARM_ATTACK_FOV: + { + F32 fv = RAD2DEG(cfg->fov_attack); + this->GetParm(pid, &fv); + cfg->fov_attack = DEG2RAD(fv); + break; + } + case NPC_PARM_SND_RAD: + this->GetParm(pid, &cfg->rad_sound); + break; + case NPC_PARM_ATK_SIZE01: + this->GetParm(pid, &cfg->rad_dmgSize); + break; + case NPC_PARM_ATK_FRAMES01: + case NPC_PARM_ATK_FRAMES01A: + case NPC_PARM_ATK_FRAMES01B: + case NPC_PARM_ATK_FRAMES02: + case NPC_PARM_ATK_FRAMES02A: + case NPC_PARM_ATK_FRAMES02B: + case NPC_PARM_ATK_FRAMES03: + case NPC_PARM_ATK_FRAMES03A: + case NPC_PARM_ATK_FRAMES03B: + this->GetParm(pid, &cfg->animFrameRange[pid - NPC_PARM_ATK_FRAMES01]); + break; + case NPC_PARM_ESTEEM_A: + case NPC_PARM_ESTEEM_B: + case NPC_PARM_ESTEEM_C: + case NPC_PARM_ESTEEM_D: + case NPC_PARM_ESTEEM_E: + this->GetParm(pid, &cfg->cnt_esteem[pid - NPC_PARM_ESTEEM_A]); + break; + case NPC_PARM_SHADOW_CASTDIST: + this->GetParm(pid, &cfg->dst_castShadow); + break; + case NPC_PARM_SHADOW_RADCACHE: + this->GetParm(pid, &cfg->rad_shadowCache); + break; + case NPC_PARM_SHADOW_RADRASTER: + this->GetParm(pid, &cfg->rad_shadowRaster); + break; + case NPC_PARAM_TEST_COUNT: + this->GetParm(pid, &cfg->test_count); + break; + } + } +} + +void zNPCCommon::ParseProps() +{ + for (S32 i = 0x3f; i < 0x42; i++) + { + switch (i) + { + case 0x3f: + MvptReset(NULL); + break; + default: + break; + } + } +} + +void zNPCCommon::CollideReview() +{ + xEntCollis* npccol = this->collis; + xCollis* colrec = &npccol->colls[0]; + S32 i; + S32 hitthings = 0; + + if (!(colrec->flags & k_HIT_IT) && (this->flg_move & 0x2)) + { + this->colFreq = 0; + } + + if (this->drv_data) + { + if ((colrec->flags & k_HIT_IT) && colrec->optr) + { + xEnt* flent = (xEnt*)colrec->optr; + if (this->IsMountableType((en_ZBASETYPE)flent->baseType)) + { + if (!this->drv_data->driver) + { + xEntDriveMount(this->drv_data, flent, 1.0f, colrec); + } + else if (flent != this->drv_data->driver) + { + xEntDriveDismount(this->drv_data, 0.3f); + xEntDriveMount(this->drv_data, flent, 1.0f, colrec); + } + } + } + else if (this->drv_data->driver) + { + xEntDriveDismount(this->drv_data, 0.3f); + } + } + + for (i = npccol->env_sidx; i < npccol->env_eidx; i++) + { + if (!(npccol->colls[i].flags & k_HIT_0x4)) + { + hitthings++; + } + } + + for (i = npccol->stat_sidx; i < npccol->stat_eidx; i++) + { + if (!(npccol->colls[i].flags & k_HIT_0x4)) + { + hitthings++; + } + } + + for (i = npccol->dyn_sidx; i < npccol->dyn_eidx; i++) + { + if (npccol->colls[i].flags & k_HIT_0x4) + { + xBase* base = (xBase*)npccol->colls[i].optr; + if (base->baseType == eBaseTypeButton) + { + zEntButton_Hold((_zEntButton*)base, 0x800); + } + } + hitthings++; + } + + for (i = npccol->npc_sidx; i < npccol->npc_eidx; i++) + { + if (!(npccol->colls[i].flags & k_HIT_0x4)) + { + hitthings++; + } + } + + if (hitthings) + { + S32 cf = (S32)(8.0f * xurand() + 2.0); + colFreq = MIN(colFreq, cf); + } +} + +bool zNPCCommon::IsMountableType(en_ZBASETYPE type) +{ + switch (type) + { + case eBaseTypePlatform: + return true; + break; + default: + return false; + break; + } +} + +void zNPCCommon::SelfDestroy() +{ + xBehaveMgr* bmgr = xBehaveMgr_GetSelf(); + if (psy_instinct != NULL) + { + bmgr->UnSubscribe(psy_instinct); + } + psy_instinct = NULL; +} + +void zNPCCommon::TagVerts() +{ + NPCConfig* cfg = this->cfg_npc; + S32 i; + xVec3 tmp_pos = {}; + F32 vertinfo[4] = {}; + + for (i = 0; i < 20; i++) + { + if (mdlVertToParm[i] != NPC_PARM_NONE) + { + this->GetParm(mdlVertToParm[i], vertinfo); + + tmp_pos.x = vertinfo[0]; + tmp_pos.y = vertinfo[1]; + tmp_pos.z = vertinfo[2]; + + if (xVec3Dot(&tmp_pos, &tmp_pos) > 0.001f) + { + xModelInstance* mdl; + S32 idx_atmoic = (S32)vertinfo[3]; + + if (idx_atmoic < 1) + { + mdl = this->model; + } + else + { + mdl = this->ModelAtomicFind(idx_atmoic, -1, NULL); + } + + if (mdl) + { + iModelTagSetup(&cfg->tag_vert[i], mdl->Data, tmp_pos.x, tmp_pos.y, tmp_pos.z); + + if (xVec3Length2(&tmp_pos)) + { + cfg->flg_vert |= (1 << i); + } + + xSceneID2Name(globals.sceneCur, this->id); + } + } + } + } +} + +S32 zNPCCommon::GetVertPos(en_mdlvert vid, xVec3* pos) +{ + NPCConfig* cfg = cfg_npc; + if (!(cfg->flg_vert & 1 << vid)) + { + return 0; + } + iModelTagEval(model->Data, &cfg->tag_vert[vid], model->Mat, pos); + return 1; +} + +S32 zNPCCommon::IsAttackFrame(F32 tym_anim, S32 series) +{ + S32 result = 0; + NPCConfig* cfg = this->cfg_npc; + + if (!cfg) + return 0; + + xVec3 var_30, var_3C, var_48; + + switch (series) + { + case 0: + case 1: + var_30 = cfg->animFrameRange[0]; + var_3C = cfg->animFrameRange[1]; + var_48 = cfg->animFrameRange[2]; + break; + case 2: + var_30 = cfg->animFrameRange[3]; + var_3C = cfg->animFrameRange[4]; + var_48 = cfg->animFrameRange[5]; + break; + default: + var_30 = cfg->animFrameRange[6]; + var_3C = cfg->animFrameRange[7]; + var_48 = cfg->animFrameRange[8]; + break; + } + + F32 tym = (tym_anim < 0.0f) ? this->AnimTimeCurrent() : tym_anim; + + if (tym >= var_30.x && tym <= var_30.y) + { + result = 1; + } + else if (tym >= var_3C.x && tym <= var_3C.y) + { + result = 2; + } + else if (tym >= var_48.x && tym <= var_48.y) + { + result = 3; + } + + return result; +} + +void zNPCCommon::GiveReward() +{ + S32 i; + U32 s; + NPCConfig* cfg = this->cfg_npc; + U32 shinies[5]; + S32 esteem; + + if (this->SelfType() != NPC_TYPE_ARFDOG) + { + if ((this->SelfType() & 0xFFFFFF00) == 'NTT\0') + { + zCombo_Add(1); + } + else + { + zCombo_Add(3); + } + } + + for (i = 0, s = 0; i < 5; i++) + { + esteem = cfg->cnt_esteem[i]; + if (esteem >= 1) + { + if (esteem == globals.player.g.ShinyValuePurple) + { + shinies[s] = 0; + s++; + } + else if (esteem == globals.player.g.ShinyValueBlue) + { + shinies[s] = 1; + s++; + } + else if (esteem == globals.player.g.ShinyValueGreen) + { + shinies[s] = 2; + s++; + } + else if (esteem == globals.player.g.ShinyValueYellow) + { + shinies[s] = 3; + s++; + } + else if (esteem == globals.player.g.ShinyValueRed) + { + shinies[s] = 4; + s++; + } + else + { + shinies[i] = 4; + s++; + break; + } + } + } + + if (s) + { + zEntPickup_SpawnNRewards(shinies, s, *this->Pos()); + } + + this->PlayerKiltMe(); +} + +void zNPCPlyrSnd_Reset() +{ + g_tmr_talkless = 10.0f; +} + +void zNPCPlyrSnd_Update(F32 dt) +{ + g_tmr_talkless = MAX(-1.0f, g_tmr_talkless - dt); +} + +void zNPCCommon::PlayerKiltMe() +{ + en_xEventTags r30 = eEventUnknown; + S32 r31 = this->SelfType(); + + if (!SomethingWonderful() && g_tmr_talkless < 0.0f) + { + g_tmr_talkless = 3.0f + (xurand() - 0.5f) * 0.25f * 3.0f; + + if ((r31 & 0xFFFFFF00) == 'NTR\0') + { + r30 = eEventSituationDestroyedRobot; + } + else if ((r31 & 0xFFFFFF00) == 'NTT\0') + { + r30 = eEventSituationDestroyedTiki; + } + + if (r30 != eEventUnknown) + { + zEntEvent(this, &globals.player.ent, r30); + } + } +} + +void zNPCCommon::ISeePlayer() //NONMATCH("https://decomp.me/scratch/M08oY") +{ + en_xEventTags ven = eEventUnknown; + + if (!SomethingWonderful() && g_tmr_talkless < 0.0f) + { + g_tmr_talkless = 3.0f + (xurand() - 0.5f) * 0.25f * 3.0f; + + switch (this->SelfType()) + { + //case NPC_TYPE_UNKNOWN: + //case NPC_TYPE_BASIC: + //case NPC_TYPE_COMMON: + case NPC_TYPE_AMBIENT: + case NPC_TYPE_VILLAGER: + case NPC_TYPE_ROBOT: + case NPC_TYPE_BOSS: + case NPC_TYPE_TEST: + case NPC_TYPE_BADGUY: + case NPC_TYPE_JELLYPINK: + case NPC_TYPE_JELLYBLUE: + case NPC_TYPE_KINGNEPTUNE: + case NPC_TYPE_MIMEFISH: + //case NPC_TYPE_COW: + break; + case NPC_TYPE_TIKI_WOOD: + ven = eEventSituationSeeWoodTiki; + break; + case NPC_TYPE_TIKI_LOVEY: + ven = eEventSituationSeeLoveyTiki; + break; + case NPC_TYPE_TIKI_QUIET: + ven = eEventSituationSeeShhhTiki; + break; + case NPC_TYPE_TIKI_THUNDER: + ven = eEventSituationSeeThunderTiki; + break; + case NPC_TYPE_TIKI_STONE: + ven = eEventSituationSeeStoneTiki; + break; + case NPC_TYPE_FISH: + //case NPC_TYPE_FISH_MALE: + //case NPC_TYPE_FISH_FEMALE: + //case NPC_TYPE_FISH_ELDER: + //case NPC_TYPE_FISH_ELDESS: + //case NPC_TYPE_FISH_BOY: + //case NPC_TYPE_FISH_GIRL: + //case NPC_TYPE_BALLOONBOY: + //case NPC_TYPE_GARY: + //case NPC_TYPE_SQUIDWARD: + case NPC_TYPE_SQUIDWARD_MUSIC: + case NPC_TYPE_SQUIDWARD_BANDAID: + //case NPC_TYPE_DUTCHMAN_NSB: + //case NPC_TYPE_SANDYBIKINI: + //case NPC_TYPE_SANDYNPC: + //case NPC_TYPE_PATNPC: + //case NPC_TYPE_BOBNPC: + //case NPC_TYPE_PLANKNPC: + //case NPC_TYPE_MRKRABS: + //case NPC_TYPE_MSPUFFS: + //case NPC_TYPE_LARRY: + //case NPC_TYPE_BUBBUDDY: + //case NPC_TYPE_NEWSFISH: + case NPC_TYPE_NEWSFISHTV: + //case NPC_TYPE_MOTORIST: + //case NPC_TYPE_MERMANCHAIR: + //case NPC_TYPE_MERMAN: + case NPC_TYPE_BARNACLEBOY: + //case NPC_TYPE_WORM: + break; + case NPC_TYPE_HAMMER: + case NPC_TYPE_HAMSPIN: + ven = eEventSituationSeeHammer; + break; + case NPC_TYPE_TARTAR: + ven = eEventSituationSeeTarTar; + break; + case NPC_TYPE_GLOVE: + ven = eEventSituationSeeGLove; + break; + case NPC_TYPE_MONSOON: + ven = eEventSituationSeeMonsoon; + break; + case NPC_TYPE_SLEEPY: + ven = eEventSituationSeeSleepyTime; + break; + case NPC_TYPE_ARFDOG: + break; + case NPC_TYPE_ARFARF: + ven = eEventSituationSeeArf; + break; + case NPC_TYPE_CHUCK: + break; + case NPC_TYPE_TUBELET: + case NPC_TYPE_TUBESLAVE: + ven = eEventSituationSeeTubelets; + break; + case NPC_TYPE_SLICK: + ven = eEventSituationSeeSlick; + break; + case NPC_TYPE_FODDER: + case NPC_TYPE_FODBOMB: + case NPC_TYPE_FODBZZT: + case NPC_TYPE_CHOMPER: + case NPC_TYPE_CRITTER: + ven = eEventSituationSeeFodder; + break; + case NPC_TYPE_DUPLOTRON: + break; + case NPC_TYPE_KINGJELLY: + ven = eEventSituationSeeKingJellyfish; + break; + case NPC_TYPE_DUTCHMAN: + ven = eEventSituationSeeDutchman; + break; + case NPC_TYPE_PRAWN: + ven = eEventSituationSeePrawn; + break; + case NPC_TYPE_BOSSSANDY: + ven = eEventSituationSeeSandyBoss; + break; + case NPC_TYPE_BOSSPAT: + ven = eEventSituationSeePatrickBoss; + break; + case NPC_TYPE_BOSS_SB1: + case NPC_TYPE_BOSSBOBBY: + ven = eEventSituationSeeSpongeBobBoss; + break; + case NPC_TYPE_BOSSPLANKTON: + ven = eEventSituationSeeRobotPlankton; + break; + //case NPC_TYPE_BADGUYMEDIUM: + // break; + } + + if (ven != eEventUnknown) + { + zEntEvent(this, &globals.player.ent, ven); + } + } +} + +void zNPCCommon::ConfigSceneDone() +{ + g_ncfghead = 0; +} + +NPCConfig* zNPCCommon::ConfigCreate(U32 modelID) +{ + NPCConfig* cfg = (NPCConfig*)xMemAllocSize(sizeof(NPCConfig)); + memset(cfg, 0, sizeof(NPCConfig)); + + cfg->modelID = modelID; + + if (!g_ncfghead) + { + g_ncfghead = cfg; + } + else + { + cfg->Insert(g_ncfghead); + } + + return cfg; +} + +NPCConfig* zNPCCommon::ConfigFind(U32 modelID) +{ + NPCConfig* cfg = NULL; + + for (NPCConfig* r3 = g_ncfghead; r3 != NULL; r3 = r3->Next()) + { + if (r3->modelID == modelID) + { + cfg = r3; + break; + } + } + + return cfg; +} + +void zNPCCommon::GetParm(en_npcparm pid, S32* val) +{ + this->GetParm(pid, (void*)val); +} + +void zNPCCommon::GetParm(en_npcparm pid, F32* val) +{ + this->GetParm(pid, (void*)val); +} + +void zNPCCommon::GetParm(en_npcparm pid, xVec3* val) +{ + this->GetParm(pid, (void*)val); +} + +void zNPCCommon::GetParm(en_npcparm pid, zMovePoint** val) +{ + this->GetParm(pid, (void*)val); +} + +void zNPCCommon::GetParm(en_npcparm pid, void* val) //NONMATCH("https://decomp.me/scratch/dg7eV") +{ + char** names = g_strz_params; + xModelAssetParam* pmdata = this->parmdata; + U32 pmsize = this->pdatsize; + + this->GetParmDefault(pid, val); + + switch (pid) + { + case NPC_PARM_BND_ISBOX: + case NPC_PARM_HITPOINTS: + case NPC_PARM_ESTEEM_A: + case NPC_PARM_ESTEEM_B: + case NPC_PARM_ESTEEM_C: + case NPC_PARM_ESTEEM_D: + case NPC_PARM_ESTEEM_E: + case NPC_PARAM_TEST_COUNT: + if (pmdata && pmsize) + { + *(S32*)val = zParamGetInt(pmdata, pmsize, names[pid], *(S32*)val); + } + break; + case NPC_PARM_MOVERATE: + case NPC_PARM_TURNRATE: + case NPC_PARM_ACCEL: + case NPC_PARM_DRIFT: + case NPC_PARM_MASS: + case NPC_PARM_TOSSGRAV: + case NPC_PARM_TOSSELASTIC: + case NPC_PARM_DETECT_RAD: + case NPC_PARM_DETECT_HYT: + case NPC_PARM_DETECT_OFF: + case NPC_PARM_ATTACK_RAD: + case NPC_PARM_ATTACK_FOV: + case NPC_PARM_SND_RAD: + case NPC_PARM_TIMEFIDGET: + case NPC_PARM_TIMEATTACK: + case NPC_PARM_TIMESTUN: + case NPC_PARM_TIMEALERT: + case NPC_PARM_ATK_SIZE01: + case NPC_PARM_SHADOW_CASTDIST: + case NPC_PARM_SHADOW_RADCACHE: + case NPC_PARM_SHADOW_RADRASTER: + if (pmdata && pmsize) + { + *(F32*)val = zParamGetFloat(pmdata, pmsize, names[pid], *(F32*)val); + } + break; + case NPC_PARM_BND_CENTER: + case NPC_PARM_BND_EXTENT: + case NPC_PARM_MODELSCALE: + case NPC_PARM_ATK_FRAMES01: + case NPC_PARM_ATK_FRAMES01A: + case NPC_PARM_ATK_FRAMES01B: + case NPC_PARM_ATK_FRAMES02: + case NPC_PARM_ATK_FRAMES02A: + case NPC_PARM_ATK_FRAMES02B: + case NPC_PARM_ATK_FRAMES03: + case NPC_PARM_ATK_FRAMES03A: + case NPC_PARM_ATK_FRAMES03B: + if (pmdata && pmsize) + { + zParamGetVector(pmdata, pmsize, names[pid], *(xVec3*)val, (xVec3*)val); + } + break; + case NPC_PARM_VTX_ATTACKBASE: + case NPC_PARM_VTX_ATTACK: + case NPC_PARM_VTX_ATTACK1: + case NPC_PARM_VTX_ATTACK2: + case NPC_PARM_VTX_ATTACK3: + case NPC_PARM_VTX_ATTACK4: + case NPC_PARM_VTX_EYEBALL: + case NPC_PARM_VTX_DMGSMOKEA: + case NPC_PARM_VTX_DMGSMOKEB: + case NPC_PARM_VTX_DMGSMOKEC: + case NPC_PARM_VTX_DMGFLAMEA: + case NPC_PARM_VTX_DMGFLAMEB: + case NPC_PARM_VTX_DMGFLAMEC: + case NPC_PARM_VTX_PROPEL: + case NPC_PARM_VTX_EXHAUST: + case NPC_PARM_VTX_GEN01: + case NPC_PARM_VTX_GEN02: + case NPC_PARM_VTX_GEN03: + case NPC_PARM_VTX_GEN04: + case NPC_PARM_VTX_GEN05: + zParamGetFloatList(pmdata, pmsize, names[pid], 4, NULL, (F32*)val); + break; + case NPC_PARM_FIRSTMVPT: + if (this->npcass->movepoint) + { + zMovePoint* mvpt = zMovePoint_From_xAssetID(this->npcass->movepoint); + if (mvpt) + { + *(zMovePoint**)val = mvpt; + } + } + break; + case NPC_PARM_BOGUSSHARE: + if (pmdata && pmsize) + { + *(S32*)val = zParamGetInt(pmdata, pmsize, names[pid], *(S32*)val); + } + break; + case NPC_PARM_NONE: + case NPC_PARM_ENDTAG_INI: + case NPC_PARM_ENDTAG_PROPS: + case NPC_PARM_ENDTAG_SHARE: + break; + } +} + +void zNPCCommon::GetParmDefault(en_npcparm pid, void* val) +//NONMATCH("https://decomp.me/scratch/mNHLS") +{ + // Should be a S32? + S32 result = 1; + S32* ivp = (S32*)val; + F32* fvp = (F32*)val; + F32* fvlist = (F32*)val; + xVec3* vec = (xVec3*)val; + zMovePoint** mvpt = (zMovePoint**)val; + + switch (pid) + { + case NPC_PARM_HITPOINTS: + *ivp = 1; + break; + case NPC_PARM_MOVERATE: + *fvp = 2.0f; + break; + case NPC_PARM_TURNRATE: + *fvp = 90.0f; + break; + case NPC_PARM_ACCEL: + *fvp = 1.0f; + break; + case NPC_PARM_DRIFT: + *fvp = 0.0f; + break; + case NPC_PARM_MASS: + *fvp = 1.0f; + break; + case NPC_PARM_TOSSGRAV: + *fvp = 25.0f; + break; + case NPC_PARM_TOSSELASTIC: + *fvp = 0.5f; + break; + case NPC_PARM_BND_ISBOX: + *ivp = 0; + break; + case NPC_PARM_BND_CENTER: + xVec3Copy(vec, &g_O3); + break; + case NPC_PARM_BND_EXTENT: + xVec3Copy(vec, &g_O3); + break; + case NPC_PARM_DETECT_RAD: + *fvp = 3.0f; + break; + case NPC_PARM_DETECT_HYT: + *fvp = 6.0f; + break; + case NPC_PARM_DETECT_OFF: + *fvp = -2.0f; + break; + case NPC_PARM_ATTACK_RAD: + *fvp = 5.0f; + break; + case NPC_PARM_ATTACK_FOV: + *fvp = 60.0f; + break; + case NPC_PARM_SND_RAD: + *fvp = 30.0f; + break; + case NPC_PARM_ESTEEM_A: + case NPC_PARM_ESTEEM_B: + case NPC_PARM_ESTEEM_C: + case NPC_PARM_ESTEEM_D: + case NPC_PARM_ESTEEM_E: + *ivp = 0; + break; + case NPC_PARM_SHADOW_CASTDIST: + case NPC_PARM_SHADOW_RADCACHE: + case NPC_PARM_SHADOW_RADRASTER: + *fvp = -1.0f; + break; + case NPC_PARM_ATK_SIZE01: + *fvp = 0.5f; + break; + case NPC_PARM_ATK_FRAMES01: + vec->x = -1.0f; + vec->y = 100.0f; + vec->z = 0.0f; + break; + case NPC_PARM_ATK_FRAMES01A: + case NPC_PARM_ATK_FRAMES01B: + case NPC_PARM_ATK_FRAMES02: + case NPC_PARM_ATK_FRAMES02A: + case NPC_PARM_ATK_FRAMES02B: + case NPC_PARM_ATK_FRAMES03: + case NPC_PARM_ATK_FRAMES03A: + case NPC_PARM_ATK_FRAMES03B: + vec->x = -1.0f; + vec->y = -2.0f; + vec->z = 0.0f; + break; + case NPC_PARM_MODELSCALE: + xVec3Copy(vec, &g_O3); + break; + case NPC_PARM_TIMEFIDGET: + *fvp = 15.0f; + break; + case NPC_PARM_TIMEATTACK: + *fvp = 15.0f; + break; + case NPC_PARM_TIMESTUN: + *fvp = 5.0f; + break; + case NPC_PARM_TIMEALERT: + *fvp = 5.0f; + break; + case NPC_PARAM_TEST_COUNT: + *ivp = 1; + break; + case NPC_PARM_VTX_ATTACKBASE: + case NPC_PARM_VTX_ATTACK: + case NPC_PARM_VTX_ATTACK1: + case NPC_PARM_VTX_ATTACK2: + case NPC_PARM_VTX_ATTACK3: + case NPC_PARM_VTX_ATTACK4: + case NPC_PARM_VTX_EYEBALL: + case NPC_PARM_VTX_DMGSMOKEA: + case NPC_PARM_VTX_DMGSMOKEB: + case NPC_PARM_VTX_DMGSMOKEC: + case NPC_PARM_VTX_DMGFLAMEA: + case NPC_PARM_VTX_DMGFLAMEB: + case NPC_PARM_VTX_DMGFLAMEC: + case NPC_PARM_VTX_PROPEL: + case NPC_PARM_VTX_EXHAUST: + case NPC_PARM_VTX_GEN01: + case NPC_PARM_VTX_GEN02: + case NPC_PARM_VTX_GEN03: + case NPC_PARM_VTX_GEN04: + case NPC_PARM_VTX_GEN05: + fvlist[0] = 0.0f; + fvlist[1] = 0.0f; + fvlist[2] = 0.0f; + fvlist[3] = 0.0f; + break; + case NPC_PARM_FIRSTMVPT: + *mvpt = NULL; + break; + case NPC_PARM_ENDTAG_INI: + case NPC_PARM_ENDTAG_PROPS: + case NPC_PARM_ENDTAG_SHARE: + result = 0; + break; + default: + result = 0; + break; + } + + //return result; +} + +void zNPCCommon_WonderReset() +{ + g_isConversation = 0; + g_flg_wonder = 0; +} + +U32 zNPCCommon::CanDoSplines() +{ + bool retval = false; + if ((npcset.useNavSplines) && ((flg_move)&8)) + { + retval = true; + } + return retval; +} + +zMovePoint* zNPCCommon::FirstAssigned() +{ + zMovePoint* nav_first = NULL; + zNPCCommon::GetParm(NPC_PARM_FIRSTMVPT, &nav_first); + return nav_first; +} + +void zNPCCommon::MvptReset(zMovePoint* nav_goto) //NONMATCH("https://decomp.me/scratch/lhUW9") +{ + if (nav_goto) + { + this->nav_dest = nav_goto; + } + else + { + this->GetParm(NPC_PARM_FIRSTMVPT, &this->nav_dest); + } + + this->nav_past = this->nav_dest; + this->nav_curr = this->nav_dest; + this->nav_lead = this->nav_dest; + this->spl_mvptspline = NULL; + this->len_mvptspline = 0.0f; + this->dst_curspline = 0.0f; +} + +S32 zNPCCommon::MvptCycle() //NONMATCH("https://decomp.me/scratch/CGooj") +{ + zMovePoint* nav_tmp = NULL; + + this->spl_mvptspline = NULL; + this->len_mvptspline = 0.0f; + this->dst_curspline = 0.0f; + + if (!this->nav_curr) + { + this->GetParm(NPC_PARM_FIRSTMVPT, &this->nav_curr); + nav_tmp = this->nav_curr; + } + else if (this->nav_curr && this->nav_dest) + { + zMovePointGetNext(this->nav_dest, this->nav_curr, &nav_tmp, NULL); + } + else if (this->nav_curr) + { + zMovePointGetNext(this->nav_curr, NULL, &nav_tmp, NULL); + } + + if (this->nav_curr) + { + this->nav_past = this->nav_curr; + } + + if (this->nav_dest) + { + this->nav_curr = this->nav_dest; + } + + this->nav_dest = nav_tmp; + + if (this->CanDoSplines() && this->nav_dest && this->nav_dest->HasSpline()) + { + this->spl_mvptspline = this->nav_dest->spl; + + while (this->nav_dest->asset->bezIndex != 0) + { + this->nav_dest = (zMovePoint*)this->nav_dest->nodes[0]; + } + + this->len_mvptspline = xSpline3_ArcTotal(this->spl_mvptspline); + } + + if (this->nav_dest) + { + zMovePointGetNext(this->nav_dest, this->nav_curr, &this->nav_lead, NULL); + } + + return (this->nav_dest != NULL); +} + +S32 zNPCCommon::HaveLOSToPos(xVec3* pos, F32 dist, xScene* xscn, xBase* tgt, xCollis* colCallers) +//NONMATCH("https://decomp.me/scratch/wxm2S") +{ + S32 result; + xRay3 ray = {}; + xVec3 mypos = {}; + + xCollis* colrec; + if (colCallers) + { + colrec = colCallers; + } + else + { + static xCollis localCollis = { k_HIT_0xF00 | k_HIT_CALC_HDNG }; + memset(&localCollis, 0, sizeof(localCollis)); + localCollis.flags = k_HIT_0xF00 | k_HIT_CALC_HDNG; + + colrec = &localCollis; + } + + this->DBG_PStatCont((en_npcperf)1); + this->DBG_PStatOn((en_npcperf)2); + + if (!this->GetVertPos(NPC_MDLVERT_LOSEYEBALL, &mypos)) + { + xVec3Copy(&mypos, xEntGetCenter(this)); + } + + NPCC_xBoundAway(&this->bound); + + ray.min_t = 0.0f; + ray.max_t = dist; + xVec3Sub(&ray.dir, pos, &mypos); + xVec3Normalize(&ray.dir, &ray.dir); + xVec3Copy(&ray.origin, &mypos); + ray.flags = XRAY3_USE_MIN | XRAY3_USE_MAX; + xRayHitsScene(xscn, &ray, colrec); + + NPCC_xBoundBack(&this->bound); + + if (!(colrec->flags & k_HIT_IT)) + { + result = 1; + } + else if (colrec->dist > dist) + { + result = 1; + } + else if (tgt && colrec->oid) + { + if (tgt->id == colrec->oid) + { + result = 1; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + + if (this->DBG_IsNormLog((en_npcdcat)13, 2)) + { + if (result) + { + xDrawSetColor(g_GREEN); + } + else + { + xDrawSetColor(g_NEON_BLUE); + } + + xDrawLine(&mypos, pos); + } + + this->DBG_PStatCont((en_npcperf)2); + this->DBG_PStatOn((en_npcperf)1); + + return result; +} + +xModelInstance* zNPCCommon::ModelAtomicHide(S32 index, xModelInstance* mdl) +{ + xModelInstance* minst = mdl ? mdl : this->ModelAtomicFind(index, -1, NULL); + if (!minst) + return minst; + + minst->Flags &= ~0x1; + + return minst; +} + +xModelInstance* zNPCCommon::ModelAtomicShow(S32 index, xModelInstance* mdl) +{ + xModelInstance* minst = mdl ? mdl : this->ModelAtomicFind(index, -1, NULL); + if (!minst) + return minst; + + minst->Flags |= 0x2; + minst->Flags |= 0x1; + + return minst; +} + +xModelInstance* zNPCCommon::ModelAtomicFind(S32 index, S32 idx_prev, xModelInstance* mdl_prev) +{ + xModelInstance* da_atomic = NULL; + xModelInstance* minst = mdl_prev; + S32 midx = 0; + + if (minst && idx_prev >= 0) + { + midx = idx_prev; + } + else + { + minst = this->model; + } + + while (minst) + { + if (midx == index) + { + da_atomic = minst; + break; + } + minst = minst->Next; + midx++; + } + + return da_atomic; +} + +void zNPCCommon::ModelScaleSet(F32 x, F32 y, F32 z) +{ + xModelInstance* minst = this->model; + + while (minst) + { + minst->Scale.x = x; + minst->Scale.y = y; + minst->Scale.z = z; + minst = minst->Next; + } +} + +S32 zNPCCommon::AnimStart(U32 animID, S32 forceRestart) +{ + static S32 dumptable = 0; + if (dumptable) + { + dumptable = 0; + this->AnimGetTable(); + } + + xAnimState* r3_ = this->AnimCurState(); + if (r3_ && r3_->ID == animID && !forceRestart) + { + return r3_->ID; + } + + xAnimTable* r3 = this->AnimGetTable(); + xAnimTransition* da_tran = r3->TransitionList; + + while (da_tran) + { + if (da_tran->Dest->ID == animID) + { + break; + } + da_tran = da_tran->Next; + } + + if (da_tran) + { + xAnimPlayStartTransition(this->model->Anim, da_tran); + } + else + { + xSceneID2Name(globals.sceneCur, this->id); + } + + if (da_tran) + { + return da_tran->Dest->ID; + } + + return 0; +} + +void zNPCCommon::AnimSetState(U32 animID, F32 time) +{ + xAnimTable* r3 = this->AnimGetTable(); + xAnimState* state = xAnimTableGetStateID(r3, animID); + + xAnimPlaySetState(this->model->Anim->Single, state, time); +} + +xAnimState* zNPCCommon::AnimFindState(U32 animID) +{ + xAnimTable* r3 = this->AnimGetTable(); + return xAnimTableGetStateID(r3, animID); +} + +xAnimSingle* zNPCCommon::AnimCurSingle() +{ + if (!this->model->Anim && (this->SelfType() & 0xFFFFFF00) == 'NTT\0') + { + return NULL; + } + + return this->model->Anim->Single; +} + +xAnimState* zNPCCommon::AnimCurState() +{ + if (!this->model->Anim && (this->SelfType() & 0xFFFFFF00) == 'NTT\0') + { + return NULL; + } + + return this->model->Anim->Single->State; +} + +zNPCSettings* zNPCSettings_Find(U32 id) +{ + zNPCSettings* set = NULL; + U32 size = 0; + + if (id) + { + set = (zNPCSettings*)xSTFindAsset(id, &size); + } + + if (!set) + { + set = g_dflt_npcsettings; + } + + return set; +} + +void zNPCCommon::Vibrate(F32 ds2_cur, F32 ds2_max) //NONMATCH("https://decomp.me/scratch/A5HIP") +{ + F32 rat = ds2_cur / MAX(ds2_max, 1.0f); + + if (rat < 0.4f) + { + this->Vibrate(NPC_VIBE_HARD, -1.0f); + } + else if (rat < 0.7f) + { + this->Vibrate(NPC_VIBE_NORM, -1.0f); + } + else if (rat < 1.0f) + { + this->Vibrate(NPC_VIBE_SOFT, -1.0f); + } +} + +void zNPCCommon::Vibrate(en_npcvibe vibe, F32 duration) +{ + _tagRumbleType typ_rum; + F32 tym_rum; + + switch (vibe) + { + case NPC_VIBE_BUILD_A: + tym_rum = 0.05f; + typ_rum = eRumble_Light; + break; + case NPC_VIBE_BUILD_B: + tym_rum = 0.05f; + typ_rum = eRumble_Medium; + break; + case NPC_VIBE_BUILD_C: + tym_rum = 0.05f; + typ_rum = eRumble_Heavy; + break; + case NPC_VIBE_SOFT: + tym_rum = 0.1f; + typ_rum = eRumble_Light; + break; + case NPC_VIBE_NORM: + tym_rum = 0.25f; + typ_rum = eRumble_Medium; + break; + case NPC_VIBE_HARD: + tym_rum = 0.45f; + typ_rum = eRumble_Heavy; + break; + default: + tym_rum = 0.0f; + typ_rum = eRumble_Medium; + zPadAddRumble(eRumble_Medium, 0.4f, 0, 0); + break; + } + + if (duration > 0.0f) + { + tym_rum = duration; + } + //return retval; +} + +U32 zNPCCommon::AnimCurStateID() +{ + xAnimState* state = AnimCurState(); + if (state != NULL) + { + return state->ID; + } + else + { + return 0; + } +} + +F32 zNPCCommon::AnimDuration(xAnimState* ast) +{ + if (ast == 0) + { + ast = AnimCurState(); + } + return (ast == 0) ? 0.0f : ast->Data->Duration; +} + +F32 zNPCCommon::AnimTimeRemain(xAnimState* ast) +{ + return (AnimDuration(ast) - AnimTimeCurrent()); +} + +F32 zNPCCommon::AnimTimeCurrent() +{ + return model->Anim->Single->Time; +} + +void zNPCSettings_MakeDummy() //NONMATCH("https://decomp.me/scratch/amPtL") +{ + static zNPCSettings dum; + dum.id = 0xFEEDCAFE; + dum.baseType = eBaseTypeNPCSettings; + dum.linkCount = 0; + dum.baseFlags = ((U16)(1 << 0)); + dum.type = 0xBAD0CACA; + dum.version = 2; + dum.handle = 0; + dum.basisType = NPCP_BASIS_NONE; + dum.allowDetect = 1; + dum.allowWander = 1; + dum.allowPatrol = 1; + dum.reduceCollide = 0; + dum.useNavSplines = 1; + dum.allowChase = 1; + dum.allowAttack = 1; + dum.assumeLOS = 0; + dum.assumeFOV = 0; + dum.duploWaveMode = NPCP_DUPOWAVE_CONTINUOUS; + dum.duploSpawnLifeMax = -1; + dum.duploSpawnDelay = 5.0f; + + g_dflt_npcsettings = &dum; } xVec3* zNPCCommon::MatPosSet(xVec3* pos) diff --git a/src/SB/Game/zNPCTypeCommon.h b/src/SB/Game/zNPCTypeCommon.h index 9ce4342c6..36794fdec 100644 --- a/src/SB/Game/zNPCTypeCommon.h +++ b/src/SB/Game/zNPCTypeCommon.h @@ -8,10 +8,21 @@ #include "xBehaveMgr.h" #include "xEnt.h" #include "xSFX.h" +#include "xstransvc.h" +#include "xDraw.h" #include "zNPCSndTable.h" #include "zMovePoint.h" #include "zShrapnel.h" +#include "zNPCMessenger.h" +#include "zNPCGoals.h" +#include "zGrid.h" +#include "iCollide.h" +#include "zEntButton.h" +#include "zCombo.h" + +#define XRAY3_USE_MIN (1 << 10) +#define XRAY3_USE_MAX (1 << 11) typedef struct NPCMsg; @@ -401,7 +412,10 @@ struct zNPCCommon : xNPCBasic //Size of zNPCCommon: 0x2A0 F32 ThrottleAccel(F32 dt, S32 speedup, F32 pct_max); F32 ThrottleAdjust(F32 dt, F32 spd_want, F32 accel); - F32 BoundAsRadius(int useCfg); + void InitBounds(); + F32 BoundAsRadius(int useCfg) const; + void ConvertHitEvent(xBase* from, xBase* to, U32 toEvent, const F32* toParam, + xBase* toParamWidget, S32* handled); void VelStop(); static void ConfigSceneDone(); U32 LassoInit(); @@ -418,6 +432,7 @@ struct zNPCCommon : xNPCBasic //Size of zNPCCommon: 0x2A0 bool IsMountableType(en_ZBASETYPE type); void MvptReset(zMovePoint* nav_goto); S32 MvptCycle(); + void TagVerts(); S32 HaveLOSToPos(xVec3*, float, xScene*, xBase*, xCollis*); void ModelScaleSet(F32 x, F32 y, F32 z); void ModelScaleSet(F32 unk); @@ -426,18 +441,33 @@ struct zNPCCommon : xNPCBasic //Size of zNPCCommon: 0x2A0 xModelInstance* ModelAtomicHide(int index, xModelInstance* mdl); xModelInstance* ModelAtomicShow(int index, xModelInstance* mdl); S32 AnimStart(U32 animID, S32 forceRestart); + void AnimSetState(U32 animID, F32 time); xAnimState* AnimFindState(U32 animID); xAnimState* AnimCurState(); xAnimSingle* AnimCurSingle(); U32 AnimCurStateID(); + void ISeePlayer(); + NPCConfig* ConfigCreate(U32 modelID); + NPCConfig* ConfigFind(U32 modelID); + void GetParm(en_npcparm pid, S32* val); + void GetParm(en_npcparm pid, F32* val); + void GetParm(en_npcparm pid, xVec3* val); + void GetParm(en_npcparm pid, zMovePoint** val); + S32 HasSpline(); U32 CanDoSplines(); + S32 IsAttackFrame(F32 tym_anim, S32 series); void GiveReward(); + void PlayerKiltMe(); S32 SndPlayFromSFX(xSFX* sfx, U32* sid_played); S32 SndPlayRandom(en_NPC_SOUND sndtype); + //U32 SndStart(U32 aid_toplay, NPCSndProp* sprop, F32 radius); S32 SndChanIsBusy(S32 flg_chan); + void SndKillSounds(S32 flg_chan, S32 all); + S32 SndQueUpdate(F32 dt); S32 LassoUseGuides(S32 idx_grabmdl, S32 idx_holdmdl); S32 GetVertPos(en_mdlvert vid, xVec3* pos); void Vibrate(en_npcvibe vibe, F32 duration); + void Vibrate(F32 vibe, F32 duration); void AddScripting(xPsyche* psy, S32 (*eval_script)(xGoal*, void*, en_trantype*, F32, void*), S32 (*eval_playanim)(xGoal*, void*, en_trantype*, F32, void*), S32 (*eval_attack)(xGoal*, void*, en_trantype*, F32, void*), @@ -646,10 +676,33 @@ void NPCC_BuildStandardAnimTran(xAnimTable* table, char** namelist, S32* ourAnim F32 blend); void zNPCCommon_Timestep(xScene* xscn, F32 dt); +xFactoryInst* ZNPC_Create_Common(S32 who, RyzMemGrow* grow, void*); +void ZNPC_Destroy_Common(xFactoryInst* inst); +void ZNPC_Common_Startup(); +void ZNPC_Common_Shutdown(); +void zNPCCommon_ScenePrepare(); +void zNPCCommon_SceneFinish(); +void zNPCCommon_SceneReset(); +void zNPCCommon_ScenePostInit(); +void zNPCCommon_Timestep(xScene*, F32 dt); +void zNPCSettings_MakeDummy(); +zNPCSettings* zNPCSettings_Find(U32); +S32 NPCC_NPCIsConversing(); +void zNPCCommon_WonderReset(); xAnimTable* ZNPC_AnimTable_Common(); xAnimTable* ZNPC_AnimTable_LassoGuide(); -S32 NPCC_NPCIsConversing(); +void NPCC_BuildStandardAnimTran(xAnimTable* table, char** namelist, S32* ourAnims, S32 idx_dflt, + F32 blend); void zNPCCommon_EjectPhlemOnPawz(); U32 xSndIsPlaying(U32 assetID, U32 parid); +// move to messenger? + +void zNPCMsg_AreaNotify(zNPCCommon* sender, en_NPC_MSG_ID msgid, F32 rad, S32 filter, + en_NPCTYPES toNPCType); + +void zNPCMsg_SendMsg(en_NPC_MSG_ID msgevent, zNPCCommon* npc_sendto); +void zNPCMsg_SendMsg(NPCMsg* inmsg, zNPCCommon* npc_sendto); +void zNPCMsg_SendMsg(NPCMsg* inmsg, F32 delay, zNPCCommon* npc_sendto); + #endif From 17f6f58842c775502ba7fb784c5626619b2336a2 Mon Sep 17 00:00:00 2001 From: Colin Miller Date: Sun, 13 Jul 2025 23:33:03 -0400 Subject: [PATCH 2/3] build fix --- src/SB/Core/x/xEntMotion.cpp | 5 ----- src/SB/Game/zNPCSupport.cpp | 4 ++-- src/SB/Game/zNPCTypeCommon.h | 1 + 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/SB/Core/x/xEntMotion.cpp b/src/SB/Core/x/xEntMotion.cpp index 39a84b177..c294f14c4 100644 --- a/src/SB/Core/x/xEntMotion.cpp +++ b/src/SB/Core/x/xEntMotion.cpp @@ -284,11 +284,6 @@ void xEntMotionMove(xEntMotion*, xScene*, F32, xEntFrame*) { } -F32 xSpline3_ArcTotal(xSpline3*) -{ - return 0.0f; -} - void xQuatCopy(xQuat* a, const xQuat* b) { a->s = b->s; diff --git a/src/SB/Game/zNPCSupport.cpp b/src/SB/Game/zNPCSupport.cpp index 29dfb2eec..7e8fdc0fc 100644 --- a/src/SB/Game/zNPCSupport.cpp +++ b/src/SB/Game/zNPCSupport.cpp @@ -674,9 +674,9 @@ void NPCC_ang_toXZDir(F32 angle, xVec3* dir) dir->z = icos(angle); } -void NPCC_dir_toXZAng(const xVec3* vec) +F32 NPCC_dir_toXZAng(const xVec3* vec) { - xatan2(vec->x, vec->z); + return xatan2(vec->x, vec->z); } void NPCC_aimMiss(xVec3* dir_aim, xVec3* pos_src, xVec3* pos_tgt, F32 dst_miss, xVec3* pos_miss) diff --git a/src/SB/Game/zNPCTypeCommon.h b/src/SB/Game/zNPCTypeCommon.h index 36794fdec..3cbb0481d 100644 --- a/src/SB/Game/zNPCTypeCommon.h +++ b/src/SB/Game/zNPCTypeCommon.h @@ -20,6 +20,7 @@ #include "iCollide.h" #include "zEntButton.h" #include "zCombo.h" +#include "zNPCTypes.h" #define XRAY3_USE_MIN (1 << 10) #define XRAY3_USE_MAX (1 << 11) From 1ee251d9b4f39496e58626a3726209486f2442dc Mon Sep 17 00:00:00 2001 From: Colin Miller Date: Tue, 12 Aug 2025 10:47:12 -0400 Subject: [PATCH 3/3] BossSB2 & BossPlankton --- src/SB/Core/x/xDecal.h | 2 +- src/SB/Core/x/xLaserBolt.h | 35 ++- src/SB/Game/zEntPlayerOOBState.cpp | 29 ++ src/SB/Game/zEntPlayerOOBState.h | 32 +++ src/SB/Game/zNPCTypeBossPlankton.cpp | 401 +++++++++++++++++++++++---- src/SB/Game/zNPCTypeBossPlankton.h | 61 ++-- src/SB/Game/zNPCTypeBossSB2.cpp | 38 ++- src/SB/Game/zNPCTypeBossSB2.h | 21 ++ src/SB/Game/zNPCTypeDutchman.cpp | 10 +- 9 files changed, 529 insertions(+), 100 deletions(-) diff --git a/src/SB/Core/x/xDecal.h b/src/SB/Core/x/xDecal.h index 958d5c34c..1d5a4120b 100644 --- a/src/SB/Core/x/xDecal.h +++ b/src/SB/Core/x/xDecal.h @@ -23,7 +23,7 @@ struct xDecalEmitter struct config { U32 flags; - F32 life_time; + F32 life_time; // 0x2ec U32 blend_src; U32 blend_dst; struct diff --git a/src/SB/Core/x/xLaserBolt.h b/src/SB/Core/x/xLaserBolt.h index c4b96f00d..4e8de6048 100644 --- a/src/SB/Core/x/xLaserBolt.h +++ b/src/SB/Core/x/xLaserBolt.h @@ -42,21 +42,6 @@ enum fx_orient_enum FORCE_INT_FX_ORIENT = 0xffffffff }; -struct bolt -{ - xVec3 origin; - xVec3 dir; - xVec3 loc; - xVec3 hit_norm; - F32 dist; - F32 hit_dist; - F32 prev_dist; - F32 prev_check_dist; - xEnt* hit_ent; - F32 emitted; - void* context; -}; - struct xLaserBoltEmitter { struct config @@ -75,6 +60,8 @@ struct xLaserBoltEmitter F32 damage; }; + struct bolt; + struct static_queue { U32 _first; @@ -104,6 +91,21 @@ struct xLaserBoltEmitter F32 irate; }; + struct bolt + { + xVec3 origin; + xVec3 dir; + xVec3 loc; + xVec3 hit_norm; + F32 dist; + F32 hit_dist; + F32 prev_dist; + F32 prev_check_dist; + xEnt* hit_ent; + F32 emitted; + void* context; + }; + config cfg; static_queue bolts; F32 ialpha; @@ -112,6 +114,7 @@ struct xLaserBoltEmitter effect_data* fx[7]; U32 fxsize[7]; + void init(U32 max_bolts, const char*); void set_texture(char* name); void set_texture(U32 aid); void set_texture(RwTexture* tex); @@ -128,6 +131,8 @@ struct xLaserBoltEmitter RxObjSpace3DVertex* get_vert_buffer(S32& dat); void applyDamage(bolt& b); void reset_fx(fx_when_enum when); + + U32 visible() const; }; #endif diff --git a/src/SB/Game/zEntPlayerOOBState.cpp b/src/SB/Game/zEntPlayerOOBState.cpp index a6510aa52..f1d664b13 100644 --- a/src/SB/Game/zEntPlayerOOBState.cpp +++ b/src/SB/Game/zEntPlayerOOBState.cpp @@ -44,6 +44,20 @@ namespace oob_state tagFixed fixed; xMat4x3 shared_target; + static void reset_camera() + { + // xMat4x3 ma, is not correct. This is a major placeholder. + // Simply have not figured out what the data/global is supposed to be + // FIXME: Correct this function + + xMat4x3* ma = xEntGetFrame(&(xEnt)globals.player.ent); + + zCameraEnableInput(); + zCameraEnableTracking(CO_OOB); + xCameraDoCollisions(1, 6); + zCameraTweakGlobal_Init(); + } + static bool assume_player_is_stupid() { shared.tutorial = (ztalkbox*)zSceneFindObject(xStrHash("OUT_OF_BOUNDS_TLKBX")); @@ -74,6 +88,21 @@ namespace oob_state { } + static void set_rect_vert(rwGameCube2DVertex&, F32, F32, F32, iColor_tag, F32) + { + } + + drop_state_type::substate_enum drop_state_type::supdate_fade_in(drop_state_type&, + xScene& scn, F32& unk0) + { + return drop_state_type::update_fade_in(); + } + + drop_state_type::substate_enum drop_state_type::update_fade_in(xScene&, float&) + { + return SS_MOVING_IN; + } + } // namespace } // namespace oob_state diff --git a/src/SB/Game/zEntPlayerOOBState.h b/src/SB/Game/zEntPlayerOOBState.h index 1bbfdaf08..0562ae88e 100644 --- a/src/SB/Game/zEntPlayerOOBState.h +++ b/src/SB/Game/zEntPlayerOOBState.h @@ -4,6 +4,8 @@ #include "xserializer.h" #include "zTalkBox.h" #include "zTaskBox.h" +#include "zCamera.h" +#include "zCameraTweak.h" extern bool oob_player_teleported; @@ -109,6 +111,36 @@ namespace oob_state void stop(); }; + struct drop_state_type : state_type + { + enum substate_enum + { + SS_MOVING_IN, + SS_STOPPING, + SS_STOPPED, + SS_STARTING, + SS_MOVING_OUT, + SS_START_FADE_IN, + SS_FADE_IN, + MAX_SS, + SS_INVALID = 0xffffffff, + }; + + substate_enum move_substate; + substate_enum fade_substate; + xVec3 player_start; + F32 stop_time; + F32 fade_start_time; + F32 fade_time; + substate_enum (*updatess)(drop_state_type&, xScene&, F32&)[7]; + + void start(); + state_enum update(xScene& s, F32& dt); + substate_enum supdate_fade_in(drop_state_type&, xScene&, F32&); + substate_enum update_fade_in(); + substate_enum update_fade_in(xScene&, F32&); + }; + struct _class_9 { int flags; diff --git a/src/SB/Game/zNPCTypeBossPlankton.cpp b/src/SB/Game/zNPCTypeBossPlankton.cpp index e9099f1ad..7b2a0e13d 100644 --- a/src/SB/Game/zNPCTypeBossPlankton.cpp +++ b/src/SB/Game/zNPCTypeBossPlankton.cpp @@ -59,54 +59,115 @@ namespace U32 flags; }; + struct bolt; + + struct effect_data + { + struct effect_callback + { + void (*fp)(bolt&, void*); + void* context; + }; + + fx_type_enum type; + fx_orient_enum orient; + F32 rate; + union + { + xParEmitter* par; + xDecalEmitter* decal; + effect_callback callback; + }; + F32 irate; + }; + + static effect_data beam_launch_effect[2]; // size: 0x30, address: 0x4E0E60 + static effect_data beam_head_effect[1]; // size: 0x18, address: 0x4E0E90 + static effect_data beam_impact_effect[3]; // size: 0x48, address: 0x4E0EB0 + static effect_data beam_death_effect[1]; // size: 0x18, address: 0x5E53F0 + static effect_data beam_kill_effect[1]; + static U32 sound_asset_ids[6][10]; static sound_data_type sound_data[6]; - static const sound_asset sound_assets[29] = - { - {0, "RSB_foot_loop", 0, 3}, - {0, "fan_loop", 0, 3}, - {0, "Rocket_burn_loop", 0, 3}, - {0, "RP_whirr_loop", 0, 3}, - {0, "RP_whirr2_loop", 0, 3}, - {0, "Glove_hover", 0, 3}, - {0, "Glove_pursuit", 0, 3}, - {1, "Prawn_FF_hit", 0, 0}, - {1, "Prawn_hit", 0, 0}, - {1, "Door_metal_shut", 0, 0}, - {1, "Ghostplat_fall", 0, 0}, - {1, "ST-death", 0, 0}, - {1, "RP_Bwrrzt", 0, 0}, - {1, "RP_chunk", 0, 0}, - {1, "b201_rp_exhale", 0, 0}, - {2, "RP_laser_alt", 0, 0}, - {3, "RP_laser_loop", 0, 1}, - {3, "ElecArc_alt_b", 0, 1}, - {3, "Laser_lrg_fire_loop", 0, 1}, - {3, "Laser_sm_fire_loop", 0, 1}, - {4, "RB_stalact_brk", 0, 0}, - {4, "Volcano_blast", 0, 0}, - {4, "RP_laser_thunk", 0, 0}, - {4, "RP_pfft", 0, 0}, - {4, "RP_thwash", 0, 0}, - {5, "RP_charge_whirr", 0, 0}, - {5, "B101_SC_jump", 0, 0}, - {5, "KJ_Charge", 0, 0}, - {5, "Laser_med_pwrup1", 0, 0} + + static const sound_asset sound_assets[29] = { + { 0, "RSB_foot_loop", 0, 3 }, { 0, "fan_loop", 0, 3 }, + { 0, "Rocket_burn_loop", 0, 3 }, { 0, "RP_whirr_loop", 0, 3 }, + { 0, "RP_whirr2_loop", 0, 3 }, { 0, "Glove_hover", 0, 3 }, + { 0, "Glove_pursuit", 0, 3 }, { 1, "Prawn_FF_hit", 0, 0 }, + { 1, "Prawn_hit", 0, 0 }, { 1, "Door_metal_shut", 0, 0 }, + { 1, "Ghostplat_fall", 0, 0 }, { 1, "ST-death", 0, 0 }, + { 1, "RP_Bwrrzt", 0, 0 }, { 1, "RP_chunk", 0, 0 }, + { 1, "b201_rp_exhale", 0, 0 }, { 2, "RP_laser_alt", 0, 0 }, + { 3, "RP_laser_loop", 0, 1 }, { 3, "ElecArc_alt_b", 0, 1 }, + { 3, "Laser_lrg_fire_loop", 0, 1 }, { 3, "Laser_sm_fire_loop", 0, 1 }, + { 4, "RB_stalact_brk", 0, 0 }, { 4, "Volcano_blast", 0, 0 }, + { 4, "RP_laser_thunk", 0, 0 }, { 4, "RP_pfft", 0, 0 }, + { 4, "RP_thwash", 0, 0 }, { 5, "RP_charge_whirr", 0, 0 }, + { 5, "B101_SC_jump", 0, 0 }, { 5, "KJ_Charge", 0, 0 }, + { 5, "Laser_med_pwrup1", 0, 0 } }; + const size_t beam_ring_curve = 2; + + xVec3* get_player_loc() + { + return (xVec3*)&globals.player.ent.model->Mat->pos; + } S32 init_sound() { return 0; } - F32 get_player_loc() + void reset_sound() + { + for (S32 i = 0; i < 6; ++i) + { + sound_data[i].handle = 0; + } + } + + void* play_sound(int, const xVec3*, float) + { + return NULL; + } + + void* kill_sound(S32, U32) + { + return 0; // to-do + } + + void* kill_sound(S32) { return 0; } - void play_sound(int, const xVec3*, float) + void play_beam_fly_sound(xLaserBoltEmitter::bolt& bolt, void* unk) + { + if (bolt.context == NULL) + { + bolt.context = play_sound(SOUND_BOLT_FLY, &bolt.loc, 1.0f); + } + } + + void kill_beam_fly_sound(xLaserBoltEmitter::bolt& bolt, void* unk) + { + if (bolt.context != NULL) + { + kill_sound(3, (U32)bolt.context); + bolt.context = NULL; + } + } + + void play_beam_fire_sound(xLaserBoltEmitter::bolt& bolt, void* unk) { + play_sound(SOUND_BOLT_FIRE, &bolt.origin, 1.0f); + } + + void play_beam_hit_sound(xLaserBoltEmitter::bolt& bolt, void* unk) + { + play_sound(SOUND_BOLT_HIT, &bolt.loc, 1.0f); } struct config @@ -233,9 +294,17 @@ namespace tweak_callback cb_sound; tweak_callback cb_sound_asset; + void load(xModelAssetParam*, U32); void register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*); }; + static tweak_group tweak; + + void tweak_group::load(xModelAssetParam* ap, U32 apsize) + { + register_tweaks(true, ap, apsize, NULL); + } + void tweak_group::register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*) { xVec3 V0; @@ -829,17 +898,20 @@ namespace if (init) { sound[SOUND_BOLT_FIRE].asset = sound_asset_ids[2][0]; - sound_data[SOUND_BOLT_FIRE].id = xStrHash(sound_assets[sound[SOUND_BOLT_FIRE].asset].name); + sound_data[SOUND_BOLT_FIRE].id = + xStrHash(sound_assets[sound[SOUND_BOLT_FIRE].asset].name); } if (init) { sound[SOUND_BOLT_FLY].asset = sound_asset_ids[3][3]; - sound_data[SOUND_BOLT_FLY].id = xStrHash(sound_assets[sound[SOUND_BOLT_FLY].asset].name); + sound_data[SOUND_BOLT_FLY].id = + xStrHash(sound_assets[sound[SOUND_BOLT_FLY].asset].name); } if (init) { sound[SOUND_BOLT_HIT].asset = sound_asset_ids[4][3]; - sound_data[SOUND_BOLT_HIT].id = xStrHash(sound_assets[sound[SOUND_BOLT_HIT].asset].name); + sound_data[SOUND_BOLT_HIT].id = + xStrHash(sound_assets[sound[SOUND_BOLT_HIT].asset].name); } if (init) { @@ -848,6 +920,18 @@ namespace } } + static void update_move_accel(xVec3& loc, zNPCBPlankton::move_info& move, F32 dt) + { + // Ghidra output, will come back to this later + + // xAccelMove((double)*(float*)(param_3 + 0x18), param_1_00, (double)*(float*)(param_3 + 0x24), + // (float*)this, (float*)(param_3 + 0xc)); + // xAccelMove((double)*(float*)(param_3 + 0x1c), param_1_00, (double)*(float*)(param_3 + 0x28), + // (float*)(this + 4), (float*)(param_3 + 0x10)); + // xAccelMove((double)*(float*)(param_3 + 0x20), param_1_00, (double)*(float*)(param_3 + 0x2c), + // (float*)(this + 8), (float*)(param_3 + 0x14)); + } + } // namespace xAnimTable* ZNPC_AnimTable_BossPlankton() @@ -988,6 +1072,33 @@ void zNPCBPlankton::Init(xEntAsset* asset) //66% zNPCBPlankton::aim_gun(play, &gun_tilt, &move.dest, 0); } +void zNPCBPlankton::Setup() +{ + U32 tmpVar; + + zNPCBoss::Setup(); + zNPCBPlankton::setup_beam(); + tmpVar = xStrHash("NPC_NEWSCASTER"); + newsfish = (zNPCNewsFish*)zSceneFindObject(tmpVar); +} + +void zNPCBPlankton::PostSetup() +{ + xUpdateCull_SetCB(xglobals->updateMgr, NULL, xUpdateCull_AlwaysTrueCB, NULL); +} + +void zNPCBPlankton::Reset() +{ + if (newsfish != 0) + { + } + + zNPCCommon::Reset(); + zNPCBPlankton::reset_beam(); + memset((void*)flag.updated, 0, 0x10); + zNPCBPlankton::face_player(); +} + void zNPCBPlankton::Destroy() { zNPCCommon::Destroy(); @@ -1050,12 +1161,37 @@ void zNPCBPlankton::Process(xScene* xscn, float dt) //Process__10zNPCCommonFP6xScenef(param_1,param_9,param_10); } +S32 zNPCBPlankton::SysEvent(xBase* from, xBase* to, U32 toEvent, const F32* toParam, + xBase* toParamWidget, S32* handled) +{ + *handled = 0; + return zNPCCommon::SysEvent(from, to, toEvent, toParam, toParamWidget, handled); + + // ((zNPCCommon*) 0x1b8??? +} + void zNPCBPlankton::Render() { xNPCBasic::Render(); zNPCBPlankton::render_debug(); } +void zNPCBPlankton::RenderExtraPostParticles() +{ + if ((beam.visible() & 0xff) != 0) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); + beam.render(); + } +} + +void zNPCBPlankton::ParseINI() +{ + zNPCCommon::ParseINI(); + tweak.load(parmdata, pdatsize); +} + void zNPCBPlankton::SelfSetup() { xBehaveMgr* bmgr = xBehaveMgr_GetSelf(); @@ -1168,15 +1304,138 @@ S32 zNPCBPlankton::next_goal() } void zNPCBPlankton::render_debug() +{ + // weak +} + +void zNPCBPlankton::reset_territories() +{ +} + +void zNPCBPlankton::update_animation(F32) { } -void zNPCBPlankton::update_animation(float) +void zNPCBPlankton::update_follow(F32 dt) { + if (flag.follow != 2) + { + update_follow_player(dt); + } + else if ((flag.follow < 2) && (1 > flag.follow)) + { + update_follow_camera(dt); + } +} + +void zNPCBPlankton::check_player_damage() +{ + // TODO +} + +void zNPCBPlankton::init_beam() +{ + beam.init((U32)&beam, "Plankton\'s Beam"); + beam.set_texture("plankton_laser_bolt"); + + beam.refresh_config(); + // + beam_ring.init(0, "Plankton\'s Beam Rings"); + beam_ring.set_curve((xDecalEmitter::curve_node*)&beam_ring.curve, beam_ring_curve); + beam_ring.set_texture("bubble"); + beam_ring.set_default_config(); + beam_ring.cfg.flags = 0; + beam_ring.cfg.life_time = 0.0f; + beam_ring.cfg.blend_src = 5; + beam_ring.cfg.blend_dst = 2; + beam_ring.refresh_config(); + + // + beam_glow.init(0, "Plankton\'s Beam Glow"); + beam_glow.set_curve((xDecalEmitter::curve_node*)&beam_glow.curve, 0); + beam_glow.set_texture("fx_firework"); + beam_glow.set_default_config(); + beam_glow.cfg.flags = 0; + beam_glow.cfg.life_time = 0.0f; + beam_glow.cfg.blend_src = 5; + beam_glow.cfg.blend_dst = 2; + beam_glow.refresh_config(); +} + +void zNPCBPlankton::setup_beam() +{ +} + +void zNPCBPlankton::reset_beam() +{ + beam.reset(); } void zNPCBPlankton::vanish() { + flags = flags & 0xfe; + flags = flags | 0x40; + pflags = 0; + moreFlags = 0; + chkby = 0; + penby = 0; + flags2.flg_colCheck = 0; + flags2.flg_penCheck = 0; + kill_sound(NULL); +} + +void zNPCBPlankton::reappear() +{ + flags = flags | 1; + flags = flags & 0xbf; + pflags = 0; + moreFlags = 16; + chkby = 16; + penby = 16; + flags2.flg_colCheck = 0; + flags2.flg_penCheck = 0; + play_sound(0, (xVec3*)&bound.pad[3], 1.0f); +} + +void zNPCBPlankton::next_territory() +{ + if ((have_cronies() & 0xff) != 0) + { + active_territory = active_territory + 1; + if (active_territory >= territory_size) + { + active_territory = territory_size + -1; + } + } +} + +S32 zNPCBPlankton::have_cronies() const +{ + // FIXME: dunno how to fix this yet + return (active_territory) & (active_territory * 0x3c + 0x4e4) >> 0x1f; +} + +void zNPCBPlankton::sickum() +{ +} + +void zNPCBPlankton::here_boy() +{ + flag.hunt = 0; +} + +void zNPCBPlankton::follow_player() +{ + flag.follow = FOLLOW_PLAYER; + follow.delay = follow.max_delay = 0.0f; + flag.move = MOVE_ORBIT; +} + +void zNPCBPlankton::follow_camera() +{ + flag.follow = FOLLOW_CAMERA; + follow.delay = follow.max_delay = 0.0f; + flag.move = MOVE_ORBIT; } S32 zNPCBPlankton::IsAlive() @@ -1204,16 +1463,64 @@ xFactoryInst* zNPCGoalBPlanktonIdle::create(S32 who, RyzMemGrow* grow, void* inf return new (who, grow) zNPCGoalBPlanktonIdle(who, (zNPCBPlankton&)*info); } +S32 zNPCGoalBPlanktonIdle::Enter(F32 dt, void* ctxt) +{ + F32 tmpFloat; + F32 local_24[3]; + + owner.reappear(); + owner.flag.attacking = false; + owner.refresh_orbit(); + owner.reset_speed(); + owner.flag.follow = owner.FOLLOW_NONE; + get_yaw(tmpFloat, dt); + apply_yaw(tmpFloat); + return zNPCGoalCommon::Enter(dt, ctxt); +} + +S32 zNPCGoalBPlanktonIdle::Exit(F32 dt, void* ctxt) +{ + owner.refresh_orbit(); + return xGoal::Exit(dt, ctxt); +} + xFactoryInst* zNPCGoalBPlanktonAttack::create(S32 who, RyzMemGrow* grow, void* info) { return new (who, grow) zNPCGoalBPlanktonAttack(who, (zNPCBPlankton&)*info); } +S32 zNPCGoalBPlanktonAttack::Enter(F32 dt, void* ctxt) +{ + owner.reappear(); + owner.flag.attacking = true; + owner.refresh_orbit(); + owner.follow_player(); + owner.delay = 0.0f; + owner.face_player(); + owner.reset_speed(); + return zNPCGoalCommon::Enter(dt, ctxt); +} + +S32 zNPCGoalBPlanktonAttack::Exit(F32 dt, void* ctxt) +{ + return xGoal::Exit(dt, ctxt); +} + xFactoryInst* zNPCGoalBPlanktonAmbush::create(S32 who, RyzMemGrow* grow, void* info) { return new (who, grow) zNPCGoalBPlanktonAmbush(who, (zNPCBPlankton&)*info); } +S32 zNPCGoalBPlanktonAmbush::Enter(F32 dt, void* ctxt) +{ + return zNPCGoalCommon::Enter(dt, ctxt); +} + +S32 zNPCGoalBPlanktonAmbush::Exit(F32 dt, void* ctxt) +{ + return xGoal::Exit(dt, ctxt); +} + xFactoryInst* zNPCGoalBPlanktonFlank::create(S32 who, RyzMemGrow* grow, void* info) { return new (who, grow) zNPCGoalBPlanktonFlank(who, (zNPCBPlankton&)*info); @@ -1229,7 +1536,7 @@ xFactoryInst* zNPCGoalBPlanktonHunt::create(S32 who, RyzMemGrow* grow, void* inf return new (who, grow) zNPCGoalBPlanktonHunt(who, (zNPCBPlankton&)*info); } -S32 zNPCGoalBPlanktonHunt::Enter(float dt, void* updCtxt) +S32 zNPCGoalBPlanktonHunt::Enter(F32 dt, void* updCtxt) { owner.reappear(); get_player_loc(); @@ -1241,7 +1548,7 @@ S32 zNPCGoalBPlanktonHunt::Enter(float dt, void* updCtxt) return zNPCGoalCommon::Enter(dt, updCtxt); } -S32 zNPCGoalBPlanktonHunt::Exit(float dt, void* updCtxt) +S32 zNPCGoalBPlanktonHunt::Exit(F32 dt, void* updCtxt) { owner.refresh_orbit(); return xGoal::Exit(dt, updCtxt); @@ -1312,24 +1619,22 @@ xFactoryInst* zNPCGoalBPlanktonBeam::create(S32 who, RyzMemGrow* grow, void* inf S32 zNPCGoalBPlanktonBeam::Enter(float dt, void* updCtxt) { - xParEmitter parE; - xParEmitter& pEmit = parE; // this is one of the codes of all time owner.reappear(); + substate = SS_WARM_UP; owner.delay = 0.0f; - emitted = 0.0f; + emitted = 0; + owner.flag.aim_gun = true; owner.flag.follow = owner.FOLLOW_NONE; - owner.enable_emitter(pEmit); - void play_sound(S32, const xVec3*, F32); // dunno how to get this to call properly + owner.enable_emitter((xParEmitter&)owner.beam_charge); + play_sound(5, (xVec3*)&owner.bound.pad[3], 1.0f); // dunno how to get this to call properly return zNPCGoalCommon::Enter(dt, updCtxt); } S32 zNPCGoalBPlanktonBeam::Exit(float dt, void* updCtxt) { - xParEmitter parE; - xParEmitter& pEmit = parE; // this is one of the codes of all time owner.flag.aim_gun = false; - owner.flag.follow = owner.FOLLOW_NONE; - owner.disable_emitter(pEmit); + owner.flag.follow = owner.FOLLOW_PLAYER; + owner.disable_emitter((xParEmitter&)owner.beam_charge); return xGoal::Exit(dt, updCtxt); } diff --git a/src/SB/Game/zNPCTypeBossPlankton.h b/src/SB/Game/zNPCTypeBossPlankton.h index 075125dad..7836d4a53 100644 --- a/src/SB/Game/zNPCTypeBossPlankton.h +++ b/src/SB/Game/zNPCTypeBossPlankton.h @@ -2,6 +2,7 @@ #define ZNPCTYPEBOSSPLANKTON_H #include "zNPCTypeBoss.h" +#include "zNPCTypeVillager.h" #include "zNPCGoalCommon.h" #include "zEntDestructObj.h" @@ -10,8 +11,7 @@ #include "xTimer.h" #include "zNPCGoals.h" #include "xParEmitter.h" - -struct zNPCNewsFish; +#include "xLaserBolt.h" namespace auto_tweak { @@ -77,8 +77,8 @@ struct zNPCBPlankton : zNPCBoss F32 delay; //0x2c8 xQuat gun_tilt; F32 ambush_delay; - F32 beam_duration; - F32 stun_duration; + F32 beam_duration; // 0x2e0 + F32 stun_duration; // 0x2e4 xDecalEmitter beam_ring; xDecalEmitter beam_glow; xLaserBoltEmitter beam; @@ -115,28 +115,43 @@ struct zNPCBPlankton : zNPCBoss zNPCBPlankton(S32 myType); void Init(xEntAsset*); + void Setup(); + void PostSetup(); + void Reset(); void Destroy(); void Process(xScene*, float); + S32 SysEvent(xBase*, xBase*, unsigned int, const float*, xBase*, int*); void Render(); + void RenderExtraPostParticles(); + void ParseINI(); void SelfSetup(); U32 AnimPick(int, en_NPC_GOAL_SPOT, xGoal*); S32 next_goal(); void render_debug(); - void update_turn(float); - void update_move(float); + void update_turn(F32); + void update_move(F32); void check_player_damage(); - void update_animation(float); - void update_follow(float); - void update_aim_gun(float); - void update_dialog(float); + void reset_territories(); + void update_animation(F32); + void update_follow(F32); + void update_follow_player(F32); + void update_follow_camera(F32); + void update_aim_gun(F32); + void update_dialog(F32); void init_beam(); + void setup_beam(); + void reset_beam(); void vanish(); void reappear(); U32 crony_attacking() const; + void next_territory(); + S32 have_cronies() const; S32 player_left_territory(); void say(int, int, bool); + void sickum(); void aim_gun(xAnimPlay*, xQuat*, xVec3*, int); void here_boy(); + void follow_player(); void follow_camera(); void reset_speed(); void refresh_orbit(); @@ -146,9 +161,10 @@ struct zNPCBPlankton : zNPCBoss U8 ColChkFlags() const; U8 ColChkByFlags() const; - // Not yet organized + // Not yet organized / WEAK void enable_emitter(xParEmitter&) const; void disable_emitter(xParEmitter&) const; + void face_player(); }; struct zNPCGoalBPlanktonIdle : zNPCGoalCommon @@ -160,6 +176,11 @@ struct zNPCGoalBPlanktonIdle : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); + S32 Enter(F32, void*); + S32 Exit(F32, void*); + + S32 get_yaw(F32&, F32&) const; + S32 apply_yaw(F32); }; struct zNPCGoalBPlanktonAttack : zNPCGoalCommon @@ -171,6 +192,8 @@ struct zNPCGoalBPlanktonAttack : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); + S32 Enter(F32, void*); + S32 Exit(F32, void*); }; struct zNPCGoalBPlanktonAmbush : zNPCGoalCommon @@ -182,6 +205,8 @@ struct zNPCGoalBPlanktonAmbush : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); + S32 Enter(F32, void*); + S32 Exit(F32, void*); }; struct zNPCGoalBPlanktonFlank : zNPCGoalCommon @@ -219,8 +244,8 @@ struct zNPCGoalBPlanktonHunt : zNPCGoalCommon static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); - S32 Enter(float, void*); - S32 Exit(float, void*); + S32 Enter(F32, void*); + S32 Exit(F32, void*); }; struct zNPCGoalBPlanktonTaunt : zNPCGoalCommon @@ -232,7 +257,7 @@ struct zNPCGoalBPlanktonTaunt : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); - S32 Process(en_trantype*, float, void*, xScene*); + S32 Process(en_trantype*, F32, void*, xScene*); }; struct zNPCGoalBPlanktonMove : zNPCGoalCommon @@ -244,7 +269,7 @@ struct zNPCGoalBPlanktonMove : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); - S32 Process(en_trantype*, float, void*, xScene*); + S32 Process(en_trantype*, F32, void*, xScene*); }; struct zNPCGoalBPlanktonStun : zNPCGoalCommon @@ -256,9 +281,9 @@ struct zNPCGoalBPlanktonStun : zNPCGoalCommon } static xFactoryInst* create(S32 who, RyzMemGrow* grow, void* info); - S32 Enter(float, void*); - S32 Exit(float, void*); - S32 Process(en_trantype*, float, void*, xScene*); + S32 Enter(F32, void*); + S32 Exit(F32, void*); + S32 Process(en_trantype*, F32, void*, xScene*); }; struct zNPCGoalBPlanktonFall : zNPCGoalCommon diff --git a/src/SB/Game/zNPCTypeBossSB2.cpp b/src/SB/Game/zNPCTypeBossSB2.cpp index c8f61e473..5df7f7e10 100644 --- a/src/SB/Game/zNPCTypeBossSB2.cpp +++ b/src/SB/Game/zNPCTypeBossSB2.cpp @@ -83,20 +83,11 @@ namespace static U32 sound_asset_ids[10][4]; static sound_data_type sound_data[10]; - static const sound_asset sound_assets[12] = - { - {0, "RSB_laugh", 0, 0}, - {1, "RSB_kah", 0, 0}, - {2, "RSB_chop_windup", 0, 0}, - {3, "RSB_chop_swing", 0, 0}, - {4, "RSB_swipe", 0, 0}, - {5, "RSB_foot_loop", 0, 1}, - {6, "RSB_armhit1", 0, 0}, - {6, "RSB_armhit2", 0, 0}, - {7, "RSB_armhit1", 0, 0}, - {7, "RSB_armhit2", 0, 0}, - {8, "RSB_armsmash", 0, 0}, - {9, "RSB_foor_impact", 0, 0}, + static const sound_asset sound_assets[12] = { + { 0, "RSB_laugh", 0, 0 }, { 1, "RSB_kah", 0, 0 }, { 2, "RSB_chop_windup", 0, 0 }, + { 3, "RSB_chop_swing", 0, 0 }, { 4, "RSB_swipe", 0, 0 }, { 5, "RSB_foot_loop", 0, 1 }, + { 6, "RSB_armhit1", 0, 0 }, { 6, "RSB_armhit2", 0, 0 }, { 7, "RSB_armhit1", 0, 0 }, + { 7, "RSB_armhit2", 0, 0 }, { 8, "RSB_armsmash", 0, 0 }, { 9, "RSB_foor_impact", 0, 0 }, }; S32 set_alpha_blend(xModelInstance*) @@ -118,6 +109,14 @@ namespace return 0; // to-do } + void reset_sound() + { + for (S32 i = 0; i < 10; ++i) + { + sound_data[i].handle = 0; + } + } + S32 play_sound(int, const xVec3*, float) { return 0; // to-do @@ -223,6 +222,11 @@ namespace void register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*); }; + void tweak_group::load(xModelAssetParam* params, U32 size) + { + tweak_group::register_tweaks(true, params, size, NULL); + } + void tweak_group::register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*) { if (init) @@ -975,6 +979,10 @@ namespace } } + void response_curve::end_t() const + { + } + } // namespace xAnimTable* ZNPC_AnimTable_BossSB2() @@ -1285,7 +1293,7 @@ S32 zNPCGoalBossSB2Idle::Exit(float dt, void* updCtxt) S32 zNPCGoalBossSB2Taunt::Enter(float dt, void* updCtxt) { - ::play_sound(0, &owner.sound_loc.mouth , 1.0f); + play_sound(0, &owner.sound_loc.mouth , 1.0f); owner.flag.face_player = 1; return zNPCGoalCommon::Enter(dt, updCtxt); } diff --git a/src/SB/Game/zNPCTypeBossSB2.h b/src/SB/Game/zNPCTypeBossSB2.h index c0b80c4e4..bff1a91a2 100644 --- a/src/SB/Game/zNPCTypeBossSB2.h +++ b/src/SB/Game/zNPCTypeBossSB2.h @@ -14,6 +14,27 @@ namespace auto_tweak void load_param(T1&, T2, T2, T2, xModelAssetParam*, U32, const char*); }; +struct inode; + +struct response_curve +{ + U32 values; // offset 0x0, + inode* curve; // offset 0x4, + U32 nodes; // offset 0x8, + U32 active_node; // offset 0xC, + + void end_t() const; +}; +struct node +{ + F32 t; +}; + +struct inode : node +{ + F32 value[1]; +}; + struct zNPCB_SB2 : zNPCBoss { enum move_enum diff --git a/src/SB/Game/zNPCTypeDutchman.cpp b/src/SB/Game/zNPCTypeDutchman.cpp index 34b003386..321bb0747 100644 --- a/src/SB/Game/zNPCTypeDutchman.cpp +++ b/src/SB/Game/zNPCTypeDutchman.cpp @@ -5,7 +5,6 @@ #include - #define f1605 0.0f #define f1606 1.0f #define f1689 0.2f @@ -36,6 +35,7 @@ static U32 dutchman_count; namespace { + void kill_sound(S32 a, U32 b) { } @@ -161,10 +161,15 @@ namespace tweak_callback cb_blob_pitch; tweak_callback cb_sound; - void register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*); }; + static tweak_group tweak; + + static void set_volume(S32 which, U32, F32 new_vol) + { + } + void tweak_group::register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, const char*) { if (init) @@ -1345,7 +1350,6 @@ xFactoryInst* zNPCGoalDutchmanPostFlame::create(S32 who, RyzMemGrow* grow, void* return new (who, grow) zNPCGoalDutchmanPostFlame(who, (zNPCDutchman&)*info); } - S32 zNPCGoalDutchmanPostFlame::Exit(F32 dt, void* updCtxt) { owner.flag.hurting = 0;