/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 Ivan Dubrov
 * Copyright (C) 2004-2006 The ScummVM project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

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

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-0/engines/gob/mult_v1.cpp $
 * $Id: mult_v1.cpp 23088 2006-06-13 17:02:53Z drmccoy $
 *
 */

#include "common/stdafx.h"
#include "common/endian.h"
#include "common/stream.h"

#include "gob/gob.h"
#include "gob/mult.h"
#include "gob/game.h"
#include "gob/scenery.h"
#include "gob/global.h"
#include "gob/inter.h"
#include "gob/anim.h"
#include "gob/draw.h"
#include "gob/palanim.h"

namespace Gob {

Mult_v1::Mult_v1(GobEngine *vm) : Mult(vm) {
}

void Mult_v1::loadMult(int16 resId) {
	int16 palIndex;
	int16 i, j;

	_sndSlotsCount = 0;
	_frameStart = 0;
	_multData = _vm->_game->loadExtData(resId, 0, 0);
	Common::MemoryReadStream data((byte *) _multData, 4294967295U);

	_staticCount = data.readSByte() + 1;
	_animCount = data.readSByte() + 1;

	for (i = 0; i < _staticCount; i++, data.seek(14, SEEK_CUR)) {
		_staticIndices[i] = _vm->_scenery->loadStatic(1);

		if (_staticIndices[i] >= 100) {
			_staticIndices[i] -= 100;
			_staticLoaded[i] = 1;
		} else {
			_staticLoaded[i] = 0;
		}
	}

	for (i = 0; i < _animCount; i++, data.seek(14, SEEK_CUR)) {
		_animIndices[i] = _vm->_scenery->loadAnim(1);

		if (_animIndices[i] >= 100) {
			_animIndices[i] -= 100;
			_animLoaded[i] = 1;
		} else {
			_animLoaded[i] = 0;
		}
	}

	_frameRate = data.readSint16LE();
	_staticKeysCount = data.readSint16LE();
	_staticKeys = new Mult_StaticKey[_staticKeysCount];
	for (i = 0; i < _staticKeysCount; i++) {
		_staticKeys[i].frame = data.readSint16LE();
		_staticKeys[i].layer = data.readSint16LE();
	}

	for (j = 0; j < 4; j++) {
		_animKeysCount[j] = data.readSint16LE();
		_animKeys[j] = new Mult_AnimKey[_animKeysCount[j]];
		for (i = 0; i < _animKeysCount[j]; i++) {
			_animKeys[j][i].frame = data.readSint16LE();
			_animKeys[j][i].layer = data.readSint16LE();
			_animKeys[j][i].posX = data.readSint16LE();
			_animKeys[j][i].posY = data.readSint16LE();
			_animKeys[j][i].order = data.readSint16LE();
		}
	}

	for (palIndex = 0; palIndex < 5; palIndex++) {
		for (i = 0; i < 16; i++) {
			_fadePal[palIndex][i].red = data.readByte();
			_fadePal[palIndex][i].green = data.readByte();
			_fadePal[palIndex][i].blue = data.readByte();
		}
	}

	_palFadeKeysCount = data.readSint16LE();
	_palFadeKeys = new Mult_PalFadeKey[_palFadeKeysCount];
	for (i = 0; i < _palFadeKeysCount; i++) {
		_palFadeKeys[i].frame = data.readSint16LE();
		_palFadeKeys[i].fade = data.readSint16LE();
		_palFadeKeys[i].palIndex = data.readSint16LE();
		_palFadeKeys[i].flag = data.readSByte();
	}

	_palKeysCount = data.readSint16LE();
	_palKeys = new Mult_PalKey[_palKeysCount];
	for (i = 0; i < _palKeysCount; i++) {
		_palKeys[i].frame = data.readSint16LE();
		_palKeys[i].cmd = data.readSint16LE();
		_palKeys[i].rates[0] = data.readSint16LE();
		_palKeys[i].rates[1] = data.readSint16LE();
		_palKeys[i].rates[2] = data.readSint16LE();
		_palKeys[i].rates[3] = data.readSint16LE();
		_palKeys[i].unknown0 = data.readSint16LE();
		_palKeys[i].unknown1 = data.readSint16LE();
		data.read(_palKeys[i].subst, 64);
	}

	_textKeysCount = data.readSint16LE();
	_textKeys = new Mult_TextKey[_textKeysCount];
	for (i = 0; i < _textKeysCount; i++) {
		_textKeys[i].frame = data.readSint16LE();
		_textKeys[i].cmd = data.readSint16LE();
		for (int k = 0; k < 9; ++k)
			_textKeys[i].unknown0[k] = data.readSint16LE();
		_textKeys[i].index = data.readSint16LE();
		_textKeys[i].unknown1[0] = data.readSint16LE();
		_textKeys[i].unknown1[1] = data.readSint16LE();
	}

	_sndKeysCount = data.readSint16LE();
	_sndKeys = new Mult_SndKey[_sndKeysCount];
	for (i = 0; i < _sndKeysCount; i++) {
		_sndKeys[i].frame = data.readSint16LE();
		_sndKeys[i].cmd = data.readSint16LE();
		_sndKeys[i].freq = data.readSint16LE();
		_sndKeys[i].channel = data.readSint16LE();
		_sndKeys[i].repCount = data.readSint16LE();
		_sndKeys[i].soundIndex = -1;
		_sndKeys[i].resId = -1;
		data.seek(26, SEEK_CUR);
		switch (_sndKeys[i].cmd) {
		case 1:
		case 4:
			_sndKeys[i].resId = READ_LE_UINT16(_vm->_global->_inter_execPtr);

			for (j = 0; j < i; j++) {
				if (_sndKeys[i].resId ==
				    _sndKeys[j].resId) {
					_sndKeys[i].soundIndex =
					    _sndKeys[j].soundIndex;
					_vm->_global->_inter_execPtr += 2;
					break;
				}
			}
			if (i == j) {
				_vm->_inter->loadSound(19 - _sndSlotsCount);
				_sndKeys[i].soundIndex =
				    19 - _sndSlotsCount;
				_sndSlotsCount++;
			}
			break;

		case 3:
			_vm->_global->_inter_execPtr += 6;
			break;

		case 5:
			_vm->_global->_inter_execPtr += _sndKeys[i].freq * 2;
			break;
		}
	}
}

void Mult_v1::setMultData(uint16 multindex) {
	error("Switching mults not supported for Gob1");
}

void Mult_v1::multSub(uint16 multindex) {
	error("Switching mults not supported for Gob1");
}

void Mult_v1::playMult(int16 startFrame, int16 endFrame, char checkEscape,
	    char handleMouse) {
	char stopNoClear;
	char stop;
	Mult_Object *multObj;
	Mult_AnimData *animData;

	if (_multData == 0)
		return;

	stopNoClear = 0;
	_frame = startFrame;
	if (endFrame == -1)
		endFrame = 32767;

	if (_frame == -1) {
		_doPalSubst = 0;
		_palFadingRed = 0;
		_palFadingGreen = 0;
		_palFadingBlue = 0;

		_oldPalette = _vm->_global->_pPaletteDesc->vgaPal;
		memcpy((char *)_palAnimPalette, (char *)_vm->_global->_pPaletteDesc->vgaPal, 768);

		if (_vm->_anim->_animSurf == 0) {
			_vm->_util->setFrameRate(_frameRate);
			_vm->_anim->_areaTop = 0;
			_vm->_anim->_areaLeft = 0;
			_vm->_anim->_areaWidth = 320;
			_vm->_anim->_areaHeight = 200;
			_objCount = 4;

			_objects = new Mult_Object[_objCount];
			memset(_objects, 0, _objCount * sizeof(Mult_Object));
			_renderData = new int16[9 * _objCount];
			memset(_renderData, 0, _objCount * 9 * sizeof(int16));

			_animArrayX = new int32[_objCount];
			_animArrayY = new int32[_objCount];

			_animArrayData = new Mult_AnimData[_objCount];
			memset(_animArrayData, 0, _objCount * sizeof(Mult_AnimData));

			for (_counter = 0; _counter < _objCount; _counter++) {
				multObj = &_objects[_counter];

				multObj->pPosX = (int32 *)&_animArrayX[_counter];
				multObj->pPosY = (int32 *)&_animArrayY[_counter];

				multObj->pAnimData = &_animArrayData[_counter];

				animData = multObj->pAnimData;
				animData->isStatic = 1;

				multObj->tick = 0;
				multObj->lastLeft = -1;
				multObj->lastTop = -1;
				multObj->lastRight = -1;
				multObj->lastBottom = -1;
			}

			_vm->_anim->_animSurf =
			    _vm->_video->initSurfDesc(_vm->_global->_videoMode, 320, 200, 0);
			_vm->_draw->_spritesArray[22] = _vm->_anim->_animSurf;

			_vm->_video->drawSprite(_vm->_draw->_backSurface, _vm->_anim->_animSurf,
			    0, 0, 319, 199, 0, 0, 0);

			_animDataAllocated = 1;
		} else
			_animDataAllocated = 0;
		_frame = 0;
	}

	do {
		stop = 1;

		if (VAR(58) == 0) {
			stop = drawStatics(stop);
			stop = drawAnims(stop);
		}

		animate();
		if (handleMouse) {
			_vm->_draw->animateCursor(-1);
		} else {
			_vm->_draw->blitInvalidated();
		}

		if (VAR(58) == 0) {
			drawText(&stop, &stopNoClear);
		}

		stop = prepPalAnim(stop);
		doPalAnim();

		stop = doFadeAnim(stop);
		stop = doSoundAnim(stop, _frame);

		if (_frame >= endFrame)
			stopNoClear = 1;

		if (_vm->_snd->_playingSound)
			stop = 0;

		_vm->_util->processInput();
		if (checkEscape && _vm->_util->checkKey() == 0x11b)	// Esc
			stop = 1;

		_frame++;
		_vm->_util->waitEndFrame();
	} while (stop == 0 && stopNoClear == 0 && !_vm->_quitRequested);

	if (stopNoClear == 0) {
		if (_animDataAllocated) {
			delete[] _objects;
			_objects = 0;

			delete[] _renderData;
			_renderData = 0;

			delete[] _animArrayX;
			_animArrayX = 0;

			delete[] _animArrayY;
			_animArrayY = 0;

			delete[] _animArrayData;
			_animArrayData = 0;

			if (_vm->_anim->_animSurf)
				_vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
			_vm->_anim->_animSurf = 0;

			_animDataAllocated = 0;
		}

		if (_vm->_snd->_playingSound != 0)
			_vm->_snd->stopSound(10);

		WRITE_VAR(57, (uint32)-1);
	} else {
		WRITE_VAR(57, _frame - 1 - _frameStart);
	}
}

char Mult_v1::drawStatics(char stop) {
	if (_staticKeys[_staticKeysCount - 1].frame > _frame)
		stop = 0;

	for (_counter = 0; _counter < _staticKeysCount;
	    _counter++) {
		if (_staticKeys[_counter].frame != _frame
		    || _staticKeys[_counter].layer == -1)
			continue;

		for (_vm->_scenery->_curStatic = 0, _vm->_scenery->_curStaticLayer = _staticKeys[_counter].layer;
			 _vm->_scenery->_curStaticLayer >= _vm->_scenery->_statics[_staticIndices[_vm->_scenery->_curStatic]].layersCount;
			 _vm->_scenery->_curStatic++) {
			_vm->_scenery->_curStaticLayer -=
			    _vm->_scenery->_statics[_staticIndices[_vm->_scenery->_curStatic]].layersCount;
		}

		_vm->_scenery->_curStatic = _staticIndices[_vm->_scenery->_curStatic];
		_vm->_scenery->renderStatic(_vm->_scenery->_curStatic, _vm->_scenery->_curStaticLayer);
		_vm->_video->drawSprite(_vm->_draw->_backSurface, _vm->_anim->_animSurf,
		    0, 0, 319, 199, 0, 0, 0);
	}
	return stop;
}

char Mult_v1::drawAnims(char stop) {
	Mult_AnimKey *key;
	Mult_Object *animObj;
	int16 i;
	int16 count;

	for (_index = 0; _index < 4; _index++) {
		for (_counter = 0; _counter < _animKeysCount[_index]; _counter++) {
			key = &_animKeys[_index][_counter];
			animObj = &_objects[_index];
			if (key->frame != _frame)
				continue;

			if (key->layer != -1) {
				(*animObj->pPosX) = key->posX;
				(*animObj->pPosY) = key->posY;

				animObj->pAnimData->frame = 0;
				animObj->pAnimData->order = key->order;
				animObj->pAnimData->animType = 1;

				animObj->pAnimData->isPaused = 0;
				animObj->pAnimData->isStatic = 0;
				animObj->pAnimData->maxTick = 0;
				animObj->tick = 0;
				animObj->pAnimData->layer = key->layer;

				count = _vm->_scenery->_animations[_animIndices[0]].layersCount;
				i = 0;
				while (animObj->pAnimData->layer >= count) {
					animObj->pAnimData->layer -= count;
					i++;

					count = _vm->_scenery->_animations[_animIndices[i]].layersCount;
				}
				animObj->pAnimData->animation = _animIndices[i];
			} else {
				animObj->pAnimData->isStatic = 1;
			}
		}
	}
	return stop;
}

void Mult_v1::drawText(char *pStop, char *pStopNoClear) {
	char *savedIP;

	int16 cmd;
	for (_index = 0; _index < _textKeysCount; _index++) {
		if (_textKeys[_index].frame != _frame)
			continue;

		cmd = _textKeys[_index].cmd;
		if (cmd == 0) {
			*pStop = 0;
		} else if (cmd == 1) {
			*pStopNoClear = 1;
			_frameStart = 0;
		} else if (cmd == 3) {
			*pStop = 0;
			savedIP = _vm->_global->_inter_execPtr;
			_vm->_global->_inter_execPtr = (char *)(&_textKeys[_index].index);
			_vm->_global->_inter_execPtr = savedIP;
		}
	}
}

char Mult_v1::prepPalAnim(char stop) {
	_palKeyIndex = -1;
	do {
		_palKeyIndex++;
		if (_palKeyIndex >= _palKeysCount)
			return stop;
	} while (_palKeys[_palKeyIndex].frame != _frame);

	if (_palKeys[_palKeyIndex].cmd == -1) {
		stop = 0;
		_doPalSubst = 0;
		_vm->_global->_pPaletteDesc->vgaPal = _oldPalette;

		memcpy((char *)_palAnimPalette, (char *)_vm->_global->_pPaletteDesc->vgaPal, 768);

		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
	} else {
		stop = 0;
		_doPalSubst = 1;
		_palAnimKey = _palKeyIndex;

		_palAnimIndices[0] = 0;
		_palAnimIndices[1] = 0;
		_palAnimIndices[2] = 0;
		_palAnimIndices[3] = 0;

		_vm->_global->_pPaletteDesc->vgaPal = _palAnimPalette;
	}
	return stop;
}

void Mult_v1::doPalAnim(void) {
	int16 off;
	int16 off2;
	Video::Color *palPtr;
	Mult_PalKey *palKey;

	if (_doPalSubst == 0)
		return;

	for (_index = 0; _index < 4; _index++) {
		palKey = &_palKeys[_palAnimKey];

		if ((_frame % palKey->rates[_index]) != 0)
			continue;

		_palAnimRed[_index] =
		    _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].red;
		_palAnimGreen[_index] =
		    _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].green;
		_palAnimBlue[_index] =
		    _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].blue;

		while (1) {
			off = palKey->subst[(_palAnimIndices[_index] + 1) % 16][_index];
			if (off == 0) {
				off = palKey->subst[_palAnimIndices[_index]][_index] - 1;

				_vm->_global->_pPaletteDesc->vgaPal[off].red = _palAnimRed[_index];
				_vm->_global->_pPaletteDesc->vgaPal[off].green = _palAnimGreen[_index];
				_vm->_global->_pPaletteDesc->vgaPal[off].blue = _palAnimBlue[_index];
			} else {
				off = palKey->subst[(_palAnimIndices[_index] + 1) % 16][_index] - 1;
				off2 = palKey->subst[_palAnimIndices[_index]][_index] - 1;

				_vm->_global->_pPaletteDesc->vgaPal[off2].red = _vm->_global->_pPaletteDesc->vgaPal[off].red;
				_vm->_global->_pPaletteDesc->vgaPal[off2].green = _vm->_global->_pPaletteDesc->vgaPal[off].green;
				_vm->_global->_pPaletteDesc->vgaPal[off2].blue = _vm->_global->_pPaletteDesc->vgaPal[off].blue;
			}

			_palAnimIndices[_index] = (_palAnimIndices[_index] + 1) % 16;

			off = palKey->subst[_palAnimIndices[_index]][_index];

			if (off == 0) {
				_palAnimIndices[_index] = 0;
				off = palKey->subst[0][_index] - 1;

				_palAnimRed[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].red;
				_palAnimGreen[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].green;
				_palAnimBlue[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].blue;
			}
			if (_palAnimIndices[_index] == 0)
				break;
		}
	}

	if (_vm->_global->_colorCount == 256) {
		_vm->_video->waitRetrace(_vm->_global->_videoMode);

		palPtr = _vm->_global->_pPaletteDesc->vgaPal;
		for (_counter = 0; _counter < 16; _counter++) {
			_vm->_video->setPalElem(_counter, palPtr->red, palPtr->green, palPtr->blue, 0, 0x13);
			palPtr++;
		}

		palPtr = _vm->_global->_pPaletteDesc->vgaPal;
		for (_counter = 0; _counter < 16; _counter++) {
			_vm->_global->_redPalette[_counter] = palPtr->red;
			_vm->_global->_greenPalette[_counter] = palPtr->green;
			_vm->_global->_bluePalette[_counter] = palPtr->blue;
			palPtr++;
		}
	} else {
		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
	}
}

char Mult_v1::doFadeAnim(char stop) {
	Mult_PalFadeKey *fadeKey;

	for (_index = 0; _index < _palFadeKeysCount; _index++) {
		fadeKey = &_palFadeKeys[_index];

		if (fadeKey->frame != _frame)
			continue;

		stop = 0;
		if ((fadeKey->flag & 1) == 0) {
			if (fadeKey->fade == 0) {
				_vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
				_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
			} else {
				_vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
				_vm->_palanim->fade(_vm->_global->_pPaletteDesc, fadeKey->fade, 0);
			}
		} else {
			_vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
			_vm->_palanim->fade(_vm->_global->_pPaletteDesc, fadeKey->fade, -1);

			_palFadingRed = (fadeKey->flag >> 1) & 1;
			_palFadingGreen = (fadeKey->flag >> 2) & 1;
			_palFadingBlue = (fadeKey->flag >> 3) & 1;
		}
	}

	if (_palFadingRed) {
		_palFadingRed = !_vm->_palanim->fadeStep(1);
		stop = 0;
	}
	if (_palFadingGreen) {
		_palFadingGreen = !_vm->_palanim->fadeStep(2);
		stop = 0;
	}
	if (_palFadingBlue) {
		_palFadingBlue = !_vm->_palanim->fadeStep(3);
		stop = 0;
	}
	return stop;
}

char Mult_v1::doSoundAnim(char stop, int16 frame) {
	Mult_SndKey *sndKey;
	for (_index = 0; _index < _sndKeysCount; _index++) {
		sndKey = &_sndKeys[_index];
		if (sndKey->frame != frame)
			continue;

		if (sndKey->cmd != -1) {
			if (sndKey->cmd == 1) {
				_vm->_snd->stopSound(0);
				stop = 0;
				playSound(_vm->_game->_soundSamples[sndKey->soundIndex], sndKey->repCount,
				    sndKey->freq, sndKey->channel);

			} else if (sndKey->cmd == 4) {
				_vm->_snd->stopSound(0);
				stop = 0;
				playSound(_vm->_game->_soundSamples[sndKey->soundIndex], sndKey->repCount,
				    sndKey->freq, sndKey->channel);
			}
		} else {
			if (_vm->_snd->_playingSound)
				_vm->_snd->stopSound(sndKey->channel);
		}
	}
	return stop;
}

void Mult_v1::animate(void) {
	int16 minOrder;
	int16 maxOrder;
	int16 *pCurLefts;
	int16 *pCurRights;
	int16 *pCurTops;
	int16 *pCurBottoms;
	int16 *pDirtyLefts;
	int16 *pDirtyRights;
	int16 *pDirtyTops;
	int16 *pDirtyBottoms;
	int16 *pNeedRedraw;
	Mult_AnimData *pAnimData;
	int16 i, j;
	int16 order;

	if (_renderData == 0)
		return;

	pDirtyLefts = _renderData;
	pDirtyRights = pDirtyLefts + _objCount;
	pDirtyTops = pDirtyRights + _objCount;
	pDirtyBottoms = pDirtyTops + _objCount;
	pNeedRedraw = pDirtyBottoms + _objCount;
	pCurLefts = pNeedRedraw + _objCount;
	pCurRights = pCurLefts + _objCount;
	pCurTops = pCurRights + _objCount;
	pCurBottoms = pCurTops + _objCount;
	minOrder = 100;
	maxOrder = 0;

	//Find dirty areas
	for (i = 0; i < _objCount; i++) {
		pNeedRedraw[i] = 0;
		pDirtyTops[i] = 1000;
		pDirtyLefts[i] = 1000;
		pDirtyBottoms[i] = 1000;
		pDirtyRights[i] = 1000;
		pAnimData = _objects[i].pAnimData;

		if (pAnimData->isStatic == 0 && pAnimData->isPaused == 0 &&
		    _objects[i].tick == pAnimData->maxTick) {
			if (pAnimData->order < minOrder)
				minOrder = pAnimData->order;

			if (pAnimData->order > maxOrder)
				maxOrder = pAnimData->order;

			pNeedRedraw[i] = 1;
			_vm->_scenery->updateAnim(pAnimData->layer, pAnimData->frame,
			    pAnimData->animation, 0,
			    *(_objects[i].pPosX), *(_objects[i].pPosY),
			    0);

			if (_objects[i].lastLeft != -1) {
				pDirtyLefts[i] =
				    MIN(_objects[i].lastLeft,
				    _vm->_scenery->_toRedrawLeft);
				pDirtyTops[i] =
				    MIN(_objects[i].lastTop,
				    _vm->_scenery->_toRedrawTop);
				pDirtyRights[i] =
				    MAX(_objects[i].lastRight,
				    _vm->_scenery->_toRedrawRight);
				pDirtyBottoms[i] =
				    MAX(_objects[i].lastBottom,
				    _vm->_scenery->_toRedrawBottom);
			} else {
				pDirtyLefts[i] = _vm->_scenery->_toRedrawLeft;
				pDirtyTops[i] = _vm->_scenery->_toRedrawTop;
				pDirtyRights[i] = _vm->_scenery->_toRedrawRight;
				pDirtyBottoms[i] = _vm->_scenery->_toRedrawBottom;
			}
			pCurLefts[i] = _vm->_scenery->_toRedrawLeft;
			pCurRights[i] = _vm->_scenery->_toRedrawRight;
			pCurTops[i] = _vm->_scenery->_toRedrawTop;
			pCurBottoms[i] = _vm->_scenery->_toRedrawBottom;
		} else {
			if (_objects[i].lastLeft != -1) {
				if (pAnimData->order < minOrder)
					minOrder = pAnimData->order;

				if (pAnimData->order > maxOrder)
					maxOrder = pAnimData->order;

				if (pAnimData->isStatic)
					*pNeedRedraw = 1;

				pCurLefts[i] = _objects[i].lastLeft;
				pDirtyLefts[i] = _objects[i].lastLeft;

				pCurTops[i] = _objects[i].lastTop;
				pDirtyTops[i] = _objects[i].lastTop;

				pCurRights[i] = _objects[i].lastRight;
				pDirtyRights[i] = _objects[i].lastRight;

				pCurBottoms[i] = _objects[i].lastBottom;
				pDirtyBottoms[i] = _objects[i].lastBottom;
			}
		}
	}

	// Find intersections
	for (i = 0; i < _objCount; i++) {
		pAnimData = _objects[i].pAnimData;
		pAnimData->intersected = 200;

		if (pAnimData->isStatic)
			continue;

		for (j = 0; j < _objCount; j++) {
			if (i == j)
				continue;

			if (_objects[j].pAnimData->isStatic)
				continue;

			if (pCurRights[i] < pCurLefts[j])
				continue;

			if (pCurRights[j] < pCurLefts[i])
				continue;

			if (pCurBottoms[i] < pCurTops[j])
				continue;

			if (pCurBottoms[j] < pCurTops[i])
				continue;

			pAnimData->intersected = j;
			break;
		}
	}

	// Restore dirty areas
	for (i = 0; i < _objCount; i++) {

		if (pNeedRedraw[i] == 0 || _objects[i].lastLeft == -1)
			continue;

		_vm->_draw->_sourceSurface = 22;
		_vm->_draw->_destSurface = 21;
		_vm->_draw->_spriteLeft = pDirtyLefts[i] - _vm->_anim->_areaLeft;
		_vm->_draw->_spriteTop = pDirtyTops[i] - _vm->_anim->_areaTop;
		_vm->_draw->_spriteRight = pDirtyRights[i] - pDirtyLefts[i] + 1;
		_vm->_draw->_spriteBottom = pDirtyBottoms[i] - pDirtyTops[i] + 1;
		_vm->_draw->_destSpriteX = pDirtyLefts[i];
		_vm->_draw->_destSpriteY = pDirtyTops[i];
		_vm->_draw->_transparency = 0;
		_vm->_draw->spriteOperation(DRAW_BLITSURF);
		_objects[i].lastLeft = -1;
	}

	// Update view
	for (order = minOrder; order <= maxOrder; order++) {
		for (i = 0; i < _objCount; i++) {
			pAnimData = _objects[i].pAnimData;
			if (pAnimData->order != order)
				continue;

			if (pNeedRedraw[i]) {
				if (pAnimData->isStatic == 0) {

					_vm->_scenery->updateAnim(pAnimData->layer,
					    pAnimData->frame,
					    pAnimData->animation, 2,
					    *(_objects[i].pPosX),
					    *(_objects[i].pPosY), 1);

					if (_vm->_scenery->_toRedrawLeft != -12345) {
						_objects[i].lastLeft =
						    _vm->_scenery->_toRedrawLeft;
						_objects[i].lastTop =
						    _vm->_scenery->_toRedrawTop;
						_objects[i].lastRight =
						    _vm->_scenery->_toRedrawRight;
						_objects[i].lastBottom =
						    _vm->_scenery->_toRedrawBottom;
					} else {
						_objects[i].lastLeft = -1;
					}
				}
				_vm->_scenery->updateStatic(order + 1);
			} else if (pAnimData->isStatic == 0) {
				for (j = 0; j < _objCount; j++) {
					if (pNeedRedraw[j] == 0)
						continue;

					if (pDirtyRights[i] < pDirtyLefts[j])
						continue;

					if (pDirtyRights[j] < pDirtyLefts[i])
						continue;

					if (pDirtyBottoms[i] < pDirtyTops[j])
						continue;

					if (pDirtyBottoms[j] < pDirtyTops[i])
						continue;

					_vm->_scenery->_toRedrawLeft = pDirtyLefts[j];
					_vm->_scenery->_toRedrawRight = pDirtyRights[j];
					_vm->_scenery->_toRedrawTop = pDirtyTops[j];
					_vm->_scenery->_toRedrawBottom = pDirtyBottoms[j];

					_vm->_scenery->updateAnim(pAnimData->layer,
					    pAnimData->frame,
					    pAnimData->animation, 4,
					    *(_objects[i].pPosX),
					    *(_objects[i].pPosY), 1);

					_vm->_scenery->updateStatic(order + 1);
				}
			}
		}
	}

	// Advance animations
	for (i = 0; i < _objCount; i++) {
		pAnimData = _objects[i].pAnimData;
		if (pAnimData->isStatic || pAnimData->isPaused)
			continue;

		if (_objects[i].tick == pAnimData->maxTick) {
			_objects[i].tick = 0;
			if (pAnimData->animType == 4) {
				pAnimData->isPaused = 1;
				pAnimData->frame = 0;
			} else {
				pAnimData->frame++;
				if (pAnimData->frame >=
				    _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer].framesCount) {
					switch (pAnimData->animType) {
					case 0:
						pAnimData->frame = 0;
						break;

					case 1:
						pAnimData->frame = 0;

						*(_objects[i].pPosX) =
						    *(_objects[i].pPosX) +
						    _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer].animDeltaX;

						*(_objects[i].pPosY) =
						    *(_objects[i].pPosY) +
						    _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer].animDeltaY;
						break;

					case 2:
						pAnimData->frame = 0;
						pAnimData->animation =
						    pAnimData->newAnimation;
						pAnimData->layer =
						    pAnimData->newLayer;
						break;

					case 3:
						pAnimData->animType = 4;
						pAnimData->frame = 0;
						break;

					case 5:
						pAnimData->isStatic = 1;
						pAnimData->frame = 0;
						break;

					case 6:
						pAnimData->frame--;
						pAnimData->isPaused = 1;
						break;
					}
					pAnimData->newCycle = 1;
				} else {
					pAnimData->newCycle = 0;
				}
			}
		} else {
			_objects[i].tick++;
		}
	}
}

void Mult_v1::freeMult(void) {
	if (_vm->_anim->_animSurf != 0)
		_vm->_video->freeSurfDesc(_vm->_anim->_animSurf);

	delete[] _objects;
	delete[] _renderData;
	delete[] _orderArray;

	_objects = 0;
	_renderData = 0;
	_orderArray = 0;
	_vm->_anim->_animSurf = 0;
}

void Mult_v1::playSound(Snd::SoundDesc * soundDesc, int16 repCount, int16 freq,
	    int16 channel) {
	_vm->_snd->playSample(soundDesc, repCount, freq);
}

void Mult_v1::freeMultKeys(void) {
	int i;

	delete[] _multData;

	for (i = 0; i < _staticCount; i++) {

		if (_staticLoaded[i] != 0)
			_vm->_scenery->freeStatic(_staticIndices[i]);
	}

	for (i = 0; i < _animCount; i++) {
		if (_animLoaded[i] != 0)
			_vm->_scenery->freeAnim(_animIndices[i]);
	}

	delete[] _staticKeys;

	for (i = 0; i < 4; i++)
		delete[] _animKeys[i];

	delete[] _palFadeKeys;
	delete[] _palKeys;
	delete[] _textKeys;

	for (i = 0; i < _sndSlotsCount; i++) {
		_vm->_game->freeSoundSlot(19 - i);
	}

	delete[] _sndKeys;

	_multData = 0;

	if (_animDataAllocated != 0) {
		delete[] _objects;
		_objects = 0;

		delete[] _renderData;
		_renderData = 0;

		delete[] _animArrayX;
		_animArrayX = 0;

		delete[] _animArrayY;
		_animArrayY = 0;

		delete[] _animArrayData;
		_animArrayData = 0;

		if (_vm->_anim->_animSurf)
			_vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
		_vm->_anim->_animSurf = 0;

		_animDataAllocated = 0;
	}
}

} // End of namespace Gob
