IMMORTAL: Implement sprite drawing through superSprites()

This commit is contained in:
Quote58 2022-09-13 03:19:29 -04:00 committed by Eugene Sandulenko
parent 8d9994ba56
commit d8944244a1
2 changed files with 84 additions and 70 deletions

View File

@ -189,21 +189,21 @@ public:
const int kScreenH__ = 128; // ???
const int kViewPortW = 256;
const int kViewPortH = 128;
const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is 320x200
const int kScreenLeft = 32;
const int kScreenTop = 20;
const int kScreenSize = (kResH * kResV) * 2; // The size of the screen buffer is (320x200) * 2 byte words
const uint16 kScreenLeft = 32;
const uint16 kScreenTop = 20;
const int kTextLeft = 8;
const int kTextTop = 4;
const int kGaugeX = 0;
const int kGaugeY = -13; // ???
const int kScreenBMW = 160; // Screen BitMap Width?
const uint16 kScreenBMW = 160; // Screen BitMap Width?
const uint16 kChrW = 64;
const uint16 kChrH = 32;
const uint16 kChrH2 = kChrH * 2;
const uint16 kChrH3 = kChrH * 3;
const int kChrLen = (kChrW / 2) * kChrH;
const int kChrBMW = kChrW / 2;
const int kLCutaway = 4;
const uint16 kChrLen = (kChrW / 2) * kChrH;
const uint16 kChrBMW = kChrW / 2;
const uint16 kLCutaway = 4;
const uint16 kChrDy[19] = {kChr0, kChrH, kChrH2, kChrH, kChrH2,
kChrH2, kChrH, kChrH2, kChrH2, kChr0,
@ -227,22 +227,22 @@ public:
const int kPaletteOffset = 21205; // This is the byte position of the palette data in the disk
// Sprite constants
const int kMaxSpriteW = 64;
const int kMaxSpriteH = 64;
const int kSpriteDY = 32;
const int kVSX = kMaxSpriteW;
const int kVSY = kSpriteDY;
const uint16 kMaxSpriteW = 64;
const uint16 kMaxSpriteH = 64;
const uint16 kSpriteDY = 32;
const uint16 kVSX = kMaxSpriteW;
const uint16 kVSY = kSpriteDY;
const uint16 kVSBMW = (kViewPortW + kMaxSpriteW) / 2;
const int kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
const int kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
const int kMySuperBottom = kVSDY + kViewPortH;
const int kSuperBottom = 200;
const int kMySuperTop = kVSDY;
const int kSuperTop = 0;
const int kViewPortSpX = 32;
const int kViewPortSpY = 0;
const int kWizardX = 28; // Common sprite center for some reason
const int kWizardY = 37;
const uint16 kVSLen = kVSBMW * (kViewPortH + kMaxSpriteH);
const uint16 kVSDY = 32; // difference from top of screen to top of viewport in the virtual screen buffer
const uint16 kMySuperBottom = kVSDY + kViewPortH;
const uint16 kSuperBottom = 200;
const uint16 kMySuperTop = kVSDY;
const uint16 kSuperTop = 0;
const uint16 kViewPortSpX = 32;
const uint16 kViewPortSpY = 0;
const uint16 kWizardX = 28; // Common sprite center for some reason
const uint16 kWizardY = 37;
// Asset constants
const char kGaugeOn = 1; // On uses the sprite at index 1 of the font spriteset
@ -409,6 +409,7 @@ GenericSprite _genSprites[6];
void addSprites(); // Add all active sprites that are in the viewport, into a list that will be sorted by priority
void sortDrawItems(); // Sort said items
void drawItems(); // Draw the items over the background
void setPen(uint16 penX, uint16 penY); // Sets the 'pen' x and y positions, including making y negative if above a certain point
// Music
void toggleSound(); // Actually pauses the sound, doesn't just turn it off/mute
@ -584,10 +585,9 @@ GenericSprite _genSprites[6];
void initDataSprite(Common::SeekableReadStream *f, DataSprite *d, int index, uint16 cenX, uint16 cenY); // Initializes the data sprite
// Main
void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, int superTop, int superBottom);
bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom);
void superSprite(DataSprite *dSprite, uint16 x, uint16 y, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom);
bool clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, uint16 superTop, uint16 superBottom);
void spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst);
void spriteNotAligned();
/*
* [Compression.cpp] Functions from Compression.GS

View File

@ -76,7 +76,7 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
uint16 numImages = f->readUint16LE();
d->_numImages = numImages;
//debug("Number of Frames: %d", numFrames);
//debug("Number of Frames: %d", numImages);
// Only here for dragon, but just in case, it's a high number so it should catch others
if (numImages >= 0x0200) {
@ -118,92 +118,110 @@ void ImmortalEngine::initDataSprite(Common::SeekableReadStream *f, DataSprite *d
d->_images = images;
}
bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, int superTop, int superBottom) {
bool ImmortalEngine::clipSprite(uint16 &height, uint16 &pointIndex, uint16 &skipY, DataSprite *dSprite, uint16 &pointX, uint16 &pointY, int img, uint16 bmw, uint16 superTop, uint16 superBottom) {
/* Something important to note here:
* In the source, bmw is not *2, and pointX is /2. However, the source
* was using a buffer of 2 pixels per byte. In ScummVM, the screen buffer
* is 1 pixel per byte. This means some calculations are slightly different.
*/
// This bit is to get the base index into the screen buffer, unless that's already been done, which is _lastPoint
if ((pointY != _lastY) || (bmw != _lastBMW)) {
_lastBMW = bmw;
_lastY = pointY;
if (pointY < 0x80) {
_lastPoint = pointY * bmw;
if (pointY < kMaskNeg) {
// The source does not double the bmw here to get the bytes, why not?
_lastPoint = pointY * (bmw * 2);
} else {
pointY = (pointY ^ 0xFF) + 1;
_lastPoint = pointY * bmw;
// Screen wrapping??
uint16 temp = (0 - pointY) + 1;
_lastPoint = temp * bmw;
_lastPoint = 0 - _lastPoint;
}
}
pointIndex = _lastPoint;
return false;
// Now we begin clipping, starting with totally offscreen
// We do this by checking if the sprite is above the top of the screen, or below the bottom of it
if (pointY > superBottom) {
return true;
} else if ((height + pointY) < superTop) {
} else if ((pointY + height) < superTop) {
return true;
// Now we actually clip top/bottom parts
/* The actual clipping is pretty simple:
* Lower height = stop drawing the sprite early. Higher SkipY = start drawing the sprite late
* So we just determine the delta for each based on superTop and superBottom
*/
} else {
// Starting with checking if any of the sprite is under the bottom of the screen
if ((height + pointY) >= superBottom) {
if ((pointY + height) >= superBottom) {
height = superBottom - pointY;
}
// Next we get the difference of overlap from the sprite if it is above the top
if ((superTop - pointY) < 0x8000) {
if (uint16((superTop - pointY)) < kMaskNeg) {
skipY = (superTop - pointY);
}
// The image is clipped, time to move the index to the sprite's first scanline base position
pointIndex += (pointX / 2) + dSprite->_images[img]._rectW;
pointIndex += (pointX) + dSprite->_images[img]._rectW;
}
return false;
}
void ImmortalEngine::spriteAligned(DataSprite *dSprite, Image &img, uint16 &skipY, uint16 &pointIndex, uint16 &height, uint16 bmw, byte *dst) {
//debug("draw the sprite");
/* This is an approximation of the sprite drawing system in the source.
* It is an approximation because the source needed to do some things
* that aren't relevant anymore, and it had some....creative solutions.
* For example, transparency was handled with a 256 byte table of masks
* that was indexed by the pixel itself, and used to find what nyble needed
* to be masked. However we are using a slightly different kind of screen buffer,
* and so I chose a more traditional method. Likewise, alignement was
* relevant for the source, but is not relevant here (thankfully, considering
* how confusing sprite drawing is when not an even position).
*/
byte pixel1 = 0;
byte pixel2 = 0;
debug("%d, %d, %04X", height, skipY, pointIndex);
byte pixel;
// For debug currently, align to the word by default
pointIndex &= 0xFFFE;
// Position is weird
pointIndex += 50;
debug("SPRITE START ------");
// For every scanline before height
for (int y = 0; y < height; y++, pointIndex += (bmw * 2)) {
//debug("%04X, %04X ", pointIndex, img._deltaPos[y]);
if (img._deltaPos[y] < 0x8000) {
// We increase the position by one screen width
if (img._deltaPos[y] < kMaskNeg) {
pointIndex += (img._deltaPos[y] * 2);
}
// And if the delta X for the line is positive, we add it. If negative we subtract
else {
pointIndex -= ((0 - img._deltaPos[y]) * 2);
}
// For every pixel in the scanline
for (int x = 0; x < img._scanWidth[y]; x++, pointIndex += 2) {
// SkipY defines the lines we don't draw because they are clipped
if (y >= skipY) {
//if (y > skipY) {
pixel = img._bitmap[y][x];
_screenBuff[pointIndex] = (pixel & kMask8High) >> 4;
_screenBuff[pointIndex + 1] = pixel & kMask8Low;
//}
// For handling transparency, I chose to simply check if the pixel is 0,
// as that is the transparent colour
pixel1 = (img._bitmap[y][x] & kMask8High) >> 4;
pixel2 = (img._bitmap[y][x] & kMask8Low);
if (pixel1 != 0) {
_screenBuff[pointIndex] = pixel1;
}
if (pixel2 != 0) {
_screenBuff[pointIndex + 1] = pixel2;
}
}
}
}
debug("SPRITE END -------");
}
void ImmortalEngine::spriteNotAligned() {
}
void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, int superTop, int superBottom) {
void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 pointY, int img, uint16 bmw, byte *dst, uint16 superTop, uint16 superBottom) {
// Main image construction routine
uint16 cenX = dSprite->_cenX;
@ -220,13 +238,9 @@ void ImmortalEngine::superSprite(DataSprite *dSprite, uint16 pointX, uint16 poin
// Normally I would just make the return from clip be reversed, but the idea is that the return would be 'offscreen == true'
if (!(clipSprite(height, pointIndex, skipY, dSprite, pointX, pointY, img, bmw, superTop, superBottom))) {
// Alignment is determined by whether the x position of the point is positive or negative
if (pointX >= 0x8000) {
spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
} else {
spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
}
// Alignment was a factor in the assembly because it was essentially 2 pixels per byte. However ScummVM is 1 pixel per byte
spriteAligned(dSprite, dSprite->_images[img], skipY, pointIndex, height, bmw, dst);
}
}