1#include "Layout/MenuSelectParts.h"
2
3#include "Library/Base/StringUtil.h"
4#include "Library/Controller/KeyRepeatCtrl.h"
5#include "Library/Controller/PadRumbleDirector.h"
6#include "Library/Controller/PadRumbleFunction.h"
7#include "Library/Layout/LayoutActionFunction.h"
8#include "Library/Layout/LayoutActor.h"
9#include "Library/Layout/LayoutActorUtil.h"
10#include "Library/Layout/LayoutInitInfo.h"
11#include "Library/LiveActor/ActorActionFunction.h"
12#include "Library/LiveActor/LiveActorFunction.h"
13#include "Library/Math/MathUtil.h"
14#include "Library/Nerve/NerveSetupUtil.h"
15#include "Library/Nerve/NerveUtil.h"
16#include "Library/Se/SeFunction.h"
17
18#include "Sequence/GameSequenceInfo.h"
19#include "System/GameDataHolderAccessor.h"
20#include "Util/StageInputFunction.h"
21
22namespace {
23NERVE_IMPL(MenuSelectParts, Hide);
24NERVE_IMPL(MenuSelectParts, Appear);
25NERVE_IMPL(MenuSelectParts, Select);
26NERVE_IMPL(MenuSelectParts, DecideEnd);
27NERVE_IMPL(MenuSelectParts, DecideParts);
28NERVE_IMPL_(MenuSelectParts, SelectSecond, Select);
29NERVE_IMPL(MenuSelectParts, DecideInterval);
30
31NERVES_MAKE_NOSTRUCT(MenuSelectParts, Hide, Appear, DecideEnd);
32NERVES_MAKE_STRUCT(MenuSelectParts, Select, DecideParts, SelectSecond, DecideInterval);
33} // namespace
34
35const MenuSelectParts::Selection sMainMenuParts[5] = {
36 MenuSelectParts::Selection::Continue, MenuSelectParts::Selection::SeparatePlay,
37 MenuSelectParts::Selection::NewGame, MenuSelectParts::Selection::Help,
38 MenuSelectParts::Selection::Setting};
39
40const MenuSelectParts::Selection sPauseMenuParts[5] = {
41 MenuSelectParts::Selection::Continue, MenuSelectParts::Selection::SeparatePlay,
42 MenuSelectParts::Selection::Help, MenuSelectParts::Selection::Save,
43 MenuSelectParts::Selection::Setting};
44
45inline const MenuSelectParts::Selection* getPartsArray(bool isPauseMenu) {
46 return isPauseMenu ? sPauseMenuParts : sMainMenuParts;
47}
48
49inline void setCursorPaneTrans(al::LayoutActor* cursorActor, al::LayoutActor* actor) {
50 sead::Vector2f cursorTrans = {0.0f, 0.0f};
51 al::calcPaneTrans(&cursorTrans, actor, "Cursor");
52 al::setLocalTrans(cursorActor, cursorTrans);
53}
54
55MenuSelectParts::MenuSelectParts(const char* name, al::LayoutActor* layoutActor,
56 al::LiveActor* marioActor, const al::LayoutInitInfo& info,
57 s32 menuItemCount)
58 : al::NerveExecutor(name), mLayoutActor(layoutActor), mMax(menuItemCount),
59 mMarioActor(marioActor) {
60 mCapActor = al::getSubActor(actor: al::getSubActor(actor: marioActor, subActorName: "頭"), subActorName: "メニュー用キャップ目");
61
62 mKeyRepeatCtrl = new al::KeyRepeatCtrl();
63 mKeyRepeatCtrl->init(initialMaxWait: 30, maxWait: 5);
64
65 mLayoutArray = new al::LayoutActor*[mMax];
66 for (s32 i = 0; i < mMax; i++) {
67 mLayoutArray[i] = new al::LayoutActor("選択肢パーツ");
68
69 al::StringTmp<32> name("%s%02d", "ParList", i);
70
71 al::initLayoutPartsActor(mLayoutArray[i], layoutActor, info, name.cstr(), nullptr);
72 al::startAction(layout: mLayoutArray[i], actionName: "Active", paneName: "State");
73 }
74
75 mCursorActor = new al::LayoutActor("カーソルパーツ");
76 al::initLayoutPartsActor(mCursorActor, layoutActor, info, "ParCursor", nullptr);
77
78 al::startAction(layout: mCursorActor, actionName: "Hide", paneName: nullptr);
79 initNerve(nerve: &Hide, stateCount: 0);
80}
81
82void MenuSelectParts::update() {
83 updateNerve();
84}
85
86void MenuSelectParts::appear(s32 menuItemAmount) {
87 mIsMainMenu = false;
88 mCursorItemIndex = 0;
89 mDefaultIndex = 0;
90 mMenuItemAmount = menuItemAmount;
91
92 for (s32 i = 0; i < mMenuItemAmount; i++) {
93 if (i == mCursorItemIndex)
94 al::startFreezeActionEnd(layout: mLayoutArray[calcPartsIndex(selection: i)], actionName: "Select", paneName: nullptr);
95 else
96 al::startFreezeActionEnd(layout: mLayoutArray[calcPartsIndex(selection: i)], actionName: "Wait", paneName: nullptr);
97 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: i)], actionName: "Active", paneName: "State");
98 }
99
100 if (rs::isSceneStatusInvalidSave(accessor: mLayoutActor)) {
101 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: 3)], actionName: "Deactive", paneName: "State");
102 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: 4)], actionName: "Deactive", paneName: "State");
103
104 al::setSeKeeperPlayNamePrefix(mLayoutArray[calcPartsIndex(selection: 3)], "Deactive");
105 al::setSeKeeperPlayNamePrefix(mLayoutArray[calcPartsIndex(selection: 4)], "Deactive");
106 }
107
108 al::setNerve(user: this, nerve: &Appear);
109}
110
111void MenuSelectParts::startActionPartsIllustSelectIndex() {}
112
113void MenuSelectParts::appearWait() {
114 al::startFreezeActionEnd(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], actionName: "Select", paneName: nullptr);
115 al::setNerve(user: this, nerve: &NrvMenuSelectParts.Select);
116}
117
118void MenuSelectParts::setSelectMessage(s32 index, const char16* message) {
119 al::setPaneString(mLayoutArray[calcPartsIndex(selection: index)], "TxtContent", message, 0);
120}
121
122bool MenuSelectParts::isDecideContinue() const {
123 if (!isDecideEnd())
124 return false;
125 return isSelectContinue();
126}
127
128bool MenuSelectParts::isDecideEnd() const {
129 return al::isNerve(user: this, nerve: &DecideEnd);
130}
131
132bool MenuSelectParts::isSelectContinue() const {
133 return calcPartsIndex(selection: mCursorItemIndex) == Selection::Continue;
134}
135
136bool MenuSelectParts::isDecideSetting() const {
137 if (!isDecideEnd())
138 return false;
139 return isSelectSetting();
140}
141
142bool MenuSelectParts::isSelectSetting() const {
143 return calcPartsIndex(selection: mCursorItemIndex) == Selection::Setting;
144}
145
146bool MenuSelectParts::isDecideSave() const {
147 if (!isDecideEnd())
148 return false;
149 return isSelectSave();
150}
151
152bool MenuSelectParts::isSelectSave() const {
153 return calcPartsIndex(selection: mCursorItemIndex) == Selection::Save;
154}
155
156bool MenuSelectParts::isDecideSeparatePlay() const {
157 if (!isDecideEnd())
158 return false;
159 return isSelectSeparatePlay();
160}
161
162bool MenuSelectParts::isSelectSeparatePlay() const {
163 return calcPartsIndex(selection: mCursorItemIndex) == Selection::SeparatePlay;
164}
165
166bool MenuSelectParts::isDecideHelp() const {
167 if (!isDecideEnd())
168 return false;
169 return isSelectHelp();
170}
171
172bool MenuSelectParts::isSelectHelp() const {
173 return calcPartsIndex(selection: mCursorItemIndex) == Selection::Help;
174}
175
176bool MenuSelectParts::isDecideNewGame() const {
177 if (!isDecideEnd())
178 return false;
179 return isSelectNewGame();
180}
181
182bool MenuSelectParts::isSelectNewGame() const {
183 return calcPartsIndex(selection: mCursorItemIndex) == Selection::NewGame;
184}
185
186s32 MenuSelectParts::calcPartsIndex(s32 selection) const {
187 bool isPauseMenu = !mIsMainMenu;
188 return getPartsArray(isPauseMenu)[selection];
189}
190
191void MenuSelectParts::exeHide() {}
192
193void MenuSelectParts::exeAppear() {
194 if (al::isFirstStep(user: this)) {
195 setCursorPaneTrans(cursorActor: mCursorActor, actor: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)]);
196 al::startAction(layout: mCursorActor, actionName: "Appear", paneName: nullptr);
197 startActionMarioSelectIndex();
198 for (s32 i = 0; i < mMenuItemAmount; i++) {
199 if (i == mCursorItemIndex)
200 continue;
201 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: i)], actionName: "Wait", paneName: nullptr);
202 }
203 }
204 setCursorPaneTrans(cursorActor: mCursorActor, actor: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)]);
205
206 if (al::isActionEnd(layout: mCursorActor, paneName: nullptr))
207 al::setNerve(user: this, nerve: &NrvMenuSelectParts.Select);
208}
209
210void MenuSelectParts::startActionMarioSelectIndex() {
211 switch (calcPartsIndex(selection: mCursorItemIndex)) {
212 case Selection::Continue:
213 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuContinue");
214 break;
215 case Selection::SeparatePlay:
216 startActionMario(marioActor: mMarioActor, actionName: "PauseMenu2Player");
217 break;
218 case Selection::NewGame:
219 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuNewGame");
220 break;
221 case Selection::Help:
222 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuHelp");
223 break;
224 case Selection::Save:
225 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuSave");
226 break;
227 case Selection::Setting:
228 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuData");
229 break;
230 }
231}
232
233void MenuSelectParts::exeSelect() {
234 if (al::isFirstStep(user: this)) {
235 if (al::isNerve(user: this, nerve: &NrvMenuSelectParts.Select))
236 al::startAction(layout: mCursorActor, actionName: "Wait", paneName: nullptr);
237 mKeyRepeatCtrl->reset();
238 }
239
240 mKeyRepeatCtrl->update(up: rs::isHoldUiUp(mLayoutActor), down: rs::isHoldUiDown(mLayoutActor));
241
242 if (mKeyRepeatCtrl->isUp() || mKeyRepeatCtrl->isDown()) {
243 s32 direction = mKeyRepeatCtrl->isUp() ? -1 : 1;
244
245 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], actionName: "Wait", paneName: nullptr);
246 mCursorItemIndex =
247 al::modi(a: mCursorItemIndex + direction + mMenuItemAmount, b: mMenuItemAmount);
248
249 f32 pitch = ((1.0f - (f32)mCursorItemIndex / (mMenuItemAmount - 1)) * 0.375f) + 1.0f;
250 al::PadRumbleParam param;
251 alPadRumbleFunction::makePadRumbleParamNearFarVolumePitch(rumbleParam: &param, near: 0.0f, far: 500.0f,
252 volume: pitch * 0.05f, pitch);
253
254 param.isUseController = 1;
255 alPadRumbleFunction::startPadRumbleNo3DWithParam(
256 director: alPadRumbleFunction::getPadRumbleDirector(layoutActor: mLayoutActor), name: "240Hz単発", rumbleParam: param, port: -1);
257
258 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], actionName: "Select", paneName: nullptr);
259 startActionMarioSelectIndex();
260 }
261
262 if (rs::isTriggerUiCancel(mLayoutActor) || rs::isTriggerUiPause(mLayoutActor)) {
263 if (mIsMainMenu)
264 return;
265 al::startAction(layout: mLayoutArray[sPauseMenuParts[mCursorItemIndex]], actionName: "Wait", paneName: nullptr);
266 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuContinue");
267 if (mCursorItemIndex != mDefaultIndex)
268 mCursorItemIndex = mDefaultIndex;
269 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], actionName: "Select", paneName: nullptr);
270 setCursorPaneTrans(cursorActor: mCursorActor, actor: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)]);
271 al::startHitReaction(mLayoutActor, "キャンセル", nullptr);
272 al::setNerve(user: this, nerve: &NrvMenuSelectParts.DecideParts);
273 } else {
274 setCursorPaneTrans(cursorActor: mCursorActor, actor: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)]);
275 if (rs::isTriggerUiDecide(mLayoutActor)) {
276 al::startHitReaction(mLayoutActor, "決定", nullptr);
277 al::setNerve(user: this, nerve: &NrvMenuSelectParts.DecideParts);
278 }
279 }
280}
281
282void MenuSelectParts::startActionMario(al::LiveActor* marioActor, const char* actionName) {
283 al::tryStartActionIfNotPlaying(actor: marioActor, actionName);
284 al::tryStartActionIfNotPlaying(actor: al::getSubActor(actor: marioActor, subActorName: "目"), actionName);
285 al::tryStartActionIfNotPlaying(actor: al::getSubActor(actor: marioActor, subActorName: "頭"), actionName);
286 al::tryStartActionIfNotPlaying(actor: al::getSubActor(actor: marioActor, subActorName: "顔"), actionName);
287 al::tryStartActionIfNotPlaying(actor: al::getSubActor(actor: marioActor, subActorName: "右手"), actionName);
288 al::tryStartActionIfNotPlaying(actor: al::getSubActor(actor: marioActor, subActorName: "左手"), actionName);
289 al::tryStartActionIfNotPlaying(actor: mCapActor, actionName);
290}
291
292void MenuSelectParts::exeDecideParts() {
293 if (al::isFirstStep(user: this)) {
294 al::startAction(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], actionName: "Decide", paneName: nullptr);
295
296 if (calcPartsIndex(selection: mCursorItemIndex) == Selection::Continue)
297 startActionMario(marioActor: mMarioActor, actionName: "PauseMenuContinueEnd");
298 if (isInvalidSelect()) {
299 al::setNerve(user: this, nerve: &NrvMenuSelectParts.SelectSecond);
300 return;
301 }
302 al::startAction(layout: mCursorActor, actionName: "End", paneName: nullptr);
303 }
304
305 if (al::isActionEnd(layout: mLayoutArray[calcPartsIndex(selection: mCursorItemIndex)], paneName: nullptr) &&
306 al::isActionEnd(layout: mCursorActor, paneName: nullptr)) {
307 al::startAction(layout: mCursorActor, actionName: "Hide", paneName: nullptr);
308 al::setNerve(user: this, nerve: &NrvMenuSelectParts.DecideInterval);
309 }
310}
311
312bool MenuSelectParts::isInvalidSelect() const {
313 if (rs::isSceneStatusInvalidSave(accessor: mLayoutActor) &&
314 (calcPartsIndex(selection: mCursorItemIndex) == Selection::Save ||
315 calcPartsIndex(selection: mCursorItemIndex) == Selection::Setting))
316 return true;
317 return false;
318}
319
320void MenuSelectParts::exeDecideInterval() {
321 al::setNerveAtGreaterEqualStep(user: this, nerve: &DecideEnd, step: 0);
322}
323
324void MenuSelectParts::exeDecideEnd() {}
325