/* 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/bbloogie.h"

namespace Bbvs {

static const int kLoogieOffY[16] = {
	0, 1, 1, 2, 2, 3, 3, 4,
	4, 5, 5, 6, 6, 7, 7, 0
};

static const int kSquirrelOffX[] = {
	-43, -43, -38, -33, -33, -27, -23, -23,
	-23, -23, -23, -23, -18, -14,  -8,  -4,
	  2,   8,  12,  18,  20,  20,  26,  31,
	 37,  37,  37,  37,  37,  37,  37,  32,
	 29,  26,  21,  14,  10,   6,   6,   6,
	  6,   6,   6,   6,   0,  -6, -15, -20,
	-27, -37, -41, -41, -41, -41
};

static const int kPlaneOffX[] = {
	0, -1, -1, -1, 0, 1, 1, 1
};

static const int kPlaneOffY[] = {
	-1, -1, 0, 1, 1, 1, 0, -1
};

static const int kLevelScores[] = {
	20, 50, 90, 140, 200, 270, 350, 440, 540, 10000
};

static const int kLevelTimes[] = {
	120, 110, 100, 90, 80, 70, 60, 50, 40, 30
};

static const uint kBeavisSounds1[] = {
	14, 15, 19, 20, 22, 23, 24, 26
};

static const uint kButtheadSounds1[] = {
	16, 14, 15, 22, 23
};

static const uint kBeavisSounds2[] = {
	9, 3, 4, 5, 7, 14, 15, 19, 20, 22, 23, 24, 26
};

static const uint kButtheadSounds2[] = {
	9, 3, 4, 5, 7, 16, 14, 15, 22, 23
};

static const uint kPrincipalSounds[] = {
	3, 4, 5, 7
};

static const char * const kSoundFilenames[] = {
	"loog1.aif", "loog2.aif", "loog3.aif", "loog4.aif", "loog5.aif",
	"loog6.aif", "loog7.aif", "loog8.aif", "loog9.aif", "loog10.aif",
	"loog11.aif", "loog12.aif", "loog13.aif", "loog14.aif", "loog15.aif",
	"loog16.aif", "loog17.aif", "loog18.aif", "loog19.aif", "loog20.aif",
	"loog21.aif", "loog22.aif", "loog23.aif", "loog24.aif", "loog25.aif",
	"loog26.aif", "loog27.aif", "meghoker.aif", "spit1.aif", "megaloog.aif",
	"megaspit.aif", "gamemuse.aif", "bing.aif", "carhit.aif", "bikehit.aif",
	"squirhit.aif", "planehit.aif", "bing2.aif"
};

static const uint kSoundFilenamesCount = ARRAYSIZE(kSoundFilenames);

void MinigameBbLoogie::buildDrawList(DrawList &drawList) {
	switch (_gameState) {
	case kGSTitleScreen:
		buildDrawList0(drawList);
		break;
	case kGSMainGame:
		buildDrawList1(drawList);
		break;
	case kGSStandaloneGame:
		buildDrawList2(drawList);
		break;
	case kGSScoreCountUp:
		buildDrawList3(drawList);
		break;
	}
}

void MinigameBbLoogie::buildDrawList0(DrawList &drawList) {
	drawList.add(_objects[0].anim->frameIndices[_objects[0].frameIndex], _objects[0].x, _objects[0].y, 2000);
	for (int i = 1; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		if (obj->kind != 0)
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, obj->y + 16);
	}
	if (_titleScreenSpriteIndex)
		drawList.add(_titleScreenSpriteIndex, 0, 0, 0);
}

void MinigameBbLoogie::buildDrawList1(DrawList &drawList) {

	for (int i = 0; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		switch (obj->kind) {
		case 0:
			// Empty object
			break;
		case 2:
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, 400);
			break;
		case 3:
			drawList.add(obj->anim->frameIndices[obj->frameIndex + obj->frameIndexAdd], obj->x, obj->y, 1000);
			break;
		case 7:
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, 390);
			break;
		case 8:
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, 1000);
			break;
		default:
			drawList.add(obj->anim->frameIndices[obj->frameIndex], obj->x, obj->y, obj->y + 16);
			break;
		}
	}

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

	if (_fromMainGame) {
		drawList.add(getAnimation(8)->frameIndices[0], 8, 2, 2000);
		drawNumber(drawList, _numberOfHits, 56, 16);
	} else {
		drawList.add(getAnimation(10)->frameIndices[0], 230, 2, 2000);
		drawNumber(drawList, _levelTimeLeft, 280, 16);
		drawList.add(getAnimation(15)->frameIndices[0], 5, 2, 2000);
		int numberX2 = drawNumber(drawList, _currScore, 68, 16);
		drawList.add(getAnimation(9)->frameIndices[10], numberX2, 16, 2000);
		drawNumber(drawList, _dispLevelScore, numberX2 + 10, 16);
	}

	for (int i = 0; i < _megaLoogieCount; ++i)
		drawList.add(getAnimation(19)->frameIndices[0], 20 + i * 25, 236, 2000);

}

void MinigameBbLoogie::buildDrawList2(DrawList &drawList) {

	buildDrawList1(drawList);

	if (_level > 0 && (_bonusDisplayDelay1 > 0 || _bonusDisplayDelay2 > 0)) {
		drawList.add(getAnimation(12)->frameIndices[0], 100, 80, 2000);
		drawNumber(drawList, _timeBonusCtr, 212, 94);
	}

	if (_bonusDisplayDelay3 > 0) {
		drawList.add(getAnimation(14)->frameIndices[0], 65, 80, 2000);
		int numberX2 = drawNumber(drawList, _nextLevelScore, 170, 92);
		drawList.add(getAnimation(11)->frameIndices[0], numberX2, 80, 2000);
	}

}

void MinigameBbLoogie::buildDrawList3(DrawList &drawList) {

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

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

	drawList.add(getAnimation(10)->frameIndices[0], 230, 2, 2000);

	drawNumber(drawList, _levelTimeLeft, 280, 16);

	drawList.add(getAnimation(15)->frameIndices[0], 5, 2, 2000);

	int numberX2 = drawNumber(drawList, _currScore, 68, 16);
	drawList.add(getAnimation(9)->frameIndices[10], numberX2, 16, 2000);
	drawNumber(drawList, _dispLevelScore, numberX2 + 10, 16);

	drawList.add(getAnimation(20)->frameIndices[0], 120, 70, 2000);
	drawList.add(getAnimation(13)->frameIndices[0], 95, 95, 2000);

	drawNumber(drawList, _hiScore, 210, 109);

}

void MinigameBbLoogie::drawSprites() {
	DrawList drawList;
	buildDrawList(drawList);
	_vm->_screen->drawDrawList(drawList, _spriteModule);
	_vm->_screen->copyToScreen();
}

void MinigameBbLoogie::initObjs() {
	for (int i = 0; i < kMaxObjectsCount; ++i)
		_objects[i].kind = 0;
}

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

MinigameBbLoogie::Obj *MinigameBbLoogie::findLoogieObj(int startObjIndex) {
	for (int i = startObjIndex; i < kMaxObjectsCount; ++i)
		if (_objects[i].kind == 3)
			return &_objects[i];
	return 0;
}

bool MinigameBbLoogie::isHit(Obj *obj1, Obj *obj2) {
	const BBRect &frameRect1 = obj1->anim->frameRects[obj1->frameIndex];
	const BBRect &frameRect2 = obj2->anim->frameRects[obj2->frameIndex];
	const int obj1X1 = obj1->x + frameRect1.x;
	const int obj1Y1 = obj1->y + frameRect1.y;
	const int obj1X2 = obj1X1 + frameRect1.width;
	const int obj1Y2 = obj1Y1 + frameRect1.height;
	const int obj2X1 = obj2->x + frameRect2.x;
	const int obj2Y1 = obj2->y + frameRect2.y;
	const int obj2X2 = obj2X1 + frameRect2.width;
	const int obj2Y2 = obj2Y1 + frameRect2.height;
	return obj1X1 <= obj2X2 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1;
}

bool MinigameBbLoogie::isCursorAtObj(int objIndex) {
	return isHit(&_objects[0], &_objects[objIndex]);
}

void MinigameBbLoogie::initObjects() {
	switch (_gameState) {
	case kGSTitleScreen:
		initObjects0();
		break;
	case kGSMainGame:
		initObjects1();
		break;
	case kGSStandaloneGame:
		// Nothing
		break;
	case kGSScoreCountUp:
		initObjects3();
		break;
	}
}

void MinigameBbLoogie::initObjects0() {
	initObjs();
	_objects[0].anim = getAnimation(25);
	_objects[0].frameIndex = 0;
	_objects[0].ticks = getAnimation(25)->frameTicks[0];
	_objects[0].x = 160;
	_objects[0].y = 120;
	_objects[0].kind = 1;
	_objects[1].anim = getAnimation(21);
	_objects[1].frameIndex = 0;
	_objects[1].ticks = getAnimation(21)->frameTicks[0];
	_objects[1].x = 40;
	_objects[1].y = 240;
	_objects[1].kind = 1;
	_objects[2].anim = getAnimation(23);
	_objects[2].frameIndex = 0;
	_objects[2].ticks = getAnimation(23)->frameTicks[0];
	_objects[2].x = 280;
	_objects[2].y = 240;
	_objects[2].kind = 1;
	_objects[3].anim = getAnimation(22);
	_objects[3].frameIndex = 0;
	_objects[3].ticks = getAnimation(22)->frameTicks[0];
	_objects[3].x = 40;
	_objects[3].y = 240;
	_objects[3].kind = 0;
	_objects[4].anim = getAnimation(24);
	_objects[4].frameIndex = 0;
	_objects[4].ticks = getAnimation(24)->frameTicks[0];
	_objects[4].x = 280;
	_objects[4].y = 240;
	_objects[4].kind = 0;
}

void MinigameBbLoogie::initObjects1() {
	initObjs();
	_objects[0].anim = _playerAnim;
	_objects[0].frameIndex = 0;
	_objects[0].ticks = _playerAnim->frameTicks[0];
	_objects[0].status = 0;
	_objects[0].x = 160;
	_objects[0].y = 240;
	_objects[0].kind = 1;
	_objects[1].anim = getAnimation(4);
	_objects[1].frameIndex = 0;
	_objects[1].ticks = getAnimation(4)->frameTicks[0];
	_objects[1].x = 248;
	_objects[1].y = 24;
	_objects[1].kind = 2;
}

void MinigameBbLoogie::initObjects3() {
	initObjs();
	_objects[0].anim = _playerAnim;
	_objects[0].frameIndex = 0;
	_objects[0].ticks = _playerAnim->frameTicks[0];
	_objects[0].status = 0;
	_objects[0].kind = 1;
	_objects[1].anim = getAnimation(4);
	_objects[1].frameIndex = 0;
	_objects[1].ticks = getAnimation(4)->frameTicks[0];
	_objects[1].x = 248;
	_objects[1].y = 24;
	_objects[1].kind = 2;
}

void MinigameBbLoogie::initVars() {
	switch (_gameState) {
	case kGSTitleScreen:
		initVars0();
		break;
	case kGSMainGame:
		initVars1();
		break;
	case kGSStandaloneGame:
		initVars2();
		break;
	case kGSScoreCountUp:
		initVars3();
		break;
	}
}

void MinigameBbLoogie::initVars0() {
	_carDelay = 120;
	_bikeDelay = 250;
	_squirrelDelay = 40;
	_paperPlaneDelay = 400; // Uninitialized in the original
	_principalDelay = 1750;
	_levelTimeDelay = 58;
	_principalAngry = false;
	_squirrelDirection = false;
	_numberOfHits = 0;
	_megaLoogieCount = 0;
	_level = 0;
	_levelTimeLeft = 0;
	_currScore = 0;
	_dispLevelScore = 0;
}

void MinigameBbLoogie::initVars1() {
	_carDelay = 120;
	_bikeDelay = 250;
	_squirrelDelay = 40;
	_paperPlaneDelay = 400; // Uninitialized in the original
	_principalDelay = 1750;
	_squirrelDirection = false;
	_numberOfHits = 0;
	_megaLoogieCount = 0;
}

void MinigameBbLoogie::initVars2() {
	_timeBonusCtr = _levelTimeLeft;
	_levelTimeDelay = 58;
	_bonusDisplayDelay1 = 60;
	_levelTimeLeft = kLevelTimes[_level];
	_nextLevelScore = kLevelScores[_level] + _currScore;
	_bonusDisplayDelay2 = 0;
	_bonusDisplayDelay3 = 0;
}

void MinigameBbLoogie::initVars3() {
	if (_currScore > _hiScore)
		_hiScore = _currScore;
	if (_playerKind) {
		playSound(11);
	} else {
		playSound(21);
	}
}

bool MinigameBbLoogie::updateStatus(int mouseX, int mouseY, uint mouseButtons) {
	switch (_gameState) {
	case kGSTitleScreen:
		return updateStatus0(mouseX, mouseY, mouseButtons);
	case kGSMainGame:
		return updateStatus1(mouseX, mouseY, mouseButtons);
	case kGSStandaloneGame:
		return updateStatus2(mouseX, mouseY, mouseButtons);
	case kGSScoreCountUp:
		return updateStatus3(mouseX, mouseY, mouseButtons);
	}
	return false;
}

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

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

	if (_objects[1].kind != 0 && isCursorAtObj(1)) {
		_objects[0].frameIndex = 1;
		_objects[1].kind = 0;
		_objects[3].kind = 11;
		_objects[3].frameIndex = 0;
		_objects[3].ticks = _objects[3].anim->frameTicks[0];
	} else if (!isCursorAtObj(3)) {
		if (_objects[4].kind == 0)
			_objects[0].frameIndex = 0;
		_objects[3].kind = 0;
		_objects[1].kind = 1;
	}

	if (_objects[2].kind && isCursorAtObj(2)) {
		_objects[0].frameIndex = 1;
		_objects[2].kind = 0;
		_objects[4].kind = 11;
		_objects[4].frameIndex = 0;
		_objects[4].ticks = _objects[4].anim->frameTicks[0];
	} else if (!isCursorAtObj(4)) {
		if (_objects[3].kind == 0)
			_objects[0].frameIndex = 0;
		_objects[4].kind = 0;
		_objects[2].kind = 1;
	}

	for (int i = 0; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		if (obj->kind == 11) {
			if (--obj->ticks == 0) {
				++obj->frameIndex;
				if (obj->frameIndex >= obj->anim->frameCount)
					obj->frameIndex = 0;
				obj->ticks = obj->anim->frameTicks[obj->frameIndex];
			}
		}
	}

	if ((mouseButtons & kLeftButtonDown) &&
		(_objects[3].kind != 0 || _objects[4].kind != 0)) {
		if (_objects[4].kind != 0) {
			// Beavis
			_playerKind = 0;
			_playerAnim = getAnimation(0);
			_playerSounds1 = kBeavisSounds1;
			_playerSounds1Count = 8;
			_playerSounds2 = kBeavisSounds2;
			_playerSounds2Count = 13;
			playSound(15);
			while (isSoundPlaying(15)) { }
		} else {
			// Butt-head
			_playerKind = 1;
			_playerAnim = getAnimation(1);
			_playerSounds1 = kButtheadSounds1;
			_playerSounds1Count = 5;
			_playerSounds2 = kButtheadSounds2;
			_playerSounds2Count = 10;
			playSound(23);
			while (isSoundPlaying(23)) { }
		}
		_gameState = _fromMainGame ? kGSMainGame : kGSStandaloneGame;
		initObjects1();
		initObjects();
		initVars();
		_gameTicks = 0;
	}

	return true;
}

bool MinigameBbLoogie::updateStatus1(int mouseX, int mouseY, uint mouseButtons) {

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

	if (!_fromMainGame && _levelTimeLeft == 0) {
		_gameState = kGSScoreCountUp;
		initObjects();
		initVars();
	} else if (_fromMainGame || _currScore < _nextLevelScore) {
		_objects->x = CLIP(mouseX, 0, 319);
		_objects->y = 240;
		if (!_principalAngry &&
			((mouseButtons & kLeftButtonDown) || ((mouseButtons & kRightButtonDown) && _megaLoogieCount)) &&
			_objects[0].status == 0 && mouseX != 32512 && mouseY != 32512) {
			_objects[0].ticks = _playerAnim->frameTicks[13];
			_objects[0].frameIndex = 14;
			_objects[0].status = 1;
			_objects[0].unk2 = 0;
			Obj *newObj = getFreeObject();
			newObj->anim = getAnimation(17);
			newObj->frameIndex = 0;
			newObj->ticks = 1;
			newObj->x = 0;
			newObj->y = 140;
			newObj->kind = 8;
			if (mouseButtons & kLeftButtonDown) {
				_doubleScore = 0;
				playSound(28);
			} else {
				_doubleScore = 17;
				playSound(30);
			}
		}
		updateObjs(mouseButtons);
	} else {
		_gameState = kGSStandaloneGame;
		++_level;
		initObjects();
		initVars();
	}
	return true;
}

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

	_objects[0].x = mouseX;

	if (_bonusDisplayDelay1 > 0) {
		if (--_bonusDisplayDelay1 == 0) {
			_bonusDisplayDelay2 = 60;
			if (_timeBonusCtr)
				playSound(33, true);
		}
	} else if (_bonusDisplayDelay2 > 0) {
		if (--_bonusDisplayDelay2 == 0) {
			_bonusDisplayDelay3 = 150;
			playSound(38);
		} else if (_timeBonusCtr > 0) {
			++_bonusDisplayDelay2;
			++_levelTimeLeft;
			if (--_timeBonusCtr == 0)
				stopSound(33);
		}
	} else if (_bonusDisplayDelay3 > 0) {
		if ((mouseButtons & kAnyButtonDown) || (--_bonusDisplayDelay3 == 0)) {
			_dispLevelScore = _nextLevelScore;
			_gameState = kGSMainGame;
			_gameTicks = 0;
		}
	}
	return true;
}

bool MinigameBbLoogie::updateStatus3(int mouseX, int mouseY, uint mouseButtons) {

	_objects[0].x = mouseX;

	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[obj->frameIndex];
			}
		}
	}

	return true;
}

void MinigameBbLoogie::updateObjs(uint mouseButtons) {

	for (int i = 0; i < kMaxObjectsCount; ++i) {
		Obj *obj = &_objects[i];
		switch (obj->kind) {
		case 1:
			updatePlayer(i, mouseButtons);
			break;
		case 2:
			updateObjKind2(i);
			break;
		case 3:
			updateLoogie(i);
			break;
		case 4:
			updateCar(i);
			break;
		case 5:
			updateBike(i);
			break;
		case 6:
			updateSquirrel(i);
			break;
		case 7:
			updatePaperPlane(i);
			break;
		case 8:
			updateIndicator(i);
			break;
		case 9:
			updatePrincipal(i);
			break;
		}
	}

	if (--_carDelay == 0) {
		// Car
		Obj *obj = getFreeObject();
		obj->anim = getAnimation(2);
		obj->kind = 4;
		obj->frameIndex = 0;
		obj->x = 379;
		obj->y = 22;
		obj->xIncr = -2;
		obj->yIncr = 0;
		_carDelay = _vm->getRandom(256) + 800;
	}

	if (--_bikeDelay == 0) {
		// Bike
		Obj *obj = getFreeObject();
		obj->kind = 5;
		obj->anim = getAnimation(3);
		obj->frameIndex = 0;
		obj->x = 360;
		obj->y = _vm->getRandom(32) + 82;
		obj->xIncr = -1;
		obj->yIncr = 0;
		_bikeDelay = _vm->getRandom(512) + 500;
	}

	if (--_squirrelDelay == 0) {
		// Squirrel
		Obj *obj = getFreeObject();
		obj->kind = 6;
		obj->anim = getAnimation(7);
		obj->frameIndex = !_squirrelDirection ? 0 : 29;
		obj->x = 160;
		obj->y = 36;
		obj->xIncr = 0;
		obj->yIncr = 0;
		_squirrelDirection = !_squirrelDirection;
		if (_vm->getRandom(5) == 1 && !isAnySoundPlaying(_playerSounds2, _playerSounds2Count))
			playSound(9);
		_squirrelDelay = _vm->getRandom(512) + 300;
	}

	if (--_paperPlaneDelay == 0) {
		// Paper plane
		Obj *obj = getFreeObject();
		obj->kind = 7;
		obj->anim = getAnimation(16);
		obj->frameIndex = 0;
		obj->x = 86;
		obj->y = 187;
		obj->xIncr = 0;
		obj->yIncr = -1;
		switch (_vm->getRandom(3)) {
		case 1:
			obj->frameIndex = 1;
			obj->xIncr = -1;
			break;
		case 2:
			obj->frameIndex = 7;
			obj->xIncr = 1;
			break;
		}
		_paperPlaneDelay = 400;
	}

	if (_principalDelay >= 0 && --_principalDelay == 0) {
		// Principal
		Obj *obj = getFreeObject();
		obj->kind = 9;
		obj->anim = getAnimation(18);
		obj->frameIndex = 11;
		obj->x = -20;
		obj->y = 130;
		obj->xIncr = 1;
		obj->yIncr = 0;
		obj->status = 0;
		obj->frameIndexAdd = 0;
		obj->unk2 = _vm->getRandom(256) + 100;
		_principalCtr = 0;
		_principalFirstFrameIndex = 11;
		_principalLastFrameIndex = 16;
	}

}

void MinigameBbLoogie::updatePlayer(int objIndex, uint mouseButtons) {

	Obj *obj = &_objects[0];

	switch (obj->status) {

	case 1:
		if (obj->ticks-- == 0) {
			if (obj->frameIndex != 15) {
				++obj->frameIndex;
				obj->ticks = _playerAnim->frameTicks[obj->frameIndex];
			}
		}
		if ((((mouseButtons & kLeftButtonDown) && _doubleScore == 0) ||
			((mouseButtons & kRightButtonDown) && _doubleScore == 17))
			&& obj->unk2 != 61) {
			++obj->unk2;
		} else {
			obj->status = 2;
			obj->frameIndex = 16;
			obj->ticks = _playerAnim->frameTicks[16];
			if (obj->unk2 >= 30) {
				obj->status = 3;
				obj->frameIndex = 21;
				obj->ticks = _playerAnim->frameTicks[21];
			}
			if (obj->unk2 < 30) {
				Obj *newObj = getFreeObject();
				newObj->kind = 3;
				newObj->anim = getAnimation(5);
				newObj->frameIndex = 0;
				newObj->ticks = getAnimation(5)->frameTicks[0];
				newObj->x = obj->x;
				newObj->y = 172;
				newObj->unk2 = obj->unk2;
				newObj->frameIndexAdd = _doubleScore;
				if (_doubleScore)
					--_megaLoogieCount;
			}
			if (_doubleScore) {
				stopSound(30);
				playSound(31);
			} else {
				stopSound(28);
				playSound(29);
			}
		}
		break;

	case 2:
		if (obj->ticks-- == 0) {
			if (obj->frameIndex == 17) {
				obj->frameIndex = 0;
				obj->status = 0;
			} else {
				++obj->frameIndex;
				obj->ticks = _playerAnim->frameTicks[obj->frameIndex];
			}
		}
		break;

	case 3:
		if (obj->ticks-- == 0) {
			if (obj->frameIndex == 23) {
				obj->frameIndex = 0;
				obj->status = 0;
			} else {
				++obj->frameIndex;
				obj->ticks = _playerAnim->frameTicks[obj->frameIndex];
				if (obj->frameIndex == 22) {
					Obj *newObj = getFreeObject();
					newObj->kind = 3;
					newObj->anim = getAnimation(5);
					newObj->frameIndex = 0;
					newObj->ticks = getAnimation(5)->frameTicks[0];
					newObj->x = obj->x;
					newObj->y = 154;
					newObj->unk2 = obj->unk2;
					newObj->frameIndexAdd = _doubleScore;
					if (_doubleScore)
						--_megaLoogieCount;
				}
			}
		}
		break;

	}

}

void MinigameBbLoogie::updateObjKind2(int objIndex) {

	Obj *obj = &_objects[objIndex];

	if (obj->ticks-- == 0) {
		obj->ticks = getAnimation(4)->frameTicks[0];
		if (obj->frameIndex > 7)
			obj->frameIndex = 1;
		if (obj->frameIndex++ >= 7)
			obj->frameIndex = 0;
	}

}

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

	if (obj->unk2 > 0) {
		obj->y -= kLoogieOffY[obj->unk2 / 8];
		--obj->unk2;
	}

	if (obj->ticks-- == 0) {
		obj->ticks = getAnimation(5)->frameTicks[0];
		++obj->frameIndex;
		if (obj->frameIndex >= 17) {
			obj->kind = 0;
			obj->anim = getAnimation(6);
			obj->frameIndex = 0;
		}
	}

}

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

	obj->x += obj->xIncr;

	if (obj->ticks-- == 0) {
		if (obj->frameIndex++ == 3 || obj->frameIndex == 6)
			obj->frameIndex = 0;
		obj->ticks = getAnimation(2)->frameTicks[obj->frameIndex];
	}

	if (obj->x <= -60) {
		obj->kind = 0;
		obj->anim = getAnimation(6);
		obj->frameIndex = 0;
	} else if (!_principalAngry && obj->frameIndex <= 3) {
		int loogieObjIndex = 0;
		Obj *loogieObj = findLoogieObj(loogieObjIndex++);
		while (loogieObj) {
			if (loogieObj->frameIndex >= 8 && loogieObj->frameIndex <= 10 && isHit(obj, loogieObj)) {
				incNumberOfHits();
				incScore(7);
				loogieObj->frameIndex = 13;
				loogieObj->ticks = getAnimation(5)->frameTicks[12];
				obj->frameIndex = 4;
				obj->ticks = getAnimation(2)->frameTicks[4];
				playSound(34);
				playRndSound();
			}
			loogieObj = findLoogieObj(loogieObjIndex++);
		}
	}

}

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

	obj->x += obj->xIncr;

	if (obj->ticks-- == 0) {
		if (obj->frameIndex++ == 3 || obj->frameIndex == 7)
			obj->frameIndex = 0;
		obj->ticks = getAnimation(3)->frameTicks[obj->frameIndex];
	}

	if (obj->x == -40) {
		obj->kind = 0;
		obj->anim = getAnimation(6);
		obj->frameIndex = 0;
	} else if (!_principalAngry && obj->frameIndex <= 3) {
		int loogieObjIndex = 0;
		Obj *loogieObj = findLoogieObj(loogieObjIndex++);
		while (loogieObj) {
			if (loogieObj->frameIndex >= 7 && loogieObj->frameIndex <= 11 && isHit(obj, loogieObj)) {
				incNumberOfHits();
				incScore(2);
				loogieObj->frameIndex = 13;
				loogieObj->ticks = getAnimation(5)->frameTicks[12];
				obj->frameIndex = 4;
				obj->ticks = getAnimation(3)->frameTicks[4];
				playSound(35);
				playRndSound();
			}
			loogieObj = findLoogieObj(loogieObjIndex++);
		}
	}

}

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

	if (obj->ticks-- == 0) {
		++obj->frameIndex;
		if (obj->frameIndex == 29 || obj->frameIndex == 54 ||
			obj->frameIndex == 58 || obj->frameIndex == 62) {
			obj->kind = 0;
			obj->anim = getAnimation(6);
			obj->frameIndex = 0;
		}
		obj->ticks = getAnimation(7)->frameTicks[obj->frameIndex];
	}

	if (!_principalAngry && obj->frameIndex <= 53) {
		int loogieObjIndex = 0;
		Obj *loogieObj = findLoogieObj(loogieObjIndex++);
		while (loogieObj) {
			if (loogieObj->frameIndex >= 7 && loogieObj->frameIndex <= 9 && isHit(obj, loogieObj)) {
				incNumberOfHits();
				incScore(10);
				loogieObj->frameIndex = 13;
				loogieObj->ticks = getAnimation(5)->frameTicks[12];
				obj->x += kSquirrelOffX[obj->frameIndex];
				obj->frameIndex = obj->frameIndex < 29 ? 54 : 58;
				obj->ticks = getAnimation(7)->frameTicks[obj->frameIndex];
				playSound(36);
				playRndSound();
			}
			loogieObj = findLoogieObj(loogieObjIndex++);
		}
  	}

}

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

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

	if (obj->x == -16 || obj->x == 336 || obj->y == -16) {
		obj->kind = 0;
		obj->anim = getAnimation(6);
		obj->frameIndex = 0;
	}

	if (!_principalAngry && obj->frameIndex <= 53) {
		int loogieObjIndex = 0;
		Obj *loogieObj = findLoogieObj(loogieObjIndex++);
		while (loogieObj) {
			if (loogieObj->frameIndex >= 4 && loogieObj->frameIndex <= 7 && isHit(obj, loogieObj)) {
				incNumberOfHits();
				incScore(5);
				loogieObj->frameIndex = 13;
				loogieObj->ticks = getAnimation(5)->frameTicks[12];
				obj->frameIndex = (obj->frameIndex + 1) % 8;
				obj->xIncr = kPlaneOffX[obj->frameIndex];
				obj->yIncr = kPlaneOffY[obj->frameIndex];
				playSound(37);
				playRndSound();
			}
			loogieObj = findLoogieObj(loogieObjIndex++);
		}
	}

}

void MinigameBbLoogie::updateIndicator(int objIndex) {
	Obj *obj = &_objects[objIndex];
	Obj *loogieObj = &_objects[0];

	if (obj->ticks-- == 0) {
		obj->frameIndex = (obj->frameIndex + 1) % 2;
		obj->ticks = getAnimation(17)->frameTicks[0];
	}

	if (loogieObj->status != 0) {
		int unk2mod = loogieObj->unk2 / 8;
		int unk2div = loogieObj->unk2 / 8 * 8;
		int v6 = 0;
		if (unk2div > 8) {
			int v7 = 1;
			do {
				v6 += 8 * kLoogieOffY[v7++];
			} while (v7 != unk2mod);
		}
		int yOfs = (loogieObj->unk2 % 8 + 1) * kLoogieOffY[loogieObj->unk2 / 8] + v6;
		if (loogieObj->unk2 >= 30)
			yOfs += 18;
		obj->y = 140 - yOfs;
	} else {
		obj->kind = 0;
		obj->anim = getAnimation(6);
	}

}

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

	switch (obj->status) {

	case 0:
		if (obj->unk2--) {
			if (obj->ticks-- == 0) {
				++obj->frameIndex;
				if (obj->frameIndex == _principalLastFrameIndex)
					obj->frameIndex = _principalFirstFrameIndex;
				obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
			}
			++_principalCtr;
			if (_principalCtr % 2 != 0) {
				obj->x += obj->xIncr;
				obj->y += obj->yIncr;
				if (obj->xIncr > 0 && obj->x == 340) {
					obj->xIncr = -1;
					_principalLastFrameIndex = 34;
					_principalFirstFrameIndex = 29;
					obj->frameIndex = 29;
					obj->status = 2;
					obj->ticks = _vm->getRandom(256) + 60;
				}
				if (obj->xIncr < 0 && obj->x == -20) {
					obj->xIncr = 1;
					_principalLastFrameIndex = 16;
					_principalFirstFrameIndex = 11;
					obj->frameIndex = 11;
					obj->status = 2;
					obj->ticks = _vm->getRandom(256) + 60;
				}
			}
		} else {
			obj->unk2 = _vm->getRandom(64) + 20;
			++obj->status;
			if (_vm->getRandom(2) == 1) {
				obj->frameIndex = _principalFirstFrameIndex < 11 ? 17 : 26;
				_principalFirstFrameIndex = 19;
			} else {
				obj->frameIndex = _principalFirstFrameIndex < 11 ? 8 : 35;
				_principalFirstFrameIndex = 1;
			}
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		break;

	case 1:
		if (obj->unk2--) {
			if (obj->ticks-- == 0)
				obj->frameIndex = _principalFirstFrameIndex;
		} else {
			obj->unk2 = _vm->getRandom(256) + 100;
			++obj->status;
			if (_vm->getRandom(2) == 1) {
				_principalLastFrameIndex = 16;
				_principalFirstFrameIndex = 11;
				obj->frameIndex = obj->frameIndex < 1 ? 8 : 17;
				obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
				obj->xIncr = 1;
			} else {
				_principalLastFrameIndex = 34;
				_principalFirstFrameIndex = 29;
				obj->frameIndex = obj->frameIndex < 1 ? 35 : 26;
				obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
				obj->xIncr = -1;
			}
		}
		break;

	case 2:
		if (obj->ticks-- == 0) {
			obj->status = 0;
			obj->frameIndex = _principalFirstFrameIndex;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		break;

	case 3:
		if (obj->ticks-- == 0) {
			obj->status = _prevPrincipalStatus;
			obj->frameIndex = _principalFirstFrameIndex;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		break;

	case 4:
		if (obj->ticks-- == 0) {
			switch (obj->frameIndex) {
			case 8:
				obj->frameIndex = 36;
				break;
			case 26:
				obj->frameIndex = 28;
				break;
			case 28:
				obj->frameIndex = 35;
				break;
			case 35:
				++obj->frameIndex;
				break;
			case 36:
				obj->status = 5;
				++obj->frameIndex;
				break;
			}
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		break;

	case 5:
		if (obj->ticks-- == 0) {
			++obj->frameIndex;
			if (obj->frameIndex == 48)
				obj->frameIndex = 36;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		if (!isSoundPlaying(1)) {
			_gameResult = true;
			if (_fromMainGame) {
				_principalAngry = true;
				if (obj->x <= 140 || obj->x >= 165) {
					obj->status = 6;
					if (obj->x >= 160) {
						_principalLastFrameIndex = 34;
						_principalFirstFrameIndex = 29;
						obj->frameIndex = 29;
						obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
						obj->xIncr = -1;
					} else {
						_principalLastFrameIndex = 16;
						_principalFirstFrameIndex = 11;
						obj->frameIndex = 11;
						obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
						obj->xIncr = 1;
					}
				} else {
					obj->status = 7;
					_principalFirstFrameIndex = 2;
					_principalLastFrameIndex = 7;
					obj->frameIndex = 2;
					obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
					obj->xIncr = 0;
					obj->yIncr = 1;
				}
			} else {
				obj->status = _prevPrincipalStatus;
				obj->frameIndex = _principalFirstFrameIndex;
				obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
			}
		}
		break;

	case 6:
		obj->x += obj->xIncr;
		obj->y += obj->yIncr;
		if (obj->ticks-- == 0) {
			++obj->frameIndex;
			if (obj->frameIndex == _principalLastFrameIndex)
				obj->frameIndex = _principalFirstFrameIndex;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		if (obj->x > 145 && obj->x < 160) {
			obj->status = 7;
			_principalFirstFrameIndex = 2;
			_principalLastFrameIndex = 7;
			obj->frameIndex = 2;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
			obj->xIncr = 0;
			obj->yIncr = 1;
		}
		break;

	case 7:
		obj->x += obj->xIncr;
		obj->y += obj->yIncr;
		if (obj->ticks-- == 0) {
			++obj->frameIndex;
			if (obj->frameIndex == _principalLastFrameIndex)
				obj->frameIndex = _principalFirstFrameIndex;
			obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
		}
		if (obj->y > 175) {
			// Angry principal enters school, end the minigame
			_gameDone = true;
		}
		break;

	}

	if (!_principalAngry) {
		int loogieObjIndex = 0;
		Obj *loogieObj = findLoogieObj(loogieObjIndex++);
		while (loogieObj) {
			if (loogieObj->frameIndex >= 7 && loogieObj->frameIndex <= 12 && isHit(obj, loogieObj)) {
				incNumberOfHits();
				incScore(1);
				loogieObj->frameIndex = 13;
				loogieObj->ticks = getAnimation(5)->frameTicks[12];
				if (obj->status != 3 && obj->status != 4 && obj->status != 5) {
					_prevPrincipalStatus = obj->status;
					obj->status = 3;
					if (_principalFirstFrameIndex == 1 || _principalFirstFrameIndex == 19)
						obj->frameIndex = _principalFirstFrameIndex - 1;
					else
						obj->frameIndex = _principalFirstFrameIndex - 2;
					obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
					if (loogieObj->frameIndexAdd > 0) {
						obj->status = 4;
						switch (obj->frameIndex) {
						case 0:
							obj->frameIndex = 36;
							break;
						case 9:
							obj->frameIndex = 8;
							break;
						case 27:
							obj->frameIndex = 35;
							break;
						case 18:
							obj->frameIndex = 26;
							break;
						}
						obj->ticks = getAnimation(18)->frameTicks[obj->frameIndex];
						playSound(1);
					} else {
						if (!isAnySoundPlaying(_playerSounds2, _playerSounds2Count))
							playSound(kPrincipalSounds[_vm->getRandom(4)]);
						playRndSound();
					}
				}
			}
			loogieObj = findLoogieObj(loogieObjIndex++);
		}
	}

}

void MinigameBbLoogie::incNumberOfHits() {
	++_numberOfHits;
	if (_numberOfHits == 1000)
		_numberOfHits = 0;
	if (_numberOfHits % 10 == 0) {
		++_megaLoogieCount;
		if (_megaLoogieCount > 11)
			_megaLoogieCount = 11;
	}
}

void MinigameBbLoogie::incScore(int incrAmount) {
	if (_doubleScore)
		_currScore += 2 * incrAmount;
	else
		_currScore += incrAmount;
}

void MinigameBbLoogie::playRndSound() {
	if (!isAnySoundPlaying(_playerSounds2, _playerSounds2Count))
		playSound(_playerSounds1[_vm->getRandom(_playerSounds1Count)]);
}

bool MinigameBbLoogie::run(bool fromMainGame) {

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

	_numbersAnim = getAnimation(9);

	_backgroundSpriteIndex = 210;
	_titleScreenSpriteIndex = 211;

	_fromMainGame = fromMainGame;

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

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

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

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

	loadSounds();

	playSound(32, true);

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

	_vm->_sound->unloadSounds();

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

	delete _spriteModule;

	return _gameResult;
}

void MinigameBbLoogie::update() {

	int inputTicks;

	if (_gameTicks > 0) {
		int currTicks = _vm->_system->getMillis();
		inputTicks = (currTicks - _gameTicks) / 17;
		_gameTicks = currTicks - (currTicks - _gameTicks) % 17;
	} 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 MinigameBbLoogie::loadSounds() {
	for (uint i = 0; i < kSoundFilenamesCount; ++i) {
		Common::String filename = Common::String::format("bbloogie/%s", kSoundFilenames[i]);
		_vm->_sound->loadSound(filename.c_str());
	}
}

} // End of namespace Bbvs