//

#include "kosSyst.h"
#include "KosFile.h"
#include "gfxdef.h"
#include "gameWnd.h"
#include "mcarray.h"
#include "lang.h"

//
#define EMPTY_PLACE				0

#define LEVEL_PROGRESS_LEFT		(132+1)
#define LEVEL_PROGRESS_TOP		(388+21)
#define LEVEL_PROGRESS_HEIGHT	4
#define LEVEL_PROGRESS_WIDTH	329
#define LEVEL_PROGRESS_COLOUR	0xDF2933

#define BONUS_SOMEBLOCK_TOP		(120+21)
#define BONUS_SOMEBLOCK_LEFT	(46+1)
#define BONUS_FREEBLOCK_TOP		(163+21)
#define BONUS_FREEBLOCK_LEFT	(9+1)
#define BONUS_DIAGBLOCK_TOP		(213+21)
#define BONUS_DIAGBLOCK_LEFT	(48+1)
#define BONUS_PROGRESS_HEIGHT	37
#define BONUS_PROGRESS_WIDTH	4

#define LEVEL_SCORE_BASE		50

//
CKosBitmap gameFace;
CKosBitmap gameBlocks;
CKosBitmap gameNumbers;
CKosBitmap gameBlocksZ[4];
//
CFishka *fishki[blocksNum];

//
char *gameWndTitle;
#if LANG == RUS
char gameWndTitle1[] = "” à ®­ ¦¤ñâ ⥡ï :)";
char gameWndTitle2[] = "ã ¢®â...";
char gameOverText[] = "„ «ìè¥ ¤®à®£¨ ­¥â!";
#else
char gameWndTitle1[] = "Pharaoh waits for you :)";
char gameWndTitle2[] = "Well...";
char gameOverText[] = "No further way!";
#endif
//
Word gcx, gcy;
//
#define mapSize			8
//
//
#define blocksLeft		(134+2)
#define blocksTop		(43+22)
//
#define blockTypesNum	10
//
Byte gameMap[mapSize * mapSize];
// óðîâåíü
int gameLevel;
//
int maxGameLevel;
int startGameLevel;
// ñ÷¸ò
Dword playerScore;
// ñ÷¸ò÷èê äîáàâëåííûõ áëîêîâ
int insertedBlocksCount;
// êîëè÷åñòâî áëîêîâ äëÿ ïåðåõîäà íà ñëåäóþùèé óðîâåíü
int levelBlocksLimit;
// íîìåð âûäåëåííîãî áëîêà
int selectedBlock;
// ïðèáàâëåíèå øàãà äëÿ èíäèêàòîðà ïðîõîæäåíèÿ óðîâíÿ * 1024
int levelProgressStep;
// çàí÷åíèå èíäèêàòîðà ïðîõîæäåíèÿ óðîâíÿ
int levelProgressCount;
// áëîêè äëÿ óäàëåíèÿ
struct sRemoveBlock
{
	short int ndx;
	short int value;
	sRemoveBlock()
	{
		this->ndx = 0;
		this->value = 0;
	};
	sRemoveBlock( int pNdx, int pValue )
	{
		this->ndx = pNdx;
		this->value = pValue;
	};
};
bool operator == ( const sRemoveBlock &b1, const sRemoveBlock &b2 )
{
	return b1.ndx == b2.ndx;
};
MCArray<sRemoveBlock> toRemoveList;
// ïàäàþùèå áëîêè
MCArray<int> toFallList;
//
bool mouseButtonDisabled;
//
bool freezeBonuses = false;
//
int bonusSomeBlockProgressStep;
int bonusSomeBlockProgressCount;
//
int bonusFreeBlockProgressStep;
int bonusFreeBlockProgressCount;
//
int bonusDiagBlockProgressStep;
int bonusDiagBlockProgressCount;



#define SELECTED_BLOCK_NONE			-1

// áîíóñ âêëþ÷åí
int bonusSomeBlock;
// ñ÷¸ò÷èê áëîêîâ äëÿ áîíóñà
int bonusSomeBlockCount;
// êîëè÷åñòâî áëîêîâ, êîòîðîå íàäî óáðàòü, ÷òîáû ïîëó÷èòü áîíóñ
int bonusSomeBlockEdge;
#define BONUS_SOMEBLOCK_SELECTED	-2
//
bool bonusFreeBlock;
int bonusFreeBlockCount;
int bonusFreeBlockEdge;
#define BONUS_FREEBLOCK_SELECTED	-3
//
int bonusDiagBlock;
int bonusDiagBlockCount;
int bonusDiagBlockEdge;
#define BONUS_DIAGBLOCK_SELECTED	-4
//
void drawScore();
// îòîáðàçèòü áëîêè áîíóñîâ
void drawBonuses();
//
void drawLevelNum();
//
void fadeOutBlocks();
//
void fallDownBlocks();


void ClearGameMeters()
{
	// èíäèêàòîð ïðîõîæäåíèå óðîâíÿ
	kos_DrawBar(
		LEVEL_PROGRESS_LEFT,
		LEVEL_PROGRESS_TOP,
		LEVEL_PROGRESS_WIDTH,
		LEVEL_PROGRESS_HEIGHT,
		0
		);
	// èíäèêàòîðû áîíóñîâ
	// some
	kos_DrawBar(
		33+1,
		118+21,
		BONUS_PROGRESS_WIDTH,
		BONUS_PROGRESS_HEIGHT,
		0
		);
	// free
	kos_DrawBar(
		58+1,
		166+21,
		BONUS_PROGRESS_WIDTH,
		BONUS_PROGRESS_HEIGHT,
		0
		);
	// diag
	kos_DrawBar(
		37+1,
		213+21,
		BONUS_PROGRESS_WIDTH,
		BONUS_PROGRESS_HEIGHT,
		0
		);
}


//
void drawGameMeters()
{
	//
	ClearGameMeters();
	// èíäèêàòîð ïðîõîæäåíèå óðîâíÿ
	kos_DrawBar(
		LEVEL_PROGRESS_LEFT,
		LEVEL_PROGRESS_TOP,
		levelProgressCount,
		LEVEL_PROGRESS_HEIGHT,
		LEVEL_PROGRESS_COLOUR
		);
	//
	if ( bonusSomeBlockProgressCount > 0 )
	// some
	kos_DrawBar(
		33+1,
		118+21+BONUS_PROGRESS_HEIGHT-bonusSomeBlockProgressCount,
		BONUS_PROGRESS_WIDTH,
		bonusSomeBlockProgressCount,
		LEVEL_PROGRESS_COLOUR
		);
	//
	if ( bonusFreeBlockProgressCount > 0 )
	// free
	kos_DrawBar(
		58+1,
		166+21+BONUS_PROGRESS_HEIGHT-bonusFreeBlockProgressCount,
		BONUS_PROGRESS_WIDTH,
		bonusFreeBlockProgressCount,
		LEVEL_PROGRESS_COLOUR
		);
	//
	if ( bonusDiagBlockProgressCount > 0 )
	// diag
	kos_DrawBar(
		37+1,
		213+21+BONUS_PROGRESS_HEIGHT-bonusDiagBlockProgressCount,
		BONUS_PROGRESS_WIDTH,
		bonusDiagBlockProgressCount,
		LEVEL_PROGRESS_COLOUR
		);
}


////////////////////////////////////////////////////////////////////////////////
void SetLevelVariables()
{
	selectedBlock = -1;
	levelBlocksLimit = ( gameLevel > 5 ) ? LEVEL_SCORE_BASE * ( gameLevel + 1 ) : LEVEL_SCORE_BASE * ( 11 - gameLevel );
	insertedBlocksCount = 0;
	//
	levelProgressCount = 0;
	levelProgressStep = ( LEVEL_PROGRESS_WIDTH * 1024 ) / levelBlocksLimit;
	//
	bonusSomeBlockEdge = levelBlocksLimit / 4;
	bonusFreeBlockEdge = levelBlocksLimit / 3;
	bonusDiagBlockEdge = levelBlocksLimit / 2;
	//
	bonusSomeBlockProgressStep = ( BONUS_PROGRESS_HEIGHT * 1024 ) / bonusSomeBlockEdge;
	bonusSomeBlockProgressCount = ( ( ( bonusSomeBlockCount > bonusSomeBlockEdge ) ? bonusSomeBlockEdge : bonusSomeBlockCount ) * bonusSomeBlockProgressStep ) / 1024;
	//
	bonusFreeBlockProgressStep = ( BONUS_PROGRESS_HEIGHT * 1024 ) / bonusFreeBlockEdge;
	bonusFreeBlockProgressCount = ( ( ( bonusFreeBlockCount > bonusFreeBlockEdge ) ? bonusFreeBlockEdge : bonusFreeBlockCount ) * bonusFreeBlockProgressStep ) / 1024;
	//
	bonusDiagBlockProgressStep = ( BONUS_PROGRESS_HEIGHT * 1024 ) / bonusDiagBlockEdge;
	bonusDiagBlockProgressCount = ( ( ( bonusDiagBlockCount > bonusDiagBlockEdge ) ? bonusDiagBlockEdge : bonusDiagBlockCount ) * bonusDiagBlockProgressStep ) / 1024;
}


//
int GetScoreByBlocks( int blocksCount )
{
	int limit, update, i, j, acc;

	//
	if ( blocksCount < 3 ) return 0;
	//
	limit = 20 * blocksNum * gameLevel;
	//
	update = gameLevel;
	acc = 1;
	//
	j = blocksCount - 3;
	//
	for ( i = 0; i < j; i++ )
	{
		//
		acc *= gameLevel + 1;
		//
		if ( ( update + acc ) > limit ) return limit;
	}
	//
	return update + acc;
}



//
int insertNewBlocks()
{
	int i, j, k, ndx, result, btn;

	//
	toFallList.Clear();
	//
	result = 0;
	//
	btn = gameLevel > 5 ? blockTypesNum : 5 + gameLevel;
	//
	ndx = ( mapSize * mapSize ) - mapSize;
	//
	for ( i = 0; i < mapSize; i++ )
	{
		for ( j = 0; j < mapSize; j++ )
		{
			//
			k = ndx + i - ( j * mapSize );
			//
			if ( gameMap[k] == EMPTY_PLACE )
			{
				//
				for ( ; j < (mapSize-1); j++ )
				{
					//
					gameMap[k] = gameMap[k-mapSize];
					toFallList.AddExclusive( k );
					k -= mapSize;
				}
				//
				gameMap[i] = ( rtlRand() % btn ) + 1;
				toFallList.AddExclusive( i );
				//
				result++;
				//
				break;
			}
		}
	}
	//
	insertedBlocksCount += result;
	//
	return result;
}


// ïîèñê öåïî÷åê áëîêîâ äëÿ óäàëåíèÿ, óäàëåíèå áëîêîâ
// âîçâðàùàåò ïðèðàùåíèå ñ÷¸òà èãðîêà
int findBlockLines()
{
	int x, y, ndx;
	int removeCount = 0;

	// î÷èñòèì ñïèñîê äëÿ çàïèñè ðåçóëüòàòîâ
	toRemoveList.Clear();
	//
	for ( y = 0; y < mapSize; y++ )
	{
		//
		for ( x = 0; x < mapSize; x++ )
		{
			//
			ndx = x + ( y * mapSize );
			//
			if ( gameMap[ndx] == EMPTY_PLACE ) continue;
			// ïðîâåðÿåì ãîðèçîíòàëüíóþ öåïî÷êó
			if ( x < (mapSize - 2) )
			{
				//
				if (
					( gameMap[ndx] == gameMap[ndx+1]  &&  gameMap[ndx] == gameMap[ndx+2] )
					|| ( BONUS_FREE_BLOCK == gameMap[ndx]  &&  gameMap[ndx+1] == gameMap[ndx+2] )
					|| ( gameMap[ndx] == gameMap[ndx+1]  &&  BONUS_FREE_BLOCK == gameMap[ndx+2] )
					|| ( gameMap[ndx] == gameMap[ndx+2]  &&  BONUS_FREE_BLOCK == gameMap[ndx+1] )
					)
				{
					// íàøëè öåïî÷êó, çàïîìíèì
					toRemoveList.AddExclusive( sRemoveBlock( ndx, gameMap[ndx] ) );
					toRemoveList.AddExclusive( sRemoveBlock( ndx+1, gameMap[ndx+1] ) );
					toRemoveList.AddExclusive( sRemoveBlock( ndx+2, gameMap[ndx+2] ) );
				}
			}
			// ïðîâåðÿåì âåðòèêàëüíóþ öåïî÷êó
			if ( y < (mapSize - 2) )
			{
				//
				if (
					( gameMap[ndx] == gameMap[ndx+mapSize]  &&  gameMap[ndx] == gameMap[ndx+mapSize+mapSize] )
					|| ( BONUS_FREE_BLOCK == gameMap[ndx]  &&  gameMap[ndx+mapSize] == gameMap[ndx+mapSize+mapSize] )
					|| ( gameMap[ndx] == gameMap[ndx+mapSize]  &&  BONUS_FREE_BLOCK == gameMap[ndx+mapSize+mapSize] )
					|| ( gameMap[ndx] == gameMap[ndx+mapSize+mapSize]  &&  BONUS_FREE_BLOCK == gameMap[ndx+mapSize] )
					)
				{
					// íàøëè öåïî÷êó, çàïîìíèì
					toRemoveList.AddExclusive( sRemoveBlock( ndx, gameMap[ndx] ) );
					toRemoveList.AddExclusive( sRemoveBlock( ndx+mapSize, gameMap[ndx+mapSize] ) );
					toRemoveList.AddExclusive( sRemoveBlock( ndx+mapSize+mapSize, gameMap[ndx+mapSize+mapSize] ) );
				}
			}
		}
	}
	//
	return toRemoveList.GetCount();
}


// ïðîâåðêà íà íåâîçìîæíîñòü ñîáðàòü êàêóþ-íèáóäü ëèíèþ
bool checkGameOut()
{
	int x, y, ndx;

	//
	ndx = 0;
	//
	for ( y = 0; y < mapSize; y++ )
	{
		//
		for ( x = 0; x < mapSize; x++ )
		{
			// ïðîâåðÿåì ãîðèçîíòàëüíûå öåïî÷êè èç äâóõ ñèìâîëîâ
			if ( x < (mapSize - 1) )
			{
				//
				if ( gameMap[ndx] == gameMap[ndx+1] )
				{
					// íàøëè öåïî÷êó èç äâóõ áëîêîâ
					// ïðîâåðêà áîíóñîâ
					if ( bonusSomeBlock == gameMap[ndx] ) return false;
					if ( bonusFreeBlock ) return false;
					// ïðîâåðêà îáû÷íûõ ïåðåñòàíîâîê
					if ( y > 0 )
					{
						//
						if ( x > 0 )
						{
							//
							if ( gameMap[ndx-mapSize-1] == gameMap[ndx] ) return false;
						}
						//
						if ( x < (mapSize - 2) )
						{
							//
							if ( gameMap[ndx-mapSize+2] == gameMap[ndx] ) return false;
						}
					}
					//
					if ( x > 1 )
					{
						//
						if ( gameMap[ndx-2] == gameMap[ndx] ) return false;
					}
					//
					if ( x < (mapSize - 3) )
					{
						//
						if ( gameMap[ndx+3] == gameMap[ndx] ) return false;
					}
					//
					if ( y < (mapSize - 1) )
					{
						//
						if ( x > 0 )
						{
							//
							if ( gameMap[ndx+mapSize-1] == gameMap[ndx] ) return false;
						}
						//
						if ( x < (mapSize - 2) )
						{
							//
							if ( gameMap[ndx+mapSize+2] == gameMap[ndx] ) return false;
						}
					}
					// ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
					if ( bonusDiagBlock > 0 )
					{
						//
						if ( y > 0 )
						{
							//
							if ( x > 1 )
							{
								//
								if ( gameMap[ndx-mapSize-2] == gameMap[ndx] ) return false;
							}
							//
							if ( gameMap[ndx-mapSize] == gameMap[ndx] ) return false;
							//
							if ( x < (mapSize - 2) )
							{
								//
								if ( gameMap[ndx-mapSize+1] == gameMap[ndx] ) return false;
							}
							//
							if ( x < (mapSize - 3) )
							{
								//
								if ( gameMap[ndx-mapSize+3] == gameMap[ndx] ) return false;
							}
						}
						//
						if ( y < (mapSize - 1) )
						{
							//
							if ( x > 1 )
							{
								//
								if ( gameMap[ndx+mapSize-2] == gameMap[ndx] ) return false;
							}
							//
							if ( gameMap[ndx+mapSize] == gameMap[ndx] ) return false;
							//
							if ( x < (mapSize - 2) )
							{
								//
								if ( gameMap[ndx+mapSize+1] == gameMap[ndx] ) return false;
							}
							//
							if ( x < (mapSize - 3) )
							{
								//
								if ( gameMap[ndx+mapSize+3] == gameMap[ndx] ) return false;
							}
						}
					} // çàêîí÷åíà ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
				}
			}
			// ïðîâåðÿåì ãîðèçîíòàëüíûå öåïî÷êè èç äâóõ áëîêîâ ñ ïðîìåæóòêîì
			if ( x < (mapSize - 2) )
			{
				//
				if ( gameMap[ndx] == gameMap[ndx+2] )
				{
					// íàøëè äâà áëîêà ñ ïðîìåæóòêîì
					// ïðîâåðêà áîíóñîâ
					if ( bonusSomeBlock == gameMap[ndx] ) return false;
					if ( bonusFreeBlock ) return false;
					//
					if ( y > 0 )
					{
						//
						if ( gameMap[ndx-mapSize+1] == gameMap[ndx] ) return false;
					}
					//
					if ( y < (mapSize-1) )
					{
						//
						if ( gameMap[ndx+mapSize+1] == gameMap[ndx] ) return false;
					}
					// ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
					if ( bonusDiagBlock > 0 )
					{
						//
						if ( y > 0 )
						{
							//
							if ( gameMap[ndx-mapSize] == gameMap[ndx] ) return false;
							//
							if ( gameMap[ndx-mapSize+2] == gameMap[ndx] ) return false;
						}
						//
						if ( y < (mapSize-1) )
						{
							//
							if ( gameMap[ndx+mapSize] == gameMap[ndx] ) return false;
							//
							if ( gameMap[ndx+mapSize+2] == gameMap[ndx] ) return false;
						}
					}
				}
			}
			// ïðîâåðÿåì âåðòèêàëüíûå öåïî÷êè èç äâóõ ñèìâîëîâ
			if ( y < (mapSize - 1) )
			{
				//
				if ( gameMap[ndx] == gameMap[ndx+mapSize] )
				{
					// íàøëè öåïî÷êó èç äâóõ áëîêîâ
					// ïðîâåðêà áîíóñîâ
					if ( bonusSomeBlock == gameMap[ndx] ) return false;
					if ( bonusFreeBlock ) return false;
					//
					if ( x > 0 )
					{
						//
						if ( y > 0 )
						{
							//
							if ( gameMap[ndx-1-mapSize] == gameMap[ndx] ) return false;
						}
						//
						if ( y < (mapSize - 2) )
						{
							//
							if ( gameMap[ndx-1+(2*mapSize)] == gameMap[ndx] ) return false;
						}
					}
					//
					if ( y > 1 )
					{
						//
						if ( gameMap[ndx-(2*mapSize)] == gameMap[ndx] ) return false;
					}
					//
					if ( y < (mapSize - 3) )
					{
						//
						if ( gameMap[ndx+(3*mapSize)] == gameMap[ndx] ) return false;
					}
					//
					if ( x < (mapSize - 1) )
					{
						//
						if ( y > 0 )
						{
							//
							if ( gameMap[ndx+1-mapSize] == gameMap[ndx] ) return false;
						}
						//
						if ( y < (mapSize - 2) )
						{
							//
							if ( gameMap[ndx+1+(2*mapSize)] == gameMap[ndx] ) return false;
						}
					}
					// ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
					if ( bonusDiagBlock > 0 )
					{
						//
						if ( x > 0 )
						{
							//
							if ( y > 1 )
							{
								//
								if ( gameMap[ndx-1-(2*mapSize)] == gameMap[ndx] ) return false;
							}
							//
							if ( gameMap[ndx-1] == gameMap[ndx] ) return false;
							//
							if ( y < (mapSize - 2) )
							{
								//
								if ( gameMap[ndx-1+mapSize] == gameMap[ndx] ) return false;
							}
							//
							if ( y < (mapSize - 3) )
							{
								//
								if ( gameMap[ndx-1+(3*mapSize)] == gameMap[ndx] ) return false;
							}
						}
						//
						if ( x < (mapSize - 1) )
						{
							//
							if ( y > 1 )
							{
								//
								if ( gameMap[ndx+1-(2*mapSize)] == gameMap[ndx] ) return false;
							}
							//
							if ( gameMap[ndx+1] == gameMap[ndx] ) return false;
							//
							if ( y < (mapSize - 2) )
							{
								//
								if ( gameMap[ndx+1+mapSize] == gameMap[ndx] ) return false;
							}
							//
							if ( y < (mapSize - 3) )
							{
								//
								if ( gameMap[ndx+1+(3*mapSize)] == gameMap[ndx] ) return false;
							}
						}
					} // çàêîí÷åíà ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
				}
			}
			// ïðîâåðÿåì âåðòèêàëüíûå öåïî÷êè èç äâóõ áëîêîâ ñ ïðîìåæóòêîì
			if ( y < (mapSize - 2) )
			{
				//
				if ( gameMap[ndx] == gameMap[ndx+(2*mapSize)] )
				{
					// íàøëè äâà áëîêà ñ ïðîìåæóòêîì
					// ïðîâåðêà áîíóñîâ
					if ( bonusSomeBlock == gameMap[ndx] ) return false;
					if ( bonusFreeBlock ) return false;
					//
					if ( x > 0 )
					{
						//
						if ( gameMap[ndx-1+mapSize] == gameMap[ndx] ) return false;
					}
					//
					if ( x < (mapSize-1) )
					{
						//
						if ( gameMap[ndx+1+mapSize] == gameMap[ndx] ) return false;
					}
					// ïðîâåðêà äèàãîíàëüíûõ ïåðåñòàíîâîê
					if ( bonusDiagBlock > 0 )
					{
						//
						if ( x > 0 )
						{
							//
							if ( gameMap[ndx-1] == gameMap[ndx] ) return false;
							//
							if ( gameMap[ndx-1+(2*mapSize)] == gameMap[ndx] ) return false;
						}
						//
						if ( x < (mapSize-1) )
						{
							//
							if ( gameMap[ndx+1] == gameMap[ndx] ) return false;
							//
							if ( gameMap[ndx+1+(2*mapSize)] == gameMap[ndx] ) return false;
						}
					}
				}
			}
			//
			ndx++;
		}
	}
	//
	gameWndTitle = gameWndTitle2;
	//
	return true;
}


//
bool exterminateLines()
{
	int deletedBlocks, btn, i, j;
	bool needRedrawBonus = false;

	//
	btn = gameLevel > 5 ? blockTypesNum : 5 + gameLevel;
	//
	playerScore += GetScoreByBlocks( deletedBlocks = findBlockLines() );
	//
	if ( ! freezeBonuses )
	{
		//
		if ( gameLevel >= 2 && bonusSomeBlock <= 0 ) bonusSomeBlockCount += deletedBlocks;
		if ( gameLevel >= 3 && !bonusFreeBlock ) bonusFreeBlockCount += deletedBlocks;
		if ( gameLevel >= 4 && bonusDiagBlock <= 0 ) bonusDiagBlockCount += deletedBlocks;
		// ïåðâûé áîíóñ
		if (
			( bonusSomeBlockCount >= bonusSomeBlockEdge || ( gameLevel >= 2 && deletedBlocks >= 4 ) )
			&& bonusSomeBlock <= 0
			)
		{
			bonusSomeBlock = ( rtlRand() % btn ) + 1;
			needRedrawBonus = true;
			if ( bonusSomeBlockCount >= bonusSomeBlockEdge ) bonusSomeBlockCount = 0;
		}
		if ( bonusSomeBlockCount >= bonusSomeBlockEdge ) bonusSomeBlockCount = 0;
		// âòîðîé áîíóñ
		if ( bonusFreeBlockCount >= bonusFreeBlockEdge || ( gameLevel >=3 && deletedBlocks >= 5 ) )
		{
			bonusFreeBlock = true;
			needRedrawBonus = true;
			if ( bonusFreeBlockCount >= bonusFreeBlockEdge ) bonusFreeBlockCount = 0;
		}
		if ( bonusFreeBlockCount >= bonusFreeBlockEdge ) bonusFreeBlockCount = 0;
		// òðåòèé áîíóñ
		if ( bonusDiagBlockCount >= bonusDiagBlockEdge || ( gameLevel >= 4 && deletedBlocks >= 6 ) )
		{
			bonusDiagBlock = 3;
			needRedrawBonus = true;
			if ( bonusDiagBlockCount >= bonusDiagBlockEdge ) bonusDiagBlockCount = 0;
		}
		if ( bonusDiagBlockCount >= bonusDiagBlockEdge ) bonusDiagBlockCount = 0;
		//
		bonusSomeBlockProgressCount = ( ( ( bonusSomeBlockCount > bonusSomeBlockEdge ) ? bonusSomeBlockEdge : bonusSomeBlockCount ) * bonusSomeBlockProgressStep ) / 1024;
		//
		bonusFreeBlockProgressCount = ( ( ( bonusFreeBlockCount > bonusFreeBlockEdge ) ? bonusFreeBlockEdge : bonusFreeBlockCount ) * bonusFreeBlockProgressStep ) / 1024;
		//
		bonusDiagBlockProgressCount = ( ( ( bonusDiagBlockCount > bonusDiagBlockEdge ) ? bonusDiagBlockEdge : bonusDiagBlockCount ) * bonusDiagBlockProgressStep ) / 1024;
		//
		if ( needRedrawBonus ) drawBonuses();
	}
	//
	j = toRemoveList.GetCount();
	//
	for ( i = 0; i < j; i++ )
	{
		gameMap[toRemoveList[i].ndx] = EMPTY_PLACE;
	}
	//
	return toRemoveList.GetCount() > 0;
}


// çàïîëíåíèå èãðîâîãî ïîëÿ ñëó÷àéíîé êîìáèíàöèåé áëîêîâ
void initGameMap()
{
	int i, localScore, localInserted, btn;

	//
	btn = gameLevel > 5 ? blockTypesNum : 5 + gameLevel;
	//
	for ( i = 0; i < (mapSize * mapSize); i++ )
	{
		gameMap[i] = ( rtlRand() % btn ) + 1;
	}
	//
	localScore = playerScore;
	localInserted = insertedBlocksCount;
	//
	freezeBonuses = true;
	//
	while ( exterminateLines() )
	{
		while ( insertNewBlocks() > 0 );
	}
	//
	freezeBonuses = false;
	//
	playerScore = localScore;
	insertedBlocksCount = localInserted;
}


// îòîáðàçèòü áëîêè áîíóñîâ
void drawBonuses()
{
	//
	kos_PutImage(
		selectedBlock != BONUS_SOMEBLOCK_SELECTED ?
			fishki[bonusSomeBlock]->GetBits() :
			fishki[bonusSomeBlock]->GetHighlightedBits(),
		blockSize, blockSize,
		BONUS_SOMEBLOCK_LEFT, BONUS_SOMEBLOCK_TOP
		);
	//
	kos_PutImage(
		bonusFreeBlock ?
		(
			selectedBlock != BONUS_FREEBLOCK_SELECTED ?
			fishki[BONUS_FREE_BLOCK]->GetBits() :
			fishki[BONUS_FREE_BLOCK]->GetHighlightedBits()
		) :
		fishki[0]->GetBits(),
		blockSize, blockSize,
		BONUS_FREEBLOCK_LEFT, BONUS_FREEBLOCK_TOP
		);
	//
	kos_PutImage(
		bonusDiagBlock > 0 ?
			fishki[bonusDiagBlock+BONUS_DIAG_BLOCK-1]->GetBits() :
			fishki[0]->GetBits(),
		blockSize, blockSize,
		BONUS_DIAGBLOCK_LEFT, BONUS_DIAGBLOCK_TOP
		);
}


// îòîáðàçèòü èãðîâîå ïîëå
void drawGameMap()
{
	int i, j, ndx;

	//
	for ( i = 0; i < mapSize; i++ )
	{
		//
		for ( j = 0; j < mapSize; j++ )
		{
			//
			ndx = (i*mapSize) + j;
			//
			kos_PutImage(
				ndx != selectedBlock ?
					fishki[gameMap[ndx]]->GetBits() :
					fishki[gameMap[ndx]]->GetHighlightedBits(),
				blockSize, blockSize,
				(j * blockSize) + blocksLeft,
				(i * blockSize) + blocksTop
				);
		}
	}
}


// êîîðäèíàòû êóðñîðà "ìûøè"
int mX, mY;

// ïðîâåðêà íà íàæàòèå ëåâîé êíîïêè "ìûøè"
bool mouseLButtonDown()
{
	static bool isDown = false;
	Dword buttons;

	//
	kos_GetMouseState( buttons, mX, mY );
	//
	if ( mouseButtonDisabled ) return false;
	//
	if ( ( buttons & 1 ) )
	{
		if ( isDown )
			return false;
		else
		{
			isDown = true;
			return true;
		}
	}
	else
	{
		if ( isDown )
		{
			isDown = false;
		}
		return false;
	}
}

//
void flipTwoBlocks( int ndx1, int ndx2 )
{
	Word blX, blY, selX, selY;
	Byte fishCode;

	//
	blX = ( ndx1 % mapSize ) * blockSize + blocksLeft;
	blY = ( ndx1 / mapSize ) * blockSize + blocksTop;
	selX = ( ndx2 % mapSize ) * blockSize + blocksLeft;
	selY = ( ndx2 / mapSize ) * blockSize + blocksTop;
	// ïåðåñòàâèì áëîêè ìåñòàìè
	fishCode = gameMap[ndx1];
	gameMap[ndx1] = gameMap[ndx2];
	gameMap[ndx2] = fishCode;
	// èçîáðàæåíèå áëîêà
	kos_PutImage(
		fishki[gameMap[ndx1]]->GetBits(),
		blockSize, blockSize,
		blX,
		blY
		);
	// èçîáðàæåíèå áëîêà
	kos_PutImage(
		fishki[gameMap[ndx2]]->GetBits(),
		blockSize, blockSize,
		selX,
		selY
		);
}


// èãðîâîé ïðîöåññ
int GameLoop()
{
	int result, ndx, blX, blY, selX, selY, ddX, ddY, nSel;
	Byte keyCode, mCode;
	bool needDecBonus;
	Dword buttonID;

	//
	gameWndTitle = gameWndTitle1;
	gameFace.GetSize( gcx, gcy );
	gameLevel = startGameLevel;
	playerScore = 0;
	bonusSomeBlock = 0;
	bonusFreeBlock = false;
	bonusDiagBlock = 0;
	bonusSomeBlockCount = 0;
	bonusFreeBlockCount = 0;
	bonusDiagBlockCount = 0;
	bonusSomeBlockProgressCount = 0;
	bonusFreeBlockProgressCount = 0;
	bonusDiagBlockProgressCount = 0;
	SetLevelVariables();
	mouseButtonDisabled = false;
	initGameMap();
	//
	kos_ChangeWindow( -1, -1, gcx + 1, gcy + 21 );
	//
	for ( result = GM_NONE; result == GM_NONE; )
	{
		switch( kos_WaitForEvent() )
		{
		// íàäî ïîëíîñòüþ ïåðåðèñîâàòü îêíî
		case 1:
			DrawGameWindow();
			break;

		// êëàâèàòóðà
		case 2:
			if ( kos_GetKey( keyCode ) )
			{
				//
				switch ( keyCode )
				{
				case 0x1B:
					result = GM_ABORT;
					break;

				default:
					break;
				}
			}
			break;

		// êíîïêè
		case 3:
			if ( kos_GetButtonID( buttonID ) )
			{
				switch ( buttonID )
				{
				case 0xA:
					result = GM_ABORT;
					break;

				default:
					break;
				}
			}

		// ñîáûòèå îò ìûøè
		case 6:
			// íàæàòèå ëåâîé êíîïêè?
			if ( mouseLButtonDown() )
			{
				// ñ÷èòàåì êîîðäèíàòû îòíîñèòåëíî èãðîâîãî ïîëÿ
				blX = mX - blocksLeft;
				blY = mY - blocksTop;
				// ïîïàëî â èãðîâîå ïîëå?
				if ( blX >= 0 && blX < (mapSize * blockSize)
					&& blY >= 0 && blY < (mapSize * blockSize) )
				{
					// ïîëó÷àåì êîîðäèíàòû â áëîêàõ
					blX /= blockSize;
					blY /= blockSize;
					// ïîëó÷àåì íîìåð áëîêà íà êàðòå
					ndx = blX + ( blY * mapSize );
					// åù¸ îäíà ïðîâåðêà, ÷òîáû íå âûëåçòè çà ïðåäåëû êàðòû
					if ( ndx >= 0 && ndx < (mapSize * mapSize) )
					{
						// íà÷èíàåì ïåðåðèñîâêó
						kos_WindowRedrawStatus( WRS_BEGIN );
						// åñëè íå áûëî âûáðàííîãî áëîêà
						if ( selectedBlock == SELECTED_BLOCK_NONE )
						{
							// çàïîìíèì âûäåëåííûé áëîê
							selectedBlock = ndx;
							// îòìåòèì áëîê íà èãðîâîì ïîëå
							kos_PutImage(
								fishki[gameMap[selectedBlock]]->GetHighlightedBits(),
								blockSize, blockSize,
								( selectedBlock % mapSize ) * blockSize + blocksLeft,
								( selectedBlock / mapSize ) * blockSize + blocksTop
								);
						}
						else // ïîìå÷åííûé áëîê óæå åñòü
						{
							if ( selectedBlock >= 0 )
							{
								// êîîðäèíàòû ïîìå÷åííîãî áëîêà
								selX = selectedBlock % mapSize;
								selY = selectedBlock / mapSize;
								// áûë âûáðàí äðóãîé áëîê?
								if ( ndx != selectedBlock )
								{
									// ñ÷èòàåì ðàçíîñòü êîîðäèíàò äâóõ áëîêîâ
									ddX = selX - blX;
									ddY = selY - blY;
									needDecBonus = ( bonusDiagBlock > 0 && abs(ddX) == 1 && abs(ddY) == 1 );
									// åñëè ýòî ñîñåäíèé áëîê
									if (
										( abs(ddX) == 1 && ddY == 0 )
										|| ( abs(ddY) == 1 && ddX == 0 )
										|| needDecBonus
										)
									{
										// ïåðåñòàâèì áëîêè ìåñòàìè
										flipTwoBlocks( ndx, selectedBlock );
										//
										kos_Pause( 16 );
										//
										if ( findBlockLines() > 0 )
										{
											//
											if ( needDecBonus )
											{
												//
												--bonusDiagBlock;
												//
												drawBonuses();
											}
											// ñíèìàåì ïîìåòêó ñ áëîêà
											selectedBlock = SELECTED_BLOCK_NONE;
											//
											while ( exterminateLines() )
											{
												//
												fadeOutBlocks();
												//
												//drawGameMap();
												//drawScore();
												//
												//kos_Pause( 25 );
												//
												while ( insertNewBlocks() > 0 )
												{
													//
													fallDownBlocks();
													//
													//drawGameMap();
													//kos_Pause( 30 );
												}
											}
											//
											drawScore();
											//
											levelProgressCount = ( ( ( insertedBlocksCount > levelBlocksLimit ) ? levelBlocksLimit : insertedBlocksCount ) * levelProgressStep ) / 1024;
											//
											drawGameMeters();
											//
											if ( insertedBlocksCount > levelBlocksLimit )
											{
												kos_Pause( 50 );
												gameLevel++;
												SetLevelVariables();
												//
												//initGameMap();
												//
												//DrawGameWindow();
												//
												drawGameMeters();
												drawLevelNum();
											}
											else
												//
												if ( mouseButtonDisabled = checkGameOut() )
												{
													//
													DrawGameWindow();
												}
										}
										else
										{
											// íå ïîëó÷àåòñÿ ëèíèè, áëîêè ñòàâèì íà ìåñòî
											flipTwoBlocks( ndx, selectedBlock );
											// ñíèìàåì ïîìåòêó ñ áëîêà
											selectedBlock = SELECTED_BLOCK_NONE;
										}
									}
									else // âûáðàí íåñîñåäíèé áëîê
									{
										// ïåðåñòèì ìàðêåð
										kos_PutImage(
											fishki[gameMap[selectedBlock]]->GetBits(),
											blockSize, blockSize,
											selX * blockSize + blocksLeft,
											selY * blockSize + blocksTop
											);
										// ïîìåòèì âûáðàííûé áëîê
										selectedBlock = ndx;
										// íà èãðîâîì ïîëå
										kos_PutImage(
											fishki[gameMap[selectedBlock]]->GetHighlightedBits(),
											blockSize, blockSize,
											blX * blockSize + blocksLeft,
											blY * blockSize + blocksTop
											);
									}
								}
								else // âûáðàí òîò æå áëîê
								{
									// ñíèìàåì ïîìåòêó
									kos_PutImage(
										fishki[gameMap[selectedBlock]]->GetBits(),
										blockSize, blockSize,
										selX * blockSize + blocksLeft,
										selY * blockSize + blocksTop
										);
									//
									selectedBlock = SELECTED_BLOCK_NONE;
								}
							}
							else
							// òêíóëè â áëîê ïðè âûáðàííîì áîíóñå
							{
								//
								mCode = gameMap[ndx];
								//
								switch ( selectedBlock )
								{
								case BONUS_SOMEBLOCK_SELECTED:
									gameMap[ndx] = bonusSomeBlock;
									break;

								case BONUS_FREEBLOCK_SELECTED:
									gameMap[ndx] = BONUS_FREE_BLOCK;
									break;

								default:
									break;
								}
								//
								if ( findBlockLines() > 0 )
								{
									// óáèðàåì èñïîëüçîâàííûé áîíóñ
									switch ( selectedBlock )
									{
									case BONUS_SOMEBLOCK_SELECTED:
										bonusSomeBlock = 0;
										break;

									case BONUS_FREEBLOCK_SELECTED:
										bonusFreeBlock = false;
										break;

									default:
										break;
									}
									// íà ýêðàíå òîæå
									drawBonuses();
									drawGameMap();
									kos_Pause( 16 );
									// óáèðàåì áëîêè
									// ñíèìàåì ïîìåòêó ñ áëîêà
									selectedBlock = SELECTED_BLOCK_NONE;
									//
									while ( exterminateLines() )
									{
										//
										fadeOutBlocks();
										//
										//drawGameMap();
										//drawScore();
										//
										//kos_Pause( 25 );
										//
										while ( insertNewBlocks() > 0 )
										{
											//
											fallDownBlocks();
											//
											//drawGameMap();
											//kos_Pause( 30 );
										}
									}
									//
									drawScore();
									//
									levelProgressCount = ( ( ( insertedBlocksCount > levelBlocksLimit ) ? levelBlocksLimit : insertedBlocksCount ) * levelProgressStep ) / 1024;
									//
									drawGameMeters();
									//
									if ( insertedBlocksCount > levelBlocksLimit )
									{
										kos_Pause( 50 );
										gameLevel++;
										SetLevelVariables();
										//
										//initGameMap();
										//
										//DrawGameWindow();
										//
										drawGameMeters();
										drawLevelNum();
									}
									else
										//
										mouseButtonDisabled = checkGameOut();
								}
								else
								// áîíóñ çäåñü íåïðèìåíèì
								{
									// âåðí¸ì âçàä
									gameMap[ndx] = mCode;
									// ïîìåòèì áëîê
									selectedBlock = ndx;
									// íà èãðîâîì ïîëå
									kos_PutImage(
										fishki[gameMap[selectedBlock]]->GetHighlightedBits(),
										blockSize, blockSize,
										blX * blockSize + blocksLeft,
										blY * blockSize + blocksTop
										);
									// óáåð¸ì ïîìåòêó ñ áîíóñà
									drawBonuses();
								}
							}
						}
						// îïðåäåëèì êíîïêó
						kos_DefineButton(
							444+1, 2+21,
							54, 20,
							0x6000000A,
							0
							);
						// çàâåðøàåì ïåðåðèñîâêó
						kos_WindowRedrawStatus( WRS_END );
					}
				}
				else
				// ïðîâåðèì ïîïàäàíèå â áîíóñû
				{
					nSel = 0;
					//
					if ( mX >= BONUS_SOMEBLOCK_LEFT && (mX - BONUS_SOMEBLOCK_LEFT) <= blockSize
						&& mY >= BONUS_SOMEBLOCK_TOP && (mY - BONUS_SOMEBLOCK_TOP ) <= blockSize )
					{
						//
						nSel = BONUS_SOMEBLOCK_SELECTED;
					}
					//
					if ( mX >= BONUS_FREEBLOCK_LEFT && (mX - BONUS_FREEBLOCK_LEFT) <= blockSize
						&& mY >= BONUS_FREEBLOCK_TOP && (mY - BONUS_FREEBLOCK_TOP ) <= blockSize )
					{
						//
						nSel = BONUS_FREEBLOCK_SELECTED;
					}
					//
					if ( mX >= BONUS_DIAGBLOCK_LEFT && (mX - BONUS_DIAGBLOCK_LEFT) <= blockSize
						&& mY >= BONUS_DIAGBLOCK_TOP && (mY - BONUS_DIAGBLOCK_TOP ) <= blockSize )
					{
						//
						nSel = BONUS_DIAGBLOCK_SELECTED;
					}
					//
					if ( nSel != 0 )
					{
						//
						if ( selectedBlock > 0 )
						{
							// ñíèìàåì ïîìåòêó
							kos_PutImage(
								fishki[gameMap[selectedBlock]]->GetBits(),
								blockSize, blockSize,
								(selectedBlock % mapSize) * blockSize + blocksLeft,
								(selectedBlock / mapSize) * blockSize + blocksTop
								);
						}
						//
						if ( selectedBlock != nSel )
							selectedBlock = nSel;
						else
							selectedBlock = SELECTED_BLOCK_NONE;
						//
						drawBonuses();
					}
				}

			}
			//
			break;

		default:
			break;
		}
	}
	// îòìåíèì êíîïêó
	kos_DefineButton(
		0, 0,
		0, 0,
		0x8000000A,
		0
		);
	//
	if ( gameLevel > maxGameLevel ) maxGameLevel = gameLevel;
	//
	return result;
}

//
void drawLevelNum()
{
	Word startX;

	//
	if ( gameLevel > 9 )
	{
		//
		startX = LEVEL_LEFT;
		//
		kos_PutImage(
			gameNumbers.GetBits() + ( ( gameLevel / 10 ) * NUM_WIDTH * NUM_HEIGHT ),
			NUM_WIDTH, NUM_HEIGHT,
			startX, LEVEL_TOP
			);
		//
		startX += NUM_WIDTH;
	}
	else
	{
		//
		startX = LEVEL_LEFT + ( (LEVEL_WIDTH - NUM_WIDTH) / 2 );
	}
	//
	kos_PutImage(
		gameNumbers.GetBits() + ( ( gameLevel % 10 ) * NUM_WIDTH * NUM_HEIGHT ),
		NUM_WIDTH, NUM_HEIGHT,
		startX, LEVEL_TOP
		);
}

//
void drawScore()
{
	Word startX;
	int i, j;
	char strNum[8];

	// ÷èñëî â òåêñòîâîì âèäå
	sprintf( strNum, "%U", playerScore );
	//
	j = strlen( strNum );
	//
	startX = SCORE_LEFT + ( ( SCORE_WIDTH - ( NUM_WIDTH * j ) ) / 2 );
	//
	for ( i = 0; i < j; i++ )
	{
		//
		kos_PutImage(
			gameNumbers.GetBits() + ( ( strNum[i] - '0' ) * NUM_WIDTH * NUM_HEIGHT ),
			NUM_WIDTH, NUM_HEIGHT,
			startX, SCORE_TOP
			);
		//
		startX += NUM_WIDTH;
	}
}


//
void DrawGameWindow()
{
	//
	kos_WindowRedrawStatus( WRS_BEGIN );
	// îêíî
	kos_DefineAndDrawWindow(
		WNDLEFT, WNDTOP,
		gcx + 1, gcy + 21,
		0, 0x0,
		0, WNDHEADCOLOUR,
		WNDHEADCOLOUR
		);
	// çàãîëîâîê îêíà
	kos_WriteTextToWindow(
		4, 7,
		0x10, WNDTITLECOLOUR,
		gameWndTitle, strlen(gameWndTitle)
		);
	//
	gameFace.Draw( 1, 21 );
	drawGameMap();
	drawGameMeters();
	drawBonuses();
	// íîìåð óðîâíÿ
	drawLevelNum();
	drawScore();
	//
	if ( mouseButtonDisabled )
	{
		//
		kos_DrawBar(
			( gcx + 1 - 160 ) / 2, ( gcy + 21 - 32 ) / 2,
			160, 32,
			0x2383B3
			);
		//
		kos_WriteTextToWindow(
			( gcx + 1 - sizeof( gameOverText ) * 6 ) / 2, ( gcy + 21 - 9 ) / 2,
			0x0, 0xF7F7F7,
			gameOverText,
			sizeof( gameOverText )
			);
	}
	// îïðåäåëèì êíîïêó
	kos_DefineButton(
		444+1, 2+21,
		54, 20,
		0x6000000A,
		0
		);
	//
	kos_WindowRedrawStatus( WRS_END );
}


//
void fadeOutBlocks()
{
	int i, j, k, ndx, x, y;

	//
	j = toRemoveList.GetCount();
	//
	for ( k = 0; k < 4; k++ )
	{
		//
		__asm{
				push	eax
				push	ebx
				mov		eax, 18
				mov		ebx, 14
				int		0x40
				pop		eax
				pop		ebx
		}
		//
		for ( i = 0; i < j; i++ )
		{
			//
			ndx = toRemoveList[i].ndx;
			y = ndx / mapSize;
			x = ndx % mapSize;
			//
			kos_PutImage(
				gameBlocksZ[k].GetBits() + ( (toRemoveList[i].value - 1) * blockSize * blockSize ),
				blockSize, blockSize,
				(x * blockSize) + blocksLeft,
				(y * blockSize) + blocksTop
				);
		}
		//
		kos_Pause( 3 );
	}
	//clear
	for ( i = 0; i < j; i++ )
	{
		//
		ndx = toRemoveList[i].ndx;
		y = ndx / mapSize;
		x = ndx % mapSize;
		//
		kos_DrawBar(
			(x * blockSize) + blocksLeft,
			(y * blockSize) + blocksTop,
			blockSize, blockSize, 0
			);
	}
	//
	kos_Pause( 16 );
}


//
void fallDownBlocks()
{
	int i, j, k, ndx, x, y, height, offset;

	//
	j = toFallList.GetCount();
	//
	for ( k = 1; k <= blockSize; k += 2 )
	{
		//
		__asm{
				push	eax
				push	ebx
				mov		eax, 18
				mov		ebx, 14
				int		0x40
				pop		eax
				pop		ebx
		}
		//
		for ( i = 0; i < j; i++ )
		{
			//
			ndx = toFallList[i];
			//
			y = ( ( ( ndx / mapSize ) - 1 ) * blockSize ) + k;
			if ( y < 0 )
			{
				y = 0;
				height = k;
				offset = blockSize - k;
			}
			else
			{
				offset = 0;
				height = blockSize;
			}
			//
			x = ( ndx % mapSize ) * blockSize;
			//
			kos_PutImage(
				fishki[gameMap[ndx]]->GetBits() + ( offset * blockSize ),
				blockSize, height,
				x + blocksLeft,
				y + blocksTop
				);
		}
		//
		kos_Pause( 1 );
	}
}