/*
 * Copyright 2010-2016 OpenXcom Developers.
 *
 * This file is part of OpenXcom.
 *
 * OpenXcom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenXcom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "DogfightState.h"
#include <cmath>
#include <sstream>
#include "GeoscapeState.h"
#include "../Engine/Game.h"
#include "../Mod/Mod.h"
#include "../Engine/Screen.h"
#include "../Engine/LocalizedText.h"
#include "../Engine/SurfaceSet.h"
#include "../Engine/Surface.h"
#include "../Interface/ImageButton.h"
#include "../Interface/Text.h"
#include "../Engine/Timer.h"
#include "Globe.h"
#include "../Savegame/SavedGame.h"
#include "../Savegame/Craft.h"
#include "../Mod/RuleCraft.h"
#include "../Savegame/CraftWeapon.h"
#include "../Mod/RuleCraftWeapon.h"
#include "../Savegame/Ufo.h"
#include "../Mod/RuleUfo.h"
#include "../Engine/RNG.h"
#include "../Engine/Sound.h"
#include "../Savegame/Base.h"
#include "../Savegame/CraftWeaponProjectile.h"
#include "../Savegame/Country.h"
#include "../Mod/RuleCountry.h"
#include "../Savegame/Region.h"
#include "../Mod/RuleRegion.h"
#include "../Savegame/AlienMission.h"
#include "DogfightErrorState.h"
#include "../Mod/RuleInterface.h"
 
namespace OpenXcom
{
 
// UFO blobs graphics ...
const int DogfightState::_ufoBlobs[8][13][13] =
{
		/*0 STR_VERY_SMALL */
	{
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 3, 5, 3, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	},
		/*1 STR_SMALL */
	{
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0},
		{0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0},
		{0, 0, 0, 1, 2, 4, 5, 4, 2, 1, 0, 0, 0},
		{0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0},
		{0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	},
		/*2 STR_MEDIUM_UC */
	{
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0},
		{0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0},
		{0, 0, 1, 2, 3, 5, 5, 5, 3, 2, 1, 0, 0},
		{0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0},
		{0, 0, 0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	},
		/*3 STR_LARGE */
	{
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 0, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0},
		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
		{0, 0, 1, 2, 3, 4, 4, 4, 3, 2, 1, 0, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	},
		/*4 STR_VERY_LARGE */
	{
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
		{0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0},
		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}
	},
		/*5 STR_HUGE */
	{
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
		{1, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 1},
		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1},
		{1, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 1},
		{0, 1, 2, 3, 3, 4, 4, 4, 3, 3, 2, 1, 0},
		{0, 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0, 0},
		{0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0}
	},
		/*6 STR_VERY_HUGE :p */
	{
		{0, 0, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0, 0},
		{0, 0, 2, 3, 3, 4, 4, 4, 3, 3, 2, 0, 0},
		{0, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 0},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2},
		{0, 2, 3, 4, 4, 5, 5, 5, 4, 4, 3, 2, 0},
		{0, 0, 2, 3, 3, 4, 4, 4, 3, 3, 2, 0, 0},
		{0, 0, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0, 0}
	},
		/*7 STR_ENOURMOUS */
	{
		{0, 0, 0, 3, 3, 4, 4, 4, 3, 3, 0, 0, 0},
		{0, 0, 3, 4, 4, 5, 5, 5, 4, 4, 3, 0, 0},
		{0, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 0},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
		{4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3},
		{0, 3, 4, 5, 5, 5, 5, 5, 5, 5, 4, 3, 0},
		{0, 0, 3, 4, 4, 5, 5, 5, 4, 4, 3, 0, 0},
		{0, 0, 0, 3, 3, 4, 4, 4, 3, 3, 0, 0, 0}
	}
};
 
// Projectile blobs
const int DogfightState::_projectileBlobs[4][6][3] =
{
		/*0 STR_STINGRAY_MISSILE ?*/
	{
		{0, 1, 0},
		{1, 9, 1},
		{1, 4, 1},
		{0, 3, 0},
		{0, 2, 0},
		{0, 1, 0}
	},
		/*1 STR_AVALANCHE_MISSILE ?*/
	{
		{1, 2, 1},
		{2, 9, 2},
		{2, 5, 2},
		{1, 3, 1},
		{0, 2, 0},
		{0, 1, 0}
	},
		/*2 STR_CANNON_ROUND ?*/
	{
		{0, 0, 0},
		{0, 7, 0},
		{0, 2, 0},
		{0, 1, 0},
		{0, 0, 0},
		{0, 0, 0}
	},
		/*3 STR_FUSION_BALL ?*/
	{
		{2, 4, 2},
		{4, 9, 4},
		{2, 4, 2},
		{0, 0, 0},
		{0, 0, 0},
		{0, 0, 0}
	}
};
/**
 * Initializes all the elements in the Dogfight window.
 * @param game Pointer to the core game.
 * @param state Pointer to the Geoscape.
 * @param craft Pointer to the craft intercepting.
 * @param ufo Pointer to the UFO being intercepted.
 */
DogfightState::DogfightState(GeoscapeState *state, Craft *craft, Ufo *ufo) : _state(state), _craft(craft), _ufo(ufo), _timeout(50), _currentDist(640), _targetDist(560),
			_w1FireCountdown(0), _w2FireCountdown(0), _end(false), _destroyUfo(false), _destroyCraft(false), _ufoBreakingOff(false), _weapon1Enabled(true), _weapon2Enabled(true),
			_minimized(false), _endDogfight(false), _animatingHit(false), _waitForPoly(false), _waitForAltitude(false), _ufoSize(0), _craftHeight(0), _currentCraftDamageColor(0),
			_interceptionNumber(0), _interceptionsCount(0), _x(0), _y(0), _minimizedIconX(0), _minimizedIconY(0)
{
	_screen = false;
 
	_craft->setInDogfight(true);
 
	// Create objects
	_window = new Surface(160, 96, _x, _y);
	_battle = new Surface(77, 74, _x + 3, _y + 3);
	_weapon1 = new InteractiveSurface(15, 17, _x + 4, _y + 52);
	_range1 = new Surface(21, 74, _x + 19, _y + 3);
	_weapon2 = new InteractiveSurface(15, 17, _x + 64, _y + 52);
	_range2 = new Surface(21, 74, _x + 43, _y + 3);
	_damage = new Surface(22, 25, _x + 93, _y + 40);
 
	_btnMinimize = new InteractiveSurface(12, 12, _x, _y);
	_preview = new InteractiveSurface(160, 96, _x, _y);
	_btnStandoff = new ImageButton(36, 15, _x + 83, _y + 4);
	_btnCautious = new ImageButton(36, 15, _x + 120, _y + 4);
	_btnStandard = new ImageButton(36, 15, _x + 83, _y + 20);
	_btnAggressive = new ImageButton(36, 15, _x + 120, _y + 20);
	_btnDisengage = new ImageButton(36, 15, _x + 120, _y + 36);
	_btnUfo = new ImageButton(36, 17, _x + 120, _y + 52);
	_txtAmmo1 = new Text(16, 9, _x + 4, _y + 70);
	_txtAmmo2 = new Text(16, 9, _x + 64, _y + 70);
	_txtDistance = new Text(40, 9, _x + 116, _y + 72);
	_txtStatus = new Text(150, 9, _x + 4, _y + 85);
	_btnMinimizedIcon = new InteractiveSurface(32, 20, _minimizedIconX, _minimizedIconY);
	_txtInterceptionNumber = new Text(16, 9, _minimizedIconX + 18, _minimizedIconY + 6);
 
	_mode = _btnStandoff;
	_craftDamageAnimTimer = new Timer(500);
 
	// Set palette
	setInterface("dogfight");
 
	add(_window);
	add(_battle);
	add(_weapon1);
	add(_range1);
	add(_weapon2);
	add(_range2);
	add(_damage);
	add(_btnMinimize);
	add(_btnStandoff, "standoffButton", "dogfight", _window);
	add(_btnCautious, "cautiousButton", "dogfight", _window);
	add(_btnStandard, "standardButton", "dogfight", _window);
	add(_btnAggressive, "aggressiveButton", "dogfight", _window);
	add(_btnDisengage, "disengageButton", "dogfight", _window);
	add(_btnUfo, "ufoButton", "dogfight", _window);
	add(_txtAmmo1, "numbers", "dogfight", _window);
	add(_txtAmmo2, "numbers", "dogfight", _window);
	add(_txtDistance, "distance", "dogfight", _window);
	add(_preview);
	add(_txtStatus, "text", "dogfight", _window);
	add(_btnMinimizedIcon);
	add(_txtInterceptionNumber, "minimizedNumber", "dogfight");
 
	_btnStandoff->invalidate(false);
	_btnCautious->invalidate(false);
	_btnStandard->invalidate(false);
	_btnAggressive->invalidate(false);
	_btnDisengage->invalidate(false);
	_btnUfo->invalidate(false);
 
	// Set up objects
	RuleInterface *dogfightInterface = _game->getMod()->getInterface("dogfight");
 
	Surface *graphic;
	graphic = _game->getMod()->getSurface("INTERWIN.DAT");
	graphic->setX(0);
	graphic->setY(0);
	graphic->getCrop()->x = 0;
	graphic->getCrop()->y = 0;
	graphic->getCrop()->w = _window->getWidth();
	graphic->getCrop()->h = _window->getHeight();
	_window->drawRect(graphic->getCrop(), 15);
	graphic->blit(_window);
 
	_preview->drawRect(graphic->getCrop(), 15);
	graphic->getCrop()->y = dogfightInterface->getElement("previewTop")->y;
	graphic->getCrop()->h = dogfightInterface->getElement("previewTop")->h;
	graphic->blit(_preview);
	graphic->setY(_window->getHeight() - dogfightInterface->getElement("previewBot")->h);
	graphic->getCrop()->y = dogfightInterface->getElement("previewBot")->y;
	graphic->getCrop()->h = dogfightInterface->getElement("previewBot")->h;
	graphic->blit(_preview);
	if (ufo->getRules()->getModSprite().empty())
	{
		graphic->getCrop()->y = dogfightInterface->getElement("previewMid")->y + dogfightInterface->getElement("previewMid")->h * _ufo->getRules()->getSprite();
		graphic->getCrop()->h = dogfightInterface->getElement("previewMid")->h;
	}
	else
	{
		graphic = _game->getMod()->getSurface(ufo->getRules()->getModSprite());
	}
	graphic->setX(dogfightInterface->getElement("previewTop")->x);
	graphic->setY(dogfightInterface->getElement("previewTop")->h);
	graphic->blit(_preview);
	_preview->setVisible(false);
	_preview->onMouseClick((ActionHandler)&DogfightState::previewClick);
 
	_btnMinimize->onMouseClick((ActionHandler)&DogfightState::btnMinimizeClick);
 
	_btnStandoff->copy(_window);
	_btnStandoff->setGroup(&_mode);
	_btnStandoff->onMousePress((ActionHandler)&DogfightState::btnStandoffPress);
 
	_btnCautious->copy(_window);
	_btnCautious->setGroup(&_mode);
	_btnCautious->onMousePress((ActionHandler)&DogfightState::btnCautiousPress);
 
	_btnStandard->copy(_window);
	_btnStandard->setGroup(&_mode);
	_btnStandard->onMousePress((ActionHandler)&DogfightState::btnStandardPress);
 
	_btnAggressive->copy(_window);
	_btnAggressive->setGroup(&_mode);
	_btnAggressive->onMousePress((ActionHandler)&DogfightState::btnAggressivePress);
 
	_btnDisengage->copy(_window);
	_btnDisengage->onMousePress((ActionHandler)&DogfightState::btnDisengagePress);
	_btnDisengage->setGroup(&_mode);
 
	_btnUfo->copy(_window);
	_btnUfo->onMouseClick((ActionHandler)&DogfightState::btnUfoClick);
 
	_txtDistance->setText("640");
 
	_txtStatus->setText(tr("STR_STANDOFF"));
 
	SurfaceSet *set = _game->getMod()->getSurfaceSet("INTICON.PCK");
 
	// Create the minimized dogfight icon.
	Surface *frame = set->getFrame(_craft->getRules()->getSprite());
	frame->setX(0);
	frame->setY(0);
	frame->blit(_btnMinimizedIcon);
	_btnMinimizedIcon->onMouseClick((ActionHandler)&DogfightState::btnMinimizedIconClick);
	_btnMinimizedIcon->setVisible(false);
 
	// Draw correct number on the minimized dogfight icon.
	std::ostringstream ss1;
	if (_craft->getInterceptionOrder() == 0)
	{
		int maxInterceptionOrder = 0;
		for (std::vector<Base*>::iterator baseIt = _game->getSavedGame()->getBases()->begin(); baseIt != _game->getSavedGame()->getBases()->end(); ++baseIt)
		{
			for (std::vector<Craft*>::iterator craftIt = (*baseIt)->getCrafts()->begin(); craftIt != (*baseIt)->getCrafts()->end(); ++craftIt)
			{
				if ((*craftIt)->getInterceptionOrder() > maxInterceptionOrder)
				{
					maxInterceptionOrder = (*craftIt)->getInterceptionOrder();
				}
			}
		}
		_craft->setInterceptionOrder(++maxInterceptionOrder);
	}
	ss1 << _craft->getInterceptionOrder();
	_txtInterceptionNumber->setText(ss1.str());
	_txtInterceptionNumber->setVisible(false);
 
	// define the colors to be used
	_colors[CRAFT_MIN] = dogfightInterface->getElement("craftRange")->color;
	_colors[CRAFT_MAX] = dogfightInterface->getElement("craftRange")->color2;
	_colors[RADAR_MIN] = dogfightInterface->getElement("radarRange")->color;
	_colors[RADAR_MAX] = dogfightInterface->getElement("radarRange")->color2;
	_colors[DAMAGE_MIN] = dogfightInterface->getElement("damageRange")->color;
	_colors[DAMAGE_MAX] = dogfightInterface->getElement("damageRange")->color2;
	_colors[BLOB_MIN] = dogfightInterface->getElement("radarDetail")->color;
	_colors[RANGE_METER] = dogfightInterface->getElement("radarDetail")->color2;
	_colors[DISABLED_WEAPON] = dogfightInterface->getElement("disabledWeapon")->color;
	_colors[DISABLED_RANGE] = dogfightInterface->getElement("disabledWeapon")->color2;
	_colors[DISABLED_AMMO] = dogfightInterface->getElement("disabledAmmo")->color;
 
	for (unsigned int i = 0; i < _craft->getRules()->getWeapons(); ++i)
	{
		CraftWeapon *w = _craft->getWeapons()->at(i);
		if (w == 0)
			continue;
 
		Surface *weapon = 0, *range = 0;
		Text *ammo = 0;
		int x1, x2;
		if (i == 0)
		{
			weapon = _weapon1;
			range = _range1;
			ammo = _txtAmmo1;
			x1 = 2;
			x2 = 0;
		}
		else
		{
			weapon = _weapon2;
			range = _range2;
			ammo = _txtAmmo2;
			x1 = 0;
			x2 = 18;
		}
 
		// Draw weapon icon
		frame = set->getFrame(w->getRules()->getSprite() + 5);
 
		frame->setX(0);
		frame->setY(0);
		frame->blit(weapon);
 
		// Draw ammo
		std::ostringstream ss;
		ss << w->getAmmo();
		ammo->setText(ss.str());
 
		// Draw range (1 km = 1 pixel)
		Uint8 color = _colors[RANGE_METER];
		range->lock();
 
		int rangeY = range->getHeight() - w->getRules()->getRange(), connectY = 57;
		for (int x = x1; x <= x1 + 18; x += 2)
		{
			range->setPixel(x, rangeY, color);
		}
 
		int minY = 0, maxY = 0;
		if (rangeY < connectY)
		{
			minY = rangeY;
			maxY = connectY;
		}
		else if (rangeY > connectY)
		{
			minY = connectY;
			maxY = rangeY;
		}
		for (int y = minY; y <= maxY; ++y)
		{
			range->setPixel(x1 + x2, y, color);
		}
		for (int x = x2; x <= x2 + 2; ++x)
		{
			range->setPixel(x, connectY, color);
		}
		range->unlock();
	}
 
	if (!(_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0))
	{
		_weapon1->setVisible(false);
		_range1->setVisible(false);
		_txtAmmo1->setVisible(false);
	}
	if (!(_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0))
	{
		_weapon2->setVisible(false);
		_range2->setVisible(false);
		_txtAmmo2->setVisible(false);
	}
 
	// Draw damage indicator.
	frame = set->getFrame(_craft->getRules()->getSprite() + 11);
	frame->setX(0);
	frame->setY(0);
	frame->blit(_damage);
 
	_craftDamageAnimTimer->onTimer((StateHandler)&DogfightState::animateCraftDamage);
 
	// don't set these variables if the ufo is already engaged in a dogfight
	if (!_ufo->getEscapeCountdown())
	{
		_ufo->setFireCountdown(0);
		int escapeCountdown = _ufo->getRules()->getBreakOffTime() + RNG::generate(0, _ufo->getRules()->getBreakOffTime()) - 30 * _game->getSavedGame()->getDifficultyCoefficient();
		_ufo->setEscapeCountdown(std::max(1, escapeCountdown));
	}
 
	// technically this block is redundant, but i figure better to initialize the variables as SOMETHING
	if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
	{
		_w1FireInterval = _craft->getWeapons()->at(0)->getRules()->getStandardReload();
	}
	if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
	{
		_w2FireInterval = _craft->getWeapons()->at(1)->getRules()->getStandardReload();
	}
 
	// Set UFO size - going to be moved to Ufo class to implement simultaneous dogfights.
	std::string ufoSize = _ufo->getRules()->getSize();
	if (ufoSize.compare("STR_VERY_SMALL") == 0)
	{
		_ufoSize = 0;
	}
	else if (ufoSize.compare("STR_SMALL") == 0)
	{
		_ufoSize = 1;
	}
	else if (ufoSize.compare("STR_MEDIUM_UC") == 0)
	{
		_ufoSize = 2;
	}
	else if (ufoSize.compare("STR_LARGE") == 0)
	{
		_ufoSize = 3;
	}
	else
	{
		_ufoSize = 4;
	}
 
	// Get crafts height. Used for damage indication.
	int x =_damage->getWidth() / 2;
	for (int y = 0; y < _damage->getHeight(); ++y)
	{
		Uint8 pixelColor = _damage->getPixel(x, y);
		if (pixelColor >= _colors[CRAFT_MIN] && pixelColor < _colors[CRAFT_MAX])
		{
			++_craftHeight;
		}
	}
 
	drawCraftDamage();
 
	// Used for weapon toggling.
	_weapon1->onMouseClick((ActionHandler)&DogfightState::weapon1Click);
	_weapon2->onMouseClick((ActionHandler)&DogfightState::weapon2Click);
}
 
/**
 * Cleans up the dogfight state.
 */
DogfightState::~DogfightState()
{
	delete _craftDamageAnimTimer;
	while (!_projectiles.empty())
	{
		delete _projectiles.back();
		_projectiles.pop_back();
	}
}
 
/**
 * Runs the higher level dogfight functionality.
 */
void DogfightState::think()
{
	if (!_endDogfight)
	{
		update();
		_craftDamageAnimTimer->think(this, 0);
	}
	if (!_craft->isInDogfight() || _craft->getDestination() != _ufo || _ufo->getStatus() == Ufo::LANDED)
	{
		endDogfight();
	}
}
 
/**
 * Animates interceptor damage by changing the color and redrawing the image.
 */
void DogfightState::animateCraftDamage()
{
	if (_minimized)
	{
		return;
	}
	--_currentCraftDamageColor;
	if (_currentCraftDamageColor < _colors[DAMAGE_MIN])
	{
		_currentCraftDamageColor = _colors[DAMAGE_MAX];
	}
	drawCraftDamage();
}
 
/**
 * Draws interceptor damage according to percentage of HP's left.
 */
void DogfightState::drawCraftDamage()
{
	if (_craft->getDamagePercentage() != 0)
	{
		if (!_craftDamageAnimTimer->isRunning())
		{
			_craftDamageAnimTimer->start();
			if (_currentCraftDamageColor < _colors[DAMAGE_MIN])
			{
				_currentCraftDamageColor = _colors[DAMAGE_MIN];
			}
		}
		int damagePercentage = _craft->getDamagePercentage();
		int rowsToColor = (int)floor((double)_craftHeight * (double)(damagePercentage / 100.));
		if (rowsToColor == 0)
		{
			return;
		}
		int rowsColored = 0;
		bool rowColored = false;
		for (int y = 0; y < _damage->getHeight(); ++y)
		{
			rowColored = false;
			for (int x = 0; x < _damage->getWidth(); ++x)
			{
				int pixelColor = _damage->getPixel(x, y);
				if (pixelColor >= _colors[DAMAGE_MIN] && pixelColor <= _colors[DAMAGE_MAX])
				{
					_damage->setPixel(x, y, _currentCraftDamageColor);
					rowColored = true;
				}
				if (pixelColor >= _colors[CRAFT_MIN] && pixelColor < _colors[CRAFT_MAX])
				{
					_damage->setPixel(x, y, _currentCraftDamageColor);
					rowColored = true;
				}
			}
			if (rowColored)
			{
				++rowsColored;
			}
			if (rowsColored == rowsToColor)
			{
				break;
			}
		}
	}
}
 
/**
 * Animates the window with a palette effect.
 */
void DogfightState::animate()
{
	// Animate radar waves and other stuff.
	for (int x = 0; x < _window->getWidth(); ++x)
	{
		for (int y = 0; y < _window->getHeight(); ++y)
		{
			Uint8 radarPixelColor = _window->getPixel(x, y);
			if (radarPixelColor >= _colors[RADAR_MIN] && radarPixelColor < _colors[RADAR_MAX])
			{
				++radarPixelColor;
				if (radarPixelColor >= _colors[RADAR_MAX])
				{
					radarPixelColor = _colors[RADAR_MIN];
				}
				_window->setPixel(x, y, radarPixelColor);
			}
		}
	}
 
	_battle->clear();
 
	// Draw UFO.
	if (!_ufo->isDestroyed())
	{
		drawUfo();
	}
 
	// Draw projectiles.
	for (std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
	{
		drawProjectile((*it));
	}
 
	// Clears text after a while
	if (_timeout == 0)
	{
		_txtStatus->setText("");
	}
	else
	{
		_timeout--;
	}
 
	// Animate UFO hit.
	bool lastHitAnimFrame = false;
	if (_animatingHit && _ufo->getHitFrame() > 0)
	{
		_ufo->setHitFrame(_ufo->getHitFrame() - 1);
		if (_ufo->getHitFrame() == 0)
		{
			_animatingHit = false;
			lastHitAnimFrame = true;
		}
	}
 
	// Animate UFO crash landing.
	if (_ufo->isCrashed() && _ufo->getHitFrame() == 0 && !lastHitAnimFrame)
	{
		--_ufoSize;
	}
}
 
/**
 * Updates all the elements in the dogfight, including ufo movement,
 * weapons fire, projectile movement, ufo escape conditions,
 * craft and ufo destruction conditions, and retaliation mission generation, as applicable.
 */
void DogfightState::update()
{
	bool finalRun = false;
	// Check if craft is not low on fuel when window minimized, and
	// Check if crafts destination hasn't been changed when window minimized.
	Ufo* u = dynamic_cast<Ufo*>(_craft->getDestination());
	if (u != _ufo || !_craft->isInDogfight() || _craft->getLowFuel() || (_minimized && _ufo->isCrashed()))
	{
		endDogfight();
		return;
	}
 
	if (!_minimized)
	{
		animate();
		if (!_ufo->isCrashed() && !_ufo->isDestroyed() && !_craft->isDestroyed() && !_ufo->getInterceptionProcessed())
		{
			_ufo->setInterceptionProcessed(true);
			int escapeCounter = _ufo->getEscapeCountdown();
 
			if (escapeCounter > 0)
			{
				escapeCounter--;
				_ufo->setEscapeCountdown(escapeCounter);
				// Check if UFO is breaking off.
				if (escapeCounter == 0)
				{
					_ufo->setSpeed(_ufo->getRules()->getMaxSpeed());
				}
			}
			if (_ufo->getFireCountdown() > 0)
			{
				_ufo->setFireCountdown(_ufo->getFireCountdown() - 1);
			}
		}
	}
	// Crappy craft is chasing UFO.
	if (_ufo->getSpeed() > _craft->getRules()->getMaxSpeed())
	{
		_ufoBreakingOff = true;
		finalRun = true;
		setStatus("STR_UFO_OUTRUNNING_INTERCEPTOR");
	}
	else
	{
		_ufoBreakingOff = false;
	}
 
	bool projectileInFlight = false;
	if (!_minimized)
	{
		int distanceChange = 0;
 
		// Update distance
		if (!_ufoBreakingOff)
		{
			if (_currentDist < _targetDist && !_ufo->isCrashed() && !_craft->isDestroyed())
			{
				distanceChange = 4;
				if (_currentDist + distanceChange >_targetDist)
				{
					distanceChange = _targetDist - _currentDist;
				}
			}
			else if (_currentDist > _targetDist && !_ufo->isCrashed() && !_craft->isDestroyed())
			{
				distanceChange = -2;
			}
 
			// don't let the interceptor mystically push or pull its fired projectiles
			for (std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
			{
				if ((*it)->getGlobalType() != CWPGT_BEAM && (*it)->getDirection() == D_UP) (*it)->setPosition((*it)->getPosition() + distanceChange);
			}
		}
		else
		{
			distanceChange = 4;
 
			// UFOs can try to outrun our missiles, don't adjust projectile positions here
			// If UFOs ever fire anything but beams, those positions need to be adjust here though.
		}
 
		_currentDist += distanceChange;
 
		std::ostringstream ss;
		ss << _currentDist;
		_txtDistance->setText(ss.str());
 
		// Move projectiles and check for hits.
		for (std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end(); ++it)
		{
			CraftWeaponProjectile *p = (*it);
			p->move();
			// Projectiles fired by interceptor.
			if (p->getDirection() == D_UP)
			{
				// Projectile reached the UFO - determine if it's been hit.
				if (((p->getPosition() >= _currentDist) || (p->getGlobalType() == CWPGT_BEAM && p->toBeRemoved())) && !_ufo->isCrashed() && !p->getMissed())
				{
					// UFO hit.
					if (RNG::percent((p->getAccuracy() * (100 + 300 / (5 - _ufoSize)) + 100) / 200))
					{
						// Formula delivered by Volutar
						int damage = RNG::generate(p->getDamage() / 2, p->getDamage());
						_ufo->setDamage(_ufo->getDamage() + damage);
						if (_ufo->isCrashed())
						{
							_ufo->setShotDownByCraftId(_craft->getUniqueId());
							_ufo->setSpeed(0);
							_ufo->setDestination(0);
							// if the ufo got destroyed here, these no longer apply
							_ufoBreakingOff = false;
							finalRun = false;
							_end = false;
						}
						if (_ufo->getHitFrame() == 0)
						{
							_animatingHit = true;
							_ufo->setHitFrame(3);
						}
 
						setStatus("STR_UFO_HIT");
						_game->getMod()->getSound("GEO.CAT", Mod::UFO_HIT)->play();
						p->remove();
					}
					// Missed.
					else
					{
						if (p->getGlobalType() == CWPGT_BEAM)
						{
							p->remove();
						}
						else
						{
							p->setMissed(true);
						}
					}
				}
				// Check if projectile passed it's maximum range.
				if (p->getGlobalType() == CWPGT_MISSILE && p->getPosition() / 8 >= p->getRange())
				{
					p->remove();
				}
				else if (!_ufo->isCrashed())
				{
					projectileInFlight = true;
				}
			}
			// Projectiles fired by UFO.
			else if (p->getDirection() == D_DOWN)
			{
				if (p->getGlobalType() == CWPGT_MISSILE || (p->getGlobalType() == CWPGT_BEAM && p->toBeRemoved()))
				{
					if (RNG::percent(p->getAccuracy()))
					{
						// Formula delivered by Volutar
						int damage = RNG::generate(0, _ufo->getRules()->getWeaponPower());
						if (damage)
						{
							_craft->setDamage(_craft->getDamage() + damage);
							drawCraftDamage();
							setStatus("STR_INTERCEPTOR_DAMAGED");
							_game->getMod()->getSound("GEO.CAT", Mod::INTERCEPTOR_HIT)->play(); //10
							if (_mode == _btnCautious && _craft->getDamagePercentage() >= 50)
							{
								_targetDist = STANDOFF_DIST;
							}
						}
					}
					p->remove();
				}
			}
		}
 
		// Remove projectiles that hit or missed their target.
		for (std::vector<CraftWeaponProjectile*>::iterator it = _projectiles.begin(); it != _projectiles.end();)
		{
			if ((*it)->toBeRemoved() == true || ((*it)->getMissed() == true && (*it)->getPosition() <= 0))
			{
				delete *it;
				it = _projectiles.erase(it);
			}
			else
			{
				++it;
			}
		}
 
		// Handle weapons and craft distance.
		for (unsigned int i = 0; i < _craft->getRules()->getWeapons(); ++i)
		{
			CraftWeapon *w = _craft->getWeapons()->at(i);
			if (w == 0)
			{
				continue;
			}
			int wTimer;
			if (i == 0)
			{
				wTimer = _w1FireCountdown;
			}
			else
			{
				wTimer = _w2FireCountdown;
			}
 
			// Handle weapon firing
			if (wTimer == 0 && _currentDist <= w->getRules()->getRange() * 8 && w->getAmmo() > 0 && _mode != _btnStandoff
				&& _mode != _btnDisengage && !_ufo->isCrashed() && !_craft->isDestroyed())
			{
				if (i == 0)
				{
					if (_weapon1Enabled)
					{
						fireWeapon1();
						projectileInFlight = true;
					}
				}
				else
				{
					if (_weapon2Enabled)
					{
						fireWeapon2();
						projectileInFlight = true;
					}
				}
			}
			else if (wTimer > 0)
			{
				if (i == 0)
				{
					_w1FireCountdown--;
				}
				else
				{
					_w2FireCountdown--;
				}
			}
 
			if (w->getAmmo() == 0 && !projectileInFlight && !_craft->isDestroyed())
			{
				// Handle craft distance according to option set by user and available ammo.
				if (_mode == _btnCautious)
				{
					minimumDistance();
				}
				else if (_mode == _btnStandard)
				{
					maximumDistance();
				}
			}
		}
 
		// Handle UFO firing.
		if (_currentDist <= _ufo->getRules()->getWeaponRange() * 8 && !_ufo->isCrashed() && !_craft->isDestroyed())
		{
			if (_ufo->getShootingAt() == 0)
			{
				_ufo->setShootingAt(_interceptionNumber);
			}
			if (_ufo->getShootingAt() == _interceptionNumber)
			{
				if (_ufo->getFireCountdown() == 0)
				{
					ufoFireWeapon();
				}
			}
		}
		else if (_ufo->getShootingAt() == _interceptionNumber)
		{
			_ufo->setShootingAt(0);
		}
	}
 
	// Check when battle is over.
	if (_end == true && (((_currentDist > 640 || _minimized) && (_mode == _btnDisengage || _ufoBreakingOff == true)) || (_timeout == 0 && (_ufo->isCrashed() || _craft->isDestroyed()))))
	{
		if (_ufoBreakingOff)
		{
			_ufo->move();
			_craft->setDestination(_ufo);
		}
		if (!_destroyCraft && (_destroyUfo || _mode == _btnDisengage))
		{
			_craft->returnToBase();
		}
		if (_ufo->isCrashed())
		{
			std::vector<Craft*> followers = _ufo->getCraftFollowers();
			for (std::vector<Craft*>::iterator i = followers.begin(); i != followers.end(); ++i)
			{
				if ((*i)->getNumSoldiers() == 0 && (*i)->getNumVehicles() == 0)
				{
					(*i)->returnToBase();
				}
			}
		}
		endDogfight();
	}
 
	if (_currentDist > 640 && _ufoBreakingOff)
	{
		finalRun = true;
	}
 
	// End dogfight if craft is destroyed.
	if (!_end)
	{
		if (_craft->isDestroyed())
		{
			setStatus("STR_INTERCEPTOR_DESTROYED");
			_timeout += 30;
			_game->getMod()->getSound("GEO.CAT", Mod::INTERCEPTOR_EXPLODE)->play();
			finalRun = true;
			_destroyCraft = true;
			_ufo->setShootingAt(0);
		}
 
		// End dogfight if UFO is crashed or destroyed.
		if (_ufo->isCrashed())
		{
			AlienMission *mission = _ufo->getMission();
			mission->ufoShotDown(*_ufo);
			// Check for retaliation trigger.
			int retaliationOdds = mission->getRules().getRetaliationOdds();
			if (retaliationOdds == -1)
			{
				retaliationOdds = 100 - (4 * (24 - _game->getSavedGame()->getDifficultyCoefficient()));
			}
 
			if (RNG::percent(retaliationOdds))
			{
				// Spawn retaliation mission.
				std::string targetRegion;
				if (RNG::percent(50 - 6 * _game->getSavedGame()->getDifficultyCoefficient()))
				{
					// Attack on UFO's mission region
					targetRegion = _ufo->getMission()->getRegion();
				}
				else
				{
					// Try to find and attack the originating base.
					targetRegion = _game->getSavedGame()->locateRegion(*_craft->getBase())->getRules()->getType();
					// TODO: If the base is removed, the mission is canceled.
				}
				// Difference from original: No retaliation until final UFO lands (Original: Is spawned).
				if (!_game->getSavedGame()->findAlienMission(targetRegion, OBJECTIVE_RETALIATION))
				{
					const RuleAlienMission &rule = *_game->getMod()->getRandomMission(OBJECTIVE_RETALIATION, _game->getSavedGame()->getMonthsPassed());
					AlienMission *newMission = new AlienMission(rule);
					newMission->setId(_game->getSavedGame()->getId("ALIEN_MISSIONS"));
					newMission->setRegion(targetRegion, *_game->getMod());
					newMission->setRace(_ufo->getAlienRace());
					newMission->start(newMission->getRules().getWave(0).spawnTimer); // fixed delay for first scout
					_game->getSavedGame()->getAlienMissions().push_back(newMission);
				}
			}
 
			if (_ufo->isDestroyed())
			{
				if (_ufo->getShotDownByCraftId() == _craft->getUniqueId())
				{
					for (std::vector<Country*>::iterator country = _game->getSavedGame()->getCountries()->begin(); country != _game->getSavedGame()->getCountries()->end(); ++country)
					{
						if ((*country)->getRules()->insideCountry(_ufo->getLongitude(), _ufo->getLatitude()))
						{
							(*country)->addActivityXcom(_ufo->getRules()->getScore()*2);
							break;
						}
					}
					for (std::vector<Region*>::iterator region = _game->getSavedGame()->getRegions()->begin(); region != _game->getSavedGame()->getRegions()->end(); ++region)
					{
						if ((*region)->getRules()->insideRegion(_ufo->getLongitude(), _ufo->getLatitude()))
						{
							(*region)->addActivityXcom(_ufo->getRules()->getScore()*2);
							break;
						}
					}
					setStatus("STR_UFO_DESTROYED");
					_game->getMod()->getSound("GEO.CAT", Mod::UFO_EXPLODE)->play(); //11
				}
				_destroyUfo = true;
			}
			else
			{
				if (_ufo->getShotDownByCraftId() == _craft->getUniqueId())
				{
					setStatus("STR_UFO_CRASH_LANDS");
					_game->getMod()->getSound("GEO.CAT", Mod::UFO_CRASH)->play(); //10
					for (std::vector<Country*>::iterator country = _game->getSavedGame()->getCountries()->begin(); country != _game->getSavedGame()->getCountries()->end(); ++country)
					{
						if ((*country)->getRules()->insideCountry(_ufo->getLongitude(), _ufo->getLatitude()))
						{
							(*country)->addActivityXcom(_ufo->getRules()->getScore());
							break;
						}
					}
					for (std::vector<Region*>::iterator region = _game->getSavedGame()->getRegions()->begin(); region != _game->getSavedGame()->getRegions()->end(); ++region)
					{
						if ((*region)->getRules()->insideRegion(_ufo->getLongitude(), _ufo->getLatitude()))
						{
							(*region)->addActivityXcom(_ufo->getRules()->getScore());
							break;
						}
					}
				}
				if (!_state->getGlobe()->insideLand(_ufo->getLongitude(), _ufo->getLatitude()))
				{
					_ufo->setStatus(Ufo::DESTROYED);
					_destroyUfo = true;
				}
				else
				{
					_ufo->setSecondsRemaining(RNG::generate(24, 96)*3600);
					_ufo->setAltitude("STR_GROUND");
					if (_ufo->getCrashId() == 0)
					{
						_ufo->setCrashId(_game->getSavedGame()->getId("STR_CRASH_SITE"));
					}
				}
			}
			_timeout += 30;
			if (_ufo->getShotDownByCraftId() != _craft->getUniqueId())
			{
				_timeout += 50;
				_ufo->setHitFrame(3);
			}
			finalRun = true;
 
			if (_ufo->getStatus() == Ufo::LANDED)
			{
				_timeout += 30;
				finalRun = true;
				_ufo->setShootingAt(0);
			}
		}
	}
 
	if (!projectileInFlight && finalRun)
	{
		_end = true;
	}
}
 
/**
 * Fires a shot from the first weapon
 * equipped on the craft.
 */
void DogfightState::fireWeapon1()
{
	CraftWeapon *w1 = _craft->getWeapons()->at(0);
	if (w1->setAmmo(w1->getAmmo() - 1))
	{
		_w1FireCountdown = _w1FireInterval;
 
		std::ostringstream ss;
		ss << w1->getAmmo();
		_txtAmmo1->setText(ss.str());
 
		CraftWeaponProjectile *p = w1->fire();
		p->setDirection(D_UP);
		p->setHorizontalPosition(HP_LEFT);
		_projectiles.push_back(p);
 
		_game->getMod()->getSound("GEO.CAT", w1->getRules()->getSound())->play();
	}
}
 
/**
 * Fires a shot from the second weapon
 * equipped on the craft.
 */
void DogfightState::fireWeapon2()
{
	CraftWeapon *w2 = _craft->getWeapons()->at(1);
	if (w2->setAmmo(w2->getAmmo() - 1))
	{
		_w2FireCountdown = _w2FireInterval;
 
		std::ostringstream ss;
		ss << w2->getAmmo();
		_txtAmmo2->setText(ss.str());
 
		CraftWeaponProjectile *p = w2->fire();
		p->setDirection(D_UP);
		p->setHorizontalPosition(HP_RIGHT);
		_projectiles.push_back(p);
 
		_game->getMod()->getSound("GEO.CAT", w2->getRules()->getSound())->play();
	}
}
 
/**
 *	Each time a UFO will try to fire it's cannons
 *	a calculation is made. There's only 10% chance
 *	that it will actually fire.
 */
void DogfightState::ufoFireWeapon()
{
	int fireCountdown = std::max(1, (_ufo->getRules()->getWeaponReload() - 2 * _game->getSavedGame()->getDifficultyCoefficient()));
	_ufo->setFireCountdown(RNG::generate(0, fireCountdown) + fireCountdown);
 
	setStatus("STR_UFO_RETURN_FIRE");
	CraftWeaponProjectile *p = new CraftWeaponProjectile();
	p->setType(CWPT_PLASMA_BEAM);
	p->setAccuracy(60);
	p->setDamage(_ufo->getRules()->getWeaponPower());
	p->setDirection(D_DOWN);
	p->setHorizontalPosition(HP_CENTER);
	p->setPosition(_currentDist - (_ufo->getRules()->getRadius() / 2));
	_projectiles.push_back(p);
	_game->getMod()->getSound("GEO.CAT", Mod::UFO_FIRE)->play();
}
 
/**
 * Sets the craft to the minimum distance
 * required to fire a weapon.
 */
void DogfightState::minimumDistance()
{
	int max = 0;
	for (std::vector<CraftWeapon*>::iterator i = _craft->getWeapons()->begin(); i < _craft->getWeapons()->end(); ++i)
	{
		if (*i == 0)
			continue;
		if ((*i)->getRules()->getRange() > max && (*i)->getAmmo() > 0)
		{
			max = (*i)->getRules()->getRange();
		}
	}
	if (max == 0)
	{
		_targetDist = STANDOFF_DIST;
	}
	else
	{
		_targetDist = max * 8;
	}
}
 
/**
 * Sets the craft to the maximum distance
 * required to fire a weapon.
 */
void DogfightState::maximumDistance()
{
	int min = 1000;
	for (std::vector<CraftWeapon*>::iterator i = _craft->getWeapons()->begin(); i < _craft->getWeapons()->end(); ++i)
	{
		if (*i == 0)
			continue;
		if ((*i)->getRules()->getRange() < min && (*i)->getAmmo() > 0)
		{
			min = (*i)->getRules()->getRange();
		}
	}
	if (min == 1000)
	{
		_targetDist = STANDOFF_DIST;
	}
	else
	{
		_targetDist = min * 8;
	}
}
 
/**
 * Updates the status text and restarts
 * the text timeout counter.
 * @param status New status text.
 */
void DogfightState::setStatus(const std::string &status)
{
	_txtStatus->setText(tr(status));
	_timeout = 50;
}
 
/**
 * Minimizes the dogfight window.
 * @param action Pointer to an action.
 */
void DogfightState::btnMinimizeClick(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		if (_currentDist >= STANDOFF_DIST)
		{
			setMinimized(true);
		}
		else
		{
			setStatus("STR_MINIMISE_AT_STANDOFF_RANGE_ONLY");
		}
	}
}
 
/**
 * Switches to Standoff mode (maximum range).
 * @param action Pointer to an action.
 */
void DogfightState::btnStandoffPress(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		_end = false;
		setStatus("STR_STANDOFF");
		_targetDist = STANDOFF_DIST;
	}
}
 
/**
 * Switches to Cautious mode (maximum weapon range).
 * @param action Pointer to an action.
 */
void DogfightState::btnCautiousPress(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		_end = false;
		setStatus("STR_CAUTIOUS_ATTACK");
		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
		{
			_w1FireInterval = _craft->getWeapons()->at(0)->getRules()->getCautiousReload();
		}
		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
		{
			_w2FireInterval = _craft->getWeapons()->at(1)->getRules()->getCautiousReload();
		}
		minimumDistance();
	}
}
 
/**
 * Switches to Standard mode (minimum weapon range).
 * @param action Pointer to an action.
 */
void DogfightState::btnStandardPress(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		_end = false;
		setStatus("STR_STANDARD_ATTACK");
		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
		{
			_w1FireInterval = _craft->getWeapons()->at(0)->getRules()->getStandardReload();
		}
		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
		{
			_w2FireInterval = _craft->getWeapons()->at(1)->getRules()->getStandardReload();
		}
		maximumDistance();
	}
}
 
/**
 * Switches to Aggressive mode (minimum range).
 * @param action Pointer to an action.
 */
void DogfightState::btnAggressivePress(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		_end = false;
		setStatus("STR_AGGRESSIVE_ATTACK");
		if (_craft->getRules()->getWeapons() > 0 && _craft->getWeapons()->at(0) != 0)
		{
			_w1FireInterval = _craft->getWeapons()->at(0)->getRules()->getAggressiveReload();
		}
		if (_craft->getRules()->getWeapons() > 1 && _craft->getWeapons()->at(1) != 0)
		{
			_w2FireInterval = _craft->getWeapons()->at(1)->getRules()->getAggressiveReload();
		}
		_targetDist = 64;
	}
}
 
/**
 * Disengages from the UFO.
 * @param action Pointer to an action.
 */
void DogfightState::btnDisengagePress(Action *)
{
	if (!_ufo->isCrashed() && !_craft->isDestroyed() && !_ufoBreakingOff)
	{
		_end = true;
		setStatus("STR_DISENGAGING");
		_targetDist = 800;
	}
}
 
/**
 * Shows a front view of the UFO.
 * @param action Pointer to an action.
 */
void DogfightState::btnUfoClick(Action *)
{
	_preview->setVisible(true);
	// Disable all other buttons to prevent misclicks
	_btnStandoff->setVisible(false);
	_btnCautious->setVisible(false);
	_btnStandard->setVisible(false);
	_btnAggressive->setVisible(false);
	_btnDisengage->setVisible(false);
	_btnUfo->setVisible(false);
	_btnMinimize->setVisible(false);
	_weapon1->setVisible(false);
	_weapon2->setVisible(false);
}
 
/**
 * Hides the front view of the UFO.
 * @param action Pointer to an action.
 */
void DogfightState::previewClick(Action *)
{
	_preview->setVisible(false);
	// Reenable all other buttons to prevent misclicks
	_btnStandoff->setVisible(true);
	_btnCautious->setVisible(true);
	_btnStandard->setVisible(true);
	_btnAggressive->setVisible(true);
	_btnDisengage->setVisible(true);
	_btnUfo->setVisible(true);
	_btnMinimize->setVisible(true);
	_weapon1->setVisible(true);
	_weapon2->setVisible(true);
}
 
/*
 * Draws the UFO blob on the radar screen.
 * Currently works only for original sized blobs
 * 13 x 13 pixels.
 */
void DogfightState::drawUfo()
{
	if (_ufoSize < 0 || _ufo->isDestroyed())
	{
		return;
	}
	int currentUfoXposition =  _battle->getWidth() / 2 - 6;
	int currentUfoYposition = _battle->getHeight() - (_currentDist / 8) - 6;
	for (int y = 0; y < 13; ++y)
	{
		for (int x = 0; x < 13; ++x)
		{
			Uint8 pixelOffset = _ufoBlobs[_ufoSize + _ufo->getHitFrame()][y][x];
			if (pixelOffset == 0)
			{
				continue;
			}
			else
			{
				if (_ufo->isCrashed() || _ufo->getHitFrame() > 0)
				{
					pixelOffset *= 2;
				}
				Uint8 radarPixelColor = _window->getPixel(currentUfoXposition + x + 3, currentUfoYposition + y + 3); // + 3 cause of the window frame
				Uint8 color = radarPixelColor - pixelOffset;
				if (color < _colors[BLOB_MIN])
				{
					color = _colors[BLOB_MIN];
				}
				_battle->setPixel(currentUfoXposition + x, currentUfoYposition + y, color);
			}
		}
	}
}
 
/*
 * Draws projectiles on the radar screen.
 * Depending on what type of projectile it is, it's
 * shape will be different. Currently works for
 * original sized blobs 3 x 6 pixels.
 */
void DogfightState::drawProjectile(const CraftWeaponProjectile* p)
{
	int xPos = _battle->getWidth() / 2 + p->getHorizontalPosition();
	// Draw missiles.
	if (p->getGlobalType() == CWPGT_MISSILE)
	{
		xPos -= 1;
		int yPos = _battle->getHeight() - p->getPosition() / 8;
		for (int x = 0; x < 3; ++x)
		{
			for (int y = 0; y < 6; ++y)
			{
				int pixelOffset = _projectileBlobs[p->getType()][y][x];
				if (pixelOffset == 0)
				{
					continue;
				}
				else
				{
					Uint8 radarPixelColor = _window->getPixel(xPos + x + 3, yPos + y + 3); // + 3 cause of the window frame
					Uint8 color = radarPixelColor - pixelOffset;
					if (color < _colors[BLOB_MIN])
					{
						color = _colors[BLOB_MIN];
					}
					_battle->setPixel(xPos + x, yPos + y, color);
				}
			}
		}
	}
	// Draw beams.
	else if (p->getGlobalType() == CWPGT_BEAM)
	{
		int yStart = _battle->getHeight() - 2;
		int yEnd = _battle->getHeight() - (_currentDist / 8);
		Uint8 pixelOffset = p->getState();
		for (int y = yStart; y > yEnd; --y)
		{
			Uint8 radarPixelColor = _window->getPixel(xPos + 3, y + 3);
			Uint8 color = radarPixelColor - pixelOffset;
			if (color < _colors[BLOB_MIN])
			{
				color = _colors[BLOB_MIN];
			}
			_battle->setPixel(xPos, y, color);
		}
	}
}
 
/**
 * Toggles usage of weapon number 1.
 * @param action Pointer to an action.
 */
void DogfightState::weapon1Click(Action *)
{
	_weapon1Enabled = !_weapon1Enabled;
	recolor(0, _weapon1Enabled);
}
 
/**
 * Toggles usage of weapon number 2.
 * @param action Pointer to an action.
 */
void DogfightState::weapon2Click(Action *)
{
	_weapon2Enabled = !_weapon2Enabled;
	recolor(1, _weapon2Enabled);
}
 
/**
 * Changes colors of weapon icons, range indicators and ammo texts base on current weapon state.
 * @param weaponNo - number of weapon for which colors must be changed.
 * @param currentState - state of weapon (enabled = true, disabled = false).
 */
void DogfightState::recolor(const int weaponNo, const bool currentState)
{
	InteractiveSurface *weapon = 0;
	Text *ammo = 0;
	Surface *range = 0;
	if (weaponNo == 0)
	{
		weapon = _weapon1;
		ammo = _txtAmmo1;
		range = _range1;
	}
	else if (weaponNo == 1)
	{
		weapon = _weapon2;
		ammo = _txtAmmo2;
		range = _range2;
	}
	else
	{
		return;
	}
 
	if (currentState)
	{
		weapon->offset(-_colors[DISABLED_WEAPON]);
		ammo->offset(-_colors[DISABLED_AMMO]);
		range->offset(-_colors[DISABLED_RANGE]);
	}
	else
	{
		weapon->offset(_colors[DISABLED_WEAPON]);
		ammo->offset(_colors[DISABLED_AMMO]);
		range->offset(_colors[DISABLED_RANGE]);
	}
}
 
/**
 * Returns true if state is minimized. Otherwise returns false.
 * @return Is the dogfight minimized?
 */
bool DogfightState::isMinimized() const
{
	return _minimized;
}
 
/**
 * Sets the state to minimized/maximized status.
 * @param minimized Is the dogfight minimized?
 */
void DogfightState::setMinimized(const bool minimized)
{
	// set these to the same as the incoming minimized state
	_minimized = minimized;
	_btnMinimizedIcon->setVisible(minimized);
	_txtInterceptionNumber->setVisible(minimized);
 
	// set these to the opposite of the incoming minimized state
	_window->setVisible(!minimized);
	_btnStandoff->setVisible(!minimized);
	_btnCautious->setVisible(!minimized);
	_btnStandard->setVisible(!minimized);
	_btnAggressive->setVisible(!minimized);
	_btnDisengage->setVisible(!minimized);
	_btnUfo->setVisible(!minimized);
	_btnMinimize->setVisible(!minimized);
	_battle->setVisible(!minimized);
	_weapon1->setVisible(!minimized);
	_range1->setVisible(!minimized);
	_weapon2->setVisible(!minimized);
	_range2->setVisible(!minimized);
	_damage->setVisible(!minimized);
	_txtAmmo1->setVisible(!minimized);
	_txtAmmo2->setVisible(!minimized);
	_txtDistance->setVisible(!minimized);
	_txtStatus->setVisible(!minimized);
 
	// set to false regardless
	_preview->setVisible(false);
}
 
/**
 * Maximizes the interception window.
 * @param action Pointer to an action.
 */
void DogfightState::btnMinimizedIconClick(Action *)
{
	if (_craft->getRules()->isWaterOnly() && _ufo->getAltitudeInt() > _craft->getRules()->getMaxAltitude())
	{
		_state->popup(new DogfightErrorState(_craft, tr("STR_UNABLE_TO_ENGAGE_DEPTH")));
		setWaitForAltitude(true);
	}
	else if (_craft->getRules()->isWaterOnly() && !_state->getGlobe()->insideLand(_craft->getLongitude(), _craft->getLatitude()))
	{
		_state->popup(new DogfightErrorState(_craft, tr("STR_UNABLE_TO_ENGAGE_AIRBORNE")));
		setWaitForPoly(true);
	}
	else
	{
		setMinimized(false);
	}
}
 
/**
 * Sets interception number. Used to draw proper number when window minimized.
 * @param number ID number.
 */
void DogfightState::setInterceptionNumber(const int number)
{
	_interceptionNumber = number;
}
 
/**
 * Sets interceptions count. Used to properly position the window.
 * @param count Amount of interception windows.
 */
void DogfightState::setInterceptionsCount(const size_t count)
{
	_interceptionsCount = count;
	calculateWindowPosition();
	moveWindow();
}
 
/**
 * Calculates dogfight window position according to
 * number of active interceptions.
 */
void DogfightState::calculateWindowPosition()
{
 
	_minimizedIconX = 5;
	_minimizedIconY = (5 * _interceptionNumber) + (16 * (_interceptionNumber - 1));
 
	if (_interceptionsCount == 1)
	{
		_x = 80;
		_y = 52;
	}
	else if (_interceptionsCount == 2)
	{
		if (_interceptionNumber == 1)
		{
			_x = 80;
			_y = 0;
		}
		else // 2
		{
			_x = 80;
			//_y = (_game->getScreen()->getHeight() / 2) - 96;
			_y = 200 - _window->getHeight();//96;
		}
	}
	else if (_interceptionsCount == 3)
	{
		if (_interceptionNumber == 1)
		{
			_x = 80;
			_y = 0;
		}
		else if (_interceptionNumber == 2)
		{
			_x = 0;
			//_y = (_game->getScreen()->getHeight() / 2) - 96;
			_y = 200 - _window->getHeight();//96;
		}
		else // 3
		{
			//_x = (_game->getScreen()->getWidth() / 2) - 160;
			//_y = (_game->getScreen()->getHeight() / 2) - 96;
			_x = 320 - _window->getWidth();//160;
			_y = 200 - _window->getHeight();//96;
		}
	}
	else
	{
		if (_interceptionNumber == 1)
		{
			_x = 0;
			_y = 0;
		}
		else if (_interceptionNumber == 2)
		{
			//_x = (_game->getScreen()->getWidth() / 2) - 160;
			_x = 320 - _window->getWidth();//160;
			_y = 0;
		}
		else if (_interceptionNumber == 3)
		{
			_x = 0;
			//_y = (_game->getScreen()->getHeight() / 2) - 96;
			_y = 200 - _window->getHeight();//96;
		}
		else // 4
		{
			//_x = (_game->getScreen()->getWidth() / 2) - 160;
			//_y = (_game->getScreen()->getHeight() / 2) - 96;
			_x = 320 - _window->getWidth();//160;
			_y = 200 - _window->getHeight();//96;
		}
	}
	_x += _game->getScreen()->getDX();
	_y += _game->getScreen()->getDY();
}
 
/**
 * Relocates all dogfight window elements to
 * calculated position. This is used when multiple
 * interceptions are running.
 */
void DogfightState::moveWindow()
{
	int x = _window->getX() - _x;
	int y = _window->getY() - _y;
	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
	{
		(*i)->setX((*i)->getX() - x);
		(*i)->setY((*i)->getY() - y);
	}
	_btnMinimizedIcon->setX(_minimizedIconX); _btnMinimizedIcon->setY(_minimizedIconY);
	_txtInterceptionNumber->setX(_minimizedIconX + 18); _txtInterceptionNumber->setY(_minimizedIconY + 6);
}
 
/**
 * Checks whether the dogfight should end.
 * @return Returns true if the dogfight should end, otherwise returns false.
 */
bool DogfightState::dogfightEnded() const
{
	return _endDogfight;
}
 
/**
 * Returns the UFO associated to this dogfight.
 * @return Returns pointer to UFO object associated to this dogfight.
 */
Ufo *DogfightState::getUfo() const
{
	return _ufo;
}
 
/**
 * Returns the craft associated to this dogfight.
 * @return Returns pointer to craft object associated to this dogfight.
 */
Craft *DogfightState::getCraft() const
{
	return _craft;
}
 
/**
 * Ends the dogfight.
 */
void DogfightState::endDogfight()
{
	if (_endDogfight)
		return;
	if (_craft)
	{
		_craft->setInDogfight(false);
		_craft->setInterceptionOrder(0);
	}
	// set the ufo as "free" for the next engagement (as applicable)
	if (_ufo)
		_ufo->setInterceptionProcessed(false);
	_endDogfight = true;
}
 
/**
 * Returns interception number.
 * @return interception number
 */
int DogfightState::getInterceptionNumber() const
{
	return _interceptionNumber;
}
 
void DogfightState::setWaitForPoly(bool wait)
{
	_waitForPoly = wait;
}
 
bool DogfightState::getWaitForPoly() const
{
	return _waitForPoly;
}
 
void DogfightState::setWaitForAltitude(bool wait)
{
	_waitForAltitude = wait;
}
 
bool DogfightState::getWaitForAltitude() const
{
	return _waitForAltitude;
}
 
}

V807 Decreased performance. Consider creating a pointer to avoid using the same expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getWeapons()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_game->getSavedGame()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_game->getMod()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the same expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the same expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getRules()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_ufo->getRules()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getWeapons()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getWeapons()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getWeapons()' expression repeatedly.

V807 Decreased performance. Consider creating a pointer to avoid using the '_craft->getRules()' expression repeatedly.