#include "grab.h"
#include <fstream>
#include "dwrite.h"
#include "FontLoader.h"
#include <stdlib.h>
#include <thread>
#include "audiere.h"
#include <limits.h>

#pragma comment (lib, "Winmm.lib")
#pragma comment (lib, "Dwrite.lib")
#pragma comment (lib, "audiere.lib")

struct GameUnit;
std::vector<GameUnit*> Units;
std::vector<GameUnit*> SelectedUnits;

struct UnitVoice {
	audiere::OutputStreamPtr Yes;
	audiere::OutputStreamPtr No;
	audiere::/*SoundEffectPtr*/OutputStreamPtr Fire;
	audiere::/*SoundEffectPtr*/OutputStreamPtr Rocket;
	audiere::/*SoundEffectPtr*/OutputStreamPtr Die;
	audiere::/*SoundEffectPtr*/OutputStreamPtr Hey;
	audiere::OutputStreamPtr Fix;
} GameVoice;

const wchar_t AboutString[] = {
	L"От Yoshi ||| Об игре\n\n"
	L"Создана специально для igdc.ru в рамках конкурса RPG III.\n"
	L"Вы, король томатов по имени Помитто, должны отдавать приказы своим подчиненным.Ваша цель - защитить себя от грозных и опасных огурцов, а также, по возможности уничтожить их короля. Доступно в игре 4 класса : врач, пулеметчик, солдат и строитель. Их можно выращивать на грядках (созревают всего за 5 секунд). У каждого юнита свои характеристики (от здоровья до стоимости). Кстати, валюта - это семена, они появляются из ниоткуда, нужно просто ждать. Ваши бойцы атакуют врага в области их видимости, а медики лечат всех союзников вокруг себя.\n"
	L"Удачи."
};

const wchar_t AboutString2[] = {
	L"Управление:\n"
	L"ЛКМ - выбрать юнита\n"
	L"CTRL+ЛКМ - выбрать юнитов или снять выделение с юнита\n"
	L"G - следовать в на указатель мыши\n"
	L"K - уничтожить вражеский объект\n"
	L"B - создать грядку томатов\n"
	L"C зажатым ctrl + G/K/B можно добавить задание в очередь (юнит возмется за него после выполнения предыдущих)\n"
	L"Z - Создать юнит на грядке под указателем мыши\n"
	L"Q/E - Изменить класс создаваемого юнита (Врач/Пулеметчик/Солдат/Строитель)"
};

const wchar_t* ClassNames[4] = {
	L"Лечебный помидор (Врач)",
	L"Наливной помидор (Пулеметчик)",
	L"Рядовой помидор (Солдат)",
	L"Поздний помидор (Строитель)"
};

const char ClassHealth[4] = { 15, 100, 50, 20 };
const float ClassVelocity[4] = { 0.100f, 0.030f, 0.060f, 0.085f };
const float ClassRange[4] = { 2.f, 2.5f, 4.5f, 1.f };
const unsigned int ClassReloadTime[4] = { 16, 20, 50, 40 };
const char ClassDamage[4] = { -1, 2, 10, -2 };
const char ClassCost[4] = { 4, 5, 5, 3 };

enum TimerReason {
	TIMER_CAMERA_UPDATE = 0x1, TIMER_AI_UPDATE = 0x2
};

enum GameTeam {  GameTeamTomato = 0, GameTeamCucumber = 1, GameTeamNone = 2 };
const GameTeam ReversedTeam[]{ GameTeamCucumber, GameTeamTomato, GameTeamNone };

enum GameClass { GameClassMedic, GameClassHeavy, GameClassSoldier, GameClassBuilder };

enum GameTeam;
enum GameTiles {
	GAME_TILE_GRASS, GAME_TILE_TOMATO_GARDEN, GAME_TILE_TOMATO_DUMMY, GAME_TILE_TOMATO_KING,
	GAME_TILE_RIVER, GAME_TILE_BRIDGE, GAME_TILE_TOMATO_WELL, GAME_TILE_CUCUMBER_DUMMY,
	GAME_TILE_CUCUMBER_KING, GAME_TILE_CONSTRUCTION, GAME_TILE_GOTO, GAME_TILE_CUCUMBER_GARDEN,
	GAME_TILE_LEFT_END_LAKE, GAME_TILE_RIGHT_END_LAKE, GAME_TILE_FENCE_START, GAME_TILE_FENCE,
	GAME_TILE_FENCE_END
};

const wchar_t TilesHealth[] {
	   0, 150, 300, 600,
	   0,    0, 300, 300,
	 600,    0,   0, 150,
	 0, 0, 0, 0,
	 0
};

GameTeam TilesDestroyable[] = {
	GameTeamNone, GameTeamCucumber, GameTeamCucumber, GameTeamCucumber,
	GameTeamNone, GameTeamNone, GameTeamCucumber, GameTeamTomato,
	GameTeamTomato, GameTeamNone, GameTeamNone, GameTeamTomato,
	GameTeamNone, GameTeamNone, GameTeamNone, GameTeamNone,
	GameTeamNone
};

bool TilesCollision[] = { 
	0, 0, 1, 1,
	1, 0, 1, 1,
	1, 0, 0, 0,
	1, 1, 1, 1,
	1
};

class ButtonSprite : public grabSprite {
public:
	bool Hovered;
	D2D1_SIZE_F Size;
	IDWriteTextFormat* Font;
	std::wstring Text;
	ID2D1Brush* Brush;

	ButtonSprite(ID2D1Bitmap* bg, D2D1_SIZE_F size, IDWriteTextFormat* font, ID2D1Brush* brush, std::wstring text)
		: grabSprite(bg), Size(size), Hovered(false), Font(font), Text(text), Brush(brush)
	{
		Scale = { 2.f, 2.f };
	}

	ButtonSprite() {}

	void Draw(grabWindow* win) {
		win->rt->SetTransform(
			D2D1::Matrix3x2F::Scale(*((D2D1_SIZE_F*)(&Scale)))
			* D2D1::Matrix3x2F::Translation(*((D2D1_SIZE_F*)(&Position)))
		);

		D2D1_SIZE_F size = Bitmap->GetSize();
		D2D1_RECT_F rectangle = D2D1::RectF(0, 0, size.width, size.height);
		win->rt->DrawBitmap(Bitmap, rectangle, 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, rectangle);
		win->rt->DrawTextW(
			Text.c_str(), Text.length(), Font,
			D2D1::RectF(0, 0, Size.width / 2, Size.height / 2),
			Brush, D2D1_DRAW_TEXT_OPTIONS_NONE, DWRITE_MEASURING_MODE_NATURAL
		);
	}

	void HoverCheck(Vec2<unsigned int>* MousePos) {
		if (Position.x < MousePos->x && Position.y < MousePos->y && MousePos->x < (Position.x + Size.width) && MousePos->y < (Position.y + Size.height)) {
			Hovered = true;
			this->Scale = { 2.1f, 2.1f };
			return;
		}

		this->Scale = { 2.f, 2.f };
		Hovered = false;
	}
};

enum CurrentGameState {
	GameStateGameMenu, GameStateSelectLevel, GameStateRTS
};

struct GameLevel {
	unsigned int Width, Height;
	std::string Tiles;
	std::wstring TileHealth;
	std::vector<GameUnit*>* Units;
};

void DrawLevel(GameLevel* level, ID2D1Bitmap* Tileset, grabWindow* win, Vec2<float>& CamPos) {
	D2D1_SIZE_F WinSize = win->rt->GetSize();
	D2D1_POINT_2F WinCenter = { WinSize.width / 2, WinSize.height / 2 };

	for (int y = 0; y < level->Height; y++) {
		unsigned int y_total = y * level->Width;
		for (int x = 0; x < level->Width; x++) {
			float x_offset = (float)(level->Tiles[y_total + x] * 64);

			D2D1_SIZE_F Tilepos = { (x - y) * 64 - CamPos.x, (x + y) * 32 - CamPos.y };
			win->rt->SetTransform(
				D2D1::Matrix3x2F::Scale(2.f, 2.f, WinCenter)
				* D2D1::Matrix3x2F::Translation(Tilepos.width, Tilepos.height)
			);
			
			win->rt->DrawBitmap(
				Tileset, D2D1::RectF(0, 0, 64, 32), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
				D2D1::RectF(x_offset, 0.f, 64.f + x_offset, 32.f)
			);
		}
	}
}

void LoadLevel(const char* filename, GameLevel* lvl) {
	std::ifstream t(filename);

	std::string str((std::istreambuf_iterator<char>(t)),
		std::istreambuf_iterator<char>());

	lvl->Width	= *((unsigned int*)&str[0]);
	lvl->Height	= *((unsigned int*)&str[4]);

	unsigned int str_size = lvl->Width * lvl->Height;
	lvl->Tiles.resize(str_size);
	lvl->TileHealth.resize(str_size);

	char* TilesSrc = &str[8];
	for (unsigned int i = 0; i < str_size; i++) {
		lvl->Tiles[i] = TilesSrc[i];
		lvl->TileHealth[i] = TilesHealth[TilesSrc[i]];
	}

	return;
}

int FindBuilding(GameLevel* level, char building) {
	for (int i = 0; i < level->Width * level->Height; i++) {
		if (level->Tiles[i] == building) {
			return i;
			break;
		}
	}

	return -1;
}

void IndexToCoord(unsigned int index, Vec2<unsigned int>* coord, GameLevel* level) {
	coord->y = index % level->Width;
	coord->x = index - coord->y * level->Width;
}

void WorldToIsometric(Vec2<float>* world, Vec2<float>* result, Vec2<float>* Camera) {
	(*result) = { (world->x - world->y) * 64.f - Camera->x, (world->x + world->y) * 32.f - Camera->y };
}

void FindThePath(Vec2<unsigned int>* from, Vec2<unsigned int>* to, Vec2<unsigned int>** result, GameLevel* lvl, unsigned int* len) {
	enum PathState {
		PATH_PREV_X_MINUS = 1, PATH_PREV_X_PLUS = 2, PATH_PREV_Y_MINUS = 4, PATH_PREV_Y_PLUS = 8,
		PATH_UNCHECKED = 16, PATH_POSSIBLE = 32, PATH_DEAD = 64, PATH_SAVE_DIR = 15, PATH_CHECK_LATER = 128,
		PATH_WALL = 0, 
		PATH_MAKE_DEAD = PATH_DEAD | PATH_SAVE_DIR
	};

	if (from->x == to->x && from->y == to->y) {
		(*result) = NULL;
		(*len) = 0;
		return;
	}

	unsigned int dead_total = 0;
	unsigned int str_size = lvl->Width * lvl->Height;
	unsigned int path_length = 0;

	std::vector<PathState> LevelData;
	LevelData.resize(str_size, PATH_UNCHECKED);
	{ // Fill walls
		unsigned int LevelSize = lvl->Width * lvl->Height;
		for (unsigned int i = 0; i < LevelSize; i++) {
			if (TilesCollision[lvl->Tiles[i]]) LevelData[i] = PATH_WALL;
		}
	}
	LevelData[from->y * lvl->Width + from->x] = PATH_POSSIBLE;
	LevelData[to->y * lvl->Width + to->x] = PATH_UNCHECKED;
	
	unsigned int x = 0, y = 0;
	while (dead_total < str_size) {
		path_length++;
		for (y = 0; y < lvl->Height; y++) {
			unsigned int y_total = y * lvl->Width;
			for (x = 0; x < lvl->Width; x++) {
				unsigned int index = y_total + x;

				if ((LevelData[index] & PATH_POSSIBLE) != 0) {
					unsigned int index_check = index - 1;

					if (x != 0) {
						if (LevelData[index_check] == PATH_UNCHECKED)
							LevelData[index_check] = (PathState)(PATH_POSSIBLE | PATH_PREV_X_PLUS);
					}
					index_check = index - lvl->Width;

					if (y != 0) {
						if (LevelData[index_check] == PATH_UNCHECKED)
							LevelData[index_check] = (PathState)(PATH_POSSIBLE | PATH_PREV_Y_PLUS);
					}

					index_check = index + 1;

					if (x != lvl->Width - 1) {
						if (LevelData[index_check] == PATH_UNCHECKED)
							LevelData[index_check] = (PathState)(PATH_CHECK_LATER | PATH_PREV_X_MINUS);
					}
					index_check = index + lvl->Width;

					if (y != lvl->Height - 1) {
						if (LevelData[index_check] == PATH_UNCHECKED)
							LevelData[index_check] = (PathState)(PATH_CHECK_LATER | PATH_PREV_Y_MINUS);
					}

					LevelData[index] = (PathState)((LevelData[index] & PATH_SAVE_DIR) | PATH_DEAD);
					dead_total++;

					// final 04ka
					if (to->y == y && to->x == x) {
						if (path_length > 1) {
							path_length--;
							unsigned int now = path_length - 1;

							(*result) = (Vec2<unsigned int>*)malloc(path_length * sizeof(Vec2<unsigned int>));
							(*result)[now] = Vec2<unsigned int>(x, y);

							while (now != 0) {
								unsigned int curr_x = x, curr_y = y;
								x -= 1 * ((LevelData[curr_x + curr_y * lvl->Width] & PATH_PREV_X_MINUS) != 0);
								x += 1 * ((LevelData[curr_x + curr_y * lvl->Width] & PATH_PREV_X_PLUS) != 0);
								y -= 1 * ((LevelData[curr_x + curr_y * lvl->Width] & PATH_PREV_Y_MINUS) != 0);
								y += 1 * ((LevelData[curr_x + curr_y * lvl->Width] & PATH_PREV_Y_PLUS) != 0);

								(*result)[--now].x = x;
								(*result)[now].y = y;
							}
						}
						else (*result) = NULL;

						(*len) = path_length;
						return;
					}
				}

				else if (LevelData[index] & PATH_CHECK_LATER) {
					LevelData[index] = (PathState)(LevelData[index] - PATH_CHECK_LATER + PATH_POSSIBLE);
				}
			}
		}
	}

	return;
}

/////////////////////////
// MISSIONS STRUCTURES //
/////////////////////////

union UnitMission;

enum MissionType {
	MISSION_TYPE_NONE = 0, MISSION_TYPE_BUILDING = 1, MISSION_TYPE_KILL = 4, MISSION_TYPE_GOTO = 2, MISSION_TYPE_LATER = 8,
	MISSION_TYPE_ATTACK = 16,

	MISSION_TYPE_GOTO_BUILDING = MISSION_TYPE_BUILDING | MISSION_TYPE_GOTO,
	MISSION_TYPE_BUILDING_LATER = MISSION_TYPE_BUILDING | MISSION_TYPE_LATER,
	MISSION_TYPE_GOTO_LATER = MISSION_TYPE_GOTO | MISSION_TYPE_LATER,
	MISSION_TYPE_GOTO_BUILDING_LATER = MISSION_TYPE_GOTO_BUILDING | MISSION_TYPE_LATER,
	MISSION_TYPE_ATTACK_BUILDING = MISSION_TYPE_ATTACK | MISSION_TYPE_BUILDING,
	MISSION_TYPE_GOTO_ATTACK_BUILDING = MISSION_TYPE_GOTO | MISSION_TYPE_ATTACK_BUILDING,
	MISSION_TYPE_GOTO_ATTACK_BUILDING_LATER = MISSION_TYPE_GOTO_ATTACK_BUILDING | MISSION_TYPE_LATER,
	MISSION_TYPE_GOTO_ATTACK = MISSION_TYPE_GOTO | MISSION_TYPE_ATTACK,
	MISSION_TYPE_GOTO_ATTACK_LATER = MISSION_TYPE_GOTO_ATTACK | MISSION_TYPE_LATER,
	MISSION_TYPE_GOTO_KILL = MISSION_TYPE_GOTO | MISSION_TYPE_KILL,
	MISSION_TYPE_GOTO_KILL_LATER = MISSION_TYPE_GOTO_KILL | MISSION_TYPE_LATER
};

struct BasicMission {
	MissionType Type;
	BasicMission(MissionType type) : Type(type) {}
};

struct BuildMission : BasicMission { 
	Vec2<unsigned int> Where; 
	GameTiles What;
	unsigned int Elapsed;
};

struct AttackMission : BasicMission {
	unsigned int Elapsed;
	GameUnit* Target;
};

struct GotoMission : BasicMission { 
	enum PathData { PATH_NULL = 0, PATH_MAKE_LATER = 1 };

	Vec2<unsigned int>* Path;
	Vec2<unsigned int> FinalPos;
	unsigned int Len;
	float CurrentPos;
};

struct KillMission : BasicMission {
	Vec2<unsigned int> Target; 
	unsigned int Elapsed;
	unsigned int DamageTotal;
};

union UnitMission {
	BasicMission Basic;
	GotoMission Goto;
	KillMission Kill;
	BuildMission Build;
	AttackMission Attack;

	UnitMission() {}
	UnitMission(MissionType type) : Basic(MISSION_TYPE_NONE) {}
	~UnitMission() {}
};

void ClearUnitReferences(GameUnit* target);

struct GameUnit {
	GameTeam Team;
	GameClass Class;
	unsigned char Health;
	Vec2<float> Pos;
	bool SelectedByPlayer;
	GameLevel* Level;

	unsigned int CurrentMission;
	std::vector<UnitMission> Mission;
	
	bool RWLocked = false;
	std::thread* PrevThread;

	static float GetDistUnits(GameUnit* u1, GameUnit* u2) {
		Vec2<float> offset = { abs(u1->Pos.x - u2->Pos.x), abs(u1->Pos.y - u2->Pos.y) };
		return sqrt(offset.x * offset.x + offset.y + offset.y);
	}

	GameUnit(const GameUnit& unit) : Team(unit.Team), Level(unit.Level), Class(unit.Class), Health(unit.Health), Pos(unit.Pos), SelectedByPlayer(false), PrevThread(NULL) {}
	GameUnit() {}
	~GameUnit() {
		if (!PrevThread) delete PrevThread;
	}

	void DrawMyMissions(grabWindow* window, ID2D1Bitmap* tileset, Vec2<float>* camera, Vec2<float>* WinCenter) {
		for (unsigned int i = 0; i < Mission.size(); i++) {
			if (Mission[i].Basic.Type & MISSION_TYPE_GOTO) {
				D2D1_SIZE_F Tilepos;
				Vec2<float> worldf = { (float)Mission[i].Goto.FinalPos.x, (float)Mission[i].Goto.FinalPos.y };
				WorldToIsometric(&worldf, (Vec2<float>*)&Tilepos, camera);
				window->rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f, *((D2D1_POINT_2F*)WinCenter)) * D2D1::Matrix3x2F::Translation(Tilepos));
				window->rt->DrawBitmap(tileset, D2D1::RectF(0.f, 0.f, 64.f, 32.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1::RectF(GAME_TILE_GOTO * 64.f, 0.f, GAME_TILE_GOTO * 64.f + 64.f, 32.f));
			}
		}
	}

	void ForgetMission() {
		for (unsigned int i = 0; i < Mission.size(); i++) {
			if (Mission[i].Basic.Type == MISSION_TYPE_BUILDING) {
				Level->Tiles[Mission[i].Build.Where.x + Mission[i].Build.Where.y * Level->Width] = GAME_TILE_GRASS;
			}
			else if (Mission[i].Basic.Type & MISSION_TYPE_GOTO && !(Mission[i].Basic.Type & MISSION_TYPE_LATER)) {
				free(Mission[i].Goto.Path);
			}
		}
		Mission.resize(0);
	}

	static void UpdateMission(GameUnit* unit) {
		while (unit->RWLocked) {}
		unit->RWLocked = true;

		if (unit->Mission.size() > 0) {
			// that is shit x1
			if (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_BUILDING_LATER) {
				Vec2<unsigned int> PosU = { (unsigned int)unit->Pos.x, (unsigned int)unit->Pos.y };
				FindThePath(&PosU, &unit->Mission[0].Goto.FinalPos, &unit->Mission[0].Goto.Path, unit->Level, &unit->Mission[0].Goto.Len);

				unit->Mission[0].Basic.Type = MISSION_TYPE_GOTO_BUILDING;
			}
			// that is shit x2
			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_LATER) {
				Vec2<unsigned int> PosU = { (unsigned int)unit->Pos.x, (unsigned int)unit->Pos.y };
				FindThePath(&PosU, &unit->Mission[0].Goto.FinalPos, &unit->Mission[0].Goto.Path, unit->Level, &unit->Mission[0].Goto.Len);

				unit->Mission[0].Basic.Type = MISSION_TYPE_GOTO;
			}
			// that is shit x3
			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_ATTACK_BUILDING_LATER) {
				Vec2<unsigned int> PosU = { (unsigned int)unit->Pos.x, (unsigned int)unit->Pos.y };
				FindThePath(&PosU, &unit->Mission[0].Goto.FinalPos, &unit->Mission[0].Goto.Path, unit->Level, &unit->Mission[0].Goto.Len);

				unit->Mission[0].Basic.Type = MISSION_TYPE_GOTO_ATTACK_BUILDING;
			}
			// that is shit x4
			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_BUILDING_LATER) {
				if (unit->Level->Tiles[unit->Mission[0].Kill.Target.x + unit->Mission[0].Kill.Target.y * unit->Level->Width] == GAME_TILE_GRASS) {
					GameVoice.Fix->setPosition(0);
					GameVoice.Fix->play();
					unit->Mission[0].Basic.Type = MISSION_TYPE_BUILDING;
				}
				else {
					unit->Mission.erase(unit->Mission.begin());
				}
			}
			// that is shit x5
			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_KILL_LATER) {
				Vec2<unsigned int> PosU = { (unsigned int)unit->Pos.x, (unsigned int)unit->Pos.y };
				FindThePath(&PosU, &unit->Mission[0].Goto.FinalPos, &unit->Mission[0].Goto.Path, unit->Level, &unit->Mission[0].Goto.Len);

				unit->Mission[0].Basic.Type = MISSION_TYPE_GOTO_KILL;
			}

			else if (unit->Mission[0].Basic.Type & MISSION_TYPE_GOTO) {
				unit->Mission[0].Goto.CurrentPos += ClassVelocity[unit->Class];

				if ((unit->Mission[0].Goto.CurrentPos + 1 * ((unit->Mission[0].Basic.Type & MISSION_TYPE_BUILDING) != 0) + ClassRange[unit->Class] * (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_KILL)) > unit->Mission[0].Goto.Len) {
					if (unit->Mission[0].Goto.Path) free((void*)unit->Mission[0].Goto.Path);
					unit->Mission.erase(unit->Mission.begin());

					unit->RWLocked = false;
					return;
				}
				
				float mult = (unit->Mission[0].Goto.CurrentPos - (int)unit->Mission[0].Goto.CurrentPos);
				float mult2 = 1 - mult;

				Vec2<unsigned int>* NewPos = &(unit->Mission[0].Goto.Path[(unsigned int)(unit->Mission[0].Goto.CurrentPos)]);

				if (NewPos) {
					unit->Pos.x = unit->Pos.x * mult2 + static_cast<float>(NewPos->x)* mult;
					unit->Pos.y = unit->Pos.y * mult2 + static_cast<float>(NewPos->y)* mult;
				}				
			}

			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_BUILDING) {
				unit->Level->Tiles[unit->Mission[0].Build.Where.x + unit->Mission[0].Build.Where.y * unit->Level->Width] = GAME_TILE_CONSTRUCTION;
				unit->Mission[0].Build.Elapsed++;

				if (unit->Mission[0].Build.Elapsed == 0x80) {
					unsigned int index = unit->Mission[0].Build.Where.x + unit->Mission[0].Build.Where.y * unit->Level->Width;
					unit->Level->Tiles[index] = unit->Mission[0].Build.What;
					unit->Level->TileHealth[index] = TilesHealth[unit->Mission[0].Build.What];
					unit->Mission.erase(unit->Mission.begin());

					unit->RWLocked = false;
					return;
				}
			}

			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_ATTACK) {
				unit->Mission[0].Attack.Elapsed++;

				if (unit->Mission[0].Attack.Elapsed >= ClassReloadTime[unit->Class]) {		
					if (unit->Mission[0].Attack.Target->Health <= ClassDamage[unit->Class]) {
						GameUnit* target = unit->Mission[0].Attack.Target;
						target->Health = 0;
						ClearUnitReferences(target);
					}
					else {
						unit->Mission[0].Attack.Target->Health -= ClassDamage[unit->Class];
						unit->Mission.erase(unit->Mission.begin());
					}

					if (ClassHealth[unit->Class] < unit->Health) unit->Health = ClassHealth[unit->Class];
					if (unit->Class != GameClassMedic) {
						switch (unit->Class) {
						case GameClassHeavy:
							GameVoice.Fire->setPosition(0);
							GameVoice.Fire->play();
							break;
						case GameClassSoldier:
							GameVoice.Rocket->setPosition(0);
							GameVoice.Rocket->play();
							break;
						}
					}

					unit->RWLocked = false;
					return;
				}
			}

			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_ATTACK_BUILDING) {
				unit->Mission[0].Kill.Elapsed++;

				if (unit->Mission[0].Kill.Elapsed >= ClassReloadTime[unit->Class]) {
					unit->Mission[0].Kill.Elapsed = 0;
					size_t index = unit->Mission[0].Kill.Target.x + unit->Mission[0].Kill.Target.y * unit->Level->Width;

					if (unit->Class == GameClassBuilder) {
						GameVoice.Fix->setPosition(0);
						GameVoice.Fix->play();
						unit->Mission.erase(unit->Mission.begin());
					}
					else {
						if (unit->Level->TileHealth[index] <= ClassDamage[unit->Class]) {
							unit->Level->Tiles[index] = GAME_TILE_GRASS;
							unit->Level->TileHealth[index] = 0;
							unit->Mission.erase(unit->Mission.begin());
						}
						else {
							unit->Level->TileHealth[index] -= ClassDamage[unit->Class];
						}

						switch (unit->Class) {
						case GameClassHeavy:
							GameVoice.Fire->setPosition(0);
							GameVoice.Fire->play();
							break;
						case GameClassSoldier:
							GameVoice.Rocket->setPosition(0);
							GameVoice.Rocket->play();
							break;
						}
					}

					unit->RWLocked = false;
					return;
				}
			}
			else if (unit->Mission[0].Basic.Type == MISSION_TYPE_GOTO_ATTACK) {
				
			}
		}
		else {
			// Create attack mission
			GameUnit** UnitsPtr = &(*unit->Level->Units)[0];
			size_t size = unit->Level->Units->size();
			GameTeam target_team = (unit->Class == GameClassMedic) ? (unit->Team) : (unit->Team == GameTeamTomato ? GameTeamCucumber : GameTeamTomato);

			if (unit->Class == GameClassMedic) { // for medic
				for (unsigned int i = 0; i < size; i++) {
					if ((UnitsPtr[i] != unit) && (UnitsPtr[i]->Team == unit->Team) && (UnitsPtr[i]->Health < ClassHealth[UnitsPtr[i]->Class])) {
						if ((abs(unit->Pos.x - UnitsPtr[i]->Pos.x) <= ClassRange[unit->Class]) && (abs(unit->Pos.y - UnitsPtr[i]->Pos.y) <= ClassRange[unit->Class])) {
							UnitMission mission;
							mission.Basic.Type = MISSION_TYPE_ATTACK;
							mission.Attack.Elapsed = 0;
							mission.Attack.Target = UnitsPtr[i];

							unit->Mission.push_back(mission);
							break;
						}
					}
				}
			}
			else if (unit->Class == GameClassBuilder) { // for builder
				Vec2<unsigned int> unit_pos = { (unsigned int)unit->Pos.x, (unsigned int)unit->Pos.y };
				GameTeam target_team = ReversedTeam[unit->Team];

				UnitMission mission;
				mission.Basic.Type = MISSION_TYPE_ATTACK_BUILDING;
				mission.Kill.Elapsed = 0;
				mission.Kill.DamageTotal = 0;

				for (unsigned int y = -1; y < 1; y++) {
					for (unsigned int x = -1; x < 1; x++) {
						unsigned int unit_pos_index = unit_pos.x + x + (unit_pos.y + y) * unit->Level->Width;
						char CurrTile = unit->Level->Tiles[unit_pos_index];
						if ((TilesDestroyable[CurrTile] == target_team) && (unit->Level->TileHealth[unit_pos_index] < TilesHealth[CurrTile])) {
							mission.Kill.Target = { unit_pos.x + x, unit_pos.y + y };
							unit->Mission.push_back(mission);
							break;
						}
					}
				}
			}
			else { // for stupid shooters
				for (unsigned int i = 0; i < size; i++) {
					if ((UnitsPtr[i]->Team != unit->Team) && (UnitsPtr[i]->Health > 0)) {
						if ((abs(unit->Pos.x - UnitsPtr[i]->Pos.x) <= ClassRange[unit->Class]) && (abs(unit->Pos.y - UnitsPtr[i]->Pos.y) <= ClassRange[unit->Class])) {
							UnitMission mission;
							mission.Basic.Type = MISSION_TYPE_ATTACK;
							mission.Attack.Elapsed = 0;
							mission.Attack.Target = UnitsPtr[i];

							unit->Mission.push_back(mission);
							break;
						}
					}
				}
			}
		}

		unit->RWLocked = false;
	}

	void UpdateMission_Async() {
		std::thread thr(GameUnit::UpdateMission, this);
		thr.detach();
	}

	static void Build(GameUnit* unit, Vec2<unsigned int> pos, GameTiles What) {
		if (unit->Class != GameClassBuilder) return;
		if (unit->Level->Tiles[pos.x + pos.y * unit->Level->Width] != GAME_TILE_GRASS) {
			if (unit->Team == GameTeamTomato) {
				GameVoice.No->setPosition(0);
				GameVoice.No->play();
				return;
			}
		}

		while (unit->RWLocked) {}
		unit->RWLocked = true;

		UnitMission mission;
		mission.Basic.Type = MISSION_TYPE_GOTO_BUILDING;
		mission.Goto.CurrentPos = 0;
		mission.Goto.FinalPos = pos;

		Vec2<unsigned int> UnitTile(unit->Pos.x, unit->Pos.y);

		if (unit->Mission.size() == 0)
			FindThePath(&UnitTile, &pos, &mission.Goto.Path, unit->Level, &mission.Goto.Len);
		else
			mission.Goto.Type = MISSION_TYPE_GOTO_BUILDING_LATER;	

		unit->Mission.push_back(mission);

		mission.Basic.Type = MISSION_TYPE_BUILDING_LATER;
		mission.Build.What = What;
		mission.Build.Where = pos;
		mission.Build.Elapsed = 0;
		
		unit->Mission.push_back(mission);
		unit->RWLocked = false;

		if (unit->Team == GameTeamTomato) {
			GameVoice.Yes->setPosition(0);
			GameVoice.Yes->play();
		}
	}

	///////////////
	// KILL TILE //
	///////////////
	static void KillTile(GameUnit* unit, Vec2<unsigned int> pos) {
		{
			GameTiles tile = (GameTiles)unit->Level->Tiles[pos.x + pos.y * unit->Level->Width];
			if (TilesDestroyable[tile] != unit->Team) {
				if (unit->Team == GameTeamTomato) {
					GameVoice.No->setPosition(0);
					GameVoice.No->play();
				}
				return;
			}
		}

		while (unit->RWLocked) {}
		unit->RWLocked = true;

		UnitMission mission;
		mission.Basic.Type = MISSION_TYPE_GOTO_ATTACK_BUILDING;
		mission.Goto.CurrentPos = 0;
		mission.Goto.FinalPos = pos;

		Vec2<unsigned int> UnitTile(unit->Pos.x, unit->Pos.y);

		if (unit->Mission.size() == 0)
			FindThePath(&UnitTile, &pos, &mission.Goto.Path, unit->Level, &mission.Goto.Len);
		else
			mission.Goto.Type = MISSION_TYPE_GOTO_ATTACK_BUILDING_LATER;

		unit->Mission.push_back(mission);

		mission.Basic.Type = MISSION_TYPE_ATTACK_BUILDING;
		mission.Kill.Target = pos;

		unit->Mission.push_back(mission);
		unit->RWLocked = false;

		if (unit->Team == GameTeamTomato) {
			GameVoice.Yes->setPosition(0);
			GameVoice.Yes->play();
		}
	}

	static void Goto(GameUnit* unit, Vec2<unsigned int> pos) {
		if (TilesCollision[unit->Level->Tiles[pos.x + pos.y * unit->Level->Width]]) {
			if (unit->Team == GameTeamTomato) {
				GameVoice.No->setPosition(0);
				GameVoice.No->play();
			}
			return;
		}

		UnitMission mission;
		mission.Basic.Type = MISSION_TYPE_GOTO;
		mission.Goto.CurrentPos = 0;
		mission.Goto.FinalPos = pos;

		Vec2<unsigned int> UnitTile(unit->Pos.x, unit->Pos.y);
		
		if (unit->Mission.size() == 0)
			FindThePath(&UnitTile, &pos, &mission.Goto.Path, unit->Level, &mission.Goto.Len);
		else
			mission.Goto.Type = MISSION_TYPE_GOTO_LATER;

		while (unit->RWLocked) {}
		unit->RWLocked = true;
		unit->Mission.push_back(mission);
		unit->RWLocked = false;


		if (unit->Team == GameTeamTomato) {
			GameVoice.Yes->setPosition(0);
			GameVoice.Yes->play();
		}
	}

	static void Goto_Async(GameUnit* unit, Vec2<unsigned int> pos) {
		std::thread thr(GameUnit::Goto, unit, pos);
		thr.detach();
	}

	void Build_Async(Vec2<unsigned int> pos, GameTiles What) {
		std::thread thr(GameUnit::Build, this, pos, What);
		thr.detach();
	}

	void KillTile_Async(Vec2<unsigned int> pos) {
		std::thread thr(GameUnit::KillTile, this, pos);
		thr.detach();
	}

	GameUnit (GameTeam Team, GameClass Class, Vec2<float> Pos, GameLevel* level)
		: Team(Team), Class(Class), Pos(Pos), Health(ClassHealth[Class]), Level(level), RWLocked(false), SelectedByPlayer(false) {};
};

void AddFirstUnits(std::vector<GameUnit*>* vec, GameLevel* level, Vec2<unsigned int>* Where, GameTeam Team) {
	vec->push_back(new GameUnit(Team, GameClassMedic, { (float)(Where->x - 1), (float)(Where->y) }, level));
	vec->push_back(new GameUnit(Team, GameClassHeavy, { (float)(Where->x - 1), (float)(Where->y + 1) }, level));
	vec->push_back(new GameUnit(Team, GameClassHeavy, { (float)(Where->x + 1), (float)(Where->y - 1) }, level));
	vec->push_back(new GameUnit(Team, GameClassSoldier, { (float)(Where->x - 1), (float)(Where->y - 1) }, level));
	vec->push_back(new GameUnit(Team, GameClassSoldier, { (float)(Where->x + 1), (float)(Where->y + 1) }, level));
	vec->push_back(new GameUnit(Team, GameClassBuilder, { (float)(Where->x + 1), (float)(Where->y) }, level));
}

void ScreenToTile(Vec2<float>* CameraPos, Vec2<float>* result, Vec2<unsigned int>* screen, Vec2<float>* WinCenter) {
	Vec2<float> NewCameraPos = { CameraPos->x + WinCenter->x - 32.f, CameraPos->y + WinCenter->y - 16.f };
	float OffsetX = (-NewCameraPos.x) * 32.f;
	float OffsetY = (-NewCameraPos.y) * 64.f;
	result->x = 1.f / 4096.f * (screen->x * 32.f + screen->y * 64.f - OffsetX - OffsetY);
	result->y = 0.5f + 1.f / 4096.f * (screen->x * -32.f + screen->y * 64.f + OffsetX - OffsetY);

	return;
}

void UpdateUnits_Async(std::vector<GameUnit*>* vec) {
	if (vec->size() > 0) {
		GameUnit** arr = &(*vec)[0];
		for (unsigned int i = 0; i < vec->size(); i++) {
			GameUnit::UpdateMission(arr[i]);
		}
	}
}

void ClearUnitReferences(GameUnit* unit) {
	GameVoice.Die->setPosition(0);
	GameVoice.Die->play();

	unit->SelectedByPlayer = false;
	unit->ForgetMission();
	for (unsigned int i = 0; i < SelectedUnits.size(); i++) {
		if (SelectedUnits[i] == unit) {
			SelectedUnits.erase(SelectedUnits.begin() + i);
		}
	}

	{
		unsigned int units_size = Units.size();
		GameUnit** all_units = &Units[0];
		for (unsigned int i = 0; i < units_size; i++) {
			unsigned int mission_size = all_units[i]->Mission.size();
			
			if (mission_size > 0) {
				UnitMission* mission = &all_units[i]->Mission[0];

				for (unsigned int m = 0; m < mission_size; m++) {
					if (mission[m].Basic.Type == MISSION_TYPE_ATTACK && mission[m].Attack.Target == unit) {
						all_units[i]->Mission.erase(all_units[i]->Mission.begin() + m);
					}
				}
			}
		}

		for (unsigned int i = 0; i < units_size; i++) {
			if (Units[i] == unit) {
				Units.erase(Units.begin() + i);
				break;
			}
		}
	}
}

ID2D1Bitmap* PomittoLose;
ID2D1Bitmap* PomittoWins;
ID2D1Bitmap* CreateUnitBitmap;
grabWindow Win;

void YouWonFun() {
	Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f));
	Win.rt->DrawBitmap(PomittoWins, NULL, 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}

void YouLoseFun() {
	Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f));
	Win.rt->DrawBitmap(PomittoLose, NULL, 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
}

void SpawnInTheGarden(Vec2<unsigned int> pos, GameClass Class, GameTeam team, GameLevel* level) {
	Sleep(5000);

	if (team == GameTeamTomato) {
		GameVoice.Hey->setPosition(0);
		GameVoice.Hey->play();
	}
	
	Units.push_back(new GameUnit(team, Class, { (float)pos.x, (float)pos.y }, level));
}

void DrawWinText(grabWindow* win, D2D1_RECT_F rect, ID2D1Brush* brush, std::wstring text, IDWriteTextFormat* font) {
	win->rt->DrawTextW(text.c_str(), text.size(), font, rect, brush);
}

void GetNearestTile(Vec2<unsigned int>* from, Vec2<unsigned int>* result, GameTiles tile, GameLevel* lvl) {
	enum PathState {
		PATH_UNCHECKED = 16, PATH_POSSIBLE = 32, PATH_DEAD = 64, PATH_SAVE_DIR = 15, PATH_CHECK_LATER = 128,
		PATH_WALL = 0,
	};

	if (lvl->Tiles[from->x + from->y * lvl->Height] == tile) {
		(*result) = (*from);
		return;
	}

	unsigned int str_size = lvl->Width * lvl->Height;
	unsigned int dead_total = 0;

	std::vector<PathState> LevelData;
	LevelData.resize(str_size, PATH_UNCHECKED);
	{ // Fill walls
		unsigned int LevelSize = lvl->Width * lvl->Height;
		for (unsigned int i = 0; i < LevelSize; i++) {
			if (TilesCollision[lvl->Tiles[i]]) LevelData[i] = PATH_WALL;
		}
	}
	LevelData[from->y * lvl->Width + from->x] = PATH_POSSIBLE;

	unsigned int x = 0, y = 0;
	while (dead_total < str_size) {
		for (y = 0; y < lvl->Height; y++) {
			unsigned int y_total = y * lvl->Width;
			for (x = 0; x < lvl->Width; x++) {
				unsigned int index = y_total + x;

				if ((LevelData[index] & PATH_POSSIBLE) != 0) {
					unsigned int index_check = index - 1;

					if (x != 0) {
						if (LevelData[index_check] == PATH_UNCHECKED) LevelData[index_check] = (PathState)(PATH_POSSIBLE);
					}
					index_check = index - lvl->Width;

					if (y != 0) {
						if (LevelData[index_check] == PATH_UNCHECKED) LevelData[index_check] = (PathState)(PATH_POSSIBLE);
					}
					index_check = index + 1;

					if (x != lvl->Width - 1) {
						if (LevelData[index_check] == PATH_UNCHECKED) LevelData[index_check] = (PathState)(PATH_CHECK_LATER);
					}
					index_check = index + lvl->Width;

					if (y != lvl->Height - 1) {
						if (LevelData[index_check] == PATH_UNCHECKED) LevelData[index_check] = (PathState)(PATH_CHECK_LATER);
					}

					LevelData[index] = (PathState)(PATH_DEAD);
					dead_total++;

					// final 04ka
					if (lvl->Tiles[x + y * lvl->Width] == tile) {
						(*result) = { x, y };
						return;
					}
				}

				else if (LevelData[index] & PATH_CHECK_LATER) {
					LevelData[index] = (PathState)(LevelData[index] - PATH_CHECK_LATER + PATH_POSSIBLE);
				}
			}
		}
	}

	(*result) = (*from);
	return;
}

GameUnit* GetNearestMyFighter(GameUnit* u, float* dist_result = NULL) {
	float buf;
	if (dist_result == NULL)  dist_result = &buf;
	(*dist_result) = FLT_MAX;

	GameUnit* result = Units[0];
	GameUnit** data = Units.data();

	for (unsigned int i = 0; i < Units.size(); i++) {
		if ((u != data[i]) && (u->Team == data[i]->Team) && ((data[i]->Class == GameClassSoldier) || (data[i]->Class == GameClassHeavy))) {
			float curr_dist = GameUnit::GetDistUnits(u, data[i]);
			if ((*dist_result) > curr_dist) {
				(*dist_result) = curr_dist;
				result = data[i];
			}
		}
	}

	return result;
}

unsigned int GetUnitsCount(GameTeam team, GameClass Class) {
	unsigned int result = 0;
	for (unsigned int i = 0; i < Units.size(); i++) {
		if ((Units[i]->Team == team) && (Units[i]->Class == Class)) result++;
	}

	return result;
}

unsigned int GetFightersCount(GameTeam team) {
	unsigned int result = 0;
	for (unsigned int i = 0; i < Units.size(); i++) {
		if (Units[i]->Team == team  && (Units[i]->Class == GameClassSoldier || Units[i]->Class == GameClassHeavy)) result++;
	}

	return result;
}

GameLevel CurrentLevel;

unsigned int GetTilesCount(GameTiles tile) {
	size_t size = CurrentLevel.Width * CurrentLevel.Height;
	unsigned int result = 0;

	for (unsigned int i = 0; i < size; i++) {
		if (CurrentLevel.Tiles[i] == tile) result++;
	}

	return result;
}

Vec2<unsigned int> GetRandomTile(GameTiles tile) {
	unsigned int count = GetTilesCount(tile);
	unsigned int now = 0;

	if (count != 0) {
		srand(timeGetTime());
		unsigned int pick = rand() % count;

		for (unsigned int i = 0; i < CurrentLevel.Height * CurrentLevel.Width; i++) {
			if (now == pick) return { i % CurrentLevel.Width, i / CurrentLevel.Width };
			else if (CurrentLevel.Tiles[i] == tile) now++;
		}
	}
	else {
		return { (unsigned int)-1, (unsigned int)-1 };
	}
}

GameUnit* GetNearestEnemy(GameUnit* u) {
	float smallest = GameUnit::GetDistUnits(u, Units[0]);
	GameUnit* result = Units[0];
	GameUnit** data = Units.data();

	for (unsigned int i = 1; i < Units.size(); i++) {
		if (u->Team != data[i]->Team) {
			float curr_dist = GameUnit::GetDistUnits(u, data[i]);
			if (smallest > curr_dist) {
				smallest = curr_dist;
				result = data[i];
			}
		}
	}

	return result;
}

grabFactory Factory;
Vec2<float> CameraPosition{ 0.f,0.f };
Vec2<float> CameraDirection{ 0.f, 0.f };
Vec2<float> CameraAcceleration{ 0.f, 0.f };

Vec2<float> TileAtMouse{ 0.f, 0.f };
Vec2<unsigned int> TileAtMouseU{ 0, 0 };

ButtonSprite ButtonSelectLevel, ButtonLeaveTheGame;
bool isPaused;
std::wstring HintString;
D2D1_POINT_2F WinCenter;
wchar_t* TomatoKingHP;
wchar_t* CucumberKingHP;

audiere::OutputStreamPtr YouLoseSound, YouWinSound, MenuUpSound, MenuDownSound;

Vec2<unsigned int> MyKingPos, EnemyKingPos;

unsigned char MoneyTimer = 0;
unsigned int PlayerMoney = 10;
unsigned int EnemyMoney = 10;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	Win = grabWindow(&Factory, std::wstring(L"Pomitto: War In The Garden"), 720, 540);

	D2D1_SIZE_F WindowSize = Win.rt->GetSize();

	ID2D1Bitmap* MenuBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/menu.png", Win.rt, &MenuBitmap);
	grabSprite MenuSprite(MenuBitmap);
	MenuSprite.Scale = { 2.f, 2.f };
	MenuSprite.Position = { 10.f, 10.f };

	ID2D1Bitmap* TilesetBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/WorldTiles.png", Win.rt, &TilesetBitmap);

	ID2D1Bitmap* ButtonBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/Button.png", Win.rt, &ButtonBitmap);

	ID2D1Bitmap* LevelGUIBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/LevelGUI.png", Win.rt, &LevelGUIBitmap);

	ID2D1Bitmap* PauseBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/PauseGUI.png", Win.rt, &PauseBitmap);
	
	ID2D1Bitmap* TomatoesBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/Tomatoes.png", Win.rt, &TomatoesBitmap);

	ID2D1Bitmap* MuteBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/MuteGUI.png", Win.rt, &MuteBitmap);

	ID2D1Bitmap* SelectedUnitBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/SelectedUnit.png", Win.rt, &SelectedUnitBitmap);

	ID2D1Bitmap* SelectedTileBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/SelectedTile.png", Win.rt, &SelectedTileBitmap);

	ID2D1Bitmap* MoneyBitmap;
	grabResourceManager::LoadBitmapFromFile("Sprites/money.png", Win.rt, &MoneyBitmap);

	grabResourceManager::LoadBitmapFromFile("Sprites/YouLose.png", Win.rt, &PomittoLose);
	grabResourceManager::LoadBitmapFromFile("Sprites/YouWin.png", Win.rt, &PomittoWins);
	grabResourceManager::LoadBitmapFromFile("Sprites/CreateUnit.png", Win.rt, &CreateUnitBitmap);

	bool IsLeftMBUp = false;
	enum WinningState { WINNING_STATE_NOTHING = 0, WINNING_STATE_LOSE = 1, WINNING_STATE_YES = 2 };
	WinningState CurrentWinningState = WINNING_STATE_NOTHING;
	GameClass CurrentClass = GameClassMedic;

	HintString = L"Для igdc.ru | RTS III";

	// Load fonts
	ID2D1SolidColorBrush* FontBrush;
	Win.rt->CreateSolidColorBrush(D2D1::ColorF(0x0), &FontBrush);
	IDWriteFactory* pDWriteFactory;
	DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), (IUnknown**)(&pDWriteFactory));
	IDWriteTextFormat* AncientFont;
	IDWriteTextFormat* SmallFont;

	pDWriteFactory->CreateTextFormat(L"", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"", &AncientFont);
	pDWriteFactory->CreateTextFormat(L"", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 6.0f, L"", &SmallFont);
	AncientFont->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
	AncientFont->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
	SmallFont->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
	SmallFont->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);

	ButtonSelectLevel = ButtonSprite(ButtonBitmap, D2D1::SizeF(256.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L"Выбрать уровень"));
	ButtonSelectLevel.Position = { 10.f, 144.f };
	ButtonLeaveTheGame = ButtonSprite(ButtonBitmap, D2D1::SizeF(256.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L"Выйти из игры"));
	ButtonLeaveTheGame.Position = { 10.f, 218.f };

	ButtonSprite LevelsButtons[3];
	
	for (unsigned int i = 0; i < 3; i++) {
		LevelsButtons[i] = ButtonSprite(LevelGUIBitmap, D2D1::SizeF(64.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::to_wstring(i + 1));
		LevelsButtons[i].Position = { 30.f + i * 80.f, 30.f };
	}

	ButtonSprite ButtonGoBack(ButtonBitmap, D2D1::SizeF(256.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L"Назад"));
	ButtonGoBack.Position = { 30.f, 110.f };
	ButtonSprite ButtonPauseOff(ButtonBitmap, D2D1::SizeF(256.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L"В игру"));
	ButtonPauseOff.Position = { 30.f, 30.f };
	ButtonSprite ButtonToMenu(ButtonBitmap, D2D1::SizeF(256.f, 64.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L"В гл. меню"));
	ButtonToMenu.Position = { 30.f, 110.f };
	ButtonSprite ButtonPause(PauseBitmap, D2D1::SizeF(32.f, 32.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L""));
	ButtonPause.Position = { 10.f, 10.f };
	ButtonSprite ButtonMute(MuteBitmap, D2D1::SizeF(32.f, 32.f), AncientFont, (ID2D1Brush*)FontBrush, std::wstring(L""));
	ButtonMute.Position = { WindowSize.width - 40.f, 10.f };
	grabWindowMessage Message = { {} };

	CurrentGameState GameState = GameStateGameMenu;

	LoadLevel("Levels/menu.lvl", &CurrentLevel);
	CurrentLevel.Units = &Units;

	float MenuCameraAngle = 0.f;
	
	SetTimer(Win.hWnd, TIMER_CAMERA_UPDATE, 16, NULL);
	SetTimer(Win.hWnd, TIMER_AI_UPDATE, 1000, NULL);

	// sound shit
	audiere::AudioDevicePtr SoundDevice = audiere::OpenDevice();
	audiere::OutputStreamPtr MainTheme = audiere::OpenSound(SoundDevice, "Audio/rush.mp3", true);
	audiere::OutputStreamPtr ButtonSound = audiere::OpenSound(SoundDevice, "Audio/button.wav", false);
	GameVoice.Die = audiere::OpenSound/*Effect*/(SoundDevice, "Audio/die.wav", audiere::SoundEffectType::MULTIPLE);
	GameVoice.Fire = audiere::OpenSound/*Effect*/(SoundDevice, "Audio/fire.wav", audiere::SoundEffectType::MULTIPLE);
	GameVoice.Hey = audiere::OpenSound/*Effect*/(SoundDevice, "Audio/hey.wav", audiere::SoundEffectType::MULTIPLE);
	GameVoice.Rocket = audiere::OpenSound/*Effect*/(SoundDevice, "Audio/rocket.wav", audiere::SoundEffectType::MULTIPLE);
	GameVoice.Fix = audiere::OpenSound(SoundDevice, "Audio/fix.wav", audiere::SoundEffectType::MULTIPLE);
	GameVoice.No = audiere::OpenSound(SoundDevice,		"Audio/no.wav");
	GameVoice.Yes = audiere::OpenSound(SoundDevice,		"Audio/yes.wav");
	MenuUpSound = audiere::OpenSound(SoundDevice,		"Audio/menu_up.wav");
	MenuDownSound = audiere::OpenSound(SoundDevice,		"Audio/menu_down.wav");
	YouWinSound = audiere::OpenSound(SoundDevice,		"Audio/win.wav");
	YouLoseSound = audiere::OpenSound(SoundDevice,		"Audio/lose.wav");
	MainTheme->setRepeat(true);
	MainTheme->play();

	while (Win.IsOpened) {
		Win.GetWindowMessage(&Message);

		switch (Message.CommonMessage.Type)
		{
			//////////////
			// KEY DOWN //
			//////////////
		case grabMESSAGE_KEY_DOWN:
			switch (GameState)
			{
			case GameStateRTS:
				// Accelerate +
				if (Message.KeyDownMessage.PrevState == false) {
					switch (Message.KeyDownMessage.Key) {
					case 'W':
						CameraAcceleration.y -= 2.f;
						break;
					case 'S':
						CameraAcceleration.y += 2.f;
						break;
					case 'A':
						CameraAcceleration.x -= 2.f;
						break;
					case 'D':
						CameraAcceleration.x += 2.f;
						break;
					case 'Q':
						CurrentClass = (GameClass)(CurrentClass - 1);
						if (CurrentClass < 0) CurrentClass = (GameClass)(CurrentClass + 4);
						MenuUpSound->setPosition(0);
						MenuUpSound->play();
						break;

					case 'E':
						CurrentClass = (GameClass)(CurrentClass + 1);
						if (CurrentClass > 3) CurrentClass = (GameClass)(CurrentClass - 4);
						MenuDownSound->setPosition(0);
						MenuDownSound->play();
						break;

					case 'Z':
						{
						size_t index = TileAtMouseU.x + TileAtMouseU.y * CurrentLevel.Width;
						if (CurrentLevel.Tiles[index] == GAME_TILE_TOMATO_GARDEN) {
							if (PlayerMoney < ClassCost[CurrentClass]) {
								GameVoice.No->setPosition(0);
								GameVoice.No->play();
							}
							else {
								PlayerMoney -= ClassCost[CurrentClass];
								std::thread thr(SpawnInTheGarden, TileAtMouseU, CurrentClass, GameTeamTomato, &CurrentLevel);
								thr.detach();

								GameVoice.Yes->setPosition(0);
								GameVoice.Yes->play();
							}
						}
						break;
						}
					}

					if (CurrentWinningState != WINNING_STATE_NOTHING) {
						LoadLevel("Levels/menu.lvl", &CurrentLevel);
						ButtonSound->setPosition(0);
						ButtonSound->play();
						GameState = GameStateGameMenu;
					}
				}
				break;
			}
			break;

			////////////
			// KEY UP //
			////////////
		case grabMESSAGE_KEY_UP:
			switch (GameState)
			{
			case GameStateRTS:
				// Accelerate -
				switch (Message.KeyUpMessage.Key) {
				case 'W':
					CameraAcceleration.y += 2.f;
					break;
				case 'S':
					CameraAcceleration.y -= 2.f;
					break;
				case 'A':
					CameraAcceleration.x += 2.f;
					break;
				case 'D':
					CameraAcceleration.x -= 2.f;
					break;

				case 'G':
					if (GetAsyncKeyState(VK_SHIFT) & 0x8000) {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++)
							GameUnit::Goto_Async(SelectedUnits[i], TileAtMouseU);
					}
					else {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++) {
							SelectedUnits[i]->ForgetMission();
							GameUnit::Goto_Async(SelectedUnits[i], TileAtMouseU);
						}
					}
					break;
				case 'B':
					if (GetAsyncKeyState(VK_SHIFT) & 0x8000) {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++)
							SelectedUnits[i]->Build_Async(TileAtMouseU, GAME_TILE_TOMATO_GARDEN);
					}
					else {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++) {
							SelectedUnits[i]->ForgetMission();
							SelectedUnits[i]->Build_Async(TileAtMouseU, GAME_TILE_TOMATO_GARDEN);
						}
					}
					break;

				case 'K':
					if (GetAsyncKeyState(VK_SHIFT) & 0x8000) {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++)
							SelectedUnits[i]->KillTile_Async(TileAtMouseU);
					}
					else {
						for (unsigned int i = 0; i < SelectedUnits.size(); i++) {
							SelectedUnits[i]->ForgetMission();
							SelectedUnits[i]->KillTile_Async(TileAtMouseU);
						}
					}
					break;
				}
			}
			break;

			//////////////////
			// DRAW MESSAGE //
			//////////////////
		case grabMESSAGE_PAINT:
			WindowSize = Win.rt->GetSize();

			Win.rt->BeginDraw();
			Win.rt->Clear(D2D1::ColorF(0x0A9F5F3));

			switch (GameState)
			{
			case GameStateGameMenu:
				DrawLevel(&CurrentLevel, TilesetBitmap, &Win, CameraPosition);

				Win.DrawSprite(&MenuSprite);
				ButtonSelectLevel.Draw(&Win);
				ButtonLeaveTheGame.Draw(&Win);

				Win.rt->SetTransform(D2D1::Matrix3x2F::Identity());
				Win.rt->DrawTextW(
					AboutString, lstrlenW(AboutString), AncientFont, D2D1::RectF(330.f, 30.f, WindowSize.width - 10.f, WindowSize.height - 10.f),
					FontBrush
				);

				break;

			case GameStateSelectLevel:
				DrawLevel(&CurrentLevel, TilesetBitmap, &Win, CameraPosition);

				for (unsigned int i = 0; i < 3; i++) {
					LevelsButtons[i].Draw(&Win);
				}
				ButtonGoBack.Draw(&Win);

				Win.rt->SetTransform(D2D1::Matrix3x2F::Identity());
				Win.rt->DrawTextW(
					AboutString2, lstrlenW(AboutString2), AncientFont, D2D1::RectF(330.f, 30.f, WindowSize.width - 10.f, WindowSize.height - 10.f),
					FontBrush
				);

				break;

			case GameStateRTS:
				WinCenter = { WindowSize.width / 2, WindowSize.height / 2 };

				DrawLevel(&CurrentLevel, TilesetBitmap, &Win, CameraPosition);
				ScreenToTile(&CameraPosition, &TileAtMouse, &Message.CommonMessage.MousePos, (Vec2<float>*) & WinCenter);

				if (TileAtMouse.x > (float)CurrentLevel.Width - 1) TileAtMouse.x = CurrentLevel.Width - 1;
				if (TileAtMouse.y > (float)CurrentLevel.Height - 1) TileAtMouse.y = CurrentLevel.Height - 1;
				if (TileAtMouse.x < 0.f) TileAtMouse.x = 0.f;
				if (TileAtMouse.y < 0.f) TileAtMouse.y = 0.f;

				TileAtMouseU.x = (unsigned int)TileAtMouse.x;
				TileAtMouseU.y = (unsigned int)TileAtMouse.y;

				Vec2<float> WorldTile = { (float)((int)TileAtMouse.x), (float)((int)TileAtMouse.y) };
				D2D1_SIZE_F IsometricTile;
				WorldToIsometric(&WorldTile, (Vec2<float>*) & IsometricTile, &CameraPosition);
				Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f, WinCenter) * D2D1::Matrix3x2F::Translation(IsometricTile));
				Win.rt->DrawBitmap(SelectedTileBitmap, D2D1::RectF(0.f, 0.f, 64.f, 32.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1::RectF(0.f, 0.f, 64.f, 32.f));

				// DRAW UNITS
				bool AlreadySelected = false;
				for (unsigned int i = 0; i < Units.size(); i++) {

					D2D1_SIZE_F Tilepos;
					float x_offset = Units[i]->Class * 64.f;
					float y_offset = Units[i]->Team * 32.f;

					WorldToIsometric(&Units[i]->Pos, (Vec2<float>*) & Tilepos, &CameraPosition);
					Tilepos.height -= 16.f;

					Win.rt->SetTransform(
						D2D1::Matrix3x2F::Scale(2.f, 2.f, WinCenter)
						* D2D1::Matrix3x2F::Translation(Tilepos)
					);

					Win.rt->DrawBitmap(
						TomatoesBitmap, D2D1::RectF(0, 0, 64, 32), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
						D2D1::RectF(x_offset, y_offset, 64.f + x_offset, 32.f + y_offset)
					);

					// DrawHP Bar (GayBAAAAAAAAAAAR)
					{
						float HPCompMax = (float)(Units[i]->Health) / (float)(ClassHealth[Units[i]->Class]);
						FontBrush->SetColor(D2D1::ColorF(1 - HPCompMax, HPCompMax, 0, 0.8));
						Win.rt->DrawLine(D2D1::Point2F(32.f - HPCompMax * 8.f, -2), D2D1::Point2F(32.f + HPCompMax * 8.f, -2), FontBrush, 2.f);
						FontBrush->SetColor(D2D1::ColorF(0x0));
					}

					// Select units
					if (Units[i]->Team == GameTeamTomato) {
						Vec2<unsigned int> NewMousePos = { Message.CommonMessage.MousePos.x + (unsigned int)WinCenter.x - 32, Message.CommonMessage.MousePos.y + (unsigned int)WinCenter.y };

						// Check mouse position
						if (!AlreadySelected) {
							if ((NewMousePos.x > Tilepos.width) && (NewMousePos.y > Tilepos.height) && (NewMousePos.x < (Tilepos.width + 128 - 32)) && (NewMousePos.y < (Tilepos.height + 64))) {
								AlreadySelected = true;

								std::wstring DataStr = std::wstring(ClassNames[Units[i]->Class]) + L"\n" + std::to_wstring((unsigned int)Units[i]->Health) + L"HP";
								Win.rt->DrawTextW(DataStr.c_str(), DataStr.length(), SmallFont, D2D1::RectF(-10.f, -32.f, 74.f, 0.f), FontBrush);

								// Select units by click
								if (/*GetAsyncKeyState(VK_LBUTTON) & 0x0001*/ IsLeftMBUp) {
									if (GetAsyncKeyState(VK_CONTROL) & 0x8000) {
										bool found = false;
										for (unsigned int check = 0; check < SelectedUnits.size(); check++) {
											if (SelectedUnits[check] == Units[i]) {
												found = true;
												SelectedUnits[check]->SelectedByPlayer = false;
												SelectedUnits.erase(SelectedUnits.begin() + check);
												break;
											}
										}

										if (!found) {
											SelectedUnits.push_back(Units[i]);
											Units[i]->SelectedByPlayer = true;
										}
									}
									else {
										for (unsigned int del = 0; del < SelectedUnits.size(); del++)
											SelectedUnits[del]->SelectedByPlayer = false;

										SelectedUnits.resize(1);
										SelectedUnits[0] = Units[i];
										Units[i]->SelectedByPlayer = true;
									}
								}
							}
						}
					}

					if (Units[i]->SelectedByPlayer) {
						Win.rt->DrawBitmap(
							SelectedUnitBitmap, D2D1::RectF(0, -32, 64, 0), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
							D2D1::RectF(0.f, 0.f, 64.f, 32.f)
						);
					}
				}
				// DRAW UNITS END

				IsLeftMBUp = false;

				if (!isPaused) {
					ButtonPause.Draw(&Win);
				}
				else {
					ButtonPauseOff.Draw(&Win);
					ButtonToMenu.Draw(&Win);
				}

				if (CurrentWinningState == WINNING_STATE_YES) YouWonFun();
				else if (CurrentWinningState == WINNING_STATE_LOSE) YouLoseFun();

				//Draw Unit selection
				Win.rt->SetTransform(D2D1::Matrix3x2F::Identity());
				{
					GameClass prevClass = (GameClass)(CurrentClass - 1);
					GameClass nextClass = (GameClass)(CurrentClass + 1);
					if (prevClass < 0) prevClass = (GameClass)(prevClass + 4);
					if (nextClass > 3) nextClass = (GameClass)(nextClass - 4);
					Win.rt->DrawBitmap(CreateUnitBitmap, D2D1::RectF(10.f, WindowSize.height - 74.f, 74.f, WindowSize.height - 10.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1::RectF(prevClass * 32.f, 32.f, 32.f + prevClass * 32.f, 64.f));
					Win.rt->DrawBitmap(CreateUnitBitmap, D2D1::RectF(84.f, WindowSize.height - 74.f, 150.f, WindowSize.height - 10.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1::RectF(CurrentClass * 32.f, 0.f, 32.f + CurrentClass * 32.f, 32.f));
					Win.rt->DrawBitmap(CreateUnitBitmap, D2D1::RectF(160.f, WindowSize.height - 74.f, 225.f, WindowSize.height - 10.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1::RectF(nextClass * 32.f, 32.f, 32.f + nextClass * 32.f, 64.f));
				}

				// draw units missions
				for (unsigned int i = 0; i < Units.size(); i++) {
					if (Units[i]->Team == GameTeamTomato) Units[i]->DrawMyMissions(&Win, TilesetBitmap, &CameraPosition, (Vec2<float>*)&WinCenter);
				}

				// Draw King hp
				{
					//My
					Vec2<float> worldf = { (float)MyKingPos.x, (float)MyKingPos.y };
					D2D1_SIZE_F screen_pos;
					WorldToIsometric(&worldf, (Vec2<float>*)&screen_pos, &CameraPosition);
					Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f, WinCenter) * D2D1::Matrix3x2F::Translation(screen_pos));

					float HPCompMax = (float)(CurrentLevel.TileHealth[MyKingPos.x + MyKingPos.y * CurrentLevel.Width]) / (float)(TilesHealth[GAME_TILE_TOMATO_KING]);
					FontBrush->SetColor(D2D1::ColorF(1 - HPCompMax, HPCompMax, 0, 0.8));
					Win.rt->DrawLine(D2D1::Point2F(32.f - HPCompMax * 8.f, -2), D2D1::Point2F(32.f + HPCompMax * 8.f, -2), FontBrush, 2.f);
					FontBrush->SetColor(D2D1::ColorF(0x0));

					// Enemy
					worldf = { (float)EnemyKingPos.x, (float)EnemyKingPos.y };
					WorldToIsometric(&worldf, (Vec2<float>*)& screen_pos, &CameraPosition);
					Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(2.f, 2.f, WinCenter)* D2D1::Matrix3x2F::Translation(screen_pos));

					HPCompMax = (float)(CurrentLevel.TileHealth[EnemyKingPos.x + EnemyKingPos.y * CurrentLevel.Width]) / (float)(TilesHealth[GAME_TILE_CUCUMBER_KING]);
					FontBrush->SetColor(D2D1::ColorF(1 - HPCompMax, HPCompMax, 0, 0.8));
					Win.rt->DrawLine(D2D1::Point2F(32.f - HPCompMax * 8.f, -2), D2D1::Point2F(32.f + HPCompMax * 8.f, -2), FontBrush, 2.f);
					FontBrush->SetColor(D2D1::ColorF(0x0));
					
				}

				Win.rt->SetTransform(D2D1::Matrix3x2F::Identity());
				Win.rt->DrawTextW(HintString.c_str(), HintString.length(), AncientFont, D2D1::RectF(50.f, 10.f, 320.f, 20.f), FontBrush);
				// draw money
				Win.rt->DrawBitmap(MoneyBitmap, D2D1::RectF(WindowSize.width - 42.f, WindowSize.height - 42.f, WindowSize.width - 10.f, WindowSize.height - 10.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
				
				AncientFont->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
				DrawWinText(&Win, D2D1::RectF(0.f, WindowSize.height - 42.f, WindowSize.width - 52.f, WindowSize.height - 10.f), FontBrush, std::to_wstring(PlayerMoney), AncientFont);
				AncientFont->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);

				break;
			}

			ButtonMute.Position = { WindowSize.width - 40.f, 10.f };
			{
				float scaleVal = 2.f + static_cast<float>(ButtonMute.Hovered) * 0.1f;
				Win.rt->SetTransform(D2D1::Matrix3x2F::Scale(scaleVal, scaleVal) * D2D1::Matrix3x2F::Translation(*(D2D1_SIZE_F*)&ButtonMute.Position));
				Win.rt->DrawBitmap(
					ButtonMute.Bitmap, D2D1::RectF(0.f, 0.f, 16.f, 16.f), 1.f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
					{ !MainTheme->isPlaying() * 16.f, 0.f, !MainTheme->isPlaying() * 16.f + 16.f, 16.f }
				);
			}

			Win.rt->EndDraw();
			break;

			///////////
			// TIMER //
			///////////
		case grabMESSAGE_TIMER:
			ButtonMute.HoverCheck(&Message.CommonMessage.MousePos);

			switch (Message.TimerMessage.ID) {
				// TIMER MENU CAMERA UPDATE
			case TIMER_CAMERA_UPDATE:
				InvalidateRect(Win.hWnd, NULL, false);

				if (GameState == GameStateGameMenu || GameState == GameStateSelectLevel) {
					MenuCameraAngle += 0.003f;
					while (MenuCameraAngle > 360.f) MenuCameraAngle -= 360.f;

					CameraPosition = { -570.f + cos(MenuCameraAngle) * 640.f, 30.f + sin(MenuCameraAngle) * 480.f };
				}

				if (GameState == GameStateGameMenu) {
					ButtonSelectLevel.HoverCheck(&Message.CommonMessage.MousePos);
					ButtonLeaveTheGame.HoverCheck(&Message.CommonMessage.MousePos);
				}

				if (GameState == GameStateSelectLevel) {
					for (unsigned int i = 0; i < 3; i++)
						LevelsButtons[i].HoverCheck(&Message.CommonMessage.MousePos);
					ButtonGoBack.HoverCheck(&Message.CommonMessage.MousePos);
				}

				if (GameState == GameStateRTS) {
					if (!isPaused) {
						CameraDirection.x += CameraAcceleration.x;
						CameraDirection.y += CameraAcceleration.y;

						CameraDirection.x *= 0.9f;
						CameraDirection.y *= 0.9f;

						CameraPosition.x += CameraDirection.x;
						CameraPosition.y += CameraDirection.y;

						ButtonPause.HoverCheck(&Message.CommonMessage.MousePos);

						std::thread thr(UpdateUnits_Async, &Units);
						thr.detach();

						if (TomatoKingHP) {
							if ((*TomatoKingHP) == 0) {
								CurrentWinningState = WINNING_STATE_LOSE;
								YouLoseSound->play();
								TomatoKingHP = NULL; // а как
							}
							else if ((*CucumberKingHP) == 0) {
								CurrentWinningState = WINNING_STATE_YES;
								YouWinSound->play();
								TomatoKingHP = NULL; // а как х2
							}
						}

						// Update money
						MoneyTimer++;
						if ((MoneyTimer == 128) && (PlayerMoney < 9999)) {
							PlayerMoney++;
							EnemyMoney += 4;
						}
					}
					else {
						ButtonPauseOff.HoverCheck(&Message.CommonMessage.MousePos);
						ButtonToMenu.HoverCheck(&Message.CommonMessage.MousePos);
					}
				}
				break; // tmr camera end

			// TIMER AI UPDATE (EVERY sec)
			case TIMER_AI_UPDATE:
			{
				if (GameState != GameStateRTS) break;

				{	// Create new units
					unsigned int cucumber_fighters = GetFightersCount(GameTeamCucumber);

					{	// Create new medics
						unsigned int medics_total = GetUnitsCount(GameTeamCucumber, GameClassMedic);

						if (medics_total * 3 < cucumber_fighters) {
							Vec2<unsigned int> garden = GetRandomTile(GAME_TILE_CUCUMBER_GARDEN);

							if (garden.x != -1) {
								if (EnemyMoney >= ClassCost[GameClassMedic]) {
									std::thread thr(SpawnInTheGarden, garden, GameClassMedic, GameTeamCucumber, &CurrentLevel);
									thr.detach();
									EnemyMoney -= ClassCost[GameClassMedic];
								}
							}
						}
					}

					{	// Create new builders
						unsigned int builders_total = GetUnitsCount(GameTeamCucumber, GameClassBuilder);

						if (builders_total == 0) {
							if (EnemyMoney >= ClassCost[GameClassBuilder]) {
								std::thread thr(SpawnInTheGarden, Vec2<unsigned int> { EnemyKingPos.x - 1, EnemyKingPos.y } , GameClassBuilder, GameTeamCucumber, & CurrentLevel);
								thr.detach();
								EnemyMoney -= ClassCost[GameClassBuilder];
							}
						}
					}

					{	// Create new fighters
						unsigned int tomato_fighters = GetFightersCount(GameTeamTomato);

						
						Vec2<unsigned int> garden = GetRandomTile(GAME_TILE_CUCUMBER_GARDEN);
						if (garden.x != -1) {
							GameClass class_to_create = (GameClass)((rand() % 2) + 1);
							std::thread thr;

							if (class_to_create == GameClassHeavy) {
								if (EnemyMoney >= ClassCost[GameClassHeavy]) {
									thr = std::thread(SpawnInTheGarden, garden, GameClassHeavy, GameTeamCucumber, &CurrentLevel);
									thr.detach();
									EnemyMoney -= ClassCost[GameClassHeavy];
								}
							}

							if (class_to_create == GameClassSoldier) {
								if (EnemyMoney >= ClassCost[GameClassSoldier]) {
									thr = std::thread(SpawnInTheGarden, garden, GameClassSoldier, GameTeamCucumber, &CurrentLevel);
									thr.detach();
									EnemyMoney -= ClassCost[GameClassSoldier];
								}
							}
						}
						
					}
				}

				if ((GetFightersCount(GameTeamCucumber) / 2) >= GetFightersCount(GameTeamTomato)) { // kill tomato king
					for (unsigned int i = 0; i < Units.size(); i++) {
						if (Units[i]->Team == GameTeamCucumber) {
							Units[i]->ForgetMission();
							GameUnit::KillTile(Units[i], MyKingPos);
						}
					}
				}
			}
				for (unsigned int i = 0; i < Units.size(); i++) {
					// find free cucumber
					GameUnit* curr_unit = Units[i];
					if (curr_unit->Team == GameTeamCucumber && curr_unit->Mission.size() == 0) {
						// check type
						if (curr_unit->Class == GameClassMedic) { // med
							float dist;
							GameUnit* fighter = GetNearestMyFighter(curr_unit, &dist);

							if (dist > ClassRange[GameClassMedic]) {
								GameUnit::Goto(curr_unit, { (unsigned int)fighter->Pos.x, (unsigned int)fighter->Pos.y });
								curr_unit->Mission[curr_unit->Mission.size() - 1].Basic.Type = MISSION_TYPE_GOTO_BUILDING;
							}
						}
						else if (curr_unit->Class == GameClassBuilder) { // builder
							unsigned int gardens = GetTilesCount(GAME_TILE_CUCUMBER_GARDEN);
							unsigned int enemies = GetFightersCount(GameTeamTomato);

							if ((unsigned int)((float)gardens * 1.5f) < enemies) { // build garden
								Vec2<unsigned int> posU = { (unsigned int)curr_unit->Pos.x, (unsigned int)curr_unit->Pos.y };
								Vec2<unsigned int> nearest;
								GetNearestTile(&posU, &nearest, GAME_TILE_GRASS, &CurrentLevel);
								GameUnit::Build(curr_unit, nearest, GAME_TILE_CUCUMBER_GARDEN);
							}
						}
						else { // fighters
							GameUnit* fighter = GetNearestEnemy(curr_unit);
							GameUnit::Goto(curr_unit, { (unsigned int)fighter->Pos.x, (unsigned int)fighter->Pos.y });
							*((DWORD*)&curr_unit->Mission[curr_unit->Mission.size() - 1].Basic.Type) |= (DWORD)MISSION_TYPE_KILL;
						}
					}
				}
				break;
			}
			break;

		case grabMESSAGE_MOUSE_BUTTON_UP:
			if (ButtonMute.Hovered) {
				ButtonSound->setPosition(0);
				ButtonSound->play();

				if (MainTheme->isPlaying()) {
					MainTheme->stop();
				}
				else {
					MainTheme->play();
				}
			}

			switch (GameState)
			{
			case GameStateGameMenu:
				if (ButtonSelectLevel.Hovered) {
					GameState = GameStateSelectLevel;
					ButtonSound->setPosition(0);
					ButtonSound->play();
				}

				else if (ButtonLeaveTheGame.Hovered) {
					Win.IsOpened = false;
					ButtonSound->setPosition(0);
					ButtonSound->play();
					while (ButtonSound->isPlaying()) {}
					PostQuitMessage(0);
				}
				break;

			case GameStateRTS:
				IsLeftMBUp = true;
				if (isPaused) {
					if (ButtonPauseOff.Hovered) {
						ButtonSound->setPosition(0);
						ButtonSound->play();
						isPaused = false;
					}

					if (ButtonToMenu.Hovered) {
						LoadLevel("Levels/menu.lvl", &CurrentLevel);
						ButtonSound->setPosition(0);
						ButtonSound->play();
						GameState = GameStateGameMenu;
					}
				}
				else {
					if (ButtonPause.Hovered) {
						isPaused = true;
						ButtonSound->setPosition(0);
						ButtonSound->play();
					}
				}
				break;

			case GameStateSelectLevel:
				if (ButtonGoBack.Hovered) {
					GameState = GameStateGameMenu;
					ButtonSound->setPosition(0);
					ButtonSound->play();
				}

				for (unsigned int i = 0; i < 3; i++) {
					if (LevelsButtons[i].Hovered) {
						////////// LOAD LEVEL ///////////
						LoadLevel(("Levels/" + std::to_string(i) + ".lvl").c_str(), &CurrentLevel);
						GameState = GameStateRTS;
						PlayerMoney = 10;
						CurrentWinningState = WINNING_STATE_NOTHING;

						isPaused = false;

						// Set camera pos
						int KingIndex = FindBuilding(&CurrentLevel, GAME_TILE_TOMATO_KING);
						Units.resize(0);

						if (KingIndex == -1) CameraPosition = { 0.f, 0.f };
						else {
							TomatoKingHP = &CurrentLevel.TileHealth[KingIndex];
							MyKingPos.y = KingIndex % CurrentLevel.Width;
							MyKingPos.x = KingIndex - MyKingPos.y * CurrentLevel.Width;

							AddFirstUnits(&Units, &CurrentLevel, &MyKingPos, GameTeamTomato);
							CameraPosition = { (MyKingPos.x - MyKingPos.y) * 64 - WindowSize.width + 64, (MyKingPos.x + MyKingPos.y) * 32 - WindowSize.height + 32 };

							KingIndex = FindBuilding(&CurrentLevel, GAME_TILE_CUCUMBER_KING);

							if (KingIndex != -1) {
								EnemyKingPos.y = KingIndex % CurrentLevel.Width;
								EnemyKingPos.x = KingIndex - EnemyKingPos.y * CurrentLevel.Width;
								CucumberKingHP = &CurrentLevel.TileHealth[KingIndex];
								AddFirstUnits(&Units, &CurrentLevel, &EnemyKingPos, GameTeamCucumber);
							}
						}
						
						CameraDirection = { 0.f, 0.f };
						CameraAcceleration = { 0.f, 0.f };

						ButtonSound->setPosition(0);
						ButtonSound->play();
						break;
					}
				}
				break;
			}
			break;
		}
	}

	MenuBitmap->Release();
	TilesetBitmap->Release();

	return 0;
}