mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 20:01:25 +00:00
823 lines
23 KiB
C++
823 lines
23 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.
|
|
*/
|
|
|
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL // FIXME: Remove
|
|
|
|
#include "saga2/std.h"
|
|
#include "saga2/sprite.h"
|
|
#include "saga2/tcoords.h"
|
|
#include "saga2/input.h"
|
|
#include "saga2/cmisc.h"
|
|
|
|
namespace Saga2 {
|
|
|
|
const int maxWeaponSpriteSets = 40;
|
|
|
|
const uint32 spriteGroupID = RES_ID('S', 'P', 'R', 'I'),
|
|
frameGroupID = RES_ID('F', 'R', 'M', 'L'),
|
|
poseGroupID = RES_ID('P', 'O', 'S', 'E'),
|
|
schemeGroupID = RES_ID('S', 'C', 'H', 'M'),
|
|
objectSpriteID = RES_ID('O', 'B', 'J', 'S'),
|
|
mentalSpriteID = RES_ID('M', 'E', 'N', 'T'),
|
|
weaponSpriteBaseID = RES_ID('W', 'P', 'N', 0),
|
|
missileSpriteID = RES_ID('M', 'I', 'S', 'S');
|
|
|
|
/* ===================================================================== *
|
|
Prototypes
|
|
* ===================================================================== */
|
|
|
|
#ifndef FTAASM_H
|
|
extern void unpackSprite(gPixelMap *map, uint8 *sprData);
|
|
extern void compositePixels(
|
|
gPixelMap *compMap,
|
|
gPixelMap *sprMap,
|
|
int32 xpos,
|
|
int32 ypos,
|
|
uint8 *lookup);
|
|
extern void compositePixelsRvs(
|
|
gPixelMap *compMap,
|
|
gPixelMap *sprMap,
|
|
int32 xpos,
|
|
int32 ypos,
|
|
uint8 *lookup);
|
|
#endif
|
|
|
|
extern uint16 rippedRoofID;
|
|
extern void drawTileMask(
|
|
const Point16 &sPos,
|
|
gPixelMap &map,
|
|
TilePoint loc,
|
|
uint16 roofID = rippedRoofID);
|
|
|
|
/* ===================================================================== *
|
|
Imports
|
|
* ===================================================================== */
|
|
|
|
extern gPixelMap tileDrawMap;
|
|
extern Point16 fineScroll; // current scroll pos
|
|
|
|
// Color map ranges
|
|
extern uint8 ColorMapRanges[][ 8 ];
|
|
|
|
extern gPort backPort;
|
|
|
|
/* ===================================================================== *
|
|
Exports
|
|
* ===================================================================== */
|
|
|
|
/* ===================================================================== *
|
|
Locals
|
|
* ===================================================================== */
|
|
|
|
// Remap table for colors which are not remapped.
|
|
|
|
extern 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 ];
|
|
|
|
// A least-recently-used list of actor appearances
|
|
static DList appearanceLRU;
|
|
|
|
/* ===================================================================== *
|
|
Quick memory routines
|
|
* ===================================================================== */
|
|
|
|
static uint8 *quickMemBase,
|
|
*quickMemPtr;
|
|
|
|
int32 quickMemSize;
|
|
|
|
void initQuickMem(int32 size) {
|
|
quickMemBase = (uint8 *)RNewPtr(size, NULL, "Quickmem heap");
|
|
if (quickMemBase == NULL)
|
|
error("Error: Memory allocation size %d failed!", size);
|
|
quickMemSize = size;
|
|
quickMemPtr = quickMemBase;
|
|
}
|
|
|
|
void cleanupQuickMem(void) {
|
|
RDisposePtr(quickMemBase);
|
|
}
|
|
|
|
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
|
|
int i;
|
|
int16 xMax, // extent of composite
|
|
xMin,
|
|
yMax,
|
|
yMin;
|
|
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.
|
|
|
|
for (i = 0, sc = scList; 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.
|
|
|
|
for (i = 0, sc = scList; 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, (uint8 *)(sp + 1));
|
|
|
|
// 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(),
|
|
i,
|
|
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 (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 != NULL) *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, (uint8 *)(sp + 1));
|
|
|
|
// 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, (uint8 *)(sp + 1));
|
|
|
|
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, (uint8 *)(sp + 1));
|
|
|
|
// 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, (uint8 *)(sp + 1));
|
|
|
|
// 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, (uint8 *)(sp + 1));
|
|
|
|
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++ ];
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
}
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Load actor appearance
|
|
* ===================================================================== */
|
|
|
|
#if DEBUG
|
|
char *idname(long s) {
|
|
static char t[ 8 ];
|
|
char *p = (char *)&s;
|
|
|
|
t[ 0 ] = *p++;
|
|
t[ 1 ] = *p++;
|
|
t[ 2 ] = *p++;
|
|
if (*p > ' ') {
|
|
t[ 3 ] = *p;
|
|
t[ 4 ] = 0;
|
|
} else {
|
|
sprintf(&t[ 3 ], ":%d", *p);
|
|
}
|
|
return t;
|
|
}
|
|
#endif
|
|
|
|
void ActorAppearance::loadSpriteBanks(int16 banksNeeded) {
|
|
int16 bank;
|
|
|
|
WriteStatusF(2, "Loading Banks: %x", banksNeeded);
|
|
|
|
// Make this one the most recently used entry
|
|
remove();
|
|
appearanceLRU.addTail(*this);
|
|
|
|
// Load in additional sprite banks if requested...
|
|
for (bank = 0; bank < elementsof(spriteBanks); bank++) {
|
|
// Wash the sprite banks...i.e. clear out dead handles
|
|
// which have been purged.
|
|
washHandle((RHANDLE &)(spriteBanks[ bank ]));
|
|
|
|
// Load the sprite handle...
|
|
if (spriteBanks[ bank ] == NULL
|
|
&& (banksNeeded & (1 << bank))) {
|
|
spriteBanks[ bank ] =
|
|
(SpriteSet **)spriteRes->load(id + RES_ID(0, 0, 0, bank), "sprite bank",
|
|
|
|
|
|
//
|
|
// THIS WAS TRUE BUT THE SPRITE CORRUPTION GOES AWAY IF IT ISNT
|
|
//
|
|
//
|
|
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
if (spriteBanks[ bank ] == NULL)
|
|
fatal("Sprite '%s' bank %d failed to load!\n",
|
|
idname(id),
|
|
bank);
|
|
#endif
|
|
|
|
// Since the sprites are so big, we'll keep them unlocked
|
|
// so that they can be purged as needed.
|
|
RUnlockHandle((RHANDLE) spriteBanks[ bank ]);
|
|
}
|
|
}
|
|
}
|
|
|
|
ActorAppearance *LoadActorAppearance(uint32 id, int16 banksNeeded) {
|
|
ActorAppearance *aa;
|
|
int16 bank;
|
|
|
|
#if DEBUG
|
|
WriteStatusF(2, "Load App: %s", idname(id));
|
|
#endif
|
|
|
|
// Search the table for either a matching appearance,
|
|
// or for an empty one.
|
|
for (aa = (ActorAppearance *)appearanceLRU.first();
|
|
aa != NULL;
|
|
aa = (ActorAppearance *)aa->next()) {
|
|
if (aa->id == id // If has same ID
|
|
&& aa->poseList != NULL) { // and frames not dumped
|
|
// then use this one!
|
|
aa->useCount++;
|
|
aa->loadSpriteBanks(banksNeeded);
|
|
return aa;
|
|
}
|
|
}
|
|
|
|
// If we couldn't find an extact match, search for an
|
|
// empty one.
|
|
if (aa == NULL) {
|
|
// Search from LRU end of list.
|
|
for (aa = (ActorAppearance *)appearanceLRU.first();
|
|
aa != NULL;
|
|
aa = (ActorAppearance *)aa->next()) {
|
|
if (aa->useCount == 0) // If not in use
|
|
break; // then use this one!
|
|
}
|
|
|
|
// If none available, that's fatal...
|
|
if (aa == NULL) {
|
|
error("All ActorAppearance records are in use!");
|
|
}
|
|
}
|
|
|
|
// Dump the sprites being stored
|
|
for (bank = 0; bank < elementsof(aa->spriteBanks); bank++) {
|
|
if (aa->spriteBanks[ bank ])
|
|
spriteRes->release((RHANDLE) aa->spriteBanks[ bank ]);
|
|
aa->spriteBanks[ bank ] = NULL;
|
|
}
|
|
|
|
if (aa->poseList) poseRes->release((RHANDLE) aa->poseList);
|
|
aa->poseList = NULL;
|
|
|
|
if (aa->schemeList) schemeRes->release((RHANDLE) aa->schemeList);
|
|
aa->schemeList = NULL;
|
|
|
|
// Set ID and use count
|
|
aa->id = id;
|
|
aa->useCount = 1;
|
|
|
|
// Load in new frame lists and sprite banks
|
|
aa->loadSpriteBanks(banksNeeded);
|
|
aa->poseList = (ActorAnimSet **)poseRes->load(id, "pose list", FALSE, FALSE);
|
|
// I just added these - dispnode may have to lock & unlock the handle now - EO
|
|
if (aa->poseList) RUnlockHandle((RHANDLE) aa->poseList);
|
|
#if DEBUG
|
|
if (aa->poseList == NULL)
|
|
fatal("PoseList '%s' failed to load!\n", idname(id));
|
|
#endif
|
|
// REM: It's OK if schemelist fails to load...
|
|
aa->schemeList = (ColorScheme **)schemeRes->load(id, "scheme list", FALSE, FALSE);
|
|
// I just added these - dispnode may have to lock & unlock the handle now - EO
|
|
if (aa->schemeList) RUnlockHandle((RHANDLE) aa->schemeList);
|
|
|
|
return aa;
|
|
}
|
|
|
|
void ReleaseActorAppearance(ActorAppearance *aa) {
|
|
if (--aa->useCount == 0) {
|
|
}
|
|
|
|
#ifndef WINKLUDGE // jeffkludge -- causes crash
|
|
#if DEBUG
|
|
WriteStatusF(2, "Release");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Sprite initialization routines
|
|
* ===================================================================== */
|
|
|
|
void initSprites(void) {
|
|
int i;
|
|
|
|
spriteRes = resFile->newContext(spriteGroupID, "sprite resources");
|
|
if (!spriteRes->_valid)
|
|
error("Error accessing sprite resource group.");
|
|
|
|
frameRes = resFile->newContext(frameGroupID, "frame resources");
|
|
VERIFY(frameRes && frameRes->_valid);
|
|
|
|
poseRes = resFile->newContext(poseGroupID, "pose resources");
|
|
VERIFY(poseRes && poseRes->_valid);
|
|
|
|
schemeRes = resFile->newContext(schemeGroupID, "scheme resources");
|
|
VERIFY(schemeRes && schemeRes->_valid);
|
|
|
|
// object sprites
|
|
objectSprites = (SpriteSet **)spriteRes->load(objectSpriteID, "object sprites");
|
|
VERIFY(objectSprites);
|
|
|
|
// intagible object sprites
|
|
mentalSprites = (SpriteSet **)spriteRes->load(mentalSpriteID, "mental sprites");
|
|
VERIFY(mentalSprites);
|
|
|
|
for (i = 0; i < maxWeaponSpriteSets; i++) {
|
|
hResID weaponSpriteID;
|
|
|
|
weaponSpriteID = weaponSpriteBaseID + RES_ID(0, 0, 0, i);
|
|
|
|
if (spriteRes->size(weaponSpriteID) == 0) {
|
|
weaponSprites[ i ] = NULL;
|
|
continue;
|
|
}
|
|
|
|
weaponSprites[ i ] = (SpriteSet **)spriteRes->load(
|
|
weaponSpriteID,
|
|
"weapon sprite set");
|
|
}
|
|
|
|
missileSprites = (SpriteSet **)spriteRes->load(missileSpriteID, "missle sprites");
|
|
|
|
initQuickMem(0x10000);
|
|
|
|
// Initialize actor appearance table
|
|
for (i = 0; i < elementsof(appearanceTable); i++) {
|
|
ActorAppearance *aa = &appearanceTable[ i ];
|
|
|
|
aa->useCount = 0;
|
|
appearanceLRU.addHead(*aa);
|
|
}
|
|
}
|
|
|
|
void cleanupSprites(void) {
|
|
int i;
|
|
|
|
cleanupQuickMem();
|
|
|
|
if (objectSprites) spriteRes->release((RHANDLE) objectSprites);
|
|
objectSprites = NULL;
|
|
|
|
if (mentalSprites) spriteRes->release((RHANDLE) mentalSprites);
|
|
mentalSprites = NULL;
|
|
|
|
for (i = 0; i < maxWeaponSpriteSets; i++) {
|
|
if (weaponSprites[ i ]) {
|
|
spriteRes->release((RHANDLE) weaponSprites[ i ]);
|
|
weaponSprites[ i ] = NULL;
|
|
}
|
|
}
|
|
|
|
if (schemeRes) resFile->disposeContext(schemeRes);
|
|
schemeRes = NULL;
|
|
|
|
if (poseRes) resFile->disposeContext(poseRes);
|
|
poseRes = NULL;
|
|
|
|
if (frameRes) resFile->disposeContext(frameRes);
|
|
frameRes = NULL;
|
|
|
|
if (spriteRes) resFile->disposeContext(spriteRes);
|
|
spriteRes = NULL;
|
|
}
|
|
|
|
} // end of namespace Saga2
|