mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
It's now possible to use the menu system and talk to some crewmates. The game now starts in bridge mode, and it's possible to beam down by using Kirk's transporter icon.
1458 lines
40 KiB
C++
1458 lines
40 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
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/stream.h"
|
|
#include "common/memstream.h"
|
|
|
|
#include "startrek/iwfile.h"
|
|
#include "startrek/resource.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++) {
|
|
delete _banFiles[i];
|
|
_banFiles[i] = nullptr;
|
|
}
|
|
|
|
_kirkActor->animationString = "kstnd";
|
|
_spockActor->animationString = "sstnd";
|
|
_mccoyActor->animationString = "mstnd";
|
|
_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);
|
|
|
|
if (actorIndex == -1) {
|
|
// TODO
|
|
warning("loadActorAnim: actor == -1");
|
|
} else {
|
|
Actor *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] = _resource->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;
|
|
actor->animationString2 = animFile;
|
|
|
|
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.
|
|
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.empty())
|
|
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);
|
|
actor->bitmapFilename = animFrameFilename;
|
|
actor->bitmapFilename.trim();
|
|
|
|
sprite->setBitmap(loadAnimationFrame(actor->bitmapFilename, actor->scale));
|
|
|
|
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) {
|
|
delete sprite->bitmap;
|
|
sprite->bitmap = nullptr;
|
|
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);
|
|
}
|
|
|
|
delete actor->sprite.bitmap;
|
|
actor->sprite.bitmap = nullptr;
|
|
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.deleteLastChar();
|
|
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.deleteLastChar();
|
|
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)
|
|
renderBan(screenPixels, bgPixels, i);
|
|
}
|
|
|
|
_gfx->unlockScreenPixels();
|
|
}
|
|
|
|
void StarTrekEngine::renderBan(byte *screenPixels, byte *bgPixels, int banFileIndex) {
|
|
Common::MemoryReadStreamEndian *banFile = _banFiles[banFileIndex];
|
|
banFile->seek(_banFileOffsets[banFileIndex], SEEK_SET);
|
|
|
|
uint16 offset = banFile->readUint16();
|
|
int32 size = banFile->readUint16();
|
|
|
|
byte *dest1 = screenPixels + offset;
|
|
byte *dest2 = bgPixels + offset;
|
|
|
|
// Skip 8 bytes (rectangle encompassing the area being drawn to)
|
|
banFile->skip(8);
|
|
|
|
while (--size >= 0) {
|
|
assert(dest1 >= screenPixels && dest1 < screenPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
|
|
assert(dest2 >= bgPixels && dest2 < bgPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
|
|
|
|
int8 b = banFile->readByte();
|
|
|
|
if (b == -128) { // Add value to destination (usually jumping to next row)
|
|
uint16 skip = banFile->readUint16();
|
|
dest1 += skip;
|
|
dest2 += skip;
|
|
} else if (b < 0) { // Repeated byte
|
|
byte c = banFile->readByte();
|
|
if (c == 0) {
|
|
dest1 += (-b) + 1;
|
|
dest2 += (-b) + 1;
|
|
} else {
|
|
for (int j = 0; j < (-b) + 1; j++) {
|
|
(*dest1++) = c;
|
|
(*dest2++) = c;
|
|
}
|
|
}
|
|
} else { // List of bytes
|
|
b++;
|
|
while (b-- != 0) {
|
|
byte c = banFile->readByte();
|
|
if (c == 0) {
|
|
dest1++;
|
|
dest2++;
|
|
} else {
|
|
*(dest1++) = c;
|
|
*(dest2++) = 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++) {
|
|
delete _banFiles[i];
|
|
_banFiles[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
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';
|
|
|
|
actor->animFilename = _animName;
|
|
actor->animType = 2;
|
|
actor->animFile = SharedPtr<Common::MemoryReadStreamEndian>(_resource->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);
|
|
|
|
actor->bitmapFilename = firstFrameFilename;
|
|
actor->bitmapFilename.trim();
|
|
sprite->setBitmap(loadAnimationFrame(actor->bitmapFilename, scale));
|
|
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->animFile.reset();
|
|
// Fall through
|
|
case 1:
|
|
delete actor->sprite.bitmap;
|
|
actor->sprite.bitmap = nullptr;
|
|
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 = actor->animationString + (char)actor->direction;
|
|
else // Default to facing south
|
|
animName = 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.c_str(), actor->field92 & 7);
|
|
actor->sprite.setBitmap(loadAnimationFrame(animName, actor->scale));
|
|
actor->bitmapFilename = animName;
|
|
|
|
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 += 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 += 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) && _room->isPointInPolygon(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 (_room->isPointInPolygon(offset, x, y)) {
|
|
int actorIndex = _room->readRdfWord(offset);
|
|
return actorIndex;
|
|
}
|
|
|
|
int numVertices = _room->readRdfWord(offset + 2);
|
|
offset = offset + 4 + numVertices * 4;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Bitmap *StarTrekEngine::loadAnimationFrame(const Common::String &filename, Fixed8 scale) {
|
|
Bitmap *bitmapToReturn = nullptr;
|
|
bool isDemo = getFeatures() & GF_DEMO;
|
|
|
|
char basename[5];
|
|
strncpy(basename, filename.c_str() + 1, 4);
|
|
basename[4] = '\0';
|
|
|
|
char mcCoyChar = !isDemo ? 'm' : 'b';
|
|
|
|
char c = filename[0];
|
|
if ((strcmp(basename, "stnd") == 0 || strcmp(basename, "tele") == 0)
|
|
&& (c == mcCoyChar || c == 's' || c == 'k' || c == 'r')) {
|
|
if (c == mcCoyChar) {
|
|
// Mccoy has the "base" animations for all crewmen
|
|
bitmapToReturn = new Bitmap(_resource->loadBitmapFile(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 = new Bitmap(loadBitmapFile(filename + ".$bm"));
|
|
|
|
if (bitmapToReturn == nullptr) {
|
|
Common::String mccoyFilename = filename;
|
|
mccoyFilename.setChar(mcCoyChar, 0);
|
|
if (isDemo && mccoyFilename.hasPrefix("bstnds"))
|
|
mccoyFilename.setChar('m', 0);
|
|
Bitmap *bitmap = new Bitmap(_resource->loadBitmapFile(mccoyFilename));
|
|
|
|
uint16 width = bitmap->width;
|
|
uint16 height = bitmap->height;
|
|
|
|
bitmapToReturn = 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;
|
|
case 'm': // McCoy
|
|
case 'b': // McCoy (demo)
|
|
colorShift = 0;
|
|
break;
|
|
default:
|
|
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
|
|
if (!isDemo) {
|
|
Common::MemoryReadStreamEndian *xorFile = _resource->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);
|
|
}
|
|
|
|
delete xorFile;
|
|
}
|
|
|
|
delete bitmap;
|
|
}
|
|
}
|
|
} else {
|
|
// TODO: when loading a bitmap, it passes a different argument than is standard to
|
|
// the "file loading with cache" function...
|
|
bitmapToReturn = new Bitmap(_resource->loadBitmapFile(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) {
|
|
bool isDemo = getFeatures() & GF_DEMO;
|
|
const char *crewmanChars = !isDemo ? "ksmr" : "ksbr"; // Kirk, Spock, McCoy (Bones), RedShirt
|
|
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(_resource->loadBitmapFile(itemFilename));
|
|
|
|
_inventoryIconSprite.pos.x = 46;
|
|
}
|
|
|
|
_gfx->addSprite(&_inventoryIconSprite);
|
|
|
|
_inventoryIconSprite.pos.y = 10;
|
|
_inventoryIconSprite.drawMode = 2;
|
|
_inventoryIconSprite.drawPriority = 15;
|
|
_inventoryIconSprite.drawPriority2 = 8;
|
|
_inventoryIconSprite.setBitmap(_resource->loadBitmapFile("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;
|
|
delete _itemIconSprite.bitmap;
|
|
_itemIconSprite.bitmap = nullptr;
|
|
}
|
|
|
|
if (_inventoryIconSprite.drawMode == 2) {
|
|
_gfx->delSprite(&_inventoryIconSprite);
|
|
_inventoryIconSprite.drawMode = 0;
|
|
delete _inventoryIconSprite.bitmap;
|
|
_inventoryIconSprite.bitmap = nullptr;
|
|
}
|
|
}
|
|
|
|
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(_resource->loadBitmapFile(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:
|
|
displayMenu = false;
|
|
break;
|
|
|
|
case TREKEVENT_RBUTTONDOWN:
|
|
displayMenu = false;
|
|
lastItemIndex = -1;
|
|
break;
|
|
|
|
case TREKEVENT_KEYDOWN:
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_ESCAPE:
|
|
case Common::KEYCODE_F2:
|
|
displayMenu = false;
|
|
lastItemIndex = -1;
|
|
break;
|
|
|
|
case Common::KEYCODE_RETURN:
|
|
case Common::KEYCODE_KP_ENTER:
|
|
case Common::KEYCODE_F1:
|
|
displayMenu = false;
|
|
break;
|
|
|
|
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();
|
|
}
|
|
|
|
_sound->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++) {
|
|
delete itemSprites[i].bitmap;
|
|
itemSprites[i].bitmap = nullptr;
|
|
_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, 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;
|
|
}
|
|
|
|
Bitmap *StarTrekEngine::scaleBitmap(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;
|
|
|
|
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;
|
|
}
|
|
|
|
delete bitmap;
|
|
|
|
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
|