1 	/*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "DogfightState.h"
20 #include <sstream>
21 #include "../Engine/Game.h"
22 #include "../Resource/ResourcePack.h"
23 #include "../Engine/Palette.h"
24 #include "../Engine/Screen.h"
25 #include "../Engine/Language.h"
26 #include "../Engine/SurfaceSet.h"
27 #include "../Engine/Surface.h"
28 #include "../Interface/ImageButton.h"
29 #include "../Interface/Text.h"
30 #include "../Engine/Timer.h"
31 #include "Globe.h"
32 #include "../Savegame/SavedGame.h"
33 #include "../Savegame/Craft.h"
34 #include "../Ruleset/RuleCraft.h"
35 #include "../Savegame/CraftWeapon.h"
36 #include "../Ruleset/RuleCraftWeapon.h"
37 #include "../Savegame/Ufo.h"
38 #include "../Ruleset/RuleUfo.h"
39 #include "../Engine/Music.h"
40 #include "../Engine/RNG.h"
41 #include "../Engine/Sound.h"
42 #include "../Savegame/Base.h"
43 #include "../Savegame/CraftWeaponProjectile.h"
44 #include "../Savegame/Country.h"
45 #include "../Ruleset/RuleCountry.h"
46 #include "../Savegame/Region.h"
47 #include "../Ruleset/RuleRegion.h"
48 #include "../Savegame/AlienMission.h"
49 #include "../Ruleset/Ruleset.h"
50 #include "../Savegame/AlienStrategy.h"
51 #include "../Engine/Options.h"
52 #include <cstdlib>
53 
54 namespace OpenXcom
55 {
56 
57 // UFO blobs graphics ...
58 const int DogfightState::_ufoBlobs[8][13][13] =
59 {
60 		/*0 STR_VERY_SMALL */
61 	{
62 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
63 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
64 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
65 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
66 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
67 		{0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0},
68 		{0, 0, 0, 0, 1, 3, 5, 3, 1, 0, 0, 0, 0},
69 		{0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0},
70 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
71 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
72 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
73 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
74 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
75 	},
76 		/*1 STR_SMALL */
77 	{
78 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
79 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
80 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
81 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
82 		{0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0},
83 		{0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0},
84 		{0, 0, 0, 1, 2, 4, 5, 4, 2, 1, 0, 0, 0},
85 		{0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0},
86 		{0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0},
87 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
88 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
89 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
90 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
91 	},
92 		/*2 STR_MEDIUM_UC */
93 	{
94 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
95 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
96 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
97 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
98 		{0, 0, 0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0},
99 		{0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0},
100 		{0, 0, 1, 2, 3, 5, 5, 5, 3, 2, 1, 0, 0},
101 		{0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0},
102 		{0, 0, 0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0},
103 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
104 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
105 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
106 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
107 	},
108 		/*3 STR_LARGE */
109 	{
110 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
111 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
112 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
113 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
114 		{0, 0, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0},
115 		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
116 		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
117 		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
118 		{0, 0, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0},
119 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
120 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
121 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
122 		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
123 	},
124 		/*4 STR_VERY_LARGE */
125 	{
126 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
127 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
128 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
129 		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
130 		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
131 		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
132 		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
133 		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
134 		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
135 		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
136 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
137 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
138 		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}
139 	},
140 		/*5 STR_HUGE */
141 	{
142 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
143 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
144 		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
145 		{1, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 1},
146 		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
147 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
148 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
149 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
150 		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
151 		{1, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 1},
152 		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
153 		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
154 		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0}
155 	},
156 		/*6 STR_VERY_HUGE :p */
157 	{
158 		{0, 0, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0, 0},
159 		{0, 0, 2, 3, 3, 4, 4, 4, 3, 3, 2, 0, 0},
160 		{0, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 0},
161 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
162 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
163 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
164 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
165 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
166 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
167 		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
168 		{0, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 0},
169 		{0, 0, 2, 3, 3, 4, 4, 4, 3, 3, 2, 0, 0},
170 		{0, 0, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0, 0}
171 	},
172 		/*7 STR_ENOURMOUS */
173 	{
174 		{0, 0, 0, 3, 3, 4, 4, 4, 3, 3, 0, 0, 0},
175 		{0, 0, 3, 4, 4, 5, 5, 5, 4, 4, 3, 0, 0},
176 		{0, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 0},
177 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
178 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
179 		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
180 		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
181 		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
182 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
183 		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
184 		{0, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 0},
185 		{0, 0, 3, 4, 4, 5, 5, 5, 4, 4, 3, 0, 0},
186 		{0, 0, 0, 3, 3, 4, 4, 4, 3, 3, 0, 0, 0}
187 	}
188 };
189 
190 // Projectile blobs
191 const int DogfightState::_projectileBlobs[4][6][3] =
192 {
193 		/*0 STR_STINGRAY_MISSILE ?*/
194 	{
195 		{0, 1, 0},
196 		{1, 9, 1},
197 		{1, 4, 1},
198 		{0, 3, 0},
199 		{0, 2, 0},
200 		{0, 1, 0}
201 	},
202 		/*1 STR_AVALANCHE_MISSILE ?*/
203 	{
204 		{1, 2, 1},
205 		{2, 9, 2},
206 		{2, 5, 2},
207 		{1, 3, 1},
208 		{0, 2, 0},
209 		{0, 1, 0}
210 	},
211 		/*2 STR_CANNON_ROUND ?*/
212 	{
213 		{0, 0, 0},
214 		{0, 7, 0},
215 		{0, 2, 0},
216 		{0, 1, 0},
217 		{0, 0, 0},
218 		{0, 0, 0}
219 	},
220 		/*3 STR_FUSION_BALL ?*/
221 	{
222 		{2, 4, 2},
223 		{4, 9, 4},
224 		{2, 4, 2},
225 		{0, 0, 0},
226 		{0, 0, 0},
227 		{0, 0, 0}
228 	}
229 };
230 /**
231  * Initializes all the elements in the Dogfight window.
232  * @param game Pointer to the core game.
233  * @param globe Pointer to the Geoscape globe.
234  * @param craft Pointer to the craft intercepting.
235  * @param ufo Pointer to the UFO being intercepted.
236  */
DogfightState(Game * game,Globe * globe,Craft * craft,Ufo * ufo)237 DogfightState::DogfightState(Game *game, Globe *globe, Craft *craft, Ufo *ufo) : State(game), _globe(globe), _craft(craft), _ufo(ufo), _timeout(50), _currentDist(640), _targetDist(560), _end(false), _destroyUfo(false), _destroyCraft(false), _ufoBreakingOff(false), _weapon1Enabled(true), _weapon2Enabled(true), _minimized(false), _endDogfight(false), _animatingHit(false), _ufoSize(0), _craftHeight(0), _currentCraftDamageColor(13), _interceptionsCount(0), _interceptionNumber(0), _x(0), _y(0), _minimizedIconX(0), _minimizedIconY(0)
238 {
239 	_screen = false;
240 
241 	_craft->setInDogfight(true);
242 	_timeScale = 50 + Options::dogfightSpeed;
243 
244 	// Create objects
245 	_window = new Surface(160, 96, _x, _y);
246 	_battle = new Surface(77, 74, _x + 3, _y + 3);
247 	_weapon1 = new InteractiveSurface(15, 17, _x + 4, _y + 52);
248 	_range1 = new Surface(21, 74, _x + 19, _y + 3);
249 	_weapon2 = new InteractiveSurface(15, 17, _x + 64, _y + 52);
250 	_range2 = new Surface(21, 74, _x + 43, _y + 3);
251 	_damage = new Surface(22, 25, _x + 93, _y + 40);
252 
253 	_btnMinimize = new InteractiveSurface(12, 12, _x, _y);
254 	_preview = new InteractiveSurface(160, 96, _x, _y);
255 	_btnStandoff = new ImageButton(36, 15, _x + 83, _y + 4);
256 	_btnCautious = new ImageButton(36, 15, _x + 120, _y + 4);
257 	_btnStandard = new ImageButton(36, 15, _x + 83, _y + 20);
258 	_btnAggressive = new ImageButton(36, 15, _x + 120, _y + 20);
259 	_btnDisengage = new ImageButton(36, 15, _x + 120, _y + 36);
260 	_btnUfo = new ImageButton(36, 17, _x + 120, _y + 52);
261 	_txtAmmo1 = new Text(16, 9, _x + 4, _y + 70);
262 	_txtAmmo2 = new Text(16, 9, _x + 64, _y + 70);
263 	_txtDistance = new Text(40, 9, _x + 116, _y + 72);
264 	_txtStatus = new Text(150, 9, _x + 4, _y + 85);
265 	_btnMinimizedIcon = new InteractiveSurface(32, 20, _minimizedIconX, _minimizedIconY);
266 	_txtInterceptionNumber = new Text(16, 9, _minimizedIconX + 18, _minimizedIconY + 6);
267 
268 	_animTimer = new Timer(Options::dogfightSpeed + 10);
269 	_moveTimer = new Timer(Options::dogfightSpeed);
270 	_w1Timer = new Timer(0);
271 	_w2Timer = new Timer(0);
272 	_mode = _btnStandoff;
273 	_ufoWtimer = new Timer(0);
274 	_ufoEscapeTimer = new Timer(0);
275 	_craftDamageAnimTimer = new Timer(500);
276 
277 	// Set palette
278 	setPalette("PAL_GEOSCAPE");
279 
280 	add(_window);
281 	add(_battle);
282 	add(_weapon1);
283 	add(_range1);
284 	add(_weapon2);
285 	add(_range2);
286 	add(_damage);
287 	add(_btnMinimize);
288 	add(_btnStandoff);
289 	add(_btnCautious);
290 	add(_btnStandard);
291 	add(_btnAggressive);
292 	add(_btnDisengage);
293 	add(_btnUfo);
294 	add(_txtAmmo1);
295 	add(_txtAmmo2);
296 	add(_txtDistance);
297 	add(_preview);
298 	add(_txtStatus);
299 	add(_btnMinimizedIcon);
300 	add(_txtInterceptionNumber);
301 
302 	// Set up objects
303 	Surface *graphic;
304 	graphic = _game->getResourcePack()->getSurface("INTERWIN.DAT");
305 	graphic->setX(0);
306 	graphic->setY(0);
307 	graphic->getCrop()->x = 0;
308 	graphic->getCrop()->y = 0;
309 	graphic->getCrop()->w = 160;
310 	graphic->getCrop()->h = 96;
311 	_window->drawRect(graphic->getCrop(), 15);
312 	graphic->blit(_window);
313 
314 	_preview->drawRect(graphic->getCrop(), 15);
315 	graphic->getCrop()->y = 96;
316 	graphic->getCrop()->h = 15;
317 	graphic->blit(_preview);
318 	graphic->setY(67);
319 	graphic->getCrop()->y = 111;
320 	graphic->getCrop()->h = 29;
321 	graphic->blit(_preview);
322 	if (ufo->getRules()->getModSprite() == "")
323 	{
324 		graphic->setY(15);
325 		graphic->getCrop()->y = 140 + 52 * _ufo->getRules()->getSprite();
326 		graphic->getCrop()->h = 52;
327 	}
328 	else
329 	{
330 		graphic = _game->getResourcePack()->getSurface(ufo->getRules()->getModSprite());
331 		graphic->setX(0);
332 		graphic->setY(15);
333 	}
334 	graphic->blit(_preview);
335 	_preview->setVisible(false);
336 	_preview->onMouseClick((ActionHandler)&DogfightState::previewClick);
337 
338 	_btnMinimize->onMouseClick((ActionHandler)&DogfightState::btnMinimizeClick);
339 
340 	_btnStandoff->copy(_window);
341 	_btnStandoff->setColor(Palette::blockOffset(5)+1);
342 	_btnStandoff->setGroup(&_mode);
343 	_btnStandoff->onMouseClick((ActionHandler)&DogfightState::btnStandoffClick);
344 
345 	_btnCautious->copy(_window);
346 	_btnCautious->setColor(Palette::blockOffset(5)+1);
347 	_btnCautious->setGroup(&_mode);
348 	_btnCautious->onMouseClick((ActionHandler)&DogfightState::btnCautiousClick);
349 
350 	_btnStandard->copy(_window);
351 	_btnStandard->setColor(Palette::blockOffset(5)+1);
352 	_btnStandard->setGroup(&_mode);
353 	_btnStandard->onMouseClick((ActionHandler)&DogfightState::btnStandardClick);
354 
355 	_btnAggressive->copy(_window);
356 	_btnAggressive->setColor(Palette::blockOffset(5)+1);
357 	_btnAggressive->setGroup(&_mode);
358 	_btnAggressive->onMouseClick((ActionHandler)&DogfightState::btnAggressiveClick);
359 
360 	_btnDisengage->copy(_window);
361 	_btnDisengage->setColor(Palette::blockOffset(5)+1);
362 	_btnDisengage->onMouseClick((ActionHandler)&DogfightState::btnDisengageClick);
363 	_btnDisengage->setGroup(&_mode);
364 
365 	_btnUfo->copy(_window);
366 	_btnUfo->setColor(Palette::blockOffset(5)+1);
367 	_btnUfo->onMouseClick((ActionHandler)&DogfightState::btnUfoClick);
368 
369 	_txtAmmo1->setColor(Palette::blockOffset(5)+9);
370 
371 	_txtAmmo2->setColor(Palette::blockOffset(5)+9);
372 
373 	_txtDistance->setColor(Palette::blockOffset(5)+9);
374 	_txtDistance->setText(L"640");
375 
376 	_txtStatus->setColor(Palette::blockOffset(5)+9);
377 	_txtStatus->setText(tr("STR_STANDOFF"));
378 
379 	SurfaceSet *set = _game->getResourcePack()->getSurfaceSet("INTICON.PCK");
380 
381 	// Create the minimized dogfight icon.
382 	Surface *frame = set->getFrame(_craft->getRules()->getSprite());
383 	frame->setX(0);
384 	frame->setY(0);
385 	frame->blit(_btnMinimizedIcon);
386 	_btnMinimizedIcon->onMouseClick((ActionHandler)&DogfightState::btnMinimizedIconClick);
387 	_btnMinimizedIcon->setVisible(false);
388 
389 	// Draw correct number on the minimized dogfight icon.
390 	std::wostringstream ss1;
391 	ss1 << _craft->getInterceptionOrder();
392 	_txtInterceptionNumber->setColor(Palette::blockOffset(5));
393 	_txtInterceptionNumber->setText(ss1.str());
394 	_txtInterceptionNumber->setVisible(false);
395 
396 	for (int i = 0; i < _craft->getRules()->getWeapons(); ++i)
397 	{
398 		CraftWeapon *w = _craft->getWeapons()->at(i);
399 		if (w == 0)
400 			continue;
401 
402 		Surface *weapon = 0, *range = 0;
403 		Text *ammo = 0;
404 		int x1, x2;
405 		if (i == 0)
406 		{
407 			weapon = _weapon1;
408 			range = _range1;
409 			ammo = _txtAmmo1;
410 			x1 = 2;
411 			x2 = 0;
412 		}
413 		else
414 		{
415 			weapon = _weapon2;
416 			range = _range2;
417 			ammo = _txtAmmo2;
418 			x1 = 0;
419 			x2 = 18;
420 		}
421 
422 		// Draw weapon icon
423 		frame = set->getFrame(w->getRules()->getSprite() + 5);
424 
425 		frame->setX(0);
426 		frame->setY(0);
427 		frame->blit(weapon);
428 
429 		// Draw ammo
430 		std::wostringstream ss;
431 		ss << w->getAmmo();
432 		ammo->setText(ss.str());
433 
434 		// Draw range (1 km = 1 pixel)
435 		Uint8 color = Palette::blockOffset(7) - 1;
436 		range->lock();
437 
438 		int rangeY = range->getHeight() - w->getRules()->getRange(), connectY = 57;
439 		for (int x = x1; x <= x1 + 18; x += 2)
440 		{
441 			range->setPixel(x, rangeY, color);
442 		}
443 
444 		int minY = 0, maxY = 0;
445 		if (rangeY < connectY)
446 		{
447 			minY = rangeY;
448 			maxY = connectY;
449 		}
450 		else if (rangeY > connectY)
451 		{
452 			minY = connectY;
453 			maxY = rangeY;
454 		}
455 		for (int y = minY; y <= maxY; ++y)
456 		{
457 			range->setPixel(x1 + x2, y, color);
458 		}
459 		for (int x = x2; x <= x2 + 2; ++x)
460 		{
461 			range->setPixel(x, connectY, color);
462 		}
463 		range->unlock();
464 	}
465 
466 	if (!(_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0))
467 	{
468 		_weapon1->setVisible(false);
469 		_range1->setVisible(false);
470 		_txtAmmo1->setVisible(false);
471 	}
472 	if (!(_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0))
473 	{
474 		_weapon2->setVisible(false);
475 		_range2->setVisible(false);
476 		_txtAmmo2->setVisible(false);
477 	}
478 
479 	// Draw damage indicator.
480 	frame = set->getFrame(_craft->getRules()->getSprite() + 11);
481 	frame->setX(0);
482 	frame->setY(0);
483 	frame->blit(_damage);
484 
485 	_animTimer->onTimer((StateHandler)&DogfightState::animate);
486 	_animTimer->start();
487 
488 	_moveTimer->onTimer((StateHandler)&DogfightState::move);
489 	_moveTimer->start();
490 
491 	_w1Timer->onTimer((StateHandler)&DogfightState::fireWeapon1);
492 
493 	_w2Timer->onTimer((StateHandler)&DogfightState::fireWeapon2);
494 
495 	_ufoWtimer->onTimer((StateHandler)&DogfightState::ufoFireWeapon);
496 	_ufoFireInterval = (_ufo->getRules()->getWeaponReload() - (int)(_game->getSavedGame()->getDifficulty()));
497 	_ufoFireInterval = (RNG::generate(0, _ufoFireInterval) + _ufoFireInterval) * _timeScale;
498 	_ufoWtimer->setInterval(_ufoFireInterval);
499 
500 	_ufoEscapeTimer->onTimer((StateHandler)&DogfightState::ufoBreakOff);
501 	int ufoBreakOffInterval = (_ufo->getRules()->getBreakOffTime() + RNG::generate(0, _ufo->getRules()->getBreakOffTime()) - 15 * (int)(_game->getSavedGame()->getDifficulty())) * _timeScale;
502 	_ufoEscapeTimer->setInterval(ufoBreakOffInterval);
503 
504 	_craftDamageAnimTimer->onTimer((StateHandler)&DogfightState::animateCraftDamage);
505 
506 	// Set UFO size - going to be moved to Ufo class to implement simultanous dogfights.
507 	std::string ufoSize = _ufo->getRules()->getSize();
508 	if(ufoSize.compare("STR_VERY_SMALL") == 0)
509 	{
510 		_ufoSize = 0;
511 	}
512 	else if(ufoSize.compare("STR_SMALL") == 0)
513 	{
514 		_ufoSize = 1;
515 	}
516 	else if(ufoSize.compare("STR_MEDIUM_UC") == 0)
517 	{
518 		_ufoSize = 2;
519 	}
520 	else if(ufoSize.compare("STR_LARGE") == 0)
521 	{
522 		_ufoSize = 3;
523 	}
524 	else
525 	{
526 		_ufoSize = 4;
527 	}
528 
529 	// Get crafts height. Used for damage indication.
530 	int x =_damage->getWidth() / 2;
531 	for(int y = 0; y < _damage->getHeight(); ++y)
532 	{
533 		Uint8 pixelColor = _damage->getPixel(x, y);
534 		if(pixelColor >= Palette::blockOffset(10) || pixelColor < Palette::blockOffset(11))
535 		{
536 			++_craftHeight;
537 		}
538 	}
539 
540 	drawCraftDamage();
541 
542 	// Used for weapon toggling.
543 	_weapon1->onMouseClick((ActionHandler)&DogfightState::weapon1Click);
544 	_weapon2->onMouseClick((ActionHandler)&DogfightState::weapon2Click);
545 }
546 
547 /**
548  * Deletes timers.
549  */
~DogfightState()550 DogfightState::~DogfightState()
551 {
552 	delete _animTimer;
553 	delete _moveTimer;
554 	delete _w1Timer;
555 	delete _w2Timer;
556 	delete _ufoWtimer;
557 	delete _ufoEscapeTimer;
558 	delete _craftDamageAnimTimer;
559 	while(!_projectiles.empty())
560 	{
561 		delete _projectiles.back();
562 		_projectiles.pop_back();
563 	}
564 	if (_craft)
565 		_craft->setInDogfight(false);
566 }
567 
568 /**
569  * Runs the dogfighter timers.
570  */
think()571 void DogfightState::think()
572 {
573 	if(!_endDogfight)
574 	{
575 		_moveTimer->think(this, 0);
576 		if(!_endDogfight && !_minimized) // check _endDogfight again, because moveTimer can change it
577 		{
578 			_animTimer->think(this, 0);
579 			_w1Timer->think(this, 0);
580 			_w2Timer->think(this, 0);
581 			_ufoWtimer->think(this, 0);
582 			_ufoEscapeTimer->think(this, 0);
583 			_craftDamageAnimTimer->think(this, 0);
584 		}
585 		else if(!_endDogfight && (_craft->getDestination() != _ufo || _ufo->getStatus() == Ufo::LANDED))
586 		{
587 			endDogfight();
588 		}
589 	}
590 }
591 
592 /**
593  * Animates interceptor damage by changing the color and redrawing the image.
594  */
animateCraftDamage()595 void DogfightState::animateCraftDamage()
596 {
597 	if(_minimized)
598 	{
599 		return;
600 	}
601 	--_currentCraftDamageColor;
602 	if(_currentCraftDamageColor < 13)
603 	{
604 		_currentCraftDamageColor = 14;
605 	}
606 	drawCraftDamage();
607 }
608 
609 /**
610  * Draws interceptor damage according to percentage of HP's left.
611  */
drawCraftDamage()612 void DogfightState::drawCraftDamage()
613 {
614 	if(_minimized)
615 	{
616 		return;
617 	}
618 	if(_craft->getDamagePercentage() != 0)
619 	{
620 		if(!_craftDamageAnimTimer->isRunning())
621 		{
622 			_craftDamageAnimTimer->start();
623 		}
624 		int damagePercentage = _craft->getDamagePercentage();
625 		int rowsToColor = (int)floor((double)_craftHeight * (double)(damagePercentage / 100.));
626 		if(rowsToColor == 0)
627 		{
628 			return;
629 		}
630 		int rowsColored = 0;
631 		bool rowColored = false;
632 		for(int y = 0; y < _damage->getHeight(); ++y)
633 		{
634 			rowColored = false;
635 			for(int x = 0; x < _damage->getWidth(); ++x)
636 			{
637 				int pixelColor = _damage->getPixel(x, y);
638 				if(pixelColor == 13 || pixelColor == 14)
639 				{
640 					_damage->setPixel(x, y, _currentCraftDamageColor);
641 					rowColored = true;
642 				}
643 				if(pixelColor >= Palette::blockOffset(10) && pixelColor < Palette::blockOffset(11))
644 				{
645 					_damage->setPixel(x, y, _currentCraftDamageColor);
646 					rowColored = true;
647 				}
648 			}
649 			if(rowColored)
650 			{
651 				++rowsColored;
652 			}
653 			if(rowsColored == rowsToColor)
654 			{
655 				break;
656 			}
657 		}
658 	}
659 }
660 
661 /**
662  * Animates the window with a palette effect.
663  */
animate()664 void DogfightState::animate()
665 {
666 	if(_minimized)
667 	{
668 		return;
669 	}
670 	// Animate radar waves and other stuff.
671 	for(int x = 0; x < _window->getWidth(); ++x)
672 	{
673 		for(int y = 0; y < _window->getHeight(); ++y)
674 		{
675 			Uint8 radarPixelColor = _window->getPixel(x, y);
676 			if(radarPixelColor >= Palette::blockOffset(7) && radarPixelColor < Palette::blockOffset(7) + 16)
677 			{
678 				++radarPixelColor;
679 				if(radarPixelColor >= Palette::blockOffset(7) + 16)
680 				{
681 					radarPixelColor = Palette::blockOffset(7);
682 				}
683 				_window->setPixel(x, y, radarPixelColor);
684 			}
685 		}
686 	}
687 
688 	_battle->clear();
689 
690 	// Draw UFO.
691 	if(!_ufo->isDestroyed())
692 	{
693 		drawUfo();
694 	}
695 
696 	// Draw projectiles.
697 	for(std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
698 	{
699 		drawProjectile((*it));
700 	}
701 
702 	// Clears text after a while
703 	if (_timeout == 0)
704 	{
705 		_txtStatus->setText(L"");
706 	}
707 	else
708 	{
709 		_timeout--;
710 	}
711 
712 	// Animate UFO hit.
713 	bool lastHitAnimFrame = false;
714 	if(_animatingHit && _ufo->getHitFrame() > 0)
715 	{
716 		_ufo->setHitFrame(_ufo->getHitFrame() - 1);
717 		if(_ufo->getHitFrame() == 0)
718 		{
719 			_animatingHit = false;
720 			lastHitAnimFrame = true;
721 		}
722 	}
723 
724 	// Animate UFO crash landing.
725 	if(_ufo->isCrashed() && _ufo->getHitFrame() == 0 && !lastHitAnimFrame)
726 	{
727 		--_ufoSize;
728 	}
729 }
730 
731 /**
732  * Moves the craft towards the UFO according to
733  * the current interception mode. Handles projectile movements as well.
734  */
move()735 void DogfightState::move()
736 {
737 	bool finalRun = false;
738 	// Check if craft is not low on fuel when window minimized, and
739 	// Check if crafts destination hasn't been changed when window minimized.
740 	Ufo* u = dynamic_cast<Ufo*>(_craft->getDestination());
741 	if (u != _ufo || _craft->getLowFuel() || (_minimized && _ufo->isCrashed()))
742 	{
743 		endDogfight();
744 		return;
745 	}
746 
747 	if(_minimized && _ufo->getSpeed() > _craft->getSpeed())
748 	{
749 		_craft->setSpeed(_craft->getRules()->getMaxSpeed());
750 		if(_ufo->getSpeed() > _craft->getSpeed())
751 		{
752 			_ufoBreakingOff = true;
753 			finalRun = true;
754 		}
755 	}
756 	// Check if UFO is not breaking off.
757 	if(_ufo->getSpeed() == _ufo->getRules()->getMaxSpeed())
758 	{
759 		_craft->setSpeed(_craft->getRules()->getMaxSpeed());
760 		// Crappy craft is chasing UFO.
761 		if(_ufo->getSpeed() > _craft->getSpeed())
762 		{
763 			_ufoBreakingOff = true;
764 			finalRun = true;
765 			setStatus("STR_UFO_OUTRUNNING_INTERCEPTOR");
766 		}
767 		else //ufo cannot break off, because it's too slow
768 		{
769 			_ufoBreakingOff = false;
770 		}
771 
772 	}
773 	bool projectileInFlight = false;
774 	if(!_minimized)
775 	{
776 		int distanceChange = 0;
777 
778 		// Update distance
779 		if(!_ufoBreakingOff)
780 		{
781 			if (_currentDist < _targetDist && !_ufo->isCrashed() && !_craft->isDestroyed())
782 			{
783 				distanceChange = 4;
784 				if (_currentDist + distanceChange >_targetDist)
785 				{
786 					distanceChange = _targetDist - _currentDist;
787 				}
788 			}
789 			else if (_currentDist > _targetDist && !_ufo->isCrashed() && !_craft->isDestroyed())
790 			{
791 				distanceChange = -2;
792 			}
793 
794 			// don't let the interceptor mystically push or pull its fired projectiles
795 			for(std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
796 			{
797 				if ((*it)->getGlobalType() != CWPGT_BEAM && (*it)->getDirection() == D_UP) (*it)->setPosition((*it)->getPosition() + distanceChange);
798 			}
799 		}
800 		else
801 		{
802 			distanceChange = 4;
803 
804 			// UFOs can try to outrun our missiles, don't adjust projectile positions here
805 			// If UFOs ever fire anything but beams, those positions need to be adjust here though.
806 		}
807 
808 		_currentDist += distanceChange;
809 
810 		std::wostringstream ss;
811 		ss << _currentDist;
812 		_txtDistance->setText(ss.str());
813 
814 		// Move projectiles and check for hits.
815 		for(std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
816 		{
817 			CraftWeaponProjectile *p = (*it);
818 			p->move();
819 			// Projectiles fired by interceptor.
820 			if(p->getDirection() == D_UP)
821 			{
822 				// Projectile reached the UFO - determine if it's been hit.
823 				if(((p->getPosition() >= _currentDist) || (p->getGlobalType() == CWPGT_BEAM && p->toBeRemoved())) && !_ufo->isCrashed())
824 				{
825 					// UFO hit.
826 					if (RNG::percent(p->getAccuracy()))
827 					{
828 						// Formula delivered by Volutar
829 						int damage = RNG::generate(p->getDamage() / 2, p->getDamage());
830 						_ufo->setDamage(_ufo->getDamage() + damage);
831 						if(_ufo->isCrashed())
832 						{
833 							_ufo->setShotDownByCraftId(_craft->getId());
834 							_ufoBreakingOff = false;
835 							_ufo->setSpeed(0);
836 						}
837 						if (_ufo->getHitFrame() == 0)
838 						{
839 							_animatingHit = true;
840 							_ufo->setHitFrame(3);
841 						}
842 
843 						setStatus("STR_UFO_HIT");
844 						_game->getResourcePack()->getSound("GEO.CAT", 12)->play(); //12
845 						p->remove();
846 					}
847 					// Missed.
848 					else
849 					{
850 						if(p->getGlobalType() == CWPGT_BEAM)
851 						{
852 							p->remove();
853 						}
854 						else
855 						{
856 							p->setMissed(true);
857 						}
858 					}
859 				}
860 				// Check if projectile passed it's maximum range.
861 				if(p->getGlobalType() == CWPGT_MISSILE)
862 				{
863 					if (p->getPosition() / 8 >= p->getRange())
864 					{
865 						p->remove();
866 					}
867 					else if (!_ufo->isCrashed())
868 					{
869 						projectileInFlight = true;
870 					}
871 				}
872 			}
873 			// Projectiles fired by UFO.
874 			else if(p->getDirection() == D_DOWN)
875 			{
876 				if(p->getGlobalType() == CWPGT_MISSILE || (p->getGlobalType() == CWPGT_BEAM && p->toBeRemoved()))
877 				{
878 					if(RNG::percent(p->getAccuracy()))
879 					{
880 						// Formula delivered by Volutar
881 						int damage = RNG::generate(0, _ufo->getRules()->getWeaponPower());
882 						if (damage)
883 						{
884 							_craft->setDamage(_craft->getDamage() + damage);
885 							drawCraftDamage();
886 							setStatus("STR_INTERCEPTOR_DAMAGED");
887 							_game->getResourcePack()->getSound("GEO.CAT", 10)->play(); //10
888 							if (_mode == _btnCautious && _craft->getDamagePercentage() >= 50)
889 							{
890 								_targetDist = STANDOFF_DIST;
891 							}
892 						}
893 					}
894 					p->remove();
895 				}
896 			}
897 		}
898 
899 		// Remove projectiles that hit or missed their target.
900 		for(std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end();)
901 		{
902 			if((*it)->toBeRemoved() == true || ((*it)->getMissed() == true && (*it)->getPosition() <= 0))
903 			{
904 				delete *it;
905 				it = _projectiles.erase(it);
906 			}
907 			else
908 			{
909 				++it;
910 			}
911 		}
912 
913 		// Handle weapons and craft distance.
914 		for (int i = 0; i < _craft->getRules()->getWeapons(); ++i)
915 		{
916 			CraftWeapon *w = _craft->getWeapons()->at(i);
917 			if (w == 0)
918 			{
919 				continue;
920 			}
921 			Timer *wTimer = 0;
922 			if (i == 0)
923 			{
924 				wTimer = _w1Timer;
925 			}
926 			else
927 			{
928 				wTimer = _w2Timer;
929 			}
930 
931 			// Handle weapon firing
932 			if (!wTimer->isRunning() && _currentDist <= w->getRules()->getRange() * 8 && w->getAmmo() > 0 && _mode != _btnStandoff
933 				&& _mode != _btnDisengage && !_ufo->isCrashed() && !_craft->isDestroyed())
934 			{
935 				wTimer->start();
936 				if (i == 0)
937 				{
938 					fireWeapon1();
939 				}
940 				else
941 				{
942 					fireWeapon2();
943 				}
944 			}
945 			else if (wTimer->isRunning() && (_currentDist > w->getRules()->getRange() * 8 || (w->getAmmo() == 0 && !projectileInFlight) || _mode == _btnStandoff
946 				|| _mode == _btnDisengage || _ufo->isCrashed() || _craft->isDestroyed()))
947 			{
948 				wTimer->stop();
949 				// Handle craft distance according to option set by user and available ammo.
950 				if (w->getAmmo() == 0 && !_craft->isDestroyed())
951 				{
952 					if (_mode == _btnCautious)
953 					{
954 						minimumDistance();
955 					}
956 					else if (_mode == _btnStandard)
957 					{
958 						maximumDistance();
959 					}
960 				}
961 			}
962 		}
963 
964 		// Handle UFO firing.
965 		if(!_ufoWtimer->isRunning() && _currentDist <= _ufo->getRules()->getWeaponRange() * 8 && !_ufo->isCrashed() && !_craft->isDestroyed())
966 		{
967 			if (_ufo->getShootingAt() == 0)
968 			{
969 				_ufo->setShootingAt(_interceptionNumber);
970 				_ufoWtimer->start();
971 				ufoFireWeapon();
972 			}
973 		}
974 		else if(_ufoWtimer->isRunning() && (_currentDist > _ufo->getRules()->getWeaponRange() * 8 || _ufo->isCrashed() || _craft->isDestroyed()))
975 		{
976 			_ufo->setShootingAt(0);
977 			_ufoWtimer->stop();
978 		}
979 	}
980 
981 	// Check when battle is over.
982 	if (_end == true && (((_currentDist > 640 || _minimized) && (_mode == _btnDisengage || _ufoBreakingOff == true)) || (_timeout == 0 && (_ufo->isCrashed() || _craft->isDestroyed()))))
983 	{
984 		if (_ufoBreakingOff)
985 		{
986 			_ufo->move();
987 			_craft->setDestination(_ufo);
988 		}
989 		if (!_destroyCraft && (_destroyUfo || _mode == _btnDisengage))
990 		{
991 			_craft->returnToBase();
992 		}
993 		endDogfight();
994 	}
995 
996 	// End dogfight if craft is destroyed.
997 	if(!_end && _craft->isDestroyed())
998 	{
999 		setStatus("STR_INTERCEPTOR_DESTROYED");
1000 		_timeout += 30;
1001 		_game->getResourcePack()->getSound("GEO.CAT", 13)->play();
1002 		finalRun = true;
1003 		_destroyCraft = true;
1004 		_ufo->setShootingAt(0);
1005 		_ufoWtimer->stop();
1006 		_w1Timer->stop();
1007 		_w2Timer->stop();
1008 	}
1009 
1010 	// End dogfight if UFO is crashed or destroyed.
1011 	if (!_end && _ufo->isCrashed())
1012 	{
1013 		AlienMission *mission = _ufo->getMission();
1014 		mission->ufoShotDown(*_ufo, *_game, *_globe);
1015 		// Check for retaliation trigger.
1016 		if (!RNG::percent(4 * (24 - (int)(_game->getSavedGame()->getDifficulty()))))
1017 		{
1018 			// Spawn retaliation mission.
1019 			std::string targetRegion;
1020 			if (RNG::percent(50 - 6 * (int)(_game->getSavedGame()->getDifficulty())))
1021 			{
1022 				// Attack on UFO's mission region
1023 				targetRegion = _ufo->getMission()->getRegion();
1024 			}
1025 			else
1026 			{
1027 				// Try to find and attack the originating base.
1028 				targetRegion = _game->getSavedGame()->locateRegion(*_craft->getBase())->getRules()->getType();
1029 				// TODO: If the base is removed, the mission is canceled.
1030 			}
1031 			// Difference from original: No retaliation until final UFO lands (Original: Is spawned).
1032 			if (!_game->getSavedGame()->getAlienMission(targetRegion, "STR_ALIEN_RETALIATION"))
1033 			{
1034 				const RuleAlienMission &rule = *_game->getRuleset()->getAlienMission("STR_ALIEN_RETALIATION");
1035 				AlienMission *mission = new AlienMission(rule);
1036 				mission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
1037 				mission->setRegion(targetRegion, *_game->getRuleset());
1038 				mission->setRace(_ufo->getAlienRace());
1039 				mission->start();
1040 				_game->getSavedGame()->getAlienMissions().push_back(mission);
1041 			}
1042 		}
1043 		_ufoEscapeTimer->stop();
1044 		if (_ufo->isDestroyed())
1045 		{
1046 			if(_ufo->getShotDownByCraftId() == _craft->getId())
1047 			{
1048 				for(std::vector<Country*>::iterator country = _game->getSavedGame()->getCountries()->begin(); country != _game->getSavedGame()->getCountries()->end(); ++country)
1049 				{
1050 					if((*country)->getRules()->insideCountry(_ufo->getLongitude(), _ufo->getLatitude()))
1051 					{
1052 						(*country)->addActivityXcom(_ufo->getRules()->getScore()*2);
1053 						break;
1054 					}
1055 				}
1056 				for(std::vector<Region*>::iterator region = _game->getSavedGame()->getRegions()->begin(); region != _game->getSavedGame()->getRegions()->end(); ++region)
1057 				{
1058 					if((*region)->getRules()->insideRegion(_ufo->getLongitude(), _ufo->getLatitude()))
1059 					{
1060 						(*region)->addActivityXcom(_ufo->getRules()->getScore()*2);
1061 						break;
1062 					}
1063 				}
1064 				setStatus("STR_UFO_DESTROYED");
1065 				_game->getResourcePack()->getSound("GEO.CAT", 10)->play(); //11
1066 			}
1067 			_destroyUfo = true;
1068 		}
1069 		else
1070 		{
1071 			if(_ufo->getShotDownByCraftId() == _craft->getId())
1072 			{
1073 				setStatus("STR_UFO_CRASH_LANDS");
1074 				_game->getResourcePack()->getSound("GEO.CAT", 10)->play(); //10
1075 				for(std::vector<Country*>::iterator country = _game->getSavedGame()->getCountries()->begin(); country != _game->getSavedGame()->getCountries()->end(); ++country)
1076 				{
1077 					if((*country)->getRules()->insideCountry(_ufo->getLongitude(), _ufo->getLatitude()))
1078 					{
1079 						(*country)->addActivityXcom(_ufo->getRules()->getScore());
1080 						break;
1081 					}
1082 				}
1083 				for(std::vector<Region*>::iterator region = _game->getSavedGame()->getRegions()->begin(); region != _game->getSavedGame()->getRegions()->end(); ++region)
1084 				{
1085 					if((*region)->getRules()->insideRegion(_ufo->getLongitude(), _ufo->getLatitude()))
1086 					{
1087 						(*region)->addActivityXcom(_ufo->getRules()->getScore());
1088 						break;
1089 					}
1090 				}
1091 			}
1092 			if (!_globe->insideLand(_ufo->getLongitude(), _ufo->getLatitude()))
1093 			{
1094 				_ufo->setStatus(Ufo::DESTROYED);
1095 				_destroyUfo = true;
1096 			}
1097 			else
1098 			{
1099 				_ufo->setSecondsRemaining(RNG::generate(24, 96)*3600);
1100 				_ufo->setAltitude("STR_GROUND");
1101 				if (_ufo->getCrashId() == 0)
1102 				{
1103 					_ufo->setCrashId(_game->getSavedGame()->getId("STR_CRASH_SITE"));
1104 				}
1105 			}
1106 		}
1107 		_timeout += 30;
1108 		if(_ufo->getShotDownByCraftId() != _craft->getId())
1109 		{
1110 			_timeout += 50;
1111 			_ufo->setHitFrame(3);
1112 		}
1113 		finalRun = true;
1114 	}
1115 
1116 	if (!_end && _ufo->getStatus() == Ufo::LANDED)
1117 	{
1118 		_timeout += 30;
1119 		finalRun = true;
1120 		_ufo->setShootingAt(0);
1121 		_ufoWtimer->stop();
1122 		_w1Timer->stop();
1123 		_w2Timer->stop();
1124 	}
1125 
1126 	if (!projectileInFlight && finalRun)
1127 	{
1128 		_end = true;
1129 	}
1130 }
1131 
1132 /**
1133  * Fires a shot from the first weapon
1134  * equipped on the craft.
1135  */
fireWeapon1()1136 void DogfightState::fireWeapon1()
1137 {
1138 	if(_weapon1Enabled)
1139 	{
1140 		CraftWeapon *w1 = _craft->getWeapons()->at(0);
1141 		if (w1->setAmmo(w1->getAmmo() - 1))
1142 		{
1143 
1144 			std::wostringstream ss;
1145 			ss << w1->getAmmo();
1146 			_txtAmmo1->setText(ss.str());
1147 
1148 			CraftWeaponProjectile *p = w1->fire();
1149 			p->setDirection(D_UP);
1150 			p->setHorizontalPosition(HP_LEFT);
1151 			_projectiles.push_back(p);
1152 
1153 			_game->getResourcePack()->getSound("GEO.CAT", w1->getRules()->getSound())->play();
1154 		}
1155 	}
1156 }
1157 
1158 /**
1159  * Fires a shot from the second weapon
1160  * equipped on the craft.
1161  */
fireWeapon2()1162 void DogfightState::fireWeapon2()
1163 {
1164 	if(_weapon2Enabled)
1165 	{
1166 		CraftWeapon *w2 = _craft->getWeapons()->at(1);
1167 		if (w2->setAmmo(w2->getAmmo() - 1))
1168 		{
1169 
1170 			std::wostringstream ss;
1171 			ss << w2->getAmmo();
1172 			_txtAmmo2->setText(ss.str());
1173 
1174 			CraftWeaponProjectile *p = w2->fire();
1175 			p->setDirection(D_UP);
1176 			p->setHorizontalPosition(HP_RIGHT);
1177 			_projectiles.push_back(p);
1178 
1179 			_game->getResourcePack()->getSound("GEO.CAT", w2->getRules()->getSound())->play();
1180 		}
1181 	}
1182 }
1183 
1184 /**
1185  *	Each time a UFO will try to fire it's cannons
1186  *	a calculation is made. There's only 10% chance
1187  *	that it will actually fire.
1188  */
ufoFireWeapon()1189 void DogfightState::ufoFireWeapon()
1190 {
1191 	_ufoFireInterval = (_ufo->getRules()->getWeaponReload() - (int)(_game->getSavedGame()->getDifficulty()));
1192 	_ufoFireInterval = (RNG::generate(0, _ufoFireInterval) + _ufoFireInterval) * _timeScale;
1193 	_ufoWtimer->setInterval(_ufoFireInterval);
1194 
1195 	setStatus("STR_UFO_RETURN_FIRE");
1196 	CraftWeaponProjectile *p = new CraftWeaponProjectile();
1197 	p->setType(CWPT_PLASMA_BEAM);
1198 	p->setAccuracy(60);
1199 	p->setDamage(_ufo->getRules()->getWeaponPower());
1200 	p->setDirection(D_DOWN);
1201 	p->setHorizontalPosition(HP_CENTER);
1202 	p->setPosition(_currentDist - (_ufo->getRules()->getRadius() / 2));
1203 	_projectiles.push_back(p);
1204 	_game->getResourcePack()->getSound("GEO.CAT", 8)->play();
1205 }
1206 
1207 /**
1208  * Sets the craft to the minimum distance
1209  * required to fire a weapon.
1210  */
minimumDistance()1211 void DogfightState::minimumDistance()
1212 {
1213 	int max = 0;
1214 	for (std::vector<CraftWeapon*>::iterator i = _craft->getWeapons()->begin(); i < _craft->getWeapons()->end(); ++i)
1215 	{
1216 		if (*i == 0)
1217 			continue;
1218 		if ((*i)->getRules()->getRange() > max && (*i)->getAmmo() > 0)
1219 		{
1220 			max = (*i)->getRules()->getRange();
1221 		}
1222 	}
1223 	if (max == 0)
1224 	{
1225 		_targetDist = STANDOFF_DIST;
1226 	}
1227 	else
1228 	{
1229 		_targetDist = max * 8;
1230 	}
1231 }
1232 
1233 /**
1234  * Sets the craft to the maximum distance
1235  * required to fire a weapon.
1236  */
maximumDistance()1237 void DogfightState::maximumDistance()
1238 {
1239 	int min = 1000;
1240 	for (std::vector<CraftWeapon*>::iterator i = _craft->getWeapons()->begin(); i < _craft->getWeapons()->end(); ++i)
1241 	{
1242 		if (*i == 0)
1243 			continue;
1244 		if ((*i)->getRules()->getRange() < min && (*i)->getAmmo() > 0)
1245 		{
1246 			min = (*i)->getRules()->getRange();
1247 		}
1248 	}
1249 	if (min == 1000)
1250 	{
1251 		_targetDist = STANDOFF_DIST;
1252 	}
1253 	else
1254 	{
1255 		_targetDist = min * 8;
1256 	}
1257 }
1258 
1259 /**
1260  * Updates the status text and restarts
1261  * the text timeout counter.
1262  * @param status New status text.
1263  */
setStatus(const std::string & status)1264 void DogfightState::setStatus(const std::string &status)
1265 {
1266 	_txtStatus->setText(tr(status));
1267 	_timeout = 50;
1268 }
1269 
1270 /**
1271  * Minimizes the dogfight window.
1272  * @param action Pointer to an action.
1273  */
btnMinimizeClick(Action *)1274 void DogfightState::btnMinimizeClick(Action *)
1275 {
1276 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1277 	{
1278 		if (_currentDist >= STANDOFF_DIST)
1279 		{
1280 			setMinimized(true);
1281 			_window->setVisible(false);
1282 			_preview->setVisible(false);
1283 			_btnStandoff->setVisible(false);
1284 			_btnCautious->setVisible(false);
1285 			_btnStandard->setVisible(false);
1286 			_btnAggressive->setVisible(false);
1287 			_btnDisengage->setVisible(false);
1288 			_btnUfo->setVisible(false);
1289 			_btnMinimize->setVisible(false);
1290 			_battle->setVisible(false);
1291 			_weapon1->setVisible(false);
1292 			_range1->setVisible(false);
1293 			_weapon2->setVisible(false);
1294 			_range2->setVisible(false);
1295 			_damage->setVisible(false);
1296 			_txtAmmo1->setVisible(false);
1297 			_txtAmmo2->setVisible(false);
1298 			_txtDistance->setVisible(false);
1299 			_preview->setVisible(false);
1300 			_txtStatus->setVisible(false);
1301 			_btnMinimizedIcon->setVisible(true);
1302 			_txtInterceptionNumber->setVisible(true);
1303 			_ufoEscapeTimer->stop();
1304 		}
1305 		else
1306 		{
1307 			setStatus("STR_MINIMISE_AT_STANDOFF_RANGE_ONLY");
1308 		}
1309 	}
1310 }
1311 
1312 /**
1313  * Switches to Standoff mode (maximum range).
1314  * @param action Pointer to an action.
1315  */
btnStandoffClick(Action *)1316 void DogfightState::btnStandoffClick(Action *)
1317 {
1318 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1319 	{
1320 		_end = false;
1321 		setStatus("STR_STANDOFF");
1322 		_targetDist = STANDOFF_DIST;
1323 	}
1324 }
1325 
1326 /**
1327  * Switches to Cautious mode (maximum weapon range).
1328  * @param action Pointer to an action.
1329  */
btnCautiousClick(Action *)1330 void DogfightState::btnCautiousClick(Action *)
1331 {
1332 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1333 	{
1334 		_end = false;
1335 		setStatus("STR_CAUTIOUS_ATTACK");
1336 		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
1337 		{
1338 			_w1Timer->setInterval(_craft->getWeapons()->at(0)->getRules()->getCautiousReload() * _timeScale);
1339 		}
1340 		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
1341 		{
1342 			_w2Timer->setInterval(_craft->getWeapons()->at(1)->getRules()->getCautiousReload() * _timeScale);
1343 		}
1344 		minimumDistance();
1345 		_ufoEscapeTimer->start();
1346 	}
1347 }
1348 
1349 /**
1350  * Switches to Standard mode (minimum weapon range).
1351  * @param action Pointer to an action.
1352  */
btnStandardClick(Action *)1353 void DogfightState::btnStandardClick(Action *)
1354 {
1355 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1356 	{
1357 		_end = false;
1358 		setStatus("STR_STANDARD_ATTACK");
1359 		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
1360 		{
1361 			_w1Timer->setInterval(_craft->getWeapons()->at(0)->getRules()->getStandardReload() * _timeScale);
1362 		}
1363 		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
1364 		{
1365 			_w2Timer->setInterval(_craft->getWeapons()->at(1)->getRules()->getStandardReload() * _timeScale);
1366 		}
1367 		maximumDistance();
1368 		_ufoEscapeTimer->start();
1369 	}
1370 }
1371 
1372 /**
1373  * Switches to Aggressive mode (minimum range).
1374  * @param action Pointer to an action.
1375  */
btnAggressiveClick(Action *)1376 void DogfightState::btnAggressiveClick(Action *)
1377 {
1378 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1379 	{
1380 		_end = false;
1381 		setStatus("STR_AGGRESSIVE_ATTACK");
1382 		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
1383 		{
1384 			_w1Timer->setInterval(_craft->getWeapons()->at(0)->getRules()->getAggressiveReload() * _timeScale);
1385 		}
1386 		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
1387 		{
1388 			_w2Timer->setInterval(_craft->getWeapons()->at(1)->getRules()->getAggressiveReload() * _timeScale);
1389 		}
1390 		_targetDist = 64;
1391 		_ufoEscapeTimer->start();
1392 	}
1393 }
1394 
1395 /**
1396  * Disengages from the UFO.
1397  * @param action Pointer to an action.
1398  */
btnDisengageClick(Action *)1399 void DogfightState::btnDisengageClick(Action *)
1400 {
1401 	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
1402 	{
1403 		_end = true;
1404 		setStatus("STR_DISENGAGING");
1405 		_targetDist = 800;
1406 		_ufoEscapeTimer->stop();
1407 	}
1408 }
1409 
1410 /**
1411  * Shows a front view of the UFO.
1412  * @param action Pointer to an action.
1413  */
btnUfoClick(Action *)1414 void DogfightState::btnUfoClick(Action *)
1415 {
1416 	_preview->setVisible(true);
1417 	// Disable all other buttons to prevent misclicks
1418 	_btnStandoff->setVisible(false);
1419 	_btnCautious->setVisible(false);
1420 	_btnStandard->setVisible(false);
1421 	_btnAggressive->setVisible(false);
1422 	_btnDisengage->setVisible(false);
1423 	_btnUfo->setVisible(false);
1424 	_btnMinimize->setVisible(false);
1425 	_weapon1->setVisible(false);
1426 	_weapon2->setVisible(false);
1427 }
1428 
1429 /**
1430  * Hides the front view of the UFO.
1431  * @param action Pointer to an action.
1432  */
previewClick(Action *)1433 void DogfightState::previewClick(Action *)
1434 {
1435 	_preview->setVisible(false);
1436 	// Reenable all other buttons to prevent misclicks
1437 	_btnStandoff->setVisible(true);
1438 	_btnCautious->setVisible(true);
1439 	_btnStandard->setVisible(true);
1440 	_btnAggressive->setVisible(true);
1441 	_btnDisengage->setVisible(true);
1442 	_btnUfo->setVisible(true);
1443 	_btnMinimize->setVisible(true);
1444 	_weapon1->setVisible(true);
1445 	_weapon2->setVisible(true);
1446 }
1447 
1448 /*
1449  * Sets UFO to break off mode. Started via timer.
1450  */
ufoBreakOff()1451 void DogfightState::ufoBreakOff()
1452 {
1453 	if(!_ufo->isCrashed() && !_ufo->isDestroyed() && !_craft->isDestroyed())
1454 	{
1455 		_ufo->setSpeed(_ufo->getRules()->getMaxSpeed());
1456 		_ufoBreakingOff = true;
1457 	}
1458 }
1459 
1460 /*
1461  * Draws the UFO blob on the radar screen.
1462  * Currently works only for original sized blobs
1463  * 13 x 13 pixels.
1464  */
drawUfo()1465 void DogfightState::drawUfo()
1466 {
1467 	if(_ufoSize < 0 || _ufo->isDestroyed() || _minimized)
1468 	{
1469 		return;
1470 	}
1471 	int currentUfoXposition =  _battle->getWidth() / 2 - 6;
1472 	int currentUfoYposition = _battle->getHeight() - (_currentDist / 8) - 6;
1473 	for(int y = 0; y < 13; ++y)
1474 	{
1475 		for(int x = 0; x < 13; ++x)
1476 		{
1477 			Uint8 pixelOffset = _ufoBlobs[_ufoSize + _ufo->getHitFrame()][y][x];
1478 			if(pixelOffset == 0)
1479 			{
1480 				continue;
1481 			}
1482 			else
1483 			{
1484 				if(_ufo->isCrashed() || _ufo->getHitFrame() > 0)
1485 				{
1486 					pixelOffset *= 2;
1487 				}
1488 				Uint8 radarPixelColor = _window->getPixel(currentUfoXposition + x + 3, currentUfoYposition + y + 3); // + 3 cause of the window frame
1489 				Uint8 color = radarPixelColor - pixelOffset;
1490 				if(color < 108)
1491 				{
1492 					color = 108;
1493 				}
1494 				_battle->setPixel(currentUfoXposition + x, currentUfoYposition + y, color);
1495 			}
1496 		}
1497 	}
1498 }
1499 
1500 /*
1501  * Draws projectiles on the radar screen.
1502  * Depending on what type of projectile it is, it's
1503  * shape will be different. Currently works for
1504  * original sized blobs 3 x 6 pixels.
1505  */
drawProjectile(const CraftWeaponProjectile * p)1506 void DogfightState::drawProjectile(const CraftWeaponProjectile* p) {
1507 	if(_minimized)
1508 	{
1509 		return;
1510 	}
1511 	int xPos = _battle->getWidth() / 2 + p->getHorizontalPosition();
1512 	// Draw missiles.
1513 	if(p->getGlobalType() == CWPGT_MISSILE)
1514 	{
1515 		xPos -= 1;
1516 		int yPos = _battle->getHeight() - p->getPosition() / 8;
1517 		for(int x = 0; x < 3; ++x)
1518 		{
1519 			for(int y = 0; y < 6; ++y)
1520 			{
1521 				int pixelOffset = _projectileBlobs[p->getType()][y][x];
1522 				if(pixelOffset == 0)
1523 				{
1524 					continue;
1525 				}
1526 				else
1527 				{
1528 					Uint8 radarPixelColor = _window->getPixel(xPos + x + 3, yPos + y + 3); // + 3 cause of the window frame
1529 					Uint8 color = radarPixelColor - pixelOffset;
1530 					if(color < 108)
1531 					{
1532 						color = 108;
1533 					}
1534 					_battle->setPixel(xPos + x, yPos + y, color);
1535 				}
1536 			}
1537 		}
1538 	}
1539 	// Draw beams.
1540 	else if(p->getGlobalType() == CWPGT_BEAM)
1541 	{
1542 		int yStart = _battle->getHeight() - 2;
1543 		int yEnd = _battle->getHeight() - (_currentDist / 8);
1544 		Uint8 pixelOffset = p->getState();
1545 		for(int y = yStart; y > yEnd; --y)
1546 		{
1547 			Uint8 radarPixelColor = _window->getPixel(xPos + 3, y + 3);
1548 			Uint8 color = radarPixelColor - pixelOffset;
1549 			if(color < 108)
1550 			{
1551 				color = 108;
1552 			}
1553 			_battle->setPixel(xPos, y, color);
1554 		}
1555 	}
1556 }
1557 
1558 /**
1559  * Toggles usage of weapon number 1.
1560  * @param action Pointer to an action.
1561  */
weapon1Click(Action *)1562 void DogfightState::weapon1Click(Action *)
1563 {
1564 	_weapon1Enabled = !_weapon1Enabled;
1565 	recolor(0, _weapon1Enabled);
1566 }
1567 
1568 /**
1569  * Toggles usage of weapon number 2.
1570  * @param action Pointer to an action.
1571  */
weapon2Click(Action *)1572 void DogfightState::weapon2Click(Action *)
1573 {
1574 	_weapon2Enabled = !_weapon2Enabled;
1575 	recolor(1, _weapon2Enabled);
1576 }
1577 
1578 /**
1579  * Changes colors of weapon icons, range indicators and ammo texts base on current weapon state.
1580  * @param weaponNo - number of weapon for which colors must be changed.
1581  * @param currentState - state of weapon (enabled = true, disabled = false).
1582  */
recolor(const int weaponNo,const bool currentState)1583 void DogfightState::recolor(const int weaponNo, const bool currentState)
1584 {
1585 	InteractiveSurface *weapon = 0;
1586 	Text *ammo = 0;
1587 	Surface *range = 0;
1588 	int weaponAndAmmoOffset = 24, rangeOffset = 7;
1589 	if(weaponNo == 0)
1590 	{
1591 		weapon = _weapon1;
1592 		ammo = _txtAmmo1;
1593 		range = _range1;
1594 	}
1595 	else if(weaponNo == 1)
1596 	{
1597 		weapon = _weapon2;
1598 		ammo = _txtAmmo2;
1599 		range = _range2;
1600 	}
1601 	else
1602 	{
1603 		return;
1604 	}
1605 
1606 	if(currentState)
1607 	{
1608 		weapon->offset(-weaponAndAmmoOffset);
1609 		ammo->offset(-weaponAndAmmoOffset);
1610 		range->offset(-rangeOffset);
1611 	}
1612 	else
1613 	{
1614 		weapon->offset(weaponAndAmmoOffset);
1615 		ammo->offset(weaponAndAmmoOffset);
1616 		range->offset(rangeOffset);
1617 	}
1618 }
1619 
1620 /**
1621  * Returns true if state is minimized. Otherwise returns false.
1622  * @return Is the dogfight minimized?
1623  */
isMinimized() const1624 bool DogfightState::isMinimized() const
1625 {
1626 	return _minimized;
1627 }
1628 
1629 /**
1630  * Sets the state to minimized/maximized status.
1631  * @param minimized Is the dogfight minimized?
1632  */
setMinimized(const bool minimized)1633 void DogfightState::setMinimized(const bool minimized)
1634 {
1635 	_minimized = minimized;
1636 }
1637 
1638 /**
1639  * Maximizes the interception window.
1640  * @param action Pointer to an action.
1641  */
btnMinimizedIconClick(Action *)1642 void DogfightState::btnMinimizedIconClick(Action *)
1643 {
1644 	setMinimized(false);
1645 	_window->setVisible(true);
1646 	_btnStandoff->setVisible(true);
1647 	_btnCautious->setVisible(true);
1648 	_btnStandard->setVisible(true);
1649 	_btnAggressive->setVisible(true);
1650 	_btnDisengage->setVisible(true);
1651 	_btnUfo->setVisible(true);
1652 	_btnMinimize->setVisible(true);
1653 	_battle->setVisible(true);
1654 	_weapon1->setVisible(true);
1655 	_range1->setVisible(true);
1656 	_weapon2->setVisible(true);
1657 	_range2->setVisible(true);
1658 	_damage->setVisible(true);
1659 	_txtAmmo1->setVisible(true);
1660 	_txtAmmo2->setVisible(true);
1661 	_txtDistance->setVisible(true);
1662 	_txtStatus->setVisible(true);
1663 	_btnMinimizedIcon->setVisible(false);
1664 	_txtInterceptionNumber->setVisible(false);
1665 	_preview->setVisible(false);
1666 }
1667 
1668 /**
1669  * Sets interception number. Used to draw proper number when window minimized.
1670  * @param number ID number.
1671  */
setInterceptionNumber(const int number)1672 void DogfightState::setInterceptionNumber(const int number)
1673 {
1674 	_interceptionNumber = number;
1675 }
1676 
1677 /**
1678  * Sets interceptions count. Used to properly position the window.
1679  * @param count Amount of interception windows.
1680  */
setInterceptionsCount(const size_t count)1681 void DogfightState::setInterceptionsCount(const size_t count)
1682 {
1683 	_interceptionsCount = count;
1684 	calculateWindowPosition();
1685 	moveWindow();
1686 }
1687 
1688 /**
1689  * Calculates dogfight window position according to
1690  * number of active interceptions.
1691  */
calculateWindowPosition()1692 void DogfightState::calculateWindowPosition()
1693 {
1694 
1695 	_minimizedIconX = 5;
1696 	_minimizedIconY = (5 * _interceptionNumber) + (16 * (_interceptionNumber - 1));
1697 
1698 	if(_interceptionsCount == 1)
1699 	{
1700 		_x = 80;
1701 		_y = 52;
1702 	}
1703 	else if(_interceptionsCount == 2)
1704 	{
1705 		if(_interceptionNumber == 1)
1706 		{
1707 			_x = 80;
1708 			_y = 0;
1709 		}
1710 		else // 2
1711 		{
1712 			_x = 80;
1713 			//_y = (_game->getScreen()->getHeight() / 2) - 96;
1714 			_y = 200 - _window->getHeight();//96;
1715 		}
1716 	}
1717 	else if(_interceptionsCount == 3)
1718 	{
1719 		if(_interceptionNumber == 1)
1720 		{
1721 			_x = 80;
1722 			_y = 0;
1723 		}
1724 		else if(_interceptionNumber == 2)
1725 		{
1726 			_x = 0;
1727 			//_y = (_game->getScreen()->getHeight() / 2) - 96;
1728 			_y = 200 - _window->getHeight();//96;
1729 		}
1730 		else // 3
1731 		{
1732 			//_x = (_game->getScreen()->getWidth() / 2) - 160;
1733 			//_y = (_game->getScreen()->getHeight() / 2) - 96;
1734 			_x = 320 - _window->getWidth();//160;
1735 			_y = 200 - _window->getHeight();//96;
1736 		}
1737 	}
1738 	else
1739 	{
1740 		if(_interceptionNumber == 1)
1741 		{
1742 			_x = 0;
1743 			_y = 0;
1744 		}
1745 		else if(_interceptionNumber == 2)
1746 		{
1747 			//_x = (_game->getScreen()->getWidth() / 2) - 160;
1748 			_x = 320 - _window->getWidth();//160;
1749 			_y = 0;
1750 		}
1751 		else if(_interceptionNumber == 3)
1752 		{
1753 			_x = 0;
1754 			//_y = (_game->getScreen()->getHeight() / 2) - 96;
1755 			_y = 200 - _window->getHeight();//96;
1756 		}
1757 		else // 4
1758 		{
1759 			//_x = (_game->getScreen()->getWidth() / 2) - 160;
1760 			//_y = (_game->getScreen()->getHeight() / 2) - 96;
1761 			_x = 320 - _window->getWidth();//160;
1762 			_y = 200 - _window->getHeight();//96;
1763 		}
1764 	}
1765 	_x += _game->getScreen()->getDX();
1766 	_y += _game->getScreen()->getDY();
1767 }
1768 
1769 /**
1770  * Relocates all dogfight window elements to
1771  * calculated position. This is used when multiple
1772  * interceptions are running.
1773  */
moveWindow()1774 void DogfightState::moveWindow()
1775 {
1776 	_window->setX(_x); _window->setY(_y);
1777 	_battle->setX(_x + 3); _battle->setY(_y + 3);
1778 	_weapon1->setX(_x + 4); _weapon1->setY(_y + 52);
1779 	_range1->setX(_x + 19); _range1->setY(_y + 3);
1780 	_weapon2->setX(_x + 64); _weapon2->setY(_y + 52);
1781 	_range2->setX(_x + 43); _range2->setY(_y + 3);
1782 	_damage->setX(_x + 93); _damage->setY(_y + 40);
1783 	_btnMinimize->setX(_x); _btnMinimize->setY(_y);
1784 	_preview->setX(_x); _preview->setY(_y);
1785 	_btnStandoff->setX(_x + 83); _btnStandoff->setY(_y + 4);
1786 	_btnCautious->setX(_x + 120); _btnCautious->setY(_y + 4);
1787 	_btnStandard->setX(_x + 83); _btnStandard->setY(_y + 20);
1788 	_btnAggressive->setX(_x + 120); _btnAggressive->setY(_y + 20);
1789 	_btnDisengage->setX(_x + 120); _btnDisengage->setY(_y + 36);
1790 	_btnUfo->setX(_x + 120); _btnUfo->setY(_y + 52);
1791 	_txtAmmo1->setX(_x + 4); _txtAmmo1->setY(_y + 70);
1792 	_txtAmmo2->setX(_x + 64); _txtAmmo2->setY(_y + 70);
1793 	_txtDistance->setX(_x + 116); _txtDistance->setY(_y + 72);
1794 	_txtStatus->setX(_x + 4); _txtStatus->setY(_y + 85);
1795 	_btnMinimizedIcon->setX(_minimizedIconX); _btnMinimizedIcon->setY(_minimizedIconY);
1796 	_txtInterceptionNumber->setX(_minimizedIconX + 18); _txtInterceptionNumber->setY(_minimizedIconY + 6);
1797 }
1798 
1799 /**
1800  * Checks whether the dogfight should end.
1801  * @return Returns true if the dogfight should end, otherwise returns false.
1802  */
dogfightEnded() const1803 bool DogfightState::dogfightEnded() const
1804 {
1805 	return _endDogfight;
1806 }
1807 
1808 /**
1809  * Returns the UFO associated to this dogfight.
1810  * @return Returns pointer to UFO object associated to this dogfight.
1811  */
getUfo() const1812 Ufo* DogfightState::getUfo() const
1813 {
1814 	return _ufo;
1815 }
1816 
1817 /**
1818  * Ends the dogfight.
1819  */
endDogfight()1820 void DogfightState::endDogfight()
1821 {
1822 	if (_craft)
1823 		_craft->setInDogfight(false);
1824 	_endDogfight = true;
1825 }
1826 
1827 /**
1828  * Returns interception number.
1829  * @return interception number
1830  */
getInterceptionNumber() const1831 int DogfightState::getInterceptionNumber() const
1832 {
1833 	return _interceptionNumber;
1834 }
1835 
1836 }
1837