1#include "Amiibo/AmiiboNpcDirector.h"
2
3#include <time/seadDateTime.h>
4
5#include "Library/Audio/System/AudioKeeper.h"
6#include "Library/Layout/LayoutActorUtil.h"
7#include "Library/LiveActor/ActorInitUtil.h"
8#include "Library/Message/MessageHolder.h"
9#include "Library/Message/MessageTagDataHolder.h"
10#include "Library/Nfp/NfpFunction.h"
11#include "Library/Nfp/NfpTypes.h"
12#include "Library/Scene/SceneObjUtil.h"
13#include "Library/Se/SeFunction.h"
14
15#include "Amiibo/SearchAmiiboDataTable.h"
16#include "Layout/AmiiboNpcLayout.h"
17#include "Layout/ShopLayoutInfo.h"
18#include "System/GameDataFunction.h"
19#include "System/ProjectNfpDirector.h"
20#include "Util/AmiiboUtil.h"
21#include "Util/ClothUtil.h"
22#include "Util/TimeUtil.h"
23
24AmiiboNpcDirector::AmiiboNpcDirector() : mNfpInfo(new al::NfpInfo()) {
25 for (s32 i = 0; i < 3; i++)
26 mAmiiboNameCstr[i] = nullptr;
27}
28
29void AmiiboNpcDirector::init(ProjectNfpDirector* nfpDirector, al::AudioDirector* audioDirector) {
30 mNfpDirector = nfpDirector;
31 mAudioKeeper =
32 alAudioKeeperFunction::createAudioKeeper(audioDirector, "AmiiboNpcDirector", nullptr);
33
34 mTagDataHolder = new al::MessageTagDataHolder(6);
35 al::registerMessageTagDataAmiiboName(mTagDataHolder, "First", &mAmiiboNameCstr[0]);
36 al::registerMessageTagDataAmiiboName(mTagDataHolder, "Second", &mAmiiboNameCstr[1]);
37 al::registerMessageTagDataAmiiboName(mTagDataHolder, "Third", &mAmiiboNameCstr[2]);
38 al::registerMessageTagDataAmiiboName(mTagDataHolder, "Touch", &mTouchAmiiboNameCstr);
39 al::registerMessageTagDataString(mTagDataHolder, "ClothName", &mClothName);
40 al::registerMessageTagDataString(mTagDataHolder, "CapName", &mCapName);
41}
42
43void AmiiboNpcDirector::initAfterPlacementSceneObj(const al::ActorInitInfo& initInfo) {
44 const al::LayoutInitInfo& layoutInitInfo = al::getLayoutInitInfo(initInfo);
45 mNpcLayout = new AmiiboNpcLayout(layoutInitInfo);
46
47 mSearchDataTable = rs::getSearchAmiiboData(user: mNpcLayout);
48 updateSearchAmiiboName();
49
50 const char* currentCostumeTypeName = GameDataFunction::getCurrentCostumeTypeName(accessor: mNpcLayout);
51 const sead::PtrArray<ShopItem::ItemInfo>& clothList = rs::getClothList(accessor: mNpcLayout);
52
53 for (s32 i = 0; i < clothList.size(); i++) {
54 if (al::isEqualString(str1: clothList.at(pos: i)->name, str2: currentCostumeTypeName)) {
55 mClothName = rs::getDisplayName(user: mNpcLayout, itemInfo: *clothList.at(pos: i));
56 break;
57 }
58 }
59
60 const char* currentCapTypeName = GameDataFunction::getCurrentCapTypeName(accessor: mNpcLayout);
61 const sead::PtrArray<ShopItem::ItemInfo>& capList = rs::getCapList(accessor: mNpcLayout);
62
63 for (s32 i = 0; i < capList.size(); i++) {
64 if (al::isEqualString(str1: capList.at(pos: i)->name, str2: currentCapTypeName)) {
65 mCapName = rs::getDisplayName(user: mNpcLayout, itemInfo: *capList.at(pos: i));
66 break;
67 }
68 }
69}
70
71void AmiiboNpcDirector::updateSearchAmiiboName() {
72 s32 dataNumMax = mSearchDataTable->getDataNumMax();
73 s32 assignedIndex = 0;
74
75 for (s32 i = 0; i < dataNumMax; i++) {
76 if (mSearchDataTable->isInvalidId(index: i))
77 continue;
78
79 bool isAssigned = false;
80 mAmiiboName[i] =
81 rs::getAmiiboMstxtLabel(&isAssigned, user: mNpcLayout, mSearchDataTable->getId(index: i),
82 mSearchDataTable->getNumberingId(index: i), assignedIndex);
83 mAmiiboNameCstr[i] = mAmiiboName[i].cstr();
84 if (isAssigned)
85 assignedIndex++;
86 }
87}
88
89bool AmiiboNpcDirector::requestAppearAmiiboLayout() {
90 if (isEndAmiiboLayout()) {
91 mNpcLayout->appear();
92 return true;
93 }
94 return false;
95}
96
97void AmiiboNpcDirector::requestDecideAmiiboLayout() {
98 mNpcLayout->decide();
99}
100
101void AmiiboNpcDirector::requestEndAmiiboLayout() {
102 mNpcLayout->end();
103}
104
105bool AmiiboNpcDirector::isEndAmiiboLayout() {
106 return al::isDead(mNpcLayout);
107}
108
109void AmiiboNpcDirector::registerSearchAmiibo(s32 id, s32 numberingId, u64 searchStartTime) {
110 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++) {
111 if (mSearchDataTable->isInvalidId(index: i)) {
112 mSearchDataTable->setId(id, numberingId, index: i);
113 mSearchDataTable->setSearchStartTime(searchStartTime, index: i);
114 updateSearchAmiiboName();
115 return;
116 }
117 }
118}
119
120void AmiiboNpcDirector::deleteSearchEndAmiibo() {
121 s32 assignedIndex = 0;
122 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++) {
123 if (mSearchDataTable->isInvalidId(index: i)) {
124 if (i != assignedIndex) {
125 mSearchDataTable->copy(index: assignedIndex, other: i);
126 mSearchDataTable->initByIndex(index: i);
127 }
128 break;
129 }
130
131 if ((s64)mTime - (s64)mSearchDataTable->getSearchStartTime(index: i) > 300) {
132 mSearchDataTable->initByIndex(index: i);
133 continue;
134 }
135
136 if (i != assignedIndex) {
137 mSearchDataTable->copy(index: assignedIndex, other: i);
138 mSearchDataTable->initByIndex(index: i);
139 assignedIndex++;
140 } else {
141 assignedIndex++;
142 }
143 }
144 updateSearchAmiiboName();
145}
146
147bool AmiiboNpcDirector::isSearchAmiibo(s32 id) {
148 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++)
149 if (mSearchDataTable->getId(index: i) == id)
150 return true;
151 return false;
152}
153
154u32 AmiiboNpcDirector::getSearchAmiiboNum() const {
155 u32 amiiboNum = 0;
156
157 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++) {
158 if (mSearchDataTable->isInvalidId(index: i))
159 continue;
160 if (mTime < mSearchDataTable->getSearchStartTime(index: i) + 300)
161 amiiboNum++;
162 }
163 return amiiboNum;
164}
165
166u32 AmiiboNpcDirector::getSearchEndAmiiboNum() const {
167 u32 amiiboNum = 0;
168
169 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++) {
170 if (mSearchDataTable->isInvalidId(index: i))
171 continue;
172 if (mTime > mSearchDataTable->getSearchStartTime(index: i) + 300)
173 amiiboNum++;
174 }
175 return amiiboNum;
176}
177
178u32 AmiiboNpcDirector::getSearchEndAmiiboNumRealTime() const {
179 u64 now = sead::DateTime(0).setNow();
180 u32 amiiboNum = 0;
181
182 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++) {
183 if (mSearchDataTable->isInvalidId(index: i))
184 continue;
185 if (now - mSearchDataTable->getSearchStartTime(index: i) > 300)
186 amiiboNum++;
187 }
188 return amiiboNum;
189}
190
191bool AmiiboNpcDirector::isEnableSearchAmiibo() {
192 for (s32 i = 0; i < mSearchDataTable->getDataNumMax(); i++)
193 if (mSearchDataTable->isInvalidId(index: i))
194 return true;
195 return false;
196}
197
198void AmiiboNpcDirector::setTouchAmiiboName(s32 id, s32 numberingId) {
199 mTouchAmiiboName = rs::getAmiiboMstxtLabel(nullptr, user: mNpcLayout, id, numberingId, 0);
200 mTouchAmiiboNameCstr = mTouchAmiiboName.cstr();
201}
202
203void AmiiboNpcDirector::trySetAmiiboCostumeName(s32 id) {
204 al::NfpInfo* nfpInfo = mNfpInfo;
205 al::NfpCharacterId charId{};
206 al::tryGetCharacterId(characterId: &charId, nfpInfo: *nfpInfo);
207
208 s32 numberingId = -1;
209 al::tryGetNumberingId(&numberingId, nfpInfo: *nfpInfo);
210
211 ShopItem::ItemInfo* itemA = nullptr;
212 ShopItem::ItemInfo* itemB = nullptr;
213 rs::tryFindAmiiboCostumeItemInfo(&itemA, &itemB, characterId: charId, numberingId, user: mNpcLayout);
214
215 if (itemA != nullptr)
216 mClothName = rs::getDisplayName(user: mNpcLayout, itemInfo: *itemA);
217 if (itemB != nullptr)
218 mCapName = rs::getDisplayName(user: mNpcLayout, itemInfo: *itemB);
219}
220
221void AmiiboNpcDirector::checkTimeReverseAndRestore() {
222 s32 dataNumMax = mSearchDataTable->getDataNumMax();
223 for (s32 i = 0; i < dataNumMax; i++) {
224 if (mSearchDataTable->isInvalidId(index: i))
225 continue;
226 u64 startTime = mSearchDataTable->getSearchStartTime(index: i);
227 if (rs::checkTimeReverseAndRestore(&startTime, mTime))
228 mSearchDataTable->setSearchStartTime(searchStartTime: startTime, index: i);
229 }
230}
231
232al::NfpInfo* AmiiboNpcDirector::tryGetTriggerTouchNfpInfo() {
233 al::NfpInfo* nfpInfo = mNfpDirector->tryGetTriggerTouchNfpInfo();
234 if (nfpInfo == nullptr)
235 return nullptr;
236
237 *mNfpInfo = *nfpInfo;
238 al::startSe(this, "TouchAmiibo");
239 return mNfpInfo;
240}
241
242namespace AmiiboFunction {
243
244inline AmiiboNpcDirector* getAmiiboNpcDirector(const al::IUseSceneObjHolder* objHolder) {
245 return al::getSceneObj<AmiiboNpcDirector>(user: objHolder);
246}
247
248al::NfpInfo* tryGetTriggerTouchNfpInfo(const al::IUseSceneObjHolder* objHolder) {
249 return getAmiiboNpcDirector(objHolder)->tryGetTriggerTouchNfpInfo();
250}
251
252al::NfpInfo* getLastTriggerTouchNfpInfo(const al::IUseSceneObjHolder* objHolder) {
253 return getAmiiboNpcDirector(objHolder)->getNfpInfo();
254}
255
256void startNfpTouch(const al::IUseSceneObjHolder* objHolder) {
257 return getAmiiboNpcDirector(objHolder)->getProjectNfpDirector()->start();
258}
259
260void stopNfpTouch(const al::IUseSceneObjHolder* objHolder) {
261 return getAmiiboNpcDirector(objHolder)->getProjectNfpDirector()->stop();
262}
263
264bool isNfpErrorHandled(const al::IUseSceneObjHolder* objHolder) {
265 return getAmiiboNpcDirector(objHolder)->getProjectNfpDirector()->isNfpErrorHandled();
266}
267
268bool requestAppearAmiiboLayout(const al::IUseSceneObjHolder* objHolder) {
269 return getAmiiboNpcDirector(objHolder)->requestAppearAmiiboLayout();
270}
271
272void requestDecideAmiiboLayout(const al::IUseSceneObjHolder* objHolder) {
273 return getAmiiboNpcDirector(objHolder)->requestDecideAmiiboLayout();
274}
275
276void requestEndAmiiboLayout(const al::IUseSceneObjHolder* objHolder) {
277 return getAmiiboNpcDirector(objHolder)->requestEndAmiiboLayout();
278}
279
280bool isEndAmiiboLayout(const al::IUseSceneObjHolder* objHolder) {
281 return al::isDead(getAmiiboNpcDirector(objHolder)->getAmiiboNpcLayout());
282}
283
284AmiiboNpcLayout* getAmiiboTouchLayout(const al::IUseSceneObjHolder* objHolder) {
285 return getAmiiboNpcDirector(objHolder)->getAmiiboNpcLayout();
286}
287
288u32 getSearchAmiiboNum(const al::IUseSceneObjHolder* objHolder) {
289 return getAmiiboNpcDirector(objHolder)->getSearchAmiiboNum();
290}
291
292u32 getSearchEndAmiiboNum(const al::IUseSceneObjHolder* objHolder) {
293 return getAmiiboNpcDirector(objHolder)->getSearchEndAmiiboNum();
294}
295
296u32 getSearchEndAmiiboNumRealTime(const al::IUseSceneObjHolder* objHolder) {
297 return getAmiiboNpcDirector(objHolder)->getSearchEndAmiiboNumRealTime();
298}
299
300void registerSearchAmiibo(const al::IUseSceneObjHolder* objHolder, const al::NfpInfo& nfpInfo) {
301 al::NfpCharacterId characterId{};
302 al::tryGetCharacterId(characterId: &characterId, nfpInfo);
303 AmiiboNpcDirector* director = getAmiiboNpcDirector(objHolder);
304
305 s32 id = rs::createCharacterIdS32(characterId);
306 s32 numberingId = 0;
307 al::tryGetNumberingId(&numberingId, nfpInfo);
308
309 u64 now = sead::DateTime(0).setNow();
310 director->registerSearchAmiibo(id, numberingId, searchStartTime: now);
311}
312
313bool isSearchAmiibo(const al::IUseSceneObjHolder* objHolder, const al::NfpInfo& nfpInfo) {
314 al::NfpCharacterId characterId{};
315 al::tryGetCharacterId(characterId: &characterId, nfpInfo);
316 return getAmiiboNpcDirector(objHolder)->isSearchAmiibo(id: rs::createCharacterIdS32(characterId));
317}
318
319void deleteSearchEndAmiibo(const al::IUseSceneObjHolder* objHolder) {
320 getAmiiboNpcDirector(objHolder)->deleteSearchEndAmiibo();
321}
322
323void setTalkStartTime(const al::IUseSceneObjHolder* objHolder) {
324 sead::DateTime dateTime = sead::DateTime(0);
325 AmiiboNpcDirector* director = getAmiiboNpcDirector(objHolder);
326
327 director->setTime(dateTime.setNow());
328 director->checkTimeReverseAndRestore();
329}
330
331al::MessageTagDataHolder* getMessageTagDataHolder(const al::IUseSceneObjHolder* objHolder) {
332 return getAmiiboNpcDirector(objHolder)->getMessageTagDataHolder();
333}
334
335void setTouchAmiiboName(const al::IUseSceneObjHolder* objHolder, s32 id, s32 numberingId) {
336 getAmiiboNpcDirector(objHolder)->setTouchAmiiboName(id, numberingId);
337}
338
339void trySetAmiiboCostumeName(const al::IUseSceneObjHolder* objHolder, s32 id) {
340 getAmiiboNpcDirector(objHolder)->trySetAmiiboCostumeName(id);
341}
342
343} // namespace AmiiboFunction
344