Files
scummvm-cursorfix/engines/scumm/he/sprite_he.cpp
2026-02-02 04:50:13 +01:00

2310 lines
63 KiB
C++

/* 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/>.
*
*/
#ifdef ENABLE_HE
#include "scumm/he/logic_he.h"
#include "scumm/he/intern_he.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/he/sprite_he.h"
#include "scumm/usage_bits.h"
#include "scumm/util.h"
#include "scumm/he/wiz_he.h"
namespace Scumm {
Sprite::Sprite(ScummEngine_v90he *vm)
:
_vm(vm),
_groupTable(0),
_spriteTable(0),
_activeSprites(0),
_activeSpriteCount(0),
_maxSpriteGroups(0),
_maxSprites(0),
_maxImageLists(0),
_imageLists(nullptr),
_imageListStack(nullptr) {
}
Sprite::~Sprite() {
free(_groupTable);
free(_spriteTable);
free(_activeSprites);
free(_imageLists);
free(_imageListStack);
}
void ScummEngine_v90he::allocateArrays() {
ScummEngine_v70he::allocateArrays();
_sprite->initializeStuff(_numSprites, MAX(64, _numSprites / 4), 64);
}
void Sprite::getSpriteDrawRect(int sprite, Common::Rect *rectPtr) {
assertRange(1, sprite, _maxSprites, "sprite");
int32 x, y;
calcSpriteSpot(&_spriteTable[sprite], true, x, y);
Common::Point spot((int16)x, (int16)y);
getSpriteRectPrim(&_spriteTable[sprite], rectPtr, true, &spot);
}
void Sprite::getSpriteLogicalRect(int sprite, Common::Rect *rectPtr) {
assertRange(1, sprite, _maxSprites, "sprite");
int32 x, y;
calcSpriteSpot(&_spriteTable[sprite], false, x, y);
Common::Point spot((int16)x, (int16)y);
getSpriteRectPrim(&_spriteTable[sprite], rectPtr, false, &spot);
}
//
// spriteInfoGet functions
//
int Sprite::spriteFromPoint(int x, int y, int groupCheck, int quickCheck, int classCount, int *classCheckTable) {
SpriteInfo **spritePtr;
const Common::Rect *r;
int image;
if (!_activeSpriteCount)
return 0;
spritePtr = &_activeSprites [_activeSpriteCount - 1];
if (quickCheck != 0) {
for (int counter = 0; counter < _activeSpriteCount; counter++, spritePtr--) {
if (_vm->_game.heversion > 90 && groupCheck != 0) {
if (groupCheck != (*spritePtr)->group)
continue;
}
if (_vm->_game.heversion >= 99 && classCount != 0) {
if (!checkSpriteClassAgaintClassSet(((*spritePtr) - _spriteTable), classCount, classCheckTable)) {
continue;
}
}
r = &(*spritePtr)->lastRect;
image = (*spritePtr)->lastImage;
if ((!image) || (!_vm->_wiz->isRectValid(*r)))
continue;
if ((r->left > x) || (r->top > y) || (r->right < x) || (r->bottom < y)) {
continue;
}
return ((*spritePtr) - _spriteTable);
}
} else {
for (int counter = 0; counter < _activeSpriteCount; counter++, spritePtr--) {
r = &(*spritePtr)->lastRect;
image = (*spritePtr)->lastImage;
if ((!image))
continue;
if (_vm->_game.heversion > 90 && groupCheck) {
if (groupCheck != (*spritePtr)->group)
continue;
}
if (_vm->_game.heversion >= 99 && classCount) {
if (!checkSpriteClassAgaintClassSet(((*spritePtr) - _spriteTable), classCount, classCheckTable)) {
continue;
}
}
if (_vm->_game.heversion >= 99) {
int state = 0;
int32 testPointX, testPointY;
if ((*spritePtr)->maskImage) {
int32 maskSpotX, maskSpotY, imageSpotX, imageSpotY;
int maskStateCount;
// Change to using the the mask image instead of the display image...
image = (*spritePtr)->maskImage;
// Get the state for the mask (wrap if necessary)...
maskStateCount = _vm->_wiz->getWizStateCount(image);
state = ((*spritePtr)->lastState % maskStateCount);
// Convert the coords to image relative coords...
testPointX = (x - (*spritePtr)->lastSpot.x);
testPointY = (y - (*spritePtr)->lastSpot.y);
// Get the spot's so we can adjust the "lastSpot" to
// be relative to the mask's link point...
_vm->_wiz->getWizSpot((*spritePtr)->lastImage, state, imageSpotX, imageSpotY);
_vm->_wiz->getWizSpot(image, state, maskSpotX, maskSpotY);
// Convert the coords to "Mask" relative coords...
testPointX += (maskSpotX - imageSpotX);
testPointY += (maskSpotY - imageSpotY);
} else {
r = &(*spritePtr)->lastRect;
if (!_vm->_wiz->isRectValid(*r) || (r->left > x) || (r->top > y) || (r->right < x) || (r->bottom < y)) {
continue;
}
// Convert the coords to image relative coords...
testPointX = (x - (*spritePtr)->lastSpot.x);
testPointY = (y - (*spritePtr)->lastSpot.y);
// Get the last active image state...
state = (*spritePtr)->lastState;
}
if (!_vm->_wiz->hitTestWiz(image, state, testPointX, testPointY, (*spritePtr)->lastRenderFlags)) {
continue;
}
return ((*spritePtr) - _spriteTable);
} else {
if (!_vm->_wiz->isRectValid(*r))
continue;
if ((r->left > x) || (r->top > y) || (r->right < x) || (r->bottom < y))
continue;
int32 state = _vm->_game.heversion > 80 ? (*spritePtr)->lastState : 0; // HE80 doesn't have states
if (!_vm->_wiz->hitTestWiz(image, state, (x - (*spritePtr)->lastSpot.x), (y - (*spritePtr)->lastSpot.y), 0)) {
continue;
}
return ((*spritePtr) - _spriteTable);
}
}
}
return 0;
}
int Sprite::getSpriteClass(int spriteId, int classId) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (classId == -1) {
return _spriteTable[spriteId].classFlags;
} else {
assertRange(1, classId, 32, "sprite");
return _spriteTable[spriteId].classFlags & (1 << (classId - 1));
}
}
int Sprite::checkSpriteClassAgaintClassSet(int sprite, int classCount, int *classCheckTable) {
int classId, classBit;
while (classCount--) {
classId = classCheckTable[classCount];
classBit = getSpriteClass(sprite, (classId & 0x7f));
if (((classId & 0x80) == 0x80) && (classBit == 0))
return 0;
if (((classId & 0x80) == 0x00) && (classBit != 0))
return 0;
}
return 1;
}
int Sprite::getSpriteRenderToBackground(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFBackgroundRender) != 0) ? 1 : 0;
}
int Sprite::getSpriteVertFlip(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFVFlip) != 0) ? 1 : 0;
}
int Sprite::getSpriteHorzFlip(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFHFlip) != 0) ? 1 : 0;
}
int Sprite::getSpriteActiveFlag(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFActive) != 0) ? 1 : 0;
}
int Sprite::getSpriteImageRemapFlag(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFUseImageRemap) != 0) ? 1 : 0;
}
int Sprite::getSpriteAutoAnimFlag(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return (_spriteTable[spriteId].flags & kSFAutoAnimate);
}
int Sprite::getSpriteUpdateType(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
if (_spriteTable[spriteId].flags & kSFSmartRender) {
return SPRDEF_SMART;
} else {
return (_spriteTable[spriteId].flags & kSFDontCombineErase) ? SPRDEF_SIMPLE : SPRDEF_DUMB;
}
} else {
return ((_spriteTable[spriteId].flags & kSFSmartRender) != 0) ? 1 : 0;
}
}
int Sprite::getSpriteEraseType(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return ((_spriteTable[spriteId].flags & kSFIgnoreErase) == 0) ? 1 : 0;
}
int Sprite::getSpriteImage(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].image;
}
int Sprite::getSpriteImageState(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].state;
}
int Sprite::getSpriteGroup(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].group;
}
int Sprite::getSpritePalette(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].palette;
}
int Sprite::getSpritePriority(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].priority;
}
int Sprite::getSpriteDisplayX(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].group)
return _spriteTable[spriteId].posX + _groupTable[_spriteTable[spriteId].group].posX;
else
return _spriteTable[spriteId].posX;
}
int Sprite::getSpriteDisplayY(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].group)
return _spriteTable[spriteId].posY + _groupTable[_spriteTable[spriteId].group].posY;
else
return _spriteTable[spriteId].posY;
}
int Sprite::getUserValue(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].userValue;
}
int Sprite::getSpriteShadow(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].shadow;
}
int Sprite::getSpriteImageStateCount(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].maxStates;
}
int Sprite::getSpriteScale(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_vm->_game.heversion == 95)
return 0;
return _spriteTable[spriteId].scale;
}
int Sprite::getSpriteAnimSpeed(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].animSpeed;
}
int Sprite::getSourceImage(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].sourceImage;
}
int Sprite::getMaskImage(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].maskImage;
}
int Sprite::getSpriteGeneralProperty(int spriteId, int type) {
debug(7, "getSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type);
assertRange(1, spriteId, _maxSprites, "sprite");
int outValue = 0;
if (((ScummEngine_v90he *)_vm)->_logicHE && ((ScummEngine_v90he *)_vm)->_logicHE->getSpriteProperty(spriteId, type, &outValue)) {
return outValue;
}
switch (type) {
case SPRPROP_SPECIAL_RENDER_FLAGS:
return _spriteTable[spriteId].specialRenderFlags;
case SPRPROP_CONDITION_BITS:
return _spriteTable[spriteId].conditionBits;
case SPRPROP_ANIMATION_SUB_STATE:
return getSpriteAnimSpeedState(spriteId);
default:
error("getSpriteGeneralProperty: Invalid type %d", type);
}
}
int Sprite::getDestImageForSprite(const SpriteInfo *spritePtr) {
if (spritePtr->image) {
if (spritePtr->group) {
return _groupTable[spritePtr->group].image;
}
}
return 0;
}
int Sprite::getSpriteAnimSpeedState(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
return _spriteTable[spriteId].animState;
}
void Sprite::getSpriteImageDim(int spriteId, int32 &w, int32 &h) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].image) {
_vm->_wiz->getWizImageDim(_spriteTable[spriteId].image, _spriteTable[spriteId].state, w, h);
} else {
w = 0;
h = 0;
}
}
void Sprite::getSpritePosition(int spriteId, int32 &tx, int32 &ty) {
assertRange(1, spriteId, _maxSprites, "sprite");
tx = _spriteTable[spriteId].posX;
ty = _spriteTable[spriteId].posY;
}
void Sprite::getSpriteRectPrim(const SpriteInfo *spritePtr, Common::Rect *rectPtr, bool includeGroupTransform, const Common::Point *spotPtr) {
bool angleSpecified, scaleSpecified;
int state, scale, angle;
int32 x, y;
int image = spritePtr->image;
if (image != 0) {
Common::Point tmpPt(spotPtr->x, spotPtr->y);
if (_vm->_game.heversion < 100 && !_vm->_isHE995) {
calcSpriteSpot(spritePtr, includeGroupTransform, x, y);
tmpPt.x = x;
tmpPt.y = y;
}
state = spritePtr->state;
angle = spritePtr->angle;
scale = spritePtr->scale;
scaleSpecified = (kSFScaleSpecified & spritePtr->flags);
angleSpecified = (kSFAngleSpecified & spritePtr->flags);
if (angleSpecified || scaleSpecified) {
Common::Point listOfPoints[4];
int32 w, h;
_vm->_wiz->getWizImageDim(image, state, w, h);
listOfPoints[0].x = -w / 2;
listOfPoints[0].y = -h / 2;
listOfPoints[1].x = listOfPoints[0].x + w - 1;
listOfPoints[1].y = listOfPoints[0].y;
listOfPoints[2].x = listOfPoints[1].x;
listOfPoints[2].y = listOfPoints[0].y + h - 1;
listOfPoints[3].x = listOfPoints[0].x;
listOfPoints[3].y = listOfPoints[2].y;
if (scaleSpecified) {
for (int i = 0; i < 4; i++) {
listOfPoints[i].x = (scale * listOfPoints[i].x) / 256;
listOfPoints[i].y = (scale * listOfPoints[i].y) / 256;
}
}
// Rotate the points...
if (angleSpecified) {
_vm->_wiz->polyRotatePoints(listOfPoints, 4, angle);
}
// Offset the points...
_vm->_wiz->polyMovePolygonPoints(listOfPoints, 4, tmpPt.x, tmpPt.y);
// Finally get down the point and get the bounding rect...
_vm->_wiz->polyBuildBoundingRect(listOfPoints, 4, *rectPtr);
} else {
int32 w, h;
_vm->_wiz->getWizImageDim(image, state, w, h);
rectPtr->left = tmpPt.x;
rectPtr->top = tmpPt.y;
rectPtr->right = tmpPt.x + w - 1;
rectPtr->bottom = tmpPt.y + h - 1;
}
} else {
rectPtr->left = 1234;
rectPtr->top = 1234;
rectPtr->right = -1234;
rectPtr->bottom = -1234;
}
}
void Sprite::getDelta(int spriteId, int32 &dx, int32 &dy) {
assertRange(1, spriteId, _maxSprites, "sprite");
dx = _spriteTable[spriteId].deltaPosX;
dy = _spriteTable[spriteId].deltaPosY;
}
void Sprite::calcSpriteSpot(const SpriteInfo *spritePtr, bool includeGroupTransform, int32 &x, int32 &y) {
if (_vm->_game.heversion <= 90) {
if (_vm->_game.heversion == 80) {
_vm->_wiz->getWizSpot(spritePtr->image, x, y);
} else {
_vm->_wiz->getWizSpot(spritePtr->image, spritePtr->state, x, y);
}
x = spritePtr->posX - x;
y = spritePtr->posY - y;
if (spritePtr->group != 0) {
x += _groupTable[spritePtr->group].posX;
y += _groupTable[spritePtr->group].posY;
}
} else if (_vm->_game.heversion == 95) {
_vm->_wiz->getWizSpot(spritePtr->image, spritePtr->state, x, y);
x = spritePtr->posX - x;
y = spritePtr->posY - y;
if (spritePtr->group != 0) {
x += _groupTable[spritePtr->group].posX;
y += _groupTable[spritePtr->group].posY;
}
} else if (_vm->_game.heversion >= 98) {
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
if (spritePtr->image == 0) {
if (includeGroupTransform && spritePtr->group) {
x = _groupTable[spritePtr->group].posX;
y = _groupTable[spritePtr->group].posY;
} else {
x = 0;
y = 0;
}
return;
}
}
_vm->_wiz->getWizSpot(spritePtr->image, spritePtr->state, x, y);
int group = spritePtr->group;
if ((includeGroupTransform || _vm->_game.heversion <= 98) && group != 0) {
if (_groupTable[group].isScaled) {
x = (int)((float)spritePtr->posX * _groupTable[group].xScale) - x;
y = (int)((float)spritePtr->posY * _groupTable[group].yScale) - y;
x += _groupTable[group].posX;
y += _groupTable[group].posY;
} else {
x = spritePtr->posX - x;
y = spritePtr->posY - y;
x += _groupTable[group].posX;
y += _groupTable[group].posY;
}
} else {
x = spritePtr->posX - x;
y = spritePtr->posY - y;
}
}
}
//
// spriteGroupGet functions
//
int ScummEngine_v90he::getGroupSpriteArray(int spriteGroupId) {
int i, numSprites = 0;
assertRange(1, spriteGroupId, _sprite->_maxSpriteGroups, "sprite group");
for (i = (_sprite->_maxSprites - 1); i > 0; i--) {
if (_sprite->_spriteTable[i].group == spriteGroupId)
numSprites++;
}
if (!numSprites)
return 0;
writeVar(0, 0);
defineArray(0, kDwordArray, 0, 0, 0, numSprites);
writeArray(0, 0, 0, numSprites);
numSprites = 1;
for (i = (_sprite->_maxSprites - 1); i > 0; i--) {
if (_sprite->_spriteTable[i].group == spriteGroupId) {
writeArray(0, 0, numSprites, i);
numSprites++;
}
}
return readVar(0);
}
int Sprite::getGroupPriority(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].priority;
}
int Sprite::getGroupImage(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].image;
}
int Sprite::getGroupXMul(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].xMul;
}
int Sprite::getGroupXDiv(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].xDiv;
}
int Sprite::getGroupYMul(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].yMul;
}
int Sprite::getGroupYDiv(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
return _groupTable[spriteGroupId].yDiv;
}
void Sprite::getGroupPoint(int spriteGroupId, int32 &tx, int32 &ty) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
tx = _groupTable[spriteGroupId].posX;
ty = _groupTable[spriteGroupId].posY;
}
int Sprite::getGroupGeneralProperty(int spriteGroupId, int property) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
int outValue = 0;
if (((ScummEngine_v90he *)_vm)->_logicHE && ((ScummEngine_v90he *)_vm)->_logicHE->getGroupProperty(spriteGroupId, property, &outValue)) {
return outValue;
}
return 0;
}
//
// spriteInfoSet functions
//
void Sprite::setSpritePalette(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].palette != value) {
_spriteTable[spriteId].palette = value;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
}
void Sprite::setSourceImage(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].sourceImage != value) {
_spriteTable[spriteId].sourceImage = value;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
}
void Sprite::setMaskImage(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].maskImage = value;
}
void Sprite::setSpriteImageState(int spriteId, int state) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_spriteTable[spriteId].image) {
int imageStateCount = _spriteTable[spriteId].maxStates - 1;
state = MAX(0, MIN(state, imageStateCount));
if (_spriteTable[spriteId].state != state) {
_spriteTable[spriteId].state = state;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
}
}
void Sprite::setSpritePosition(int spriteId, int tx, int ty) {
assertRange(1, spriteId, _maxSprites, "sprite");
int32 oldX, oldY;
oldX = _spriteTable[spriteId].posX;
oldY = _spriteTable[spriteId].posY;
_spriteTable[spriteId].posX = tx;
_spriteTable[spriteId].posY = ty;
if (oldX != tx || oldY != ty) {
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
}
void Sprite::setSpriteGroup(int spriteId, int group) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (group != 0)
assertRange(1, group, _maxSpriteGroups, "sprite group");
_spriteTable[spriteId].group = group;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setDelta(int spriteId, int dx, int dy) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].deltaPosX = dx;
_spriteTable[spriteId].deltaPosY = dy;
}
void Sprite::setSpriteShadow(int spriteId, int shadow) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].shadow = shadow;
if (_spriteTable[spriteId].image)
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setUserValue(int spriteId, int value1, int value2) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].userValue = value2;
}
void Sprite::setSpritePriority(int spriteId, int priority) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].priority = priority;
}
void Sprite::moveSprite(int spriteId, int dx, int dy) {
assertRange(1, spriteId, _maxSprites, "sprite");
int32 oldX, oldY;
oldX = _spriteTable[spriteId].posX;
oldY = _spriteTable[spriteId].posY;
_spriteTable[spriteId].posX += dx;
_spriteTable[spriteId].posY += dy;
if ((oldX != _spriteTable[spriteId].posX) || (oldY != _spriteTable[spriteId].posY))
_spriteTable[spriteId].flags |= (kSFErase | kSFRender);
}
void Sprite::setSpriteScale(int spriteId, int scale) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].flags |= kSFScaleSpecified;
if (_spriteTable[spriteId].scale != scale) {
_spriteTable[spriteId].scale = scale;
if (_spriteTable[spriteId].image)
_spriteTable[spriteId].flags |= (kSFErase | kSFRender);
}
}
void Sprite::setSpriteAngle(int spriteId, int angle) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].flags |= kSFAngleSpecified;
if (_spriteTable[spriteId].angle != angle) {
_spriteTable[spriteId].angle = angle;
if (_spriteTable[spriteId].image)
_spriteTable[spriteId].flags |= (kSFErase | kSFRender);
}
}
void Sprite::setSpriteRenderToBackground(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
int oldFlags = _spriteTable[spriteId].flags;
if (value) {
_spriteTable[spriteId].flags |= kSFBackgroundRender;
} else {
_spriteTable[spriteId].flags &= ~kSFBackgroundRender;
}
if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setSpriteVertFlip(int spriteId, int flip) {
assertRange(1, spriteId, _maxSprites, "sprite");
int oldFlags = _spriteTable[spriteId].flags;
if (flip) {
_spriteTable[spriteId].flags |= kSFVFlip;
} else {
_spriteTable[spriteId].flags &= ~kSFVFlip;
}
if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setSpriteHorzFlip(int spriteId, int flip) {
assertRange(1, spriteId, _maxSprites, "sprite");
int oldFlags = _spriteTable[spriteId].flags;
if (flip) {
_spriteTable[spriteId].flags |= kSFHFlip;
} else {
_spriteTable[spriteId].flags &= ~kSFHFlip;
}
if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setSpriteActiveFlag(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (value) {
_spriteTable[spriteId].flags |= kSFActive;
} else {
_spriteTable[spriteId].flags &= ~kSFActive;
}
}
void Sprite::setSpriteImageRemapFlag(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
int oldFlags = _spriteTable[spriteId].flags;
if (value) {
_spriteTable[spriteId].flags |= kSFUseImageRemap;
} else {
_spriteTable[spriteId].flags &= ~kSFUseImageRemap;
}
if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
}
void Sprite::setSpriteAutoAnimFlag(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (value) {
_spriteTable[spriteId].flags |= kSFAutoAnimate;
} else {
_spriteTable[spriteId].flags &= ~kSFAutoAnimate;
}
}
void Sprite::setSpriteUpdateType(int spriteId, int eraseType) {
assertRange(1, spriteId, _maxSprites, "sprite");
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
switch (eraseType) {
default:
case SPRDEF_SMART:
_spriteTable[spriteId].flags |= kSFSmartRender | kSFDontCombineErase;
break;
case SPRDEF_DUMB:
_spriteTable[spriteId].flags &= ~(kSFSmartRender | kSFDontCombineErase);
break;
case SPRDEF_SIMPLE:
_spriteTable[spriteId].flags &= ~(kSFSmartRender);
_spriteTable[spriteId].flags |= kSFDontCombineErase;
break;
}
} else {
if (eraseType != 0) {
_spriteTable[spriteId].flags |= (kSFSmartRender | kSFDontCombineErase);
} else {
_spriteTable[spriteId].flags &= ~(kSFSmartRender | kSFDontCombineErase);
}
}
}
void Sprite::setSpriteEraseType(int spriteId, int eraseType) {
assertRange(1, spriteId, _maxSprites, "sprite");
// Note that condition is inverted!
if (!eraseType) {
_spriteTable[spriteId].flags |= kSFIgnoreErase;
} else {
_spriteTable[spriteId].flags &= ~kSFIgnoreErase;
}
}
void Sprite::setSpriteAnimSpeed(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].animSpeed = value;
_spriteTable[spriteId].animState = value;
}
void Sprite::setSpriteClass(int spriteId, int classId, int toggle) {
assertRange(1, spriteId, _maxSprites, "sprite");
assertRange(1, classId, 32, "class");
if (toggle) {
_spriteTable[spriteId].classFlags |= (1 << (classId - 1));
} else {
_spriteTable[spriteId].classFlags &= ~(1 << (classId - 1));
}
}
void Sprite::clearSpriteClasses(int spriteId) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].classFlags = 0;
}
void Sprite::setSpriteZBuffer(int spriteId, int value) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].zbufferImage = value;
}
void Sprite::setSpriteAnimSpeedState(int spriteId, int animState) {
assertRange(1, spriteId, _maxSprites, "sprite");
_spriteTable[spriteId].animState = MAX<int32>(0, MIN<int32>(animState, _spriteTable[spriteId].animSpeed));
}
void Sprite::setSpriteGeneralProperty(int spriteId, int property, int value) {
if (((ScummEngine_v90he *)_vm)->_logicHE && ((ScummEngine_v90he *)_vm)->_logicHE->setSpriteProperty(spriteId, property, value)) {
return;
}
debug(7, "setSpriteGeneralProperty: spriteId %d type 0x%x value 0x%x", spriteId, property, value);
assertRange(1, spriteId, _maxSprites, "sprite");
switch (property) {
case SPRPROP_SPECIAL_RENDER_FLAGS:
_spriteTable[spriteId].specialRenderFlags = value;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
break;
case SPRPROP_CONDITION_BITS:
_spriteTable[spriteId].conditionBits = value;
_spriteTable[spriteId].flags |= kSFErase | kSFRender;
break;
case SPRPROP_ANIMATION_SUB_STATE:
setSpriteAnimSpeedState(spriteId, value);
break;
default:
warning("Sprite::setSpriteGeneralProperty(): Unknown sprite property %d", property);
}
}
void Sprite::newSprite(int sprite) {
assertRange(1, sprite, _maxSprites, "sprite");
_spriteTable[sprite].angle = 0;
_spriteTable[sprite].scale = 0;
setSpriteImage(sprite, 0);
setSpriteShadow(sprite, 0);
setSpritePosition(sprite, 0, 0);
setSpriteHorzFlip(sprite, 0);
setSpriteVertFlip(sprite, 0);
setDelta(sprite, 0, 0);
setUserValue(sprite, 0, 0);
setSpriteGroup(sprite, 0);
setSpriteAnimSpeed(sprite, 0);
clearSpriteClasses(sprite);
if (_vm->_game.heversion > 98) {
setSpritePalette(sprite, 0);
setSourceImage(sprite, 0);
setMaskImage(sprite, 0);
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
setSpriteUpdateType(sprite, SPRDEF_SIMPLE);
setSpritePriority(sprite, 0);
setSpriteZBuffer(sprite, 0);
_spriteTable[sprite].flags |= kSFAutoAnimate;
_spriteTable[sprite].conditionBits = 0;
_spriteTable[sprite].specialRenderFlags = 0;
if (((ScummEngine_v90he *)_vm)->_logicHE)
((ScummEngine_v90he *)_vm)->_logicHE->spriteNewHook(sprite);
}
}
}
void Sprite::setImageList(int sprite, int count, const int *list) {
SpriteImageList *imageListPtr;
int counter;
assertRange(1, sprite, _maxSprites, "sprite");
// Release the last image list if one...
if (_spriteTable[sprite].imageList) {
releaseImageList(_spriteTable[sprite].imageList);
_spriteTable[sprite].imageList = 0;
}
// HE90+
int32 lastMaxStates = _spriteTable[sprite].maxStates;
int32 lastImage = _spriteTable[sprite].image;
if (count == 1) {
_spriteTable[sprite].image = *list;
} else {
_spriteTable[sprite].imageList = getFreeImageList(count);
imageListPtr = getImageListPtr(_spriteTable[sprite].imageList);
imageListPtr->count = count;
for (counter = 0; counter < count; counter++) {
imageListPtr->list[counter] = *list++;
}
_spriteTable[sprite].image = imageListPtr->list[0];
}
_spriteTable[sprite].animIndex = 0;
_spriteTable[sprite].state = 0; // HE90+
if (_spriteTable[sprite].image) {
if (_vm->_game.heversion == 80) {
_spriteTable[sprite].flags |=
(kSFActive | kSFAutoAnimate |
kSFRender | kSFErase);
} else {
_spriteTable[sprite].maxStates = _vm->_wiz->getWizStateCount(_spriteTable[sprite].image);
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
if (_vm->VAR(_vm->VAR_SPRITE_IMAGE_CHANGE_DOES_NOT_RESET_SETTINGS)) {
_spriteTable[sprite].flags |= kSFActive;
} else {
_spriteTable[sprite].flags |= kSFDefaultFlagActive;
}
} else {
_spriteTable[sprite].flags |= kSFDefaultFlagActive;
}
if (_vm->_game.heversion > 80 &&
((lastImage != _spriteTable[sprite].image) || (_spriteTable[sprite].maxStates != lastMaxStates))) {
_spriteTable[sprite].flags |= (kSFRender | kSFErase);
}
}
} else {
if (_vm->_game.heversion >= 98 && _vm->_game.heversion < 100) {
if (_spriteTable[sprite].flags & kSFIgnoreErase) {
_spriteTable[sprite].flags = 0;
} else {
_spriteTable[sprite].flags = kSFDefaultFlagInactive;
}
} else if (_vm->_game.heversion > 99 || _vm->_isHE995) {
if (_vm->VAR(_vm->VAR_SPRITE_IMAGE_CHANGE_DOES_NOT_RESET_SETTINGS)) {
_spriteTable[sprite].flags &= ~kSFActive;
} else {
if (lastImage) {
if (_spriteTable[sprite].flags & kSFIgnoreErase) {
_spriteTable[sprite].flags = 0;
} else {
_spriteTable[sprite].flags = kSFDefaultFlagInactive;
}
} else {
_spriteTable[sprite].flags = 0;
}
}
} else {
_spriteTable[sprite].flags = (_vm->_game.heversion == 80 ? kSFErase : kSFDefaultFlagInactive);
}
_spriteTable[sprite].lastImage = 0;
// HE90+
_spriteTable[sprite].lastState = 0;
_spriteTable[sprite].maxStates = 0;
}
}
void Sprite::setSpriteImage(int sprite, int imageNum) {
setImageList(sprite, 1, &imageNum);
}
//
// spriteGroupSet functions
//
void Sprite::orInGroupMembersFlags(int spriteGroupId, int32 flags) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 0; i < _activeSpriteCount; ++i) {
SpriteInfo *spi = _activeSprites[i];
if (spi->group == spriteGroupId) {
spi->flags |= flags;
}
}
}
void Sprite::moveGroupMembers(int spriteGroupId, int value1, int value2) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId) {
_spriteTable[i].posX += value1;
_spriteTable[i].posY += value2;
if (value1 || value2)
_spriteTable[i].flags |= kSFErase | kSFRender;
}
}
}
void Sprite::setGroupMembersPriority(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId)
_spriteTable[i].priority = value;
}
}
void Sprite::changeGroupMembersGroup(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId) {
_spriteTable[i].group = value;
_spriteTable[i].flags |= kSFErase | kSFRender;
}
}
}
void Sprite::setGroupMembersUpdateType(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId)
setSpriteUpdateType(i, value);
}
}
void Sprite::performNewOnGroupMembers(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId)
newSprite(i);
}
}
void Sprite::setGroupMembersAnimationSpeed(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId) {
_spriteTable[i].animSpeed = value;
_spriteTable[i].animState = value;
}
}
}
void Sprite::setGroupMembersAutoAnimFlag(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId) {
if (value)
_spriteTable[i].flags |= kSFAutoAnimate;
else
_spriteTable[i].flags &= ~kSFAutoAnimate;
}
}
}
void Sprite::setGroupMembersShadow(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
for (int i = 1; i < _maxSprites; i++) {
if (_spriteTable[i].group == spriteGroupId) {
_spriteTable[i].shadow = value;
if (_spriteTable[i].image)
_spriteTable[i].flags |= kSFErase | kSFRender;
}
}
}
void Sprite::setGroupClipRect(int spriteGroupId, int x1, int y1, int x2, int y2) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
_groupTable[spriteGroupId].flags |= kSGFUseClipRect;
_groupTable[spriteGroupId].clipRect.left = x1;
_groupTable[spriteGroupId].clipRect.top = y1;
_groupTable[spriteGroupId].clipRect.right = x2;
_groupTable[spriteGroupId].clipRect.bottom = y2;
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
void Sprite::setGroupPriority(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (_groupTable[spriteGroupId].priority != value) {
_groupTable[spriteGroupId].priority = value;
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupPoint(int spriteGroupId, int x, int y) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (_groupTable[spriteGroupId].posX != x || _groupTable[spriteGroupId].posY != y) {
_groupTable[spriteGroupId].posX = x;
_groupTable[spriteGroupId].posY = y;
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::moveGroup(int spriteGroupId, int dx, int dy) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (dx || dy) {
_groupTable[spriteGroupId].posX += dx;
_groupTable[spriteGroupId].posY += dy;
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupImage(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (_groupTable[spriteGroupId].image != value) {
_groupTable[spriteGroupId].image = value;
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupScaling(int spriteGroupId) {
if ((_groupTable[spriteGroupId].xMul != _groupTable[spriteGroupId].xDiv) || (_groupTable[spriteGroupId].yMul != _groupTable[spriteGroupId].yDiv))
_groupTable[spriteGroupId].isScaled = 1;
else
_groupTable[spriteGroupId].isScaled = 0;
}
void Sprite::setGroupXMul(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (_groupTable[spriteGroupId].xMul != value) {
_groupTable[spriteGroupId].xMul = value;
_groupTable[spriteGroupId].xScale = (float)_groupTable[spriteGroupId].xMul / (float)_groupTable[spriteGroupId].xDiv;
setGroupScaling(spriteGroupId);
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupXDiv(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (value == 0)
error("setGroupXDiv: Divisor must not be 0");
if (_groupTable[spriteGroupId].xDiv != value) {
_groupTable[spriteGroupId].xDiv = value;
_groupTable[spriteGroupId].xScale = (float)_groupTable[spriteGroupId].xMul / (float)_groupTable[spriteGroupId].xDiv;
setGroupScaling(spriteGroupId);
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupYMul(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (_groupTable[spriteGroupId].yMul != value) {
_groupTable[spriteGroupId].yMul = value;
_groupTable[spriteGroupId].yScale = (float)_groupTable[spriteGroupId].yMul / (float)_groupTable[spriteGroupId].yDiv;
setGroupScaling(spriteGroupId);
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::setGroupYDiv(int spriteGroupId, int value) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
if (value == 0)
error("setGroupYDiv: Divisor must not be 0");
if (_groupTable[spriteGroupId].yDiv != value) {
_groupTable[spriteGroupId].yDiv = value;
_groupTable[spriteGroupId].yScale = (float)_groupTable[spriteGroupId].yMul / (float)_groupTable[spriteGroupId].yDiv;
setGroupScaling(spriteGroupId);
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
}
void Sprite::clearGroupClipRect(int spriteGroupId) {
assertRange(1, spriteGroupId, _maxSpriteGroups, "sprite group");
_groupTable[spriteGroupId].flags &= ~(kSGFUseClipRect);
orInGroupMembersFlags(spriteGroupId, (kSFRender | kSFErase));
}
void Sprite::clearGroupScaleInfo(int group) {
assertRange(1, group, _maxSpriteGroups, "sprite group");
_groupTable[group].isScaled = 0;
_groupTable[group].xScale = 1.0;
_groupTable[group].xMul = 1;
_groupTable[group].xDiv = 1;
_groupTable[group].yScale = 1.0;
_groupTable[group].yMul = 1;
_groupTable[group].yDiv = 1;
}
void Sprite::initializeStuff(int spriteCount, int groupCount, int imageListCount) {
_activeSpriteCount = 0;
_maxSpriteGroups = groupCount;
_maxSprites = spriteCount;
_maxImageLists = imageListCount;
_groupTable = (SpriteGroup *)malloc((_maxSpriteGroups + 1) * sizeof(SpriteGroup));
_spriteTable = (SpriteInfo *)malloc((_maxSprites + 1) * sizeof(SpriteInfo));
_activeSprites = (SpriteInfo **)malloc((_maxSprites + 1) * sizeof(SpriteInfo *));
_imageLists = (SpriteImageList *)malloc((_maxImageLists + 1) * sizeof(SpriteImageList));
_imageListStack = (int16 *)malloc((_maxImageLists + 1) * sizeof(int16));
resetSpriteSystem(false);
}
void Sprite::newGroup(int group) {
assertRange(1, group, _maxSpriteGroups, "sprite group");
setGroupPriority(group, 0);
setGroupPoint(group, 0, 0);
clearGroupClipRect(group);
setGroupImage(group, 0);
clearGroupScaleInfo(group);
if (((ScummEngine_v90he *)_vm)->_logicHE) {
((ScummEngine_v90he *)_vm)->_logicHE->groupNewHook(group);
}
}
void Sprite::resetSpriteSystem(bool eraseScreen) {
resetImageLists();
for (int i = 0; i < _maxSprites; i++) {
_spriteTable[i].reset();
}
for (int i = 0; i < _maxSpriteGroups; i++) {
_groupTable[i].reset();
}
if (_vm->_game.heversion >= 98) {
for (int i = 1; i < _maxSprites; i++) {
newSprite(i);
}
for (int i = 1; i < _maxSpriteGroups; i++) {
newGroup(i);
}
}
if (_vm->_game.heversion > 80) {
if (eraseScreen) {
_vm->backgroundToForegroundBlit(Common::Rect(_vm->_screenWidth - 1, _vm->_screenHeight - 1));
}
} else {
_vm->_completeScreenRedraw = true;
}
_activeSpriteCount = 0;
}
void Sprite::resetImageLists() {
_imageListStackIndex = _maxImageLists;
for (int i = 0; i < _maxImageLists; i++) {
_imageListStack[i] = i;
}
}
SpriteImageList *Sprite::getImageListPtr(int imageList) {
assertRange(1, imageList, _maxImageLists, "sprite image list");
return &_imageLists[imageList - 1];
}
int Sprite::getFreeImageList(int imageCount) {
if (!_imageListStackIndex) {
error("Sprite::getFreeImageList(): Out of image lists");
}
if (imageCount > 32) {
error("Sprite::getFreeImageList(): Too many images in image list (%d)!", imageCount);
}
--_imageListStackIndex;
return (_imageListStack[_imageListStackIndex] + 1);
}
void Sprite::releaseImageList(int imageList) {
assertRange(1, imageList, _maxImageLists, "sprite image list");
_imageListStack[_imageListStackIndex++] = (imageList - 1);
}
void Sprite::eraseSprites() {
Common::Rect *lastRectPtr;
Common::Rect eraseRect;
SpriteInfo **spritePtr;
bool first, valid;
int32 flags;
eraseRect.left = 1234;
eraseRect.top = 1234;
eraseRect.right = -1234;
eraseRect.bottom = -1234;
spritePtr = _activeSprites;
first = true;
valid = false;
for (int i = 0; i < _activeSpriteCount; i++) {
// Get the current flags & clear the erase flag...
flags = spritePtr[i]->flags;
spritePtr[i]->flags &= ~kSFErase;
// Check to see if this sprite should erase it's last rect...
if (!(flags & kSFErase))
continue;
if (_vm->_game.heversion >= 98) {
if (flags & kSFIgnoreErase)
continue;
}
// Combine the rect with the erase rect...
lastRectPtr = &spritePtr[i]->lastRect;
if (_vm->_wiz->isRectValid(*lastRectPtr)) {
if (_vm->_game.heversion < 90 || !(flags & kSFDontCombineErase)) {
if (!first) {
_vm->_wiz->combineRects(&eraseRect, &eraseRect, lastRectPtr);
} else {
eraseRect = *lastRectPtr;
first = false;
}
valid = true;
} else {
_vm->backgroundToForegroundBlit(*lastRectPtr, USAGE_BIT_RESTORED);
}
if (_vm->_game.version >= 90) {
if (!(flags & (kSFIgnoreErase | kSFRender))) {
if (spritePtr[i]->image) {
spritePtr[i]->flags |= kSFRender;
}
}
}
}
}
// Erase the cumulative sprites rectangle...
if (valid) {
_vm->backgroundToForegroundBlit(eraseRect, USAGE_BIT_RESTORED);
}
}
bool Sprite::doesRectIntersectUpdateAreas(const Common::Rect *rectPtr) {
int sMin, sMax, y1, y2, minValue;
VirtScreen *vs = &_vm->_virtscr[kMainVirtScreen];
int strips = _vm->_gdi->_numStrips;
int stripsBytes = 8;
y1 = rectPtr->top;
y2 = rectPtr->bottom;
sMin = rectPtr->left / stripsBytes;
sMin = MAX(0, MIN(sMin, (strips - 1)));
sMax = (rectPtr->right + stripsBytes - 1) / stripsBytes;
sMax = MAX(0, MIN(sMax, (strips - 1)));
for (int i = sMin; i <= sMax; i++) {
minValue = vs->tdirty[i];
if (minValue >= vs->h)
continue;
if (y2 < minValue)
continue;
if (y1 > vs->bdirty[i])
continue;
return true;
}
return false;
}
void Sprite::checkForForcedRedraws(bool checkOnlyPositivePriority) {
if (_vm->_game.heversion < 90)
return;
SpriteInfo **spritePtr;
int32 flags;
spritePtr = _activeSprites;
for (int i = 0; i < _activeSpriteCount; i++) {
flags = spritePtr[i]->flags;
if (flags & (kSFRender | kSFIgnoreRender)) {
continue;
}
// NOTE: Apparently some HE90 games still have this in their code...
//if (_vm->_game.heversion > 90) {
if (checkOnlyPositivePriority && (spritePtr[i]->priority < 0)) {
continue;
}
//}
if (kSFSmartRender & flags) {
if (doesRectIntersectUpdateAreas(&spritePtr[i]->lastRect)) {
spritePtr[i]->flags |= kSFRender;
}
}
}
}
void Sprite::runSpriteEngines() {
int lastIndex, index, lastState;
SpriteImageList *imageListPtr;
SpriteInfo **spritePtr;
int32 flags;
spritePtr = _activeSprites;
for (int i = 0; i < _activeSpriteCount; i++) {
// Handle movement...
if (spritePtr[i]->deltaPosX || spritePtr[i]->deltaPosY) {
moveSprite(
spritePtr[i] - _spriteTable,
spritePtr[i]->deltaPosX, spritePtr[i]->deltaPosY);
}
// Handle animation...
flags = spritePtr[i]->flags;
if (flags & kSFAutoAnimate) {
// Check to see if the speed is set!
if (_vm->_game.heversion >= 95) {
if (spritePtr[i]->animSpeed) {
if (--spritePtr[i]->animState) {
continue;
}
spritePtr[i]->animState = spritePtr[i]->animSpeed;
}
}
// Handle "state" animation...
lastState = spritePtr[i]->state;
spritePtr[i]->state++;
if ((spritePtr[i]->state >= spritePtr[i]->maxStates)) {
spritePtr[i]->state = 0;
if (!spritePtr[i]->imageList) {
if (lastState) {
spritePtr[i]->flags |= (kSFRender | kSFErase);
}
} else {
// Handle image list animation...
if (!(flags & kSFDontAnimImageList)) {
imageListPtr = getImageListPtr(spritePtr[i]->imageList);
index = lastIndex = spritePtr[i]->animIndex;
if ((++index) >= imageListPtr->count) {
index = 0;
}
if (lastIndex != index) {
spritePtr[i]->animIndex = index;
spritePtr[i]->image = imageListPtr->list[index];
spritePtr[i]->flags |= (kSFRender | kSFErase);
spritePtr[i]->maxStates = _vm->_wiz->getWizStateCount(spritePtr[i]->image);
}
}
}
} else {
spritePtr[i]->flags |= (kSFRender | kSFErase);
}
}
}
}
static int compareSpriteCombinedPriority(const void *a, const void *b) {
const SpriteInfo *spr1 = *(const SpriteInfo *const*)a;
const SpriteInfo *spr2 = *(const SpriteInfo *const*)b;
return spr1->combinedPriority - spr2->combinedPriority;
}
static int compareSpritePriority(const void *a, const void *b) {
const SpriteInfo *spr1 = *(const SpriteInfo *const *)a;
const SpriteInfo *spr2 = *(const SpriteInfo *const *)b;
return spr1->priority - spr2->priority;
}
#define SWAP_SPRITE_PTRS(e1, e2, te) { \
te = *e1; \
*e1 = *e2; \
*e2 = te; \
}
#define SPRITE_QSORT_CUTOFF 8
void Sprite::qsortSpriteArray(SpriteInfo **base, uint num) {
SpriteInfo **lostk[30], **histk[30];
SpriteInfo **loguy, **higuy;
SpriteInfo **lo, **hi;
SpriteInfo **mid;
SpriteInfo *t;
uint size;
int stkptr;
if (num < 2)
return;
stkptr = 0;
lo = base;
hi = base + (num - 1);
recurse:
size = (hi - lo) + 1;
if (size <= SPRITE_QSORT_CUTOFF) {
shortsortSpriteArray(lo, hi);
} else {
mid = lo + (size / 2);
SWAP_SPRITE_PTRS(mid, lo, t);
loguy = lo;
higuy = hi + 1;
for (;;) {
do {
++loguy;
} while (loguy <= hi && compareSpriteCombinedPriority(const_cast<SpriteInfo **>(loguy), const_cast<SpriteInfo **>(lo)) <= 0);
do {
--higuy;
} while (higuy > lo && compareSpriteCombinedPriority(const_cast<SpriteInfo **>(higuy), const_cast<SpriteInfo **>(lo)) >= 0);
if (higuy < loguy)
break;
SWAP_SPRITE_PTRS(loguy, higuy, t);
}
SWAP_SPRITE_PTRS(lo, higuy, t);
if (higuy - 1 - lo >= hi - loguy) {
if (lo + 1 < higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy - 1;
++stkptr;
}
if (loguy < hi) {
lo = loguy;
goto recurse;
}
} else {
if (loguy < hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr;
}
if (lo + 1 < higuy) {
hi = higuy - 1;
goto recurse;
}
}
}
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse;
} else {
return;
}
}
void Sprite::shortsortSpriteArray(SpriteInfo **lo, SpriteInfo **hi) {
SpriteInfo **p, **max, *t;
while (hi > lo) {
max = lo;
for (p = lo + 1; p <= hi; p++) {
if (compareSpriteCombinedPriority(const_cast<SpriteInfo **>(p), const_cast<SpriteInfo **>(max)) > 0) {
max = p;
}
}
SWAP_SPRITE_PTRS(max, hi, t);
hi--;
}
}
#undef SPRITE_QSORT_CUTOFF
#undef SWAP_SPRITE_PTRS
void Sprite::buildActiveSpriteList() {
SpriteInfo **spritePtr;
// Build the list of active sprites...
_activeSpriteCount = 0;
spritePtr = _activeSprites;
for (int i = 1; i < _maxSprites; i++) {
if (!(_spriteTable[i].flags & kSFActive))
continue;
if (!(_spriteTable[i].flags & kSFSmartRender)) {
if (!(_spriteTable[i].flags & kSFIgnoreRender)) {
_spriteTable[i].flags |= kSFRender;
}
if (!(_spriteTable[i].flags & kSFIgnoreErase)) {
_spriteTable[i].flags |= kSFErase;
}
}
if (_vm->_game.heversion > 95) {
_spriteTable[i].combinedPriority = (_spriteTable[i].priority +
(((_spriteTable[i].group) ? _groupTable[_spriteTable[i].group].priority : 0)));
}
*spritePtr++ = &_spriteTable[i];
_activeSpriteCount++;
}
// Sort the list of active sprites...
if (_activeSpriteCount) {
if (_vm->_game.heversion > 95) {
qsortSpriteArray(_activeSprites, _activeSpriteCount);
} else {
qsort(_activeSprites, _activeSpriteCount, sizeof(SpriteInfo *), compareSpritePriority);
}
}
}
void Sprite::renderSprites(bool negativeOrPositiveRender) {
int image, group, shadow, state, angle;
int sourceImage, scale, destImageNumber;
WizImageCommand imageRenderCmd;
Common::Rect clippedLastRect;
Common::Rect *clipRectPtr;
SpriteInfo **spritePtr;
int32 flags, renderFlags;
bool angleSpecified;
bool scaleSpecified;
Common::Point spot;
bool simpleDraw = false;
imageRenderCmd.reset();
spritePtr = _activeSprites;
for (int i = 0; i < _activeSpriteCount; i++) {
// Get the current flags & clear the render flag...
flags = spritePtr[i]->flags;
// Check to see if this sprite needs to render...
if (!(flags & kSFRender)) {
continue;
}
// This code is how we draw the negative priority sprites first.
// If it reaches a positive priority it stops, because the active list
// should be sorted by this point...
if (negativeOrPositiveRender) {
if (_vm->_game.heversion > 95) {
if (spritePtr[i]->combinedPriority >= 0) {
break;
}
} else {
if (spritePtr[i]->priority >= 0) {
break;
}
}
} else {
if (_vm->_game.heversion > 95) {
if (spritePtr[i]->combinedPriority < 0) {
continue;
}
} else {
if (spritePtr[i]->priority < 0) {
continue;
}
}
}
// Clear the sprite render flag...
spritePtr[i]->flags &= ~kSFRender;
// Get the current image and the rect it will render to...
image = spritePtr[i]->image;
state = spritePtr[i]->state;
int32 spotX = 0, spotY = 0;
calcSpriteSpot(spritePtr[i], true, spotX, spotY);
// In the hope that it never overflows...
spot.x = (int16)spotX;
spot.y = (int16)spotY;
if (_vm->_game.heversion > 98) {
// Setup the image render command...
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
imageRenderCmd.extendedRenderInfo.sprite = (int32)(spritePtr[i] - _spriteTable);
imageRenderCmd.extendedRenderInfo.group = spritePtr[i]->group;
imageRenderCmd.extendedRenderInfo.conditionBits = spritePtr[i]->conditionBits;
}
imageRenderCmd.actionFlags = kWAFSpot;
imageRenderCmd.xPos = spot.x;
imageRenderCmd.yPos = spot.y;
imageRenderCmd.actionFlags |= kWAFState;
imageRenderCmd.image = image;
imageRenderCmd.state = state;
spritePtr[i]->lastAngle = spritePtr[i]->angle;
spritePtr[i]->lastScale = spritePtr[i]->scale;
spritePtr[i]->lastImage = image;
spritePtr[i]->lastState = state;
spritePtr[i]->lastSpot = spot;
// The the potential update rect (we'll clip it later)
getSpriteRectPrim(spritePtr[i], &spritePtr[i]->lastRect, true, &spot);
} else {
spritePtr[i]->lastImage = image;
spritePtr[i]->lastState = state;
spritePtr[i]->lastSpot = spot;
int32 w, h;
_vm->_wiz->getWizImageDim(image, state, w, h);
spritePtr[i]->lastRect.left = spot.x;
spritePtr[i]->lastRect.top = spot.y;
spritePtr[i]->lastRect.right = spot.x + w - 1;
spritePtr[i]->lastRect.bottom = spot.y + h - 1;
}
// Setup the renderFlags...
renderFlags = kWRFForeground;
if (flags & kSFHFlip) {
renderFlags |= kWRFHFlip;
}
if (flags & kSFVFlip) {
renderFlags |= kWRFVFlip;
}
if (_vm->_game.heversion >= 95) {
if (flags & kSFBackgroundRender) {
renderFlags &= ~kWRFForeground;
renderFlags |= kWRFBackground;
}
}
if (_vm->_game.heversion > 98) {
// Check to see if there is any shadow attached to this sprite...
shadow = spritePtr[i]->shadow;
if (shadow != 0) {
renderFlags |= kWRFUseShadow;
imageRenderCmd.actionFlags |= kWAFShadow;
imageRenderCmd.shadow = shadow;
}
}
if (_vm->_game.heversion >= 95) {
// Check to see if the sprite is supposed to remap...
if (flags & kSFUseImageRemap) {
renderFlags |= kWRFRemap;
}
}
if (_vm->_game.heversion > 98) {
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
// Handle Z-Clipping...
if (spritePtr[i]->zbufferImage != 0) {
imageRenderCmd.actionFlags |= kWAFZBufferImage;
imageRenderCmd.zbufferImage = spritePtr[i]->zbufferImage;
imageRenderCmd.zPos = spritePtr[i]->priority;
}
}
// Set the source image...
sourceImage = spritePtr[i]->sourceImage;
if (sourceImage != 0) {
imageRenderCmd.actionFlags |= kWAFSourceImage;
imageRenderCmd.sourceImage = sourceImage;
}
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
renderFlags |= spritePtr[i]->specialRenderFlags;
}
// Finally set the image render flags...
imageRenderCmd.actionFlags |= kWAFFlags;
imageRenderCmd.flags = renderFlags;
// Read the angle/scale variables...
angle = spritePtr[i]->angle;
scale = spritePtr[i]->scale;
scaleSpecified = (spritePtr[i]->flags & kSFScaleSpecified);
angleSpecified = (spritePtr[i]->flags & kSFAngleSpecified);
// Check for "complex" image draw mode...
if (angleSpecified) {
imageRenderCmd.actionFlags |= kWAFAngle;
imageRenderCmd.angle = angle;
}
if (scaleSpecified) {
imageRenderCmd.actionFlags |= kWAFScale;
imageRenderCmd.scale = scale;
}
// Store off the render flags...
spritePtr[i]->lastRenderFlags = renderFlags;
} else {
// Check for complex image draw mode...
simpleDraw = true;
angle = spritePtr[i]->angle;
scale = spritePtr[i]->scale;
if (angle != 0) {
simpleDraw = false;
}
if (scale != 0) {
simpleDraw = false;
}
}
simpleDraw = _vm->_game.heversion <= 95 ? true : simpleDraw;
// Check to see if the group has a clipping rect...
group = spritePtr[i]->group;
if (group != 0) {
if (_groupTable[group].flags & kSGFUseClipRect) {
if (_vm->_game.heversion > 98) {
if (!_vm->_wiz->findRectOverlap(&spritePtr[i]->lastRect, &_groupTable[group].clipRect)) {
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
spritePtr[i]->lastRect.left = 1234;
spritePtr[i]->lastRect.top = 1234;
spritePtr[i]->lastRect.right = -1234;
spritePtr[i]->lastRect.bottom = -1234;
}
continue;
}
// Setup the clipping rect to the overlap rect...
// This will eventually be clipped down to the limits of the bitmap!
imageRenderCmd.actionFlags |= kWAFRect;
imageRenderCmd.box.left = spritePtr[i]->lastRect.left;
imageRenderCmd.box.top = spritePtr[i]->lastRect.top;
imageRenderCmd.box.right = spritePtr[i]->lastRect.right;
imageRenderCmd.box.bottom = spritePtr[i]->lastRect.bottom;
clippedLastRect = spritePtr[i]->lastRect;
clipRectPtr = &clippedLastRect;
} else {
if (simpleDraw) {
if (!_vm->_wiz->findRectOverlap(&spritePtr[i]->lastRect, &_groupTable[group].clipRect)) {
spritePtr[i]->lastRect.left = 1234;
spritePtr[i]->lastRect.top = 1234;
spritePtr[i]->lastRect.right = -1234;
spritePtr[i]->lastRect.bottom = -1234;
continue;
}
}
clipRectPtr = &_groupTable[group].clipRect;
}
} else {
clipRectPtr = nullptr;
}
} else {
clipRectPtr = nullptr;
}
if (_vm->_game.heversion > 98) {
// Get the palette...
if (spritePtr[i]->palette != 0) {
imageRenderCmd.actionFlags |= kWAFPalette;
imageRenderCmd.palette = spritePtr[i]->palette;
}
// Get the associated dest image if any...
destImageNumber = getDestImageForSprite(spritePtr[i]);
if (destImageNumber) {
imageRenderCmd.actionFlags |= kWAFDestImage;
imageRenderCmd.destImageNumber = destImageNumber;
}
// Finally actually do something by calling the command parser...
if (_vm->_game.heversion > 99 || _vm->_isHE995) {
imageRenderCmd.actionType = kWADraw;
_vm->_wiz->processWizImageCmd(&imageRenderCmd);
} else {
_vm->_wiz->processWizImageDrawCmd(&imageRenderCmd);
}
} else {
// Check to see if there is a shadow attached to this sprite...
shadow = spritePtr[i]->shadow;
if (shadow != 0) {
renderFlags |= kWRFUseShadow;
}
WizSimpleBitmap *bitmapPtr = nullptr;
WizSimpleBitmap simpleBitmap;
if (_vm->_game.heversion >= 95) {
// This was originally an inlined function, getSimpleBitmapForSprite().
// I got fed up with the fact it returned the address of a local WizSimpleBitmap variable,
// and it was used here and only here, so I decided to get rid of it and just unroll it...
if (spritePtr[i]->image != 0 && spritePtr[i]->group != 0 && _groupTable[group].image != 0) {
if (_vm->_wiz->dwSetSimpleBitmapStructFromImage(_groupTable[group].image, 0, &simpleBitmap)) {
bitmapPtr = &simpleBitmap;
}
}
}
if (simpleDraw) {
_vm->_wiz->drawAWizPrim(image, state, spot.x, spot.y, 0, shadow, 0, clipRectPtr, renderFlags, bitmapPtr, nullptr);
} else {
_vm->_wiz->dwHandleComplexImageDraw(
image, state, spot.x, spot.y, shadow,
angle, scale, clipRectPtr, renderFlags, nullptr, nullptr);
}
}
}
}
int Sprite::pixelPerfectSpriteCollisionCheck(int spriteA, int deltaAX, int deltaAY, int spriteB, int deltaBX, int deltaBY) {
int overlapWidth, overlapHeight;
int imageA, imageB, stateA, stateB;
int32 wizAFlags, wizBFlags, flags;
Common::Rect rectA, rectB, originalA, originalB;
int imageAType, imageBType;
int aWidth, aHeight;
int bWidth, bHeight;
WizRawPixel transparentColor;
int32 spotAX, spotAY, spotBX, spotBY;
const byte *imageAHeader;
const byte *imageAData;
const byte *imageBHeader;
const byte *imageBData;
assertRange(1, spriteA, _maxSprites, "sprite");
assertRange(1, spriteB, _maxSprites, "sprite");
// Get the current potential draw rect...
getSpriteDrawRect(spriteA, &originalA);
getSpriteDrawRect(spriteB, &originalB);
if (!_vm->_wiz->isRectValid(originalA) || !_vm->_wiz->isRectValid(originalB)) {
return 0;
}
_vm->_wiz->moveRect(&originalA, deltaAX, deltaAY);
_vm->_wiz->moveRect(&originalB, deltaBX, deltaBY);
// Find the overlap if any...
rectA = originalA;
if (!_vm->_wiz->findRectOverlap(&rectA, &originalB)) {
return 0;
}
rectB = rectA;
// Adjust the coords to be image relative...
calcSpriteSpot(&_spriteTable[spriteA], true, spotAX, spotAY);
calcSpriteSpot(&_spriteTable[spriteB], true, spotBX, spotBY);
_vm->_wiz->moveRect(&rectA, -spotAX - deltaAX, -spotAY - deltaAY);
_vm->_wiz->moveRect(&rectB, -spotBX - deltaBX, -spotBY - deltaBY);
// Limit the compare to only the compare buffer size...
overlapWidth = _vm->_wiz->getRectWidth(&rectA);
if (overlapWidth > 640) {
overlapWidth = 640;
}
overlapHeight = _vm->_wiz->getRectHeight(&rectA);
transparentColor = (WizRawPixel)_vm->VAR(_vm->VAR_WIZ_TRANSPARENT_COLOR);
// Get the image / state here...
imageA = (_spriteTable[spriteA].image);
stateA = (_spriteTable[spriteA].state);
imageB = (_spriteTable[spriteB].image);
stateB = (_spriteTable[spriteB].state);
// Get image A's data...
imageAHeader = _vm->_wiz->getWizStateHeaderPrim(imageA, stateA);
imageAData = _vm->_wiz->getWizStateDataPrim(imageA, stateA);
imageAData += _vm->_resourceHeaderSize;
// Read A's header...
imageAType = READ_LE_UINT32(imageAHeader + _vm->_resourceHeaderSize + 0);
aWidth = READ_LE_UINT32(imageAHeader + _vm->_resourceHeaderSize + 4);
aHeight = READ_LE_UINT32(imageAHeader + _vm->_resourceHeaderSize + 8);
if ((imageAType != kWCTNone) && (imageAType != kWCTTRLE)) {
error("%d has invalid compression type %d", imageA, imageAType);
}
// Get the render flag options...
flags = _spriteTable[spriteA].flags;
wizAFlags = 0;
if (flags & kSFHFlip) {
wizAFlags |= kWRFHFlip;
}
if (flags & kSFVFlip) {
wizAFlags |= kWRFVFlip;
}
// Get image B's data...
imageBHeader = _vm->_wiz->getWizStateHeaderPrim(imageB, stateB);
imageBData = _vm->_wiz->getWizStateDataPrim(imageB, stateB);
imageBData += _vm->_resourceHeaderSize;
// Read B's header...
imageBType = READ_LE_UINT32(imageBHeader + _vm->_resourceHeaderSize + 0);
bWidth = READ_LE_UINT32(imageBHeader + _vm->_resourceHeaderSize + 4);
bHeight = READ_LE_UINT32(imageBHeader + _vm->_resourceHeaderSize + 8);
if ((imageBType != kWCTNone) && (imageBType != kWCTTRLE)) {
error("%d has invalid compression type %d", imageB, imageBType);
}
// Get the render flag options...
flags = _spriteTable[spriteB].flags;
wizBFlags = 0;
if (flags & kSFHFlip) {
wizBFlags |= kWRFHFlip;
}
if (flags & kSFVFlip) {
wizBFlags |= kWRFVFlip;
}
// Get down to business :-)
for (int yCounter = 0; yCounter < overlapHeight; yCounter++) {
if (_vm->_wiz->collisionCompareImageLines(
imageAData, imageAType, aWidth, aHeight, wizAFlags, rectA.left, rectA.top,
imageBData, imageBType, bWidth, bHeight, wizBFlags, rectB.left, rectB.top,
overlapWidth, transparentColor)) {
return 1;
}
// Advance to the next line...
++rectA.top;
++rectB.top;
}
return 0;
}
static void syncWithSerializer(Common::Serializer &s, SpriteInfo &si) {
s.syncAsSint32LE(si.id, VER(48));
s.syncAsSint32LE(si.combinedPriority, VER(48));
s.syncAsSint32LE(si.flags, VER(48));
s.syncAsSint32LE(si.image, VER(48));
s.syncAsSint32LE(si.state, VER(48));
s.syncAsSint32LE(si.group, VER(48));
s.syncAsSint32LE(si.palette, VER(48));
s.syncAsSint32LE(si.priority, VER(48));
s.syncAsSint32LE(si.lastRect.left, VER(48));
s.syncAsSint32LE(si.lastRect.top, VER(48));
s.syncAsSint32LE(si.lastRect.right, VER(48));
s.syncAsSint32LE(si.lastRect.bottom, VER(48));
s.syncAsSint32LE(si.deltaPosX, VER(48));
s.syncAsSint32LE(si.deltaPosY, VER(48));
s.syncAsSint32LE(si.lastSpot.x, VER(48));
s.syncAsSint32LE(si.lastSpot.y, VER(48));
s.syncAsSint32LE(si.posX, VER(48));
s.syncAsSint32LE(si.posY, VER(48));
s.syncAsSint32LE(si.userValue, VER(48));
s.syncAsSint32LE(si.lastState, VER(48));
s.syncAsSint32LE(si.lastImage, VER(48));
s.syncAsSint32LE(si.imageList, VER(48));
s.syncAsSint32LE(si.shadow, VER(48));
s.syncAsSint32LE(si.maxStates, VER(48));
s.syncAsSint32LE(si.angle, VER(48));
s.syncAsSint32LE(si.scale, VER(48));
s.syncAsSint32LE(si.animState, VER(48));
s.syncAsSint32LE(si.lastAngle, VER(48));
s.syncAsSint32LE(si.lastScale, VER(48));
s.syncAsSint32LE(si.lastRenderFlags, VER(48));
s.syncAsSint32LE(si.animIndex, VER(48));
s.syncAsSint32LE(si.animSpeed, VER(48));
s.syncAsSint32LE(si.sourceImage, VER(48));
s.syncAsSint32LE(si.maskImage, VER(48));
s.syncAsSint32LE(si.zbufferImage, VER(48));
s.syncAsSint32LE(si.classFlags, VER(48));
s.syncAsSint32LE(si.specialRenderFlags, VER(48));
s.syncAsSint32LE(si.conditionBits, VER(48));
}
static void syncWithSerializer(Common::Serializer &s, SpriteGroup &sg) {
s.syncAsSint32LE(sg.clipRect.left, VER(48));
s.syncAsSint32LE(sg.clipRect.top, VER(48));
s.syncAsSint32LE(sg.clipRect.right, VER(48));
s.syncAsSint32LE(sg.clipRect.bottom, VER(48));
s.syncAsSint32LE(sg.priority, VER(48));
s.syncAsSint32LE(sg.flags, VER(48));
s.syncAsSint32LE(sg.posX, VER(48));
s.syncAsSint32LE(sg.posY, VER(48));
s.syncAsSint32LE(sg.image, VER(48));
s.syncAsSint32LE(sg.isScaled, VER(48));
s.syncAsSint32LE(sg.xMul, VER(48));
s.syncAsSint32LE(sg.xDiv, VER(48));
s.syncAsSint32LE(sg.yMul, VER(48));
s.syncAsSint32LE(sg.yDiv, VER(48));
}
static void syncWithSerializer(Common::Serializer &s, SpriteImageList &sil) {
for (int i = 0; i < ARRAYSIZE(sil.list); i++) {
s.syncAsSint16LE(sil.list[i], VER(119));
}
s.syncAsSint16LE(sil.count, VER(119));
}
void Sprite::saveLoadWithSerializer(Common::Serializer &s) {
if (s.getVersion() >= VER(64)) {
s.syncArray(_spriteTable, _maxSprites + 1, syncWithSerializer);
s.syncArray(_groupTable, _maxSpriteGroups + 1, syncWithSerializer);
if (s.getVersion() >= VER(119)) {
s.syncArray(_imageLists, _maxImageLists + 1, syncWithSerializer);
}
} else {
// TODO: This had been bogus, what is it really supposed to do?
// s->saveLoadArrayOf(_activeSprites, _maxSprites, sizeof(_activeSprites[0]), spriteEntries);
s.syncArray(*_activeSprites, _maxSprites, syncWithSerializer);
s.syncArray(_spriteTable, _maxSprites, syncWithSerializer);
s.syncArray(_groupTable, _maxSpriteGroups, syncWithSerializer);
}
// Reset active sprite table
if (s.isLoading())
_activeSpriteCount = 0;
}
} // End of namespace Scumm
#endif // ENABLE_HE