| 1 | #include "MapObj/ElectricWire/ElectricWireRailKeeper.h" |
| 2 | |
| 3 | #include "Library/Camera/CameraTicket.h" |
| 4 | #include "Library/Camera/CameraUtil.h" |
| 5 | #include "Library/LiveActor/ActorClippingFunction.h" |
| 6 | #include "Library/LiveActor/ActorFlagFunction.h" |
| 7 | #include "Library/LiveActor/ActorInitFunction.h" |
| 8 | #include "Library/LiveActor/ActorInitInfo.h" |
| 9 | #include "Library/LiveActor/ActorInitUtil.h" |
| 10 | #include "Library/Math/MathUtil.h" |
| 11 | #include "Library/Nerve/NerveSetupUtil.h" |
| 12 | #include "Library/Nerve/NerveUtil.h" |
| 13 | #include "Library/Placement/PlacementFunction.h" |
| 14 | #include "Library/Placement/PlacementInfo.h" |
| 15 | #include "Library/Rail/RailUtil.h" |
| 16 | #include "Library/Stage/StageSwitchUtil.h" |
| 17 | #include "Library/Thread/FunctorV0M.h" |
| 18 | |
| 19 | #include "MapObj/ElectricWire/ElectricWire.h" |
| 20 | |
| 21 | namespace { |
| 22 | NERVE_IMPL(ElectricWireRailKeeper, Standby) |
| 23 | NERVE_IMPL(ElectricWireRailKeeper, Wait) |
| 24 | |
| 25 | NERVES_MAKE_NOSTRUCT(ElectricWireRailKeeper, Standby, Wait) |
| 26 | } // namespace |
| 27 | |
| 28 | ElectricWireRailKeeper::ElectricWireRailKeeper(const char* name) : LiveActor(name) {} |
| 29 | |
| 30 | ElectricWireRailKeeper::ElectricWireRailKeeper(const char* name, al::LiveActor* wire) |
| 31 | : LiveActor(name), mElectricWire(reinterpret_cast<ElectricWire*>(wire)) {} |
| 32 | |
| 33 | void ElectricWireRailKeeper::init(const al::ActorInitInfo& info) { |
| 34 | using ElectricWireRailKeeperFunctor = |
| 35 | al::FunctorV0M<ElectricWireRailKeeper*, void (ElectricWireRailKeeper::*)()>; |
| 36 | |
| 37 | al::initActorSceneInfo(actor: this, info); |
| 38 | al::initActorPoseTRSV(actor: this); |
| 39 | al::initActorSRT(actor: this, info); |
| 40 | al::initActorClipping(actor: this, initInfo: info); |
| 41 | al::initStageSwitch(this, info); |
| 42 | |
| 43 | ElectricWire* wire = mElectricWire; |
| 44 | al::tryGetArg(arg: &mIsShowLine, initInfo: info, key: "IsShowLine" ); |
| 45 | if (wire->isElectricWireRadio()) |
| 46 | mIsShowLine = false; |
| 47 | al::tryGetArg(arg: &mIsThrowaway, initInfo: info, key: "IsThrowaway" ); |
| 48 | if (al::tryGetLinksTrans(trans: &mPlayerPosOnVerticalMove, initInfo: info, linkName: "PlayerHeadPosOnVerticalMove" )) |
| 49 | mPosType = PosType::HEAD; |
| 50 | else if (al::tryGetLinksTrans(trans: &mPlayerPosOnVerticalMove, initInfo: info, linkName: "PlayerBottomPosOnVerticalMove" )) |
| 51 | mPosType = PosType::BOTTOM; |
| 52 | if (!al::isExistRail(initInfo: info, linkName: "Rail" )) { |
| 53 | makeActorDead(); |
| 54 | return; |
| 55 | } |
| 56 | initRailKeeper(info, linkName: "Rail" ); |
| 57 | s32 pointNum = al::getRailPointNum(railHolder: this); |
| 58 | s32 needCameraPointNum = 0; |
| 59 | for (s32 i = 0; i < pointNum; ++i) |
| 60 | if (isRailPointIsNeedCamera(index: i) || isRailPointIsNeedStartCameraHackEnd(index: i)) |
| 61 | ++needCameraPointNum; |
| 62 | if (needCameraPointNum > 0) |
| 63 | mCameraTickets.allocBuffer(ptrNumMax: needCameraPointNum, heap: nullptr); |
| 64 | for (s32 i = 0; i < pointNum; ++i) { |
| 65 | al::CameraTicket* ticket = nullptr; |
| 66 | // Yes, memory leak in new FixedSafeString |
| 67 | if (isRailPointIsNeedCamera(index: i)) { |
| 68 | auto* id = new sead::FixedSafeString<0x20>(); |
| 69 | id->format(formatStr: "%d" , i); |
| 70 | ticket = al::initObjectCamera(user: mElectricWire, actorInitInfo: info, id->cstr(), nullptr); |
| 71 | } |
| 72 | al::CameraTicket* ticketHack = nullptr; |
| 73 | if (isRailPointIsNeedStartCameraHackEnd(index: i)) { |
| 74 | auto* id = new sead::FixedSafeString<0x20>(); |
| 75 | id->format(formatStr: "%d(Entrance)" , i); |
| 76 | ticketHack = al::initEntranceCamera(user: mElectricWire, placementInfo: *info.placementInfo, id->cstr()); |
| 77 | } |
| 78 | if (ticket != nullptr || ticketHack != nullptr) |
| 79 | mCameraTickets.pushBack(ptr: new TicketHolder{.ticket: ticket, .ticketHackEnd: ticketHack, .pointIdx: i}); |
| 80 | } |
| 81 | al::initExecutorUpdate(actor: this, info, "地形オブジェ[Movement]" ); |
| 82 | al::initNerve(actor: this, nerve: &Wait, maxStates: 0); |
| 83 | makeActorAlive(); |
| 84 | if (al::listenStageSwitchOnAppear( |
| 85 | user: this, action: ElectricWireRailKeeperFunctor(this, &ElectricWireRailKeeper::appearBySwitch))) |
| 86 | kill(); |
| 87 | al::listenStageSwitchOnKill( |
| 88 | user: this, action: ElectricWireRailKeeperFunctor(this, &ElectricWireRailKeeper::killBySwitch)); |
| 89 | } |
| 90 | |
| 91 | void ElectricWireRailKeeper::appear() { |
| 92 | LiveActor::appear(); |
| 93 | mElectricWire->tryUpdateDisplayModel(); |
| 94 | al::setNerve(user: this, nerve: &Wait); |
| 95 | } |
| 96 | |
| 97 | void ElectricWireRailKeeper::kill() { |
| 98 | LiveActor::kill(); |
| 99 | mElectricWire->tryUpdateDisplayModel(); |
| 100 | } |
| 101 | |
| 102 | bool ElectricWireRailKeeper::isRailPointFaceToCameraDir(s32 index) const { |
| 103 | al::PlacementInfo* info = getRailPointInfo(index); |
| 104 | bool ret; |
| 105 | if (al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsFaceToCamera" )) |
| 106 | return ret; |
| 107 | return false; |
| 108 | } |
| 109 | |
| 110 | bool ElectricWireRailKeeper::isRailPointPlacementPole(s32 index) const { |
| 111 | al::PlacementInfo* info = getRailPointInfo(index); |
| 112 | bool ret; |
| 113 | if (al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsPlacementPole" )) |
| 114 | return ret; |
| 115 | return false; |
| 116 | } |
| 117 | |
| 118 | bool ElectricWireRailKeeper::isRailPointEnableTargetEndCollision(s32 index) const { |
| 119 | al::PlacementInfo* info = getRailPointInfo(index); |
| 120 | bool ret = true; |
| 121 | al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsEnableTargetEndCollision" ); |
| 122 | return ret; |
| 123 | } |
| 124 | |
| 125 | bool ElectricWireRailKeeper::isRailPointIgnore(s32 index) const { |
| 126 | al::PlacementInfo* info = getRailPointInfo(index); |
| 127 | bool ret; |
| 128 | if (al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsIgnore" )) |
| 129 | return ret; |
| 130 | return false; |
| 131 | } |
| 132 | |
| 133 | bool ElectricWireRailKeeper::isRailPointSpringFix(s32 index) const { |
| 134 | al::PlacementInfo* info = getRailPointInfo(index); |
| 135 | bool ret; |
| 136 | if (al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsSpringFix" )) |
| 137 | return ret; |
| 138 | return false; |
| 139 | } |
| 140 | |
| 141 | bool ElectricWireRailKeeper::isRailPointIsNeedCamera(s32 index) const { |
| 142 | al::PlacementInfo* info = getRailPointInfo(index); |
| 143 | bool ret = false; |
| 144 | al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsNeedCamera" ); |
| 145 | return ret; |
| 146 | } |
| 147 | |
| 148 | bool ElectricWireRailKeeper::isRailPointIsNeedStartCameraHackEnd(s32 index) const { |
| 149 | al::PlacementInfo* info = getRailPointInfo(index); |
| 150 | bool ret = false; |
| 151 | al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsNeedStartCameraHackEnd" ); |
| 152 | return ret; |
| 153 | } |
| 154 | |
| 155 | bool ElectricWireRailKeeper::isRailPointIsExpandRailSelectableAngle(s32 index) const { |
| 156 | al::PlacementInfo* info = getRailPointInfo(index); |
| 157 | bool ret = false; |
| 158 | al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsExpandRailSelectableAngle" ); |
| 159 | return ret; |
| 160 | } |
| 161 | |
| 162 | bool ElectricWireRailKeeper::isRailPointIsDisplayPointModelForce(s32 index) const { |
| 163 | al::PlacementInfo* info = getRailPointInfo(index); |
| 164 | bool ret = false; |
| 165 | al::tryGetArg(arg: &ret, placementInfo: *info, key: "IsDisplayPointModelForce" ); |
| 166 | return ret; |
| 167 | } |
| 168 | |
| 169 | bool ElectricWireRailKeeper::tryGetRailPointOutDir(sead::Vector3f* out, s32 index) const { |
| 170 | al::PlacementInfo* info = getRailPointInfo(index); |
| 171 | al::PlacementInfo linksInfo{}; |
| 172 | if (al::tryGetLinksInfo(railPlacementInfo: &linksInfo, placementInfo: *info, linkName: "DestinationPoint" )) { |
| 173 | sead::Vector3f linksTrans; |
| 174 | if (!al::tryGetLinksTrans(trans: &linksTrans, placementInfo: *info, linkName: "DestinationPoint" )) |
| 175 | return false; |
| 176 | sead::Vector3f railPointPos{}; |
| 177 | al::calcRailPointPos(&railPointPos, railHolder: this, index); |
| 178 | out->set(linksTrans); |
| 179 | *out -= railPointPos; |
| 180 | return al::tryNormalizeOrZero(out); |
| 181 | } else { |
| 182 | bool isOutToRailPointDir = false; |
| 183 | al::tryGetArg(arg: &isOutToRailPointDir, placementInfo: *info, key: "IsOutToRailPointDir" ); |
| 184 | if (isOutToRailPointDir) |
| 185 | return al::tryGetUp(up: out, placementInfo: *info); |
| 186 | } |
| 187 | return false; |
| 188 | } |
| 189 | |
| 190 | bool ElectricWireRailKeeper::tryGetRailPointDestinationTrans(sead::Vector3f* out, s32 index) const { |
| 191 | al::PlacementInfo* info = getRailPointInfo(index); |
| 192 | return al::tryGetLinksTrans(trans: out, placementInfo: *info, linkName: "DestinationPoint" ); |
| 193 | } |
| 194 | |
| 195 | bool ElectricWireRailKeeper::tryGetRailPointFastenerMoveLimitAreaFlag(s32* out, s32 index) const { |
| 196 | al::PlacementInfo* info = getRailPointInfo(index); |
| 197 | return al::tryGetArg(arg: out, placementInfo: *info, key: "FastenerMoveLimitAreaFlag" ); |
| 198 | } |
| 199 | |
| 200 | al::CameraTicket* ElectricWireRailKeeper::findRailPointCameraTicket(s32 pointIdx) const { |
| 201 | for (s32 i = 0; i < mCameraTickets.size(); ++i) { |
| 202 | TicketHolder* holder = mCameraTickets[i]; |
| 203 | if (holder->pointIdx == pointIdx) |
| 204 | return holder->ticket; |
| 205 | } |
| 206 | return nullptr; |
| 207 | } |
| 208 | |
| 209 | const al::CameraTicket* |
| 210 | ElectricWireRailKeeper::findRailPointStartCameraHackEndTicket(s32 pointIdx) const { |
| 211 | for (s32 i = 0; i < mCameraTickets.size(); ++i) { |
| 212 | TicketHolder* ticket = mCameraTickets[i]; |
| 213 | if (ticket->pointIdx == pointIdx) |
| 214 | return ticket->ticketHackEnd; |
| 215 | } |
| 216 | return nullptr; |
| 217 | } |
| 218 | |
| 219 | bool ElectricWireRailKeeper::tryGetPlayerHeadPosOnVerticalMove( |
| 220 | sead::Vector3f* playerHeadPos) const { |
| 221 | if (mPosType == PosType::HEAD) { |
| 222 | playerHeadPos->set(mPlayerPosOnVerticalMove); |
| 223 | return true; |
| 224 | } |
| 225 | return false; |
| 226 | } |
| 227 | |
| 228 | bool ElectricWireRailKeeper::tryGetPlayerBottomPosOnVerticalMove( |
| 229 | sead::Vector3f* playerBottomPos) const { |
| 230 | if (mPosType == PosType::BOTTOM) { |
| 231 | playerBottomPos->set(mPlayerPosOnVerticalMove); |
| 232 | return true; |
| 233 | } |
| 234 | return false; |
| 235 | } |
| 236 | |
| 237 | void ElectricWireRailKeeper::endCameraIfActive() { |
| 238 | for (s32 i = 0; i < mCameraTickets.size(); ++i) { |
| 239 | TicketHolder* holder = mCameraTickets[i]; |
| 240 | if (al::isActiveCamera(ticket: holder->ticket)) |
| 241 | al::endCamera(user: mElectricWire, ticket: holder->ticket, -1, false); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | al::PlacementInfo* ElectricWireRailKeeper::getRailPointInfo(s32 index) const { |
| 246 | return al::getRailPointInfo(railHolder: this, index); |
| 247 | } |
| 248 | |
| 249 | void ElectricWireRailKeeper::appearBySwitch() { |
| 250 | if (al::isAlive(actor: this)) |
| 251 | return; |
| 252 | |
| 253 | appear(); |
| 254 | al::invalidateClipping(actor: this); |
| 255 | al::setNerve(user: this, nerve: &Standby); |
| 256 | } |
| 257 | |
| 258 | void ElectricWireRailKeeper::killBySwitch() { |
| 259 | if (al::isDead(actor: this)) |
| 260 | return; |
| 261 | kill(); |
| 262 | } |
| 263 | |
| 264 | void ElectricWireRailKeeper::exeStandby() { |
| 265 | if (al::isGreaterStep(user: this, step: 10)) { |
| 266 | al::validateClipping(actor: this); |
| 267 | al::setNerve(user: this, nerve: &Wait); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | void ElectricWireRailKeeper::exeWait() {} |
| 272 | |
| 273 | bool ElectricWireRailKeeper::isNerveStandby() const { |
| 274 | return al::isNerve(user: this, nerve: &Standby); |
| 275 | } |
| 276 | |