1#include "Npc/BirdPlayerGlideCtrl.h"
2
3#include "Library/LiveActor/ActorClippingFunction.h"
4#include "Library/LiveActor/ActorFlagFunction.h"
5#include "Library/LiveActor/ActorInitFunction.h"
6#include "Library/LiveActor/ActorInitUtil.h"
7#include "Library/LiveActor/ActorModelFunction.h"
8#include "Library/LiveActor/ActorPoseUtil.h"
9#include "Library/Math/MathUtil.h"
10#include "Library/Matrix/MatrixUtil.h"
11#include "Library/Nerve/NerveSetupUtil.h"
12#include "Library/Nerve/NerveUtil.h"
13#include "Library/Resource/ResourceHolder.h"
14#include "Library/Scene/SceneObjUtil.h"
15#include "Library/Yaml/ByamlUtil.h"
16
17#include "Npc/Bird.h"
18#include "System/GameDataFunction.h"
19#include "Util/DemoUtil.h"
20#include "Util/PlayerUtil.h"
21#include "Util/ScenePlayerCapFunction.h"
22
23namespace {
24NERVE_IMPL(BirdPlayerGlideCtrl, Invalid);
25NERVE_IMPL(BirdPlayerGlideCtrl, ValidOnNose);
26NERVE_IMPL(BirdPlayerGlideCtrl, WaitFlyAway);
27NERVE_IMPL_(BirdPlayerGlideCtrl, ValidOnSitDownHeadNoCap, ValidOnSitDownHead);
28NERVE_IMPL_(BirdPlayerGlideCtrl, ValidOnSitDownHeadWithCap, ValidOnSitDownHead);
29
30NERVES_MAKE_STRUCT(BirdPlayerGlideCtrl, Invalid, ValidOnNose, WaitFlyAway, ValidOnSitDownHeadNoCap,
31 ValidOnSitDownHeadWithCap);
32} // namespace
33
34static const sead::Vector3f gDefaultCapOffset = {0, -50, 0};
35
36BirdPlayerGlideCtrl::BirdPlayerGlideCtrl(const char* name)
37 : al::LiveActor(name), mCapOffset(gDefaultCapOffset) {}
38
39static void setBirdScale(Bird* bird) {
40 if (al::isEqualString(str1: al::getModelName(actor: bird), str2: "BirdCloud") ||
41 al::isEqualString(str1: al::getModelName(actor: bird), str2: "BirdCity") ||
42 al::isEqualString(str1: al::getModelName(actor: bird), str2: "BirdSky"))
43 al::setScaleAll(actor: bird, scale: 0.875f);
44 else
45 al::setScaleAll(actor: bird, scale: 0.7f);
46}
47
48void BirdPlayerGlideCtrl::init(const al::ActorInitInfo& info) {
49 al::initActorSceneInfo(actor: this, info);
50 al::initActorPoseTRSV(actor: this);
51 al::initActorSRT(actor: this, info);
52 al::initActorClipping(actor: this, initInfo: info);
53 al::invalidateClipping(actor: this);
54 al::initNerve(actor: this, nerve: &NrvBirdPlayerGlideCtrl.Invalid, maxStates: 0);
55 al::initExecutorMapObjMovement(actor: this, info);
56
57 mCommonBird = new Bird(al::getLinksActorDisplayName(initInfo: info, linkName: "Bird", linkIndex: 0));
58 al::initLinksActor(actor: mCommonBird, initInfo: info, suffix: "Bird", linkIndex: 0);
59 setBirdScale(mCommonBird);
60 mCommonBird->initGlideOff(&mDestinationMtx, sead::Vector3f::zero, true);
61 mCommonBird->makeActorDead();
62
63 mUfoBird = Bird::createBirdGlideDownUfo("月の鳥", info);
64 setBirdScale(mUfoBird);
65 mUfoBird->initGlideOff(&mDestinationMtx, sead::Vector3f::zero, true);
66
67 al::setSceneObj(user: this, this, sceneObjId: sSceneObjId);
68
69 al::Resource* resource = al::findOrCreateResourceSystemData("BirdPlayerGlideInfo", nullptr);
70 {
71 al::ByamlIter iter{al::getByml(resource, "CapOffsetInfo")};
72
73 s32 len = iter.getSize();
74 if (len > 0) {
75 mCapOffsetInfo.allocBuffer(node_max: len, heap: nullptr);
76
77 for (s32 i = 0; i < iter.getSize(); ++i) {
78 al::ByamlIter subiter;
79 al::getByamlIterByIndex(&subiter, iter, i);
80 sead::Vector3f value = {0, 0, 0};
81 al::tryGetByamlV3f(&value, subiter, "Offset");
82 const char* name = al::getByamlKeyString(subiter, "CapName");
83 mCapOffsetInfo.insert(key: name, value);
84 }
85 }
86 }
87 {
88 al::ByamlIter iter{al::getByml(resource, "CostumeOffsetScaleInfo")};
89
90 s32 len = iter.getSize();
91 if (len > 0) {
92 mCostumeOffsetScaleInfo.allocBuffer(node_max: len, heap: nullptr);
93
94 for (s32 i = 0; i < iter.getSize(); ++i) {
95 al::ByamlIter subiter;
96 al::getByamlIterByIndex(&subiter, iter, i);
97 sead::Vector3f value = {0, 0, 0};
98 al::tryGetByamlV3f(&value, subiter, "OffsetScale");
99 const char* name = al::getByamlKeyString(subiter, "CostumeName");
100 mCostumeOffsetScaleInfo.insert(key: name, value);
101 }
102 }
103 }
104
105 makeActorAlive();
106}
107
108void BirdPlayerGlideCtrl::initAfterPlacement() {
109 if (mCapOffsetInfo.isBufferReady()) {
110 const char* name = GameDataFunction::getCurrentCapTypeName(accessor: this);
111 if (auto* node = mCapOffsetInfo.find(key: name))
112 mCapOffset.set(node->value());
113 }
114 if (mCostumeOffsetScaleInfo.isBufferReady()) {
115 const char* name = GameDataFunction::getCurrentCostumeTypeName(accessor: this);
116 if (auto* node = mCostumeOffsetScaleInfo.find(key: name))
117 mCostumeOffsetScale.set(node->value());
118 }
119}
120
121void BirdPlayerGlideCtrl::validateGlideOnNose() {
122 mIsValidOnNose = true;
123 if (al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.Invalid)) {
124 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnNose);
125 return;
126 }
127 if (!al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnNose) && mBird)
128 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
129}
130
131void BirdPlayerGlideCtrl::invalidateGlideOnNose() {
132 mIsValidOnNose = false;
133 if (mBird)
134 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
135 else if (mIsValidOnSitDownHead)
136 if (PlayerCapFunction::isEnableBirdLandPlayerCapOn(this))
137 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap);
138 else
139 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadNoCap);
140 else
141 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.Invalid);
142}
143
144void BirdPlayerGlideCtrl::validateGlideOnSitDownHead() {
145 mIsValidOnSitDownHead = true;
146 if (mIsValidOnNose)
147 return;
148 if (al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap) ||
149 al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadNoCap))
150 return;
151 if (mBird)
152 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
153 else if (PlayerCapFunction::isEnableBirdLandPlayerCapOn(this))
154 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap);
155 else
156 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadNoCap);
157}
158
159void BirdPlayerGlideCtrl::invalidateGlideOnSitDownHead() {
160 mIsValidOnSitDownHead = false;
161 if (!al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap) &&
162 !al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadNoCap))
163 return;
164 if (mBird)
165 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
166 else
167 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.Invalid);
168}
169
170void BirdPlayerGlideCtrl::addDemoActorAndFlyAway() {
171 if (!mBird)
172 return;
173 rs::addDemoActor(this, false);
174 rs::addDemoActor(mBird, false);
175 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
176}
177
178void BirdPlayerGlideCtrl::exeInvalid() {
179 if (al::isFirstStep(user: this) && mBird && al::isAlive(actor: mBird)) {
180 mBird->tryStartFlyAway();
181 mBird = nullptr;
182 }
183}
184
185static bool tryCalcGlideOnNoseMtx(sead::Matrix34f* out, const al::LiveActor* actor) {
186 sead::Matrix34f mtx = sead::Matrix34f::ident;
187 if (!rs::tryCalcPlayerModelNoseJointMtx(&mtx, actor))
188 return false;
189 sead::Vector3f side = mtx.getBase(axis: 0);
190 sead::Vector3f up = mtx.getBase(axis: 1);
191 sead::Vector3f front = mtx.getBase(axis: 2);
192 sead::Vector3f pos = mtx.getBase(axis: 3);
193 sead::Vector3f offset = {13, -2.5, 0};
194 al::makeMtxFrontUpPos(outMtx: out, front, up, pos: pos + offset.x * side + offset.y * up + offset.z * front);
195 return true;
196}
197
198void BirdPlayerGlideCtrl::exeValidOnNose() {
199 if (al::isFirstStep(user: this))
200 mGlideDownTime = al::getRandom(min: 180, max: 1200);
201 if (al::isGreaterEqualStep(user: this, step: mGlideDownTime)) {
202 if (al::isStep(user: this, step: mGlideDownTime)) {
203 if (!mBird) {
204 mBird = GameDataFunction::isGameClear(accessor: this) && al::getRandom() < 0x1p-9 ?
205 mUfoBird :
206 mCommonBird;
207 }
208 if (al::isDead(actor: mBird) && tryCalcGlideOnNoseMtx(out: &mDestinationMtx, actor: mBird))
209 mBird->tryStartGlideDown();
210 }
211 if (al::isAlive(actor: mBird))
212 tryCalcGlideOnNoseMtx(out: &mDestinationMtx, actor: mBird);
213 }
214}
215
216static bool tryCalcGlideOnSitDownHeadMtx(sead::Matrix34f* out, const al::LiveActor* actor,
217 const sead::Vector3f& offset) {
218 sead::Vector3f pos = {0, 0, 0};
219 sead::Vector3f front = {0, 0, 0};
220 sead::Vector3f up = {0, 0, 0};
221 sead::Vector3f side = {0, 0, 0};
222 if (!rs::tryCalcPlayerModelHeadJointPos(&pos, actor) ||
223 !rs::tryCalcPlayerModelHeadJointFront(&front, actor) ||
224 !rs::tryCalcPlayerModelHeadJointUp(&up, actor) ||
225 !rs::tryCalcPlayerModelHeadJointSide(&side, actor))
226 return false;
227 al::makeMtxFrontUpPos(outMtx: out, front, up, pos: pos + offset.x * side + offset.y * up + offset.z * front);
228 return true;
229}
230
231static const sead::Vector3f gNoCapOffset = {0, -42.5, 0};
232
233void BirdPlayerGlideCtrl::exeValidOnSitDownHead() {
234 if (al::isFirstStep(user: this))
235 mGlideDownTime = al::getRandom(min: 180, max: 1200);
236 if (al::isGreaterEqualStep(user: this, step: mGlideDownTime)) {
237 sead::Vector3f cap_offset;
238 if (al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap))
239 cap_offset = mCapOffset;
240 else
241 cap_offset = gNoCapOffset;
242 sead::Vector3f costume_offset_scale = mCostumeOffsetScale;
243 sead::Vector3f offset = {cap_offset.x * costume_offset_scale.x,
244 cap_offset.y * costume_offset_scale.y,
245 cap_offset.z * costume_offset_scale.z};
246 if (tryCalcGlideOnSitDownHeadMtx(out: &mDestinationMtx, actor: this, offset) &&
247 al::isStep(user: this, step: mGlideDownTime)) {
248 if (!mBird)
249 mBird = mCommonBird;
250 mBird->tryStartGlideDown();
251 }
252 }
253 if (al::isNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap) !=
254 PlayerCapFunction::isEnableBirdLandPlayerCapOn(this)) {
255 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.WaitFlyAway);
256 }
257}
258
259void BirdPlayerGlideCtrl::exeWaitFlyAway() {
260 if (mBird && !al::isDead(actor: mBird))
261 mBird->tryStartFlyAway();
262 else if (mIsValidOnNose)
263 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnNose);
264 else if (mIsValidOnSitDownHead)
265 if (PlayerCapFunction::isEnableBirdLandPlayerCapOn(this))
266 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadWithCap);
267 else
268 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.ValidOnSitDownHeadNoCap);
269 else
270 al::setNerve(user: this, nerve: &NrvBirdPlayerGlideCtrl.Invalid);
271}
272
273namespace rs {
274
275void validateGlideBirdOnPlayerNose(const al::LiveActor* player) {
276 if (al::isExistSceneObj(user: player, sceneObjId: BirdPlayerGlideCtrl::sSceneObjId))
277 al::getSceneObj<BirdPlayerGlideCtrl>(user: player)->validateGlideOnNose();
278}
279
280void invalidateGlideBirdOnPlayerNose(const al::LiveActor* player) {
281 if (al::isExistSceneObj(user: player, sceneObjId: BirdPlayerGlideCtrl::sSceneObjId))
282 al::getSceneObj<BirdPlayerGlideCtrl>(user: player)->invalidateGlideOnNose();
283}
284
285void validateGlideBirdOnSitDownPlayerHead(const al::LiveActor* player) {
286 if (al::isExistSceneObj(user: player, sceneObjId: BirdPlayerGlideCtrl::sSceneObjId))
287 al::getSceneObj<BirdPlayerGlideCtrl>(user: player)->validateGlideOnSitDownHead();
288}
289
290void invalidateGlideBirdOnSitDownPlayerHead(const al::LiveActor* player) {
291 if (al::isExistSceneObj(user: player, sceneObjId: BirdPlayerGlideCtrl::sSceneObjId))
292 al::getSceneObj<BirdPlayerGlideCtrl>(user: player)->invalidateGlideOnSitDownHead();
293}
294
295bool isPlayerSitDownChair(const Bird* bird) {
296 return al::isExistSceneObj(user: bird, sceneObjId: BirdPlayerGlideCtrl::sSceneObjId) &&
297 al::getSceneObj<BirdPlayerGlideCtrl>(user: bird)->isValidOnSitDownHead();
298}
299
300} // namespace rs
301