]> git.localhorst.tv Git - l2e.git/blob - src/battle/states/PerformAttacks.cpp
extracted battle logic into a class
[l2e.git] / src / battle / states / PerformAttacks.cpp
1 #include "PerformAttacks.h"
2
3 #include "../BattleState.h"
4 #include "../Hero.h"
5 #include "../Monster.h"
6 #include "../TargetSelection.h"
7 #include "../../app/Application.h"
8 #include "../../app/Input.h"
9 #include "../../common/Ikari.h"
10 #include "../../common/Item.h"
11 #include "../../common/Spell.h"
12 #include "../../graphics/Animation.h"
13 #include "../../graphics/Font.h"
14 #include "../../graphics/Frame.h"
15
16 #include <cstring>
17
18 using app::Application;
19 using app::Input;
20 using math::Vector;
21 using graphics::AnimationRunner;
22 using std::vector;
23
24 namespace battle {
25
26 PerformAttacks::PerformAttacks(Battle *battle, BattleState *parent)
27 : battle(battle)
28 , parent(parent)
29 , moveAnimation(0)
30 , targetAnimation(0)
31 , titleBarText(0)
32 , cursor(-1) {
33
34 }
35
36
37 void PerformAttacks::OnEnterState(SDL_Surface *screen) {
38         battle->CalculateAttackOrder();
39         numberAnimation.reserve(battle->MaxMonsters() > battle->NumHeroes() + 1
40                         ? battle->MaxMonsters()
41                         : battle->NumHeroes() + 1);
42         numberPosition.reserve(numberAnimation.size());
43         OnResize(screen->w, screen->h);
44 }
45
46 void PerformAttacks::OnExitState(SDL_Surface *screen) {
47         battle->ClearAllAttacks();
48 }
49
50 void PerformAttacks::OnResumeState(SDL_Surface *screen) {
51
52 }
53
54 void PerformAttacks::OnPauseState(SDL_Surface *screen) {
55
56 }
57
58
59 void PerformAttacks::OnResize(int width, int height) {
60         const Resources &res = parent->Res();
61         framePosition = parent->ScreenOffset();
62         frameSize = Vector<int>(
63                         parent->Width(),
64                         res.titleFrame->BorderHeight() * 2 + res.titleFont->CharHeight());
65 }
66
67
68 void PerformAttacks::HandleEvents(const Input &input) {
69         CheckAnimations();
70         if (HasAnimationsRunning()) return;
71         ResetAnimation();
72         battle->ApplyDamage();
73         battle->NextAttack();
74         if (battle->AttacksFinished()) {
75                 Ctrl().PopState();
76                 return;
77         }
78
79         battle->CalculateDamage();
80
81         if (battle->CurrentAttack().IsMonster()) {
82                 Monster &monster(battle->MonsterAt(battle->CurrentAttack().index));
83                 titleBarText = monster.Name();
84                 targetAnimation = AnimationRunner(monster.MeleeAnimation());
85                 moveAnimation = AnimationRunner(monster.AttackAnimation());
86                 monster.SetAnimation(moveAnimation);
87                 AddNumberAnimations(battle->MonsterAt(battle->CurrentAttack().index).GetAttackChoice().Selection());
88         } else if (battle->CurrentAttack().IsCapsule()) {
89                 Capsule &capsule(battle->GetCapsule());
90                 titleBarText = capsule.Name();
91                 targetAnimation = AnimationRunner(capsule.MeleeAnimation());
92                 moveAnimation = AnimationRunner(capsule.AttackAnimation());
93                 capsule.SetAnimation(moveAnimation);
94                 AddNumberAnimations(capsule.GetAttackChoice().Selection());
95         } else {
96                 Hero &hero(battle->HeroAt(battle->CurrentAttack().index));
97                 const AttackChoice &ac(battle->CurrentAttackAttackChoice());
98
99                 switch (ac.GetType()) {
100                         case AttackChoice::SWORD:
101                                 if (hero.HasWeapon()) {
102                                         titleBarText = hero.Weapon()->Name();
103                                         targetAnimation = AnimationRunner(hero.Weapon()->AttackAnimation());
104                                 } else {
105                                         titleBarText = "Melee attack!";
106                                         targetAnimation = AnimationRunner(hero.MeleeAnimation());
107                                 }
108                                 moveAnimation = AnimationRunner(hero.AttackAnimation());
109                                 AddNumberAnimations(ac.Selection());
110                                 break;
111                         case AttackChoice::MAGIC:
112                                 titleBarText = ac.GetSpell()->Name();
113                                 moveAnimation = AnimationRunner(hero.SpellAnimation());
114                                 break;
115                         case AttackChoice::DEFEND:
116                                 titleBarText = "Defends.";
117                                 moveAnimation.Clear();
118                                 break;
119                         case AttackChoice::IKARI:
120                                 if (ac.GetItem()->HasIkari()) {
121                                         titleBarText = ac.GetItem()->GetIkari()->Name();
122                                         if (ac.GetItem()->GetIkari()->IsMagical()) {
123                                                 moveAnimation = AnimationRunner(hero.SpellAnimation());
124                                         } else {
125                                                 moveAnimation = AnimationRunner(hero.AttackAnimation());
126                                         }
127                                 }
128                                 break;
129                         case AttackChoice::ITEM:
130                                 titleBarText = ac.GetItem()->Name();
131                                 moveAnimation.Clear();
132                                 break;
133                         case AttackChoice::UNDECIDED:
134                                 titleBarText = "UNDECIDED";
135                                 moveAnimation.Clear();
136                                 break;
137                 }
138         }
139
140         if (titleBarText) {
141                 titleBarTimer = GraphicsTimers().StartCountdown(850);
142                 textPosition = parent->ScreenOffset() + Vector<int>(
143                                 (parent->Width() - std::strlen(titleBarText) * parent->Res().titleFont->CharWidth()) / 2,
144                                 parent->Res().titleFrame->BorderHeight());
145         }
146         if (moveAnimation.Valid()) {
147                 moveAnimation.Start(*this);
148                 if (battle->CurrentAttack().IsMonster()) {
149                         battle->MonsterAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
150                 } else if (battle->CurrentAttack().IsHero()) {
151                         battle->HeroAt(battle->CurrentAttack().index).SetAnimation(moveAnimation);
152                 } else {
153                         battle->GetCapsule().SetAnimation(moveAnimation);
154                 }
155         }
156         if (targetAnimation.Valid()) {
157                 targetAnimationTimer = GraphicsTimers().StartCountdown(150);
158         } else {
159                 targetAnimationTimer.Clear();
160         }
161 }
162
163 void PerformAttacks::AddNumberAnimations(const TargetSelection &ts) {
164         if (ts.TargetsMonsters()) {
165                 for (int i(0); i < battle->MaxMonsters(); ++i) {
166                         if (ts.IsBad(i)) {
167                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
168                                 numberPosition.push_back(
169                                                 battle->MonsterAt(i).Position() + parent->ScreenOffset());
170                         } else if (ts.IsGood(i)) {
171                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
172                                 numberPosition.push_back(
173                                                 battle->MonsterAt(i).Position() + parent->ScreenOffset());
174                         }
175                 }
176         } else {
177                 for (int i(0); i < battle->NumHeroes(); ++i) {
178                         if (ts.IsBad(i)) {
179                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
180                                 numberPosition.push_back(
181                                                 battle->HeroAt(i).Position() + parent->ScreenOffset());
182                         } else if (ts.IsGood(i)) {
183                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
184                                 numberPosition.push_back(
185                                                 battle->HeroAt(i).Position() + parent->ScreenOffset());
186                         }
187                 }
188         }
189 }
190
191 void PerformAttacks::CheckAnimations() {
192         if (targetAnimation.Valid() && targetAnimationTimer.JustHit()) {
193                 targetAnimation.Start(*this);
194         }
195         if (moveAnimation.Valid() && !moveAnimation.Finished()) return;
196         if (targetAnimation.Valid() && !targetAnimation.Finished()) return;
197         if (moveAnimation.Valid() || targetAnimation.Valid()) {
198                 moveAnimation.Clear();
199                 targetAnimation.Clear();
200                 for (vector<NumberAnimation>::iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
201                         i->Start(*this);
202                 }
203         } else {
204                 for (vector<NumberAnimation>::iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
205                         i->CheckTimers(*this);
206                 }
207         }
208 }
209
210 bool PerformAttacks::HasAnimationsRunning() const {
211         if (titleBarTimer.Running()) return true;
212         if (moveAnimation.Valid() && moveAnimation.Running()) return true;
213         if (targetAnimation.Valid() && targetAnimation.Running()) return true;
214         for (vector<NumberAnimation>::const_iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
215                 if (i->Running()) return true;
216         }
217         return false;
218 }
219
220 void PerformAttacks::ResetAnimation() {
221         if (moveAnimation.Valid()) {
222                 moveAnimation.Clear();
223                 if (!battle->CurrentAttack().IsMonster()) {
224                         battle->HeroAt(battle->CurrentAttack().index).GetAnimation().Clear();
225                 }
226         }
227         if (targetAnimation.Valid()) {
228                 targetAnimation.Clear();
229         }
230         titleBarTimer.Clear();
231         numberAnimation.clear();
232         numberPosition.clear();
233 }
234
235
236 void PerformAttacks::UpdateWorld(Uint32 deltaT) {
237
238 }
239
240 void PerformAttacks::Render(SDL_Surface *screen) {
241         parent->RenderBackground(screen);
242         parent->RenderMonsters(screen);
243         parent->RenderHeroes(screen);
244         parent->RenderCapsule(screen);
245         parent->RenderSmallHeroTags(screen);
246         RenderTitleBar(screen);
247         RenderNumbers(screen);
248         RenderTargetAnimation(screen);
249 }
250
251 void PerformAttacks::RenderTitleBar(SDL_Surface *screen) const {
252         if (!titleBarText || !titleBarTimer.Running()) return;
253
254         parent->Res().titleFrame->Draw(screen, framePosition, frameSize.X(), frameSize.Y());
255
256         parent->Res().titleFont->DrawString(titleBarText, screen, textPosition);
257 }
258
259 void PerformAttacks::RenderNumbers(SDL_Surface *screen) const {
260         for (vector<NumberAnimation>::size_type i(0), end(numberAnimation.size()); i < end; ++i) {
261                 if (numberAnimation[i].Running()) {
262                         Vector<int> align(numberAnimation[i].Width() / -2, numberAnimation[i].Height() * -3 / 4);
263                         numberAnimation[i].Draw(screen, numberPosition[i] + align);
264                 }
265         }
266 }
267
268 void PerformAttacks::RenderTargetAnimation(SDL_Surface *screen) const {
269         if (!targetAnimation.Valid() || !targetAnimation.Running()) return;
270         const TargetSelection &ts(battle->CurrentAttackAttackChoice().Selection());
271         if (ts.TargetsHeroes()) {
272                 for (vector<Vector<int> >::size_type i(0), end(battle->NumHeroes()); i < end; ++i) {
273                         if (ts.IsSelected(i)) {
274                                 targetAnimation.DrawCenter(screen, battle->HeroAt(i).Position() + parent->ScreenOffset());
275                         }
276                 }
277         } else {
278                 for (vector<Vector<int> >::size_type i(0), end(battle->MaxMonsters()); i < end; ++i) {
279                         if (ts.IsSelected(i)) {
280                                 targetAnimation.DrawCenter(screen, battle->MonsterAt(i).Position() + parent->ScreenOffset());
281                         }
282                 }
283         }
284 }
285
286 }