diff options
| author | robojumper <robojumper@gmail.com> | 2025-08-23 23:25:01 +0200 |
|---|---|---|
| committer | robojumper <robojumper@gmail.com> | 2025-09-15 22:24:09 +0200 |
| commit | 72dd147e80db6db639e9ae79996b2d2d64456c90 (patch) | |
| tree | 9c0e9aa455d61d02a7e1e0b93ee9d9448e08f51d /src/d/a | |
| parent | c5f7ab571a31b7e047d25b4c58fd01055094e95a (diff) | |
Starting to make sense of some of these vtables in NPCs
Diffstat (limited to 'src/d/a')
| -rw-r--r-- | src/d/a/npc/d_a_npc.cpp | 388 |
1 files changed, 387 insertions, 1 deletions
diff --git a/src/d/a/npc/d_a_npc.cpp b/src/d/a/npc/d_a_npc.cpp index fa18bc45..e4421a7f 100644 --- a/src/d/a/npc/d_a_npc.cpp +++ b/src/d/a/npc/d_a_npc.cpp @@ -1,12 +1,120 @@ #include "d/a/npc/d_a_npc.h" +#include "c/c_math.h" +#include "common.h" +#include "d/a/d_a_base.h" +#include "d/a/npc/d_a_npc tke.h" +#include "d/d_message.h" +#include "d/d_sc_game.h" +#include "d/d_stage.h" #include "f/f_list_mg.h" +#include "f/f_profile_name.h" +#include "m/m3d/m3d.h" +#include "m/m_mtx.h" +#include "m/m_quat.h" +#include "m/m_vec.h" +#include "nw4r/g3d/res/g3d_resmdl.h" #include "nw4r/ut/ut_Color.h" +#include "rvl/MTX/mtx.h" +#include "s/s_Math.h" #include "s/s_StateID.hpp" #include "sized_string.h" +#include "toBeSorted/d_d3d.h" +#include "toBeSorted/event_manager.h" fLiMgBa_c dAcNpc_c::NPC_ACTOR_LIST; +const char *dAcNpc_c::sHeadNodeName = "Head"; +const char *dAcNpc_c::sNeckNodeName = "Neck"; +const char *dAcNpc_c::sSpine2NodeName = "Spine2"; + +bool dFlowNpc_c::handleEventInternal(const MsbFlowInfo *element) { + switch (element->param3) { + case EVENT_SET_ITEM: { + if (mpOwner->doGiveItem(element->param3, element->params1n2)) { + setField0x3C(); + } + return true; + } + case EVENT_EXIT: { + u16 exitId, trial; + extract2xU16Params(element, &exitId, &trial); + if (field_0x10) { + dAcBase_c *ac = EventManager::getMainActorInEvent(); + if (ac != nullptr && ac->profile_name == fProfile::NPC_TKE) { + static_cast<daNpcTke_c *>(ac)->setFinishFlags(); + } else if (mpOwner->profile_name == fProfile::NPC_TKE) { + static_cast<daNpcTke_c *>(mpOwner)->setFinishFlags(); + } + } + sExitId = exitId; + if (trial == 1) { + dScGame_c::GetInstance()->triggerExit( + dStage_c::GetInstance()->getCurrRoomId(), sExitId, SpawnInfo::RETAIN_TOD, SpawnInfo::TRIAL + ); + } else { + dScGame_c::GetInstance()->triggerExit( + dStage_c::GetInstance()->getCurrRoomId(), sExitId, SpawnInfo::RETAIN_TOD, SpawnInfo::RETAIN_TRIAL + ); + } + return false; + } + // TODO - more cases + default: { + return dFlow_c::handleEventInternal(element); + } + } +} + +u16 dFlowNpc_c::getSwitchChoice(const MsbFlowInfo *element, u16 param) const { + u16 result = 0; + dAcNpc_c *npc = mpOwner; + u32 arg = element->params1n2; + switch (param) { + case BRANCH_NPC_1: + if (npc != nullptr) { + result = npc->doFlowSwitch1(arg); + } + break; + case BRANCH_NPC_2: + if (npc != nullptr) { + result = npc->doFlowSwitch2(arg); + } + break; + case BRANCH_NPC_3: + if (npc != nullptr) { + result = npc->doFlowSwitch3(arg); + } + break; + default: result = dFlow_c::getSwitchChoice(element, param); break; + } + + return result; +} + +void dFlowNpc_c::triggerEntryPoint(s32 labelPart1, s32 labelPart2) { + setupActorRefs(); + dFlow_c::triggerEntryPoint(labelPart1, labelPart2); +} + +void dFlowNpc_c::triggerEntryPoint(const char *label) { + setupActorRefs(); + dFlow_c::triggerEntryPoint(label); +} + +void dFlowNpc_c::setupActorRefs() { + mObjRefs[0].link(mpOwner); + for (int i = 1; i < (int)ARRAY_LENGTH(mObjRefs); i++) { + mObjRefs[i].unlink(); + } + + for (int i = 0; i < ARRAY_LENGTH(field_0x128); i++) { + field_0x128[i] = 0; + } + field_0x138 = 1; + field_0x13C = 0; +} + STATE_VIRTUAL_DEFINE(dAcNpc_c, Wait); STATE_VIRTUAL_DEFINE(dAcNpc_c, Demo); @@ -36,8 +144,286 @@ SizedString<128> sNpcStr2; SizedString<128> sNpcStr3; SizedString<128> sNpcStr4; -dAcNpc_c::dAcNpc_c() : mStateMgr(*this, sStateID::null), mActorListEntry(this) {} +dAcNpc_c::dAcNpc_c() : mFlow(this), mStateMgr(*this, sStateID::null), mActorListEntry(this) {} dAcNpc_c::~dAcNpc_c() { // TODO } + +u16 dAcNpc_c::doFlowSwitch1(u32 arg) { + return eventFlowSwitch1(arg); +} + +u16 dAcNpc_c::doFlowSwitch2(u32 arg) { + return eventFlowSwitch2(arg); +} + +u16 dAcNpc_c::doFlowSwitch3(u32 arg) { + return eventFlowSwitch3(arg); +} + +bool dAcNpc_c::addHeadNeckSpineCallbacks( + d3d::AnmMdlWrapper *mdl, dNpcMdlCallbackMulti_c *multi, dNpcMdlCallbackBase_c *head, dNpcMdlCallbackBase_c *neck, + dNpcMdlCallbackBase_c *spine +) { + if (!multi->addCallback(head)) { + return false; + } + head->loadNodeId(mdl, sHeadNodeName); + + if (!multi->addCallback(neck)) { + return false; + } + neck->loadNodeId(mdl, sNeckNodeName); + + if (!multi->addCallback(spine)) { + return false; + } + spine->loadNodeId(mdl, sSpine2NodeName); + + return true; +} + +static u32 findNodeId(d3d::AnmMdlWrapper *mdl, const char *nodeName) { + return m3d::getNodeID(mdl->getModel().getResMdl(), nodeName); +} + +void dNpcMdlCallbackBase_c::loadNodeId(d3d::AnmMdlWrapper *mdl, const char *nodeName) { + mNodeId = findNodeId(mdl, nodeName); +} + +void dNpcMdlCallbackBase_c::timingA(nw4r::g3d::ChrAnmResult *result) { + // no-op +} + +void dNpcMdlCallbackBase_c::timingB(mMtx_c *result) { + mVec3_c v1(0.0f, 0.0f, 1.0f); + mVec3_c v2(0.0f, 0.0f, 1.0f); + MTXMultVecSR(*result, v2, v1); + field_0x04 = cM::atan2s(-v1.y, v1.absXZ()); + field_0x06 = cM::atan2s(v1.x, v1.z); + if (mForceCalc || !isDone()) { + apply(result); + } +} + +dNpcMdlCallbackAng_c::dNpcMdlCallbackAng_c() + : mTarget(0, 0, 0), + mNumSteps(2), + mMaxStepSizeYZ(768), + mMinStepSizeYZ(205), + mMaxStepSizeX(252), + mMinStepSizeX(1), + mCurrent(0, 0, 0) { + mScaleZ = 1; + mScaleX = 1; + mScaleY = 1; +} + +void dNpcMdlCallbackAng_c::setMinMaxStepSize(const mAng &max, const mAng &min) { + mMaxStepSizeYZ = max; + mMinStepSizeYZ = min; + mMaxStepSizeX = max; + mMinStepSizeX = min; + if (max < min) { + mMaxStepSizeYZ = min; + mMaxStepSizeX = min; + } +} + +void dNpcMdlCallbackAng_c::setMinMaxStepSizeX(const mAng &max, const mAng &min) { + mMaxStepSizeX = max; + mMinStepSizeX = min; + if (max < min) { + mMaxStepSizeX = min; + } +} + +void dNpcMdlCallbackAng_c::reset() { + s32 zero = 0; + mTarget.set(zero, zero, zero); + mCurrent.set(zero, zero, zero); + mForceCalc = false; +} + +void dNpcMdlCallbackAng_c::resetTarget() { + s32 zero = 0; + mTarget.set(zero, zero, zero); + mForceCalc = false; +} + +void dNpcMdlCallbackAng_c::setTarget(const mAng3_c &target) { + mTarget = target; + mForceCalc = true; +} + +void dNpcMdlCallbackAng_c::setTargetNow(const mAng3_c &target) { + mTarget = target; + mCurrent = target; + mForceCalc = true; +} + +void dNpcMdlCallbackAng_c::finishTarget() { + mCurrent = mTarget; + mForceCalc = true; +} + + +void dNpcMdlCallbackAng_c::calc() { + mAng3_c ang = mCurrent; + + ang.x.step(mTarget.x, mNumSteps, mMaxStepSizeX, mMinStepSizeX); + ang.y.step(mTarget.y, mNumSteps, mMaxStepSizeYZ, mMinStepSizeYZ); + ang.z.step(mTarget.z, mNumSteps, mMaxStepSizeYZ, mMinStepSizeYZ); + + mCurrent = ang; +} + +void dNpcMdlCallbackYXZ_c::apply(mMtx_c *result) const { + result->YrotM(mCurrent.y * getScaleY()); + result->XrotM(mCurrent.x * getScaleX()); + result->ZrotM(mCurrent.z * getScaleZ()); +} + +void dNpcMdlCallbackYXZFixed_c::apply(mMtx_c *result) const { + mMtx_c tmp; + mVec3_c trans; + + result->getTranslation(trans); + + // Yes, this + is needed and sort of makes sense with + // the symmetry below... + tmp.YrotS(+mActorRotation.y); + tmp.YrotM(mCurrent.y * getScaleY()); + tmp.XrotM(mCurrent.x * getScaleX()); + tmp.ZrotM(mCurrent.z * getScaleZ()); + tmp.YrotM(-mActorRotation.y); + + MTXConcat(tmp, *result, *result); + + result->setTranslation(trans); +} + +void dNpcMdlCallbackYXZFixed_c::vt_0x20(const dAcBase_c *ac) { + mActorRotation = ac->rotation; +} + +void dNpcMdlCallbackYZX_c::apply(mMtx_c *result) const { + result->YrotM(mCurrent.y * getScaleY()); + result->ZrotM(mCurrent.x * getScaleX()); + result->XrotM(mCurrent.z * getScaleZ()); +} + +void dNpcMdlCallbackXZY_c::apply(mMtx_c *result) const { + result->XrotM(mCurrent.y * getScaleY()); + result->ZrotM(mCurrent.x * getScaleX()); + result->YrotM(mCurrent.z * getScaleZ()); +} + +void dNpcMdlCallbackQuat_c::calc() { + if (mSlerpFactor < 1.0f) { + sLib::addCalc(&mSlerpFactor, 1.0f, mRatio, mMaxStepSize, mMinStepSize); + } else { + mSlerpFactor = 1.0f; + } +} + +void dNpcMdlCallbackQuat_c::apply(mMtx_c *result) const { + mMtx_c quatMtx; + mQuat_c resultQuat; + mVec3_c trans; + mMtx_c tmp; + + mStart.slerpTo(mTarget, mSlerpFactor, resultQuat); + quatMtx.fromQuat(resultQuat); + + result->getTranslation(trans); + + tmp.YrotS(*mActorRotY); + MTXConcat(tmp, quatMtx, tmp); + tmp.YrotM(-*mActorRotY); + + MTXConcat(tmp, *result, *result); + + result->setTranslation(trans); +} + +dNpcMdlCallbackNode *dNpcMdlCallbackMulti_c::newCallbackNode() { + dNpcMdlCallbackNode *node = new dNpcMdlCallbackNode; + if (node == nullptr) { + return nullptr; + } + node->mpNext = nullptr; + return node; +} + +bool dNpcMdlCallbackMulti_c::createCallbackNode(dNpcMdlCallbackBase_c *cb) { + dNpcMdlCallbackNode *node = newCallbackNode(); + if (node == nullptr) { + return false; + } + mpTail = node; + node->mpCallback = cb; + return true; +} + +void dNpcMdlCallbackMulti_c::clearList() { + dNpcMdlCallbackNode *node = mpHead; + dNpcMdlCallbackNode *next; + if (node != nullptr) { + while (node != nullptr) { + next = node->mpNext; + delete node; + node = next; + } + } +} + +bool dNpcMdlCallbackMulti_c::addCallback(dNpcMdlCallbackBase_c *callback) { + if (mpHead == nullptr) { + if (!createCallbackNode(callback)) { + return false; + } + mpHead = mpTail; + } else { + dNpcMdlCallbackNode *prev = mpTail; + if (!createCallbackNode(callback)) { + return false; + } + prev->mpNext = mpTail; + } + mNumNodes++; + return true; +} + +void dNpcMdlCallbackMulti_c::timingA(u32 nodeId, nw4r::g3d::ChrAnmResult *result, nw4r::g3d::ResMdl mdl) { + if (mpHead == nullptr) { + return; + } + dNpcMdlCallbackNode *node = mpHead; + while (node != nullptr) { + if (nodeId == node->mpCallback->getNodeId()) { + node->mpCallback->timingA(result); + return; + } + node = node->mpNext; + } +} + +void dNpcMdlCallbackMulti_c::timingB(u32 nodeId, nw4r::g3d::WorldMtxManip *manip, nw4r::g3d::ResMdl mdl) { + if (mpHead == nullptr) { + return; + } + dNpcMdlCallbackNode *node = mpHead; + while (node != nullptr) { + if (nodeId == node->mpCallback->getNodeId()) { + node->mpCallback->calc(); + mMtx_c mtx; + manip->GetMtx(mtx); + node->mpCallback->timingB(&mtx); + manip->SetMtx(mtx); + return; + } + node = node->mpNext; + } +} |
