summaryrefslogtreecommitdiff
path: root/src/d
diff options
context:
space:
mode:
authorrobojumper <robojumper@gmail.com>2026-05-28 17:35:04 +0200
committerrobojumper <robojumper@gmail.com>2026-05-28 17:55:54 +0200
commit401bab69247514b6a604e784a17faec1ff353c08 (patch)
treeb60792d9531ce80756b910d00bf655d67bd0fe33 /src/d
parent59af442eac2a5312704d5b3b6e9e6d2469e0efec (diff)
A few NPC functions
Diffstat (limited to 'src/d')
-rw-r--r--src/d/a/npc/d_a_npc.cpp250
1 files changed, 247 insertions, 3 deletions
diff --git a/src/d/a/npc/d_a_npc.cpp b/src/d/a/npc/d_a_npc.cpp
index efba2f9f..94d99430 100644
--- a/src/d/a/npc/d_a_npc.cpp
+++ b/src/d/a/npc/d_a_npc.cpp
@@ -3,13 +3,19 @@
#include "c/c_math.h"
#include "common.h"
#include "d/a/d_a_base.h"
+#include "d/a/d_a_player.h"
#include "d/a/npc/d_a_npc tke.h"
+#include "d/a/npc/d_a_npc_inv.h"
#include "d/d_message.h"
+#include "d/d_room.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/m3d/m_anmtexsrt.h"
+#include "m/m3d/m_fanm.h"
+#include "m/m_angle.h"
#include "m/m_mtx.h"
#include "m/m_quat.h"
#include "m/m_vec.h"
@@ -17,7 +23,6 @@
#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"
@@ -115,6 +120,106 @@ void dFlowNpc_c::setupActorRefs() {
field_0x13C = 0;
}
+dAcObjBase_c *dFlowNpc_c::getActorForId(s32 flowNpcId) {
+ if (flowNpcId == FLOW_LINK) {
+ return dAcPy_c::GetLinkM();
+ } else {
+ return getObj(getIdx(flowNpcId));
+ }
+}
+
+u16 dFlowNpc_c::flowNpcIdToProfile(s32 flowNpcId) {
+ switch (flowNpcId) {
+ case FLOW_NPC_ZLD: return fProfile::NPC_ZLD;
+ case FLOW_NPC_DSK: return fProfile::NPC_DSK;
+ case FLOW_NPC_TALK_KENSEI: return fProfile::NPC_TALK_KENSEI;
+ case FLOW_NPC_RVL: return fProfile::NPC_RVL;
+ case FLOW_NPC_KBN: return fProfile::NPC_KBN;
+ case FLOW_NPC_KBN2: return fProfile::NPC_KBN2;
+ case FLOW_NPC_SORAJIMA_MALE: return fProfile::NPC_SORAJIMA_MALE;
+ case FLOW_NPC_SORAJIMA_FEMALE: return fProfile::NPC_SORAJIMA_FEMALE;
+ case FLOW_NPC_SKN: return fProfile::NPC_SKN;
+ case FLOW_NPC_SKN2: return fProfile::NPC_SKN2;
+ case FLOW_NPC_KNIGHT_LEADER: return fProfile::NPC_KNIGHT_LEADER;
+ case FLOW_NPC_CE_FRIEND: return fProfile::NPC_CE_FRIEND;
+ case FLOW_NPC_GHM: return fProfile::NPC_GHM;
+ case FLOW_NPC_OIM: return fProfile::NPC_OIM;
+ case FLOW_NPC_YIM: return fProfile::NPC_YIM;
+ case FLOW_NPC_MOLE_NORMAL: return fProfile::NPC_MOLE_NORMAL;
+ case FLOW_NPC_MOLE_NORMAL2: return fProfile::NPC_MOLE_NORMAL2;
+ case FLOW_NPC_SENPAI: return fProfile::NPC_SENPAI;
+ case FLOW_NPC_DRB: return fProfile::NPC_DRB;
+ case FLOW_NPC_SENPAI_B: return fProfile::NPC_SENPAI_B;
+ case FLOW_NPC_SORAJIMA_MOTHER: return fProfile::NPC_SORAJIMA_MOTHER;
+ case FLOW_NPC_SLRP: return fProfile::NPC_SLRP;
+ case FLOW_NPC_SLB: return fProfile::NPC_SLB;
+ case FLOW_NPC_GRA: return fProfile::NPC_GRA;
+ case FLOW_NPC_MOLE_TACKLE2: return fProfile::NPC_MOLE_TACKLE2;
+ case FLOW_NPC_PDU: return fProfile::NPC_PDU;
+ case FLOW_NPC_SMA2: return fProfile::NPC_SMA2;
+ case FLOW_NPC_MED_HUS_NIGHT: return fProfile::NPC_MED_HUS_NIGHT;
+ case FLOW_NPC_MED_WIFE_NIGHT: return fProfile::NPC_MED_WIFE_NIGHT;
+ case FLOW_NPC_DOUGUYA_NIGHT: return fProfile::NPC_DOUGUYA_NIGHT;
+ case FLOW_NPC_DOUGUYA_MOTHER: return fProfile::NPC_DOUGUYA_MOTHER;
+ case FLOW_NPC_AZUKARIYA_NIGHT: return fProfile::NPC_AZUKARIYA_NIGHT;
+ case FLOW_NPC_AZUKARIYA_FATHER: return fProfile::NPC_AZUKARIYA_FATHER;
+ case FLOW_NPC_SENPAIA_MOTHER: return fProfile::NPC_SENPAIA_MOTHER;
+ case FLOW_NPC_AKU_HUMAN: return fProfile::NPC_AKU_HUMAN;
+ case FLOW_NPC_SALBAGE_MORRY: return fProfile::NPC_SALBAGE_MORRY;
+ case FLOW_NPC_DOUGUYA_MOTHER_LOD: return fProfile::NPC_DOUGUYA_MOTHER_LOD;
+ case FLOW_NPC_JUNK_MOTHER_LOD: return fProfile::NPC_JUNK_MOTHER_LOD;
+ case FLOW_NPC_SENPAIA_MOTHER_LOD: return fProfile::NPC_SENPAIA_MOTHER_LOD;
+ case FLOW_NPC_BBRVL: return fProfile::NPC_BBRVL;
+ case FLOW_NPC_SORAJIMA_MAN_D: return fProfile::NPC_SORAJIMA_MAN_D;
+ case FLOW_NPC_SORAJIMA_MAN_E: return fProfile::NPC_SORAJIMA_MAN_E;
+ case FLOW_NPC_DAISHINKAN_N: return fProfile::NPC_DAISHINKAN_N;
+ case FLOW_NPC_PMA: return fProfile::NPC_PMA;
+ case FLOW_NPC_DRBC: return fProfile::NPC_DRBC;
+ case FLOW_NPC_MOLE_MG: return fProfile::NPC_MOLE_MG;
+ case FLOW_NPC_GRD: return fProfile::NPC_GRD;
+ case FLOW_NPC_GRC: return fProfile::NPC_GRC;
+ case FLOW_NPC_SLB2: return fProfile::NPC_SLB2;
+ case FLOW_NPC_CE_LADY: return fProfile::NPC_CE_LADY;
+ case FLOW_NPC_MOLE: return fProfile::NPC_MOLE;
+ }
+ if (flowNpcId >= FLOW_NPC_INV_OFFSET) {
+ return fProfile::NPC_INV;
+ }
+ return fProfile::PROFILE_MAX;
+}
+
+dAcObjBase_c *dFlowNpc_c::getObj(s32 idx) {
+ if (0 <= idx && idx < field_0x138) {
+ return mObjRefs[idx].get();
+ }
+ return nullptr;
+}
+
+s32 dFlowNpc_c::getIdx(s32 flowNpcId) {
+ if (flowNpcId == FLOW_NPC_SELF) {
+ return 0;
+ }
+ u16 profile = flowNpcIdToProfile(flowNpcId);
+ if (profile == mpOwner->mProfileName) {
+ return 0;
+ }
+
+ for (int i = 1; i < field_0x138; i++) {
+ if (profile == mObjRefs[i].get()->mProfileName) {
+ if (profile == fProfile::NPC_INV) {
+ if (flowNpcId - FLOW_NPC_INV_OFFSET ==
+ static_cast<dAcNpcInv_c *>(mObjRefs[i].get())->getActualFlowNpcId()) {
+ return i;
+ }
+ } else {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
STATE_VIRTUAL_DEFINE(dAcNpc_c, Wait);
STATE_VIRTUAL_DEFINE(dAcNpc_c, Demo);
@@ -144,10 +249,46 @@ SizedString<128> sNpcStr2;
SizedString<128> sNpcStr3;
SizedString<128> sNpcStr4;
-dAcNpc_c::dAcNpc_c() : mFlow(this), mStateMgr(*this), mActorListEntry(this) {}
+void dAcNpc_c::loadMainPos(f32 offsetY1, f32 offsetY2) {
+ mPositionCopy3 = mPosition;
+ mPositionCopy3.y += offsetY1;
+ mPositionCopy2 = mPosition;
+ mPositionCopy2.y += offsetY2;
+}
+
+void dAcNpc_c::loadMainAndHeadPos(f32 posYOffset, f32 headYOffset, d3d::AnmMdlWrapper *mdl) {
+ mPositionCopy3 = mPosition;
+ mPositionCopy3.y += posYOffset;
+ mMtx_c mtx;
+ mdl->getModel().getNodeWorldMtx(mdl->getModel().getResMdl().GetResNode(sHeadNodeName).GetID(), mtx);
+ mtx.getTranslation(mPositionCopy2);
+ mPositionCopy2.y += headYOffset;
+}
+
+void dAcNpc_c::loadMainAndHeadPos(const mVec3_c *posOffset, const mVec3_c *headOffset, d3d::AnmMdlWrapper *mdl) {
+ loadMainPos(posOffset);
+ mMtx_c mtx;
+ mdl->getModel().getNodeWorldMtx(mdl->getModel().getResMdl().GetResNode(sHeadNodeName).GetID(), mtx);
+ mtx.getTranslation(mPositionCopy2);
+ mPositionCopy2 += *headOffset;
+}
+
+dAcNpc_c::dAcNpc_c() : mSoundInfo(this), mFlow(this), mStateMgr(*this), mNpcListEntry(this) {
+ addToNpcList();
+}
+
+dAcNpc_c::dAcNpc_c(dFlowMgrBase_c *flowMgr) : mSoundInfo(this), mFlow(this), mStateMgr(*this), mNpcListEntry(this) {
+ addToNpcList();
+}
+
+void dAcNpc_c::loadMainPos(const mVec3_c *offset) {
+ mVec3_c v = *offset;
+ v.rotY(mRotation.y);
+ mPositionCopy3 = mPosition + v;
+}
dAcNpc_c::~dAcNpc_c() {
- // TODO
+ NPC_ACTOR_LIST.remove(&mNpcListEntry);
}
u16 dAcNpc_c::doFlowSwitch1(u32 arg) {
@@ -184,6 +325,78 @@ bool dAcNpc_c::addHeadNeckSpineCallbacks(
return true;
}
+bool dAcNpc_c::initHeadNeckSpineCallbacks(
+ d3d::AnmMdlWrapper *mdl, dNpcMdlCallbackMulti_c *multi, dNpcMdlCallbackAng_c *head, dNpcMdlCallbackAng_c *neck,
+ dNpcMdlCallbackAng_c *spine
+) {
+ if (!addHeadNeckSpineCallbacks(mdl, multi, head, neck, spine)) {
+ return false;
+ }
+ mAng max = acNpc_vt_0x198();
+ mAng min = acNpc_vt_0x19C();
+ mAng maxX = acNpc_vt_0x190();
+ mAng minX = acNpc_vt_0x194();
+
+ head->setMinMaxStepSize(max, min);
+ head->setMinMaxStepSizeX(maxX, minX);
+ neck->setMinMaxStepSize(max, min);
+ neck->setMinMaxStepSizeX(maxX, minX);
+ spine->setMinMaxStepSize(max, min);
+ spine->setMinMaxStepSizeX(maxX, minX);
+
+ return true;
+}
+
+m3d::anmTexSrt_c *
+dAcNpc_c::newAnmTexSrt(d3d::AnmMdlWrapper *mdl, mAllocator_c *allocator, const char *mdlName, const char *anmName) {
+ m3d::anmTexSrt_c *ret = new m3d::anmTexSrt_c();
+ if (!ret) {
+ return nullptr;
+ }
+ if (!ret->create(
+ mdl->getMdlFile().GetResMdl(mdlName), mdl->getMdlFile().GetResAnmTexSrt(anmName), allocator, nullptr, 1
+ )) {
+ delete ret;
+ return nullptr;
+ }
+ ret->setFrame(0.0f, 0);
+ mdl->getModel().setAnm(*ret);
+ return ret;
+}
+
+void dAcNpc_c::setAnmTexSrt(d3d::AnmMdlWrapper *mdl, m3d::anmTexSrt_c *texSrt, const char *anmName) {
+ texSrt->setAnm(mdl->getModel(), mdl->getMdlFile().GetResAnmTexSrt(anmName), 0, m3d::PLAY_MODE_4);
+ texSrt->setFrame(0.0f, 0);
+}
+
+bool dAcNpc_c::createAnmChr(
+ d3d::AnmMdlWrapper *mdl, const char *mdlName, const char *anmName, mAllocator_c *allocator, m3d::anmChr_c *anm
+) {
+ // TODO - what?
+ return !!anm->create2(mdl->getMdlFile().GetResMdl(mdlName), mdl->getMdlFile().GetResAnmChr(anmName), allocator);
+}
+
+s32 dAcNpc_c::getPathIndexForId(s32 pathId, s32 roomId, bool spth) {
+ dRoom_c *room = dStage_c::GetInstance()->getRoom(roomId);
+ int ret = -1;
+ if (spth) {
+ for (int i = 0; i < room->getSpthCount(); i++) {
+ if (room->getSpth(i)->pathId == pathId) {
+ ret = i;
+ break;
+ }
+ }
+ } else {
+ for (int i = 0; i < room->getPathCount(); i++) {
+ if (room->getPath(i)->pathId == pathId) {
+ ret = i;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
static u32 findNodeId(d3d::AnmMdlWrapper *mdl, const char *nodeName) {
return m3d::getNodeID(mdl->getModel().getResMdl(), nodeName);
}
@@ -426,3 +639,34 @@ void dNpcMdlCallbackMulti_c::timingB(u32 nodeId, nw4r::g3d::WorldMtxManip *manip
node = node->mpNext;
}
}
+
+/**
+ * @brief Clamps an angle such that bounds[1] <= result <= bounds[0] and result + return = value
+ *
+ * @param value the input value
+ * @param bounds the max, min bounds
+ * @param result the clamped value
+ * @return mAng the excess
+ */
+extern mAng clampAngle(mAng &value, mAng *bounds, mAng &result) {
+ // TODO instruction order
+ mAng ret = value;
+
+ if (bounds[1] <= ret && ret <= bounds[0]) {
+ result = ret;
+ ret = 0;
+ } else if (ret > 0) {
+ result = bounds[0];
+ ret = ret - bounds[0];
+ } else {
+ result = bounds[1];
+ ret = ret - bounds[1];
+ }
+
+ return ret;
+}
+
+extern void clampAngleXY(mAng3_c &value, mAng *boundsX, mAng *boundsY, mAng3_c &result) {
+ clampAngle(value.x, boundsX, result.x);
+ clampAngle(value.y, boundsY, result.y);
+}