replaced all use of scale items with scale slots. This allowed me to get rid of two big FIXME's, and might fix other scaling bugs in FT/DIG

svn-id: r10140
This commit is contained in:
Max Horn 2003-09-09 17:29:22 +00:00
parent 163ecdc054
commit 07e8084eee
7 changed files with 83 additions and 133 deletions

View file

@ -412,47 +412,10 @@ void Actor::setupActorScale() {
return;
scale = _vm->getScale(walkbox, x, y);
if (_vm->_version == 8) {
// At least in COMI, scale values are clipped to range 1-255
if (scale < 1)
scale = 1;
else if (scale > 255)
scale = 255;
}
// FIXME - Hack for The Dig 'Tomb' (room 88)
// Otherwise walking to the far-left door causes the actor
// to shrink to a one-pixel dot. The reason for this is that
// scale items (as handled in setScaleItem etc.) are only
// working as long as the room height is <= 200!!! That's a
// serious problem for DIG/FT, and causes the FIXME below, too.
// A way to properly fix the problem would be to use the
// V8 "scale slots" instead. This would be almost perfect, the
// only problem being that it might render some old savegames
// partially broken...
if (_vm->_gameId == GID_DIG && _vm->_currentRoom == 88) {
scale = 0xFF;
}
// FIXME - Quick fix to ft's fuel tower bug (by yazoo)
//
// Ben's Y position can be anything between 272 and 398 inclusive
// (which by the way means that we're always looking at the same
// element in the scale table... hmmm...)
//
// When standing at the bottom of the ladder, Ben's Y position is
// 356, and by the looks of it he ought to be unscaled there.
if (_vm->_gameId == GID_FT && scale == 1 && _vm->_currentRoom == 76) {
scale = 0xff;
if (y < 356)
scale -= 2 * (356 - y);
}
if (scale > 255) {
warning("Actor %d at %d, scale %d out of range", number, y, scale);
}
scalex = (byte)scale;
scaley = (byte)scale;
}

View file

@ -156,8 +156,22 @@ int Scumm::getScale(int box, int x, int y) {
if (!ptr)
return 255;
int slot , scale;
if (_version == 8) {
int slot = FROM_LE_32(ptr->v8.scaleSlot);
// COMI has a separate field for the scale slot...
slot = FROM_LE_32(ptr->v8.scaleSlot);
scale = FROM_LE_32(ptr->v8.scale);
} else {
scale = READ_LE_UINT16(&ptr->old.scale);
if (scale & 0x8000)
slot = (scale & 0x7FFF) + 1;
else
slot = 0;
}
// Was a scale slot specified? If so, we compute the effective scale
// from it, ignoring the box scale.
if (slot) {
assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
int scaleX = 0, scaleY = 0;
@ -171,37 +185,28 @@ int Scumm::getScale(int box, int x, int y) {
y = 0;
scaleY = (s.scale2 - s.scale1) * (y - s.y1) / (s.y2 - s.y1) + s.scale1;
}
if (s.x1 == s.x2) {
return scaleY;
}
}
scale = scaleY;
} else {
scaleX = (s.scale2 - s.scale1) * (x - s.x1) / (s.x2 - s.x1) + s.scale1;
if (s.y1 == s.y2) {
return scaleX;
scale = scaleX;
} else {
return (scaleX + scaleY - s.x1) / 2;
scale = (scaleX + scaleY - s.x1) / 2;
}
} else
return FROM_LE_32(ptr->v8.scale);
} else {
uint16 scale = READ_LE_UINT16(&ptr->old.scale);
if (scale & 0x8000) {
scale = (scale & 0x7FFF) + 1;
byte *resptr = getResourceAddress(rtScaleTable, scale);
if (resptr == NULL)
error("Scale table %d not defined", scale);
if (y >= _screenHeight)
y = _screenHeight - 1;
else if (y < 0)
y = 0;
scale = resptr[y];
}
// Clip the scale to range 1-255
if (scale < 1)
scale = 1;
else if (scale > 255)
scale = 255;
}
// Finally return the scale
return scale;
}
}
int Scumm::getBoxScale(int box) {
@ -216,60 +221,36 @@ int Scumm::getBoxScale(int box) {
return READ_LE_UINT16(&ptr->old.scale);
}
/*
FIXME: It seems that scale items and scale slots are the same thing after
all - they only differ in some details (scale item is used to precompute
a scale table, while for the scale slots the computations are done on the
fly; also for scale slots, the scale along the x axis can vary, too).
Now, there are various known scale glitches in FT (and apparently also
in The Dig, see FIXME comments in Actor::setupActorScale). In this context
it is very interesting that for V5, there is an opcode which invokes
setScaleItem, and for V8 one that invokes setScaleSlot. But there is no
such opcode to be found for V6/V7 games.
Hypothesis: we simple are missing this opcode, and implementing it might
fix some or all of the Dig/FT scaling issues.
*/
void Scumm::setScaleItem(int slot, int y1, int scale1, int y2, int scale2) {
byte *ptr;
int y, tmp;
if (y1 == y2)
return;
ptr = createResource(rtScaleTable, slot, 200);
for (y = 0; y < 200; y++) {
tmp = ((scale2 - scale1) * (y - y1)) / (y2 - y1) + scale1;
if (tmp < 1)
tmp = 1;
if (tmp > 255)
tmp = 255;
*ptr++ = tmp;
}
/*
// TEST!
printf("setScaleItem(%d, %d, %d, %d, %d)\n", slot, y1, scale1, y2, scale2);
convertScaleTableToScaleSlot(slot);
*/
}
/**
* Convert a rtScaleTable resource to a corresponding scale slot entry.
*
* At some point, we discovered that the old scale items (stored in rtScaleTable
* resources) are in fact the same as (or rather, a predecessor of) the
* scale slots used in COMI. While not being precomputed (and thus slightly
* slower), they are more flexible, and most importantly, can cope with
* rooms higher than 200 pixels. That's an essential feature for DIG and FT
* and in fact the lack of it caused various bugs in the past.
*
* Hence, we decided to switch all games to use the more powerful scale slots.
* To accomodate old savegames, we attempt here to convert rtScaleTable
* resources to scale slots.
*/
void Scumm::convertScaleTableToScaleSlot(int slot) {
assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
byte *resptr = getResourceAddress(rtScaleTable, slot);
ScaleSlot &s = _scaleSlots[slot-1];
int lowerIdx, upperIdx;
float m, oldM;
// Do nothing if the given scale table doesn't exist
if (resptr == 0)
return;
if (resptr[0] == resptr[199]) {
// The scale is constant This usually means we encountered one of the
// "broken" cases. We set pseudo scale item values which lead to a
// constant scale of 255.
// TODO
printf("Broken case!\n");
s.y1 = 0;
s.y2 = 199;
s.scale1 = s.scale2 = 255;

View file

@ -658,6 +658,15 @@ void Scumm::saveOrLoad(Serializer *s, uint32 savegameVersion) {
}
}
// With version 22, we replace the scale items with scale slots
if (savegameVersion < VER(22) && !s->isSaving()) {
// Convert all rtScaleTable resources to matching scale items
for (i = 1; i < res.num[rtScaleTable]; i++) {
convertScaleTableToScaleSlot(i);
}
}
if (_imuse && (_saveSound || !_saveLoadCompatible)) {
_imuse->save_or_load(s, this);
_imuse->setMasterVolume(_sound->_sound_volume_master);

View file

@ -28,7 +28,7 @@
// Can be useful for other ports too :)
#define VER(x) x
#define CURRENT_VER 21
#define CURRENT_VER 22
// To work around a warning in GCC 3.2 (and 3.1 ?) regarding non-POD types,
// we use a small trick: instead of 0 we use 42. Why? Well, it seems newer GCC

View file

@ -1867,7 +1867,7 @@ void Scumm_v5::o5_roomOps() {
d = getVarOrDirectByte(0x40);
_opcode = fetchScriptByte();
e = getVarOrDirectByte(0x40);
setScaleItem(e - 1, b, a, d, c);
setScaleSlot(e - 1, 0, b, a, 0, d, c);
break;
case 8: /* room scale? */
if (_features & GF_SMALL_HEADER) {

View file

@ -1051,7 +1051,6 @@ public:
int getBoxScale(int box);
int getScale(int box, int x, int y);
void setScaleItem(int slot, int a, int b, int c, int d);
protected:
// Scaling slots/items

View file

@ -1996,7 +1996,7 @@ void Scumm::startScene(int room, Actor *a, int objectNr) {
}
void Scumm::initRoomSubBlocks() {
int i, offs;
int i;
const byte *ptr;
byte *roomptr, *searchptr, *roomResPtr;
const RoomHeader *rmhd;
@ -2178,29 +2178,27 @@ void Scumm::initRoomSubBlocks() {
// Load scale data
//
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO ?
ptr = 0;
else
ptr = findResourceData(MKID('SCAL'), roomptr);
if (ptr) {
offs = ptr - roomptr;
int s1, s2, y1, y2;
if (_version == 8) {
for (i = 1; i < _maxScaleTable; i++, offs += 16) {
s1 = READ_LE_UINT32(roomptr + offs);
y1 = READ_LE_UINT32(roomptr + offs + 4);
s2 = READ_LE_UINT32(roomptr + offs + 8);
y2 = READ_LE_UINT32(roomptr + offs + 12);
for (i = 1; i < _maxScaleTable; i++, ptr += 16) {
s1 = READ_LE_UINT32(ptr);
y1 = READ_LE_UINT32(ptr + 4);
s2 = READ_LE_UINT32(ptr + 8);
y2 = READ_LE_UINT32(ptr + 12);
setScaleSlot(i, 0, y1, s1, 0, y2, s2);
}
} else {
for (i = 1; i < _maxScaleTable; i++, offs += 8) {
s1 = READ_LE_UINT16(roomptr + offs);
y1 = READ_LE_UINT16(roomptr + offs + 2);
s2 = READ_LE_UINT16(roomptr + offs + 4);
y2 = READ_LE_UINT16(roomptr + offs + 6);
for (i = 1; i < _maxScaleTable; i++, ptr += 8) {
s1 = READ_LE_UINT16(ptr);
y1 = READ_LE_UINT16(ptr + 2);
s2 = READ_LE_UINT16(ptr + 4);
y2 = READ_LE_UINT16(ptr + 6);
if (s1 || y1 || s2 || y2) {
setScaleItem(i, y1, s1, y2, s2);
roomptr = getResourceAddress(rtRoom, _roomResource);
setScaleSlot(i, 0, y1, s1, 0, y2, s2);
}
}
}