1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #include "battle_animation.h"
20 #include "game_enemy.h"
21 #include "sprite_actor.h"
22 #include "game_battler.h"
23 #include "game_actor.h"
24 #include "game_screen.h"
25 #include "bitmap.h"
26 #include "cache.h"
27 #include "main_data.h"
28 #include "player.h"
29 #include <lcf/reader_util.h>
30 #include "output.h"
31
Sprite_Actor(Game_Actor * actor)32 Sprite_Actor::Sprite_Actor(Game_Actor* actor)
33 : Sprite_Battler(actor, actor->GetId())
34 {
35 CreateSprite();
36 }
37
~Sprite_Actor()38 Sprite_Actor::~Sprite_Actor() {
39 }
40
GetBattler() const41 Game_Actor* Sprite_Actor::GetBattler() const {
42 return static_cast<Game_Actor*>(Sprite_Battler::GetBattler());
43 }
44
Update()45 void Sprite_Actor::Update() {
46 auto* battler = GetBattler();
47
48 if (!battler->IsHidden() && old_hidden != battler->IsHidden()) {
49 DoIdleAnimation();
50 }
51
52 old_hidden = battler->IsHidden();
53
54 ++cycle;
55
56 if (anim_state > 0) {
57 // Animations for allies
58 if (Player::IsRPG2k3()) {
59 UpdatePosition();
60
61 if (animation) {
62 // Is a battle animation
63 animation->SetInvert(battler->IsDirectionFlipped());
64 animation->Update();
65
66 if (animation->IsDone()) {
67 if (loop_state == LoopState_DefaultAnimationAfterFinish) {
68 DoIdleAnimation();
69 } else if (loop_state == LoopState_LoopAnimation) {
70 animation->SetFrame(0);
71 } else if (loop_state == LoopState_WaitAfterFinish) {
72 if (animation->GetFrames() > 0) {
73 animation->SetFrame(animation->GetFrames() - 1);
74 }
75 idling = true;
76 }
77 }
78
79 return;
80 }
81 // Is a battle charset animation
82 static const int frames[] = {0,1,2,1,0};
83 int frame = (battler->IsDefending() ? 0 : (normal_attacking ? std::min(2, cycle / 10) : frames[cycle / 10]));
84
85 if (battler->IsDirectionFlipped()) {
86 frame = 2 - frame;
87 }
88
89 if (frame == sprite_frame)
90 return;
91
92 const lcf::rpg::BattlerAnimation* anim = lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, battler->GetBattleAnimationId());
93 if (!anim) {
94 Output::Warning("Invalid battler animation ID {}", battler->GetBattleAnimationId());
95 return;
96 }
97
98 const auto* ext = lcf::ReaderUtil::GetElement(anim->poses, anim_state);
99 if (!ext) {
100 Output::Warning("Animation {}: Invalid battler anim-extension state {}", anim->ID, anim_state);
101 return;
102 }
103
104 SetSrcRect(Rect(frame * 48, ext->battler_index * 48, 48, 48));
105
106 if (cycle == ((idling || normal_attacking || anim_state == AnimationState_WalkingLeft || anim_state == AnimationState_WalkingRight || anim_state == AnimationState_Victory) ? 40 : 30)) {
107 switch (loop_state) {
108 case LoopState_DefaultAnimationAfterFinish:
109 DoIdleAnimation();
110 break;
111 case LoopState_WaitAfterFinish:
112 --cycle; // incremented to last cycle next update
113 idling = true;
114 break;
115 case LoopState_LoopAnimation:
116 cycle = 0;
117 break;
118 default:
119 assert(false && "Bad loop state");
120 }
121 }
122 }
123 }
124
125 if (animation) {
126 animation->SetVisible(IsVisible());
127 }
128
129 const bool flip = battler->IsDirectionFlipped();
130 SetFlipX(flip);
131 }
132
SetAnimationState(int state,LoopState loop,int animation_id)133 void Sprite_Actor::SetAnimationState(int state, LoopState loop, int animation_id) {
134 // Default value is 100 (function called with val+1)
135 // 100 maps all states to "Bad state" (7)
136 if (state == 101) {
137 state = 7;
138 }
139
140 anim_state = state;
141
142 loop_state = loop;
143
144 cycle = 0;
145
146 idling = false;
147
148 auto* battler = GetBattler();
149
150 if (battler->GetBattleAnimationId() > 0) {
151 const lcf::rpg::BattlerAnimation* anim = lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, battler->GetBattleAnimationId());
152 if (!anim) {
153 Output::Warning("Invalid battler animation ID {}", battler->GetBattleAnimationId());
154 return;
155 }
156
157 const auto* ext = lcf::ReaderUtil::GetElement(anim->poses, anim_state);
158 if (!ext) {
159 Output::Warning("Animation {}: Invalid battler anim-extension state {}", anim->ID, anim_state);
160 return;
161 }
162
163 StringView sprite_file = ext->battler_name;
164
165 if (ext->animation_type == lcf::rpg::BattlerAnimationPose::AnimType_battle) {
166 do_not_draw = false;
167 SetBitmap(BitmapRef());
168 if (animation_id == 0) {
169 animation_id = ext->battle_animation_id;
170 }
171 lcf::rpg::Animation* battle_anim = lcf::ReaderUtil::GetElement(lcf::Data::animations, animation_id);
172 if (!battle_anim) {
173 Output::Warning("Invalid battle animation ID {}", animation_id);
174 animation.reset();
175 } else {
176 animation.reset(new BattleAnimationBattler(*battle_anim, { battler }));
177 animation->SetZ(GetZ());
178 }
179 animation->SetInvert(battler->IsDirectionFlipped());
180 }
181 else {
182 do_not_draw = sprite_file.empty();
183 animation.reset();
184 if (!sprite_file.empty()) {
185 FileRequestAsync* request = AsyncHandler::RequestFile("BattleCharSet", sprite_file);
186 request->SetGraphicFile(true);
187 request_id = request->Bind(&Sprite_Actor::OnBattlercharsetReady, this, ext->battler_index);
188 request->Start();
189 }
190 }
191 }
192 }
193
SetAnimationLoop(LoopState loop)194 void Sprite_Actor::SetAnimationLoop(LoopState loop) {
195 loop_state = loop;
196 }
197
DetectStateChange()198 void Sprite_Actor::DetectStateChange() {
199 if (idling) {
200 DoIdleAnimation();
201 }
202 }
203
IsIdling()204 bool Sprite_Actor::IsIdling() {
205 return idling;
206 }
207
GetWidth() const208 int Sprite_Actor::GetWidth() const {
209 if (animation) {
210 return animation->GetAnimationCellWidth() / 2;
211 }
212 return Sprite::GetWidth();
213 }
214
GetHeight() const215 int Sprite_Actor::GetHeight() const {
216 if (animation) {
217 return animation->GetAnimationCellHeight() / 2;
218 }
219 return Sprite::GetHeight();
220 }
221
CreateSprite()222 void Sprite_Actor::CreateSprite() {
223 auto* battler = GetBattler();
224
225 images = {{battler->GetDisplayX(), battler->GetDisplayY()}};
226
227 SetX(images.back().x);
228 SetY(images.back().y);
229
230 // Not animated -> Monster
231 SetOx(24);
232 SetOy(24);
233 ResetZ();
234 SetAnimationState(anim_state);
235 idling = true;
236 }
237
DoIdleAnimation()238 void Sprite_Actor::DoIdleAnimation() {
239 auto* battler = GetBattler();
240 if (battler->IsDefending()) {
241 SetAnimationState(AnimationState_Defending);
242 idling = true;
243 return;
244 }
245
246 const lcf::rpg::State* state = battler->GetSignificantState();
247 int idling_anim;
248 if (battler->GetBattleAnimationId() <= 0) {
249 // Monster
250 // Only visually different state is Death
251 if (state && state->ID == 1) {
252 idling_anim = AnimationState_Dead;
253 } else {
254 idling_anim = AnimationState_Idle;
255 }
256 } else {
257 idling_anim = state ? state->battler_animation_id + 1 : AnimationState_Idle;
258 }
259
260 if (idling_anim == 101)
261 idling_anim = 7;
262
263 if (idling_anim != anim_state || loop_state == LoopState_DefaultAnimationAfterFinish)
264 SetAnimationState(idling_anim, idling_anim == AnimationState_Dead ? LoopState_WaitAfterFinish : LoopState_LoopAnimation);
265
266 idling = true;
267 }
268
OnBattlercharsetReady(FileRequestResult * result,int32_t battler_index)269 void Sprite_Actor::OnBattlercharsetReady(FileRequestResult* result, int32_t battler_index) {
270 SetBitmap(Cache::Battlecharset(result->file));
271 SetSrcRect(Rect((battler->IsDirectionFlipped() ? 96 : 0), battler_index * 48, 48, 48));
272 }
273
Draw(Bitmap & dst)274 void Sprite_Actor::Draw(Bitmap& dst) {
275 auto* battler = GetBattler();
276 // "do_not_draw" is set to true if the CBA battler name is empty, this
277 // makes the sprite not being drawn. This fixes issue #1708.
278 if (battler->IsHidden() || do_not_draw) {
279 return;
280 }
281
282 SetTone(Main_Data::game_screen->GetTone());
283 SetFlashEffect(battler->GetFlashColor());
284
285 int steps = static_cast<int>(256 / images.size());
286 int opacity = steps;
287 for (auto it = images.crbegin(); it != images.crend(); ++it) {
288 Sprite_Battler::SetX(it->x);
289 Sprite_Battler::SetY(it->y);
290 Sprite_Battler::SetOpacity(std::min(opacity, 255));
291 Sprite_Battler::Draw(dst);
292 opacity += steps;
293 }
294 }
295
UpdatePosition()296 void Sprite_Actor::UpdatePosition() {
297 assert(!images.empty());
298 images.pop_back();
299 images.insert(images.begin(), {battler->GetDisplayX(), battler->GetDisplayY()});
300
301 if (afterimage_fade >= 0) {
302 ++afterimage_fade;
303 if (afterimage_fade >= images.size()) {
304 // CBA finished, remove afterimages
305 images.resize(1);
306 afterimage_fade = -1;
307 }
308 }
309 }
310
ResetZ()311 void Sprite_Actor::ResetZ() {
312 Sprite_Battler::ResetZ();
313 if (animation) {
314 animation->SetZ(GetZ());
315 }
316 }
317
SetNormalAttacking(bool nnormal_attacking)318 void Sprite_Actor::SetNormalAttacking(bool nnormal_attacking) {
319 normal_attacking = nnormal_attacking;
320 }
321
SetAfterimageAmount(unsigned amount)322 void Sprite_Actor::SetAfterimageAmount(unsigned amount) {
323 images.resize(1 + amount);
324 std::fill(images.begin() + 1, images.end(), images.front());
325 }
326
DoAfterimageFade()327 void Sprite_Actor::DoAfterimageFade() {
328 if (images.size() > 1) {
329 afterimage_fade = 0;
330 }
331 }
332