scummvm/engines/saga2/sprite.cpp

902 lines
25 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 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
* aint32 with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
#include "saga2/saga2.h"
#include "saga2/fta.h"
#include "saga2/blitters.h"
#include "saga2/sprite.h"
#include "saga2/tcoords.h"
#include "saga2/hresmgr.h"
namespace Saga2 {
const int maxWeaponSpriteSets = 40;
const uint32 spriteGroupID = MKTAG('S', 'P', 'R', 'I'),
frameGroupID = MKTAG('F', 'R', 'M', 'L'),
poseGroupID = MKTAG('P', 'O', 'S', 'E'),
schemeGroupID = MKTAG('S', 'C', 'H', 'M'),
objectSpriteID = MKTAG('O', 'B', 'J', 'S'),
mentalSpriteID = MKTAG('M', 'E', 'N', 'T'),
weaponSpriteBaseID = MKTAG('W', 'P', 'N', 0),
missileSpriteID = MKTAG('M', 'I', 'S', 'S');
extern uint16 rippedRoofID;
extern void drawTileMask(
const Point16 &sPos,
gPixelMap &map,
TilePoint loc,
uint16 roofID = rippedRoofID);
// Color map ranges
extern uint8 *ColorMapRanges;
/* ===================================================================== *
Exports
* ===================================================================== */
/* ===================================================================== *
Locals
* ===================================================================== */
// Remap table for colors which are not remapped.
const uint8 fixedColors[] = {
0, 10, 12, 14, 16, 18, 21, 24,
101, 104, 130, 132, 197, 199, 228, 230
};
SpriteSet *objectSprites, // object sprites
*mentalSprites, // intangible object sprites
*weaponSprites[maxWeaponSpriteSets], // weapon sprites
*missileSprites; // missile sprites
hResContext *spriteRes, // sprite resource handle
*frameRes, // framelist resource handle
*poseRes, // poselist resource handle
*schemeRes; // schemelist resource handle
// An array of 32 actor appearances
static ActorAppearance appearanceTable[32];
/* ===================================================================== *
Quick memory routines
* ===================================================================== */
static uint8 *quickMemBase,
*quickMemPtr;
int32 quickMemSize;
void initQuickMem(int32 size) {
quickMemBase = new uint8[size]();
if (quickMemBase == nullptr)
error("Error: Memory allocation size %d failed!", size);
quickMemSize = size;
quickMemPtr = quickMemBase;
}
void cleanupQuickMem(void) {
if (quickMemBase)
delete[] quickMemBase;
quickMemBase = nullptr;
}
void *getQuickMem(int32 size) {
if (quickMemPtr + size > quickMemBase + quickMemSize) {
error("Error: QuickMem allocation failed, size %d", size);
}
quickMemPtr += size;
return quickMemPtr - size;
}
void freeQuickMem(void *ptr) {
quickMemPtr = ptr ? (uint8 *)ptr : quickMemBase;
}
/* ===================================================================== *
Sprite rendering routines
* ===================================================================== */
void DrawCompositeMaskedSprite(
gPort &port, // destination gPort
SpriteComponent *scList, // list of components
int16 numParts, // number of components
const Point16 &destPoint, // where to render to
const TilePoint &loc, // location on map
int16 effects, // effects flags
bool *obscured) { // set if object obscured by terrain
SpriteComponent *sc; // sprite component
int16 xMax = 0, // extent of composite
xMin = 0,
yMax = 0,
yMin = 0;
Rect16 clip; // clip rect of port
gPixelMap compMap, // pixel map for composite
sprMap; // sprite map
Point16 org;
int x, y;
port.getClip(clip);
// First, determine the enclosing rectangle which
// surrounds all of the sprites.
sc = scList;
for (int i = 0; i < numParts; i++, sc++) {
Sprite *sp = sc->sp;
int16 left,
right,
bottom,
top;
// Compute the rectangle of the sprites
if (sc->flipped)
left = destPoint.x + sc->offset.x - sp->size.x - sp->offset.x;
else left = destPoint.x + sc->offset.x + sp->offset.x;
top = destPoint.y + sc->offset.y + sp->offset.y;
right = left + sp->size.x;
bottom = top + sp->size.y;
if (i == 0) {
xMin = left;
xMax = right;
yMin = top;
yMax = bottom;
} else {
if (left < xMin) xMin = left;
if (right > xMax) xMax = right;
if (top < yMin) yMin = top;
if (bottom > yMax) yMax = bottom;
}
}
// If the composite area is outside the screen rect, then
// nothing needs to be done.
if (xMax <= clip.x
|| yMax <= clip.y
|| xMin >= clip.x + clip.width
|| yMin >= clip.y + clip.height)
return;
// Justify the x coords to the nearest tile boundary
xMin = xMin & ~31;
xMax = (xMax + 31) & ~31;
// Build a temporary bitmap to composite the sprite within
compMap.size.x = xMax - xMin;
compMap.size.y = yMax - yMin;
compMap.data = (uint8 *)getQuickMem(compMap.bytes());
memset(compMap.data, 0, compMap.bytes());
// Calculate the offset from the upper-left corner of
// our composite map to the origin point where the sprites
// should be drawn.
org.x = destPoint.x - xMin;
org.y = destPoint.y - yMin;
// First, determine the enclosing rectangle which
// surrounds all of the sprites.
sc = scList;
for (int i = 0; i < numParts; i++, sc++) {
Sprite *sp = sc->sp;
// Create a temp map for the sprite to unpack in
sprMap.size = sp->size;
if (sprMap.size.x <= 0 || sprMap.size.y <= 0) continue;
sprMap.data = (uint8 *)getQuickMem(compMap.bytes());
// Unpack the sprite into the temp map
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
// Blit the temp map onto the composite map
if (sc->flipped) {
compositePixelsRvs(
&compMap,
&sprMap,
org.x + sc->offset.x - sp->offset.x,
org.y + sc->offset.y + sp->offset.y,
sc->colorTable);
} else {
compositePixels(
&compMap,
&sprMap,
org.x + sc->offset.x + sp->offset.x,
org.y + sc->offset.y + sp->offset.y,
sc->colorTable);
}
freeQuickMem(sprMap.data);
}
// do terrain masking
if (effects & sprFXTerrainMask) {
if (!(effects & sprFXGhostIfObscured)) {
drawTileMask(
Point16(xMin, yMin),
compMap,
loc);
} else {
gPixelMap tempMap;
int32 compMapBytes = compMap.bytes(),
visiblePixels;
bool isObscured;
tempMap.size = compMap.size;
tempMap.data = (uint8 *)getQuickMem(compMapBytes);
memcpy(tempMap.data, compMap.data, compMapBytes);
drawTileMask(
Point16(xMin, yMin),
compMap,
loc);
visiblePixels = 0;
for (int i = 0; i < compMapBytes; i++) {
if (compMap.data[i] != 0) {
visiblePixels++;
if (visiblePixels > 10) break;
}
}
isObscured = visiblePixels <= 10;
if (isObscured) {
memcpy(compMap.data, tempMap.data, compMapBytes);
effects |= sprFXGhosted;
}
if (obscured != nullptr) *obscured = isObscured;
freeQuickMem(tempMap.data);
}
}
// Check if location is underwater
if (loc.z < 0) {
uint8 *submergedArea = &compMap.data[(-loc.z < compMap.size.y ?
(compMap.size.y + loc.z)
* compMap.size.x :
0)];
uint16 submergedSize = &compMap.data[compMap.bytes()] -
submergedArea;
memset(submergedArea, 0, submergedSize);
}
// Add in "ghost" effects
if (effects & sprFXGhosted) {
uint32 *dstRow = (uint32 *)compMap.data;
uint32 mask = (yMin & 1) ? 0xff00ff00 : 0x00ff00ff;
for (y = 0; y < compMap.size.y; y++) {
for (x = 0; x < compMap.size.x; x += 4) {
*dstRow++ &= mask;
}
mask = ~mask;
}
}
// Blit to the port
TBlit(port.map, &compMap, xMin, yMin);
freeQuickMem(compMap.data);
}
void DrawSprite(
gPort &port, // destination gPort
const Point16 &destPoint, // where to render to
Sprite *sp) { // sprite pointer
gPixelMap sprMap; // sprite map
// Create a temp map for the sprite to unpack in
sprMap.size = sp->size;
sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
// Unpack the sprite into the temp map
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
// Blit to the port
port.setMode(drawModeMatte);
port.bltPixels(sprMap,
0, 0,
destPoint.x + sp->offset.x,
destPoint.y + sp->offset.y,
sprMap.size.x, sprMap.size.y);
freeQuickMem(sprMap.data);
}
// Draw a single sprite with no masking, but with color mapping.
void DrawColorMappedSprite(
gPort &port, // destination gPort
const Point16 &destPoint, // where to render to
Sprite *sp, // sprite pointer
uint8 *colorTable) { // color remapping table
gPixelMap sprMap, // sprite map
sprReMap; // remapped sprite map
// Create a temp map for the sprite to unpack in
sprMap.size = sp->size;
sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
sprReMap.size = sp->size;
sprReMap.data = (uint8 *)getQuickMem(sprReMap.bytes());
// Unpack the sprite into the temp map
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
memset(sprReMap.data, 0, sprReMap.bytes());
// remap the sprite to the color table given
compositePixels(
&sprReMap,
&sprMap,
0,
0,
colorTable);
// Blit to the port
port.setMode(drawModeMatte);
port.bltPixels(sprReMap,
0, 0,
destPoint.x + sp->offset.x,
destPoint.y + sp->offset.y,
sprReMap.size.x, sprReMap.size.y);
freeQuickMem(sprReMap.data);
freeQuickMem(sprMap.data);
}
// Draw a single sprite with no masking, but with color mapping.
void ExpandColorMappedSprite(
gPixelMap &map, // destination gPixelMap
Sprite *sp, // sprite pointer
uint8 *colorTable) { // color remapping table
gPixelMap sprMap, // sprite map
sprReMap; // remapped sprite map
// Create a temp map for the sprite to unpack in
sprMap.size = sp->size;
sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
// Unpack the sprite into the temp map
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
// remap the sprite to the color table given
compositePixels(
&map,
&sprMap,
0,
0,
colorTable);
freeQuickMem(sprMap.data);
}
// Unpacks a sprite for a moment and returns the value of a
// specific pixel in the sprite. This is used to do hit testing
// against sprites.
uint8 GetSpritePixel(
Sprite *sp, // sprite pointer
int16 flipped, // true if sprite was flipped
const Point16 &testPoint) { // where to render to
gPixelMap sprMap; // sprite map
uint8 result;
// Create a temp map for the sprite to unpack in
sprMap.size = sp->size;
sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
// Unpack the sprite into the temp map
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
// Map the coords to the bitmap and return the pixel
if (flipped) {
result = sprMap.data[testPoint.y * sprMap.size.x
+ sprMap.size.x - testPoint.x];
} else {
result = sprMap.data[testPoint.y * sprMap.size.x + testPoint.x];
}
freeQuickMem(sprMap.data);
return result;
}
// Return the number of visible pixels in a sprite after terrain masking
uint16 visiblePixelsInSprite(
Sprite *sp, // sprite pointer
bool flipped, // is sprite flipped
ColorTable colors, // sprite's color table
Point16 drawPos, // XY position of sprite
TilePoint loc, // UVZ coordinates of sprite
uint16 roofID) { // ID of ripped roof
Point16 org;
int16 xMin, // extent of sprite
xMax,
yMin,
yMax;
gPixelMap sprMap, // sprite map
compMap;
uint16 compBytes,
i,
visiblePixels;
// Determine the extent of the sprite
xMin = drawPos.x + sp->offset.x;
yMin = drawPos.y + sp->offset.y;
xMax = xMin + sp->size.x;
yMax = yMin + sp->size.y;
// Justify the x coords to the nearest tile boundary
xMin &= ~31;
xMax = (xMax + 31) & ~31;
// Build a temporary bitmap to composite the sprite within
compMap.size.x = xMax - xMin;
compMap.size.y = yMax - yMin;
compMap.data = (uint8 *)getQuickMem(compBytes = compMap.bytes());
memset(compMap.data, 0, compBytes);
// Build bitmap in which to unpack the sprite
sprMap.size = sp->size;
sprMap.data = (uint8 *)getQuickMem(sprMap.bytes());
unpackSprite(&sprMap, sp->_data, sp->_dataSize);
org.x = drawPos.x - xMin;
org.y = drawPos.y - yMin;
// Blit the sprite onto the composite map
if (!flipped) {
compositePixels(
&compMap,
&sprMap,
org.x + sp->offset.x,
org.y + sp->offset.y,
colors);
} else {
compositePixelsRvs(
&compMap,
&sprMap,
org.x - sp->offset.x,
org.y + sp->offset.y,
colors);
}
// do terrain masking
drawTileMask(
Point16(xMin, yMin),
compMap,
loc,
roofID);
// count the visible pixels in the composite map
for (i = 0, visiblePixels = 0; i < compBytes; i++)
if (compMap.data[i]) visiblePixels++;
#if DEBUG*0
WriteStatusF(8, "Visible pixels = %u", visiblePixels);
#endif
freeQuickMem(sprMap.data);
freeQuickMem(compMap.data);
return visiblePixels;
}
/* ===================================================================== *
Color table assembly
* ===================================================================== */
void buildColorTable(
uint8 *colorTable, // color table to build
uint8 *colorOptions, // colors ranges chosen
int16 numOptions) {
uint32 *src,
*dst;
memcpy(colorTable, fixedColors, sizeof fixedColors);
dst = (uint32 *)(colorTable + sizeof fixedColors);
while (numOptions--) {
src = (uint32 *)&ColorMapRanges[*colorOptions * 8];
colorOptions++;
*dst++ = *src++;
*dst++ = *src++;
}
}
/* ===================================================================== *
Load actor appearance
* ===================================================================== */
void ActorAppearance::loadSpriteBanks(int16 banksNeeded) {
int16 bank;
WriteStatusF(2, "Loading Banks: %x", banksNeeded);
// Make this one the most recently used entry
g_vm->_appearanceLRU.push_back(this);
// Load in additional sprite banks if requested...
for (bank = 0; bank < (long)ARRAYSIZE(spriteBanks); bank++) {
// Load the sprite handle...
if (spriteBanks[bank] == nullptr && (banksNeeded & (1 << bank))) {
Common::SeekableReadStream *stream = loadResourceToStream(spriteRes, id + MKTAG(0, 0, 0, bank), "sprite bank");
if (stream) {
spriteBanks[bank] = new SpriteSet(stream);
delete stream;
}
}
}
}
ActorAnimation::ActorAnimation(Common::SeekableReadStream *stream) {
for (int i = 0; i < numPoseFacings; i++)
start[i] = stream->readUint16LE();
for (int i = 0; i < numPoseFacings; i++)
count[i] = stream->readUint16LE();
for (int i = 0; i < numPoseFacings; i++)
debugC(2, kDebugLoading, "anim%d: start: %d count: %d", i, start[i], count[i]);
}
ActorPose::ActorPose() {
flags = 0;
actorFrameIndex = actorFrameBank = leftObjectIndex = rightObjectIndex = 0;
}
ActorPose::ActorPose(Common::SeekableReadStream *stream) {
load(stream);
}
void ActorPose::load(Common::SeekableReadStream *stream) {
flags = stream->readUint16LE();
actorFrameIndex = stream->readByte();
actorFrameBank = stream->readByte();
leftObjectIndex = stream->readByte();
rightObjectIndex = stream->readByte();
leftObjectOffset.load(stream);
rightObjectOffset.load(stream);
}
void ActorPose::write(Common::MemoryWriteStreamDynamic *out) {
out->writeUint16LE(flags);
out->writeByte(actorFrameIndex);
out->writeByte(actorFrameBank);
out->writeByte(leftObjectIndex);
out->writeByte(rightObjectIndex);
leftObjectOffset.write(out);
rightObjectOffset.write(out);
}
ColorScheme::ColorScheme(Common::SeekableReadStream *stream) {
for (int i = 0; i < 11; ++i)
bank[i] = stream->readByte();
speechColor = stream->readByte();
for (int i = 0; i < 32; ++i)
name[i] = stream->readSByte();
}
ColorSchemeList::ColorSchemeList(int count, Common::SeekableReadStream *stream) {
_count = count;
_schemes = (ColorScheme **)malloc(_count * sizeof(ColorScheme *));
for (int i = 0; i < _count; ++i)
_schemes[i] = new ColorScheme(stream);
}
ColorSchemeList::~ColorSchemeList() {
for (int i = 0; i < _count; ++i)
delete _schemes[i];
free(_schemes);
}
ActorAppearance *LoadActorAppearance(uint32 id, int16 banksNeeded) {
int16 bank;
int schemeListSize;
Common::SeekableReadStream *stream;
// Search the table for either a matching appearance,
// or for an empty one.
for (Common::List<ActorAppearance *>::iterator it = g_vm->_appearanceLRU.begin(); it != g_vm->_appearanceLRU.end(); ++it) {
if ((*it)->id == id // If has same ID
&& (*it)->poseList != nullptr) { // and frames not dumped
// then use this one!
(*it)->useCount++;
(*it)->loadSpriteBanks(banksNeeded);
return *it;
}
}
// If we couldn't find an extact match, search for an
// empty one.
ActorAppearance *aa = nullptr;
// Search from LRU end of list.
for (Common::List<ActorAppearance *>::iterator it = g_vm->_appearanceLRU.begin(); it != g_vm->_appearanceLRU.end(); ++it) {
if ((*it)->useCount == 0) { // If not in use
aa = *it; // then use this one!
break;
}
}
// If none available, that's fatal...
if (aa == nullptr) {
error("All ActorAppearance records are in use!");
}
// Dump the sprites being stored
for (bank = 0; bank < (long)ARRAYSIZE(aa->spriteBanks); bank++) {
if (aa->spriteBanks[bank])
delete aa->spriteBanks[bank];
aa->spriteBanks[bank] = nullptr;
}
if (aa->poseList) {
for (uint i = 0; i < aa->poseList->numPoses; i++)
delete aa->poseList->poses[i];
free(aa->poseList->poses);
for (uint i = 0; i < aa->poseList->numAnimations; i++)
delete aa->poseList->animations[i];
free(aa->poseList->animations);
delete aa->poseList;
}
aa->poseList = nullptr;
if (aa->schemeList) {
delete aa->schemeList;
}
aa->schemeList = nullptr;
// Set ID and use count
aa->id = id;
aa->useCount = 1;
// Load in new frame lists and sprite banks
aa->loadSpriteBanks(banksNeeded);
Common::SeekableReadStream *poseStream = loadResourceToStream(poseRes, id, "pose list");
if (poseStream == nullptr) {
warning("LoadActorAppearance: Could not load pose list");
} else {
ActorAnimSet *as = new ActorAnimSet;
aa->poseList = as;
as->numAnimations = poseStream->readUint32LE();
as->poseOffset = poseStream->readUint32LE();
// compute number of ActorPoses
uint32 poseBytes = poseStream->size() - as->poseOffset;
const int poseSize = 14;
debugC(1, kDebugLoading, "Pose List: bytes: %ld numAnimations: %d poseOffset: %d calculated offset: %d numPoses: %d",
poseStream->size(), as->numAnimations, as->poseOffset, 8 + as->numAnimations * 32, poseBytes / poseSize);
if (poseBytes % poseSize != 0)
warning("Incorrect number of poses, %d bytes more", poseBytes % poseSize);
as->numPoses = poseBytes / poseSize;
as->animations = (ActorAnimation **)malloc(as->numAnimations * sizeof(ActorAnimation *));
for (uint i = 0; i < as->numAnimations; i++)
as->animations[i] = new ActorAnimation(poseStream);
as->poses = (ActorPose **)malloc(as->numPoses * sizeof(ActorPose *));
for (uint i = 0; i < as->numPoses; i++)
as->poses[i] = new ActorPose(poseStream);
delete poseStream;
}
if (schemeRes->seek(id) == 0) {
warning("LoadActorAppearance: Could not load scheme list");
} else {
const int colorSchemeSize = 44;
if (schemeRes->size(id) % colorSchemeSize != 0)
warning("Incorrect number of colorschemes, %d bytes more", schemeRes->size(id) % colorSchemeSize);
schemeListSize = schemeRes->size(id) / colorSchemeSize;
stream = loadResourceToStream(schemeRes, id, "scheme list");
aa->schemeList = new ColorSchemeList(schemeListSize, stream);
delete stream;
}
return aa;
}
void ReleaseActorAppearance(ActorAppearance *aa) {
aa->useCount--;
}
/* ===================================================================== *
Sprite initialization routines
* ===================================================================== */
Sprite::Sprite(Common::SeekableReadStream *stream) {
size.load(stream);
offset.load(stream);
_dataSize = size.x * size.y;
_data = (byte *)malloc(_dataSize);
stream->read(_data, _dataSize);
}
Sprite::~Sprite() {
free(_data);
}
SpriteSet::SpriteSet(Common::SeekableReadStream *stream) {
count = stream->readUint32LE();
_sprites = (Sprite **)malloc(count * sizeof(Sprite *));
for (uint i = 0; i < count; ++i) {
stream->seek(4 + i * 4);
uint32 offset = stream->readUint32LE();
stream->seek(offset);
_sprites[i] = new Sprite(stream);
}
}
SpriteSet::~SpriteSet() {
for (uint i = 0; i < count; ++i) {
if (_sprites[i])
delete _sprites[i];
}
free(_sprites);
}
void initSprites(void) {
int i;
Common::SeekableReadStream *stream = nullptr;
spriteRes = resFile->newContext(spriteGroupID, "sprite resources");
if (!spriteRes->_valid)
error("Error accessing sprite resource group.");
frameRes = resFile->newContext(frameGroupID, "frame resources");
assert(frameRes && frameRes->_valid);
poseRes = resFile->newContext(poseGroupID, "pose resources");
assert(poseRes && poseRes->_valid);
schemeRes = resFile->newContext(schemeGroupID, "scheme resources");
assert(schemeRes && schemeRes->_valid);
// object sprites
stream = loadResourceToStream(spriteRes, objectSpriteID, "object sprites");
objectSprites = new SpriteSet(stream);
delete stream;
assert(objectSprites);
// intagible object sprites
stream = loadResourceToStream(spriteRes, mentalSpriteID, "mental sprites");
mentalSprites = new SpriteSet(stream);
delete stream;
assert(mentalSprites);
for (i = 0; i < maxWeaponSpriteSets; i++) {
hResID weaponSpriteID;
weaponSpriteID = weaponSpriteBaseID + MKTAG(0, 0, 0, i);
if (spriteRes->size(weaponSpriteID) == 0) {
weaponSprites[i] = nullptr;
continue;
}
stream = loadResourceToStream(spriteRes, weaponSpriteID, "weapon sprite set");
weaponSprites[i] = new SpriteSet(stream);
delete stream;
}
stream = loadResourceToStream(spriteRes, missileSpriteID, "missle sprites");
missileSprites = new SpriteSet(stream);
delete stream;
initQuickMem(0x10000);
// Initialize actor appearance table
for (i = 0; i < ARRAYSIZE(appearanceTable); i++) {
ActorAppearance *aa = &appearanceTable[i];
aa->useCount = 0;
g_vm->_appearanceLRU.push_front(aa);
}
}
void cleanupSprites(void) {
int i;
cleanupQuickMem();
if (objectSprites)
delete objectSprites;
objectSprites = nullptr;
if (mentalSprites)
delete mentalSprites;
mentalSprites = nullptr;
for (i = 0; i < maxWeaponSpriteSets; i++) {
if (weaponSprites[i]) {
delete weaponSprites[i];
weaponSprites[i] = nullptr;
}
}
if (schemeRes) resFile->disposeContext(schemeRes);
schemeRes = nullptr;
if (poseRes) resFile->disposeContext(poseRes);
poseRes = nullptr;
if (frameRes) resFile->disposeContext(frameRes);
frameRes = nullptr;
if (spriteRes) resFile->disposeContext(spriteRes);
spriteRes = nullptr;
}
} // end of namespace Saga2