aboutsummaryrefslogtreecommitdiff
path: root/engines/startrek/actors.cpp
diff options
context:
space:
mode:
authorMatthew Stewart2018-08-02 05:20:23 -0400
committerEugene Sandulenko2018-08-09 08:37:30 +0200
commite5e54b8a1f937e2df1dfd141de9d4444257f9540 (patch)
tree36400c273a77bc516daa1caddba7b713bb41d1cd /engines/startrek/actors.cpp
parenta84746bf660f0413a205636edd39b484018e73b7 (diff)
downloadscummvm-rg350-e5e54b8a1f937e2df1dfd141de9d4444257f9540.tar.gz
scummvm-rg350-e5e54b8a1f937e2df1dfd141de9d4444257f9540.tar.bz2
scummvm-rg350-e5e54b8a1f937e2df1dfd141de9d4444257f9540.zip
STARTREK: Split up startrek.cpp into more files
Diffstat (limited to 'engines/startrek/actors.cpp')
-rw-r--r--engines/startrek/actors.cpp1421
1 files changed, 1421 insertions, 0 deletions
diff --git a/engines/startrek/actors.cpp b/engines/startrek/actors.cpp
new file mode 100644
index 0000000000..04c2a175c4
--- /dev/null
+++ b/engines/startrek/actors.cpp
@@ -0,0 +1,1421 @@
+/* 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
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "startrek/iwfile.h"
+#include "startrek/room.h"
+#include "startrek/startrek.h"
+
+namespace StarTrek {
+
+void StarTrekEngine::initActors() {
+ for (int i = 0; i < NUM_ACTORS; i++)
+ _actorList[i] = Actor();
+ for (int i = 0; i < MAX_BAN_FILES; i++)
+ _banFiles[i].reset();
+
+ strcpy(_kirkActor->animationString, "kstnd");
+ strcpy(_spockActor->animationString, "sstnd");
+ strcpy(_mccoyActor->animationString, "mstnd");
+ strcpy(_redshirtActor->animationString, "rstnd");
+}
+
+int StarTrekEngine::loadActorAnim(int actorIndex, const Common::String &animName, int16 x, int16 y, Fixed8 scale) {
+ debugC(6, kDebugGraphics, "Load animation '%s' on actor %d", animName.c_str(), actorIndex);
+
+ Actor *actor;
+
+ if (actorIndex == -1) {
+ // TODO
+ warning("loadActorAnim: actor == -1");
+ } else
+ actor = &_actorList[actorIndex];
+
+ if (actor->spriteDrawn) {
+ releaseAnim(actor);
+ drawActorToScreen(actor, animName, x, y, scale, false);
+ } else {
+ drawActorToScreen(actor, animName, x, y, scale, true);
+ }
+
+ actor->triggerActionWhenAnimFinished = false;
+ actor->finishedAnimActionParam = 0;
+
+ return actorIndex;
+}
+
+void StarTrekEngine::loadBanFile(const Common::String &name) {
+ debugC(kDebugGeneral, 7, "Load BAN file: %s.ban", name.c_str());
+ for (int i = 0; i < MAX_BAN_FILES; i++) {
+ if (!_banFiles[i]) {
+ _banFiles[i] = loadFile(name + ".ban");
+ _banFileOffsets[i] = 0;
+ return;
+ }
+ }
+
+ warning("Couldn't load .BAN file \"%s.ban\"", name.c_str());
+}
+
+bool StarTrekEngine::actorWalkToPosition(int actorIndex, const Common::String &animFile, int16 srcX, int16 srcY, int16 destX, int16 destY) {
+ debugC(6, "Obj %d: walk from (%d,%d) to (%d,%d)", actorIndex, srcX, srcY, destX, destY);
+
+ Actor *actor = &_actorList[actorIndex];
+
+ actor->triggerActionWhenAnimFinished = false;
+ if (isPositionSolid(destX, destY))
+ return false;
+
+ if (actor->spriteDrawn)
+ releaseAnim(actor);
+ else
+ _gfx->addSprite(&actor->sprite);
+
+ actor->spriteDrawn = true;
+ actor->animType = 1;
+ actor->frameToStartNextAnim = _frameIndex + 1;
+ strcpy(actor->animationString2, animFile.c_str());
+
+ actor->dest.x = destX;
+ actor->dest.y = destY;
+ actor->field92 = 0;
+ actor->triggerActionWhenAnimFinished = false;
+
+ actor->iwDestPosition = -1;
+ actor->iwSrcPosition = -1;
+
+ if (directPathExists(srcX, srcY, destX, destY)) {
+ chooseActorDirectionForWalking(actor, srcX, srcY, destX, destY);
+ updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
+ return true;
+ } else {
+ actor->iwSrcPosition = _iwFile->getClosestKeyPosition(srcX, srcY);
+ actor->iwDestPosition = _iwFile->getClosestKeyPosition(destX, destY);
+
+ if (actor->iwSrcPosition == -1 || actor->iwDestPosition == -1) {
+ // No path exists; face south by default.
+ strcat(actor->animationString2, "S");
+ actor->direction = 'S';
+
+ updateActorPositionWhileWalking(actor, srcX, srcY);
+ initStandAnim(actorIndex);
+
+ return false;
+ } else {
+ Common::Point iwSrc = _iwFile->_keyPositions[actor->iwSrcPosition];
+ chooseActorDirectionForWalking(actor, srcX, srcY, iwSrc.x, iwSrc.y);
+ updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
+ return true;
+ }
+ }
+}
+
+void StarTrekEngine::updateActorAnimations() {
+ for (int i = 0; i < NUM_ACTORS; i++) {
+ Actor *actor = &_actorList[i];
+ if (!actor->spriteDrawn)
+ continue;
+
+ switch (actor->animType) {
+ case 0: // Not walking?
+ case 2:
+ if (_frameIndex >= actor->frameToStartNextAnim) {
+ int nextAnimIndex = getRandomWord() & 3;
+ actor->animFile->seek(18 + nextAnimIndex + actor->animFrame * 22, SEEK_SET);
+ byte nextAnimFrame = actor->animFile->readByte();
+
+ if (actor->animFrame != nextAnimFrame) {
+ if (nextAnimFrame == actor->numAnimFrames - 1) {
+ actor->field62++;
+ if (actor->triggerActionWhenAnimFinished) {
+ addAction(ACTION_FINISHED_ANIMATION, actor->finishedAnimActionParam, 0, 0);
+ }
+ }
+ }
+
+ actor->animFrame = nextAnimFrame;
+ if (actor->animFrame >= actor->numAnimFrames) {
+ if (actor->animationString[0] == '\0')
+ removeActorFromScreen(i);
+ else
+ initStandAnim(i);
+ } else {
+ Sprite *sprite = &actor->sprite;
+
+ actor->animFile->seek(actor->animFrame * 22, SEEK_SET);
+ char animFrameFilename[16];
+ actor->animFile->read(animFrameFilename, 16);
+ sprite->setBitmap(loadAnimationFrame(animFrameFilename, actor->scale));
+
+ memset(actor->bitmapFilename, 0, 10);
+ strncpy(actor->bitmapFilename, animFrameFilename, 9);
+
+ actor->animFile->seek(10 + actor->animFrame * 22, SEEK_SET);
+ uint16 xOffset = actor->animFile->readUint16();
+ uint16 yOffset = actor->animFile->readUint16();
+ uint16 basePriority = actor->animFile->readUint16();
+ uint16 frames = actor->animFile->readUint16();
+
+ sprite->pos.x = xOffset + actor->pos.x;
+ sprite->pos.y = yOffset + actor->pos.y;
+ sprite->drawPriority = _gfx->getPriValue(0, yOffset + actor->pos.y) + basePriority;
+ sprite->bitmapChanged = true;
+
+ actor->frameToStartNextAnim = frames + _frameIndex;
+ }
+ }
+ break;
+ case 1: // Walking
+ if (_frameIndex < actor->frameToStartNextAnim)
+ break;
+ if (i == 0) // Kirk only
+ checkTouchedLoadingZone(actor->pos.x, actor->pos.y);
+ if (actor->field90 != 0) {
+ Sprite *sprite = &actor->sprite;
+ int loops;
+ if (getActorScaleAtPosition((actor->granularPosY + 0.5).toInt()) < 0.625)
+ loops = 1;
+ else
+ loops = 2;
+ for (int k = 0; k < loops; k++) {
+ if (actor->field90 == 0)
+ break;
+ actor->field90--;
+ Fixed16 newX = actor->granularPosX + actor->speedX;
+ Fixed16 newY = actor->granularPosY + actor->speedY;
+ if ((actor->field90 & 3) == 0) {
+ sprite->bitmap.reset();
+ updateActorPositionWhileWalking(actor, (newX + 0.5).toInt(), (newY + 0.5).toInt());
+ actor->field92++;
+ }
+
+ actor->granularPosX = newX;
+ actor->granularPosY = newY;
+ actor->frameToStartNextAnim = _frameIndex;
+ }
+ } else { // actor->field90 == 0
+ if (actor->iwSrcPosition == -1) {
+ if (actor->triggerActionWhenAnimFinished) {
+ actor->triggerActionWhenAnimFinished = false;
+ addAction(ACTION_FINISHED_WALKING, actor->finishedAnimActionParam & 0xff, 0, 0);
+ }
+
+ actor->sprite.bitmap.reset();
+ updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
+ initStandAnim(i);
+ } else { // actor->iwSrcPosition != -1
+ if (actor->iwSrcPosition == actor->iwDestPosition) {
+ actor->animationString2[strlen(actor->animationString2) - 1] = '\0';
+ actor->iwDestPosition = -1;
+ actor->iwSrcPosition = -1;
+ chooseActorDirectionForWalking(actor, actor->pos.x, actor->pos.y, actor->dest.x, actor->dest.y);
+ } else {
+ int index = _iwFile->_iwEntries[actor->iwSrcPosition][actor->iwDestPosition];
+ actor->iwSrcPosition = index;
+ Common::Point dest = _iwFile->_keyPositions[actor->iwSrcPosition];
+ actor->animationString2[strlen(actor->animationString2) - 1] = '\0';
+ chooseActorDirectionForWalking(actor, actor->pos.x, actor->pos.y, dest.x, dest.y);
+ }
+ }
+ }
+ break;
+ default:
+ error("Invalid anim type.");
+ break;
+ }
+ }
+}
+
+void StarTrekEngine::renderBanBelowSprites() {
+ if ((_frameIndex & 3) != 0)
+ return;
+
+ byte *screenPixels = _gfx->lockScreenPixels();
+ byte *bgPixels = _gfx->getBackgroundPixels();
+
+ for (int i = 0; i < MAX_BAN_FILES; i++) {
+ if (!_banFiles[i])
+ continue;
+
+ // TODO: video modes other than VGA
+
+ _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
+ uint16 offset = _banFiles[i]->readUint16();
+
+ if (offset == 0xffff) {
+ _banFileOffsets[i] = 0;
+ _banFiles[i]->seek(0, SEEK_SET);
+ offset = _banFiles[i]->readSint16();
+ }
+
+ int16 size = _banFiles[i]->readSint16();
+ if (size != 0) {
+ _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
+ renderBan(screenPixels, _banFiles[i]);
+
+ _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
+ renderBan(bgPixels, _banFiles[i]);
+ }
+
+ }
+
+ _gfx->unlockScreenPixels();
+}
+
+void StarTrekEngine::renderBan(byte *destPixels, SharedPtr<FileStream> banFile) {
+ uint16 offset = banFile->readUint16();
+ int32 size = banFile->readUint16();
+
+ byte *dest = destPixels + offset;
+
+ // Skip 8 bytes (rectangle encompassing the area being drawn to)
+ banFile->readSint32();
+ banFile->readSint32();
+
+ while (--size >= 0) {
+ assert(dest >= destPixels && dest < destPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
+ int8 b = banFile->readByte();
+
+ if (b == -128) // Add value to destination (usually jumping to next row)
+ dest += banFile->readUint16();
+ else if (b < 0) { // Repeated byte
+ byte c = banFile->readByte();
+ if (c == 0)
+ dest += (-b) + 1;
+ else {
+ for (int j = 0; j < (-b) + 1; j++)
+ (*dest++) = c;
+ }
+ } else { // List of bytes
+ b++;
+ while (b-- != 0) {
+ byte c = banFile->readByte();
+ if (c == 0)
+ dest++;
+ else
+ *(dest++) = c;
+ }
+ }
+ }
+}
+
+void StarTrekEngine::renderBanAboveSprites() {
+ if ((_frameIndex & 3) != 0)
+ return;
+
+ for (int i = 0; i < MAX_BAN_FILES; i++) {
+ if (!_banFiles[i])
+ continue;
+
+ _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
+ uint16 offset = _banFiles[i]->readUint16();
+
+ if (offset == 0xffff) {
+ _banFileOffsets[i] = 0;
+ _banFiles[i]->seek(0, SEEK_SET);
+ offset = _banFiles[i]->readSint16();
+ }
+
+ int16 size = _banFiles[i]->readSint16();
+ if (size != 0) {
+ Common::Rect rect;
+ rect.left = _banFiles[i]->readSint16();
+ rect.top = _banFiles[i]->readSint16();
+ rect.right = _banFiles[i]->readSint16() + 1;
+ rect.bottom = _banFiles[i]->readSint16() + 1;
+
+ // Draw all sprites in this rectangle to a custom surface, and only update the
+ // specific pixels that were updated by the BAN file this frame.
+ // Rationale behind this is that, since the background may not have been
+ // redrawn, the transparent sprites (ie. textboxes) would further darken any
+ // pixels behind them that haven't been updated this frame. So, we can't just
+ // update everything in this rectangle.
+ // FIXME: This copies the entire screen surface for temporary drawing, which
+ // is somewhat wasteful. Original game had one more graphics layer it drew to
+ // before the screen was updated...
+ ::Graphics::Surface surface;
+ _gfx->drawAllSpritesInRectToSurface(rect, &surface);
+
+ byte *destPixels = _gfx->lockScreenPixels();
+ byte *src = (byte *)surface.getPixels() + offset;
+ byte *dest = destPixels + offset;
+
+ // This is similar to renderBan(), except it copies pixels from the surface
+ // above instead of drawing directly to it. (Important since sprites may be
+ // drawn on top.)
+ while (--size >= 0) {
+ assert(dest >= destPixels && dest < destPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
+ int8 b = _banFiles[i]->readByte();
+ if (b == -128) {
+ uint16 skip = _banFiles[i]->readUint16();
+ dest += skip;
+ src += skip;
+ } else if (b < 0) {
+ byte c = _banFiles[i]->readByte();
+ if (c == 0) {
+ dest += (-b) + 1;
+ src += (-b) + 1;
+ }
+ else {
+ for (int j = 0; j < (-b) + 1; j++)
+ *(dest++) = *(src++);
+ }
+ } else {
+ b++;
+ while (b-- != 0) {
+ byte c = _banFiles[i]->readByte();
+ if (c == 0) {
+ dest++;
+ src++;
+ } else
+ *(dest++) = *(src++);
+ }
+ }
+ }
+
+ _gfx->unlockScreenPixels();
+ surface.free();
+
+ _banFileOffsets[i] = _banFiles[i]->pos();
+ }
+ }
+}
+
+void StarTrekEngine::removeActorFromScreen(int actorIndex) {
+ Actor *actor = &_actorList[actorIndex];
+
+ if (actor->spriteDrawn != 1)
+ return;
+
+ debugC(6, kDebugGraphics, "Stop drawing actor %d", actorIndex);
+
+ Sprite *sprite = &actor->sprite;
+ sprite->field16 = true;
+ sprite->bitmapChanged = true;
+ _gfx->drawAllSprites();
+ _gfx->delSprite(sprite);
+ releaseAnim(actor);
+}
+
+void StarTrekEngine::actorFunc1() {
+ for (int i = 0; i < NUM_ACTORS; i++) {
+ if (_actorList[i].spriteDrawn == 1) {
+ removeActorFromScreen(i);
+ }
+ }
+
+ for (int i = 0; i < MAX_BAN_FILES; i++) {
+ _banFiles[i].reset();
+ }
+}
+
+void StarTrekEngine::drawActorToScreen(Actor *actor, const Common::String &_animName, int16 x, int16 y, Fixed8 scale, bool addSprite) {
+ Common::String animFilename = _animName;
+ if (_animName.hasPrefixIgnoreCase("stnd") /* && word_45d20 == -1 */) // TODO
+ animFilename += 'j';
+ memcpy(actor->animFilename, _animName.c_str(), sizeof(actor->animFilename));
+
+ actor->animType = 2;
+ actor->animFile = loadFile(animFilename + ".anm");
+ actor->numAnimFrames = actor->animFile->size() / 22;
+ actor->animFrame = 0;
+ actor->pos.x = x;
+ actor->pos.y = y;
+ actor->field62 = 0;
+ actor->scale = scale;
+
+ actor->animFile->seek(16, SEEK_SET);
+ actor->frameToStartNextAnim = actor->animFile->readUint16() + _frameIndex;
+
+ char firstFrameFilename[11];
+ actor->animFile->seek(0, SEEK_SET);
+ actor->animFile->read(firstFrameFilename, 10);
+ firstFrameFilename[10] = '\0';
+
+ Sprite *sprite = &actor->sprite;
+ if (addSprite)
+ _gfx->addSprite(sprite);
+
+ sprite->setBitmap(loadAnimationFrame(firstFrameFilename, scale));
+ memset(actor->bitmapFilename, 0, sizeof(char) * 10);
+ strncpy(actor->bitmapFilename, firstFrameFilename, sizeof(char) * 9);
+
+ actor->scale = scale;
+
+ actor->animFile->seek(10, SEEK_SET);
+ uint16 xOffset = actor->animFile->readUint16();
+ uint16 yOffset = actor->animFile->readUint16();
+ uint16 basePriority = actor->animFile->readUint16();
+
+ sprite->pos.x = xOffset + actor->pos.x;
+ sprite->pos.y = yOffset + actor->pos.y;
+ sprite->drawPriority = _gfx->getPriValue(0, yOffset + actor->pos.y) + basePriority;
+ sprite->bitmapChanged = true;
+
+ actor->spriteDrawn = 1;
+}
+
+void StarTrekEngine::releaseAnim(Actor *actor) {
+ switch (actor->animType) {
+ case 0:
+ case 2:
+ actor->sprite.bitmap.reset();
+ actor->animFile.reset();
+ break;
+ case 1:
+ actor->sprite.bitmap.reset();
+ break;
+ default:
+ error("Invalid anim type");
+ break;
+ }
+
+ actor->spriteDrawn = 0;
+}
+
+void StarTrekEngine::initStandAnim(int actorIndex) {
+ Actor *actor = &_actorList[actorIndex];
+
+ if (!actor->spriteDrawn)
+ error("initStandAnim: dead anim");
+
+ ////////////////////
+ // sub_239d2
+ const char *directions = "nsew";
+
+ if (actorIndex >= 0 && actorIndex <= 3) {
+ int8 dir = _awayMission.crewDirectionsAfterWalk[actorIndex];
+ if (dir != -1) {
+ actor->direction = directions[dir];
+ _awayMission.crewDirectionsAfterWalk[actorIndex] = -1;
+ }
+ }
+ // end of sub_239d2
+ ////////////////////
+
+ Common::String animName;
+ if (actor->direction != 0)
+ animName = Common::String(actor->animationString) + (char)actor->direction;
+ else // Default to facing south
+ animName = Common::String(actor->animationString) + 's';
+
+ Fixed8 scale = getActorScaleAtPosition(actor->pos.y);
+ loadActorAnim(actorIndex, animName, actor->pos.x, actor->pos.y, scale);
+ actor->animType = 0;
+}
+
+void StarTrekEngine::updateActorPositionWhileWalking(Actor *actor, int16 x, int16 y) {
+ actor->scale = getActorScaleAtPosition(y);
+ Common::String animName = Common::String::format("%s%02d", actor->animationString2, actor->field92 & 7);
+ actor->sprite.setBitmap(loadAnimationFrame(animName, actor->scale));
+
+ memset(actor->bitmapFilename, 0, 10);
+ strncpy(actor->bitmapFilename, animName.c_str(), 9);
+
+ Sprite *sprite = &actor->sprite;
+ sprite->drawPriority = _gfx->getPriValue(0, y);
+ sprite->pos.x = x;
+ sprite->pos.y = y;
+ sprite->bitmapChanged = true;
+
+ actor->frameToStartNextAnim = _frameIndex;
+ actor->pos.x = x;
+ actor->pos.y = y;
+}
+
+void StarTrekEngine::chooseActorDirectionForWalking(Actor *actor, int16 srcX, int16 srcY, int16 destX, int16 destY) {
+ actor->granularPosX = srcX;
+ actor->granularPosY = srcY;
+
+ int16 distX = destX - srcX;
+ int16 distY = destY - srcY;
+ int16 absDistX = abs(distX);
+ int16 absDistY = abs(distY);
+
+ if (absDistX > absDistY) {
+ char d;
+ if (distX > 0)
+ d = 'E';
+ else
+ d = 'W';
+
+ // Append direction to animation string
+ actor->animationString2[strlen(actor->animationString2) + 1] = '\0';
+ actor->animationString2[strlen(actor->animationString2)] = d;
+
+ actor->direction = d;
+ actor->field90 = absDistX;
+
+ if (distX != 0) {
+ if (distX > 0)
+ actor->speedX = 1.0;
+ else
+ actor->speedX = -1.0;
+
+ actor->speedY = Fixed16(distY) / absDistX;
+ }
+ } else {
+ char d;
+ if (distY > 0)
+ d = 'S';
+ else
+ d = 'N';
+
+ // Append direction to animation string
+ actor->animationString2[strlen(actor->animationString2) + 1] = '\0';
+ actor->animationString2[strlen(actor->animationString2)] = d;
+
+ actor->direction = d;
+ actor->field90 = absDistY;
+
+ if (distY != 0) {
+ if (distY > 0)
+ actor->speedY = 1.0;
+ else
+ actor->speedY = -1.0;
+
+ actor->speedX = Fixed16(distX) / absDistY;
+ }
+ }
+}
+
+bool StarTrekEngine::directPathExists(int16 srcX, int16 srcY, int16 destX, int16 destY) {
+ int32 distX = destX - srcX;
+ int32 distY = destY - srcY;
+
+ int32 absDistX = abs(distX);
+ int32 absDistY = abs(distY);
+
+ int32 distCounter;
+ Fixed16 speedX, speedY;
+
+ if (absDistX > absDistY) {
+ distCounter = absDistX;
+
+ if (distCounter == 0)
+ return true;
+
+ speedY = Fixed16(distY) / absDistX;
+
+ if (distX > 0)
+ speedX = 1.0;
+ else
+ speedX = -1.0;
+ } else { // absDistX <= absDistY
+ distCounter = absDistY;
+
+ if (distCounter == 0)
+ return true;
+
+ speedX = Fixed16(distX) / absDistY;
+
+ if (distY > 0)
+ speedY = 1.0;
+ else
+ speedY = -1.0;
+ }
+
+ Fixed16 fixedX = srcX;
+ Fixed16 fixedY = srcY;
+
+ if (isPositionSolid((fixedX + 0.5).toInt(), (fixedY + 0.5).toInt()))
+ return false;
+
+ while (distCounter-- > 0) {
+ fixedX += speedX;
+ fixedY += speedY;
+
+ if (isPositionSolid((fixedX + 0.5).toInt(), (fixedY + 0.5).toInt()))
+ return false;
+ }
+
+ return true;
+}
+
+int StarTrekEngine::findObjectAt(int x, int y) {
+ Sprite *sprite = _gfx->getSpriteAt(x, y);
+
+ if (sprite != nullptr) {
+ if (sprite == &_inventoryIconSprite)
+ return OBJECT_INVENTORY_ICON;
+ else if (sprite == &_itemIconSprite)
+ return _awayMission.activeObject;
+
+ for (int i = 0; i < NUM_ACTORS; i++) {
+ Actor *actor = &_actorList[i];
+ if (sprite == &actor->sprite)
+ return i;
+ }
+
+ error("findObject: Clicked on an unknown sprite");
+ }
+
+ _objectHasWalkPosition = false;
+ int actionBit = 1 << (_awayMission.activeAction - 1);
+ int offset = _room->getFirstHotspot();
+
+ while (offset != _room->getHotspotEnd()) {
+ uint16 word = _room->readRdfWord(offset);
+ if (word & 0x8000) {
+ if ((word & actionBit) && isPointInPolygon((int16 *)(_room->_rdfData + offset + 6), x, y)) {
+ int actorIndex = _room->readRdfWord(offset + 6);
+ _objectHasWalkPosition = true;
+ _objectWalkPosition.x = _room->readRdfWord(offset + 2);
+ _objectWalkPosition.y = _room->readRdfWord(offset + 4);
+ return actorIndex;
+ }
+
+ int numVertices = _room->readRdfWord(offset + 8);
+ offset = offset + 10 + numVertices * 4;
+ } else {
+ if (isPointInPolygon((int16 *)(_room->_rdfData + offset), x, y)) {
+ int actorIndex = _room->readRdfWord(offset);
+ return actorIndex;
+ }
+
+ int numVertices = _room->readRdfWord(offset + 2);
+ offset = offset + 4 + numVertices * 4;
+ }
+ }
+
+ return -1;
+}
+
+SharedPtr<Bitmap> StarTrekEngine::loadAnimationFrame(const Common::String &filename, Fixed8 scale) {
+ SharedPtr<Bitmap> bitmapToReturn;
+
+ char basename[5];
+ strncpy(basename, filename.c_str() + 1, 4);
+ basename[4] = '\0';
+
+ char c = filename[0];
+ if ((strcmp(basename, "stnd") == 0 || strcmp(basename, "tele") == 0)
+ && (c == 'm' || c == 's' || c == 'k' || c == 'r')) {
+ if (c == 'm') {
+ // Mccoy has the "base" animations for all crewmen
+ bitmapToReturn = _gfx->loadBitmap(filename);
+ } else {
+ // All crewman other than mccoy copy the animation frames from mccoy, change
+ // the colors of the uniforms, and load an "xor" file to redraw the face.
+
+ // TODO: The ".$bm" extension is a "virtual file"? Caches the changes to the
+ // file made here?
+ // bitmapToReturn = _gfx->loadBitmap(filename + ".$bm");
+
+ if (bitmapToReturn == nullptr) {
+ Common::String mccoyFilename = filename;
+ mccoyFilename.setChar('m', 0);
+ SharedPtr<Bitmap> bitmap = _gfx->loadBitmap(mccoyFilename);
+
+ uint16 width = bitmap->width;
+ uint16 height = bitmap->height;
+
+ bitmapToReturn = SharedPtr<Bitmap>(new Bitmap(width, height));
+ bitmapToReturn->xoffset = bitmap->xoffset;
+ bitmapToReturn->yoffset = bitmap->yoffset;
+
+ // Change uniform color
+ int16 colorShift;
+ switch (c) {
+ case 'k': // Kirk
+ colorShift = 8;
+ break;
+ case 'r': // Redshirt
+ colorShift = -8;
+ break;
+ case 's': // Spock
+ colorShift = 0;
+ break;
+ }
+
+ if (colorShift == 0) {
+ memcpy(bitmapToReturn->pixels, bitmap->pixels, width * height);
+ } else {
+ byte *src = bitmap->pixels;
+ byte *dest = bitmapToReturn->pixels;
+ byte baseUniformColor = 0xa8;
+
+ for (int i = 0; i < width * height; i++) {
+ byte b = *src++;
+ if (b >= baseUniformColor && b < baseUniformColor + 8)
+ *dest++ = b + colorShift;
+ else
+ *dest++ = b;
+ }
+ }
+
+ // Redraw face with xor file
+ SharedPtr<FileStream> xorFile = loadFile(filename + ".xor");
+ xorFile->seek(0, SEEK_SET);
+ uint16 xoffset = bitmap->xoffset - xorFile->readUint16();
+ uint16 yoffset = bitmap->yoffset - xorFile->readUint16();
+ uint16 xorWidth = xorFile->readUint16();
+ uint16 xorHeight = xorFile->readUint16();
+
+ byte *dest = bitmapToReturn->pixels + yoffset * bitmap->width + xoffset;
+
+ for (int i = 0; i < xorHeight; i++) {
+ for (int j = 0; j < xorWidth; j++)
+ *dest++ ^= xorFile->readByte();
+ dest += (bitmap->width - xorWidth);
+ }
+ }
+ }
+ } else {
+ // TODO: when loading a bitmap, it passes a different argument than is standard to
+ // the "file loading with cache" function...
+ bitmapToReturn = _gfx->loadBitmap(filename);
+ }
+
+ if (scale != 1.0) {
+ bitmapToReturn = scaleBitmap(bitmapToReturn, scale);
+ }
+
+ return bitmapToReturn;
+}
+
+
+int StarTrekEngine::selectObjectForUseAction() {
+ while (true) {
+ if (!(_awayMission.crewDownBitset & (1 << OBJECT_KIRK)))
+ showInventoryIcons(false);
+
+ TrekEvent event;
+
+ while (true) {
+ if (!getNextEvent(&event))
+ continue;
+
+ if (event.type == TREKEVENT_TICK) {
+ updateMouseBitmap();
+ _gfx->drawAllSprites();
+ _sound->checkLoopMusic();
+ } else if (event.type == TREKEVENT_LBUTTONDOWN) {
+ removeNextEvent();
+ break;
+ } else if (event.type == TREKEVENT_MOUSEMOVE) {
+ } else if (event.type == TREKEVENT_RBUTTONDOWN) {
+ // Allow this to be processed by main away mission loop
+ break;
+ } else if (event.type == TREKEVENT_KEYDOWN) {
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE
+ || event.kbd.keycode == Common::KEYCODE_w
+ || event.kbd.keycode == Common::KEYCODE_t
+ || event.kbd.keycode == Common::KEYCODE_u
+ || event.kbd.keycode == Common::KEYCODE_g
+ || event.kbd.keycode == Common::KEYCODE_l
+ || event.kbd.keycode == Common::KEYCODE_SPACE
+ || event.kbd.keycode == Common::KEYCODE_F2) {
+ // Allow these buttons to be processed by main away mission loop
+ break;
+ } else if (event.kbd.keycode == Common::KEYCODE_i) {
+ removeNextEvent();
+ break;
+ } else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER || event.kbd.keycode == Common::KEYCODE_F1) {
+ // Simulate left-click
+ removeNextEvent();
+ event.type = TREKEVENT_LBUTTONDOWN;
+ break;
+ }
+ }
+
+ removeNextEvent();
+ }
+
+ if (event.type == TREKEVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_i) {
+ hideInventoryIcons();
+ int clickedObject = showInventoryMenu(50, 50, true);
+ if (clickedObject == -1)
+ continue;
+ return clickedObject;
+ } else if (event.type == TREKEVENT_LBUTTONDOWN) {
+ int clickedObject = findObjectAt(_gfx->getMousePos());
+ hideInventoryIcons();
+
+ if (clickedObject == -1)
+ continue;
+ else if (isObjectUnusable(clickedObject, ACTION_USE))
+ continue;
+ else if (clickedObject == OBJECT_INVENTORY_ICON) {
+ clickedObject = showInventoryMenu(50, 50, false);
+ if (clickedObject == -1)
+ continue;
+ else
+ return clickedObject;
+ } else if (clickedObject <= OBJECT_REDSHIRT)
+ return clickedObject;
+ else if (isObjectUnusable(OBJECT_KIRK, ACTION_USE))
+ continue;
+ else if (_room->actionHasCode(ACTION_USE, OBJECT_KIRK, clickedObject, 0)
+ || _room->actionHasCode(ACTION_GET, clickedObject, 0, 0)
+ || _room->actionHasCode(ACTION_WALK, clickedObject, 0, 0)) {
+ _awayMission.activeObject = OBJECT_KIRK;
+ _awayMission.passiveObject = clickedObject;
+ _awayMission.activeAction = ACTION_USE;
+ clickedObject = OBJECT_KIRK;
+ if (!walkActiveObjectToHotspot())
+ addAction(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0);
+ return clickedObject;
+ } else
+ continue;
+ } else {
+ hideInventoryIcons();
+ return -1;
+ }
+ }
+}
+
+Common::String StarTrekEngine::getCrewmanAnimFilename(int actorIndex, const Common::String &basename) {
+ const char *crewmanChars = "ksmr";
+ assert(actorIndex >= 0 && actorIndex < 4);
+ return crewmanChars[actorIndex] + basename;
+}
+
+void StarTrekEngine::updateMouseBitmap() {
+ const bool worksOnCrewmen[] = { // True if the action reacts with crewmen
+ false, // ACTION_WALK
+ true, // ACTION_USE
+ false, // ACTION_GET
+ true, // ACTION_LOOK
+ true // ACTION_TALK
+ };
+ const bool worksOnActors[] = { // True if the action reacts with other objects
+ false, // ACTION_WALK
+ true, // ACTION_USE
+ true, // ACTION_GET
+ true, // ACTION_LOOK
+ true // ACTION_TALK
+ };
+ const bool worksOnHotspots[] = { // True if the action reacts with hotspots
+ false, // ACTION_WALK
+ true, // ACTION_USE
+ true, // ACTION_GET
+ true, // ACTION_LOOK
+ false // ACTION_TALK
+ };
+
+ Common::Point mousePos = _gfx->getMousePos();
+ int selected = findObjectAt(mousePos.x, mousePos.y);
+ int action = _awayMission.activeAction;
+ assert(action >= 1 && action <= 5);
+
+ bool withRedOutline;
+
+ if (selected >= 0 && selected <= 3 && worksOnCrewmen[action - 1])
+ withRedOutline = true;
+ else if (selected > 3 && selected < NUM_ACTORS && worksOnActors[action - 1])
+ withRedOutline = true;
+ else if (selected >= NUM_ACTORS && selected < HOTSPOTS_END && worksOnHotspots[action - 1])
+ withRedOutline = true;
+ else
+ withRedOutline = false;
+
+ chooseMouseBitmapForAction(action, withRedOutline);
+}
+
+bool StarTrekEngine::walkActiveObjectToHotspot() {
+ if (!_objectHasWalkPosition)
+ return false;
+
+ int objectIndex;
+ if (_awayMission.activeAction != ACTION_USE)
+ objectIndex = OBJECT_KIRK;
+ else if (_awayMission.activeObject <= OBJECT_REDSHIRT)
+ objectIndex = _awayMission.activeObject;
+ else if (_awayMission.activeObject >= ITEMS_START && _awayMission.activeObject <= ITEMS_END) { // FIXME: "<= ITEMS_END" doesn't make sense?
+ if (_awayMission.activeObject == OBJECT_ISTRICOR)
+ objectIndex = OBJECT_SPOCK;
+ else if (_awayMission.activeObject == OBJECT_IMTRICOR)
+ objectIndex = OBJECT_MCCOY;
+ else
+ objectIndex = OBJECT_KIRK;
+ } else // This is the original error message...
+ error("Jay didn't think about pmcheck");
+
+ byte finishedAnimActionParam = false;
+ bool walk = false;
+
+ if (_awayMission.activeAction == ACTION_WALK)
+ walk = true;
+ else {
+ // If this action has code defined for it in this room, buffer the action to be
+ // done after the object finished walking there.
+ Action action = {_awayMission.activeAction, _awayMission.activeObject, 0, 0};
+ if (_awayMission.activeAction == ACTION_USE)
+ action.b2 = _awayMission.passiveObject;
+
+ if (_room->actionHasCode(action)) {
+ for (int i = 0; i < MAX_BUFFERED_WALK_ACTIONS; i++) {
+ if (!_actionOnWalkCompletionInUse[i]) {
+ finishedAnimActionParam = i + 0xe0;
+ _actionOnWalkCompletionInUse[i] = true;
+ _actionOnWalkCompletion[i] = action;
+ walk = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (walk) {
+ Actor *actor = &_actorList[objectIndex];
+ Common::String anim = getCrewmanAnimFilename(objectIndex, "walk");
+ actorWalkToPosition(objectIndex, anim, actor->pos.x, actor->pos.y, _objectWalkPosition.x, _objectWalkPosition.y);
+ if (finishedAnimActionParam != 0) {
+ actor->triggerActionWhenAnimFinished = true;
+ actor->finishedAnimActionParam = finishedAnimActionParam;
+ }
+ _objectHasWalkPosition = false;
+ return true;
+ } else {
+ _objectHasWalkPosition = false;
+ return false;
+ }
+}
+
+void StarTrekEngine::showInventoryIcons(bool showItem) {
+ const char *crewmanFilenames[] = {
+ "ikirk",
+ "ispock",
+ "imccoy",
+ "iredshir"
+ };
+
+ Common::String itemFilename;
+
+ if (showItem) {
+ int i = _awayMission.activeObject;
+ if (i >= OBJECT_KIRK && i <= OBJECT_REDSHIRT)
+ itemFilename = crewmanFilenames[i];
+ else {
+ assert(i >= ITEMS_START && i < ITEMS_END);
+ Item *item = &_itemList[i - ITEMS_START];
+ itemFilename = item->name;
+ }
+ }
+
+ if (itemFilename.empty())
+ _inventoryIconSprite.pos.x = 10;
+ else {
+ _gfx->addSprite(&_itemIconSprite);
+ _itemIconSprite.drawMode = 2;
+ _itemIconSprite.pos.x = 10;
+ _itemIconSprite.pos.y = 10;
+ _itemIconSprite.drawPriority = 15;
+ _itemIconSprite.drawPriority2 = 8;
+ _itemIconSprite.setBitmap(_gfx->loadBitmap(itemFilename));
+
+ _inventoryIconSprite.pos.x = 46;
+ }
+
+ _gfx->addSprite(&_inventoryIconSprite);
+
+ _inventoryIconSprite.pos.y = 10;
+ _inventoryIconSprite.drawMode = 2;
+ _inventoryIconSprite.drawPriority = 15;
+ _inventoryIconSprite.drawPriority2 = 8;
+ _inventoryIconSprite.setBitmap(_gfx->loadBitmap("inv00"));
+}
+
+bool StarTrekEngine::isObjectUnusable(int object, int action) {
+ if (action == ACTION_LOOK)
+ return false;
+ if (object == OBJECT_REDSHIRT && _awayMission.redshirtDead)
+ return true;
+ if (object >= OBJECT_KIRK && object <= OBJECT_REDSHIRT && (_awayMission.crewDownBitset & (1 << object)))
+ return true;
+ if (object == OBJECT_IMTRICOR && (_awayMission.crewDownBitset & (1 << OBJECT_MCCOY)))
+ return true;
+ if (object == OBJECT_ISTRICOR && (_awayMission.crewDownBitset & (1 << OBJECT_SPOCK)))
+ return true;
+ return false;
+}
+
+void StarTrekEngine::hideInventoryIcons() {
+ // Clear these sprites from the screen
+ if (_itemIconSprite.drawMode == 2)
+ _itemIconSprite.dontDrawNextFrame();
+ if (_inventoryIconSprite.drawMode == 2)
+ _inventoryIconSprite.dontDrawNextFrame();
+
+ _gfx->drawAllSprites();
+
+ if (_itemIconSprite.drawMode == 2) {
+ _gfx->delSprite(&_itemIconSprite);
+ _itemIconSprite.drawMode = 0;
+ _itemIconSprite.bitmap.reset();
+ }
+
+ if (_inventoryIconSprite.drawMode == 2) {
+ _gfx->delSprite(&_inventoryIconSprite);
+ _inventoryIconSprite.drawMode = 0;
+ _inventoryIconSprite.bitmap.reset();
+ }
+}
+
+void StarTrekEngine::updateCrewmanGetupTimers() {
+ if (_awayMission.crewDownBitset == 0)
+ return;
+ for (int i = OBJECT_KIRK; i <= OBJECT_REDSHIRT; i++) {
+ Actor *actor = &_actorList[i];
+
+ if (!(_awayMission.crewDownBitset & (1 << i)))
+ continue;
+
+ _awayMission.crewGetupTimers[i]--;
+ if (_awayMission.crewGetupTimers[i] <= 0) {
+ Common::String anim = getCrewmanAnimFilename(i, "getu");
+ int8 dir = _awayMission.crewDirectionsAfterWalk[i];
+ char d;
+ if (dir == -1) {
+ d = actor->direction;
+ } else {
+ const char *dirs = "nsew";
+ Fixed8 scale = getActorScaleAtPosition(actor->sprite.pos.y);
+ d = dirs[dir];
+
+ int16 xOffset = 0, yOffset = 0;
+ if (d == 'n') {
+ xOffset = -24;
+ yOffset = -8;
+ } else if (d == 'w') {
+ xOffset = -35;
+ yOffset = -12;
+ }
+ actor->sprite.pos.x += scale.multToInt(xOffset);
+ actor->sprite.pos.y += scale.multToInt(yOffset);
+ }
+
+ anim += (char)d;
+ loadActorAnimWithRoomScaling(i, anim, actor->sprite.pos.x, actor->sprite.pos.y);
+ _awayMission.crewDownBitset &= ~(1 << i);
+ }
+ }
+}
+
+int StarTrekEngine::showInventoryMenu(int x, int y, bool restoreMouse) {
+ const int ITEMS_PER_ROW = 5;
+
+ Common::Point oldMousePos = _gfx->getMousePos();
+ bool keyboardControlledMouse = _keyboardControlsMouse;
+ _keyboardControlsMouse = false;
+
+ int itemIndex = 0;
+ int numItems = 0;
+
+ char itemNames[NUM_OBJECTS][10];
+ Common::Point itemPositions[NUM_OBJECTS];
+ int16 itemIndices[NUM_OBJECTS];
+
+ while (itemIndex < NUM_OBJECTS) {
+ if (_itemList[itemIndex].have) {
+ strcpy(itemNames[numItems], _itemList[itemIndex].name);
+
+ int16 itemX = (numItems % ITEMS_PER_ROW) * 32 + x;
+ int16 itemY = (numItems / ITEMS_PER_ROW) * 32 + y;
+ itemPositions[numItems] = Common::Point(itemX, itemY);
+ itemIndices[numItems] = _itemList[itemIndex].field2;
+
+ numItems++;
+ }
+ itemIndex++;
+ }
+
+ Sprite itemSprites[NUM_OBJECTS];
+
+ for (int i = 0; i < numItems; i++) {
+ _gfx->addSprite(&itemSprites[i]);
+
+ itemSprites[i].drawMode = 2;
+ itemSprites[i].pos.x = itemPositions[i].x;
+ itemSprites[i].pos.y = itemPositions[i].y;
+ itemSprites[i].drawPriority = 15;
+ itemSprites[i].drawPriority2 = 8;
+ itemSprites[i].setBitmap(_gfx->loadBitmap(itemNames[i]));
+ }
+
+ chooseMousePositionFromSprites(itemSprites, numItems, -1, 4);
+ bool displayMenu = true;
+ int lastItemIndex = -1;
+
+ while (displayMenu) {
+ _sound->checkLoopMusic();
+
+ TrekEvent event;
+ if (!getNextEvent(&event))
+ continue;
+
+ switch (event.type) {
+ case TREKEVENT_TICK: {
+ Common::Point mousePos = _gfx->getMousePos();
+ itemIndex = getMenuButtonAt(itemSprites, numItems, mousePos.x, mousePos.y);
+ if (itemIndex != lastItemIndex) {
+ if (lastItemIndex != -1) {
+ drawMenuButtonOutline(itemSprites[lastItemIndex].bitmap, 0);
+ itemSprites[lastItemIndex].bitmapChanged = true;
+ }
+ if (itemIndex != -1) {
+ drawMenuButtonOutline(itemSprites[itemIndex].bitmap, 15);
+ itemSprites[itemIndex].bitmapChanged = true;
+ }
+ lastItemIndex = itemIndex;
+ }
+ _gfx->drawAllSprites();
+ break;
+ }
+
+ case TREKEVENT_LBUTTONDOWN:
+exitWithSelection:
+ displayMenu = false;
+ break;
+
+ case TREKEVENT_RBUTTONDOWN:
+exitWithoutSelection:
+ displayMenu = false;
+ lastItemIndex = -1;
+ break;
+
+ case TREKEVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ case Common::KEYCODE_F2:
+ goto exitWithoutSelection;
+
+ case Common::KEYCODE_RETURN:
+ case Common::KEYCODE_KP_ENTER:
+ case Common::KEYCODE_F1:
+ goto exitWithSelection;
+
+ case Common::KEYCODE_HOME:
+ case Common::KEYCODE_KP7:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 4);
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ case Common::KEYCODE_PAGEUP:
+ case Common::KEYCODE_KP9:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 2);
+ break;
+
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_KP4:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 1);
+ break;
+
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_KP6:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 0);
+ break;
+
+ case Common::KEYCODE_END:
+ case Common::KEYCODE_KP1:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 5);
+ break;
+
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ case Common::KEYCODE_PAGEDOWN:
+ case Common::KEYCODE_KP3:
+ chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 3);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ removeNextEvent();
+ }
+
+ playSoundEffectIndex(0x10);
+ if (lastItemIndex >= 0)
+ drawMenuButtonOutline(itemSprites[lastItemIndex].bitmap, 0);
+
+ for (int i = 0; i < numItems; i++)
+ itemSprites[i].dontDrawNextFrame();
+
+ _gfx->drawAllSprites();
+
+ for (int i = 0; i < numItems; i++) {
+ itemSprites[i].bitmap.reset();
+ _gfx->delSprite(&itemSprites[i]);
+ }
+
+ if (lastItemIndex >= 0) {
+ lastItemIndex = itemIndices[lastItemIndex];
+ }
+
+ if (restoreMouse)
+ _gfx->warpMouse(oldMousePos.x, oldMousePos.y);
+
+ _keyboardControlsMouse = keyboardControlledMouse;
+ return lastItemIndex;
+}
+
+void StarTrekEngine::initStarfieldSprite(Sprite *sprite, SharedPtr<Bitmap> bitmap, const Common::Rect &rect) {
+ sprite->setXYAndPriority(rect.left, rect.top, 0);
+ sprite->setBitmap(bitmap);
+ bitmap->xoffset = 0;
+ bitmap->yoffset = 0;
+ bitmap->width = rect.width();
+ bitmap->height = rect.height();
+ _gfx->addSprite(sprite);
+ sprite->drawMode = 1;
+}
+
+SharedPtr<Bitmap> StarTrekEngine::scaleBitmap(SharedPtr<Bitmap> bitmap, Fixed8 scale) {
+ int scaledWidth = scale.multToInt(bitmap->width);
+ int scaledHeight = scale.multToInt(bitmap->height);
+ int origWidth = bitmap->width;
+ int origHeight = bitmap->height;
+
+ if (scaledWidth < 1)
+ scaledWidth = 1;
+ if (scaledHeight < 1)
+ scaledHeight = 1;
+
+ SharedPtr<Bitmap> scaledBitmap(new Bitmap(scaledWidth, scaledHeight));
+ scaledBitmap->xoffset = scale.multToInt(bitmap->xoffset);
+ scaledBitmap->yoffset = scale.multToInt(bitmap->yoffset);
+
+ // sub_344a5(scaledWidth, origWidth);
+
+ origHeight--;
+ scaledHeight--;
+
+ byte *src = bitmap->pixels;
+ byte *dest = scaledBitmap->pixels;
+
+ if (scale <= 1.0) {
+ int16 var2e = 0;
+ uint16 var30 = scaledHeight << 1;
+ uint16 var32 = (scaledHeight - origHeight) << 1;
+ uint16 origRow = 0;
+
+ while (origRow <= origHeight) {
+ if (var2e < 0) {
+ var2e += var30;
+ } else {
+ var2e += var32;
+ scaleBitmapRow(src, dest, origWidth, scaledWidth);
+ dest += scaledWidth;
+ }
+
+ src += bitmap->width;
+ origRow++;
+ }
+ } else {
+ int16 var2e = (origHeight << 1) - scaledHeight;
+ uint16 var30 = origHeight << 1;
+ uint16 var32 = (origHeight - scaledHeight) << 1;
+ uint16 srcRowChanged = true;
+ origWidth = bitmap->width;
+ uint16 scaledRow = 0;
+ byte *rowData = new byte[scaledWidth];
+
+ while (scaledRow++ <= scaledHeight) {
+ if (srcRowChanged) {
+ scaleBitmapRow(src, rowData, origWidth, scaledWidth);
+ srcRowChanged = false;
+ }
+
+ memcpy(dest, rowData, scaledWidth);
+ dest += scaledWidth;
+
+ if (var2e < 0) {
+ var2e += var30;
+ } else {
+ var2e += var32;
+ src += origWidth;
+ srcRowChanged = true;
+ }
+ }
+
+ delete[] rowData;
+ }
+
+ return scaledBitmap;
+}
+
+void StarTrekEngine::scaleBitmapRow(byte *src, byte *dest, uint16 origWidth, uint16 scaledWidth) {
+ if (origWidth >= scaledWidth) {
+ int16 var2 = (scaledWidth << 1) - origWidth;
+ uint16 var4 = scaledWidth << 1;
+ uint16 var6 = (scaledWidth - origWidth) << 1;
+ uint16 varE = 0;
+ uint16 varA = 0;
+ uint16 var8 = origWidth;
+ uint16 di = 0;
+
+ while (var8-- != 0) {
+ if (var2 < 0) {
+ var2 += var4;
+ } else {
+ var2 += var6;
+ if (di != 0) {
+ if (varE != 0) {
+ *(dest - 1) = *src++;
+ varE = 0;
+ di--;
+ }
+ src += di;
+ di = 0;
+ }
+ *dest++ = *src;
+ varE = 1;
+ }
+
+ di++;
+ varA++;
+ }
+ } else {
+ int16 var2 = ((origWidth - 1) << 1) - (scaledWidth - 1);
+ uint16 var4 = (origWidth - 1) << 1;
+ uint16 var6 = ((origWidth - 1) - (scaledWidth - 1)) << 1;
+ uint16 varA = 0;
+ uint16 var8 = scaledWidth;
+ uint16 di = 0;
+
+ while (var8-- != 0) {
+ if (di != 0) {
+ src += di;
+ di = 0;
+ }
+ *dest++ = *src;
+
+ if (var2 < 0)
+ var2 += var4;
+ else {
+ var2 += var6;
+ di++;
+ }
+
+ varA++;
+ }
+ }
+}
+
+} // End of namespace StarTrek