1#include "System/GameProgressData.h"
2
3#include "Library/Yaml/ByamlUtil.h"
4#include "Library/Yaml/Writer/ByamlWriter.h"
5
6#include "System/GameDataFunction.h"
7#include "System/WorldList.h"
8
9GameProgressData::GameProgressData(const WorldList* worldList) : mWorldList(worldList) {
10 s32 worldNum = worldList->getWorldNum();
11
12 mIsUnlockWorld = new bool[worldNum];
13 mWorldIdForWorldWarpHole = new s32[worldNum];
14 mWorldIdForShineList = new s32[worldNum];
15 mWorldIdForWorldMap = new s32[worldNum];
16 mIsFirstTimeWorld = new bool[worldNum];
17
18 init();
19}
20
21void GameProgressData::init() {
22 mHomeStatus = HomeStatus::None;
23 mHomeLevel = 0;
24 mUnlockWorldStatusFirstBranch = FirstBranch::None;
25 mUnlockWorldStatusSecondBranch = SecondBranch::None;
26 mUnlockWorldNum = 1;
27 mWaterfallWorldProgress = WaterfallWorldProgress::None;
28
29 for (s32 i = 0; i < mWorldList->getWorldNum(); i++)
30 mIsFirstTimeWorld[i] = true;
31
32 setAlreadyGoWorld(0);
33
34 updateList();
35}
36
37void GameProgressData::updateList() {
38 initList();
39
40 s32 worldNum = mWorldList->getWorldNum();
41 s32 idxPeach = mWorldList->tryFindWorldIndexByDevelopName("Peach");
42
43 for (s32 j = 0, i = 0; i < worldNum; i++) {
44 s32 worldId = calcWorldIdByOrderUnlock(idx: i);
45 if (worldId == -1)
46 continue;
47
48 mIsUnlockWorld[worldId] = true;
49 if (++j == mUnlockWorldNum)
50 break;
51 }
52
53 s32 idxCloud = mWorldList->tryFindWorldIndexByDevelopName("Cloud");
54 s32 idxAttack = mWorldList->tryFindWorldIndexByDevelopName("Attack");
55 s32 idxCity = mWorldList->tryFindWorldIndexByDevelopName("City");
56 s32 idxSky = mWorldList->tryFindWorldIndexByDevelopName("Sky");
57
58 bool isUnlockPeach = mIsUnlockWorld[idxPeach];
59
60 for (s32 i = 0; i < worldNum; i++) {
61 s32 worldId = calcWorldIdByOrderUnlock(idx: i);
62 if (worldId == -1)
63 continue;
64
65 if (isUnlockPeach) {
66 if (i == idxPeach)
67 mWorldIdForWorldMap[0] = idxPeach;
68 else {
69 if (worldId < idxPeach)
70 worldId++;
71 mWorldIdForWorldMap[worldId] = i;
72 }
73 } else {
74 if (worldId == idxCloud && mUnlockWorldNum == idxCloud + 1)
75 mWorldIdForWorldMap[worldId] = idxCity;
76 else if (worldId == idxAttack && mUnlockWorldNum == idxAttack + 1)
77 mWorldIdForWorldMap[worldId] = idxSky;
78 else
79 mWorldIdForWorldMap[i] = worldId;
80 }
81 }
82
83 for (s32 i = 0; i < worldNum; i++) {
84 s32 worldId = calcWorldIdByOrderUnlock(idx: i);
85 if (worldId != -1) {
86 mWorldIdForWorldWarpHole[i] = worldId;
87 mWorldIdForShineList[i] = worldId;
88 }
89 }
90
91 if (isUnlockPeach) {
92 for (s32 i = 0; i < worldNum; i++)
93 if (i == idxPeach)
94 mWorldIdForShineList[0] = idxPeach;
95 else if (i < idxPeach)
96 mWorldIdForShineList[i + 1] = mWorldIdForWorldWarpHole[i];
97 else
98 mWorldIdForShineList[i] = mWorldIdForWorldWarpHole[i];
99 }
100}
101
102void GameProgressData::checkAndChangeCorrectStatus(s32 worldId, s32 nextScenarioNo) {
103 if (worldId == GameDataFunction::getWorldIndexWaterfall() && nextScenarioNo > 1)
104 mWaterfallWorldProgress = WaterfallWorldProgress::TalkedCapNearHome;
105
106 if (worldId > GameDataFunction::getWorldIndexWaterfall())
107 mWaterfallWorldProgress = WaterfallWorldProgress::TalkedCapNearHome;
108
109 if (worldId == GameDataFunction::getWorldIndexHat() && nextScenarioNo > 1)
110 mWaterfallWorldProgress = WaterfallWorldProgress::TalkedCapNearHome;
111
112 if (worldId != GameDataFunction::getWorldIndexCloud() && isFindKoopa())
113 mHomeStatus = HomeStatus::LaunchedHome;
114
115 if (worldId != GameDataFunction::getWorldIndexBoss() && isBossAttackedHome())
116 mHomeStatus = HomeStatus::RepairedHome;
117}
118
119bool GameProgressData::isFindKoopa() const {
120 return mHomeStatus == HomeStatus::FoundKoopa;
121}
122
123bool GameProgressData::isBossAttackedHome() const {
124 return mHomeStatus == HomeStatus::BossAttackedHome;
125}
126
127bool GameProgressData::isActivateHome() const {
128 return mHomeStatus > HomeStatus::None;
129}
130
131void GameProgressData::activateHome() {
132 if (mHomeStatus < HomeStatus::ActivatedHome)
133 mHomeStatus = HomeStatus::ActivatedHome;
134}
135
136bool GameProgressData::isLaunchHome() const {
137 return mHomeStatus > HomeStatus::ActivatedHome;
138}
139
140void GameProgressData::launchHome() {
141 if (mHomeStatus < HomeStatus::LaunchedHome)
142 mHomeStatus = HomeStatus::LaunchedHome;
143}
144
145void GameProgressData::findKoopa() {
146 if (mHomeStatus < HomeStatus::FoundKoopa)
147 mHomeStatus = HomeStatus::FoundKoopa;
148}
149
150bool GameProgressData::isCrashHome() const {
151 return mHomeStatus == HomeStatus::CrashedHome;
152}
153
154void GameProgressData::crashHome() {
155 if (mHomeStatus < HomeStatus::CrashedHome)
156 mHomeStatus = HomeStatus::CrashedHome;
157}
158
159bool GameProgressData::isRepairHome() const {
160 return mHomeStatus > HomeStatus::CrashedHome;
161}
162
163void GameProgressData::repairHome() {
164 if (mHomeStatus < HomeStatus::RepairedHome)
165 mHomeStatus = HomeStatus::RepairedHome;
166}
167
168void GameProgressData::bossAttackHome() {
169 if (mHomeStatus < HomeStatus::BossAttackedHome)
170 mHomeStatus = HomeStatus::BossAttackedHome;
171}
172
173bool GameProgressData::isRepairHomeByCrashedBoss() const {
174 return mHomeStatus > HomeStatus::BossAttackedHome;
175}
176
177void GameProgressData::repairHomeByCrashedBoss() {
178 if (mHomeStatus < HomeStatus::RepairedHomeByCrashedBoss)
179 mHomeStatus = HomeStatus::RepairedHomeByCrashedBoss;
180}
181
182s32 GameProgressData::getHomeLevel() const {
183 return mHomeLevel;
184}
185
186void GameProgressData::upHomeLevel() {
187 s32 nextHomeLevels[18] = {0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, 9, 9, 9};
188 s32 nextHomeLevel = nextHomeLevels[mUnlockWorldNum];
189 nextHomeLevel = sead::Mathi::min(a: nextHomeLevel, b: mHomeLevel + 1);
190 mHomeLevel = sead::Mathi::clampMax(val: nextHomeLevel, max_: 9);
191}
192
193s32 GameProgressData::getUnlockWorldNum() const {
194 return mUnlockWorldNum;
195}
196
197bool GameProgressData::isUnlockWorld(s32 idx) const {
198 return mIsUnlockWorld[idx];
199}
200
201s32 GameProgressData::getWorldIdForWorldMap(s32 idx) const {
202 return mWorldIdForWorldMap[idx];
203}
204
205s32 GameProgressData::calcNextLockedWorldNumForWorldMap() const {
206 if (mUnlockWorldNum == 3 || mUnlockWorldNum == 8)
207 return 2;
208 else
209 return 1;
210}
211
212s32 GameProgressData::calcNextLockedWorldIdForWorldMap(s32 idx) const {
213 switch (mUnlockWorldNum) {
214 case 3:
215 return idx == 0 ? GameDataFunction::getWorldIndexForest() :
216 GameDataFunction::getWorldIndexLake();
217 case 4:
218 return isUnlockFirstForest() ? GameDataFunction::getWorldIndexLake() :
219 GameDataFunction::getWorldIndexForest();
220 case 5:
221 return GameDataFunction::getWorldIndexCity();
222 case 8:
223 return idx != 0 ? GameDataFunction::getWorldIndexSnow() :
224 GameDataFunction::getWorldIndexSea();
225 case 9:
226 return isUnlockFirstSea() ? GameDataFunction::getWorldIndexSea() :
227 GameDataFunction::getWorldIndexSnow();
228 case 11:
229 return GameDataFunction::getWorldIndexSky();
230 default:
231 return mUnlockWorldNum;
232 }
233}
234
235bool GameProgressData::isUnlockFirstForest() const {
236 if (mUnlockWorldStatusFirstBranch == FirstBranch::None ||
237 mUnlockWorldStatusFirstBranch == FirstBranch::Forest)
238 return true;
239
240 return false;
241}
242
243bool GameProgressData::isUnlockFirstSea() const {
244 if (mUnlockWorldStatusSecondBranch == SecondBranch::None ||
245 mUnlockWorldStatusSecondBranch == SecondBranch::Sea)
246 return true;
247
248 return false;
249}
250
251s32 GameProgressData::getWorldIdForWorldWarpHole(s32 idx) const {
252 return mWorldIdForWorldWarpHole[idx];
253}
254
255s32 GameProgressData::getWorldIdForShineList(s32 idx) const {
256 return mWorldIdForShineList[idx];
257}
258
259s32 GameProgressData::calcWorldNumForShineList() const {
260 s32 idxSpecial2 = GameDataFunction::getWorldIndexSpecial2();
261 if (isUnlockWorld(idx: idxSpecial2) && isAlreadyGoWorld(idx: idxSpecial2))
262 return mWorldList->getWorldNum();
263
264 s32 worldNum = 0;
265 for (s32 i = 0; i < mWorldList->getWorldNum(); i++)
266 worldNum += isAlreadyGoWorld(idx: i);
267
268 return worldNum;
269}
270
271bool GameProgressData::isAlreadyGoWorld(s32 idx) const {
272 return !mIsFirstTimeWorld[idx];
273}
274
275void GameProgressData::unlockNextWorld(s32 idx) {
276 while (true) {
277 updateList();
278 if (isUnlockWorld(idx))
279 return;
280
281 s32 idxForest = mWorldList->tryFindWorldIndexByDevelopName("Forest");
282 s32 idxLake = mWorldList->tryFindWorldIndexByDevelopName("Lake");
283 s32 idxSnow = mWorldList->tryFindWorldIndexByDevelopName("Snow");
284 s32 idxSea = mWorldList->tryFindWorldIndexByDevelopName("Sea");
285
286 if (idx == idxForest)
287 unlockForest();
288 else if (idx == idxLake)
289 unlockLake();
290 else if (idx == idxSnow)
291 unlockSnow();
292 else if (idx == idxSea)
293 unlockSea();
294 else
295 unlockNormalWorld();
296
297 updateList();
298
299 s32 nextHomeLevels[17] = {0, 0, 1, 2, 2, 4, 4, 4, 5, 5, 7, 8, 8, 9, 9, 9, 9};
300
301 mHomeLevel = sead::Mathi::max(a: mHomeLevel, b: nextHomeLevels[idx]);
302
303 if (mHomeLevel == 9)
304 mHomeStatus = HomeStatus::RepairedHomeByCrashedBoss;
305
306 if (isUnlockWorld(idx))
307 return;
308 }
309}
310
311void GameProgressData::unlockForest() {
312 if (mUnlockWorldStatusFirstBranch == FirstBranch::None)
313 mUnlockWorldStatusFirstBranch = FirstBranch::Forest;
314
315 mUnlockWorldNum++;
316}
317
318void GameProgressData::unlockLake() {
319 if (mUnlockWorldStatusFirstBranch == FirstBranch::None)
320 mUnlockWorldStatusFirstBranch = FirstBranch::Lake;
321
322 mUnlockWorldNum++;
323}
324
325void GameProgressData::unlockSnow() {
326 if (mUnlockWorldStatusSecondBranch == SecondBranch::None)
327 mUnlockWorldStatusSecondBranch = SecondBranch::Snow;
328
329 mUnlockWorldNum++;
330}
331
332void GameProgressData::unlockSea() {
333 if (mUnlockWorldStatusSecondBranch == SecondBranch::None)
334 mUnlockWorldStatusSecondBranch = SecondBranch::Sea;
335
336 mUnlockWorldNum++;
337}
338
339void GameProgressData::unlockNormalWorld() {
340 mUnlockWorldNum++;
341}
342
343bool GameProgressData::isFirstTimeGoWorld(s32 idx) const {
344 return mIsFirstTimeWorld[idx];
345}
346
347void GameProgressData::setAlreadyGoWorld(s32 idx) {
348 mIsFirstTimeWorld[idx] = false;
349}
350
351bool GameProgressData::isTalkedCapNearHomeInWaterfall() const {
352 return mWaterfallWorldProgress > WaterfallWorldProgress::GotFirstMoon;
353}
354
355void GameProgressData::talkCapNearHomeInWaterfall() {
356 if (mWaterfallWorldProgress < WaterfallWorldProgress::TalkedCapNearHome)
357 mWaterfallWorldProgress = WaterfallWorldProgress::TalkedCapNearHome;
358}
359
360void GameProgressData::write(al::ByamlWriter* writer) {
361 writer->pushHash("GameProgressData");
362 writer->addInt("HomeStatus", (s32)mHomeStatus);
363 writer->addInt("HomeLevel", mHomeLevel);
364 writer->addInt("UnlockWorldNum", mUnlockWorldNum);
365 writer->addInt("UnlockWorldStatusFirstBranch", (s32)mUnlockWorldStatusFirstBranch);
366 writer->addInt("UnlockWorldStatusSecondBranch", (s32)mUnlockWorldStatusSecondBranch);
367 writer->addInt("WaterfallWorldProgress", (s32)mWaterfallWorldProgress);
368
369 writer->pushArray("IsFirstTimeWorld");
370 for (s32 i = 0; i < mWorldList->getWorldNum(); i++)
371 writer->addBool(isFirstTimeGoWorld(idx: i));
372 writer->pop();
373
374 writer->pop();
375}
376
377void GameProgressData::read(const al::ByamlIter& iter) {
378 init();
379
380 al::ByamlIter hash;
381 iter.tryGetIterByKey(iter: &hash, key: "GameProgressData");
382 hash.tryGetIntByKey(val: (s32*)&mHomeStatus, key: "HomeStatus");
383 hash.tryGetIntByKey(val: &mHomeLevel, key: "HomeLevel");
384 hash.tryGetIntByKey(val: &mUnlockWorldNum, key: "UnlockWorldNum");
385 hash.tryGetIntByKey(val: (s32*)&mUnlockWorldStatusFirstBranch, key: "UnlockWorldStatusFirstBranch");
386 hash.tryGetIntByKey(val: (s32*)&mUnlockWorldStatusSecondBranch, key: "UnlockWorldStatusSecondBranch");
387 hash.tryGetIntByKey(val: (s32*)&mWaterfallWorldProgress, key: "WaterfallWorldProgress");
388
389 al::ByamlIter array;
390 hash.tryGetIterByKey(iter: &array, key: "IsFirstTimeWorld");
391 for (s32 i = 0; i < mWorldList->getWorldNum(); i++)
392 array.tryGetBoolByIndex(val: &mIsFirstTimeWorld[i], index: i);
393
394 updateList();
395}
396
397void GameProgressData::initList() {
398 for (s32 i = 0; i < mWorldList->getWorldNum(); i++) {
399 mIsUnlockWorld[i] = false;
400 mWorldIdForWorldMap[i] = -1;
401 mWorldIdForWorldWarpHole[i] = -1;
402 mWorldIdForShineList[i] = -1;
403 }
404}
405
406s32 GameProgressData::calcWorldIdByOrderUnlock(s32 idx) const {
407 s32 idxForest = mWorldList->tryFindWorldIndexByDevelopName("Forest");
408 s32 idxLake = mWorldList->tryFindWorldIndexByDevelopName("Lake");
409 s32 idxSnow = mWorldList->tryFindWorldIndexByDevelopName("Snow");
410 s32 idxSea = mWorldList->tryFindWorldIndexByDevelopName("Sea");
411
412 if (idx == idxForest)
413 return mUnlockWorldStatusFirstBranch < FirstBranch::Lake ? idx : idxLake;
414
415 if (idx == idxLake)
416 return mUnlockWorldStatusFirstBranch < FirstBranch::Lake ? idx : idxForest;
417
418 if (idx == idxSnow) {
419 if (mUnlockWorldStatusSecondBranch != SecondBranch::None &&
420 mUnlockWorldStatusSecondBranch != SecondBranch::Sea)
421 return idxSea;
422 else
423 return idx;
424 }
425 if (idx == idxSea) {
426 if (mUnlockWorldStatusSecondBranch != SecondBranch::None &&
427 mUnlockWorldStatusSecondBranch != SecondBranch::Sea)
428 return idxSnow;
429 else
430 return idx;
431 }
432
433 return idx;
434}
435