/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/endian.h" #include "common/stream.h" #include "gob/gob.h" #include "gob/mult.h" #include "gob/global.h" #include "gob/util.h" #include "gob/draw.h" #include "gob/game.h" #include "gob/script.h" #include "gob/resources.h" #include "gob/inter.h" #include "gob/scenery.h" namespace Gob { Mult_v1::Mult_v1(GobEngine *vm) : Mult(vm) { } void Mult_v1::loadMult(int16 resId) { debugC(4, kDebugGameFlow, "Loading mult"); _multData = new Mult_Data; memset(_multData, 0, sizeof(Mult_Data)); _multData->sndSlotsCount = 0; _multData->frameStart = 0; Resource *resource = _vm->_game->_resources->getResource(resId); if (!resource) return; Common::SeekableReadStream &data = *resource->stream(); _multData->staticCount = data.readSByte() + 1; _multData->animCount = data.readSByte() + 1; for (int i = 0; i < 10; i++) { _multData->staticLoaded[i] = false; _multData->animLoaded[i] = false; } for (int i = 0; i < _multData->staticCount; i++, data.seek(14, SEEK_CUR)) { _multData->staticIndices[i] = _vm->_scenery->loadStatic(1); if (_multData->staticIndices[i] >= 100) { _multData->staticIndices[i] -= 100; _multData->staticLoaded[i] = true; } } for (int i = 0; i < _multData->animCount; i++, data.seek(14, SEEK_CUR)) { _multData->animIndices[i] = _vm->_scenery->loadAnim(1); if (_multData->animIndices[i] >= 100) { _multData->animIndices[i] -= 100; _multData->animLoaded[i] = true; } } _multData->frameRate = data.readSint16LE(); _multData->staticKeysCount = data.readSint16LE(); _multData->staticKeys = new Mult_StaticKey[_multData->staticKeysCount]; for (int i = 0; i < _multData->staticKeysCount; i++) { _multData->staticKeys[i].frame = data.readSint16LE(); _multData->staticKeys[i].layer = data.readSint16LE(); } for (int i = 0; i < 4; i++) { _multData->animKeysCount[i] = data.readSint16LE(); _multData->animKeys[i] = new Mult_AnimKey[_multData->animKeysCount[i]]; for (int j = 0; j < _multData->animKeysCount[i]; j++) { _multData->animKeys[i][j].frame = data.readSint16LE(); _multData->animKeys[i][j].layer = data.readSint16LE(); _multData->animKeys[i][j].posX = data.readSint16LE(); _multData->animKeys[i][j].posY = data.readSint16LE(); _multData->animKeys[i][j].order = data.readSint16LE(); } } for (int i = 0; i < 5; i++) { for (int j = 0; j < 16; j++) { _multData->fadePal[i][j].red = data.readByte(); _multData->fadePal[i][j].green = data.readByte(); _multData->fadePal[i][j].blue = data.readByte(); } } _multData->palFadeKeysCount = data.readSint16LE(); _multData->palFadeKeys = new Mult_PalFadeKey[_multData->palFadeKeysCount]; for (int i = 0; i < _multData->palFadeKeysCount; i++) { _multData->palFadeKeys[i].frame = data.readSint16LE(); _multData->palFadeKeys[i].fade = data.readSint16LE(); _multData->palFadeKeys[i].palIndex = data.readSint16LE(); _multData->palFadeKeys[i].flag = data.readSByte(); } _multData->palKeysCount = data.readSint16LE(); _multData->palKeys = new Mult_PalKey[_multData->palKeysCount]; for (int i = 0; i < _multData->palKeysCount; i++) { _multData->palKeys[i].frame = data.readSint16LE(); _multData->palKeys[i].cmd = data.readSint16LE(); _multData->palKeys[i].rates[0] = data.readSint16LE(); _multData->palKeys[i].rates[1] = data.readSint16LE(); _multData->palKeys[i].rates[2] = data.readSint16LE(); _multData->palKeys[i].rates[3] = data.readSint16LE(); _multData->palKeys[i].unknown0 = data.readSint16LE(); _multData->palKeys[i].unknown1 = data.readSint16LE(); data.read(_multData->palKeys[i].subst, 64); } _multData->textKeysCount = data.readSint16LE(); _multData->textKeys = new Mult_TextKey[_multData->textKeysCount]; for (int i = 0; i < _multData->textKeysCount; i++) { _multData->textKeys[i].frame = data.readSint16LE(); _multData->textKeys[i].cmd = data.readSint16LE(); data.read(_multData->textKeys[i].unknown, 18); data.read(_multData->textKeys[i].script, 6); } _multData->sndKeysCount = data.readSint16LE(); _multData->sndKeys = new Mult_SndKey[_multData->sndKeysCount]; for (int i = 0; i < _multData->sndKeysCount; i++) { int j; _multData->sndKeys[i].frame = data.readSint16LE(); _multData->sndKeys[i].cmd = data.readSint16LE(); _multData->sndKeys[i].freq = data.readSint16LE(); _multData->sndKeys[i].fadeLength = data.readSint16LE(); _multData->sndKeys[i].repCount = data.readSint16LE(); _multData->sndKeys[i].soundIndex = -1; _multData->sndKeys[i].resId = -1; data.seek(26, SEEK_CUR); switch (_multData->sndKeys[i].cmd) { case 1: case 4: _multData->sndKeys[i].resId = _vm->_game->_script->peekUint16(); for (j = 0; j < i; j++) { if (_multData->sndKeys[i].resId == _multData->sndKeys[j].resId) { _multData->sndKeys[i].soundIndex = _multData->sndKeys[j].soundIndex; _vm->_game->_script->skip(2); break; } } if (i == j) { _vm->_inter->loadSound(19 - _multData->sndSlotsCount); _multData->sndKeys[i].soundIndex = 19 - _multData->sndSlotsCount; _multData->sndSlotsCount++; } break; case 3: _vm->_game->_script->skip(6); break; case 5: _vm->_game->_script->skip(_multData->sndKeys[i].freq * 2); break; } } delete resource; } void Mult_v1::freeMultKeys() { for (int i = 0; i < _multData->staticCount; i++) if (_multData->staticLoaded[i]) _vm->_scenery->freeStatic(_multData->staticIndices[i]); for (int i = 0; i < _multData->animCount; i++) if (_multData->animLoaded[i]) _vm->_scenery->freeAnim(_multData->animIndices[i]); delete[] _multData->staticKeys; for (int i = 0; i < 4; i++) delete[] _multData->animKeys[i]; delete[] _multData->palFadeKeys; delete[] _multData->palKeys; delete[] _multData->textKeys; for (int i = 0; i < _multData->sndSlotsCount; i++) _vm->_game->freeSoundSlot(19 - i); delete[] _multData->sndKeys; if (_animDataAllocated) { clearObjectVideos(); if (_objects) for (int i = 0; i < _objCount; i++) { delete _objects[i].pPosX; delete _objects[i].pPosY; } delete[] _objects; delete[] _renderData; delete _animArrayX; delete _animArrayY; delete[] _animArrayData; _objects = 0; _renderData = 0; _animArrayX = 0; _animArrayY = 0; _animArrayData = 0; _animSurf.reset(); _vm->_draw->freeSprite(Draw::kAnimSurface); _animDataAllocated = false; } delete _multData; _multData = 0; } bool Mult_v1::hasMultData(uint16 multIndex) { error("Switching mults not supported for Gob1"); } void Mult_v1::setMultData(uint16 multIndex) { error("Switching mults not supported for Gob1"); } void Mult_v1::zeroMultData(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::playMultInit() { _doPalSubst = false; _palFadingRed = 0; _palFadingGreen = 0; _palFadingBlue = 0; _oldPalette = _vm->_global->_pPaletteDesc->vgaPal; if (!_animSurf) { if (_objects) for (int i = 0; i < _objCount; i++) { delete _objects[i].pPosX; delete _objects[i].pPosY; } delete[] _objects; _vm->_util->setFrameRate(_multData->frameRate); _animTop = 0; _animLeft = 0; _animWidth = 320; _animHeight = 200; _objCount = 4; delete[] _renderData; delete _animArrayX; delete _animArrayY; delete[] _animArrayData; _objects = new Mult_Object[_objCount]; _renderData = new int16[9 * _objCount]; _animArrayX = new VariablesLE(_objCount * 4); _animArrayY = new VariablesLE(_objCount * 4); _animArrayData = new Mult_AnimData[_objCount]; memset(_objects, 0, _objCount * sizeof(Mult_Object)); memset(_renderData, 0, _objCount * 9 * sizeof(int16)); memset(_animArrayData, 0, _objCount * sizeof(Mult_AnimData)); for (_counter = 0; _counter < _objCount; _counter++) { Mult_Object &multObj = _objects[_counter]; Mult_AnimData &animData = _animArrayData[_counter]; multObj.pPosX = new VariableReference(*_animArrayX, _counter * 4); multObj.pPosY = new VariableReference(*_animArrayY, _counter * 4); multObj.pAnimData = &animData; animData.isStatic = 1; multObj.lastLeft = -1; multObj.lastTop = -1; multObj.lastRight = -1; multObj.lastBottom = -1; } _animSurf = _vm->_video->initSurfDesc(320, 200); _vm->_draw->_spritesArray[Draw::kAnimSurface] = _animSurf; _animSurf->blit(*_vm->_draw->_backSurface, 0, 0, 319, 199, 0, 0); _animDataAllocated = true; } else _animDataAllocated = false; _frame = 0; } void Mult_v1::drawStatics(bool &stop) { if (_multData->staticKeys[_multData->staticKeysCount - 1].frame > _frame) stop = false; for (_counter = 0; _counter < _multData->staticKeysCount; _counter++) { if ((_multData->staticKeys[_counter].frame != _frame) || (_multData->staticKeys[_counter].layer == -1)) continue; _vm->_scenery->_curStaticLayer = _multData->staticKeys[_counter].layer; for (_vm->_scenery->_curStatic = 0; _vm->_scenery->_curStaticLayer >= _vm->_scenery->getStaticLayersCount(_multData->staticIndices[_vm->_scenery->_curStatic]); _vm->_scenery->_curStatic++) { _vm->_scenery->_curStaticLayer -= _vm->_scenery->getStaticLayersCount(_multData->staticIndices[_vm->_scenery->_curStatic]); } _vm->_scenery->_curStatic = _multData->staticIndices[_vm->_scenery->_curStatic]; _vm->_scenery->renderStatic(_vm->_scenery->_curStatic, _vm->_scenery->_curStaticLayer); _animSurf->blit(*_vm->_draw->_backSurface, 0, 0, 319, 199, 0, 0); } } void Mult_v1::drawAnims(bool &stop) { int16 count; int animIndex; for (_index = 0; _index < 4; _index++) { for (_counter = 0; _counter < _multData->animKeysCount[_index]; _counter++) { Mult_AnimKey &key = _multData->animKeys[_index][_counter]; Mult_Object &animObj = _objects[_index]; Mult_AnimData &animData = *(animObj.pAnimData); if (key.frame != _frame) continue; if (key.layer != -1) { *(animObj.pPosX) = key.posX; *(animObj.pPosY) = key.posY; animData.frame = 0; animData.order = key.order; animData.animType = 1; animData.isPaused = 0; animData.isStatic = 0; animData.maxTick = 0; animObj.tick = 0; animData.layer = key.layer; int i = 0; animIndex = _multData->animIndices[i]; count = _vm->_scenery->getAnimLayersCount(animIndex); while (animData.layer >= count) { animData.layer -= count; animIndex = _multData->animIndices[++i]; count = _vm->_scenery->getAnimLayersCount(animIndex); } animData.animation = animIndex; } else animData.isStatic = 1; } } } void Mult_v1::newCycleAnim(Mult_Object &animObj) { Mult_AnimData &animData = *(animObj.pAnimData); int nAnim = animData.animation; int nLayer = animData.layer; Scenery::AnimLayer *animLayer = _vm->_scenery->getAnimLayer(nAnim, nLayer); animData.frame++; if (animData.frame < animLayer->framesCount) { animData.newCycle = 0; return; } switch (animData.animType) { case 0: animData.frame = 0; break; case 1: animData.frame = 0; *(animObj.pPosX) += animLayer->animDeltaX; *(animObj.pPosY) += animLayer->animDeltaY; break; case 2: animData.frame = 0; animData.animation = animData.newAnimation; animData.layer = animData.newLayer; break; case 3: animData.animType = 4; animData.frame = 0; break; case 5: animData.isStatic = 1; animData.frame = 0; break; case 6: animData.frame--; animData.isPaused = 1; break; } animData.newCycle = 1; } void Mult_v1::animate() { int16 minOrder; int16 maxOrder; int16 *pCurLefts, *pCurRights, *pCurTops, *pCurBottoms; int16 *pDirtyLefts, *pDirtyRights, *pDirtyTops, *pDirtyBottoms; int16 *pNeedRedraw; Mult_AnimData *pAnimData; int16 i, j; int16 order; if (!_renderData) 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++) { Mult_Object &animObj = _objects[i]; Mult_AnimData &animData = *(animObj.pAnimData); pNeedRedraw[i] = 0; pDirtyTops[i] = 1000; pDirtyLefts[i] = 1000; pDirtyBottoms[i] = 1000; pDirtyRights[i] = 1000; if (!animData.isStatic && !animData.isPaused && (animObj.tick == animData.maxTick)) { if (animData.order < minOrder) minOrder = animData.order; if (animData.order > maxOrder) maxOrder = animData.order; pNeedRedraw[i] = 1; _vm->_scenery->updateAnim(animData.layer, animData.frame, animData.animation, 0, *(animObj.pPosX), *(animObj.pPosY), 0); if (animObj.lastLeft != -1) { pDirtyLefts[i] = MIN(animObj.lastLeft, _vm->_scenery->_toRedrawLeft); pDirtyTops[i] = MIN(animObj.lastTop, _vm->_scenery->_toRedrawTop); pDirtyRights[i] = MAX(animObj.lastRight, _vm->_scenery->_toRedrawRight); pDirtyBottoms[i] = MAX(animObj.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 (animObj.lastLeft != -1) { if (animData.order < minOrder) minOrder = animData.order; if (animData.order > maxOrder) maxOrder = animData.order; if (animData.isStatic) *pNeedRedraw = 1; pCurLefts[i] = animObj.lastLeft; pDirtyLefts[i] = animObj.lastLeft; pCurTops[i] = animObj.lastTop; pDirtyTops[i] = animObj.lastTop; pCurRights[i] = animObj.lastRight; pDirtyRights[i] = animObj.lastRight; pCurBottoms[i] = animObj.lastBottom; pDirtyBottoms[i] = animObj.lastBottom; } } } // Find intersections for (i = 0; i < _objCount; i++) { Mult_AnimData &animData = *(_objects[i].pAnimData); animData.intersected = 200; if (animData.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; animData.intersected = j; break; } } // Restore dirty areas for (i = 0; i < _objCount; i++) { if ((pNeedRedraw[i] == 0) || (_objects[i].lastLeft == -1)) continue; _vm->_draw->_sourceSurface = Draw::kAnimSurface; _vm->_draw->_destSurface = Draw::kBackSurface; _vm->_draw->_spriteLeft = pDirtyLefts[i] - _animLeft; _vm->_draw->_spriteTop = pDirtyTops[i] - _animTop; _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++) { Mult_Object &animObj = _objects[i]; Mult_AnimData &animData = *(animObj.pAnimData); if (animData.order != order) continue; if (pNeedRedraw[i]) { if (!animData.isStatic) { _vm->_scenery->updateAnim(animData.layer, animData.frame, animData.animation, 2, *(animObj.pPosX), *(animObj.pPosY), 1); if (_vm->_scenery->_toRedrawLeft != -12345) { animObj.lastLeft = _vm->_scenery->_toRedrawLeft; animObj.lastTop = _vm->_scenery->_toRedrawTop; animObj.lastRight = _vm->_scenery->_toRedrawRight; animObj.lastBottom = _vm->_scenery->_toRedrawBottom; } else animObj.lastLeft = -1; } _vm->_scenery->updateStatic(order + 1); } else if (!animData.isStatic) { 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(animData.layer, animData.frame, animData.animation, 4, *(animObj.pPosX), *(animObj.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 newCycleAnim(_objects[i]); } else _objects[i].tick++; } } } // End of namespace Gob