1#include "Library/Play/Layout/BalloonMessage.h"
2
3#include "Library/Layout/LayoutActionFunction.h"
4#include "Library/Layout/LayoutActorUtil.h"
5#include "Library/Layout/LayoutInitInfo.h"
6#include "Library/LiveActor/ActorClippingFunction.h"
7#include "Library/LiveActor/ActorFlagFunction.h"
8#include "Library/LiveActor/ActorInitUtil.h"
9#include "Library/LiveActor/ActorMovementFunction.h"
10#include "Library/LiveActor/ActorPoseUtil.h"
11#include "Library/LiveActor/LiveActor.h"
12#include "Library/Nerve/NerveSetupUtil.h"
13#include "Library/Nerve/NerveUtil.h"
14#include "Library/Play/Layout/TalkMessageVoicePlayer.h"
15#include "Library/Player/PlayerUtil.h"
16#include "Library/Screen/ScreenFunction.h"
17#include "Library/Se/SeFunction.h"
18
19namespace {
20using namespace al;
21
22NERVE_IMPL(BalloonMessage, Wait)
23NERVE_IMPL(BalloonMessage, Appear)
24NERVE_IMPL(BalloonMessage, Hide)
25NERVE_IMPL(BalloonMessage, End)
26
27NERVES_MAKE_NOSTRUCT(BalloonMessage, Wait)
28NERVES_MAKE_STRUCT(BalloonMessage, Appear, Hide, End)
29} // namespace
30
31namespace al {
32BalloonMessage* BalloonMessage::create(const LiveActor* hostActor, const LayoutInitInfo& info,
33 const BalloonMessageInitParam& initParam,
34 bool isAutoUpdate) {
35 return new BalloonMessage(hostActor, info, initParam, isAutoUpdate);
36}
37
38BalloonMessage::BalloonMessage(const LiveActor* hostActor, const LayoutInitInfo& info,
39 const BalloonMessageInitParam& initParam, bool isAutoUpdate)
40 : LayoutActor(initParam.name), mHostActor(hostActor), mPaneName(initParam.paneName),
41 mAppearDist(initParam.appearDist), mKillDist(initParam.killDist),
42 mPlayerIndex(initParam.playerIndex), _158(initParam._38), mIsAutoUpdate(isAutoUpdate),
43 _15d(initParam._3c) {
44 mPosOffset.y = initParam.yPosOffset;
45
46 initLayoutActor(this, info, initParam.archiveName, initParam.suffix);
47
48 mTalkMessageVoicePlayer = new TalkMessageVoicePlayer();
49 mTalkMessageVoicePlayer->set_420(true);
50
51 setText(initParam.message);
52 initNerve(&Wait, 0);
53
54 kill();
55}
56
57void BalloonMessage::setText(const char* message) {
58 setPaneStringFormat(this, mPaneName, message);
59}
60
61inline void startAppear(LayoutActor* layout) {
62 startAction(layout, actionName: "Appear", paneName: nullptr);
63}
64
65void BalloonMessage::appear() {
66 updateTrans();
67 hidePushA();
68
69 if (!mIsAutoUpdate) {
70 startAppear(layout: this);
71 LayoutActor::appear();
72
73 if (!mIsTalkMessageVoicePlayerStarted)
74 mTalkMessageVoicePlayer->start(this, mHostActor, getPaneStringBuffer(this, mPaneName),
75 5);
76
77 setNerve(user: this, nerve: &NrvBalloonMessage.Appear);
78
79 return;
80 }
81
82 if (isEnableAppear()) {
83 startAppear(layout: this);
84 LayoutActor::appear();
85 setActionFrameRate(layout: this, frameRate: 1.0f, paneName: nullptr);
86 setNerve(user: this, nerve: &NrvBalloonMessage.Appear);
87
88 return;
89 }
90
91 LayoutActor::appear();
92 startFreezeActionEnd(layout: this, actionName: "End", paneName: nullptr);
93 setNerve(user: this, nerve: &NrvBalloonMessage.Hide);
94}
95
96void BalloonMessage::updateTrans() {
97 sead::Vector2f layoutPos = sead::Vector2f::zero;
98
99 if (_158 == 0)
100 calcLayoutPosFromWorldPos(output: &layoutPos, mHostActor, getTrans(actor: mHostActor) + mPosOffset);
101 else
102 calcLayoutPosFromWorldPosSub(output: &layoutPos, mHostActor, getTrans(actor: mHostActor) + mPosOffset);
103
104 setLocalTrans(this, layoutPos);
105}
106
107void BalloonMessage::hidePushA() {
108 if (!isExistPane(this, "TxtA"))
109 return;
110
111 hidePane(this, "TxtA");
112}
113
114bool BalloonMessage::isEnableAppear() const {
115 if (!isNerve(user: this, nerve: &NrvBalloonMessage.End) && !isNerve(user: this, nerve: &NrvBalloonMessage.Hide) &&
116 isAlive())
117 return false;
118
119 return isNearPlayerActor(threshold: mAppearDist);
120}
121
122void BalloonMessage::appearWithPushA() {
123 appear();
124 showPushA();
125}
126
127void BalloonMessage::showPushA() {
128 if (!isExistPane(this, "TxtA"))
129 return;
130
131 showPane(this, "TxtA");
132}
133
134void BalloonMessage::control() {
135 if (mIsAutoUpdate)
136 update();
137
138 if (_15d && !isNerve(user: this, nerve: &NrvBalloonMessage.End) && !isNerve(user: this, nerve: &NrvBalloonMessage.Hide) &&
139 (isClipped(mHostActor) || isDead(actor: mHostActor))) {
140 setNerve(user: this, nerve: &NrvBalloonMessage.End);
141
142 return;
143 }
144
145 mTalkMessageVoicePlayer->update();
146 updateTrans();
147}
148
149void BalloonMessage::update() {
150 if (isEnableAppear())
151 appear();
152
153 if (isEnableEnd())
154 setNerve(user: this, nerve: &NrvBalloonMessage.End);
155}
156
157bool BalloonMessage::isWait() const {
158 return isNerve(user: this, nerve: &Wait);
159}
160
161bool BalloonMessage::isVoicePlayerPlaying() const {
162 if (mTalkMessageVoicePlayer->isPlaying())
163 return true;
164
165 if (!mSeName.cstr())
166 return false;
167
168 return checkIsPlayingSe(mHostActor, mSeName.cstr(), nullptr);
169}
170
171bool BalloonMessage::isShowPushA() const {
172 return !isHidePane(this, "TxtA");
173}
174
175bool BalloonMessage::isEnableEnd() const {
176 if (isNerve(user: this, nerve: &NrvBalloonMessage.End) || isNerve(user: this, nerve: &NrvBalloonMessage.Hide) ||
177 !isAlive())
178 return false;
179
180 return !isNearPlayerActor(threshold: mKillDist);
181}
182
183void BalloonMessage::end() {
184 if (isNerve(user: this, nerve: &NrvBalloonMessage.End) || isNerve(user: this, nerve: &NrvBalloonMessage.Hide))
185 return;
186
187 setNerve(user: this, nerve: &NrvBalloonMessage.End);
188}
189
190void BalloonMessage::setTextW(const char16* message) {
191 setPaneString(this, mPaneName, message, 0);
192}
193
194void BalloonMessage::exeAppear() {
195 if (isActionEnd(layout: this, paneName: nullptr))
196 setNerve(user: this, nerve: &Wait);
197}
198
199void BalloonMessage::exeWait() {
200 if (isFirstStep(user: this))
201 startAction(layout: this, actionName: "Wait", paneName: nullptr);
202}
203
204void BalloonMessage::exeEnd() {
205 if (isFirstStep(user: this))
206 startAction(layout: this, actionName: "End", paneName: nullptr);
207
208 if (isActionEnd(layout: this, paneName: nullptr)) {
209 if (mIsAutoUpdate)
210 setNerve(user: this, nerve: &NrvBalloonMessage.Hide);
211 else
212 kill();
213 }
214}
215
216void BalloonMessage::exeHide() {}
217
218bool BalloonMessage::isNearPlayerActor(f32 threshold) const {
219 if (mPlayerIndex >= 0)
220 return isNear(actor: mHostActor, target: getPlayerActor(mHostActor, mPlayerIndex), threshold);
221
222 return isNearPlayer(mHostActor, threshold);
223}
224
225BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info,
226 const BalloonMessageInitParam& initParam) {
227 return BalloonMessage::create(hostActor, info: getLayoutInitInfo(initInfo: info), initParam, isAutoUpdate: true);
228}
229
230BalloonMessage* createBalloonMessageNoAutoUpdate(const LiveActor* hostActor,
231 const ActorInitInfo& info,
232 const BalloonMessageInitParam& initParam) {
233 return BalloonMessage::create(hostActor, info: getLayoutInitInfo(initInfo: info), initParam, isAutoUpdate: false);
234}
235
236BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info) {
237 BalloonMessageInitParam initParam = {.message = "未設定"};
238
239 return BalloonMessage::create(hostActor, info: getLayoutInitInfo(initInfo: info), initParam, isAutoUpdate: true);
240}
241
242BalloonMessage* createBalloonMessage(const LiveActor* hostActor, const ActorInitInfo& info,
243 const char* message) {
244 BalloonMessageInitParam initParam = {.message = message};
245
246 return BalloonMessage::create(hostActor, info: getLayoutInitInfo(initInfo: info), initParam, isAutoUpdate: true);
247}
248} // namespace al
249