]> git.localhorst.tv Git - l2e.git/blob - src/battle/states/PerformAttacks.cpp
less pointer chasing in battle::PerformAttacks
[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         const Battle::Order &attack = battle->CurrentAttack();
81
82         if (attack.IsMonster()) {
83                 Monster &monster(battle->MonsterAt(attack.index));
84                 titleBarText = monster.Name();
85                 targetAnimation = AnimationRunner(monster.MeleeAnimation());
86                 moveAnimation = AnimationRunner(monster.AttackAnimation());
87                 monster.SetAnimation(moveAnimation);
88                 AddNumberAnimations(monster.GetAttackChoice().Selection());
89         } else if (attack.IsCapsule()) {
90                 Capsule &capsule(battle->GetCapsule());
91                 titleBarText = capsule.Name();
92                 targetAnimation = AnimationRunner(capsule.MeleeAnimation());
93                 moveAnimation = AnimationRunner(capsule.AttackAnimation());
94                 capsule.SetAnimation(moveAnimation);
95                 AddNumberAnimations(capsule.GetAttackChoice().Selection());
96         } else {
97                 Hero &hero(battle->HeroAt(attack.index));
98                 const AttackChoice &ac(hero.GetAttackChoice());
99
100                 switch (ac.GetType()) {
101                         case AttackChoice::SWORD:
102                                 if (hero.HasWeapon()) {
103                                         titleBarText = hero.Weapon()->Name();
104                                         targetAnimation = AnimationRunner(hero.Weapon()->AttackAnimation());
105                                 } else {
106                                         titleBarText = "Melee attack!";
107                                         targetAnimation = AnimationRunner(hero.MeleeAnimation());
108                                 }
109                                 moveAnimation = AnimationRunner(hero.AttackAnimation());
110                                 AddNumberAnimations(ac.Selection());
111                                 break;
112                         case AttackChoice::MAGIC:
113                                 titleBarText = ac.GetSpell()->Name();
114                                 moveAnimation = AnimationRunner(hero.SpellAnimation());
115                                 break;
116                         case AttackChoice::DEFEND:
117                                 titleBarText = "Defends.";
118                                 moveAnimation.Clear();
119                                 break;
120                         case AttackChoice::IKARI:
121                                 if (ac.GetItem()->HasIkari()) {
122                                         titleBarText = ac.GetItem()->GetIkari()->Name();
123                                         if (ac.GetItem()->GetIkari()->IsMagical()) {
124                                                 moveAnimation = AnimationRunner(hero.SpellAnimation());
125                                         } else {
126                                                 moveAnimation = AnimationRunner(hero.AttackAnimation());
127                                         }
128                                 }
129                                 break;
130                         case AttackChoice::ITEM:
131                                 titleBarText = ac.GetItem()->Name();
132                                 moveAnimation.Clear();
133                                 break;
134                         case AttackChoice::UNDECIDED:
135                                 titleBarText = "UNDECIDED";
136                                 moveAnimation.Clear();
137                                 break;
138                 }
139         }
140
141         if (titleBarText) {
142                 titleBarTimer = GraphicsTimers().StartCountdown(850);
143                 textPosition = parent->ScreenOffset() + Vector<int>(
144                                 (parent->Width() - std::strlen(titleBarText) * parent->Res().titleFont->CharWidth()) / 2,
145                                 parent->Res().titleFrame->BorderHeight());
146         }
147         if (moveAnimation.Valid()) {
148                 moveAnimation.Start(*this);
149                 if (attack.IsMonster()) {
150                         battle->MonsterAt(attack.index).SetAnimation(moveAnimation);
151                 } else if (attack.IsHero()) {
152                         battle->HeroAt(attack.index).SetAnimation(moveAnimation);
153                 } else {
154                         battle->GetCapsule().SetAnimation(moveAnimation);
155                 }
156         }
157         if (targetAnimation.Valid()) {
158                 targetAnimationTimer = GraphicsTimers().StartCountdown(150);
159         } else {
160                 targetAnimationTimer.Clear();
161         }
162 }
163
164 void PerformAttacks::AddNumberAnimations(const TargetSelection &ts) {
165         if (ts.TargetsMonsters()) {
166                 for (int i(0); i < battle->MaxMonsters(); ++i) {
167                         if (ts.IsBad(i)) {
168                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
169                                 numberPosition.push_back(
170                                                 battle->MonsterAt(i).Position() + parent->ScreenOffset());
171                         } else if (ts.IsGood(i)) {
172                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
173                                 numberPosition.push_back(
174                                                 battle->MonsterAt(i).Position() + parent->ScreenOffset());
175                         }
176                 }
177         } else {
178                 for (int i(0); i < battle->NumHeroes(); ++i) {
179                         if (ts.IsBad(i)) {
180                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().bigNumberSprite));
181                                 numberPosition.push_back(
182                                                 battle->HeroAt(i).Position() + parent->ScreenOffset());
183                         } else if (ts.IsGood(i)) {
184                                 numberAnimation.push_back(NumberAnimation(ts.GetAmount(i), parent->Res().numberAnimationPrototype, parent->Res().greenNumberSprite));
185                                 numberPosition.push_back(
186                                                 battle->HeroAt(i).Position() + parent->ScreenOffset());
187                         }
188                 }
189         }
190 }
191
192 void PerformAttacks::CheckAnimations() {
193         if (targetAnimation.Valid() && targetAnimationTimer.JustHit()) {
194                 targetAnimation.Start(*this);
195         }
196         if (moveAnimation.Valid() && !moveAnimation.Finished()) return;
197         if (targetAnimation.Valid() && !targetAnimation.Finished()) return;
198         if (moveAnimation.Valid() || targetAnimation.Valid()) {
199                 moveAnimation.Clear();
200                 targetAnimation.Clear();
201                 for (vector<NumberAnimation>::iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
202                         i->Start(*this);
203                 }
204         } else {
205                 for (vector<NumberAnimation>::iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
206                         i->CheckTimers(*this);
207                 }
208         }
209 }
210
211 bool PerformAttacks::HasAnimationsRunning() const {
212         if (titleBarTimer.Running()) return true;
213         if (moveAnimation.Valid() && moveAnimation.Running()) return true;
214         if (targetAnimation.Valid() && targetAnimation.Running()) return true;
215         for (vector<NumberAnimation>::const_iterator i(numberAnimation.begin()), end(numberAnimation.end()); i != end; ++i) {
216                 if (i->Running()) return true;
217         }
218         return false;
219 }
220
221 void PerformAttacks::ResetAnimation() {
222         if (moveAnimation.Valid()) {
223                 moveAnimation.Clear();
224                 if (!battle->CurrentAttack().IsMonster()) {
225                         battle->HeroAt(battle->CurrentAttack().index).GetAnimation().Clear();
226                 }
227         }
228         if (targetAnimation.Valid()) {
229                 targetAnimation.Clear();
230         }
231         titleBarTimer.Clear();
232         numberAnimation.clear();
233         numberPosition.clear();
234 }
235
236
237 void PerformAttacks::UpdateWorld(Uint32 deltaT) {
238
239 }
240
241 void PerformAttacks::Render(SDL_Surface *screen) {
242         parent->RenderBackground(screen);
243         parent->RenderMonsters(screen);
244         parent->RenderHeroes(screen);
245         parent->RenderCapsule(screen);
246         parent->RenderSmallHeroTags(screen);
247         RenderTitleBar(screen);
248         RenderNumbers(screen);
249         RenderTargetAnimation(screen);
250 }
251
252 void PerformAttacks::RenderTitleBar(SDL_Surface *screen) const {
253         if (!titleBarText || !titleBarTimer.Running()) return;
254
255         parent->Res().titleFrame->Draw(screen, framePosition, frameSize.X(), frameSize.Y());
256
257         parent->Res().titleFont->DrawString(titleBarText, screen, textPosition);
258 }
259
260 void PerformAttacks::RenderNumbers(SDL_Surface *screen) const {
261         for (vector<NumberAnimation>::size_type i(0), end(numberAnimation.size()); i < end; ++i) {
262                 if (numberAnimation[i].Running()) {
263                         Vector<int> align(numberAnimation[i].Width() / -2, numberAnimation[i].Height() * -3 / 4);
264                         numberAnimation[i].Draw(screen, numberPosition[i] + align);
265                 }
266         }
267 }
268
269 void PerformAttacks::RenderTargetAnimation(SDL_Surface *screen) const {
270         if (!targetAnimation.Valid() || !targetAnimation.Running()) return;
271         const TargetSelection &ts(battle->CurrentAttackAttackChoice().Selection());
272         if (ts.TargetsHeroes()) {
273                 for (vector<Vector<int> >::size_type i(0), end(battle->NumHeroes()); i < end; ++i) {
274                         if (ts.IsSelected(i)) {
275                                 targetAnimation.DrawCenter(screen, battle->HeroAt(i).Position() + parent->ScreenOffset());
276                         }
277                 }
278         } else {
279                 for (vector<Vector<int> >::size_type i(0), end(battle->MaxMonsters()); i < end; ++i) {
280                         if (ts.IsSelected(i)) {
281                                 targetAnimation.DrawCenter(screen, battle->MonsterAt(i).Position() + parent->ScreenOffset());
282                         }
283                 }
284         }
285 }
286
287 }