/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "bbvs/minigames/bbant.h"

namespace Bbvs {

static const BBPoint kPosIncrTbl1[] = {
	{0, -1}, {-1, -1}, {-1, 0}, {-1,  1},
	{ 0, 1}, { 1,  1}, { 1, 0}, { 1, -1}
};

static const BBPoint kPosIncrTbl2[] = {
	{0, -2}, {-2, -2}, {-2, 0}, {-2,  2},
	{ 0, 2}, { 2,  2}, { 2, 0}, { 2, -2}
};

static const int kScoreTbl[] = {
	0, 1, 1, 3, 2, 4
};

static const char * const kSoundFilenames[] = {
	"ant1.aif", "ant2.aif", "ant3.aif", "ant4.aif", "ant5.aif",
	"ant6.aif", "ant7.aif", "ant8.aif", "ant9.aif", "ant10.aif",
	"ant11.aif", "antmus1.aif", "fryant.aif", "stomp.aif", "bing.aif",
	"bvyell.aif"
};

static const uint kSoundFilenamesCount = ARRAYSIZE(kSoundFilenames);

static const uint kSoundTbl1[] = {
	2, 3, 4, 6
};

static const uint kSoundTbl2[] = {
	5, 7, 11
};

static const uint kSoundTbl3[] = {
	8, 10, 11
};

static const uint kSoundTbl4[] = {
	2, 3, 4, 6, 8, 10, 11, 5, 7, 16
};

void MinigameBbAnt::buildDrawList0(DrawList &drawList) {

	if (_titleScreenSpriteIndex)
		drawList.add(_titleScreenSpriteIndex, 0, 0, 0);

	for (int i = 0; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		if (obj->kind)
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, obj->priority);
	}

}

void MinigameBbAnt::buildDrawList1(DrawList &drawList) {

	if (_backgroundSpriteIndex)
		drawList.add(_backgroundSpriteIndex, _stompX, _stompY, 0);

	for (int i = 1; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		if (obj->kind) {
			drawList.add(obj->anim->frameIndices[obj->frameIndex],
				_stompX + (obj->x >> 16), _stompY + (obj->y >> 16),
				obj->priority);
		}
	}

	drawList.add(getAnimation(164)->frameIndices[0], 5, 2, 2000);
	drawNumber(drawList, _score, 68, 16);
	drawList.add(getAnimation(166)->frameIndices[0], 230, 2, 2000);
	drawNumber(drawList, _levelTimeLeft, 280, 16);

	for (int i = 0; i < _stompCount; ++i)
		drawList.add(getAnimation(130)->frameIndices[0], 20 + i * 30, 230, 2000);

}

void MinigameBbAnt::buildDrawList2(DrawList &drawList) {
	buildDrawList1(drawList);
	drawList.add(getAnimation(168)->frameIndices[0], 40, 100, 2000);
	drawNumber(drawList, _counter1, 190, 112);
	drawNumber(drawList, _countdown5, 258, 112);
	drawList.add(getAnimation(169)->frameIndices[0], 120, 120, 2000);
	drawNumber(drawList, _counter4, 192, 132);
}

void MinigameBbAnt::buildDrawList3(DrawList &drawList) {
	buildDrawList1(drawList);
	drawList.add(getAnimation(163)->frameIndices[0], 120, 70, 2000);
	drawList.add(getAnimation(165)->frameIndices[0], 95, 95, 2000);
	drawNumber(drawList, _hiScore, 208, 107);
}

void MinigameBbAnt::drawMagnifyingGlass(DrawList &drawList) {
	scale2x(_objects[0].x - 28, _objects[0].y - 27);
	drawList.clear();
	drawList.add(_objects[0].anim->frameIndices[0], _objects[0].x, _objects[0].y, _objects[0].priority);
	drawList.add(_objects[0].anim->frameIndices[1], _objects[0].x, _objects[0].y, _objects[0].priority);
	drawList.add(_objects[0].anim->frameIndices[2], _objects[0].x, _objects[0].y, _objects[0].priority);
}

void MinigameBbAnt::drawSprites() {
	switch (_gameState) {
	case 0:
		drawSprites0();
		break;
	case 1:
		drawSprites1();
		break;
	case 2:
		drawSprites2();
		break;
	case 3:
		drawSprites3();
		break;
	}
}

void MinigameBbAnt::drawSprites0() {
	DrawList drawList;
	buildDrawList0(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	_vm->_screen->copyToScreen();
}

void MinigameBbAnt::drawSprites1() {
	DrawList drawList;
	buildDrawList1(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	drawMagnifyingGlass(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	_vm->_screen->copyToScreen();
}

void MinigameBbAnt::drawSprites2() {
	DrawList drawList;
	buildDrawList2(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	drawMagnifyingGlass(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	_vm->_screen->copyToScreen();
}

void MinigameBbAnt::drawSprites3() {
	DrawList drawList;
	buildDrawList3(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	_vm->_screen->copyToScreen();
}

MinigameBbAnt::Obj *MinigameBbAnt::getFreeObject() {
	for (int i = 12; i < kMaxObjectsCount; ++i)
		if (_objects[i].kind == 0)
			return &_objects[i];
	return 0;
}

void MinigameBbAnt::initObjects() {
	switch (_gameState) {
	case 0:
		initObjects0();
		break;
	case 1:
		initObjects1();
		break;
	case 2:
	case 3:
		// Nothing
		break;
	}
}

void MinigameBbAnt::initObjects0() {
	_objects[0].anim = getAnimation(172);
	_objects[0].frameIndex = 0;
	_objects[0].ticks = getAnimation(172)->frameTicks[0];
	_objects[0].x = 160;
	_objects[0].y = 120;
	_objects[0].priority = 2000;
	_objects[0].kind = 1;
	_objects[1].anim = getAnimation(170);
	_objects[1].frameIndex = 0;
	_objects[1].ticks = getAnimation(170)->frameTicks[0];
	_objects[1].x = 40;
	_objects[1].y = 240;
	_objects[1].priority = 100;
	_objects[1].kind = 2;
	_objects[2].anim = getAnimation(171);
	_objects[2].frameIndex = 0;
	_objects[2].ticks = getAnimation(171)->frameTicks[0];
	_objects[2].x = 280;
	_objects[2].y = 240;
	_objects[2].priority = 100;
	_objects[2].kind = 2;
}

void MinigameBbAnt::initObjects1() {
	_objects[0].kind = 0;
	_objects[0].x = 160;
	_objects[0].y = 120;
	_objects[0].xIncr = 0;
	_objects[0].yIncr = 0;
	_objects[0].anim = getAnimation(159);
	_objects[0].frameIndex = 0;
	_objects[0].ticks = _objects[0].anim->frameTicks[0];
	_objects[0].priority = 1000;
	_objects[1].kind = 8;
	_objects[1].x = 0x1E0000;
	_objects[1].y = 0x280000;
	_objects[1].xIncr = 0;
	_objects[1].yIncr = 0;
	_objects[1].anim = getAnimation(160);
	_objects[1].frameIndex = 0;
	_objects[1].ticks = _objects[0].anim->frameTicks[0];
	_objects[1].priority = 900;
	_objects[1].smokeCtr = 0;
	_objects[1].hasSmoke = false;
	_objects[1].status = 0;
	_objects[2].kind = 8;
	_objects[2].x = 0x280000;
	_objects[2].y = 0x4B0000;
	_objects[2].xIncr = 0;
	_objects[2].yIncr = 0;
	_objects[2].anim = getAnimation(161);
	_objects[2].frameIndex = 0;
	_objects[2].ticks = _objects[0].anim->frameTicks[0];
	_objects[2].priority = 900;
	_objects[2].smokeCtr = 0;
	_objects[2].hasSmoke = false;
	_objects[2].status = 0;
	for (int i = 3; i < 12; ++i) {
		const ObjInit *objInit = getObjInit(i - 3);
		_objects[i].kind = 6;
		_objects[i].x = objInit->x << 16;
		_objects[i].y = objInit->y << 16;
		_objects[i].xIncr = 0;
		_objects[i].yIncr = 0;
		_objects[i].anim = objInit->anim1;
		_objects[i].frameIndex = 0;
		_objects[i].ticks = _objects[0].anim->frameTicks[0];
		_objects[i].priority = 600;
		_objects[i].status = 9;
		_objects[i].damageCtr = 0;

	}
}

void MinigameBbAnt::initVars() {
	switch (_gameState) {
	case 0:
		// Nothing
		break;
	case 1:
		initVars1();
		break;
	case 2:
		initVars2();
		break;
	case 3:
		initVars3();
		break;
	}
}

void MinigameBbAnt::initVars1() {
	_stompX = 0;
	_stompY = 0;
	_stompDelay1 = 0;
	_stompCount = 1;
	_stompCounter1 = 80;
	_stompCounter2 = 80;
	_totalBugsCount = 0;
	_hasLastStompObj = false;
	_counter1 = 9;
	_countdown10 = 140;
	_score = 0;
	_counter4 = 1;
	_gameTicks = 0;
	_skullBugCtr = 500;
	_levelTimeDelay = 58;
	_levelTimeLeft = 30;
	_bugsChanceByKind[0] = 0;
	_bugsChanceByKind[1] = 20;
	_bugsChanceByKind[2] = 20;
	_bugsChanceByKind[3] = 5;
	_bugsChanceByKind[4] = 7;
	_bugsCountByKind[0] = 0;
	_bugsCountByKind[1] = 0;
	_bugsCountByKind[2] = 0;
	_bugsCountByKind[3] = 0;
	_bugsCountByKind[4] = 0;
	_bugsCountByKind[5] = 0;
}

void MinigameBbAnt::initVars2() {
	_countdown4 = 0;
	_countdown3 = 0;
	_levelTimeDelay = 58;
	_countdown6 = 60;
	_countdown5 = 50 * _counter1;
}

void MinigameBbAnt::initVars3() {
	if (_score > _hiScore)
		_hiScore = _score;
	playSound(9);
}

bool MinigameBbAnt::updateStatus(int mouseX, int mouseY, uint mouseButtons) {
	switch (_gameState) {
	case 0:
		return updateStatus0(mouseX, mouseY, mouseButtons);
	case 1:
		return updateStatus1(mouseX, mouseY, mouseButtons);
	case 2:
		return updateStatus2(mouseX, mouseY, mouseButtons);
	case 3:
		return updateStatus3(mouseX, mouseY, mouseButtons);
	}
	return false;
}

bool MinigameBbAnt::updateStatus0(int mouseX, int mouseY, uint mouseButtons) {

	_objects[0].x = mouseX;
	_objects[0].y = mouseY;

	if (_objects[0].x >= 320)
		_objects[0].x = 320 - 1;
	if (_objects[0].y >= 240)
		_objects[0].y = 240 - 1;
	if (_objects[0].x < 0)
		_objects[0].x = 0;
	if (_objects[0].y < 0)
		_objects[0].y = 0;

	if ((mouseButtons & kLeftButtonDown) || (mouseButtons & kRightButtonDown)) {
		_gameState = 1;
		initObjects();
		initVars();
		_gameTicks = 0;
		playSound(1);
	} else {
		for (int i = 0; i < kMaxObjectsCount; ++i) {
			Obj *obj = &_objects[i];
			if (obj->kind == 2) {
				if (--obj->ticks == 0) {
					++obj->frameIndex;
					if (obj->frameIndex >= obj->anim->frameCount)
						obj->frameIndex = 0;
					obj->ticks = obj->anim->frameTicks[0];
				}
			}
		}
	}

	return true;
}

bool MinigameBbAnt::updateStatus1(int mouseX, int mouseY, uint mouseButtons) {
	const int kMaxBugsCount = 52;

	--_levelTimeDelay;
	if (!_levelTimeDelay) {
		_levelTimeDelay = 58;
		--_levelTimeLeft;
	}

	_objects[0].x = mouseX;
	_objects[0].y = mouseY;

	if (_objects[0].x >= 320)
		_objects[0].x = 320 - 1;
	if (_objects[0].y >= 240)
		_objects[0].y = 240 - 1;
	if (_objects[0].x < 0)
		_objects[0].x = 0;
	if (_objects[0].y < 0)
		_objects[0].y = 0;

	if (!_levelTimeLeft) {
		_gameState = 2;
		initVars();
		initObjects();
		_gameTicks = 0;
		return true;
	}

	if (_counter1 == 0) {
		_gameState = 3;
		initVars();
		initObjects();
		_gameTicks = 0;
		return true;
	}

	if ((mouseButtons & kRightButtonClicked) && (_stompCount > 0|| _hasLastStompObj) && !_objects[2].status) {
		if (_hasLastStompObj)
			removeStompObj(_lastStompObj);
		--_stompCount;
		_objects[2].status = 1;
	}

	if ((mouseButtons & kLeftButtonClicked) && _objects[2].status == 0 && isMagGlassAtBeavisLeg(2)) {
		if (_vm->getRandom(10) == 1 && !isAnySoundPlaying(kSoundTbl4, 10))
			playSound(16);
		insertSmokeObj(_objects[0].x << 16, _objects[0].y << 16);
	}

	if (_skullBugCtr > 0) {
		if (--_skullBugCtr == 0) {
			_skullBugCtr = _vm->getRandom(150) + 500;
			insertRandomBugObj(5);
		}
	}

	if (_stompCounter2 > 0)
		--_stompCounter2;

	if (_totalBugsCount < kMaxBugsCount && _vm->getRandom(_stompCounter2) == 0) {
		int testTbl[4];
		int maxKindCount = 0, objKind = 0;

		_stompCounter2 = _stompCounter1;

		for (int i = 0; i < 4; ++i)
			testTbl[i] = _vm->getRandom(_bugsChanceByKind[i] - _bugsCountByKind[i]);

		for (int i = 0; i < 4; ++i) {
			if (testTbl[i] >= maxKindCount) {
				maxKindCount = testTbl[i];
				objKind = i + 1;
			}
		}

		if (objKind)
			insertRandomBugObj(objKind);

	}

	updateObjs(mouseButtons);
	updateFootObj(2);

	if (--_countdown10 == 0) {
		_countdown10 = 140;
		if (_stompCounter1 > 20)
			--_stompCounter1;
	}

	return true;
}

bool MinigameBbAnt::updateStatus2(int mouseX, int mouseY, uint mouseButtons) {

	_objects[0].x = mouseX;
	_objects[0].y = mouseY;

	if (_objects[0].x >= 320)
		_objects[0].x = 320 - 1;
	if (_objects[0].y >= 240)
		_objects[0].y = 240 - 1;
	if (_objects[0].x < 0)
		_objects[0].x = 0;
	if (_objects[0].y < 0)
		_objects[0].y = 0;

	if (_countdown6 > 0) {
		if (--_countdown6 == 0) {
			_countdown4 = 150;
			playSound(15, true);
		}
	} else if (_countdown4 > 0) {
		if (--_countdown4 == 0) {
			_countdown3 = 150;
		} else if (_countdown5 > 0) {
			++_countdown4;
			++_score;
			if (--_countdown5 == 0) {
				stopSound(15);
				_bugsChanceByKind[5] = 10;
				_countdown7 = 40;
				_countdown4 = 10 * (13 - _counter1);
				return true;
			}
		} else {
			if (--_countdown7 == 0) {
				bool flag1 = false;
				_countdown7 = _bugsChanceByKind[5];
				for (int i = 3; i < 12 && !flag1; ++i) {
					Obj *obj = &_objects[i];
					if (obj->status == 13) {
						const ObjInit *objInit = getObjInit(i - 3);
						obj->x = objInit->x << 16;
						obj->y = objInit->y << 16;
						obj->anim = objInit->anim3;
						obj->frameIndex = 0;
						obj->ticks = _objects[0].anim->frameTicks[0];
						obj->status = 9;
						obj->damageCtr = 0;
						obj->priority = 600;
						++_counter1;
						playSound(15);
						flag1 = true;
					}
				}
			}
		}
	} else if (_countdown3 > 0) {
		if ((mouseButtons & kLeftButtonDown) || (mouseButtons & kRightButtonDown) || (--_countdown3 == 0)) {
			_levelTimeDelay = 58;
			_levelTimeLeft = 30;
			_gameState = 1;
			_gameTicks = 0;
			++_counter4;
		}
	}

	return true;
}

bool MinigameBbAnt::updateStatus3(int mouseX, int mouseY, uint mouseButtons) {
	if (!isSoundPlaying(9) && _fromMainGame) {
		_vm->_system->delayMillis(1000);
		_gameDone = true;
	}
	return true;
}

void MinigameBbAnt::getRandomBugObjValues(int &x, int &y, int &animIndexIncr, int &field30) {
	field30 = _vm->getRandom(4);
	switch (field30) {
	case 0:
		y = -5;
		x = _vm->getRandom(190) + 120;
		animIndexIncr = 4;
		break;
	case 1:
		x = 325;
		y = _vm->getRandom(220) + 10;
		animIndexIncr = 2;
		break;
	case 2:
		y = 245;
		x = _vm->getRandom(300) + 10;
		animIndexIncr = 0;
		break;
	case 3:
		x = -5;
		y = _vm->getRandom(190) + 120;
		animIndexIncr = 6;
		break;
	}
}

void MinigameBbAnt::insertBugSmokeObj(int x, int y, int bugObjIndex) {
	Obj *obj = getFreeObject();
	if (obj) {
		Obj *bugObj = &_objects[bugObjIndex];
		bugObj->hasSmoke = true;
		obj->kind = 7;
		obj->x = x;
		obj->y = y;
		obj->priority = 950;
		if (bugObj->status >= 4 && (bugObj->status <= 6 || bugObj->status == 8)) {
			obj->xIncr = 0;
			obj->yIncr = (-1 << 16);
		} else {
			obj->xIncr = bugObj->xIncr / 8;
			obj->yIncr = bugObj->yIncr / 8;
		}
		obj->anim = getAnimation(158);
		obj->frameIndex = 0;
		obj->ticks = obj->anim->frameTicks[0];
	}
}

void MinigameBbAnt::insertSmokeObj(int x, int y) {
	Obj *obj = getFreeObject();
	if (obj) {
		obj->kind = 7;
		obj->x = x;
		obj->y = y;
		obj->priority = 950;
		obj->xIncr = 0x2000;
		obj->yIncr = -0xC000;
		obj->anim = getAnimation(158);
		obj->frameIndex = 0;
		obj->ticks = obj->anim->frameTicks[0];
	}
}

void MinigameBbAnt::resetObj(int objIndex) {
	_objects[objIndex].kind = 0;
}

void MinigameBbAnt::insertStompObj(int x, int y) {
	Obj *obj = getFreeObject();
	if (obj) {
		obj->kind = 9;
		obj->x = x;
		obj->y = y;
		obj->priority = 2000;
		obj->xIncr = (0x1E0000 * _stompCount - x + 0x140000) / 15;
		obj->yIncr = (0xE60000 - y) / 15;
		obj->anim = getAnimation(130);
		obj->frameIndex = 0;
		obj->ticks = 15;
		_lastStompObj = obj;
		_hasLastStompObj = true;
	}
}

void MinigameBbAnt::removeStompObj(Obj *obj) {
	++_stompCount;
	_hasLastStompObj = false;
	obj->kind = 0;
}

void MinigameBbAnt::insertBugObj(int kind, int animIndexIncr, int always0, int x, int y, int field30, int always1) {
	Obj *obj = getFreeObject();
	if (obj) {
		const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(kind);
		obj->field30 = field30;
		obj->animIndexIncr = animIndexIncr;
		obj->kind = kind;
		obj->x = x << 16;
		obj->y = y << 16;
		obj->priority = 610;
		obj->xIncr = kPosIncrTbl1[0].x << 16;
		obj->yIncr = kPosIncrTbl1[0].y << 16;
		obj->anim = objKindAnimTable[0];
		obj->frameIndex = 0;
		obj->ticks = obj->anim->frameTicks[0];
		obj->animIndex = 0;
		obj->status = 1;
		obj->damageCtr = 0;
		obj->hasSmoke = false;
		obj->flag = 0;
		++_bugsCountByKind[kind];
		++_totalBugsCount;
	}
}

void MinigameBbAnt::removeBugObj(int objIndex) {
	Obj *obj = &_objects[objIndex];
	--_totalBugsCount;
	--_bugsCountByKind[obj->kind];
	obj->hasSmoke = false;
	obj->kind = 0;
}

void MinigameBbAnt::updateBugObjAnim(int objIndex) {
	Obj *obj = &_objects[objIndex];

	switch (obj->field30) {
	case 0:
		obj->animIndexIncr = 4;
		break;
	case 1:
		obj->animIndexIncr = 2;
		break;
	case 2:
		obj->animIndexIncr = 0;
		break;
	case 3:
		obj->animIndexIncr = 6;
		break;
	}
	const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
	obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x << 16;
	obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y << 16;
	obj->anim = objKindAnimTable[obj->animIndexIncr];
	obj->frameIndex = 0;
	obj->ticks = obj->anim->frameTicks[0];
}

void MinigameBbAnt::updateObjAnim2(int objIndex) {
	Obj *obj = &_objects[objIndex];

	obj->animIndexIncr += _vm->getRandom(3) - 1;
	if (obj->animIndexIncr < 0)
		obj->animIndexIncr = 7;
	if (obj->animIndexIncr > 7)
		obj->animIndexIncr = 0;
	obj->animIndexIncr += 4;
	if (obj->animIndexIncr >= 8)
		obj->animIndexIncr %= 8;
	const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
	obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x << 16;
	obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y << 16;
	obj->anim = objKindAnimTable[obj->animIndex + obj->animIndexIncr];
	obj->frameIndex = 0;
	obj->ticks = obj->anim->frameTicks[0];
	obj->x += obj->xIncr;
	obj->y += obj->yIncr;
}

void MinigameBbAnt::insertRandomBugObj(int kind) {
	int x, y, animIndexIncr, field30;
	getRandomBugObjValues(x, y, animIndexIncr, field30);
	insertBugObj(kind, animIndexIncr, 0, x, y, field30, 1);
}

bool MinigameBbAnt::isBugOutOfScreen(int objIndex) {
	Obj *obj = &_objects[objIndex];

	return
		obj->x < (-10 << 16) || obj->x > (330 << 16) ||
		obj->y < (-10 << 16) || obj->y > (250 << 16);
}

void MinigameBbAnt::updateObjAnim3(int objIndex) {
	Obj *obj = &_objects[objIndex];

	obj->animIndexIncr += _vm->getRandom(3) - 1;
	if (obj->animIndexIncr < 0)
		obj->animIndexIncr = 7;
	if (obj->animIndexIncr > 7)
		obj->animIndexIncr = 0;
	const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
	obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x << 16;
	obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y << 16;
	obj->anim = objKindAnimTable[obj->animIndexIncr];
}

void MinigameBbAnt::updateBugObj1(int objIndex) {
	Obj *obj = &_objects[objIndex];
	bool flag1 = false;
	bool flag2 = false;

	if (--obj->ticks == 0) {
		++obj->frameIndex;
		if (obj->anim->frameCount == obj->frameIndex) {
			obj->frameIndex = 0;
			obj->ticks = obj->anim->frameTicks[0];
			flag1 = true;
		} else {
			obj->ticks = obj->anim->frameTicks[obj->frameIndex];
			flag2 = true;
		}
	}

	obj->x += obj->xIncr;
	obj->y += obj->yIncr;

	if (obj->status != 7) {
		if (obj->damageCtr <= 5) {
			obj->hasSmoke = false;
		} else if (!obj->hasSmoke) {
			obj->smokeCtr = 6;
			insertBugSmokeObj(obj->x, obj->y, objIndex);
		} else if (obj->damageCtr > 200 && obj->status != 4 && obj->status != 6) {
			_score += kScoreTbl[obj->kind];
			if (obj->status == 3) {
				_objects[obj->otherObjIndex].status = 9;
				_objects[obj->otherObjIndex].priority = 600;
				if (_vm->getRandom(3) == 1 && !isAnySoundPlaying(kSoundTbl4, 10))
					playSound(kSoundTbl3[_vm->getRandom(3)]);
			} else {
				if (_vm->getRandom(3) == 1 && !isAnySoundPlaying(kSoundTbl4, 10))
					playSound(kSoundTbl2[_vm->getRandom(3)]);
			}
			flag1 = false;
			const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
			obj->hasSmoke = false;
			obj->status = 4;
			obj->xIncr = 0;
			obj->yIncr = 0;
			obj->anim = objKindAnimTable[16];
			obj->frameIndex = 0;
			obj->ticks = obj->anim->frameTicks[0];
			obj->priority = 605;
			if (obj->kind == 5) {
				// Skull Beetle
				if (_stompCount < 10)
					insertStompObj(obj->x, obj->y);
				obj->kind = 4;
				obj->anim = getObjAnim(70);
				obj->ticks = obj->anim->frameTicks[0];
			}
		} else if (--obj->smokeCtr == 0) {
			obj->smokeCtr = 6;
			insertBugSmokeObj(obj->x, obj->y, objIndex);
		}
	}

	switch (obj->status) {

	case 1:
		if (isBugOutOfScreen(objIndex))
			removeBugObj(objIndex);
		else if (flag1 && !obj->flag)
			updateObjAnim3(objIndex);
		break;

	case 3:
		// Bug carries candy
		_objects[obj->otherObjIndex].x = obj->x;
		_objects[obj->otherObjIndex].y = obj->y;
		if (isBugOutOfScreen(objIndex)) {
			_objects[obj->otherObjIndex].status = 13;
			_objects[obj->otherObjIndex].x = (500 << 16);
			_objects[obj->otherObjIndex].y = (500 << 16);
			removeBugObj(objIndex);
			--_counter1;
		}
		break;

	case 4:
		if (flag1) {
			const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
			obj->status = 6;
			obj->xIncr = 0;
			obj->yIncr = 0;
			obj->anim = objKindAnimTable[17];
			obj->frameIndex = 0;
			obj->ticks = obj->anim->frameTicks[0];
		}
		break;

	case 6:
		if (flag1) {
			const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind);
			obj->status = 7;
			obj->xIncr = kPosIncrTbl2[obj->animIndexIncr].x << 16;
			obj->yIncr = kPosIncrTbl2[obj->animIndexIncr].y << 16;
			obj->anim = objKindAnimTable[obj->animIndexIncr + 8];
			obj->frameIndex = 0;
			obj->ticks = obj->anim->frameTicks[0];
			obj->animIndex = 8;
			obj->priority = 610;
		}
		break;

	case 7:
		if (isBugOutOfScreen(objIndex))
			removeBugObj(objIndex);
		break;

	case 8:
		if (--obj->counter != 0) {
			if (flag2 && obj->frameIndex == 13) {
				obj->frameIndex = 4;
				obj->ticks = obj->anim->frameTicks[4];
			}
		} else {
			obj->status = obj->status2;
			obj->anim = obj->anim2;
			obj->frameIndex = obj->frameIndex2;
			obj->ticks = obj->ticks2;
			obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x << 16;
			obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y << 16;
			obj->priority = 610;
		}
		break;

	}

}

void MinigameBbAnt::updateObjKind2(int objIndex) {
	updateBugObj1(objIndex);
}

void MinigameBbAnt::updateObjKind3(int objIndex) {
	updateBugObj1(objIndex);
}

void MinigameBbAnt::updateObjKind4(int objIndex) {
	updateBugObj1(objIndex);
}

void MinigameBbAnt::updateObjKind5(int objIndex) {
	++_skullBugCtr;
	updateBugObj1(objIndex);
}

void MinigameBbAnt::updateStompObj(int objIndex) {
	Obj *obj = &_objects[objIndex];

	obj->x += obj->xIncr;
	obj->y += obj->yIncr;
	if (--obj->ticks == 0)
		removeStompObj(obj);
}

void MinigameBbAnt::updateSmokeObj(int objIndex) {
	Obj *obj = &_objects[objIndex];

	obj->x += obj->xIncr;
	obj->y += obj->yIncr;

	if (--obj->ticks == 0) {
		++obj->frameIndex;
		if (obj->anim->frameCount == obj->frameIndex)
			resetObj(objIndex);
		else
			obj->ticks = obj->anim->frameTicks[obj->frameIndex];
	}
}

void MinigameBbAnt::updateFootObj(int objIndex) {
	Obj *obj = &_objects[objIndex];

	switch (obj->status) {

	case 1:
		obj->xIncr = -0x8000;
		obj->yIncr = (-4 << 16);
		obj->status = 2;
		_stompCounter1 += 5;
		_stompCounter2 = 100;
		break;

	case 2:
		obj->x += obj->xIncr;
		obj->y += obj->yIncr;
		obj->yIncr += 0x2000;
		if (obj->y < (20 << 16)) {
			obj->xIncr = 0x8000;
			obj->yIncr = (7 << 16);
			obj->status = 3;
		}
		break;

	case 3:
		obj->x += obj->xIncr;
		obj->y += obj->yIncr;
		obj->yIncr += 0x2000;
		if (obj->y >= 0x4B0000) {
			obj->x = (40 << 16);
			obj->y = (75 << 16);
			obj->status = 4;
			_stompDelay1 = 6;
			_stompY = 0;
			playSound(14);
		}
		break;

	case 4:
		if (--_stompDelay1 == 0) {
			_gameTicks = 0;
			if (_stompDelay1 % 2)
				_stompY = _stompY < 1 ? -8 : 0;
		} else {
			obj->status = 0;
			_stompX = 0;
			_stompY = 0;
			// Stun all bugs
			for (int i = 12; i < kMaxObjectsCount; ++i) {
				Obj *bugObj = &_objects[i];
				if (bugObj->kind >= 1 && bugObj->kind <= 5) {
					bugObj->counter = _vm->getRandom(200) + 360;
					const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(bugObj->kind);
					if (bugObj->status == 8) {
						bugObj->hasSmoke = false;
						bugObj->xIncr = 0;
						bugObj->yIncr = 0;
						bugObj->status2 = 7;
						bugObj->anim2 = objKindAnimTable[bugObj->animIndexIncr + 8];
						bugObj->frameIndex2 = 0;
						bugObj->ticks2 = obj->anim->frameTicks[0];
						bugObj->anim = objKindAnimTable[17];
						bugObj->frameIndex = 0;
						bugObj->ticks = _vm->getRandom(4) + obj->anim->frameTicks[0];
						bugObj->animIndex = 8;
					} else {
						if (bugObj->status == 3) {
							bugObj->priority = 610;
							_objects[bugObj->otherObjIndex].status = 9;
							_objects[bugObj->otherObjIndex].priority = 600;
						}
						bugObj->hasSmoke = false;
						bugObj->xIncr = 0;
						bugObj->yIncr = 0;
						bugObj->status2 = 1;
						bugObj->anim2 = bugObj->anim;
						bugObj->frameIndex2 = bugObj->frameIndex;
						bugObj->ticks2 = bugObj->ticks;
						bugObj->anim = objKindAnimTable[17];
						bugObj->frameIndex = 0;
						bugObj->ticks = _vm->getRandom(4) + obj->anim->frameTicks[0];
					}
					bugObj->status = 8;
					bugObj->priority = 605;
				}
			}
		}
		break;

	}

}

bool MinigameBbAnt::isBugAtCandy(int objIndex, int &candyObjIndex) {
	Obj *obj = &_objects[objIndex];
	bool result = false;

	if (obj->kind >= 1 && obj->kind <= 4) {
		const BBRect &frameRect1 = obj->anim->frameRects[obj->frameIndex];
		const int obj1X1 = frameRect1.x + (obj->x >> 16);
		const int obj1Y1 = frameRect1.y + (obj->y >> 16);
		const int obj1X2 = obj1X1 + frameRect1.width;
		const int obj1Y2 = obj1Y1 + frameRect1.height;
		for (int i = 3; i < 12 && !result; ++i) {
			Obj *obj2 = &_objects[i];
			const BBRect &frameRect2 = obj->anim->frameRects[obj2->frameIndex]; // sic
			const int obj2X1 = (obj2->x >> 16) + frameRect2.x;
			const int obj2Y1 = (obj2->y >> 16) + frameRect2.y;
			const int obj2X2 = obj2X1 + frameRect2.width;
			const int obj2Y2 = obj2Y1 + frameRect2.height;
			if (obj2->status == 9 && obj1X1 <= obj2X2 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1) {
				result = true;
				candyObjIndex = i;
			}
		}
	}
	return result;
}

bool MinigameBbAnt::isMagGlassAtBug(int objIndex) {
	Obj *obj = &_objects[objIndex];
	Obj *obj0 = &_objects[0];
	bool result = false;

	if (obj->kind >= 1 && obj->kind <= 5) {
		const BBRect &frameRect1 = obj0->anim->frameRects[0];
		const int obj1X1 = obj0->x + frameRect1.x;
		const int obj1Y1 = obj0->y + frameRect1.y;
		const int obj1X2 = obj1X1 + frameRect1.width;
		const int obj1Y2 = obj1Y1 + frameRect1.height;
		const BBRect &frameRect2 = obj->anim->frameRects[obj->frameIndex];
		const int obj2X1 = (obj->x >> 16) + frameRect2.x;
		const int obj2Y1 = (obj->y >> 16) + frameRect2.y;
		const int obj2X2 = obj2X1 + frameRect2.width;
		const int obj2Y2 = obj2Y1 + frameRect2.height;
		if (obj2X2 >= obj1X1 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1)
			result = true;
	}
	return result;
}

bool MinigameBbAnt::isMagGlassAtBeavisLeg(int objIndex) {
	Obj *obj = &_objects[objIndex];
	Obj *magGlassObj = &_objects[0];
	bool result = false;

	const BBRect &frameRect1 = magGlassObj->anim->frameRects[0];
	const int obj1X1 = magGlassObj->x + frameRect1.x;
	const int obj1Y1 = magGlassObj->y + frameRect1.y;
	const int obj1X2 = obj1X1 + frameRect1.width;
	const int obj1Y2 = obj1Y1 + frameRect1.height;
	const BBRect &frameRect2 = obj->anim->frameRects[obj->frameIndex];
	const int obj2X1 = (obj->x >> 16) + frameRect2.x;
	const int obj2Y1 = (obj->y >> 16) + frameRect2.y;
	const int obj2X2 = obj2X1 + frameRect2.width;
	const int obj2Y2 = obj2Y1 + frameRect2.height;
	if (obj2X2 >= obj1X1 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1)
		result = true;
	return result;
}

bool MinigameBbAnt::testObj5(int objIndex) {
	Obj *obj = &_objects[objIndex];
	bool result = false;
	if (obj->kind >= 1 && obj->kind <= 5) {
		const int x = obj->x >> 16;
		const int y = obj->y >> 16;
		if (x < 0 || x >= 110 || y < 0 || y >= 110) {
			obj->flag = 0;
		} else if (!obj->flag) {
			obj->flag = 1;
			result = true;
		}
	}
	return result;
}

void MinigameBbAnt::updateObjs(uint mouseButtons) {

	for (int i = 12; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];

		if (obj->kind) {

			if ((mouseButtons & kLeftButtonClicked) && isMagGlassAtBug(i))
				obj->damageCtr += 100;

			if (obj->status == 1) {
				int candyObjIndex;
				if (isBugAtCandy(i, candyObjIndex)) {
					obj->status = 3;
					obj->otherObjIndex = candyObjIndex;
					_objects[candyObjIndex].otherObjIndex = i;
					_objects[candyObjIndex].status = 10;
					_objects[candyObjIndex].priority = 620;
					_objects[candyObjIndex].status = 11;
					_objects[candyObjIndex].anim = getObjInit(candyObjIndex - 3)->anim3;
					updateBugObjAnim(i);
					if (_vm->getRandom(3) == 1 && !isAnySoundPlaying(kSoundTbl4, 10))
						playSound(kSoundTbl1[_vm->getRandom(4)]);
				}
			}

			if (testObj5(i)) {
				updateObjAnim2(i);
			}

			if (obj->damageCtr) {
				--obj->damageCtr;
				if (!isSoundPlaying(13))
					playSound(13);
			}

			switch (obj->kind) {
			case 1:
				updateBugObj1(i);
				break;
			case 2:
				updateObjKind2(i);
				break;
			case 3:
				updateObjKind3(i);
				break;
			case 4:
				updateObjKind4(i);
				break;
			case 5:
				updateObjKind5(i);
				break;
			case 7:
				updateSmokeObj(i);
				break;
			case 9:
				updateStompObj(i);
				break;
			}

		}

	}

}

bool MinigameBbAnt::run(bool fromMainGame) {

	memset(_objects, 0, sizeof(_objects));

	_numbersAnim = getAnimation(167);

	_backgroundSpriteIndex = 303;
	_titleScreenSpriteIndex = 304;

	_fromMainGame = fromMainGame;

	_hiScore = 0;
	if (!_fromMainGame)
		_hiScore = loadHiscore(kMinigameBbAnt);

	_gameState = 0;
	_gameResult = false;
	_gameDone = false;
	initObjects();
	initVars();

	_spriteModule = new SpriteModule();
	_spriteModule->load("bbant/bbant.000");

	Palette palette = _spriteModule->getPalette();
	_vm->_screen->setPalette(palette);

	loadSounds();

	_gameTicks = 0;
	playSound(12, true);

	while (!_vm->shouldQuit() &&!_gameDone) {
		_vm->updateEvents();
		update();
	}

	_vm->_sound->unloadSounds();

	if (!_fromMainGame)
		saveHiscore(kMinigameBbAnt, _hiScore);

	delete _spriteModule;

	return _gameResult;
}

void MinigameBbAnt::update() {

	int inputTicks;

	if (_gameTicks > 0) {
		int currTicks = _vm->_system->getMillis();
		inputTicks = 3 * (currTicks - _gameTicks) / 50;
		_gameTicks = currTicks - (currTicks - _gameTicks - 50 * inputTicks / 3);
	} else {
		inputTicks = 1;
		_gameTicks = _vm->_system->getMillis();
	}

	if (_vm->_keyCode == Common::KEYCODE_ESCAPE) {
		_gameDone = true;
		return;
	}

	if (inputTicks == 0)
		return;

	bool done;

	do {
		done = !updateStatus(_vm->_mouseX, _vm->_mouseY, _vm->_mouseButtons);
		_vm->_mouseButtons &= ~kLeftButtonClicked;
		_vm->_mouseButtons &= ~kRightButtonClicked;
		_vm->_keyCode = Common::KEYCODE_INVALID;
	} while (--inputTicks && _gameTicks > 0 && !done);

	drawSprites();

	_vm->_system->delayMillis(10);

}

void MinigameBbAnt::scale2x(int x, int y) {
	Graphics::Surface *surface = _vm->_screen->_surface;

	int srcX = x + 14, srcY = y + 14;
	int srcW = kScaleDim, srcH = kScaleDim;

	if (srcX < 0) {
		srcW += srcX;
		srcX = 0;
	}

	if (srcY < 0) {
		srcH += srcY;
		srcY = 0;
	}

	if (srcX + srcW >= 320)
		srcW = 320 - srcX - 1;

	if (srcY + srcH >= 240)
		srcH = 240 - srcY - 1;

	for (int yc = 0; yc < srcH; ++yc) {
		byte *src = (byte*)surface->getBasePtr(srcX, srcY + yc);
		memcpy(&_scaleBuf[yc * kScaleDim], src, srcW);
	}

	int dstX = x, dstY = y;
	int dstW = 2 * kScaleDim, dstH = 2 * kScaleDim;

	if (dstX < 0) {
		dstW += dstX;
		dstX = 0;
	}

	if (dstY < 0) {
		dstH += dstY;
		dstY = 0;
	}

	if (dstX + dstW >= 320)
		dstW = 320 - dstX - 1;

	if (dstY + dstH >= 240)
		dstH = 240 - dstY - 1;

	int w = MIN(srcW * 2, dstW), h = MIN(srcH * 2, dstH);

	for (int yc = 0; yc < h; ++yc) {
		byte *src = _scaleBuf + kScaleDim * (yc / 2);
		byte *dst = (byte*)surface->getBasePtr(dstX, dstY + yc);
		for (int xc = 0; xc < w; ++xc)
			dst[xc] = src[xc / 2];
	}

}

void MinigameBbAnt::loadSounds() {
	for (uint i = 0; i < kSoundFilenamesCount; ++i) {
		Common::String filename = Common::String::format("bbant/%s", kSoundFilenames[i]);
		_vm->_sound->loadSound(filename.c_str());
	}
}

} // End of namespace Bbvs
