/* 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 "dragons/actorresource.h"
#include "dragons/background.h"
#include "dragons/cursor.h"
#include "dragons/cutscene.h"
#include "dragons/credits.h"
#include "dragons/dragons.h"
#include "dragons/dragonflg.h"
#include "dragons/dragonini.h"
#include "dragons/dragonobd.h"
#include "dragons/inventory.h"
#include "dragons/specialopcodes.h"
#include "dragons/scene.h"
#include "dragons/sound.h"
#include "dragons/actor.h"
#include "dragons/minigame1.h"
#include "dragons/talk.h"
#include "dragons/screen.h"
#include "dragons/minigame2.h"
#include "dragons/minigame3.h"
#include "dragons/minigame4.h"
#include "dragons/minigame5.h"


namespace Dragons {

const int16 shakeTbl[16] = {
	5,      2,     -2,     -5,
	5,      2,     -2,     -5,
	-1,      2,     -1,     -1,
	2,     -1,      1,      0
};

// SpecialOpcodes

SpecialOpcodes::SpecialOpcodes(DragonsEngine *vm)
	: _vm(vm), _specialOpCounter(0) {
	_dat_80083148 = 0;
	_uint16_t_80083154 = 0;
	initOpcodes();
}

SpecialOpcodes::~SpecialOpcodes() {
	freeOpcodes();
}

void SpecialOpcodes::run(int16 op) {
	if (!_opcodes[op])
		error("SpecialOpcodes::execOpcode() Unimplemented opcode %d (0x%X)", op, op);
	debug(3, "run(%d) %s", op, _opcodeNames[op].c_str());
	(*_opcodes[op])();
}

typedef Common::Functor0Mem<void, SpecialOpcodes> SpecialOpcodeI;
#define OPCODE(op, func) \
	_opcodes[op] = new SpecialOpcodeI(this, &SpecialOpcodes::func); \
	_opcodeNames[op] = #func;

void SpecialOpcodes::initOpcodes() {
	// First clear everything
	for (uint i = 0; i < DRAGONS_NUM_SPECIAL_OPCODES; ++i) {
		_opcodes[i] = nullptr;
	}
	// Register opcodes
	OPCODE(1, spcCatapultMiniGame);
	OPCODE(2, spcThumbWrestlingMiniGame);
	OPCODE(3, spcClearEngineFlag10);
	OPCODE(4, spcSetEngineFlag10);

	OPCODE(6, spcRabbitsMiniGame);
	OPCODE(7, spcDancingMiniGame);
	OPCODE(8, spcCastleGardenLogic);
	OPCODE(9, spcUnk9);
	OPCODE(0xa, spcUnkA);
	OPCODE(0xb, clearSceneUpdateFunction);
	OPCODE(0xc, spcUnkC);
	OPCODE(0xd, spcFadeScreen);
	OPCODE(0xe, spcLadyOfTheLakeCapturedSceneLogic);
	OPCODE(0xf, spcLadyOfTheLakeCapturedSceneLogic);
	OPCODE(0x10, spcStopLadyOfTheLakeCapturedSceneLogic);
	OPCODE(0x11, spc11ShakeScreen);
	OPCODE(0x12, spcHandleInventionBookTransition);
	OPCODE(0x13, spcUnk13InventionBookCloseRelated);
	OPCODE(0x14, spcClearEngineFlag8);
	OPCODE(0x15, spcSetEngineFlag8);
	OPCODE(0x16, spcNoop1);
	OPCODE(0x17, spcKnightPoolReflectionLogic);
	OPCODE(0x18, clearSceneUpdateFunction);
	OPCODE(0x19, spcWalkOnStilts);
	OPCODE(0x1a, spcActivatePizzaMakerActor);
	OPCODE(0x1b, spcDeactivatePizzaMakerActor);
	OPCODE(0x1c, spcPizzaMakerActorStopWorking);
	OPCODE(0x1d, spcDragonArrivesAtTournament);
	OPCODE(0x1e, spcDragonCatapultMiniGame);
	OPCODE(0x1f, spcStGeorgeDragonLanded);

	OPCODE(0x21, spcSetEngineFlag0x20000);
	OPCODE(0x22, spcClearEngineFlag0x20000);
	OPCODE(0x23, spcSetEngineFlag0x200000);
	OPCODE(0x24, spcClearEngineFlag0x200000);
	OPCODE(0x25, spcFlickerSetPriority2);
	OPCODE(0x26, spcMenInMinesSceneLogic);
	OPCODE(0x27, spcStopMenInMinesSceneLogic);
	OPCODE(0x28, spcMonksAtBarSceneLogic);
	OPCODE(0x29, spcStopMonksAtBarSceneLogic);

	OPCODE(0x2b, spcFlameBedroomEscapeSceneLogic);
	OPCODE(0x2c, spcStopFlameBedroomEscapeSceneLogic);

	OPCODE(0x2e, spcCastleMoatFull);
	OPCODE(0x2f, spcCastleRestoreScalePoints);
	OPCODE(0x30, spcCastleMoatUpdateActorSceneScalePoints);
	OPCODE(0x31, spcCastleGateMoatDrainedSceneLogic);

	OPCODE(0x34, spcUnk34);

	OPCODE(0x36, spcFlickerClearFlag0x80);

	OPCODE(0x38, spcNoop1);
	OPCODE(0x39, spcTownAngryVillagersSceneLogic);
	OPCODE(0x3a, spcBlackDragonCrashThroughGate);
	OPCODE(0x3b, spcSetEngineFlag0x2000000);
	OPCODE(0x3c, spcClearEngineFlag0x2000000);
	OPCODE(0x3d, clearSceneUpdateFunction);
	OPCODE(0x3e, spcZigmondFraudSceneLogic);
	OPCODE(0x3f, clearSceneUpdateFunction);
	OPCODE(0x40, spcZigmondFraudSceneLogic1);
	OPCODE(0x41, spcBrokenBlackDragonSceneLogic);
	OPCODE(0x42, spcDodoUnderAttackSceneLogic);
	OPCODE(0x43, spcForestWithoutDodoSceneLogic);

	OPCODE(0x46, spcBlackDragonOnHillSceneLogic);

	OPCODE(0x48, spcHedgehogTest);
	OPCODE(0x49, spcLoadScene1);

	OPCODE(0x4b, spcKnightsSavedCastleCutScene);
	OPCODE(0x4c, spcFlickerReturnsCutScene);
	OPCODE(0x4d, spcKnightsSavedAgainCutScene);
	OPCODE(0x4e, spcUnk4e);
	OPCODE(0x4f, spcUnk4f);
	OPCODE(0x50, spcCloseInventory);
	OPCODE(0x51, spcOpenInventionBook);
	OPCODE(0x52, spcCloseInventionBook);
	OPCODE(0x53, spcClearEngineFlag0x4000000);
	OPCODE(0x54, spcSetEngineFlag0x4000000);
	OPCODE(0x55, spcSetCursorSequenceIdToZero);

	OPCODE(0x5b, spcFlickerSetFlag0x80);

	OPCODE(0x5d, spcUnk5d); //knights in castle garden
	OPCODE(0x5e, spcUnk5e);
	OPCODE(0x5f, spcUnk5f);

	OPCODE(0x61, spcCastleBuildBlackDragonSceneLogic);
	OPCODE(0x62, spcStopSceneUpdateFunction)
	OPCODE(0x63, spcSetInventorySequenceTo5);
	OPCODE(0x64, spcResetInventorySequence);
	OPCODE(0x65, spcUnk65ScenePaletteRelated);
	OPCODE(0x66, spcUnk66);
	OPCODE(0x67, spcTournamentSetCamera);
	OPCODE(0x68, spcTournamentCutScene);
	OPCODE(0x69, spcInsideBlackDragonUpdatePalette);
	OPCODE(0x6a, spcCastleGateSceneLogic);
	OPCODE(0x6b, spcTransitionToMap);
	OPCODE(0x6c, spcTransitionFromMap);
	OPCODE(0x6d, spcCaveOfDilemmaSceneLogic);

	OPCODE(0x70, spcLoadLadyOfTheLakeActor);
	OPCODE(0x71, spcFadeCreditsToBackStageScene);
	OPCODE(0x72, spcRunCredits);
	OPCODE(0x73, spcEndCreditsAndRestartGame);
	OPCODE(0x74, spcUseClickerOnLever);

	OPCODE(0x77, spcJesterInLibrarySceneLogic);

	OPCODE(0x79, spcSetUnkFlag2);
	OPCODE(0x7a, spcBlackDragonDialogForCamelhot);
	OPCODE(0x7b, spcSetCameraXToZero);
	OPCODE(0x7c, spcDiamondIntroSequenceLogic);
	OPCODE(0x7d, spcLoadFileS10a6act);
	OPCODE(0x7e, spcLoadFileS10a7act);
	OPCODE(0x7f, spcFlickerPutOnStGeorgeArmor);
	OPCODE(0x80, spcUnk80FlickerArmorOn);
	OPCODE(0x81, spcShakeScreenSceneLogic);
	OPCODE(0x82, spcClearTextFromScreen);
	OPCODE(0x83, spcStopScreenShakeUpdater);
	OPCODE(0x84, spcInsideBlackDragonScreenShake);
	OPCODE(0x85, spc85SetScene1To0x35);
	OPCODE(0x86, spc86SetScene1To0x33);
	OPCODE(0x87, spc87SetScene1To0x17);
	OPCODE(0x88, spc88SetScene1To0x16);
	OPCODE(0x89, spcSetUnkFlag2);
	OPCODE(0x8a, spcClearUnkFlag2);
	OPCODE(0x8b, spcUnk8b);
}

#undef OPCODE

void SpecialOpcodes::freeOpcodes() {
	for (uint i = 0; i < DRAGONS_NUM_SPECIAL_OPCODES; ++i) {
		delete _opcodes[i];
	}
}

// Opcodes

void SpecialOpcodes::spcCatapultMiniGame() {
	Minigame1 minigame1(_vm);
	minigame1.run();
}

void SpecialOpcodes::spcThumbWrestlingMiniGame() {
	Minigame2 minigame2(_vm);
	minigame2.run(0, 1, 1);
}

void SpecialOpcodes::spcClearEngineFlag10() {
	_vm->clearFlags(ENGINE_FLAG_10);
}

void SpecialOpcodes::spcSetEngineFlag10() {
	_vm->setFlags(ENGINE_FLAG_10);
}

void SpecialOpcodes::spcRabbitsMiniGame() {
	Minigame3 minigame3(_vm);
	minigame3.run();
}

void SpecialOpcodes::spcDancingMiniGame() {
	Minigame4 minigame4(_vm);
	minigame4.run();
}

void SpecialOpcodes::spcCastleGardenLogic() {
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x22660);
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x226CA);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x22738);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x22790);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x227E8);
	sceneUpdater.textTbl[2][1] = _vm->getDialogTextId(0x2283C);
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x228A0);
	sceneUpdater.textTbl[3][1] = _vm->getDialogTextId(0x228EC);
	if (_vm->getCurrentSceneId() == 0x1a) {
		sceneUpdater.sequenceIDTbl[0][0] = 1;
		sceneUpdater.sequenceIDTbl[1][0] = 1;
		sceneUpdater.sequenceIDTbl[2][0] = 1;
		sceneUpdater.sequenceIDTbl[3][0] = 1;
		sceneUpdater.iniIDTbl[3][0] = 0x145;
		sceneUpdater.iniIDTbl[4][0] = 0x144;
	} else {
		sceneUpdater.sequenceIDTbl[0][0] = 0xffff;
		sceneUpdater.sequenceIDTbl[1][0] = 0xffff;
		sceneUpdater.sequenceIDTbl[2][0] = 0xffff;
		sceneUpdater.sequenceIDTbl[3][0] = 0xffff;
		sceneUpdater.iniIDTbl[3][0] = 0x83;
		sceneUpdater.iniIDTbl[4][0] = 0x74;
	}
	sceneUpdater.sequenceIDTbl[3][1] = 1;
	sceneUpdater.sequenceIDTbl[2][1] = 1;
	sceneUpdater.sequenceIDTbl[1][1] = 1;
	sceneUpdater.sequenceIDTbl[0][1] = 1;
	sceneUpdater.numSteps[0] = 2;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[2] = 2;
	sceneUpdater.numSteps[3] = 2;
	sceneUpdater.iniIDTbl[0][0] = sceneUpdater.iniIDTbl[3][0];
	sceneUpdater.iniIDTbl[0][1] = sceneUpdater.iniIDTbl[4][0];
	sceneUpdater.iniIDTbl[1][0] = sceneUpdater.iniIDTbl[3][0];
	sceneUpdater.iniIDTbl[1][1] = sceneUpdater.iniIDTbl[4][0];
	sceneUpdater.iniIDTbl[2][0] = sceneUpdater.iniIDTbl[3][0];
	sceneUpdater.iniIDTbl[2][1] = sceneUpdater.iniIDTbl[4][0];
	sceneUpdater.iniIDTbl[3][1] = sceneUpdater.iniIDTbl[4][0];
	setupTableBasedSceneUpdateFunction(1, 4, 0xb4);
}

void SpecialOpcodes::spcUnk9() {
	DragonINI *flicker = _vm->_dragonINIResource->getFlickerRecord();
	assert(flicker);
	flicker->flags |= INI_FLAG_20;
	assert(flicker->actor);
	flicker->actor->setFlag(ACTOR_FLAG_100);
	flicker->actor->_priorityLayer = 0;
	_vm->getINI(1)->flags |= INI_FLAG_20;
}


void SpecialOpcodes::spcUnkA() {
	DragonINI *flicker = _vm->_dragonINIResource->getFlickerRecord();
	flicker->flags &= ~INI_FLAG_20;
	flicker->actor->clearFlag(ACTOR_FLAG_100);
	_vm->getINI(1)->flags &= ~INI_FLAG_20;
}

void SpecialOpcodes::spcUnkC() {
	_vm->fadeToBlack();
}

void SpecialOpcodes::spcFadeScreen() {
	_vm->fadeFromBlack();
}

void SpecialOpcodes::spcLadyOfTheLakeCapturedSceneLogic() {
	_dat_80083148 = 0;
	_uint16_t_80083154 = 0;
	_vm->setSceneUpdateFunction(ladyOfTheLakeCapturedUpdateFunction);
}

void SpecialOpcodes::spcStopLadyOfTheLakeCapturedSceneLogic() {
	_vm->clearSceneUpdateFunction();
	_vm->_sound->resumeMusic();
	if ((_dat_80083148 != 0) || (_uint16_t_80083154 != 0)) {
		//TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	}
	_dat_80083148 = 0;
	_uint16_t_80083154 = 0;
}

void SpecialOpcodes::spc11ShakeScreen() {
	for (int i = 0; i < 16; i++) {
		_vm->_screen->setScreenShakeOffset(0, shakeTbl[i]);
		_vm->waitForFrames(1);
	}
}

void SpecialOpcodes::spcHandleInventionBookTransition() {
	int16 invType = _vm->_inventory->getState();
	if (invType == InventoryOpen) {
		_vm->_inventory->closeInventory();
		_vm->_inventory->setState(Closed);
	}
	if (invType == InventionBookOpen) {
		_vm->_inventory->closeInventionBook();
		_vm->_inventory->setState(Closed);
	}
	_vm->_cursor->updateSequenceID(1);
	_vm->setFlags(ENGINE_FLAG_400);
	_vm->clearFlags(ENGINE_FLAG_10);
}

void SpecialOpcodes::spcUnk13InventionBookCloseRelated() {
	_vm->clearFlags(ENGINE_FLAG_400);
	_vm->setFlags(ENGINE_FLAG_10);
}

void SpecialOpcodes::spcClearEngineFlag8() {
	_vm->clearFlags(ENGINE_FLAG_8);
}

void SpecialOpcodes::spcSetEngineFlag8() {
	_vm->setFlags(ENGINE_FLAG_8);
}

void SpecialOpcodes::spcKnightPoolReflectionLogic() {
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x23E90);
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x23EE6);
	sceneUpdater.textTbl[0][2] = _vm->getDialogTextId(0x23F0C);
	sceneUpdater.textTbl[0][3] = _vm->getDialogTextId(0x23F86);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x24000);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x2406A);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x240C2);
	sceneUpdater.textTbl[2][1] = _vm->getDialogTextId(0x2411E);
	sceneUpdater.textTbl[2][2] = _vm->getDialogTextId(0x24158);
	sceneUpdater.textTbl[2][3] = _vm->getDialogTextId(0x241BC);
	sceneUpdater.textTbl[2][4] = _vm->getDialogTextId(0x241EE);
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x24240);
	sceneUpdater.textTbl[3][1] = _vm->getDialogTextId(0x24286);
	sceneUpdater.textTbl[3][2] = _vm->getDialogTextId(0x242B0);
	sceneUpdater.textTbl[3][3] = _vm->getDialogTextId(0x2431C);
	sceneUpdater.textTbl[4][0] = _vm->getDialogTextId(0x2437C);
	sceneUpdater.textTbl[4][1] = _vm->getDialogTextId(0x243B2);
	sceneUpdater.textTbl[4][2] = _vm->getDialogTextId(0x2440A);
	sceneUpdater.textTbl[4][3] = _vm->getDialogTextId(0x24432);
	sceneUpdater.textTbl[5][0] = _vm->getDialogTextId(0x24480);
	sceneUpdater.textTbl[5][1] = _vm->getDialogTextId(0x244EE);
	sceneUpdater.textTbl[5][2] = _vm->getDialogTextId(0x2453C);

	sceneUpdater.iniIDTbl[0][0] = 0x155;
	sceneUpdater.iniIDTbl[0][1] = 0x14B;
	sceneUpdater.iniIDTbl[0][2] = 0x155;
	sceneUpdater.iniIDTbl[0][3] = 0x14B;
	sceneUpdater.iniIDTbl[1][0] = 0x155;
	sceneUpdater.iniIDTbl[1][1] = 0x14B;
	sceneUpdater.iniIDTbl[2][0] = 0x155;
	sceneUpdater.iniIDTbl[2][1] = 0x14B;
	sceneUpdater.iniIDTbl[2][2] = 0x155;
	sceneUpdater.iniIDTbl[2][3] = 0x14B;
	sceneUpdater.iniIDTbl[2][4] = 0x155;
	sceneUpdater.iniIDTbl[3][0] = 0x155;
	sceneUpdater.iniIDTbl[3][1] = 0x14B;
	sceneUpdater.iniIDTbl[3][2] = 0x155;
	sceneUpdater.iniIDTbl[3][3] = 0x14B;
	sceneUpdater.iniIDTbl[4][0] = 0x14B;
	sceneUpdater.iniIDTbl[4][1] = 0x155;
	sceneUpdater.iniIDTbl[4][2] = 0x14B;
	sceneUpdater.iniIDTbl[4][3] = 0x155;
	sceneUpdater.iniIDTbl[5][0] = 0x155;
	sceneUpdater.iniIDTbl[5][1] = 0x14B;
	sceneUpdater.iniIDTbl[5][2] = 0x155;
	sceneUpdater.sequenceIDTbl[0][0] = 10;
	sceneUpdater.sequenceIDTbl[0][1] = 2;
	sceneUpdater.sequenceIDTbl[0][2] = 10;
	sceneUpdater.sequenceIDTbl[0][3] = 2;
	sceneUpdater.sequenceIDTbl[1][0] = 10;
	sceneUpdater.sequenceIDTbl[1][1] = 2;
	sceneUpdater.sequenceIDTbl[2][0] = 10;
	sceneUpdater.sequenceIDTbl[2][1] = 2;
	sceneUpdater.sequenceIDTbl[2][2] = 10;
	sceneUpdater.sequenceIDTbl[2][3] = 2;
	sceneUpdater.sequenceIDTbl[2][4] = 10;
	sceneUpdater.sequenceIDTbl[3][0] = 10;
	sceneUpdater.sequenceIDTbl[3][1] = 2;
	sceneUpdater.sequenceIDTbl[3][2] = 10;
	sceneUpdater.sequenceIDTbl[3][3] = 2;
	sceneUpdater.sequenceIDTbl[4][0] = 2;
	sceneUpdater.sequenceIDTbl[4][1] = 10;
	sceneUpdater.sequenceIDTbl[4][2] = 2;
	sceneUpdater.sequenceIDTbl[4][3] = 10;
	sceneUpdater.sequenceIDTbl[5][0] = 10;
	sceneUpdater.sequenceIDTbl[5][2] = 10;
	sceneUpdater.numSteps[2] = 5;
	sceneUpdater.sequenceIDTbl[5][1] = 2;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[0] = 4;
	sceneUpdater.numSteps[3] = 4;
	sceneUpdater.numSteps[4] = 4;
	sceneUpdater.numSteps[5] = 3;
	setupTableBasedSceneUpdateFunction(0x168, 6, 300);
}

void SpecialOpcodes::spcWalkOnStilts() {
	bool isInWater = false;

	Actor *flickerOnStilts = _vm->_dragonINIResource->getRecord(0x2a0)->actor;
	Actor *waterRipples = _vm->_dragonINIResource->getRecord(0x2a9)->actor;

	flickerOnStilts->_walkSlopeX = flickerOnStilts->_walkSlopeX / 3;
	flickerOnStilts->_walkSlopeY = flickerOnStilts->_walkSlopeY / 3;

	while (flickerOnStilts->isFlagSet(ACTOR_FLAG_10)) {
		if (flickerOnStilts->_frame->field_c == 0) {
			isInWater = false;
		} else {
			if (!isInWater && flickerOnStilts->_y_pos >= 0x6a && flickerOnStilts->_y_pos < 0x96) {
				isInWater = true;
				waterRipples->_x_pos = flickerOnStilts->_x_pos - flickerOnStilts->_frame->field_e;
				waterRipples->_y_pos = flickerOnStilts->_y_pos - flickerOnStilts->_frame->field_10;
				waterRipples->updateSequence(9);
			}
		}
		_vm->waitForFrames(1);
	}
}

void SpecialOpcodes::spcActivatePizzaMakerActor() {
	_vm->setSceneUpdateFunction(pizzaUpdateFunction);
}

void SpecialOpcodes::spcDeactivatePizzaMakerActor() {
	if (_vm->getSceneUpdateFunction() == pizzaUpdateFunction) {
		_vm->clearSceneUpdateFunction();
	}
}

void SpecialOpcodes::spcPizzaMakerActorStopWorking() {
	spcDeactivatePizzaMakerActor();
	pizzaMakerStopWorking();
}

void SpecialOpcodes::spcDragonArrivesAtTournament() {
	_vm->_dragonINIResource->getRecord(0x123)->actor->setFlag(ACTOR_FLAG_400);
	_vm->_dragonINIResource->getRecord(0x124)->actor->setFlag(ACTOR_FLAG_400);
	_vm->_dragonINIResource->getRecord(0)->actor->setFlag(ACTOR_FLAG_400);
}

void SpecialOpcodes::spcDragonCatapultMiniGame() {
	Minigame5 minigame5(_vm);
	minigame5.run();
}

void SpecialOpcodes::spcStGeorgeDragonLanded() {
//	DisableVSyncEvent();
	DragonINI *ini121 = _vm->_dragonINIResource->getRecord(0x121);
	Actor *origActor = ini121->actor;
	ini121->actor = _vm->_actorManager->loadActor(0x48, 4, ini121->actor->_x_pos, ini121->actor->_y_pos);
	origActor->reset_maybe();
//TODO	reset_actor_maybe();
	ini121->actor->setFlag(ACTOR_FLAG_80);
	ini121->actor->_scale = DRAGONS_ENGINE_SPRITE_100_PERCENT_SCALE;
	ini121->actor->_priorityLayer = 2;
	ini121->actorResourceId = 0x48;

	_vm->updateActorSequences();
	_vm->_scene->draw();
}

void SpecialOpcodes::spcSetEngineFlag0x20000() {
	_vm->setFlags(ENGINE_FLAG_20000);
}

void SpecialOpcodes::spcClearEngineFlag0x20000() {
	_vm->clearFlags(ENGINE_FLAG_20000);
}

void SpecialOpcodes::spcSetEngineFlag0x200000() {
	_vm->setFlags(ENGINE_FLAG_200000);
}

void SpecialOpcodes::spcClearEngineFlag0x200000() {
	_vm->clearFlags(ENGINE_FLAG_200000);
}

void SpecialOpcodes::spcFlickerSetPriority2() {
	_vm->_dragonINIResource->getFlickerRecord()->actor->_priorityLayer = 2;
}

void SpecialOpcodes::spcMenInMinesSceneLogic() {
	_vm->setSceneUpdateFunction(menInMinesSceneUpdateFunction);
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
	setSpecialOpCounter(0);
}

void SpecialOpcodes::spcStopMenInMinesSceneLogic() {
	if (_vm->getSceneUpdateFunction() == menInMinesSceneUpdateFunction) {
		_vm->clearSceneUpdateFunction();
		if (0x3c < _specialOpCounter) {
			_specialOpCounter = 0x3c;
		}
		for (; _specialOpCounter > 0; _specialOpCounter--) {
			_vm->waitForFrames(1);
		}
		//TODO
		//FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	}
}

void SpecialOpcodes::spcMonksAtBarSceneLogic() {
	setSpecialOpCounter(-1);
	_vm->setSceneUpdateFunction(monksAtBarSceneUpdateFunction);
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
}

void SpecialOpcodes::spcStopMonksAtBarSceneLogic() {
	if (_vm->getSceneUpdateFunction() == monksAtBarSceneUpdateFunction) {
		_vm->clearSceneUpdateFunction();
		if ((_dat_80083148 != 0) && (_uint16_t_80083154 != 0)) {
			//TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
		}
		setSpecialOpCounter(0);
		_uint16_t_80083154 = 0;
		_dat_80083148 = 0;
	}

}

void SpecialOpcodes::spcFlameBedroomEscapeSceneLogic() {
	setSpecialOpCounter(-1);
	if ((_dat_80083148 != 0) && (_uint16_t_80083154 != 0)) {
		//TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	}
	_vm->setSceneUpdateFunction(flameEscapeSceneUpdateFunction);
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
}

void SpecialOpcodes::spcStopFlameBedroomEscapeSceneLogic() {
	setSpecialOpCounter(0);
//	TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
	_vm->_dragonINIResource->getRecord(0x96)->actor->updateSequence(0);
	if (_vm->getSceneUpdateFunction() == flameEscapeSceneUpdateFunction) {
		_vm->clearSceneUpdateFunction();
	}
}

void SpecialOpcodes::spcCastleMoatFull() {
	_vm->_scene->getScaleLayer()->backup();
}

void SpecialOpcodes::spcCastleRestoreScalePoints() {
	_vm->_scene->getScaleLayer()->restore();
}

void SpecialOpcodes::spcCastleMoatUpdateActorSceneScalePoints() {
	_vm->_scene->getScaleLayer()->clearAll();
	_vm->_scene->getScaleLayer()->setValue(0, 199, 7);
}

void SpecialOpcodes::spcCastleGateMoatDrainedSceneLogic() {
	setSpecialOpCounter(-1);
	if ((_dat_80083148 != 0) && (_uint16_t_80083154 != 0)) {
		//TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	}
	_vm->setSceneUpdateFunction(moatDrainedSceneUpdateFunction);
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
}
void SpecialOpcodes::spcUnk34() {
	Actor *flicker = _vm->_dragonINIResource->getFlickerRecord()->actor;
	flicker->setFlag(ACTOR_FLAG_80);
	flicker->_scale = DRAGONS_ENGINE_SPRITE_100_PERCENT_SCALE;
}

void SpecialOpcodes::spcFlickerClearFlag0x80() {
	_vm->_dragonINIResource->getFlickerRecord()->actor->clearFlag(ACTOR_FLAG_80);
}

void SpecialOpcodes::spcNoop1() {
	//No op
}

void SpecialOpcodes::spcTownAngryVillagersSceneLogic() {
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x35946);
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x359BC);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x35A38);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x35ABC);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x35B28);
	sceneUpdater.textTbl[2][1] = _vm->getDialogTextId(0x35B9C);
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x35C10);
	sceneUpdater.textTbl[3][1] = _vm->getDialogTextId(0x35C80);
	sceneUpdater.textTbl[4][0] = _vm->getDialogTextId(0x35CFA);
	sceneUpdater.textTbl[4][1] = _vm->getDialogTextId(0x35D64);
	sceneUpdater.iniIDTbl[0][0] = 0x1DC;
	sceneUpdater.iniIDTbl[0][1] = 0x1DD;
	sceneUpdater.iniIDTbl[1][0] = 0x1DC;
	sceneUpdater.iniIDTbl[1][1] = 0x1DD;
	sceneUpdater.iniIDTbl[2][0] = 0x1DC;
	sceneUpdater.iniIDTbl[2][1] = 0x1DD;
	sceneUpdater.iniIDTbl[3][0] = 0x1DC;
	sceneUpdater.iniIDTbl[3][1] = 0x1DD;
	sceneUpdater.iniIDTbl[4][0] = 0x1DC;
	sceneUpdater.iniIDTbl[4][1] = 0x1DD;
	sceneUpdater.sequenceIDTbl[0][0] = 0x11;
	sceneUpdater.sequenceIDTbl[0][1] = 6;
	sceneUpdater.sequenceIDTbl[1][0] = 0x11;
	sceneUpdater.sequenceIDTbl[1][1] = 6;
	sceneUpdater.sequenceIDTbl[2][0] = 0x11;
	sceneUpdater.sequenceIDTbl[2][1] = 6;
	sceneUpdater.sequenceIDTbl[3][0] = 0x11;
	sceneUpdater.sequenceIDTbl[3][1] = 6;
	sceneUpdater.sequenceIDTbl[4][0] = 0x11;
	sceneUpdater.sequenceIDTbl[4][1] = 6;
	sceneUpdater.numSteps[0] = 2;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[2] = 2;
	sceneUpdater.numSteps[3] = 2;
	sceneUpdater.numSteps[4] = 2;
	setupTableBasedSceneUpdateFunction(0xf0, 5, 0x708);
}

void SpecialOpcodes::spcBlackDragonCrashThroughGate() {
	for (int i = 0; i < 16; i++) {
		_vm->_screen->setScreenShakeOffset(shakeTbl[i], shakeTbl[i]);
		_vm->waitForFrames(1);
	}
}

void SpecialOpcodes::spcSetEngineFlag0x2000000() {
	_vm->setFlags(ENGINE_FLAG_2000000);
}

void SpecialOpcodes::spcClearEngineFlag0x2000000() {
	_vm->clearFlags(ENGINE_FLAG_2000000);
}

void SpecialOpcodes::spcZigmondFraudSceneLogic() {
	sceneUpdater.numSteps[0] = 2;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[2] = 1;
	sceneUpdater.numSteps[3] = 1;
	sceneUpdater.numSteps[4] = 2;
	sceneUpdater.numSteps[5] = 2;
	sceneUpdater.numSteps[6] = 2;
	sceneUpdater.numSteps[7] = 2;
//TODO do we need this -> DAT_80072dc0 = 2;
	sceneUpdater.iniIDTbl[0][0] = 0x19E;
	sceneUpdater.iniIDTbl[0][1] = 0x197;
	sceneUpdater.iniIDTbl[1][0] = 0x19E;
	sceneUpdater.iniIDTbl[1][1] = 0x197;
	sceneUpdater.iniIDTbl[2][0] = 0x197;
	sceneUpdater.iniIDTbl[3][0] = 0x19e;
	sceneUpdater.iniIDTbl[4][0] = 0x197;
	sceneUpdater.iniIDTbl[4][1] = 0x19e;
	sceneUpdater.iniIDTbl[5][0] = 0x19e;
	sceneUpdater.iniIDTbl[5][1] = 0x197;
	sceneUpdater.iniIDTbl[6][0] = 0x197;
	sceneUpdater.iniIDTbl[6][1] = 0x19e;
	sceneUpdater.iniIDTbl[7][0] = 0x19e;
	sceneUpdater.iniIDTbl[7][1] = 0x197;

	sceneUpdater.sequenceIDTbl[0][0] = 2;
	sceneUpdater.sequenceIDTbl[0][1] = 0xe;
	sceneUpdater.sequenceIDTbl[1][0] = 2;
	sceneUpdater.sequenceIDTbl[1][1] = 0xe;
	sceneUpdater.sequenceIDTbl[2][0] = 0xe;
	sceneUpdater.sequenceIDTbl[3][0] = 2;
	sceneUpdater.sequenceIDTbl[4][0] = 0xe;
	sceneUpdater.sequenceIDTbl[4][1] = 2;
	sceneUpdater.sequenceIDTbl[5][0] = 2;
	sceneUpdater.sequenceIDTbl[5][1] = 0xe;
	sceneUpdater.sequenceIDTbl[6][0] = 0xe;
	sceneUpdater.sequenceIDTbl[6][1] = 2;
	sceneUpdater.sequenceIDTbl[7][0] = 2;
	sceneUpdater.sequenceIDTbl[7][1] = 0xe;

	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x2D000);
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x2D044);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x2D0B2);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x2D0D6);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x2D152);
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x2D1A4);
	sceneUpdater.textTbl[4][0] = _vm->getDialogTextId(0x2D20A);
	sceneUpdater.textTbl[4][1] = _vm->getDialogTextId(0x2D27C);
	sceneUpdater.textTbl[5][0] = _vm->getDialogTextId(0x2D2EC);
	sceneUpdater.textTbl[5][1] = _vm->getDialogTextId(0x2D336);
	sceneUpdater.textTbl[6][0] = _vm->getDialogTextId(0x2D3E0);
	sceneUpdater.textTbl[6][1] = _vm->getDialogTextId(0x2D456);
	sceneUpdater.textTbl[7][0] = _vm->getDialogTextId(0x2D4A8);
	sceneUpdater.textTbl[7][1] = _vm->getDialogTextId(0x2D504);

	setupTableBasedSceneUpdateFunction(0x168, 8, 0xb4);
}

void SpecialOpcodes::spcZigmondFraudSceneLogic1() {
	sceneUpdater.numSteps[0] = 1;
	sceneUpdater.iniIDTbl[0][0] = 0x197;
	sceneUpdater.sequenceIDTbl[0][0] = 0x12;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x2F422);

	setupTableBasedSceneUpdateFunction(300, 1, 0x708);
}

void SpecialOpcodes::spcBrokenBlackDragonSceneLogic() {
	sceneUpdater.sequenceIDTbl[0][0] = 5;
	sceneUpdater.sequenceIDTbl[1][0] = 5;
	sceneUpdater.sequenceIDTbl[1][1] = 2;
	sceneUpdater.sequenceIDTbl[2][0] = 5;
	sceneUpdater.numSteps[0] = 1;
	sceneUpdater.sequenceIDTbl[2][1] = 2;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[2] = 2;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x40802);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x40852);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x40896);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x408C0);
	sceneUpdater.textTbl[2][1] = _vm->getDialogTextId(0x4092A);
	sceneUpdater.iniIDTbl[0][0] = 0x23B;
	sceneUpdater.iniIDTbl[1][0] = 0x23B;
	sceneUpdater.iniIDTbl[1][1] = 0x231;
	sceneUpdater.iniIDTbl[2][0] = 0x23B;
	sceneUpdater.iniIDTbl[2][1] = 0x231;
	setupTableBasedSceneUpdateFunction(300, 3, 600);
}

void SpecialOpcodes::spcDodoUnderAttackSceneLogic() {
	sceneUpdater.sequenceIDTbl[0][0] = 8;
	sceneUpdater.sequenceIDTbl[1][0] = 8;
	sceneUpdater.sequenceIDTbl[2][0] = 8;
	sceneUpdater.sequenceIDTbl[3][0] = 8;
	sceneUpdater.sequenceIDTbl[4][0] = 8;
	sceneUpdater.sequenceIDTbl[5][0] = 8;
	sceneUpdater.numSteps[0] = 1;
	sceneUpdater.numSteps[1] = 1;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x3353A);
	sceneUpdater.iniIDTbl[0][0] = 0x1C4;
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x335AC);
	sceneUpdater.iniIDTbl[1][0] = 0x1C4;
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x335F8);
	sceneUpdater.iniIDTbl[2][0] = 0x1C4;
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x33660);
	sceneUpdater.iniIDTbl[3][0] = 0x1C4;
	sceneUpdater.textTbl[4][0] = _vm->getDialogTextId(0x336DE);
	sceneUpdater.iniIDTbl[4][0] = 0x1C4;
	sceneUpdater.textTbl[5][0] = _vm->getDialogTextId(0x3375C);
	sceneUpdater.iniIDTbl[5][0] = 0x1C4;
	sceneUpdater.numSteps[2] = 1;
	sceneUpdater.numSteps[3] = 1;
	sceneUpdater.numSteps[4] = 1;
	sceneUpdater.numSteps[5] = 1;
	setupTableBasedSceneUpdateFunction(0x1e0, 6, 0x1e0);
}

void SpecialOpcodes::spcForestWithoutDodoSceneLogic() {
	sceneUpdater.sequenceIDTbl[0][0] = 5;
	sceneUpdater.sequenceIDTbl[0][1] = 0xb;
	sceneUpdater.sequenceIDTbl[1][0] = 0xb;
	sceneUpdater.sequenceIDTbl[1][2] = 0xb;
	sceneUpdater.numSteps[0] = 2;
	sceneUpdater.numSteps[1] = 3;
	sceneUpdater.sequenceIDTbl[1][1] = 5;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x33EA0);
	sceneUpdater.iniIDTbl[0][0] = 0x1C5;
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x33EFC);
	sceneUpdater.iniIDTbl[0][1] = 0x1C4;
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x33F34);
	sceneUpdater.iniIDTbl[1][0] = 0x1C4;
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x34000);
	sceneUpdater.iniIDTbl[1][1] = 0x1C5;
	sceneUpdater.textTbl[1][2] = _vm->getDialogTextId(0x34074);
	sceneUpdater.iniIDTbl[1][2] = 0x1C4;
	setupTableBasedSceneUpdateFunction(300, 2, 600);
}

void SpecialOpcodes::spcBlackDragonOnHillSceneLogic() {
	sceneUpdater.sequenceIDTbl[0][0] = 3;
	sceneUpdater.sequenceIDTbl[0][1] = 7;
	sceneUpdater.sequenceIDTbl[1][0] = 3;
	sceneUpdater.sequenceIDTbl[1][1] = 7;
	sceneUpdater.sequenceIDTbl[2][0] = 3;
	sceneUpdater.sequenceIDTbl[2][1] = 7;
	sceneUpdater.sequenceIDTbl[3][0] = 3;
	sceneUpdater.numSteps[0] = 2;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x325EA);
	sceneUpdater.textTbl[0][1] = _vm->getDialogTextId(0x3262A);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x32686);
	sceneUpdater.textTbl[1][1] = _vm->getDialogTextId(0x326D8);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x3270E);
	sceneUpdater.textTbl[2][1] = _vm->getDialogTextId(0x32774);
	sceneUpdater.textTbl[3][0] = _vm->getDialogTextId(0x32D72);
	sceneUpdater.iniIDTbl[0][0] = 0x1B6;
	sceneUpdater.iniIDTbl[0][1] = 0x1B7;
	sceneUpdater.iniIDTbl[1][0] = 0x1B6;
	sceneUpdater.iniIDTbl[1][1] = 0x1B7;
	sceneUpdater.iniIDTbl[2][0] = 0x1B6;
	sceneUpdater.iniIDTbl[2][1] = 0x1B7;
	sceneUpdater.iniIDTbl[3][0] = 0x1B6;
	sceneUpdater.numSteps[1] = 2;
	sceneUpdater.numSteps[2] = 2;
	sceneUpdater.numSteps[3] = 1;
	setupTableBasedSceneUpdateFunction(300, 4, 300);
}

void SpecialOpcodes::spcUnk4e() {
	panCamera(1);
}

void SpecialOpcodes::spcUnk4f() {
	panCamera(2);
}

void SpecialOpcodes::spcCloseInventory() {
	if (_vm->_inventory->getState() == InventoryOpen) {
		_vm->_inventory->closeInventory();
		_vm->_inventory->setState(Closed);
	}
}

void SpecialOpcodes::spcOpenInventionBook() {
	if (_vm->_inventory->getState() == InventoryOpen) {
		_vm->_inventory->closeInventory();
	}
	_vm->_inventory->openInventionBook();
	_vm->_inventory->setState(InventionBookOpen);
}

void SpecialOpcodes::spcCloseInventionBook() {
	_vm->_inventory->closeInventionBook();
	_vm->_inventory->setState(Closed);
}

void SpecialOpcodes::spcSetEngineFlag0x4000000() {
	_vm->setFlags(ENGINE_FLAG_4000000);
}

void SpecialOpcodes::spcClearEngineFlag0x4000000() {
	_vm->clearFlags(ENGINE_FLAG_4000000);
}

void SpecialOpcodes::spcSetCursorSequenceIdToZero() {
	_vm->_cursor->_sequenceID = 0;
}

void SpecialOpcodes::spcFlickerSetFlag0x80() {
	_vm->_dragonINIResource->getFlickerRecord()->actor->setFlag(ACTOR_FLAG_80);
}

//used in castle garden scene with knights
void SpecialOpcodes::spcUnk5d() {
	_vm->getINI(_vm->getINI(0x13b)->objectState + 0x13c)->actor->updateSequence(_vm->getINI(0x13b)->objectState2);
}

void SpecialOpcodes::spcUnk5e() {
	panCamera(1);
	_vm->_dragonINIResource->setFlickerRecord(_vm->getINI(0));
}

void SpecialOpcodes::spcUnk5f() {
	_vm->getINI(0x2ab)->objectState = 0;
	panCamera(2);
}

void SpecialOpcodes::spcCastleBuildBlackDragonSceneLogic() {
	_vm->setSceneUpdateFunction(castleBuildingBlackDragon2UpdateFunction);
}

void SpecialOpcodes::spcStopSceneUpdateFunction() {
	_vm->clearSceneUpdateFunction();
}

void SpecialOpcodes::spcSetInventorySequenceTo5() {
	_vm->_inventory->updateActorSequenceId(5);
}

void SpecialOpcodes::spcResetInventorySequence() {
	_vm->_inventory->resetSequenceId();
}

void SpecialOpcodes::spcUnk65ScenePaletteRelated() {
	byte *palette = _vm->_screen->getPalette(0);
	memset(palette + 0xb1 * 2, 0, 32); //zero out 16 palette records from index 0xb1 to 0xc0
}

void SpecialOpcodes::spcUnk66() {
	uint16 var =_vm->getVar(2);

	uint16 bVar1 = (var & 1) == 0;
	uint16 uVar9 = bVar1;
	if ((var & 4) == 0) {
		uVar9 = (uint16)bVar1 + 1;
	}
	if ((var & 2) == 0) {
		uVar9 = uVar9 + 1;
	}
	_vm->getINI(1)->objectState = uVar9;
}

void SpecialOpcodes::spcTournamentSetCamera() {
	_vm->_scene->_camera.x = 0x140; // TODO should have method for updating camera.
}

void SpecialOpcodes::spcTournamentCutScene() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->tournamentCutScene();
	delete cutScene;
}

void SpecialOpcodes::spcInsideBlackDragonUpdatePalette() {
	memcpy(_vm->_scene->getPalette() + 0x180,
			_vm->_dragonINIResource->getRecord(0x2b2)->actor->_actorResource->getPalette() + 0x180,
			0x80);
	_vm->_screen->loadPalette(0, _vm->_scene->getPalette());
}

void SpecialOpcodes::spcCastleGateSceneLogic() {
	_vm->_screen->updatePaletteTransparency(0, 0xc0, 0xff, true);
	//TODO FUN_80017d68(3, 0); sets sprite layer attribute from layers 0 and 1. Doesn't seem to be needed.
	setSpecialOpCounter(-1);
	_vm->clearFlags(ENGINE_FLAG_1);
	_vm->setSceneUpdateFunction(castleFogUpdateFunction);
}

// 0x80038c1c
void SpecialOpcodes::panCamera(int16 mode) {
	int iVar1;
	int iVar2;

	if (mode == 1) {
		_vm->getINI(0x2ab)->objectState = _vm->_scene->_camera.x;
		_vm->_dragonINIResource->setFlickerRecord(nullptr);
		iVar2 = (int) _vm->_scene->_camera.x;
		iVar1 = iVar2;
		while (iVar1 <= (_vm->_scene->getStageWidth() - 320)) {
			_vm->_scene->_camera.x = (short) iVar2;
			_vm->waitForFrames(1);
			iVar2 = iVar2 + 4;
			iVar1 = iVar2 * 0x10000 >> 0x10;
		}
		_vm->_scene->_camera.x = _vm->_scene->getStageWidth() - 320;
	}
	if (mode == 2) {
		iVar2 = (int) _vm->_scene->_camera.x;
		iVar1 = iVar2;
		while (-1 < iVar1) {
			_vm->_scene->_camera.x = (short) iVar2;
			_vm->waitForFrames(1);
			iVar2 = iVar2 + -3;
			iVar1 = iVar2 * 0x10000;
		}
		_vm->_scene->_camera.x = 0;
		_vm->_dragonINIResource->setFlickerRecord(_vm->getINI(0));
	}
	return;
}

void SpecialOpcodes::spcBlackDragonDialogForCamelhot() {
	uint16 buffer[1024];
	uint32 textId = _vm->getDialogTextId(0x30DD8);
	_vm->_talk->loadText(textId, buffer, 1024);
	_vm->_talk->displayDialogAroundPoint(buffer, 0x27, 0xc, 0xc01, 0, textId);
	//TODO this isn't quite right. The audio isn't played and it's not waiting long enough.
}

void SpecialOpcodes::spcSetCameraXToZero() {
	_vm->_scene->_camera.x = 0;
}

void SpecialOpcodes::spcDiamondIntroSequenceLogic() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->diamondScene();
	delete cutScene;
}

void SpecialOpcodes::spcLoadFileS10a6act() {
	//TODO is this needed?
}

void SpecialOpcodes::spcLoadFileS10a7act() {
	//TODO is this needed?
}

void SpecialOpcodes::spcFlickerPutOnStGeorgeArmor() {
	Actor *actor = _vm->_dragonINIResource->getRecord(0x21f)->actor;
	actor->setFlag(ACTOR_FLAG_100);
	actor->_priorityLayer = 1;
}

void SpecialOpcodes::spcClearTextFromScreen() {
	_vm->clearAllText();
}

void SpecialOpcodes::spcStopScreenShakeUpdater() {
	_vm->clearSceneUpdateFunction();
	_vm->_screen->setScreenShakeOffset(0, 0);
}

void SpecialOpcodes::spcInsideBlackDragonScreenShake() {
	for (int i = 0; i < 5; i ++) {
		_vm->_screen->setScreenShakeOffset(0, shakeTbl[i]);
		_vm->waitForFrames(1);
	}
	_vm->_screen->setScreenShakeOffset(0, 0);
}

void SpecialOpcodes::spc85SetScene1To0x35() {
	_vm->_sceneId1 = 0x35;
}

void SpecialOpcodes::spc86SetScene1To0x33() {
	_vm->_sceneId1 = 0x33;
}

void SpecialOpcodes::spc87SetScene1To0x17() {
	_vm->_sceneId1 = 0x17;
}

void SpecialOpcodes::spc88SetScene1To0x16() {
	_vm->_sceneId1 = 0x16;
}

void SpecialOpcodes::spcSetUnkFlag2() {
	_vm->setUnkFlags(ENGINE_UNK1_FLAG_2);
}

void SpecialOpcodes::spcClearUnkFlag2() {
	_vm->clearUnkFlags(ENGINE_UNK1_FLAG_2);
}

void SpecialOpcodes::spcUnk8b() {
	//TODO sceneId_1 = DAT_80063e20; //0xA
}

void SpecialOpcodes::spcHedgehogTest() {
	if (_vm->_dragonINIResource->getRecord(0x168)->actor->_sequenceID == 4 &&
			_vm->_dragonINIResource->getRecord(0x169)->actor->_sequenceID == 4 &&
			_vm->_dragonINIResource->getRecord(0x16a)->actor->_sequenceID == 4) {
		_vm->_dragonINIResource->getRecord(0x169)->objectState = 1;
	} else {
		_vm->_dragonINIResource->getRecord(0x169)->objectState = 0;
	}
}

void SpecialOpcodes::spcLoadScene1() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->scene1();
	delete cutScene;
}

void SpecialOpcodes::spcKnightsSavedCastleCutScene() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->knightsSavedBackAtCastle();
	delete cutScene;
}

void SpecialOpcodes::spcFlickerReturnsCutScene() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->flameReturnsCutScene();
	delete cutScene;
}

void SpecialOpcodes::spcKnightsSavedAgainCutScene() {
	CutScene *cutScene = new CutScene(_vm);
	cutScene->knightsSavedAgain();
	delete cutScene;
}

void SpecialOpcodes::spcTransitionToMap() {
	_vm->_scene->setMgLayerPriority(0);
	_vm->_scene->setFgLayerPriority(0);
	_vm->_cursor->updateSequenceID(0);
	_vm->waitForFrames(1);
	_vm->setFlags(ENGINE_FLAG_20000000);
	mapTransition(1);
}

void SpecialOpcodes::spcTransitionFromMap() {
	mapTransition(0);
	_vm->_scene->setMgLayerPriority(2);
	_vm->_scene->setFgLayerPriority(3);
	_vm->clearFlags(ENGINE_FLAG_20000000);
}

void SpecialOpcodes::spcCaveOfDilemmaSceneLogic() {
	_vm->setSceneUpdateFunction(caveOfDilemmaUpdateFunction);
}

void SpecialOpcodes::spcFadeCreditsToBackStageScene() {
	_vm->fadeToBlackExcludingFont();
	_vm->_screen->loadPalette(0, _vm->_scene->getPalette());
	_vm->_scene->_camera.x = 0x140;
	_vm->waitForFrames(0x3c);
	_vm->fadeFromBlackExcludingFont();
}

void SpecialOpcodes::spcRunCredits() {
//	int iVar1;
//	DAT_8006a440 = 0x1a;
//	DAT_80087270 = 0x78;
	_vm->setUnkFlags(ENGINE_UNK1_FLAG_1);
	_vm->setUnkFlags(ENGINE_UNK1_FLAG_2);
//	open_files_slot_number_tbl = 0;
//	DAT_80072de4 = 0;
//	buf2048bytes = actor_dictionary;
//	DAT_8007273c = actor_dictionary;
	_vm->_cursor->setActorFlag400();
	_vm->clearFlags(ENGINE_FLAG_8);
	_vm->setFlags(ENGINE_FLAG_8000000);
//	iVar1 = file_read_to_buffer(strCredits_txt);
//	DAT_800728ec = iVar1 + (int)DAT_8007273c;
//	buf2048bytes = (int32 *)((iVar1 + 3U & 0xfffffffc) + (int)buf2048bytes);
//	memcpy2((byte *)buf2048bytes, scrFileData_maybe, 0x200);
//	buf2048bytes = buf2048bytes + 0x80;
	_vm->_screen->loadPalette(0, _vm->_dragonINIResource->getRecord(0x2C8)->actor->_actorResource->getPalette());
	_vm->_scene->setMgLayerPriority(0);
	_vm->_scene->setFgLayerPriority(0);
//	vsync_updater_function = creditsUpdateFunction;
	_vm->_credits->start();
}

void SpecialOpcodes::spcEndCreditsAndRestartGame() {
	_vm->fadeToBlackExcludingFont();
//	_volumeSFX = 0;
//	setCDAVolumes();
	while (_vm->_credits->isRunning()) {
		_vm->waitForFrames(1);
	}
//	ReloadGameFlag = 2;
//	Exec_FMV_RELOADTT();

	_vm->waitForFrames(100);

	Dragons::DragonsEngine::quitGame();
}

void SpecialOpcodes::spcLoadLadyOfTheLakeActor() {
	//TODO
	DragonINI *ini = _vm->_dragonINIResource->getRecord(0x2a7);
	_vm->_actorManager->loadActor(0xcd, ini->actor->_actorID);
	ini->actor->setFlag(ACTOR_FLAG_4);
	ini->actorResourceId = 0xcd;
	//DisableVSyncEvent();
//	uVar17 = (uint)(uint16)dragon_ini_pointer[DAT_8006398c + -1].field_0x1c;
//	uVar7 = load_actor_file(0xcc);
//	file_read_to_buffer(s_s12a6.act_80011740, (&actor_dictionary)[(uVar7 & 0xffff) * 2]);
//	actors[uVar17].﻿actorFileDictionaryIndex = (uint16_t)uVar7;
//	actors[uVar17].﻿resourceID = 0xcd;
//	iVar18 = DAT_8006398c;
//	actors[uVar17]._flags = actors[uVar17]._flags | 4;
//	dragon_ini_pointer[iVar18 + -1].x = 0xcd;
//	LAB_8002ad94:
	//EnableVSyncEvent();
}

void SpecialOpcodes::spcUseClickerOnLever() {
	if (_vm->_inventory->isOpen()) {
		_vm->_talk->flickerRandomDefaultResponse();
		_vm->_dragonINIResource->getRecord(0)->objectState = 1;
	} else {
		_vm->_dragonINIResource->getRecord(0)->objectState = 0;
	}
}

void SpecialOpcodes::spcJesterInLibrarySceneLogic() {
	sceneUpdater.sequenceIDTbl[0][0] = 0xffff;
	sceneUpdater.sequenceIDTbl[1][0] = 0xffff;
	sceneUpdater.sequenceIDTbl[2][0] = 0xffff;
	sceneUpdater.numSteps[0] = 1;
	sceneUpdater.numSteps[1] = 1;
	sceneUpdater.numSteps[2] = 1;
	sceneUpdater.textTbl[0][0] = _vm->getDialogTextId(0x18502);
	sceneUpdater.textTbl[1][0] = _vm->getDialogTextId(0x185E0);
	sceneUpdater.textTbl[2][0] = _vm->getDialogTextId(0x18596);
	sceneUpdater.iniIDTbl[0][0] = 0xD7;
	sceneUpdater.iniIDTbl[1][0] = 0xD7;
	sceneUpdater.iniIDTbl[2][0] = 0xD7;
	setupTableBasedSceneUpdateFunction(300, 3, 0x708);
}

void SpecialOpcodes::pizzaMakerStopWorking() {
	Actor *actorf4 = _vm->getINI(0xf4)->actor;
	Actor *actorf5 = _vm->getINI(0xf5)->actor;

	if (actorf4->_sequenceID != 0) {
		if (actorf4->_sequenceID == 1) {
			actorf4->waitUntilFlag8And4AreSet();
			actorf4->updateSequence(2);
			actorf5->_x_pos = 277;
			actorf5->_y_pos = 92;
			actorf5->updateSequence(7);
			_vm->waitForFrames(0x78);
			actorf5->updateSequence(8);
			actorf5->waitUntilFlag8And4AreSet();
			actorf5->_x_pos = -100;
			actorf5->_y_pos = 100;
			actorf4->updateSequence(3);
		} else {
			if (actorf4->_sequenceID == 2) {
				_vm->waitForFrames(0x78);
				actorf5->updateSequence(8);
				actorf5->waitUntilFlag8And4AreSet();
				actorf5->_x_pos = -100;
				actorf5->_y_pos = 100;
				actorf4->updateSequence(3);
			} else {
				if (actorf4->_sequenceID != 3) {
					return;
				}
			}
		}
		actorf4->waitUntilFlag8And4AreSet();
		actorf4->updateSequence(0);
	}
}

void SpecialOpcodes::clearSceneUpdateFunction() {
	if (_dat_80083148 != _uint16_t_80083154) {
		//TODO FUN_8001ac5c((uint)_dat_80083148, (uint)DAT_80083150, (uint)_uint16_t_80083154, (uint)DAT_80083158);
	}
	if (sceneUpdater.sequenceID != -1) {
		_vm->getINI(sceneUpdater.iniID)->actor->updateSequence(sceneUpdater.sequenceID);
	}
	_vm->clearSceneUpdateFunction();
}

void SpecialOpcodes::setupTableBasedSceneUpdateFunction(uint16 initialCounter, uint16 numSequences,
														uint16 sequenceDuration) {
	sceneUpdater.sequenceID = -1;
	_uint16_t_80083154 = 0;
	_dat_80083148 = 0;
//TODO
//	DAT_80072858 = 0;
	sceneUpdater.curSequenceIndex = 0;
	sceneUpdater.numTotalSequences = numSequences;
	sceneUpdater.curSequence = _vm->getRand(numSequences);
	sceneUpdater.sequenceDuration = sequenceDuration;
	sceneUpdater.counter = initialCounter;

	//TODO

	_vm->setSceneUpdateFunction(tableBasedSceneUpdateFunction);
}

void SpecialOpcodes::spcUnk80FlickerArmorOn() {
	Actor *actor = _vm->_dragonINIResource->getRecord(0x21f)->actor;
	actor->_priorityLayer = 2;
	actor->clearFlag(ACTOR_FLAG_100);
}

void SpecialOpcodes::spcShakeScreenSceneLogic() {
	setSpecialOpCounter(0x1e);
	_vm->setSceneUpdateFunction(shakeScreenUpdateFunction);
}

int16 SpecialOpcodes::getSpecialOpCounter() {
	return _specialOpCounter;
}

void SpecialOpcodes::setSpecialOpCounter(int16 newValue) {
	_specialOpCounter = newValue;
}

void SpecialOpcodes::mapTransition(uint16 mode) {
	const uint16 mapLookupTbl[26] = {
			160,    100,     45,    100,
			105,    170,     14,     87,
			 83,     33,     86,    100,
			 25,    180,    161,    156,
			195,     47,    287,     35,
			292,     80,    202,    182,
			127,     78
	};

	const uint16 mapSceneIdTbl[13] = {
			0,
			0xa, 0x15, 0x1a,
			0x1b, 0x1c, 0x1e,
			0x20, 0x23, 0x25,
			0x2d, 0x30, 0x31
	};

	uint16 targetLocation = _vm->getINI(0x1e)->objectState;
	if (targetLocation == 0) {
		targetLocation = 0xc;
		for (int i = 1; i < 0xd; i++) {
			if (_vm->_scene->_mapTransitionEffectSceneID < mapSceneIdTbl[i]) {
				targetLocation = i - 1;
				break;
			}
		}
	}

	_vm->_cursor->updatePosition(mapLookupTbl[targetLocation * 2], mapLookupTbl[targetLocation * 2 + 1]);
	_vm->setFlags(ENGINE_FLAG_20);

	int16 cursorX = _vm->_cursor->_x;
	int16 cursorY = _vm->_cursor->_y;

	int32 topIncrement = (cursorY << 0x10) >> 4;
	int32 bottomIncrement = (DRAGONS_SCREEN_HEIGHT - cursorY) * 0x10000 >> 4;
	int32 rightIncrement = (DRAGONS_SCREEN_WIDTH - cursorX) * 0x10000 >> 4;

	if (mode == 0) { //Close map
		FlatQuad *topQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0,0,0x140,0,0x140,0,0,0,1,4,0));
		FlatQuad *bottomQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0,200,0x140,200,0x140,200,0,200,1,4,0));
		FlatQuad *leftQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0,0,0,0,0,200,0,200,1,4,0));
		FlatQuad *rightQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0x140,0,0x140,0,0x140,200,0x140,200,1,4,0));
		int32 topY = topQuad->points[3].y << 0x10;
		int32 bottomY = bottomQuad->points[0].y << 0x10;
		int32 rightX = rightQuad->points[0].x << 0x10;
		int32 leftX = leftQuad->points[1].x << 0x10;

		while (topY < (cursorY << 0x10)) {
			topY = topY + topIncrement;
			bottomY = bottomY - bottomIncrement;
			leftX = leftX + cursorX * 0x1000;
			topQuad->points[3].y = topY >> 0x10;
			topQuad->points[2].y = topY >> 0x10;
			bottomQuad->points[0].y = bottomY >> 0x10;
			bottomQuad->points[1].y = bottomY >> 0x10;
			leftQuad->points[1].x = leftX >> 0x10;
			leftQuad->points[3].x = leftX >> 0x10;
			rightX = rightX - rightIncrement;
			rightQuad->points[0].x = rightX >> 0x10;
			rightQuad->points[2].x = rightX >> 0x10;
			_vm->waitForFrames(1);
		}

		_vm->fadeToBlack();
	} else if (mode == 1) { // Open map
		FlatQuad *topQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0,0,0x140,0,0x140,cursorY,0,cursorY,1,4,0));
		FlatQuad *bottomQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0, cursorY, 0x140, cursorY, 0x140, 200, 0, 200, 1, 4, 0));
		FlatQuad *leftQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(0, 0, cursorX, 0, cursorX, 200, 0, 200, 1, 4, 0));
		FlatQuad *rightQuad = _vm->_screen->getFlatQuad(_vm->_screen->addFlatQuad(cursorX,0,0x140,0,0x140,200,cursorX,200,1,4,0));
		int32 topY = topQuad->points[3].y << 0x10;
		int32 bottomY = bottomQuad->points[0].y << 0x10;
		int32 leftX = leftQuad->points[1].x << 0x10;
		int32 rightX = rightQuad->points[0].x << 0x10;
		_vm->waitForFrames(2);
		bool hasDoneFade = false;
		while (0 < topY) {
			topY = topY - topIncrement;
			bottomY = bottomY + bottomIncrement;
			leftX = leftX + cursorX * -0x1000;
			topQuad->points[3].y = topY >> 0x10;
			topQuad->points[2].y = topY >> 0x10;
			bottomQuad->points[0].y = bottomY >> 0x10;
			bottomQuad->points[1].y = bottomY >> 0x10;
			leftQuad->points[1].x = leftX >> 0x10;
			leftQuad->points[3].x = leftX >> 0x10;
			rightX = rightX + rightIncrement;
			rightQuad->points[0].x = rightX >> 0x10;
			rightQuad->points[2].x = rightX >> 0x10;
			_vm->waitForFrames(1);
			if (!hasDoneFade) {
				_vm->fadeFromBlack();
				hasDoneFade = true;
			}
		}
	}
	_vm->_screen->clearAllFlatQuads();
}

void pizzaUpdateFunction() {
	static int16 counter = 0;
	DragonsEngine *vm = getEngine();

	Actor *actorf4 = vm->getINI(0xf4)->actor;
	Actor *actorf5 = vm->getINI(0xf5)->actor;

	if (counter == 0) {
		if (actorf4->isFlagSet(ACTOR_FLAG_4)) {
			if (actorf4->_sequenceID == 0) {
				actorf4->updateSequence(1);
			} else {
				if (actorf4->_sequenceID == 1) {
					actorf4->updateSequence(2);
					actorf5->_x_pos = 0x115;
					actorf5->_y_pos = 0x5c;
					actorf5->updateSequence(7);
					counter = 0x2d;
					return;
				}
				if (actorf4->_sequenceID == 2) {
					if ((actorf5->_sequenceID == 8) &&
						(actorf5->isFlagSet(ACTOR_FLAG_4))) {
						actorf5->_x_pos = -100;
						actorf5->_y_pos = 100;
						actorf4->updateSequence(3);
					} else {
						if (actorf5->_sequenceID == 8) {
							return;
						}
						actorf5->updateSequence(8);
					}
				} else {
					if (actorf4->_sequenceID != 3) {
						return;
					}
					actorf4->updateSequence(0);
				}
			}
		}
	} else {
		counter--;
	}
}

void tableBasedSceneUpdateFunction() {
	uint uVar3;
	DragonsEngine *vm = getEngine();
	SpecialOpcodes *spc = vm->_scriptOpcodes->_specialOpCodes;

	uVar3 = (uint)spc->sceneUpdater.curSequence;
	if (!vm->isFlagSet(ENGINE_FLAG_8000) || vm->_isLoadingDialogAudio) {
		if (spc->sceneUpdater.sequenceID != -1) {
			vm->getINI(spc->sceneUpdater.iniID)->actor->updateSequence(spc->sceneUpdater.sequenceID);
			spc->sceneUpdater.sequenceID = -1;
		}
		if (!vm->_isLoadingDialogAudio) {
			spc->sceneUpdater.counter--;
			if (spc->sceneUpdater.counter == 0) {
				spc->sceneUpdater.sequenceID = spc->sceneUpdater.sequenceIDTbl[uVar3][spc->sceneUpdater.curSequenceIndex]; //*(int16_t *) (sceneUpdateSequenceTbl[uVar3].sequenceIdPtr + (uint) spc->sceneUpdater.curSequenceIndex);
				spc->sceneUpdater.iniID = spc->sceneUpdater.iniIDTbl[uVar3][spc->sceneUpdater.curSequenceIndex] - 1;
//						*(short *) (sceneUpdateSequenceTbl[uVar3].iniIdPtr + (uint) spc->sceneUpdater.curSequenceIndex) -
//						1;
				if (spc->sceneUpdater.sequenceID != -1) {
					Actor *actor = vm->getINI(spc->sceneUpdater.iniID)->actor;
					uint16 originalSequenceID = actor->_sequenceID;
					actor->updateSequence(spc->sceneUpdater.sequenceID);
					spc->sceneUpdater.sequenceID = originalSequenceID;
				}
				vm->_talk->playDialogAudioDontWait(spc->sceneUpdater.textTbl[uVar3][spc->sceneUpdater.curSequenceIndex]);
				spc->sceneUpdater.curSequenceIndex++;
				spc->sceneUpdater.counter = 0x1e;
				if (spc->sceneUpdater.numSteps[uVar3] <= (uint) spc->sceneUpdater.curSequenceIndex) {
					spc->sceneUpdater.curSequenceIndex = 0;
					spc->sceneUpdater.curSequence = vm->getRand((uint) spc->sceneUpdater.numTotalSequences);
					spc->sceneUpdater.counter = spc->sceneUpdater.sequenceDuration;
				}
			}
		}
	}
}

void castleBuildingBlackDragon2UpdateFunction() {
	DragonINI *ini;
	DragonsEngine *vm = getEngine();

	ini = vm->_dragonINIResource->getRecord(0x231);
	if (ini->counter <= 0) {
		if (ini->objectState == 0) {
			ini->actor->updateSequence(0xb);
			ini->counter = 0x68;
			ini->objectState = 1;
		} else if (ini->objectState == 1) {
			vm->_dragonINIResource->getRecord(0x234)->actor->updateSequence(4);
			ini->counter = vm->getRand(0xb4) + 900;
			ini->objectState = 0;
		}
	}
}

void shakeScreenUpdateFunction() {
	static uint8 shakeDirection = 0;
	DragonsEngine *vm = getEngine();
	int16 counter = vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter();

	if (counter == 0) {
		shakeDirection = shakeDirection ^ 1u;
		int16 shakeValue = shakeDirection != 0 ? 1 : -1;
		vm->_screen->setScreenShakeOffset(shakeValue, shakeValue);
	} else {
		counter--;
	}
	vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(counter);
}

void ladyOfTheLakeCapturedUpdateFunction() {
	const uint32 dialogTbl[3] = {
			0x490C8, 0x490FC, 0x4913A
	};
	static int ladyofLakeCountdownTimer = 0x12c;
	static uint8 ladyOfLakeDialogIndex = 0;
	DragonsEngine *vm = getEngine();

	if (!vm->isFlagSet(ENGINE_FLAG_8000)) {
		ladyofLakeCountdownTimer--;
		if (ladyofLakeCountdownTimer == 0) {
			vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(dialogTbl[ladyOfLakeDialogIndex]));
			if (ladyOfLakeDialogIndex == 2) {
				ladyOfLakeDialogIndex = 0;
			} else {
				ladyOfLakeDialogIndex++;
			}
			ladyofLakeCountdownTimer = 0x708;
		}
	}
}

void castleFogUpdateFunction() {
	static int16 castleFogXOffset = 0;
	DragonsEngine *vm = getEngine();
	int16 specialOpCounter = vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter();
	if (specialOpCounter == -1) {
		castleFogXOffset = 0;
		specialOpCounter = 6;
	}
	if (specialOpCounter == 0) {
		castleFogXOffset--;
		if (castleFogXOffset < 0) {
			castleFogXOffset = 0x13f;
		}
		vm->_scene->setLayerOffset(2, Common::Point(castleFogXOffset, 0));
		specialOpCounter = 6;
	} else {
		specialOpCounter--;
	}
	vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(specialOpCounter);
}

void menInMinesSceneUpdateFunction() {
	const uint32 sceneUpdateFuncDialogTbl[4] = {
		0x4590A, 0x45994, 0x459F4, 0x45A60
	};
	DragonsEngine *vm = getEngine();
	uint16 sequenceId;
	Actor *actor = vm->_dragonINIResource->getRecord(0x293)->actor;

	if (!vm->isFlagSet(ENGINE_FLAG_8000)) {
		uint16 specialOpCounter = vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter();
		if (specialOpCounter != 0) {
			if (actor->_sequenceID != 0) {
				actor->updateSequence(0);
			}
			vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(specialOpCounter - 1);
			return;
		}
		vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(vm->getRand(5) * 0x3c + 0x708);
		vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(sceneUpdateFuncDialogTbl[vm->getRand(4)]));
		sequenceId = 2;
	} else {
		if (!vm->_isLoadingDialogAudio) {
			return;
		}
		sequenceId = 0;
	}
	actor->updateSequence(sequenceId);
}

void monksAtBarSceneUpdateFunction() {
	static uint8 monksAtBarCurrentState = 0;
	static const uint32 sceneUpdateFuncDialogTbl[6] = {
		0x37800, 0x37854, 0x378CA,
		0x39152, 0x3919A, 0x3922C
	};
	static const uint32 barKeeperTextIdTbl[10] = {
		0x38C68, 0x38CE2, 0x38D4E, 0x38CE2,
		0x38DC2, 0x38E0C, 0x38C68, 0x38E5C,
		0x38ED0, 0x38CE2
	};
	static const uint32 DAT_800832f0[4] = {0x38F2A, 0x39000, 0x39084, 0x390E8};

	DragonsEngine *vm = getEngine();
	Actor *barKeeper = vm->_dragonINIResource->getRecord(0x1e7)->actor;
	Actor *monk1 = vm->_dragonINIResource->getRecord(0x1ec)->actor;
	Actor *monk2 = vm->_dragonINIResource->getRecord(0x1ed)->actor;
	DragonINI *ini = vm->_dragonINIResource->getRecord(0x1e6);
	bool bVar1;
	short sVar2;
	uint32 textIndex;
	uint16 sequenceId;
	int16 specialOpCounter = vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter();
	if (specialOpCounter == -1) {
		monksAtBarCurrentState = vm->getRand(2) * 2;
		specialOpCounter = 600;
	}
	if (vm->isFlagSet(ENGINE_FLAG_8000)) {
		if (!vm->_isLoadingDialogAudio) {
			return;
		}
		monk1->updateSequence(0);
		monk2->updateSequence(8);
		return;
	}
	bVar1 = ini->sceneId != 0;
	if (specialOpCounter != 0) {
		specialOpCounter = specialOpCounter + -1;
	}
	if (specialOpCounter == 0) {
		switch (monksAtBarCurrentState) {
		case 0:
			if (0x31 < vm->getRand(100)) {
				sVar2 = vm->getRand(3);
				specialOpCounter = (sVar2 + 3) * 0x3c;
				monksAtBarCurrentState = 2;
				break;
			}
			if (bVar1) {
				barKeeper->updateSequence(0xc);
				textIndex = sceneUpdateFuncDialogTbl[vm->getRand(3) + 3];
			} else {
				barKeeper->updateSequence(2);
				textIndex = sceneUpdateFuncDialogTbl[vm->getRand(3)];
			}
			vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(textIndex));
			monksAtBarCurrentState = 1;
			specialOpCounter = 0;
			break;
		case 1:
			if (bVar1) {
				sequenceId = 8;
			} else {
				sequenceId = 0;
			}
			barKeeper->updateSequence(sequenceId);
			specialOpCounter = 0x168;
			monksAtBarCurrentState = 2;
			break;
		case 2: {
			uint16 randTextId = vm->getRand(10);
			if ((randTextId & 1) == 0) {
				monk1->updateSequence(2);
			} else {
				monk2->updateSequence(10);
			}
			vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(barKeeperTextIdTbl[randTextId]));
			monksAtBarCurrentState = 3;
			specialOpCounter = 0;
			break;
		}
		case 3:
			monk1->updateSequence(0);
			monk2->updateSequence(8);
			specialOpCounter = 0x3c;
			monksAtBarCurrentState = 4;
			break;
		case 4:
			if (bVar1) {
				barKeeper->updateSequence(0xc);
				textIndex = sceneUpdateFuncDialogTbl[vm->getRand(3) + 3];
			} else {
				barKeeper->updateSequence(2);
				textIndex = DAT_800832f0[vm->getRand(4)];
			}
			vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(textIndex));
			monksAtBarCurrentState = 5;
			specialOpCounter = 0;
			break;
		case 5:
			if (bVar1) {
				sequenceId = 8;
			} else {
				sequenceId = 0;
			}
			barKeeper->updateSequence(sequenceId);
			specialOpCounter = 0x78;
			if (!bVar1) {
				monksAtBarCurrentState = 0x37;
				break;
			}
			monksAtBarCurrentState = 6;
			break;
		case 6:
			if (bVar1) {
				barKeeper->updateSequence(0xb);
				ini->actor->updateSequence(7);
				monksAtBarCurrentState = 10;
			} else {
				barKeeper->updateSequence(0xd);
				monk1->updateSequence(0x14);
				monksAtBarCurrentState = 0x41;
			}
			specialOpCounter = 0;
			break;
		case 7:
			if (!barKeeper->isFlagSet(ACTOR_FLAG_4)) {
				break;
			}
			barKeeper->updateSequence(0xe);
			monk2->updateSequence(0x15);
			monksAtBarCurrentState = 8;
			specialOpCounter = 0;
			break;
		case 8:
			if (barKeeper->isFlagSet(ACTOR_FLAG_4)) {
				monk2->updateSequence(8);
				barKeeper->updateSequence(0);
				monksAtBarCurrentState = 9;
				specialOpCounter = 300;
			}
			break;
		case 9:
			sequenceId = 0x10;
			if (0x31 < vm->getRand(100)) {
				sequenceId = 0x16;
			}
			monk1->updateSequence(sequenceId);
			sequenceId = 0x11;
			if (0x31 < vm->getRand(100)) {
				sequenceId = 0x17;
			}
			monk2->updateSequence(sequenceId);
			monksAtBarCurrentState = 0;
			sVar2 = vm->getRand(10);
			specialOpCounter = (sVar2 + 10) * 0x3c;
			break;
		case 10:
			if (!barKeeper->isFlagSet(ACTOR_FLAG_4)) {
				break;
			}
			ini->actor->updateSequence(0);
			sequenceId = 8;
			monk2 = barKeeper;
			monk2->updateSequence(sequenceId);
			monksAtBarCurrentState = 0;
			sVar2 = vm->getRand(10);
			specialOpCounter = (sVar2 + 10) * 0x3c;
			break;
		case 0x37:
			barKeeper->updateSequence(0x11);
			specialOpCounter = 0x14;
			monksAtBarCurrentState = 6;
			break;
		case 0x41:
			if (!barKeeper->isFlagSet(ACTOR_FLAG_4)) {
				break;
			}
			barKeeper->updateSequence(0xf);
			monk1->updateSequence(0);
			monksAtBarCurrentState = 7;
			specialOpCounter = 0;
			break;
		}
	}
	vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(specialOpCounter);
	vm->_dragonINIResource->getRecord(0)->objectState = 0;
}

void flameEscapeSceneUpdateFunction() {
	static const uint32 dialogTbl[6] = {
		0x10458, 0x104A0, 0x10500, 0x10500, 0x10550, 0x10578
	};
	static bool DAT_800634c0 = false;
	DragonsEngine *vm = getEngine();
	Actor *flame = vm->_dragonINIResource->getRecord(0x96)->actor;

	if (!vm->isFlagSet(ENGINE_FLAG_8000)) {
		int16 specialOpCounter = vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter();
		if (specialOpCounter == -1) {
			DAT_800634c0 = false;
			specialOpCounter = 300;
		}
		if (specialOpCounter != 0) {
			specialOpCounter = specialOpCounter + -1;
		}
		if (specialOpCounter == 0) {
			if (DAT_800634c0 == 0) {
				flame->updateSequence(0x12);
				vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(dialogTbl[vm->getRand(6)]));
				specialOpCounter = 0;
				DAT_800634c0 = 1;
			} else {
				if (DAT_800634c0 == 1) {
					flame->updateSequence(0x10);
					specialOpCounter = (vm->getRand(0x14) + 10) * 0x3c;
					DAT_800634c0 = 0;
				}
			}
		}
		vm->_scriptOpcodes->_specialOpCodes->setSpecialOpCounter(specialOpCounter);
	} else {
		if (vm->_isLoadingDialogAudio && flame->_sequenceID != 0x10) {
			flame->updateSequence(0x10);
		}
	}
}

void caveOfDilemmaUpdateFunction() {
	static int32 counter = 0;
	static int16 direction = 1;
	int16 yOffset;
	Actor *oldManActor;
	Actor *cloudChairActor;
	DragonsEngine *vm = getEngine();

	if (counter == 0) {
		oldManActor = vm->_dragonINIResource->getRecord(0x161)->actor;
		cloudChairActor = vm->_dragonINIResource->getRecord(0x160)->actor;
		if (oldManActor->_y_pos < 0x53) {
			oldManActor->_y_pos = 0x52;
			cloudChairActor->_y_pos = 0x52;
			direction = 1;
		}
		if (0x5b < oldManActor->_y_pos) {
			oldManActor->_y_pos = 0x5c;
			cloudChairActor->_y_pos = 0x5c;
			direction = -1;
		}
		yOffset = direction * 2;
		oldManActor->_y_pos = oldManActor->_y_pos + yOffset;
		cloudChairActor->_y_pos = cloudChairActor->_y_pos + yOffset;
		counter = 10;
	} else {
		counter--;
	}
}

void moatDrainedSceneUpdateFunction() {
	static const uint32 moatDrainedTextIdTbl[4] = {
		0x3C97A, 0x3C9AC, 0x3C9F8, 0x3CA48
	};
	static uint16 moatDrainedUpdateCounter = 0;
	static bool moatDrainedStatus = false;
	DragonsEngine *vm = getEngine();

	if (vm->_scriptOpcodes->_specialOpCodes->getSpecialOpCounter() == -1) {
		moatDrainedUpdateCounter = 600;
	}
	castleFogUpdateFunction();
	if (((vm->_dragonINIResource->getRecord(0x208)->objectState2 == 2) &&
		 !vm->isFlagSet(ENGINE_FLAG_8000))) {
		if (moatDrainedUpdateCounter != 0) {
			moatDrainedUpdateCounter--;
		}
		if (moatDrainedUpdateCounter <= 0) {
			if (!moatDrainedStatus) {
				vm->_talk->playDialogAudioDontWait(vm->getDialogTextId(moatDrainedTextIdTbl[vm->getRand(4)]));
				moatDrainedStatus = true;
				moatDrainedUpdateCounter = 0x1e;
			} else {
				if (moatDrainedStatus) {
					vm->_dragonINIResource->getRecord(0x1fa)->actor->updateSequence(7);
					moatDrainedUpdateCounter = vm->getRand(300) + 0x4b0;
					moatDrainedStatus = false;
				}
			}
		}
	}
}

} // End of namespace Dragons
