scummvm/engines/kyra/sprites.cpp
Johannes Schickel 0bea9cf47b Made the kyra debug extensions more generic, i.e. scumm engine could replace
their debugC calls now with the new introduced debugC calls.
(A mail how to use it will follow shortly on -devel)
Also now these special engine debug flags can be specified from the commandline.
Also made the -c & --config parameter check more secure.

svn-id: r20695
2006-02-14 23:31:25 +00:00

570 lines
17 KiB
C++

/* ScummVM - Scumm Interpreter
* 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$
* $Id$
*
*/
#include "common/stdafx.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/system.h"
#include "kyra/screen.h"
#include "kyra/kyra.h"
#include "kyra/sprites.h"
#include "kyra/resource.h"
#include "kyra/animator.h"
namespace Kyra {
Sprites::Sprites(KyraEngine *engine, OSystem *system) {
_engine = engine;
_res = engine->resource();
_screen = engine->screen();
_system = system;
_dat = 0;
memset(_anims, 0, sizeof(_anims));
memset(_sceneShapes, 0, sizeof(_sceneShapes));
_animDelay = 16;
_spriteDefStart = 0;
memset(_drawLayerTable, 0, sizeof(_drawLayerTable));
_sceneAnimatorBeaconFlag = 0;
}
Sprites::~Sprites() {
delete[] _dat;
freeSceneShapes();
for (int i = 0; i < MAX_NUM_ANIMS; i++) {
if (_anims[i].background)
free(_anims[i].background);
}
}
void Sprites::setupSceneAnims() {
debugC(9, kDebugLevelSprites, "Sprites::setupSceneAnims()");
uint8 *data;
for (int i = 0; i < MAX_NUM_ANIMS; i++) {
if (_anims[i].background) {
free(_anims[i].background);
_anims[i].background = 0;
}
if (_anims[i].script != 0) {
data = _anims[i].script;
assert( READ_LE_UINT16(data) == 0xFF86 );
data += 4;
_anims[i].disable = READ_LE_UINT16(data) != 0;
data += 4;
_anims[i].unk2 = READ_LE_UINT16(data);
data += 4;
if (_engine->_northExitHeight > READ_LE_UINT16(data))
_anims[i].drawY = _engine->_northExitHeight;
else
_anims[i].drawY = READ_LE_UINT16(data);
data += 4;
//sceneUnk2[i] = READ_LE_UINT16(data);
data += 4;
_anims[i].x = READ_LE_UINT16(data);
data += 4;
_anims[i].y = READ_LE_UINT16(data);
data += 4;
_anims[i].width = *(data) - 1;
data += 4;
_anims[i].height = *(data);
data += 4;
_anims[i].sprite = READ_LE_UINT16(data);
data += 4;
_anims[i].flipX = READ_LE_UINT16(data) != 0;
data += 4;
_anims[i].width2 = *(data);
data += 4;
_anims[i].height2 = *(data);
data += 4;
_anims[i].unk1 = READ_LE_UINT16(data) != 0;
data += 4;
_anims[i].play = READ_LE_UINT16(data) != 0;
data += 2;
_anims[i].script = data;
int bkgdWidth = _anims[i].width << 3;
int bkgdHeight = _anims[i].height;
if (_anims[i].width2)
bkgdWidth += _anims[i].width2 << 3;
if (_anims[i].height2)
bkgdHeight += _anims[i].height2;
_anims[i].background = (uint8 *)malloc(_screen->getRectSize(bkgdWidth + 1, bkgdHeight));
memset(_anims[i].background, 0, _screen->getRectSize(bkgdWidth + 1, bkgdHeight));
assert(_anims[i].background);
}
}
}
void Sprites::updateSceneAnims() {
debugC(9, kDebugLevelSprites, "Sprites::updateSceneAnims()");
uint32 currTime = _system->getMillis();
uint8 *data;
bool endLoop;
uint16 rndNr;
uint16 anim;
uint16 sound;
for (int i = 0; i < MAX_NUM_ANIMS; i++) {
if (_anims[i].script == 0 || !_anims[i].play || _anims[i].nextRun != 0 && _anims[i].nextRun > currTime)
continue;
if (_anims[i].reentry == 0) {
data = _anims[i].script;
if (READ_LE_UINT16(data) == 0xFF8B)
continue;
} else {
data = _anims[i].reentry;
_anims[i].reentry = 0;
}
endLoop = false;
while (READ_LE_UINT16(data) != 0xFF87 && !endLoop) {
assert((data - _anims[i].script) < _anims[i].length);
switch (READ_LE_UINT16(data)) {
case 0xFF88:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set sprite image.");
debugC(6, kDebugLevelSprites, "Sprite index %i", READ_LE_UINT16(data));
_anims[i].sprite = READ_LE_UINT16(data);
data += 2;
//debugC(6, kDebugLevelSprites, "Unused %i", READ_LE_UINT16(data));
data += 2;
debugC(6, kDebugLevelSprites, "X %i", READ_LE_UINT16(data));
_anims[i].x = READ_LE_UINT16(data);
data += 2;
debugC(6, kDebugLevelSprites, "Y %i", READ_LE_UINT16(data));
_anims[i].y = READ_LE_UINT16(data);
data += 2;
_anims[i].flipX = false;
refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
break;
case 0xFF8D:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set sprite image, flipped.");
debugC(6, kDebugLevelSprites, "Sprite index %i", READ_LE_UINT16(data));
_anims[i].sprite = READ_LE_UINT16(data);
data += 2;
//debugC(9, kDebugLevelSprites, "Unused %i", READ_LE_UINT16(data));
data += 2;
debugC(6, kDebugLevelSprites, "X %i", READ_LE_UINT16(data));
_anims[i].x = READ_LE_UINT16(data);
data += 2;
debugC(6, kDebugLevelSprites, "Y %i", READ_LE_UINT16(data));
_anims[i].y = READ_LE_UINT16(data);
data += 2;
_anims[i].flipX = true;
refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
break;
case 0xFF8A:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set time to wait");
debugC(6, kDebugLevelSprites, "Time %i", READ_LE_UINT16(data));
_anims[i].nextRun = _system->getMillis() + READ_LE_UINT16(data) * _animDelay;
data += 2;
break;
case 0xFFB3:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set time to wait to random value");
rndNr = READ_LE_UINT16(data) + _rnd.getRandomNumber( READ_LE_UINT16(data) + 2);
debugC(6, kDebugLevelSprites, "Minimum time %i", READ_LE_UINT16(data));
data += 2;
debugC(6, kDebugLevelSprites, "Maximum time %i", READ_LE_UINT16(data));
data += 2;
_anims[i].nextRun = _system->getMillis() + rndNr * _animDelay;
break;
case 0xFF8C:
data += 2;
debugC(6, kDebugLevelSprites, "func: Wait until wait time has elapsed");
_anims[i].reentry = data;
endLoop = true;
//assert( _anims[i].nextRun > _system->getMillis());
break;
case 0xFF99:
data += 2;
debugC(1, kDebugLevelSprites, "func: Set value of unknown animation property to 1");
_anims[i].unk1 = 1;
break;
case 0xFF9A:
data += 2;
debugC(1, kDebugLevelSprites, "func: Set value of unknown animation property to 0");
_anims[i].unk1 = 0;
break;
case 0xFF97:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set default X coordinate of sprite");
debugC(6, kDebugLevelSprites, "X %i", READ_LE_UINT16(data));
_anims[i].x = READ_LE_UINT16(data);
data += 2;
break;
case 0xFF98:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set default Y coordinate of sprite");
debugC(6, kDebugLevelSprites, "Y %i", READ_LE_UINT16(data));
_anims[i].y = READ_LE_UINT16(data);
data += 2;
break;
case 0xFF8B:
debugC(6, kDebugLevelSprites, "func: Jump to start of script section");
//data = scriptStart;
_anims[i].nextRun = _system->getMillis();
endLoop = true;
break;
case 0xFF8E:
data += 2;
debugC(6, kDebugLevelSprites, "func: Begin for () loop");
debugC(6, kDebugLevelSprites, "Iterations: %i", READ_LE_UINT16(data));
_anims[i].loopsLeft = READ_LE_UINT16(data);
data += 2;
_anims[i].loopStart = data;
break;
case 0xFF8F:
data += 2;
debugC(6, kDebugLevelSprites, "func: End for () loop");
if (_anims[i].loopsLeft > 0) {
_anims[i].loopsLeft--;
data = _anims[i].loopStart;
}
break;
case 0xFF90:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set sprite image using default X and Y");
debugC(6, kDebugLevelSprites, "Sprite index %i", READ_LE_UINT16(data));
_anims[i].sprite = READ_LE_UINT16(data);
_anims[i].flipX = false;
data += 2;
refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
break;
case 0xFF91:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set sprite image using default X and Y, flipped.");
debugC(6, kDebugLevelSprites, "Sprite index %i", READ_LE_UINT16(data));
_anims[i].sprite = READ_LE_UINT16(data);
_anims[i].flipX = true;
data += 2;
refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
break;
case 0xFF92:
data += 2;
debugC(6, kDebugLevelSprites, "func: Increase value of default X-coordinate");
debugC(6, kDebugLevelSprites, "Increment %i", READ_LE_UINT16(data));
_anims[i].x += READ_LE_UINT16(data);
data += 2;
break;
case 0xFF93:
data += 2;
debugC(6, kDebugLevelSprites, "func: Increase value of default Y-coordinate");
debugC(6, kDebugLevelSprites, "Increment %i", READ_LE_UINT16(data));
_anims[i].y += READ_LE_UINT16(data);
data += 2;
break;
case 0xFF94:
data += 2;
debugC(6, kDebugLevelSprites, "func: Decrease value of default X-coordinate");
debugC(6, kDebugLevelSprites, "Decrement %i", READ_LE_UINT16(data));
_anims[i].x -= READ_LE_UINT16(data);
data += 2;
break;
case 0xFF95:
data += 2;
debugC(6, kDebugLevelSprites, "func: Decrease value of default Y-coordinate");
debugC(6, kDebugLevelSprites, "Decrement %i", READ_LE_UINT16(data));
_anims[i].y -= READ_LE_UINT16(data);
data += 2;
break;
case 0xFF96:
data += 2;
debugC(9, kDebugLevelSprites, "func: Stop animation");
debugC(9, kDebugLevelSprites, "Animation index %i", READ_LE_UINT16(data));
anim = READ_LE_UINT16(data);
data += 2;
_anims[anim].play = false;
_anims[anim].sprite = -1;
break;
/* case 0xFF97:
data += 2;
debugC(1, kDebugLevelSprites, "func: Set value of animation property 34h to 0");
break;*/
case 0xFFAD:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set Brandon's X coordinate");
debugC(6, kDebugLevelSprites, "X %i", READ_LE_UINT16(data));
_engine->currentCharacter()->x1 = READ_LE_UINT16(data);
data += 2;
break;
case 0xFFAE:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set Brandon's Y coordinate");
debugC(6, kDebugLevelSprites, "Y %i", READ_LE_UINT16(data));
_engine->currentCharacter()->y1 = READ_LE_UINT16(data);
data += 2;
break;
case 0xFFAF:
data += 2;
debugC(6, kDebugLevelSprites, "func: Set Brandon's sprite");
debugC(6, kDebugLevelSprites, "Sprite %i", READ_LE_UINT16(data));
_engine->currentCharacter()->currentAnimFrame = READ_LE_UINT16(data);
data += 2;
break;
case 0xFFAA:
data += 2;
debugC(1, kDebugLevelSprites, "TODO func: Reset Brandon's sprite");
break;
case 0xFFAB:
data += 2;
debugC(6, kDebugLevelSprites, "func: Update Brandon's sprite");
_engine->animator()->animRefreshNPC(0);
_engine->animator()->flagAllObjectsForRefresh();
_engine->animator()->updateAllObjectShapes();
break;
case 0xFFB0:
data += 2;
debugC(6, kDebugLevelSprites, "func: Play sound");
debugC(6, kDebugLevelSprites, "Sound index %i", READ_LE_UINT16(data));
_engine->snd_playSoundEffect(READ_LE_UINT16(data));
data += 2;
break;
case 0xFFB1:
data += 2;
_sceneAnimatorBeaconFlag = 1;
break;
case 0xFFB2:
data += 2;
_sceneAnimatorBeaconFlag = 0;
break;
case 0xFFB4:
data += 2;
debugC(6, kDebugLevelSprites, "func: Play (at random) a certain sound at a certain percentage of time");
debugC(6, kDebugLevelSprites, "Sound index %i", READ_LE_UINT16(data));
sound = READ_LE_UINT16(data);
data += 2;
debugC(6, kDebugLevelSprites, "Percentage %i", READ_LE_UINT16(data));
rndNr = _rnd.getRandomNumber(100);
if (rndNr <= READ_LE_UINT16(data))
_engine->snd_playSoundEffect(sound);
data += 2;
break;
case 0xFFA7:
data += 2;
_anims[READ_LE_UINT16(data)].play = 1;
data += 2;
break;
default:
debugC(1, kDebugLevelSprites, "Unsupported anim command %X in script %i", READ_LE_UINT16(data), i);
//endLoop = true;
data += 1;
break;
}
}
if (READ_LE_UINT16(data) == 0xFF87)
_anims[i].play = false;
}
}
void Sprites::loadDAT(const char *filename, SceneExits &exits) {
debugC(9, kDebugLevelSprites, "Sprites::loadDat('%s')", filename);
uint32 fileSize;
delete[] _dat;
_spriteDefStart = 0;
_dat = _res->fileData(filename, &fileSize);
memset(_anims, 0, sizeof(_anims));
uint8 nextAnim = 0;
assert(fileSize > 0x6D);
memcpy(_drawLayerTable, (_dat + 0x0D), 8);
_engine->_northExitHeight = READ_LE_UINT16(_dat + 0x15);
if (_engine->_northExitHeight & 1)
_engine->_northExitHeight += 1;
// XXX
memcpy(_screen->_currentPalette + 744 - 60, _dat + 0x17, 60);
uint8 *data = _dat + 0x6B;
uint16 length = READ_LE_UINT16(data);
data += 2;
if (length > 2) {
assert( length < fileSize);
uint8 *animstart;
uint8 *start = data;
while (1) {
if (((uint16)(data - _dat) >= fileSize) || (data - start) >= length)
break;
if (READ_LE_UINT16(data) == 0xFF83) {
//debugC(1, kDebugLevelSprites, "Body section end.");
data += 2;
break;
}
switch (READ_LE_UINT16(data)) {
case 0xFF81:
data += 2;
//debugC(1, kDebugLevelSprites, "Body section start");
break;
case 0xFF82:
data += 2;
//debugC(1, kDebugLevelSprites, "Unknown 0xFF82 section");
break;
case 0xFF84:
data += 2;
_spriteDefStart = data;
while (READ_LE_UINT16(data) != 0xFF85) {
data += 2;
}
data += 2;
break;
case 0xFF86:
assert(nextAnim < MAX_NUM_ANIMS);
_anims[nextAnim].script = data;
_anims[nextAnim].sprite = -1;
_anims[nextAnim].play = true;
animstart = data;
data += 2;
while (READ_LE_UINT16(data) != 0xFF87) {
assert((uint16)(data - _dat) < fileSize);
data += 2;
}
_anims[nextAnim].length = data - animstart;
//debugC(1, kDebugLevelSprites, "Found an anim script of length %i!", _anims[nextAnim].length);
nextAnim++;
data += 2;
break;
default:
debugC(1, kDebugLevelSprites, "Unknown code in DAT file: %x", READ_LE_UINT16(data));
data += 2;
break;
}
}
} else {
data += 2;
}
assert(fileSize - (data - _dat) == 0xC);
exits.northXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
exits.northYPos = *data++ & 0xFFFE;
exits.eastXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
exits.eastYPos = *data++ & 0xFFFE;
exits.southXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
exits.southYPos = *data++ & 0xFFFE;
exits.westXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
exits.westYPos = *data++ & 0xFFFE;
}
void Sprites::freeSceneShapes() {
debugC(9, kDebugLevelSprites, "Sprites::freeSceneShapes()");
for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++ ) {
free(_sceneShapes[i]);
_sceneShapes[i] = 0;
}
}
void Sprites::loadSceneShapes() {
debugC(9, kDebugLevelSprites, "Sprites::loadSceneShapes()");
uint8 *data = _spriteDefStart;
int spriteNum, x, y, width, height;
freeSceneShapes();
memset( _sceneShapes, 0, sizeof(_sceneShapes));
if (_spriteDefStart == 0)
return;
int bakPage = _screen->_curPage;
_screen->_curPage = 3;
while (READ_LE_UINT16(data) != 0xFF85) {
spriteNum = READ_LE_UINT16(data);
assert(spriteNum < ARRAYSIZE(_sceneShapes));
data += 2;
x = READ_LE_UINT16(data) * 8;
data += 2;
y = READ_LE_UINT16(data);
data += 2;
width = READ_LE_UINT16(data) * 8;
data += 2;
height = READ_LE_UINT16(data);
data += 2;
_sceneShapes[spriteNum] = _screen->encodeShape(x, y, width, height, 2);
debugC(9, kDebugLevelSprites, "Sprite %i is at (%i, %i), width %i, height %i", spriteNum, x, y, width, height);
}
_screen->_curPage = bakPage;
}
void Sprites::refreshSceneAnimObject(uint8 animNum, uint8 shapeNum, uint16 x, uint16 y, bool flipX, bool unkFlag) {
debugC(9, kDebugLevelSprites, "Sprites::refreshSceneAnimObject(%i, %i, %i, %i, %i, %i", animNum, shapeNum, x, y, flipX, unkFlag);
AnimObject &anim = _engine->animator()->sprites()[animNum];
anim.refreshFlag = 1;
anim.bkgdChangeFlag = 1;
if (unkFlag)
anim.flags |= 0x0200;
else
anim.flags &= 0xFD00;
if (flipX)
anim.flags |= 1;
else
anim.flags &= 0xFE;
anim.sceneAnimPtr = _sceneShapes[shapeNum];
anim.animFrameNumber = -1;
anim.x1 = x;
anim.y1 = y;
}
int Sprites::getDrawLayer(int y) {
debugC(9, kDebugLevelSprites, "getDrawLayer(%d)", y);
uint8 returnValue = 0;
for (int i = 0; i < ARRAYSIZE(_drawLayerTable); ++i) {
uint8 temp = _drawLayerTable[i];
if (temp) {
if (temp <= y) {
returnValue = i;
}
}
}
if (returnValue <= 0) {
returnValue = 1;
} else if (returnValue >= 7) {
returnValue = 6;
}
return returnValue;
}
} // end of namespace Kyra