#include "d/a/obj/d_a_obj_tower_hand_D101.h" #include "common.h" #include "d/a/d_a_item.h" #include "d/a/d_a_player.h" #include "d/col/bg/d_bg_s.h" #include "d/col/bg/d_bg_w.h" #include "d/d_stage.h" #include "d/flag/sceneflag_manager.h" #include "d/snd/d_snd_wzsound.h" #include "f/f_base.h" #include "m/m3d/m_fanm.h" #include "m/m_angle.h" #include "m/m_mtx.h" #include "m/m_vec.h" #include "nw4r/g3d/res/g3d_resfile.h" #include "rvl/MTX/mtx.h" #include "rvl/MTX/vec.h" #include "s/s_Math.h" #include "toBeSorted/actor_event.h" #include "toBeSorted/event_manager.h" SPECIAL_ACTOR_PROFILE(OBJ_TOWER_HAND_D101, dAcOTowerHandD101_c, fProfile::OBJ_TOWER_HAND_D101, 0x180, 0, 6); const mVec3_c sScales[2] = {mVec3_c(1.0f, 1.0f, 1.0f), mVec3_c(-1.0f, 1.0f, 1.0f)}; const mVec3_c sBoundingLower = mVec3_c(-1000.0f, 0.0f, -600.0f); const mVec3_c sBoundingUpper = mVec3_c(1000.0f, 1000.0f, 1000.0f); const mVec3_c v1 = mVec3_c::Ex * 50.0f; const mVec3_c v2 = mVec3_c::Ey * 50.0f; const mVec3_c v3 = mVec3_c::Ez * 50.0f; STATE_DEFINE(dAcOTowerHandD101_c, RemainOpen); STATE_DEFINE(dAcOTowerHandD101_c, Close); STATE_DEFINE(dAcOTowerHandD101_c, RemainClosed); STATE_DEFINE(dAcOTowerHandD101_c, Open); STATE_DEFINE(dAcOTowerHandD101_c, Hold); static const char *const sOarcName = "TowerHandD101"; static const char *const sMdlName = "TowerHandD101"; static const char *const sAnmName = "TowerHandD101"; static const char *const sCollisionParts[2][3] = { { "TowerHandD101A", "TowerHandD101B", "TowerHandD101C"}, {"TowerHandD101A_R", "TowerHandD101B_R", "TowerHandD101C_R"}, }; static const char *const sLocator1 = "locator1"; static const char *const sLocator2 = "locator2"; // TODO there are tons of unused floats in the rodata section bool dAcOTowerHandD101_c::getScale(int direction, mVec3_c &outScale) { if (!isValidDirectionParam(direction)) { return false; } outScale.x = sScales[direction].x; outScale.y = sScales[direction].y; outScale.z = sScales[direction].z; return true; } const mVec3_c &dAcOTowerHandD101_c::getBoundingLower() { return sBoundingLower; } const mVec3_c &dAcOTowerHandD101_c::getBoundingUpper() { return sBoundingUpper; } bool dAcOTowerHandD101_c::isValidDirectionParam(int param) { return 0 <= param && param <= 1; } void dAcOTowerHandD101_c::transformMtx( const mVec3_c &localTranslation, const mAng3_c &rotation, const mVec3_c &offset, mVec3_c &result ) { mMtx_c tmp1, tmp2; tmp1.transS(localTranslation); tmp1.ZXYrotM(rotation.x, rotation.y, rotation.z); tmp2.transS(offset); tmp1 += tmp2; tmp1.getTranslation(result); } f32 dAcOTowerHandD101_c::f32Sq(f32 value) { return value * value; } bool dAcOTowerHandD101_c::isValidBgWIndex(int index) { return 0 <= index && index <= 2; } bool dAcOTowerHandD101_c::createHeap() { const char *oarcName = sOarcName; void *resP = getOarcResFile(oarcName); if (resP == nullptr) { return false; } if (!mMdl.create(resP, sMdlName, sAnmName, &mAllocator, 0x130)) { return false; } int direction = getDirection(); if (!isValidDirectionParam(direction)) { return false; } nw4r::g3d::ResFile res(resP); if (!res.IsValid()) { return false; } dStage_c::bindStageResToFile(&res); if (direction == 1) { mMdl.getModel().setCullModeAll(GX_CULL_FRONT, false); } updateMatrix(); mMdl.getModel().setLocalMtx(mWorldMtx); mVec3_c newScale; if (!getScale(direction, newScale)) { return false; } mMdl.getModel().setScale(newScale); mMdl.getModel().calc(false); mMtx_c loc1Mtx; getLoc1Mtx(loc1Mtx); mLoc1Mtx.copyFrom(loc1Mtx); mMtx_c loc2Mtx; getLoc2Mtx(loc2Mtx); mLoc2Mtx.copyFrom(loc2Mtx); const char *const *parts = sCollisionParts[direction]; for (int i = 0; i <= 2; i++) { void *dzb = getOarcDZB(oarcName, parts[i]); if (dzb == nullptr) { return false; } void *plc = getOarcPLC(oarcName, parts[i]); if (plc == nullptr) { return false; } dBgW *bgW = nullptr; getBgW(i, bgW); mMtx_c *bgWMtx; getBgWMtx(i, bgWMtx); bool ok = !(BOOL)bgW->Set((cBgD_t *)dzb, (PLC *)plc, cBgW::MOVE_BG_e, bgWMtx, &mScale); if (!ok) { return false; } } return true; } int dAcOTowerHandD101_c::actorCreate() { CREATE_ALLOCATOR(dAcOTowerHandD101_c); mMdl.getModel().setLocalMtx(mWorldMtx); mMdl.setAnm(sAnmName, m3d::PLAY_MODE_4, 0.0f); mMdl.getModel().setPriorityDraw(0x1C, 0); for (int i = 0; i <= 2; i++) { dBgW *bgW = nullptr; getBgW(i, bgW); bgW->SetCrrFunc(dBgS_MoveBGProc_Typical); dBgS::GetInstance()->Regist(bgW, this); } mBoundingBox.Set(getBoundingLower(), getBoundingUpper()); return SUCCEEDED; } int dAcOTowerHandD101_c::actorPostCreate() { mVec3_c pos; getItemPos(pos); bool handClosed = true; dAcObjBase_c *ac = dAcObjBase_c::getNextObject(&dAcItem_c::sItemList, nullptr); f32 distLimit = 90000.0f; while (handClosed && ac != nullptr) { if (VECSquareDistance(pos, ac->mPosition) < distLimit) { handClosed = false; mHeldItem.link(static_cast(ac)); } else { ac = dAcObjBase_c::getNextObject(&dAcItem_c::sItemList, ac); } } if (handClosed) { mFlags |= 1; } if (handClosed) { mStateMgr.changeState(StateID_RemainClosed); } else { mStateMgr.changeState(StateID_RemainOpen); } return SUCCEEDED; } inline bool dAcOTowerHandD101_c::getItem(dAcItem_c *&outItem) { outItem = !mHeldItem.isLinked() ? nullptr : mHeldItem.get(); bool ret = outItem == nullptr; return ret; } int dAcOTowerHandD101_c::actorExecute() { dAcPy_c *link; dAcItem_c *item; link = dAcPy_c::LINK; UNKWORD w = link->IfCurrentActionToActor(this, 0x3D); bool b = getItem(item); if (!b && item->isStateWait()) { f32 dist = VECSquareDistance(item->mPosition, link->mPosition); if (w == 0 && dist < 15625.0f) { item->getItemFromBWheelItem(); } else { item->setItemPosition(item->mPosition); } } bool doSomething; if (b) { doSomething = true; } else { doSomething = !item->isStateWait(); } if (b || doSomething) { link->vt_0x084(this, 0x3D); } mStateMgr.executeState(); mVec3_c newScale; int direction = getDirection(); getScale(direction, newScale); mMdl.getModel().calc(false); mMtx_c tmpMtx2; mMtx_c tmpMtx3; mMtx_c tmpMtx1; mMtx_c tmpMtx4; getLoc1Mtx(tmpMtx2); MTXScale(tmpMtx1, newScale.x, newScale.y, newScale.z); MTXConcat(tmpMtx2, tmpMtx1, tmpMtx2); mLoc1Mtx.copyFrom(tmpMtx2); getLoc2Mtx(tmpMtx3); MTXScale(tmpMtx4, newScale.x, newScale.y, newScale.z); MTXConcat(tmpMtx3, tmpMtx4, tmpMtx3); mLoc2Mtx.copyFrom(tmpMtx3); if (mEffects.hasEmitters()) { if (direction == 1) { tmpMtx3.YrotM(-0x8000); } mEffects.setTransform(tmpMtx3); } for (int i = 0; i <= 2; i++) { dBgW *bgW = nullptr; getBgW(i, bgW); bgW->Move(); } return SUCCEEDED; } int dAcOTowerHandD101_c::actorExecuteInEvent() { u32 cmd = getEventStuff().getCurrentEventCommand(); switch (cmd) { case 'wait': getEventStuff().advanceNext(); break; case 'clos': { if (getEventStuff().isAdvance()) { if (mStateMgr.isState(StateID_Close) || mStateMgr.isState(StateID_RemainClosed)) { mStateMgr.changeState(StateID_Open); } } mStateMgr.executeState(); if (mStateMgr.isState(StateID_RemainClosed)) { getEventStuff().advanceNext(); } mVec3_c newScale; int direction = getDirection(); getScale(direction, newScale); mMdl.getModel().calc(false); mMtx_c tmpMtx2; mMtx_c tmpMtx3; mMtx_c tmpMtx1; mMtx_c tmpMtx4; getLoc1Mtx(tmpMtx2); MTXScale(tmpMtx1, newScale.x, newScale.y, newScale.z); MTXConcat(tmpMtx2, tmpMtx1, tmpMtx2); mLoc1Mtx.copyFrom(tmpMtx2); getLoc2Mtx(tmpMtx3); MTXScale(tmpMtx4, newScale.x, newScale.y, newScale.z); MTXConcat(tmpMtx3, tmpMtx4, tmpMtx3); mLoc2Mtx.copyFrom(tmpMtx3); if (mEffects.hasEmitters()) { if (direction == 1) { tmpMtx3.YrotM(-0x8000); } mEffects.setTransform(tmpMtx3); } for (int i = 0; i <= 2; i++) { dBgW *bgW = nullptr; getBgW(i, bgW); bgW->Move(); } } break; case '????': actorExecute(); break; default: actorExecute(); // Cool and here we're NOT using getEventStuff. Why even bother? mEvent.advanceNext(); } return SUCCEEDED; } int dAcOTowerHandD101_c::draw() { drawModelType1(&mMdl.getModel()); return SUCCEEDED; } bool dAcOTowerHandD101_c::getBgW(int index, dBgW *&outBgW) { if (isValidBgWIndex(index)) { outBgW = &mBgWs[index]; return true; } return false; } ActorEventRelated &dAcOTowerHandD101_c::getEventStuff() { return mEvent; } f32 dAcOTowerHandD101_c::getItemOffsetX() { return 0.0f; } f32 dAcOTowerHandD101_c::getItemOffsetY() { return 100.0f; } f32 dAcOTowerHandD101_c::getItemOffsetZ() { return 0.0f; } f32 dAcOTowerHandD101_c::getDist1() { return 300.0f; } f32 dAcOTowerHandD101_c::getDist2() { return 200.0f; } f32 dAcOTowerHandD101_c::getDist3() { return 300.0f; } f32 dAcOTowerHandD101_c::getLinkOffsetX() { return 0.0f; } f32 dAcOTowerHandD101_c::getLinkOffsetY() { return 180.0f; } f32 dAcOTowerHandD101_c::getLinkOffsetZ() { return -180.0f; } f32 dAcOTowerHandD101_c::getFrame1() { return 15.0f; } f32 dAcOTowerHandD101_c::getFrame2() { return 30.0f; } f32 dAcOTowerHandD101_c::getCloseRate() { return 3.65f; } f32 dAcOTowerHandD101_c::getOpenRate() { return 1.0f; } void dAcOTowerHandD101_c::doSomethingHold(f32 arg) { field_0xAF4 = arg; } int dAcOTowerHandD101_c::getDirection() const { return mParams & 0xFF; } int dAcOTowerHandD101_c::getHoldFlag() const { return (mParams >> 8) & 0xFF; } int dAcOTowerHandD101_c::getSceneFlag() const { return (mParams >> 16) & 0xFF; } u32 dAcOTowerHandD101_c::getEventId() const { return (mParams >> 24) & 0xFF; } bool dAcOTowerHandD101_c::getBgWMtx(int index, mMtx_c *&outMtx) { switch (index) { case 0: outMtx = &mWorldMtx; return true; case 1: outMtx = &mLoc1Mtx; return true; case 2: outMtx = &mLoc2Mtx; return true; default: return false; } } bool dAcOTowerHandD101_c::getNodeMtx(const char *nodeName, mMtx_c &outMtx) const { int nodeId = mMdl.getModel().getNodeID(nodeName); if (nodeId == -1) { return false; } mMtx_c mtx; if (mMdl.getModel().getNodeWorldMtx(nodeId, mtx)) { outMtx.copyFrom(mtx); return true; } return false; } bool dAcOTowerHandD101_c::getLoc1Mtx(mMtx_c &outMtx) const { return getNodeMtx(sLocator1, outMtx); } bool dAcOTowerHandD101_c::getLoc2Mtx(mMtx_c &outMtx) const { return getNodeMtx(sLocator2, outMtx); } mVec3_c dAcOTowerHandD101_c::getItemOffset() { return mVec3_c(getItemOffsetX(), getItemOffsetY(), getItemOffsetZ()); } void dAcOTowerHandD101_c::getItemPos(mVec3_c &outVec) const { calcItemPosition(getItemOffset(), outVec); } f32 dAcOTowerHandD101_c::getDist1Sq() { return f32Sq(getDist1()); } f32 dAcOTowerHandD101_c::getDist1PlusDist2() { return getDist1() + getDist2(); } f32 dAcOTowerHandD101_c::getDist1PlusDist2Sq() { return f32Sq(getDist1PlusDist2()); } f32 dAcOTowerHandD101_c::getDist3Sq() { return f32Sq(getDist3()); } void dAcOTowerHandD101_c::getLinkOffsetNormal(mVec3_c &out) { out.set(getLinkOffsetX(), getLinkOffsetY(), getLinkOffsetZ()); } void dAcOTowerHandD101_c::getLinkOffsetMirror(mVec3_c &out) { out.set(-getLinkOffsetX(), getLinkOffsetY(), getLinkOffsetZ()); } void dAcOTowerHandD101_c::getLinkOffset(mVec3_c &out) const { switch (getDirection()) { case 0: getLinkOffsetNormal(out); break; case 1: getLinkOffsetMirror(out); break; } } void dAcOTowerHandD101_c::setSceneFlag(int flag) const { u16 theFlag = flag; if (theFlag >= 255) { return; } SceneflagManager::sInstance->setFlag(mRoomID, theFlag); } void dAcOTowerHandD101_c::unsetSceneFlag(int flag) const { u16 theFlag = flag; if (theFlag >= 255) { return; } SceneflagManager::sInstance->unsetFlag(mRoomID, theFlag); } bool dAcOTowerHandD101_c::checkSceneFlag1(int flag, bool &result) const { u16 theFlag = flag; // I guess 255 is not a valid scene flag bool isValidSceneFlag = theFlag < 255; if (isValidSceneFlag) { result = SceneflagManager::sInstance->checkBoolFlag(mRoomID, theFlag); } return isValidSceneFlag; } void dAcOTowerHandD101_c::setHoldFlag() { setSceneFlag(getHoldFlag()); } void dAcOTowerHandD101_c::unsetHoldFlag() { unsetSceneFlag(getHoldFlag()); } bool dAcOTowerHandD101_c::checkFlag(bool &result) const { return checkSceneFlag1(getSceneFlag(), result); } // ??? inline u32 getFlags() { static volatile u32 FLAGS_1 = 0x00000001; static u32 FLAGS_2 = 0x00100001; // The remarkable bit here is that this does not use an `andc` instruction, // but rather `nor`s FLAGS_1, contrary to the other usages so far u32 f1 = ~FLAGS_1; u32 f2 = FLAGS_2; return f2 & f1; } void dAcOTowerHandD101_c::doEvent() { Event e = Event(getEventId(), mRoomID, getFlags(), (void *)&eventCallback, nullptr); getEventStuff().scheduleEvent(e, 0); } void dAcOTowerHandD101_c::eventCallback(void *arg) { dAcOTowerHandD101_c *self = static_cast(arg); self->mFlags |= 1; dAcPy_c *link = dAcPy_c::LINK; if (link->IfCurrentActionToActor(self, 0x3D)) { link->vt_0x084(self, 0x3D); } } void dAcOTowerHandD101_c::calcItemPosition(const mVec3_c &offset, mVec3_c &outPosition) const { transformMtx(mPosition, mRotation, offset, outPosition); } void dAcOTowerHandD101_c::initializeState_RemainOpen() { mMdl.setFrame(mMdl.getAnm().getStartFrame()); } inline bool dAcOTowerHandD101_c::getItem(dAcItem_c *&outItem, bool &outIsNotWait) { dAcItem_c *item = mHeldItem.get(); if (item == nullptr) { outItem = nullptr; } else { outItem = item; } bool ret = item == nullptr; if (ret) { outIsNotWait = true; } else { outIsNotWait = !outItem->isStateWait(); } return ret; } void dAcOTowerHandD101_c::executeState_RemainOpen() { dAcPy_c *link = dAcPy_c::LINK; mVec3_c pos; getItemPos(pos); f32 linkDistToItem = VECSquareDistance(pos, link->mPosition); if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'wait') { return; } if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'clos') { mStateMgr.changeState(StateID_Close); return; } bool isNotWait; dAcItem_c *item; if (getItem(item, isNotWait)) { if (160000.0f < linkDistToItem) { doEvent(); } } else if (!isNotWait) { if (linkDistToItem < getDist1Sq()) { mStateMgr.changeState(StateID_Close); } } } void dAcOTowerHandD101_c::finalizeState_RemainOpen() {} void dAcOTowerHandD101_c::initializeState_Close() { mMdl.getAnm().setPlayState(m3d::PLAY_MODE_1); mMdl.setRate(getCloseRate()); mEffects.startEffect(PARTICLE_RESOURCE_ID_MAPPING_573_, mPosition, nullptr, nullptr, nullptr, nullptr); startSound(SE_TowerHa_CLENCH); } void dAcOTowerHandD101_c::executeState_Close() { dAcPy_c *link = dAcPy_c::LINK; mVec3_c pos; getItemPos(pos); f32 linkDistToItem = VECSquareDistance(pos, link->mPosition); if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'wait') { mStateMgr.changeState(StateID_Open); return; } if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'clos') { mMdl.play(); if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_RemainClosed); } return; } bool isNotWait; dAcItem_c *item; if (getItem(item, isNotWait)) { if (160000.0f < linkDistToItem) { doEvent(); } mStateMgr.changeState(StateID_Open); } else if (isNotWait) { mStateMgr.changeState(StateID_Open); } else if (link->IfCurrentActionToActor(this, 0x3D)) { mStateMgr.changeState(StateID_Hold); } else { if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_RemainClosed); } else { if (getFrame1() <= mMdl.getAnm().getFrame() && getFrame2() <= mMdl.getAnm().getFrame() && linkDistToItem < getDist1PlusDist2Sq()) { link->set0x439F(this, 5); } mMdl.play(); } } } void dAcOTowerHandD101_c::finalizeState_Close() {} void dAcOTowerHandD101_c::initializeState_Open() { bool isFlagSet; checkFlag(isFlagSet); if (!isFlagSet) { mMdl.getAnm().setPlayState(m3d::PLAY_MODE_3); mMdl.setRate(getOpenRate()); } startSound(SE_TowerHa_OPEN); } void dAcOTowerHandD101_c::executeState_Open() { dAcPy_c *link = dAcPy_c::LINK; mVec3_c pos; getItemPos(pos); f32 linkDistToItem = VECSquareDistance(pos, link->mPosition); if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'wait') { if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_RemainOpen); } else { mMdl.play(); } return; } if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'clos') { if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_Close); } else { mMdl.play(); } return; } bool isNotWait; dAcItem_c *item; if (getItem(item, isNotWait)) { if (160000.0f < linkDistToItem) { doEvent(); } if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_Close); } else { mMdl.play(); } } else if (isNotWait) { if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_Close); } else { mMdl.play(); } } else { if (mMdl.getAnm().isStop()) { mStateMgr.changeState(StateID_Close); } else { mMdl.play(); } } } void dAcOTowerHandD101_c::finalizeState_Open() {} void dAcOTowerHandD101_c::initializeState_RemainClosed() { mMdl.setFrame(mMdl.getAnm().getEndFrame()); bool isFlagSet = false; checkFlag(isFlagSet); if (isFlagSet) { mFlags |= 1; } field_0xB08 = 0; } void dAcOTowerHandD101_c::executeState_RemainClosed() { dAcPy_c *link = dAcPy_c::LINK; mVec3_c pos; getItemPos(pos); f32 linkDistToItem = VECSquareDistance(pos, link->mPosition); if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'wait') { mStateMgr.changeState(StateID_Open); return; } if (EventManager::isInEvent() && getEventStuff().getCurrentEventCommand() == 'clos') { return; } if ((mFlags & 1) == 0) { bool isNotWait; dAcItem_c *item; // Okay here the inline theory completely falls apart because // the 160000.0f is only being loaded once in the middle of that // this suspected inline body does if (getItem(item, isNotWait)) { if (160000.0f < linkDistToItem) { doEvent(); } mStateMgr.changeState(StateID_Open); } else if (isNotWait) { mStateMgr.changeState(StateID_Open); } else { if (getDist1PlusDist2Sq() < 160000.0f) { mStateMgr.changeState(StateID_Open); } } } } void dAcOTowerHandD101_c::finalizeState_RemainClosed() {} void dAcOTowerHandD101_c::initializeState_Hold() { mMdl.getAnm().setPlayState(m3d::PLAY_MODE_1); mMdl.setRate(getCloseRate()); setHoldFlag(); doSomethingHold(0.0f); } void dAcOTowerHandD101_c::executeState_Hold() { dAcPy_c *link = dAcPy_c::LINK; if (link->IfCurrentActionToActor(this, 0x3D)) { mMdl.play(); f32 linkValue = link->vt_0x090(); u32 i = 0; bool keepGoing = true; f32 counter = 0.8f; while (keepGoing == true && i < 4) { if (counter < linkValue) { keepGoing = false; } else { counter -= 0.2f; i++; } } f32 value = field_0xAF4; sLib::addCalcScaledDiff(&value, i * 0.25f, 0.5f, 1.0f); doSomethingHold(value); mMtx_c tmpMtx1; tmpMtx1.transS(mPosition); // Different address calculation here tmpMtx1.ZXYrotM(mRotation.x, mRotation.y, mRotation.z); mVec3_c linkVec; getLinkOffset(linkVec); mMtx_c tmpMtx2; tmpMtx2.transS(linkVec); tmpMtx1 += tmpMtx2; tmpMtx1.YrotM(-0x8000); mMtx_c tmpMtx3; f32 transScale = 100.0f; tmpMtx3.transS(0.0f, 0.0f, field_0xAF4 * transScale); tmpMtx1 += tmpMtx3; tmpMtx1.XrotM(0x4000); link->setTransform(tmpMtx1, 1, 0, 0); } else { mStateMgr.changeState(StateID_Open); } } void dAcOTowerHandD101_c::finalizeState_Hold() { unsetHoldFlag(); }