/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "common/debug.h"
#include "common/rect.h"
#include "common/savefile.h"
#include "common/system.h"

#include "graphics/cursorman.h"

#include "alg/graphics.h"
#include "alg/logic/game_johnnyrock.h"
#include "alg/scene.h"

namespace Alg {

GameJohnnyRock::GameJohnnyRock(AlgEngine *vm, const AlgGameDescription *gd) : Game(vm) {
}

GameJohnnyRock::~GameJohnnyRock() {
	for (auto item : *_difficultyIcon) {
		item->free();
		delete item;
	}
	if (_levelIcon) {
		_levelIcon->free();
		delete _levelIcon;
	}
	if (_bulletholeIcon) {
		_bulletholeIcon->free();
		delete _bulletholeIcon;
	}
}

void GameJohnnyRock::init() {
	Game::init();

	_videoPosX = 11;
	_videoPosY = 2;

	setupCursorTimer();

	if(_vm->useSingleSpeedVideos()) {
		loadLibArchive("johnroc.lib");
	} else {
		loadLibArchive("johnrocd.lib");
	}

	_sceneInfo->loadScnFile("johnroc.scn");
	_startScene = _sceneInfo->getStartScene();

	registerScriptFunctions();
	verifyScriptFunctions();

	_menuzone = new Zone("MainMenu", "GLOBALHIT");
	_menuzone->addRect(0x0C, 0xBB, 0x3C, 0xC7, nullptr, 0, "SHOTMENU", "0");

	_submenzone = new Zone("SubMenu", "GLOBALHIT");
	_submenzone->addRect(0x10, 0x0F, 0x78, 0x34, nullptr, 0, "STARTMENU", "0");
	_submenzone->addRect(0x10, 0x8E, 0x8A, 0xAF, nullptr, 0, "CONTMENU", "0");
	_submenzone->addRect(0x10, 0x3A, 0x6A, 0x5C, nullptr, 0, "RECTSAVE", "0");
	_submenzone->addRect(0x10, 0x64, 0x84, 0x99, nullptr, 0, "RECTLOAD", "0");
	_submenzone->addRect(0xD2, 0x8D, 0x12F, 0xB0, nullptr, 0, "EXITMENU", "0");
	_submenzone->addRect(0xD0, 0x35, 0x123, 0x51, nullptr, 0, "RECTEASY", "0");
	_submenzone->addRect(0xD2, 0x50, 0x125, 0x6B, nullptr, 0, "RECTAVG", "0");
	_submenzone->addRect(0xD2, 0x6D, 0x122, 0x86, nullptr, 0, "RECTHARD", "0");

	_shotSound = loadSoundFile("blow.8b");
	_emptySound = loadSoundFile("empty.8b");
	_saveSound = loadSoundFile("saved.8b");
	_loadSound = loadSoundFile("loaded.8b");
	_skullSound = loadSoundFile("money.8b");

	_gun = AlgGraphics::loadScreenCoordAniImage("gun.ani", _palette);
	_numbers = AlgGraphics::loadAniImage("numbers.ani", _palette);
	_difficultyIcon = AlgGraphics::loadAniImage("diff.ani", _palette);
	auto level = AlgGraphics::loadScreenCoordAniImage("level.ani", _palette);
	_levelIcon = (*level)[0];
	auto hole = AlgGraphics::loadScreenCoordAniImage("hole.ani", _palette);
	_bulletholeIcon = (*hole)[0];

	_background = AlgGraphics::loadVgaBackground("backgrnd.vga", _palette);
	_screen->copyRectToSurface(_background->getPixels(), _background->pitch, 0, 0, _background->w, _background->h);

	moveMouse();
}

// FIXME: Need to unregisterScriptFunctions() in destructor to delete all allocated JRScriptFunctionRect and prevent reallocation to fix memory leakage here...
void GameJohnnyRock::registerScriptFunctions() {
#define RECT_HIT_FUNCTION(name, func) _rectHitFuncs[name] = new JRScriptFunctionRect(this, &GameJohnnyRock::func);
	RECT_HIT_FUNCTION("DEFAULT", rectNewScene);
	RECT_HIT_FUNCTION("NEWSCENE", rectNewScene);
	RECT_HIT_FUNCTION("EXITMENU", rectExit);
	RECT_HIT_FUNCTION("CONTMENU", rectContinue);
	RECT_HIT_FUNCTION("STARTMENU", rectStart);
	RECT_HIT_FUNCTION("SHOTMENU", rectShotMenu);
	RECT_HIT_FUNCTION("RECTSAVE", rectSave);
	RECT_HIT_FUNCTION("RECTLOAD", rectLoad);
	RECT_HIT_FUNCTION("RECTEASY", rectEasy);
	RECT_HIT_FUNCTION("RECTAVG", rectAverage);
	RECT_HIT_FUNCTION("RECTHARD", rectHard);
	RECT_HIT_FUNCTION("KILLINNOCENT", rectKillInnocent);
	RECT_HIT_FUNCTION("SELCASINO", rectSelectCasino);
	RECT_HIT_FUNCTION("SELPOOLH", rectSelectPoolhall);
	RECT_HIT_FUNCTION("SELWAREHSE", rectSelectWarehouse);
	RECT_HIT_FUNCTION("SELGARAGE", rectSelectGarage);
	RECT_HIT_FUNCTION("SELMANSION", rectSelectMansion);
	RECT_HIT_FUNCTION("SELAMMO", rectSelectAmmo);
	RECT_HIT_FUNCTION("SELOFFICE", rectSelectOffice);
	RECT_HIT_FUNCTION("MANBUST", rectShotManBust);
	RECT_HIT_FUNCTION("WOMANBUST", rectShotWomanBust);
	RECT_HIT_FUNCTION("BLUEVASE", rectShotBlueVase);
	RECT_HIT_FUNCTION("CAT", rectShotCat);
	RECT_HIT_FUNCTION("INDIAN", rectShotIndian);
	RECT_HIT_FUNCTION("PLATE", rectShotPlate);
	RECT_HIT_FUNCTION("BLUEDRESSPIC", rectShotBlueDressPic);
	RECT_HIT_FUNCTION("MODERNPIC", rectShotModernPic);
	RECT_HIT_FUNCTION("MONALISA", rectShotMonaLisa);
	RECT_HIT_FUNCTION("GWASHINGTON", rectShotGWashington);
	RECT_HIT_FUNCTION("BOYINREDPIC", rectShotBoyInRedPic);
	RECT_HIT_FUNCTION("COATOFARMS", rectShotCoatOfArms);
	RECT_HIT_FUNCTION("COMBNOA0", rectShotCombinationA0);
	RECT_HIT_FUNCTION("COMBNOA1", rectShotCombinationA1);
	RECT_HIT_FUNCTION("COMBNOA2", rectShotCombinationA2);
	RECT_HIT_FUNCTION("COMBNOA3", rectShotCombinationA3);
	RECT_HIT_FUNCTION("COMBNOA4", rectShotCombinationA4);
	RECT_HIT_FUNCTION("COMBNOA5", rectShotCombinationA5);
	RECT_HIT_FUNCTION("COMBNOB0", rectShotCombinationB0);
	RECT_HIT_FUNCTION("COMBNOB1", rectShotCombinationB1);
	RECT_HIT_FUNCTION("COMBNOB2", rectShotCombinationB2);
	RECT_HIT_FUNCTION("COMBNOB3", rectShotCombinationB3);
	RECT_HIT_FUNCTION("COMBNOB4", rectShotCombinationB4);
	RECT_HIT_FUNCTION("COMBNOB5", rectShotCombinationB5);
	RECT_HIT_FUNCTION("LUCKNO0", rectShotLuckyNumber0);
	RECT_HIT_FUNCTION("LUCKNO1", rectShotLuckyNumber1);
	RECT_HIT_FUNCTION("LUCKNO2", rectShotLuckyNumber2);
	RECT_HIT_FUNCTION("LUCKNO3", rectShotLuckyNumber3);
	RECT_HIT_FUNCTION("LUCKNO4", rectShotLuckyNumber4);
	RECT_HIT_FUNCTION("LUCKNO5", rectShotLuckyNumber5);
#undef RECT_HIT_FUNCTION

#define PRE_OPS_FUNCTION(name, func) _scenePreOps[name] = new JRScriptFunctionScene(this, &GameJohnnyRock::func);
	PRE_OPS_FUNCTION("DRAWRCT", scenePsoDrawRct);
	PRE_OPS_FUNCTION("PAUSE", scenePsoPause);
	PRE_OPS_FUNCTION("FADEIN", scenePsoFadeIn);
	PRE_OPS_FUNCTION("PAUSFI", scenePsoPauseFadeIn);
	PRE_OPS_FUNCTION("PREREAD", scenePsoPreRead);
	PRE_OPS_FUNCTION("PAUSPR", scenePsoPausePreRead);
	PRE_OPS_FUNCTION("DEFAULT", scenePsoDrawRct);
	PRE_OPS_FUNCTION("DRAWRCTFDI", scenePsoDrawRctFadeIn);
#undef PRE_OPS_FUNCTION

#define INS_OPS_FUNCTION(name, func) _sceneInsOps[name] = new JRScriptFunctionScene(this, &GameJohnnyRock::func);
	INS_OPS_FUNCTION("DEFAULT", sceneIsoDoNothing);
	INS_OPS_FUNCTION("PAUSE", sceneIsoPause);
	INS_OPS_FUNCTION("SPAUSE", sceneIsoShootpastPause);
	INS_OPS_FUNCTION("STARTGAME", sceneIsoStartGame);
	INS_OPS_FUNCTION("SHOOTPAST", sceneIsoShootpast);
	INS_OPS_FUNCTION("GOTTOCASINO", sceneIsoGotoCasino);
	INS_OPS_FUNCTION("GOTTOPOOLH", sceneIsoGotoPoolhall);
	INS_OPS_FUNCTION("GOTTOWAREHSE", sceneIsoGotoWarehouse);
	INS_OPS_FUNCTION("INWAREHSE2", sceneIsoInWarehouse2);
	INS_OPS_FUNCTION("INWAREHSE3", sceneIsoInwarehouse3);
	INS_OPS_FUNCTION("GOTOGARAGE", sceneIsoGotoGarage);
	INS_OPS_FUNCTION("GOTOMANSION", sceneIsoGotoMansion);
	INS_OPS_FUNCTION("INMANSION1", sceneIsoInMansion1);
#undef INS_OPS_FUNCTION

#define NXT_SCN_FUNCTION(name, func) _sceneNxtScn[name] = new JRScriptFunctionScene(this, &GameJohnnyRock::func);
	NXT_SCN_FUNCTION("DEFAULT", sceneDefaultNxtscn);
	NXT_SCN_FUNCTION("DRAWGUN", sceneDefaultNxtscn);
	NXT_SCN_FUNCTION("DIED", sceneNxtscnDied);
	NXT_SCN_FUNCTION("BOMBDEAD", sceneNxtscnBombDead);
	NXT_SCN_FUNCTION("PIKUNDRTAKR", sceneNxtscnPickUndertaker);
	NXT_SCN_FUNCTION("CALLATTRACT", sceneNxtscnCallAttract);
	NXT_SCN_FUNCTION("PICKLUCKNO", sceneNxtscnPickLuckyNumber);
	NXT_SCN_FUNCTION("PICKMAP", sceneNxtscnPickMap);
	NXT_SCN_FUNCTION("PICKCLUE", sceneNxtscnPickClue);
	NXT_SCN_FUNCTION("MAPTIMEOUT", sceneNxtscnMapTimeout);
	NXT_SCN_FUNCTION("ENTCASINO", sceneNxtscnEnterCasino);
	NXT_SCN_FUNCTION("CASINOWHAT?", sceneNxtscnCasinoWhat);
	NXT_SCN_FUNCTION("ENTPOOLH", sceneNxtscnEnterPoolhall);
	NXT_SCN_FUNCTION("POOLHCLUE", sceneNxtscnPoolhallClue);
	NXT_SCN_FUNCTION("ENTWAREHSE", sceneNxtscnEnterWarehouse);
	NXT_SCN_FUNCTION("WAREHSECLUE", sceneNxtscnWarehouseClue);
	NXT_SCN_FUNCTION("ENTGARAGE", sceneNxtscnEnterGarage);
	NXT_SCN_FUNCTION("GARAGECLUE", sceneNxtscnGarageClue);
	NXT_SCN_FUNCTION("ENTMANSION", sceneNxtscnEnterMansion);
	NXT_SCN_FUNCTION("GIVECLUE", sceneNxtscnGiveClue);
	NXT_SCN_FUNCTION("PICKFLOWERMAN", sceneNxtscnPickFlowerMan);
	NXT_SCN_FUNCTION("RANDOMSCENE", sceneNxtscnRandomScene);
	NXT_SCN_FUNCTION("ENDRANDSCENE", sceneNxtscnEndRandScene);
	NXT_SCN_FUNCTION("SCN_KILLINNOCENT", sceneNxtscnKillInnocent);
#undef NXT_SCN_FUNCTION

	_zonePtrFb["DEFAULT"] = new JRScriptFunctionPoint(this, &GameJohnnyRock::zoneBullethole);
	_sceneShowMsg["DEFAULT"] = new JRScriptFunctionScene(this, &GameJohnnyRock::sceneSmDonothing);
	_sceneWepDwn["DEFAULT"] = new JRScriptFunctionScene(this, &GameJohnnyRock::sceneDefaultWepdwn);
	_sceneScnScr["DEFAULT"] = new JRScriptFunctionScene(this, &GameJohnnyRock::sceneDefaultScore);
	_sceneNxtFrm["DEFAULT"] = new JRScriptFunctionScene(this, &GameJohnnyRock::sceneNxtfrm);
}

void GameJohnnyRock::verifyScriptFunctions() {
	auto scenes = _sceneInfo->getScenes();
	for (auto scene : *scenes) {
		getScriptFunctionScene(PREOP, scene->_preop);
		getScriptFunctionScene(SHOWMSG, scene->_scnmsg);
		getScriptFunctionScene(INSOP, scene->_insop);
		getScriptFunctionScene(WEPDWN, scene->_wepdwn);
		getScriptFunctionScene(SCNSCR, scene->_scnscr);
		getScriptFunctionScene(NXTFRM, scene->_nxtfrm);
		getScriptFunctionScene(NXTSCN, scene->_nxtscn);
		for (auto zone : scene->_zones) {
			for (auto rect : zone->_rects) {
				getScriptFunctionRectHit(rect->_rectHit);
			}
		}
	}
}

JRScriptFunctionPoint GameJohnnyRock::getScriptFunctionZonePtrFb(Common::String name) {
	auto it = _zonePtrFb.find(name);
	if (it != _zonePtrFb.end()) {
		return *it->_value;
	} else {
		error("GameJohnnyRock::getScriptFunctionZonePtrFb(): Could not find zonePtrFb function: %s", name.c_str());
	}
}

JRScriptFunctionRect GameJohnnyRock::getScriptFunctionRectHit(Common::String name) {
	auto it = _rectHitFuncs.find(name);
	if (it != _rectHitFuncs.end()) {
		return *it->_value;
	} else {
		error("GameJohnnyRock::getScriptFunctionRectHit(): Could not find rectHit function: %s", name.c_str());
	}
}

JRScriptFunctionScene GameJohnnyRock::getScriptFunctionScene(SceneFuncType type, Common::String name) {
	JRScriptFunctionSceneMap *functionMap;
	switch (type) {
	case PREOP:
		functionMap = &_scenePreOps;
		break;
	case SHOWMSG:
		functionMap = &_sceneShowMsg;
		break;
	case INSOP:
		functionMap = &_sceneInsOps;
		break;
	case WEPDWN:
		functionMap = &_sceneWepDwn;
		break;
	case SCNSCR:
		functionMap = &_sceneScnScr;
		break;
	case NXTFRM:
		functionMap = &_sceneNxtFrm;
		break;
	case NXTSCN:
		functionMap = &_sceneNxtScn;
		break;
	default:
		error("GameJohnnyRock::getScriptFunctionScene(): Unkown scene script type: %u", type);
		break;
	}
	JRScriptFunctionSceneMap::iterator it;
	it = functionMap->find(name);
	if (it != functionMap->end()) {
		return *it->_value;
	} else {
		error("GameJohnnyRock::getScriptFunctionScene(): Could not find scene type %u function: %s", type, name.c_str());
	}
}

void GameJohnnyRock::callScriptFunctionZonePtrFb(Common::String name, Common::Point *point) {
	JRScriptFunctionPoint function = getScriptFunctionZonePtrFb(name);
	function(point);
}

void GameJohnnyRock::callScriptFunctionRectHit(Common::String name, Rect *rect) {
	JRScriptFunctionRect function = getScriptFunctionRectHit(name);
	function(rect);
}

void GameJohnnyRock::callScriptFunctionScene(SceneFuncType type, Common::String name, Scene *scene) {
	JRScriptFunctionScene function = getScriptFunctionScene(type, name);
	function(scene);
}

Common::Error GameJohnnyRock::run() {
	init();
	newGame();
	_curScene = _startScene;
	Common::String oldscene;
	while (!_vm->shouldQuit()) {
		_leftDown = false;
		oldscene = _curScene;
		_fired = false;
		Scene *scene = _sceneInfo->findScene(_curScene);
		if (!loadScene(scene)) {
			error("GameJohnnyRock::run(): Cannot find scene %s in libfile", scene->_name.c_str());
		}
		_paletteDirty = true;
		_nextFrameTime = getMsTime() + 100;
		callScriptFunctionScene(PREOP, scene->_preop, scene);
		_currentFrame = getFrame(scene);
		while (_currentFrame <= scene->_endFrame && _curScene == oldscene && !_vm->shouldQuit()) {
			updateMouse();
			// TODO: call scene->messageFunc
			callScriptFunctionScene(INSOP, scene->_insop, scene);
			_holster = weaponDown();
			if (_holster) {
				callScriptFunctionScene(WEPDWN, scene->_wepdwn, scene);
			}
			Common::Point firedCoords;
			if (fired(&firedCoords)) {
				if (!_holster) {
					Rect *hitGlobalRect = checkZone(_menuzone, &firedCoords);
					if (hitGlobalRect != nullptr) {
						callScriptFunctionRectHit(hitGlobalRect->_rectHit, hitGlobalRect);
					} else if (_shots > 0) {
						if (!_debug_unlimitedAmmo) {
							_shots--;
						}
						updateStat();
						Rect *hitRect = nullptr;
						Zone *hitSceneZone = checkZonesV1(scene, hitRect, &firedCoords);
						if (hitSceneZone != nullptr) {
							callScriptFunctionZonePtrFb(hitSceneZone->_ptrfb, &firedCoords);
							callScriptFunctionRectHit(hitRect->_rectHit, hitRect);
						} else {
							defaultBullethole(&firedCoords);
						}
					} else {
						playSound(_emptySound);
						_whichGun = 9;
					}
				}
			}
			if (_curScene == oldscene) {
				callScriptFunctionScene(NXTFRM, scene->_nxtfrm, scene);
			}
			displayScore();
			moveMouse();
			if (_pauseTime > 0) {
				_videoDecoder->pauseAudio(true);
			} else {
				_videoDecoder->pauseAudio(false);
			}
			if (_videoDecoder->getCurrentFrame() == 0) {
				_videoDecoder->getNextFrame();
			}
			updateScreen();
			int32 remainingMillis = _nextFrameTime - getMsTime();
			if (remainingMillis < 10) {
				if (_videoDecoder->getCurrentFrame() > 0) {
					_videoDecoder->getNextFrame();
				}
				remainingMillis = _nextFrameTime - getMsTime();
				_nextFrameTime = getMsTime() + (remainingMillis > 0 ? remainingMillis : 0) + 100;
			}
			if (remainingMillis > 0) {
				if (remainingMillis > 15) {
					remainingMillis = 15;
				}
				g_system->delayMillis(remainingMillis);
			}
			_currentFrame = getFrame(scene);
		}
		// frame limit reached or scene changed, prepare for next scene
		_hadPause = false;
		_pauseTime = 0;
		if (_retScene != "") {
			_curScene = _retScene;
			_retScene = "";
		}
		if (_subScene != "") {
			_retScene = _subScene;
			_subScene = "";
		}
		if (_curScene == oldscene) {
			callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
		}
		if (_curScene == "") {
			shutdown();
		}
	}
	removeCursorTimer();
	return Common::kNoError;
}

bool GameJohnnyRock::fired(Common::Point *point) {
	pollEvents();
	_fired = false;
	if (!_leftDown) {
		_buttonDown = false;
		return false;
	}
	if (_leftDown && !_inMenu) {
		_leftDown = true;
	}
	if (_buttonDown) {
		if (_thisGameTimer - _machGunTimer > 3) {
			_buttonDown = false;
			_mgunCnt++;
			if (_mgunCnt > 5) {
				_mgunCnt = 0;
				_leftDown = false;
			}
		}
		return false;
	}
	_fired = true;
	point->x = _mousePos.x;
	point->y = _mousePos.y;
	_machGunTimer = _thisGameTimer;
	_buttonDown = true;
	return true;
}

void GameJohnnyRock::newGame() {
	_gameMoney = 2000;
	_shots = 400;
	_score = 0;
	_holster = false;
	updateStat();
	_retScene = "";
	_subScene = "";
}

void GameJohnnyRock::resetParams() {
	_subScene = "";
	_moneyScene = "";
	_thisMap = _rnd->getRandomNumber(2);
	_clues = 0;
	_gotThisNumber = 0;
	_gotThisClue = 0;
	_thisClue = 0;
	_office = 0;
	_casino = 0;
	_poolHall = 0;
	_warehouse = 0;
	_garage = 0;
	_mansion = 0;
	_didContinue = 0;
	_thisDifficulty = _difficulty - 1;
	_casinoType = _rnd->getRandomNumber(1);
	_poolHallType = _rnd->getRandomNumber(2);
	_warehouseType = _rnd->getRandomNumber(2);
	_garageType = _rnd->getRandomNumber(1);
	_mapTimeout = 0;
	_ammoAgain = 0;
	_combinations[0] = _rnd->getRandomNumber(5);
	_combinations[1] = _rnd->getRandomNumber(5);
	_combinations[2] = _rnd->getRandomNumber(5);
	_combinations[3] = _rnd->getRandomNumber(5);
	_whoDidIt = _rnd->getRandomNumber(3);
	_inWarehouse = 0;
	_officeCount = 0;
	_hadGoToMansion = 0;
	_randomPlaceBits = 0;
	for (int i = 0; i < 5; i++) {
		// this assigns places from _randomPlaces
		uint16 picked = pickBits(&_randomPlaceBits, 6);
		_entranceIndex[i] = picked;
	}
	_randomPlaceBits = 0;
	for (uint8 i = 5; i < 19; i++) {
		// this assigns places from _randomPlacesMR
		uint16 picked = pickBits(&_randomPlaceBits, 8);
		_entranceIndex[i] = picked;
	}
	_maxRepeat = _thisDifficulty + 4;
	_repeatRandomPlace = 0;
	_gotTo = 0;
	updateStat();
}

void GameJohnnyRock::outShots() {
	_shots = 400;
	_score = 0;
	_holster = false;
	updateStat();
}

void GameJohnnyRock::doMenu() {
	uint32 startTime = getMsTime();
	updateCursor();
	_inMenu = true;
	moveMouse();
	_videoDecoder->pauseAudio(true);
	_screen->copyRectToSurface(_background->getBasePtr(_videoPosX, _videoPosY), _background->pitch, _videoPosX, _videoPosY, _videoDecoder->getWidth(), _videoDecoder->getHeight());
	showDifficulty(_difficulty, false);
	while (_inMenu && !_vm->shouldQuit()) {
		Common::Point firedCoords;
		if (fired(&firedCoords)) {
			Rect *hitMenuRect = checkZone(_submenzone, &firedCoords);
			if (hitMenuRect != nullptr) {
				callScriptFunctionRectHit(hitMenuRect->_rectHit, hitMenuRect);
			}
		}
		_leftDown = false;
		if (_difficulty != _oldDifficulty) {
			changeDifficulty(_difficulty);
		}
		updateScreen();
		g_system->delayMillis(15);
	}
	updateCursor();
	_videoDecoder->pauseAudio(false);
	if (_hadPause) {
		uint32 endTime = getMsTime();
		uint32 timeDiff = endTime - startTime;
		_pauseTime += timeDiff;
		_nextFrameTime += timeDiff;
	}
}

void GameJohnnyRock::updateStat() {
	if (_score != _oldScore) {
		_oldScore = _score;
		Common::String buffer = Common::String::format("%05d", _score);
		for (int i = 0; i < 5; i++) {
			uint8 digit = buffer[i] - '0';
			AlgGraphics::drawImage(_screen, (*_numbers)[digit], (i * 7) + 0x7D, 0xBE);
		}
	}
	if (_gameMoney != _oldGameMoney) {
		_oldGameMoney = _gameMoney;
		Common::String buffer = Common::String::format("%04d", _gameMoney < 0 ? 0 : _gameMoney);
		for (int i = 0; i < 4; i++) {
			uint8 digit = buffer[i] - '0';
			AlgGraphics::drawImage(_screen, (*_numbers)[digit], (i * 7) + 0x43, 0xBE);
		}
	}
	if (_shots != _oldShots) {
		_oldShots = _shots;
		Common::String buffer = Common::String::format("%04d", _shots);
		for (int i = 0; i < 4; i++) {
			uint8 digit = buffer[i] - '0';
			AlgGraphics::drawImage(_screen, (*_numbers)[digit], (i * 7) + 0x10A, 0xBE);
		}
	}
	AlgGraphics::drawImage(_screen, (*_difficultyIcon)[_difficulty - 1], 0xBA, 0xBE);
}

void GameJohnnyRock::displayScore() {
	updateStat();
}

void GameJohnnyRock::showDifficulty(uint8 newDifficulty, bool cursor) {
	// reset menu screen
	_screen->copyRectToSurface(_background->getBasePtr(_videoPosX, _videoPosY), _background->pitch, _videoPosX, _videoPosY, _videoDecoder->getWidth(), _videoDecoder->getHeight());
	AlgGraphics::drawImageCentered(_screen, _levelIcon, _diffPos[newDifficulty][0], _diffPos[newDifficulty][1]);
	if (cursor) {
		updateCursor();
	}
}

void GameJohnnyRock::changeDifficulty(uint8 newDifficulty) {
	if (newDifficulty == _oldDifficulty) {
		return;
	}
	showDifficulty(newDifficulty, true);
	Game::adjustDifficulty(newDifficulty, _oldDifficulty);
	_oldDifficulty = newDifficulty;
	_difficulty = newDifficulty;
}

void GameJohnnyRock::updateCursor() {
	_oldWhichGun = _whichGun;
}

void GameJohnnyRock::updateMouse() {
	if (_oldWhichGun != _whichGun) {
		Graphics::Surface *cursor = (*_gun)[_whichGun];
		uint16 hotspotX = (cursor->w / 2);
		uint16 hotspotY = (cursor->h / 2);
		if (debugChannelSet(1, Alg::kAlgDebugGraphics)) {
			cursor->drawLine(0, hotspotY, cursor->w, hotspotY, 1);
			cursor->drawLine(hotspotX, 0, hotspotX, cursor->h, 1);
		}
		CursorMan.replaceCursor(cursor->getPixels(), cursor->w, cursor->h, hotspotX, hotspotY, 0);
		CursorMan.showMouse(true);
		_oldWhichGun = _whichGun;
	}
}

void GameJohnnyRock::moveMouse() {
	if (_inMenu) {
		if (_mousePos.y > 0xB7) {
			_mousePos.y = 0xB7;
		}
		_whichGun = 8;
	} else {
		if (_mousePos.y > 0xBC) {
			_whichGun = 6;
		} else if (_whichGun > 5) {
			_whichGun = 0;
		}
	}
	updateMouse();
}

bool GameJohnnyRock::weaponDown() {
	if (_rightDown && _mousePos.y >= 0xBC && _mousePos.x >= 0x37) {
		return true;
	}
	return false;
}

bool GameJohnnyRock::saveState() {
	Common::OutSaveFile *outSaveFile;
	Common::String saveFileName = _vm->getSaveStateName(0);
	if (!(outSaveFile = g_system->getSavefileManager()->openForSaving(saveFileName))) {
		warning("GameJohnnyRock::saveState(): Can't create file '%s', game not saved", saveFileName.c_str());
		return false;
	}
	outSaveFile->writeUint32BE(MKTAG('A', 'L', 'G', 'S')); // header
	outSaveFile->writeByte(0);                             // version, unused for now
	outSaveFile->writeUint16LE(_totalDies);
	outSaveFile->writeSint16LE(_gameMoney);
	outSaveFile->writeUint16LE(_ammoAgain);
	outSaveFile->writeUint16LE(_mapTimeout);
	outSaveFile->writeByte(_luckyNumber);
	outSaveFile->writeByte(_thisMap);
	outSaveFile->writeUint16LE(_clues);
	outSaveFile->writeUint16LE(_placeBits);
	outSaveFile->writeByte(_randomCount);
	outSaveFile->writeUint16LE(_doctorBits);
	outSaveFile->writeUint16LE(_undertakerBits);
	for (int i = 0; i < 4; i++) {
		outSaveFile->writeByte(_clueTable[i]);
	}
	outSaveFile->writeUint16LE(_thisClue);
	outSaveFile->writeByte(_gotThisNumber);
	outSaveFile->writeByte(_casino);
	outSaveFile->writeByte(_poolHall);
	outSaveFile->writeByte(_warehouse);
	outSaveFile->writeByte(_garage);
	outSaveFile->writeByte(_office);
	outSaveFile->writeByte(_casinoType);
	outSaveFile->writeByte(_poolHallType);
	outSaveFile->writeByte(_warehouseType);
	outSaveFile->writeByte(_garageType);
	outSaveFile->writeByte(_mansion);
	outSaveFile->writeByte(_inWarehouse);
	outSaveFile->writeByte(_inOffice);
	outSaveFile->writeUint16LE(_gotTo);
	for (int i = 0; i < 20; i++) {
		outSaveFile->writeUint16LE(_entranceIndex[i]);
	}
	for (int i = 0; i < 10; i++) {
		outSaveFile->writeUint16LE(_randomScenesIndex[i]);
	}
	for (int i = 0; i < 4; i++) {
		outSaveFile->writeByte(_combinations[i]);
	}
	outSaveFile->writeByte(_whoDidIt);
	outSaveFile->writeByte(_hadGoToMansion);
	outSaveFile->writeUint16LE(_officeCount);
	outSaveFile->writeUint16LE(_randomPlaceBits);
	outSaveFile->writeByte(_maxRandomCount);
	outSaveFile->writeUint16LE(_gotoAfterRandom);
	outSaveFile->writeUint16LE(_repeatRandomPlace);
	outSaveFile->writeUint16LE(_maxRepeat);
	outSaveFile->writeUint16LE(_gotThisClue);
	outSaveFile->writeUint16LE(_didContinue);
	outSaveFile->writeUint16LE(_thisGameTime);
	outSaveFile->writeUint16LE(_shots);
	outSaveFile->writeSint32LE(_score);
	outSaveFile->writeByte(_holster);
	outSaveFile->writeByte(_difficulty);
	outSaveFile->writeByte(_thisDifficulty);
	outSaveFile->writeString(_moneyScene);
	outSaveFile->writeByte(0);
	outSaveFile->writeString(_curScene);
	outSaveFile->writeByte(0);
	outSaveFile->writeString(_subScene);
	outSaveFile->writeByte(0);
	outSaveFile->writeString(_retScene);
	outSaveFile->writeByte(0);
	outSaveFile->writeByte(_randomScenesSavestateIndex);
	outSaveFile->finalize();
	delete outSaveFile;
	return true;
}

bool GameJohnnyRock::loadState() {
	Common::InSaveFile *inSaveFile;
	Common::String saveFileName = _vm->getSaveStateName(0);
	if (!(inSaveFile = g_system->getSavefileManager()->openForLoading(saveFileName))) {
		debug("GameJohnnyRock::loadState(): Can't load file '%s', game not loaded", saveFileName.c_str());
		return false;
	}
	uint32 header = inSaveFile->readUint32BE();
	if (header != MKTAG('A', 'L', 'G', 'S')) {
		warning("GameJohnnyRock::loadState(): Unkown save file, header: %s", tag2str(header));
		return false;
	}
	inSaveFile->skip(1); // version, unused for now
	_totalDies = inSaveFile->readUint16LE();
	_gameMoney = inSaveFile->readSint16LE();
	_ammoAgain = inSaveFile->readUint16LE();
	_mapTimeout = inSaveFile->readUint16LE();
	_luckyNumber = inSaveFile->readByte();
	_thisMap = inSaveFile->readByte();
	_clues = inSaveFile->readUint16LE();
	_placeBits = inSaveFile->readUint16LE();
	_randomCount = inSaveFile->readByte();
	_doctorBits = inSaveFile->readUint16LE();
	_undertakerBits = inSaveFile->readUint16LE();
	for (int i = 0; i < 4; i++) {
		_clueTable[i] = inSaveFile->readByte();
	}
	_thisClue = inSaveFile->readUint16LE();
	_gotThisNumber = inSaveFile->readByte();
	_casino = inSaveFile->readByte();
	_poolHall = inSaveFile->readByte();
	_warehouse = inSaveFile->readByte();
	_garage = inSaveFile->readByte();
	_office = inSaveFile->readByte();
	_casinoType = inSaveFile->readByte();
	_poolHallType = inSaveFile->readByte();
	_warehouseType = inSaveFile->readByte();
	_garageType = inSaveFile->readByte();
	_mansion = inSaveFile->readByte();
	_inWarehouse = inSaveFile->readByte();
	_inOffice = inSaveFile->readByte();
	_gotTo = inSaveFile->readUint16LE();
	for (int i = 0; i < 20; i++) {
		_entranceIndex[i] = inSaveFile->readUint16LE();
	}
	for (int i = 0; i < 10; i++) {
		_randomScenesIndex[i] = inSaveFile->readUint16LE();
	}
	for (int i = 0; i < 4; i++) {
		_combinations[i] = inSaveFile->readByte();
	}
	_whoDidIt = inSaveFile->readByte();
	_hadGoToMansion = inSaveFile->readByte();
	_officeCount = inSaveFile->readUint16LE();
	_randomPlaceBits = inSaveFile->readUint16LE();
	_maxRandomCount = inSaveFile->readByte();
	_gotoAfterRandom = inSaveFile->readUint16LE();
	_repeatRandomPlace = inSaveFile->readUint16LE();
	_maxRepeat = inSaveFile->readUint16LE();
	_gotThisClue = inSaveFile->readUint16LE();
	_didContinue = inSaveFile->readUint16LE();
	_thisGameTime = inSaveFile->readUint16LE();
	_shots = inSaveFile->readUint16LE();
	_score = inSaveFile->readSint32LE();
	_holster = inSaveFile->readByte();
	_difficulty = inSaveFile->readByte();
	_thisDifficulty = inSaveFile->readByte();
	_moneyScene = inSaveFile->readString();
	_curScene = inSaveFile->readString();
	_subScene = inSaveFile->readString();
	_retScene = inSaveFile->readString();
	_randomScenesSavestateIndex = inSaveFile->readByte();
	delete inSaveFile;
	// find out where _random_scenes should point
	uint16 placeIndex = _entranceIndex[_randomScenesSavestateIndex];
	if (_randomScenesSavestateIndex < 5) {
		_randomScenes = _randomPlaces[placeIndex];
	} else {
		_randomScenes = _randomPlacesMR[placeIndex];
	}
	changeDifficulty(_difficulty);
	debug("lucky number: %d", (_luckyNumber + 1));
	return true;
}

void GameJohnnyRock::doMoneySound() {
	playSound(_skullSound);
}

// Misc game functions
Common::String GameJohnnyRock::numToScene(int n) {
	switch (n) {
	case 1:
		return "scene1a";
	case 10:
		return "scene10a";
	case 12:
		return "scene12a";
	case 14:
		return "scene14a";
	case 18:
		return "scene18a";
	case 63:
		return "scene63a";
	case 71:
		return "scene71a";
	case 120:
		return "scn120a";
	case 137:
		return "scn137a";
	default:
		return Common::String::format("scene%d", n);
	}
}

uint16 GameJohnnyRock::sceneToNum(Common::String sceneName) {
	Common::String temp;
	uint16 index = 4;
	if (sceneName[index] == 'e') {
		index++;
	}
	while (sceneName[index] != 'a' && sceneName[index] != 'b' && sceneName[index] != 'c' && sceneName[index] != '\0') {
		temp += sceneName[index];
		index++;
		if (index >= sceneName.size()) {
			break;
		}
	}
	return atoi(temp.c_str());
}

void GameJohnnyRock::defaultBullethole(Common::Point *point) {
	if (point->x >= 14 && point->x <= 306 && point->y >= 5 && point->y <= 169) {
		uint16 targetX = point->x - _videoPosX;
		uint16 targetY = point->y - _videoPosY;
		AlgGraphics::drawImageCentered(_videoDecoder->getVideoFrame(), _bulletholeIcon, targetX, targetY);
		updateCursor();
		_shotFired = true;
		doShot();
	}
}

uint16 GameJohnnyRock::pickBits(uint16 *bits, uint8 max) {
	// reset bits if full
	if (*bits == (0xFFFF >> (16 - max))) {
		*bits = 0;
	}
	uint8 randomNum = _rnd->getRandomNumber(max - 1);
	// find first unused bit position
	while (*bits & (1 << randomNum)) {
		randomNum++;
		if (randomNum >= max) {
			randomNum = 0;
		}
	}
	*bits |= (1 << randomNum);
	return randomNum;
}

uint16 GameJohnnyRock::pickRandomPlace(uint8 place) {
	_randomScenesSavestateIndex = place;
	uint16 placeIndex = _entranceIndex[place];
	if (place < 5) {
		_randomScenes = _randomPlaces[placeIndex];
	} else {
		_randomScenes = _randomPlacesMR[placeIndex];
	}
	_placeBits = 0;
	_randomCount = 0;
	memset(&_randomScenesIndex, 0, (10 * sizeof(uint16)));
	for (int i = 0; i < (_randomScenes[0] + 4); i++) {
		_randomScenesIndex[i] = _randomScenes[i];
	}
	if (_randomScenes[1] < 0) {
		_maxRandomCount = (_thisDifficulty * 2) - _randomScenes[1];
	} else {
		_maxRandomCount = _randomScenes[1];
	}
	uint16 index = pickBits(&_placeBits, _randomScenes[0]);
	return _randomScenes[index + 4];
}

void GameJohnnyRock::showCombination() {
	uint16 offset = (_gotThisClue * 6) + _combinations[_gotThisClue];
	_gotThisClue++;
	if (_gotThisClue == 4) {
		_mansion = 3;
	}
	_curScene = numToScene(offset + 0xDB);
}

// Script functions: Zone
void GameJohnnyRock::zoneBullethole(Common::Point *point) {
	defaultBullethole(point);
}

void GameJohnnyRock::rectShotMenu(Rect *rect) {
	doMenu();
}

void GameJohnnyRock::rectSave(Rect *rect) {
	if (saveState()) {
		doSaveSound();
	}
}

void GameJohnnyRock::rectLoad(Rect *rect) {
	if (loadState()) {
		doLoadSound();
	}
}

void GameJohnnyRock::rectContinue(Rect *rect) {
	_inMenu = false;
	_fired = false;
	if (_gameMoney < 0) {
		newGame();
		_retScene = "";
		_subScene = "";
		if (_inOffice) {
			_curScene = numToScene(_inOffice);
		} else {
			_curScene = numToScene(_thisMap + 174);
		}
		_didContinue++;
	}
	if (_shots <= 0) {
		outShots();
		_didContinue++;
	}
}

void GameJohnnyRock::rectStart(Rect *rect) {
	_inMenu = false;
	_fired = false;
	_thisDifficulty = 0;
	Scene *scene = _sceneInfo->findScene(_startScene);
	if (scene->_nxtscn == "DRAWGUN") {
		callScriptFunctionScene(NXTSCN, "DRAWGUN", scene);
	}
	_curScene = _startScene;
	resetParams();
	newGame();
	updateStat();
}

void GameJohnnyRock::rectKillInnocent(Rect *rect) {
	_inOffice = sceneToNum(_curScene);
	if (_inOffice >= 0x13) {
		_inOffice = 0;
	}
	if (!_debug_godMode) {
		_gameMoney -= 400;
	}
	if (_gameMoney < 0) {
		_subScene = "scene358";
		_retScene = "";
		_curScene = "scene151";
	} else {
		switch (_rnd->getRandomNumber(2)) {
		case 0:
			_curScene = "scene151";
			break;
		case 1:
			_curScene = "scene152";
			break;
		case 2:
			_curScene = "scene153";
			break;
		}
	}
}

void GameJohnnyRock::rectSelectCasino(Rect *rect) {
	_retScene = "";
	if (_mansion == 5 && _whoDidIt == 1) {
		_repeatRandomPlace = _maxRepeat;
		_gotoAfterRandom = 0xF5;
		_curScene = numToScene(pickRandomPlace(_maxRepeat + 5));
	} else if (_casino == 0) {
		if (_gotTo & 1) {
			_curScene = "scene19";
		} else {
			_gotoAfterRandom = 0x13;
			_curScene = numToScene(pickRandomPlace(0));
		}
	} else if (_casino == 1 || _casino == 3) {
		_curScene = "scene44";
	} else {
		_casino = 3;
		if (_rnd->getRandomBit()) { // original: (_this_game_time & 1) == 0
			_curScene = "scene64";
		} else {
			_curScene = "scene65";
		}
	}
}

void GameJohnnyRock::rectSelectPoolhall(Rect *rect) {
	_retScene = "";
	if (_mansion == 5 && _whoDidIt == 0) {
		_repeatRandomPlace = _maxRepeat;
		_gotoAfterRandom = 0xF7;
		_curScene = numToScene(pickRandomPlace(_maxRepeat + 5));
	} else if (_poolHall == 0) {
		if (_gotTo & 2) {
			_curScene = "scene66";
		} else {
			_gotoAfterRandom = 0x42;
			_curScene = numToScene(pickRandomPlace(1));
		}
	} else if (_poolHall == 1 || _poolHall == 3) {
		_curScene = "scene82";
	} else {
		_poolHall = 3;
		_curScene = "scene89";
	}
}

void GameJohnnyRock::rectSelectWarehouse(Rect *rect) {
	_retScene = "";
	if (_mansion == 5 && _whoDidIt == 2) {
		_repeatRandomPlace = _maxRepeat;
		_gotoAfterRandom = 0xF9;
		_curScene = numToScene(pickRandomPlace(_maxRepeat + 5));
	} else if (_warehouse == 0) {
		if (_inWarehouse < 2) {
			if (_gotTo & 4) {
				_curScene = "scene90";
			} else {
				_inWarehouse = 1;
				_gotoAfterRandom = 0x5A;
				_curScene = numToScene(pickRandomPlace(2));
			}
		} else if (_inWarehouse == 2) {
			_curScene = "scene93";
		} else if (_inWarehouse == 3) {
			_curScene = "scene119";
		}
	} else if (_warehouse == 1 || _warehouse == 3) {
		_curScene = "scene122";
	} else {
		_warehouse = 3;
		_curScene = "scene121";
	}
}

void GameJohnnyRock::rectSelectGarage(Rect *rect) {
	_retScene = "";
	if (_mansion == 5 && _whoDidIt == 3) {
		_repeatRandomPlace = _maxRepeat;
		_gotoAfterRandom = 0xFB;
		_curScene = numToScene(pickRandomPlace(_maxRepeat + 5));
	} else if (_garage == 0) {
		if (_gotTo & 8) {
			_curScene = "scene123";
		} else {
			_gotoAfterRandom = 0x7B;
			_curScene = numToScene(pickRandomPlace(3));
		}
	} else if (_garage == 1 || _garage == 3) {
		_curScene = "scene138";
	} else {
		_garage = 3;
		_curScene = "scene139";
	}
}

void GameJohnnyRock::rectSelectMansion(Rect *rect) {
	_placeBits = 0;
	_randomCount = 1;
	_retScene = "";
	if (_mansion == 1) {
		uint16 picked = pickBits(&_placeBits, 5);
		_curScene = numToScene(0xB8 + (picked * 2));
	} else if (_mansion == 2) {
		_curScene = "scene194";
	} else if (_mansion == 3) {
		_curScene = "scene207";
	} else if (_mansion == 4) {
		_gotThisNumber = 0;
		_curScene = "scene212";
	} else if (_mansion == 5) {
		_curScene = "scene243";
	} else {
		if (_garage == 0 || _casino == 0 || _poolHall == 0 || _warehouse == 0) {
			_curScene = "scene243";
		} else if (_gotTo & 0x10) {
			_curScene = "scene180";
		} else {
			_gotoAfterRandom = 0xB4;
			_curScene = numToScene(pickRandomPlace(4));
		}
	}
}

void GameJohnnyRock::rectSelectAmmo(Rect *rect) {
	_retScene = "";
	if (_gameMoney >= 100) {
		if (!_debug_godMode) {
			_gameMoney -= 100;
		}
		_shots += 200;
		_ammoAgain = 0;
		doMoneySound();
		_curScene = "scene178";
	} else {
		_ammoAgain++;
		if (_ammoAgain >= 2) {
			_curScene = "scene243";
		} else {
			_curScene = "scene179";
		}
	}
}

void GameJohnnyRock::rectSelectOffice(Rect *rect) {
	_retScene = "";
	if (!_office) {
		_office = 1;
		_curScene = "scene168";
	} else {
		if (_rnd->getRandomBit()) { // original: _this_game_time & 1
			_curScene = "scene243";
		} else {
			_curScene = "scene262";
		}
	}
}

void GameJohnnyRock::shotClue(uint8 clue) {
	if (_clueTable[_gotThisClue] == clue) {
		showCombination();
	} else {
		_gotThisClue = 0;
		_curScene = "scene374";
	}
}

void GameJohnnyRock::rectShotManBust(Rect *rect) {
	shotClue(0);
}

void GameJohnnyRock::rectShotWomanBust(Rect *rect) {
	shotClue(1);
}

void GameJohnnyRock::rectShotBlueVase(Rect *rect) {
	shotClue(2);
}

void GameJohnnyRock::rectShotCat(Rect *rect) {
	shotClue(3);
}

void GameJohnnyRock::rectShotIndian(Rect *rect) {
	shotClue(4);
}

void GameJohnnyRock::rectShotPlate(Rect *rect) {
	shotClue(5);
}

void GameJohnnyRock::rectShotBlueDressPic(Rect *rect) {
	shotClue(6);
}

void GameJohnnyRock::rectShotModernPic(Rect *rect) {
	shotClue(7);
}

void GameJohnnyRock::rectShotMonaLisa(Rect *rect) {
	shotClue(8);
}

void GameJohnnyRock::rectShotGWashington(Rect *rect) {
	shotClue(9);
}

void GameJohnnyRock::rectShotBoyInRedPic(Rect *rect) {
	shotClue(10);
}

void GameJohnnyRock::rectShotCoatOfArms(Rect *rect) {
	shotClue(11);
}

void GameJohnnyRock::shotCombination(uint8 combination, bool combinationB) {
	if (_combinations[_gotThisNumber] == combination) {
		_gotThisNumber++;
		if (_gotThisNumber >= 4) {
			_mansion = 5;
			_curScene = numToScene(_whoDidIt + 0xD7);
		} else {
			if (combinationB) {
				_curScene = "scene213";
			} else {
				_curScene = "scene214";
			}
		}
	} else {
		_gotThisNumber = 0;
		_curScene = "scene376";
	}
}

void GameJohnnyRock::rectShotCombinationA0(Rect *rect) {
	shotCombination(0, false);
}

void GameJohnnyRock::rectShotCombinationA1(Rect *rect) {
	shotCombination(1, false);
}

void GameJohnnyRock::rectShotCombinationA2(Rect *rect) {
	shotCombination(2, false);
}

void GameJohnnyRock::rectShotCombinationA3(Rect *rect) {
	shotCombination(3, false);
}

void GameJohnnyRock::rectShotCombinationA4(Rect *rect) {
	shotCombination(4, false);
}

void GameJohnnyRock::rectShotCombinationA5(Rect *rect) {
	shotCombination(5, false);
}

void GameJohnnyRock::rectShotCombinationB0(Rect *rect) {
	shotCombination(0, true);
}

void GameJohnnyRock::rectShotCombinationB1(Rect *rect) {
	shotCombination(1, true);
}

void GameJohnnyRock::rectShotCombinationB2(Rect *rect) {
	shotCombination(2, true);
}

void GameJohnnyRock::rectShotCombinationB3(Rect *rect) {
	shotCombination(3, true);
}

void GameJohnnyRock::rectShotCombinationB4(Rect *rect) {
	shotCombination(4, true);
}

void GameJohnnyRock::rectShotCombinationB5(Rect *rect) {
	shotCombination(5, true);
}

void GameJohnnyRock::shotLuckyNumber(uint8 number) {
	if (_luckyNumber != number || _curScene == _moneyScene) {
		return;
	}
	doMoneySound();
	_gameMoney += 100;
	_score += 500;
	_moneyScene = _curScene;
}

void GameJohnnyRock::rectShotLuckyNumber0(Rect *rect) {
	shotLuckyNumber(0);
}

void GameJohnnyRock::rectShotLuckyNumber1(Rect *rect) {
	shotLuckyNumber(1);
}

void GameJohnnyRock::rectShotLuckyNumber2(Rect *rect) {
	shotLuckyNumber(2);
}

void GameJohnnyRock::rectShotLuckyNumber3(Rect *rect) {
	shotLuckyNumber(3);
}

void GameJohnnyRock::rectShotLuckyNumber4(Rect *rect) {
	shotLuckyNumber(4);
}

void GameJohnnyRock::rectShotLuckyNumber5(Rect *rect) {
	shotLuckyNumber(5);
}

// Script functions: Scene PreOps

// Script functions: Scene Scene InsOps
void GameJohnnyRock::sceneIsoShootpast(Scene *scene) {
	if (_fired) {
		if (_retScene != "") {
			_curScene = _retScene;
			_retScene = "";
		} else if (_subScene != "") {
			_curScene = _subScene;
			_subScene = "";
		} else {
			callScriptFunctionScene(NXTSCN, scene->_nxtscn, scene);
		}
	}
}

void GameJohnnyRock::sceneIsoShootpastPause(Scene *scene) {
	sceneIsoShootpast(scene);
	sceneIsoPause(scene);
}

void GameJohnnyRock::sceneIsoGotoCasino(Scene *scene) {
	_gotTo |= 1;
	sceneIsoShootpast(scene);
}

void GameJohnnyRock::sceneIsoGotoPoolhall(Scene *scene) {
	_gotTo |= 2;
	sceneIsoShootpast(scene);
}

void GameJohnnyRock::sceneIsoGotoWarehouse(Scene *scene) {
	_gotTo |= 4;
}

void GameJohnnyRock::sceneIsoInWarehouse2(Scene *scene) {
	_inWarehouse = 2;
	sceneIsoShootpast(scene);
}

void GameJohnnyRock::sceneIsoInwarehouse3(Scene *scene) {
	_inWarehouse = 3;
	sceneIsoShootpast(scene);
}

void GameJohnnyRock::sceneIsoGotoGarage(Scene *scene) {
	_gotTo |= 8;
	sceneIsoShootpast(scene);
}

void GameJohnnyRock::sceneIsoGotoMansion(Scene *scene) {
	_gotTo |= 0x10;
}

void GameJohnnyRock::sceneIsoInMansion1(Scene *scene) {
	_mansion = 1;
}

// Script functions: Scene NxtScn
void GameJohnnyRock::sceneNxtscnDied(Scene *scene) {
	uint16 sceneNum = sceneToNum(_curScene);
	_inOffice = sceneNum;
	if (sceneNum >= 0x13) {
		_inOffice = 0;
	}
	_totalDies++;
	if (!_debug_godMode) {
		_gameMoney -= 400;
	}
	if (_gameMoney < 0) {
		switch (_rnd->getRandomNumber(2)) {
		case 0:
			_curScene = "scene148";
			break;
		case 1:
			_curScene = "scene149";
			break;
		case 2:
			_curScene = "scene150";
			break;
		}
	} else {
		switch (pickBits(&_doctorBits, 8)) {
		case 0:
			_curScene = "scene140";
			break;
		case 1:
			_curScene = "scene141";
			break;
		case 2:
			_curScene = "scene142";
			break;
		case 3:
			_curScene = "scene143";
			break;
		case 4:
			_curScene = "scene144";
			break;
		case 5:
			_curScene = "scene145";
			break;
		case 6:
			_curScene = "scene146";
			break;
		case 7:
			_curScene = "scene147";
			break;
		}
	}
}

void GameJohnnyRock::sceneNxtscnBombDead(Scene *scene) {
	uint16 sceneNum = sceneToNum(_curScene);
	_inOffice = sceneNum;
	if (sceneNum >= 0x13) {
		_inOffice = 0;
	}
	_totalDies++;
	if (!_debug_godMode) {
		_gameMoney -= 400;
	}
	if (_gameMoney < 0) {
		switch (_rnd->getRandomNumber(2)) {
		case 0:
			_curScene = "scene148";
			break;
		case 1:
			_curScene = "scene149";
			break;
		case 2:
			_curScene = "scene150";
			break;
		}
	} else {
		_curScene = "scene142";
	}
}

void GameJohnnyRock::sceneNxtscnPickUndertaker(Scene *scene) {
	switch (pickBits(&_undertakerBits, 3)) {
	case 0:
		_curScene = "scene154";
		break;
	case 1:
		_curScene = "scene155";
		break;
	case 2:
		_curScene = "scene156";
		break;
	}
}

void GameJohnnyRock::sceneNxtscnCallAttract(Scene *scene) {
	resetParams();
	newGame();
	_curScene = "scn354aa";
}

void GameJohnnyRock::sceneNxtscnPickLuckyNumber(Scene *scene) {
	_luckyNumber = _rnd->getRandomNumber(5);
	debug("lucky number: %d", (_luckyNumber + 1));
	_curScene = Common::String::format("scene%d", _luckyNumber + 3);
}

void GameJohnnyRock::sceneNxtscnPickMap(Scene *scene) {
	Common::String nextScene;
	if (_gameMoney < 0) {
		switch (_rnd->getRandomNumber(2)) {
		case 0:
			_curScene = "scene148";
			break;
		case 1:
			_curScene = "scene149";
			break;
		case 2:
			_curScene = "scene150";
			break;
		}
		return;
	}
	uint16 sceneNum = sceneToNum(_curScene);
	if (sceneNum == 18) {
		_inOffice = 0;
	}
	if (_inOffice) {
		nextScene = numToScene(_inOffice);
	} else if (_officeCount < 10) {
		if ((_officeCount % 3) == 0) {
			uint16 sceneNume = _officeTable[_officeCount / 3];
			nextScene = numToScene(sceneNume);
		}
		_officeCount++;
	} else if (_thisClue == 2 && !_hadGoToMansion) {
		if (!_didContinue) {
			_thisDifficulty++;
			if (_thisDifficulty > 7)
				_thisDifficulty = 7;
		}
		_hadGoToMansion = 1;
		if (_poolHall) {
			_poolHall = 2;
			nextScene = "scene162";
		} else if (_casino) {
			_casino = 2;
			nextScene = "scene163";
		} else if (_warehouse) {
			_warehouse = 2;
			nextScene = "scene165";
		}
	} else if (_thisClue == 3 && _hadGoToMansion == 1) {
		if (_totalDies < 4) {
			_thisDifficulty++;
			if (_thisDifficulty > 7)
				_thisDifficulty = 7;
		}
		_hadGoToMansion = 2;
		if (_poolHall == 1) {
			_poolHall = 2;
			nextScene = "scene162";
		} else if (_casino == 1) {
			_casino = 2;
			nextScene = "scene163";
		} else if (_warehouse == 1) {
			_warehouse = 2;
			nextScene = "scene165";
		}
	} else if (_hadGoToMansion == 2 && _garage && _casino && _poolHall && _warehouse) {
		if (_totalDies < 5 || _didContinue <= 1) {
			_thisDifficulty++;
			if (_thisDifficulty > 7)
				_thisDifficulty = 7;
		}
		_hadGoToMansion = 3;
		nextScene = "scene166";
	}
	if (!nextScene.empty()) {
		_curScene = nextScene;
	} else {
		_curScene = numToScene(_thisMap + 174);
	}
}

void GameJohnnyRock::sceneNxtscnPickClue(Scene *scene) {
	uint16 picked = pickBits(&_clues, 12);
	_clueTable[_thisClue] = picked;
	_thisClue++;
	_curScene = numToScene(picked + 0xC3);
}

void GameJohnnyRock::sceneNxtscnMapTimeout(Scene *scene) {
	_mapTimeout++;
	if (_mapTimeout < 3) {
		_curScene = "scene360";
	} else {
		_curScene = "scene262";
	}
}

void GameJohnnyRock::sceneNxtscnEnterCasino(Scene *scene) {
	_placeBits = 0;
	_randomCount = 0;
	uint16 sceneNum;
	if (_casinoType != 0) {
		sceneNum = (pickBits(&_placeBits, 12) * 2) + 0x14;
	} else {
		sceneNum = (pickBits(&_placeBits, 8) * 2) + 0x2D;
	}
	_curScene = numToScene(sceneNum);
}

void GameJohnnyRock::sceneNxtscnCasinoWhat(Scene *scene) {
	_randomCount++;
	uint16 maxRandom = ((_thisDifficulty * 3) + 6);
	if (_randomCount > maxRandom) {
		_casino = 1;
		_curScene = "scene63a";
	} else {
		uint16 sceneNum;
		if (_casinoType != 0) {
			sceneNum = (pickBits(&_placeBits, 12) * 2) + 0x14;
		} else {
			sceneNum = (pickBits(&_placeBits, 8) * 2) + 0x2D;
		}
		_curScene = numToScene(sceneNum);
	}
}

void GameJohnnyRock::sceneNxtscnEnterPoolhall(Scene *scene) {
	if (_poolHallType == 0) {
		_curScene = "scene67";
	} else if (_poolHallType == 1) {
		_curScene = "scene73";
	} else {
		_curScene = "scene78";
	}
}

void GameJohnnyRock::sceneNxtscnPoolhallClue(Scene *scene) {
	_poolHall = 1;
	uint16 clue = pickBits(&_clues, 12);
	_clueTable[_thisClue] = clue;
	_thisClue++;
	_curScene = numToScene(clue + 0xC3);
}

void GameJohnnyRock::sceneNxtscnEnterWarehouse(Scene *scene) {
	if (_warehouseType == 0) {
		_curScene = "scene94";
	} else if (_warehouseType == 1) {
		_curScene = "scene102";
	} else {
		_curScene = "scene110";
	}
}

void GameJohnnyRock::sceneNxtscnWarehouseClue(Scene *scene) {
	_warehouse = 1;
	uint16 clue = pickBits(&_clues, 12);
	_clueTable[_thisClue] = clue;
	_thisClue++;
	_curScene = numToScene(clue + 0xC3);
}

void GameJohnnyRock::sceneNxtscnEnterGarage(Scene *scene) {
	if (_garageType != 0) {
		_curScene = "scene124";
	} else {
		_curScene = "scene131";
	}
}

void GameJohnnyRock::sceneNxtscnGarageClue(Scene *scene) {
	_garage = 1;
	uint16 clue = pickBits(&_clues, 12);
	_clueTable[_thisClue] = clue;
	_thisClue++;
	_curScene = numToScene(clue + 0xC3);
}

void GameJohnnyRock::sceneNxtscnEnterMansion(Scene *scene) {
	_mansion = 1;
	_randomCount++;
	uint16 maxRandom = ((_thisDifficulty * 2) + 7);
	if (_randomCount <= maxRandom) {
		uint16 picked = pickBits(&_placeBits, 5);
		_curScene = numToScene((picked * 2) + 0xB8);
	} else {
		_mansion = 2;
		_curScene = "scene194";
	}
}

void GameJohnnyRock::sceneNxtscnGiveClue(Scene *scene) {
	_score += 1000;
	_gameMoney += 50;
	sceneNxtscnPickMap(scene);
}

void GameJohnnyRock::sceneNxtscnPickFlowerMan(Scene *scene) {
	if (_rnd->getRandomBit()) {
		_curScene = "scene10a";
	} else {
		_curScene = "scene12a";
	}
}

void GameJohnnyRock::sceneNxtscnRandomScene(Scene *scene) {
	_randomCount++;
	if (_randomCount <= _maxRandomCount) {
		_placeBits = pickBits(&_placeBits, _randomScenes[0]);
		_curScene = numToScene(_randomScenes[_placeBits + 4]);
	} else {
		if (_randomScenes[2] != 0) {
			_curScene = numToScene(_randomScenes[2]);
		} else if (_repeatRandomPlace > 0) {
			_repeatRandomPlace--;
			_maxRepeat--;
			uint16 picked = pickRandomPlace(_repeatRandomPlace + 5);
			_curScene = numToScene(picked);
		} else {
			_curScene = numToScene(_gotoAfterRandom);
			_gotoAfterRandom = 0;
			_placeBits = 0;
			_randomCount = 1;
		}
	}
}

void GameJohnnyRock::sceneNxtscnEndRandScene(Scene *scene) {
	if (_repeatRandomPlace > 0) {
		_repeatRandomPlace--;
		_maxRepeat--;
		uint16 picked = pickRandomPlace(_repeatRandomPlace + 5);
		_curScene = numToScene(picked);
	} else {
		_curScene = numToScene(_gotoAfterRandom);
		_gotoAfterRandom = 0;
		_placeBits = 0;
		_randomCount = 1;
	}
}

void GameJohnnyRock::sceneNxtscnKillInnocent(Scene *scene) {
	if (!_debug_godMode) {
		_gameMoney -= 400;
	}
	if (_gameMoney < 0) {
		_retScene = "scene358";
		_curScene = "scene151";
	} else {
		switch (_rnd->getRandomNumber(2)) {
		case 0:
			_curScene = "scene151";
			break;
		case 1:
			_curScene = "scene152";
			break;
		case 2:
			_curScene = "scene153";
			break;
		}
	}
}

// Script functions: WepDwn
void GameJohnnyRock::sceneDefaultWepdwn(Scene *scene) {
	_inHolster = 9;
	_whichGun = 7;
	updateMouse();
}

// Debug methods
void GameJohnnyRock::debugWarpTo(int val) {
	// TODO implement
}

// Debugger methods
DebuggerJohnnyRock::DebuggerJohnnyRock(GameJohnnyRock *game) {
	_game = game;
	registerVar("drawRects", &game->_debug_drawRects);
	registerVar("godMode", &game->_debug_godMode);
	registerVar("unlimitedAmmo", &game->_debug_unlimitedAmmo);
	registerCmd("warpTo", WRAP_METHOD(DebuggerJohnnyRock, cmdWarpTo));
	registerCmd("dumpLib", WRAP_METHOD(DebuggerJohnnyRock, cmdDumpLib));
}

bool DebuggerJohnnyRock::cmdWarpTo(int argc, const char **argv) {
	if (argc != 2) {
		debugPrintf("Usage: warp <int>");
		return true;
	} else {
		int val = atoi(argv[1]);
		_game->debugWarpTo(val);
		return false;
	}
}

bool DebuggerJohnnyRock::cmdDumpLib(int argc, const char **argv) {
	return _game->debug_dumpLibFile();
}

} // End of namespace Alg
