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; return;
scale = _vm->getScale(walkbox, x, y); 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) { if (scale > 255) {
warning("Actor %d at %d, scale %d out of range", number, y, scale); warning("Actor %d at %d, scale %d out of range", number, y, scale);
} }
scalex = (byte)scale; scalex = (byte)scale;
scaley = (byte)scale; scaley = (byte)scale;
} }

View file

@ -156,8 +156,22 @@ int Scumm::getScale(int box, int x, int y) {
if (!ptr) if (!ptr)
return 255; return 255;
int slot , scale;
if (_version == 8) { 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) { if (slot) {
assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots)); assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
int scaleX = 0, scaleY = 0; int scaleX = 0, scaleY = 0;
@ -171,37 +185,28 @@ int Scumm::getScale(int box, int x, int y) {
y = 0; y = 0;
scaleY = (s.scale2 - s.scale1) * (y - s.y1) / (s.y2 - s.y1) + s.scale1; scaleY = (s.scale2 - s.scale1) * (y - s.y1) / (s.y2 - s.y1) + s.scale1;
}
if (s.x1 == s.x2) { if (s.x1 == s.x2) {
return scaleY; scale = scaleY;
} } else {
}
scaleX = (s.scale2 - s.scale1) * (x - s.x1) / (s.x2 - s.x1) + s.scale1; scaleX = (s.scale2 - s.scale1) * (x - s.x1) / (s.x2 - s.x1) + s.scale1;
if (s.y1 == s.y2) { if (s.y1 == s.y2) {
return scaleX; scale = scaleX;
} else { } 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; return scale;
}
} }
int Scumm::getBoxScale(int box) { int Scumm::getBoxScale(int box) {
@ -216,60 +221,36 @@ int Scumm::getBoxScale(int box) {
return READ_LE_UINT16(&ptr->old.scale); return READ_LE_UINT16(&ptr->old.scale);
} }
/* /**
FIXME: It seems that scale items and scale slots are the same thing after * Convert a rtScaleTable resource to a corresponding scale slot entry.
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 * At some point, we discovered that the old scale items (stored in rtScaleTable
fly; also for scale slots, the scale along the x axis can vary, too). * 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
Now, there are various known scale glitches in FT (and apparently also * slower), they are more flexible, and most importantly, can cope with
in The Dig, see FIXME comments in Actor::setupActorScale). In this context * rooms higher than 200 pixels. That's an essential feature for DIG and FT
it is very interesting that for V5, there is an opcode which invokes * and in fact the lack of it caused various bugs in the past.
setScaleItem, and for V8 one that invokes setScaleSlot. But there is no *
such opcode to be found for V6/V7 games. * Hence, we decided to switch all games to use the more powerful scale slots.
* To accomodate old savegames, we attempt here to convert rtScaleTable
Hypothesis: we simple are missing this opcode, and implementing it might * resources to scale slots.
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);
*/
}
void Scumm::convertScaleTableToScaleSlot(int slot) { void Scumm::convertScaleTableToScaleSlot(int slot) {
assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots)); assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
byte *resptr = getResourceAddress(rtScaleTable, slot); byte *resptr = getResourceAddress(rtScaleTable, slot);
ScaleSlot &s = _scaleSlots[slot-1]; ScaleSlot &s = _scaleSlots[slot-1];
int lowerIdx, upperIdx; int lowerIdx, upperIdx;
float m, oldM; float m, oldM;
// Do nothing if the given scale table doesn't exist
if (resptr == 0)
return;
if (resptr[0] == resptr[199]) { if (resptr[0] == resptr[199]) {
// The scale is constant This usually means we encountered one of the // The scale is constant This usually means we encountered one of the
// "broken" cases. We set pseudo scale item values which lead to a // "broken" cases. We set pseudo scale item values which lead to a
// constant scale of 255. // constant scale of 255.
// TODO
printf("Broken case!\n");
s.y1 = 0; s.y1 = 0;
s.y2 = 199; s.y2 = 199;
s.scale1 = s.scale2 = 255; 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)) { if (_imuse && (_saveSound || !_saveLoadCompatible)) {
_imuse->save_or_load(s, this); _imuse->save_or_load(s, this);
_imuse->setMasterVolume(_sound->_sound_volume_master); _imuse->setMasterVolume(_sound->_sound_volume_master);

View file

@ -28,7 +28,7 @@
// Can be useful for other ports too :) // Can be useful for other ports too :)
#define VER(x) x #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, // 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 // 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); d = getVarOrDirectByte(0x40);
_opcode = fetchScriptByte(); _opcode = fetchScriptByte();
e = getVarOrDirectByte(0x40); e = getVarOrDirectByte(0x40);
setScaleItem(e - 1, b, a, d, c); setScaleSlot(e - 1, 0, b, a, 0, d, c);
break; break;
case 8: /* room scale? */ case 8: /* room scale? */
if (_features & GF_SMALL_HEADER) { if (_features & GF_SMALL_HEADER) {

View file

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

View file

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