2011-02-20 07:12:35 +00:00
|
|
|
/* 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 "base/plugins.h"
|
|
|
|
#include "base/version.h"
|
|
|
|
#include "common/archive.h"
|
|
|
|
#include "common/config-manager.h"
|
2018-05-07 21:12:07 +00:00
|
|
|
#include "common/debug-channels.h"
|
2011-02-20 07:12:35 +00:00
|
|
|
#include "common/error.h"
|
|
|
|
#include "common/events.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
#include "common/macresman.h"
|
|
|
|
#include "graphics/surface.h"
|
|
|
|
#include "engines/util.h"
|
|
|
|
#include "video/qt_decoder.h"
|
|
|
|
|
2018-02-22 02:06:57 +00:00
|
|
|
#include "startrek/filestream.h"
|
2018-05-14 22:25:47 +00:00
|
|
|
#include "startrek/iwfile.h"
|
2011-02-20 07:12:35 +00:00
|
|
|
#include "startrek/lzss.h"
|
|
|
|
#include "startrek/startrek.h"
|
|
|
|
|
|
|
|
namespace StarTrek {
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
StarTrekEngine::StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gamedesc) :
|
|
|
|
Engine(syst),
|
|
|
|
_gameDescription(gamedesc),
|
2018-05-16 22:21:34 +00:00
|
|
|
_randomSource("Star Trek"),
|
2018-05-11 06:17:57 +00:00
|
|
|
_kirkObject(&_objectList[0]),
|
|
|
|
_spockObject(&_objectList[1]),
|
|
|
|
_mccoyObject(&_objectList[2]),
|
|
|
|
_redshirtObject(&_objectList[3]) {
|
|
|
|
|
2018-05-07 21:12:07 +00:00
|
|
|
DebugMan.addDebugChannel(kDebugSound, "sound", "Sound");
|
2018-05-11 06:17:57 +00:00
|
|
|
DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics");
|
2018-05-07 21:12:07 +00:00
|
|
|
|
2018-03-11 03:47:52 +00:00
|
|
|
_gfx = nullptr;
|
|
|
|
_sound = nullptr;
|
|
|
|
_macResFork = nullptr;
|
2018-05-07 20:40:13 +00:00
|
|
|
|
2018-05-09 02:48:40 +00:00
|
|
|
_clockTicks = 0;
|
|
|
|
|
2018-05-07 20:40:13 +00:00
|
|
|
_musicEnabled = true;
|
|
|
|
_sfxEnabled = true;
|
2018-05-09 02:48:40 +00:00
|
|
|
_word_467a6 = true;
|
|
|
|
_musicWorking = true;
|
|
|
|
_sfxWorking = true;
|
|
|
|
_finishedPlayingSpeech = false;
|
|
|
|
|
2018-05-16 22:21:34 +00:00
|
|
|
_lookActionBitmapIndex = 0;
|
|
|
|
|
2018-05-09 02:48:40 +00:00
|
|
|
_mouseControllingShip = false;
|
|
|
|
_keyboardControlsMouse = true;
|
2018-05-09 05:26:49 +00:00
|
|
|
|
|
|
|
_inQuitGameMenu = false;
|
2018-05-16 18:05:42 +00:00
|
|
|
_textDisplayMode = TEXTDISPLAY_WAIT;
|
|
|
|
_textboxVar2 = 0;
|
|
|
|
_textboxVar6 = 0;
|
|
|
|
_textboxHasMultipleChoices = false;
|
2018-05-14 03:58:58 +00:00
|
|
|
|
|
|
|
_missionToLoad = "DEMON";
|
|
|
|
_roomIndexToLoad = 0;
|
2018-05-17 23:35:31 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < NUM_ITEMS; i++)
|
|
|
|
_itemList[i] = g_itemList[i];
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StarTrekEngine::~StarTrekEngine() {
|
|
|
|
delete _gfx;
|
|
|
|
delete _sound;
|
|
|
|
delete _macResFork;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error StarTrekEngine::run() {
|
|
|
|
_gfx = new Graphics(this);
|
|
|
|
_sound = new Sound(this);
|
|
|
|
|
|
|
|
if (getPlatform() == Common::kPlatformMacintosh) {
|
|
|
|
_macResFork = new Common::MacResManager();
|
|
|
|
if (!_macResFork->open("Star Trek Data"))
|
|
|
|
error("Could not load Star Trek Data");
|
|
|
|
assert(_macResFork->hasDataFork() && _macResFork->hasResFork());
|
|
|
|
}
|
|
|
|
|
2018-05-09 20:26:08 +00:00
|
|
|
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
|
2018-03-11 23:22:24 +00:00
|
|
|
initializeEventsAndMouse();
|
2018-05-11 06:17:57 +00:00
|
|
|
|
|
|
|
_frameIndex = 0;
|
|
|
|
|
|
|
|
_gameMode = -1;
|
|
|
|
_lastGameMode = -1;
|
|
|
|
|
2018-05-14 17:26:47 +00:00
|
|
|
runGameMode(GAMEMODE_AWAYMISSION);
|
2018-05-11 06:17:57 +00:00
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error StarTrekEngine::runGameMode(int mode) {
|
|
|
|
_gameMode = mode;
|
|
|
|
|
|
|
|
_sound->stopAllVocSounds();
|
|
|
|
if (!_sound->_loopingAudioName.empty())
|
|
|
|
_sound->playVoc(_sound->_loopingAudioName);
|
|
|
|
|
|
|
|
if (_gameMode == GAMEMODE_START)
|
|
|
|
_gameMode = GAMEMODE_BRIDGE;
|
|
|
|
|
|
|
|
while (true) {
|
2018-05-13 00:48:39 +00:00
|
|
|
TrekEvent event;
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
if (_gameMode != _lastGameMode) {
|
|
|
|
// Cleanup previous game mode
|
|
|
|
switch (_lastGameMode) {
|
|
|
|
case GAMEMODE_BRIDGE:
|
2018-05-14 03:58:58 +00:00
|
|
|
//cleanupBridge();
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_AWAYMISSION:
|
2018-05-14 03:58:58 +00:00
|
|
|
cleanupAwayMission();
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_BEAMDOWN:
|
|
|
|
case GAMEMODE_BEAMUP:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastGameMode = _gameMode;
|
|
|
|
|
|
|
|
// Load next game mode
|
|
|
|
switch (_gameMode) {
|
|
|
|
case GAMEMODE_BRIDGE:
|
|
|
|
_sound->loadMusicFile("bridge");
|
|
|
|
//initBridge();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_AWAYMISSION:
|
2018-05-14 03:58:58 +00:00
|
|
|
initAwayMission();
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_BEAMDOWN:
|
2018-05-14 03:58:58 +00:00
|
|
|
_awayMission.redshirtDead = false;
|
2018-05-11 06:17:57 +00:00
|
|
|
_sound->loadMusicFile("ground");
|
|
|
|
runTransportSequence("teled");
|
|
|
|
_gameMode = GAMEMODE_AWAYMISSION;
|
|
|
|
continue; // Back to start of loop
|
|
|
|
|
|
|
|
case GAMEMODE_BEAMUP:
|
|
|
|
runTransportSequence("teleb");
|
|
|
|
_gameMode = GAMEMODE_BRIDGE;
|
|
|
|
//sub_15c61();
|
|
|
|
_sound->stopAllVocSounds();
|
|
|
|
_sound->playVoc("bridloop");
|
|
|
|
continue; // Back to start of loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run current game mode
|
|
|
|
switch (_gameMode) {
|
|
|
|
case GAMEMODE_BRIDGE:
|
2018-05-13 00:48:39 +00:00
|
|
|
popNextEvent(&event);
|
2018-05-11 06:17:57 +00:00
|
|
|
//runBridge();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_AWAYMISSION:
|
2018-05-14 03:58:58 +00:00
|
|
|
runAwayMission();
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GAMEMODE_BEAMDOWN:
|
|
|
|
case GAMEMODE_BEAMUP:
|
|
|
|
error("Can't be here.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-02-20 07:12:35 +00:00
|
|
|
|
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
void StarTrekEngine::runTransportSequence(const Common::String &name) {
|
|
|
|
const uint16 crewmanTransportPositions[][2] = {
|
|
|
|
{ 0x8e, 0x7c },
|
|
|
|
{ 0xbe, 0x7c },
|
|
|
|
{ 0x7e, 0x72 },
|
|
|
|
{ 0xaa, 0x72 }
|
|
|
|
};
|
|
|
|
|
|
|
|
_sound->stopAllVocSounds();
|
2018-05-13 19:29:57 +00:00
|
|
|
_gfx->fadeoutScreen();
|
2018-05-11 06:17:57 +00:00
|
|
|
objectFunc1();
|
|
|
|
initObjects();
|
|
|
|
|
|
|
|
SharedPtr<Bitmap> bgImage = _gfx->loadBitmap("transprt");
|
|
|
|
_gfx->setBackgroundImage(bgImage);
|
|
|
|
_gfx->clearPri();
|
|
|
|
_gfx->loadPalette("palette");
|
|
|
|
_gfx->drawDirectToScreen(bgImage);
|
|
|
|
_system->updateScreen();
|
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
|
2018-05-11 06:17:57 +00:00
|
|
|
Common::String filename = getCrewmanAnimFilename(i, name);
|
|
|
|
int x = crewmanTransportPositions[i][0];
|
|
|
|
int y = crewmanTransportPositions[i][1];
|
2018-05-14 03:58:58 +00:00
|
|
|
loadObjectAnim(i, filename, x, y, 256);
|
2018-05-11 06:17:57 +00:00
|
|
|
_objectList[i].animationString[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_missionToLoad.equalsIgnoreCase("feather") && name[4] == 'b') {
|
2018-05-14 03:58:58 +00:00
|
|
|
loadObjectAnim(9, "qteleb", 0x61, 0x79, 0x100);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
else if (_missionToLoad.equalsIgnoreCase("trial")) {
|
|
|
|
if (name[4] == 'd') {
|
2018-05-14 03:58:58 +00:00
|
|
|
loadObjectAnim(9, "qteled", 0x61, 0x79, 0x100);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
/* TODO
|
|
|
|
else if (word_51156 >= 3) {
|
2018-05-14 03:58:58 +00:00
|
|
|
loadObjectAnim(9, "qteleb", 0x61, 0x79, 0x100);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
loadObjectAnim(8, "transc", 0, 0, 0x100);
|
2018-05-11 06:17:57 +00:00
|
|
|
|
|
|
|
// TODO: redraw mouse and sprite_52c4e?
|
|
|
|
|
|
|
|
_gfx->drawAllSprites();
|
2018-05-13 19:29:57 +00:00
|
|
|
_gfx->fadeinScreen();
|
2018-05-11 06:17:57 +00:00
|
|
|
|
|
|
|
playSoundEffectIndex(0x0a);
|
|
|
|
|
|
|
|
if (name.equalsIgnoreCase("teled"))
|
|
|
|
playSoundEffectIndex(0x08);
|
|
|
|
else
|
|
|
|
playSoundEffectIndex(0x09);
|
|
|
|
|
|
|
|
while (_objectList[0].field62 == 0) {
|
|
|
|
TrekEvent event;
|
|
|
|
if (popNextEvent(&event)) {
|
|
|
|
if (event.type == TREKEVENT_TICK) {
|
|
|
|
// TODO: redraw sprite_52c4e?
|
|
|
|
_frameIndex++;
|
|
|
|
updateObjectAnimations();
|
|
|
|
_gfx->drawAllSprites();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: redraw sprite_52c4e?
|
|
|
|
|
|
|
|
_gfx->drawAllSprites();
|
2018-05-13 19:29:57 +00:00
|
|
|
_gfx->fadeoutScreen();
|
2018-05-11 06:17:57 +00:00
|
|
|
objectFunc1();
|
|
|
|
initObjects();
|
|
|
|
}
|
|
|
|
|
2018-05-06 08:13:10 +00:00
|
|
|
void StarTrekEngine::playSoundEffectIndex(int index) {
|
2018-05-11 06:17:57 +00:00
|
|
|
switch (index) {
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x04:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("tricorde");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x05:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("STDOOR1");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x06:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("PHASSHOT");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x07:
|
2018-05-06 08:13:10 +00:00
|
|
|
_sound->playMidiTrack(index);
|
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x08:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("TRANSDEM");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-15 21:36:11 +00:00
|
|
|
case 0x09: // Beaming in?
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("TRANSMAT");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-15 21:36:11 +00:00
|
|
|
case 0x0a: // Beaming out?
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("TRANSENE");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x10: // Menu selection sound
|
2018-05-06 08:13:10 +00:00
|
|
|
_sound->playMidiTrack(index);
|
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x22:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("HAILING");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x24:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("PHASSHOT");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x25:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("PHOTSHOT");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x26:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("HITSHIEL");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x27:
|
2018-05-06 08:13:10 +00:00
|
|
|
_sound->playMidiTrack(index);
|
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x28:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("REDALERT");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
2018-05-07 20:40:13 +00:00
|
|
|
case 0x29:
|
2018-05-09 20:26:08 +00:00
|
|
|
_sound->playVoc("WARP");
|
2018-05-06 08:13:10 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-03-11 23:22:24 +00:00
|
|
|
}
|
|
|
|
|
2018-05-07 20:40:13 +00:00
|
|
|
void StarTrekEngine::playSpeech(const Common::String &filename) {
|
|
|
|
_sound->playSpeech(filename.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::stopPlayingSpeech() {
|
|
|
|
_sound->stopPlayingSpeech();
|
|
|
|
}
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
void StarTrekEngine::initObjects() {
|
|
|
|
for (int i = 0; i < MAX_OBJECTS; i++) {
|
|
|
|
_objectList[i] = Object();
|
|
|
|
}
|
|
|
|
for (int i = 0; i < MAX_OBJECTS / 2; i++)
|
|
|
|
_objectBanFiles[i].reset();
|
|
|
|
|
|
|
|
strcpy(_kirkObject->animationString, "kstnd");
|
|
|
|
strcpy(_spockObject->animationString, "sstnd");
|
|
|
|
strcpy(_mccoyObject->animationString, "mstnd");
|
|
|
|
strcpy(_redshirtObject->animationString, "rstnd");
|
|
|
|
}
|
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
/**
|
|
|
|
* Set an object's animation, position, and scale.
|
|
|
|
*/
|
2018-05-19 18:18:57 +00:00
|
|
|
int StarTrekEngine::loadObjectAnim(int objectIndex, const Common::String &animName, int16 x, int16 y, Fixed16 scale) {
|
2018-05-11 06:17:57 +00:00
|
|
|
debugC(6, kDebugGraphics, "Load animation '%s' on object %d", animName.c_str(), objectIndex);
|
|
|
|
|
|
|
|
Object *object;
|
|
|
|
|
|
|
|
if (objectIndex == -1) {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
else
|
|
|
|
object = &_objectList[objectIndex];
|
|
|
|
|
|
|
|
if (object->spriteDrawn) {
|
|
|
|
releaseAnim(object);
|
2018-05-13 05:40:31 +00:00
|
|
|
drawObjectToScreen(object, animName, x, y, scale, false);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
else {
|
2018-05-13 05:40:31 +00:00
|
|
|
drawObjectToScreen(object, animName, x, y, scale, true);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 21:36:11 +00:00
|
|
|
object->walkingIntoRoom = 0;
|
2018-05-11 06:17:57 +00:00
|
|
|
object->field66 = 0;
|
|
|
|
|
|
|
|
return objectIndex;
|
|
|
|
}
|
|
|
|
|
2018-05-14 17:26:47 +00:00
|
|
|
/**
|
|
|
|
* Tries to make an object walk to a position.
|
|
|
|
* Returns true if successful in initiating the walk.
|
|
|
|
*/
|
|
|
|
bool StarTrekEngine::objectWalkToPosition(int objectIndex, const Common::String &animFile, int16 srcX, int16 srcY, int16 destX, int16 destY) {
|
|
|
|
debugC(6, "Obj %d: walk from (%d,%d) to (%d,%d)", objectIndex, srcX, srcY, destX, destY);
|
|
|
|
|
|
|
|
Object *object = &_objectList[objectIndex];
|
|
|
|
|
2018-05-15 21:36:11 +00:00
|
|
|
object->walkingIntoRoom = 0;
|
2018-05-14 17:26:47 +00:00
|
|
|
if (isPositionSolid(destX, destY))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (object->spriteDrawn)
|
|
|
|
releaseAnim(object);
|
|
|
|
else
|
|
|
|
_gfx->addSprite(&object->sprite);
|
|
|
|
|
|
|
|
object->spriteDrawn = true;
|
|
|
|
object->animType = 1;
|
|
|
|
object->frameToStartNextAnim = _frameIndex + 1;
|
|
|
|
strcpy(object->animationString2, animFile.c_str());
|
|
|
|
|
|
|
|
object->dest.x = destX;
|
|
|
|
object->dest.y = destY;
|
|
|
|
object->field92 = 0;
|
2018-05-15 21:36:11 +00:00
|
|
|
object->walkingIntoRoom = 0;
|
2018-05-14 17:26:47 +00:00
|
|
|
|
|
|
|
object->iwDestPosition = -1;
|
|
|
|
object->iwSrcPosition = -1;
|
|
|
|
|
2018-05-14 22:25:47 +00:00
|
|
|
if (directPathExists(srcX, srcY, destX, destY)) {
|
|
|
|
chooseObjectDirectionForWalking(object, srcX, srcY, destX, destY);
|
|
|
|
updateObjectPositionWhileWalking(object, (object->granularPosX + 0x8000) >> 16, (object->granularPosY + 0x8000) >> 16);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
object->iwSrcPosition = _iwFile->getClosestKeyPosition(srcX, srcY);
|
|
|
|
object->iwDestPosition = _iwFile->getClosestKeyPosition(destX, destY);
|
|
|
|
|
|
|
|
if (object->iwSrcPosition == -1 || object->iwDestPosition == -1) {
|
|
|
|
// No path exists; face south by default.
|
|
|
|
strcat(object->animationString2, "S");
|
|
|
|
object->direction = 'S';
|
2018-05-14 17:26:47 +00:00
|
|
|
|
2018-05-14 22:25:47 +00:00
|
|
|
updateObjectPositionWhileWalking(object, srcX, srcY);
|
|
|
|
initStandAnim(objectIndex);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Common::Point iwSrc = _iwFile->_keyPositions[object->iwSrcPosition];
|
|
|
|
chooseObjectDirectionForWalking(object, srcX, srcY, iwSrc.x, iwSrc.y);
|
|
|
|
updateObjectPositionWhileWalking(object, (object->granularPosX + 0x8000) >> 16, (object->granularPosY + 0x8000) >> 16);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2018-05-14 17:26:47 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
void StarTrekEngine::updateObjectAnimations() {
|
|
|
|
for (int i = 0; i < MAX_OBJECTS; i++) {
|
|
|
|
Object *object = &_objectList[i];
|
|
|
|
if (!object->spriteDrawn)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (object->animType) {
|
2018-05-14 17:26:47 +00:00
|
|
|
case 0: // Not walking?
|
2018-05-11 06:17:57 +00:00
|
|
|
case 2:
|
2018-05-13 00:48:39 +00:00
|
|
|
if (_frameIndex >= object->frameToStartNextAnim) {
|
2018-05-16 22:21:34 +00:00
|
|
|
int nextAnimIndex = getRandomWord() & 3;
|
2018-05-11 06:17:57 +00:00
|
|
|
object->animFile->seek(18 + nextAnimIndex + object->animFrame * 22, SEEK_SET);
|
|
|
|
byte nextAnimFrame = object->animFile->readByte();
|
|
|
|
|
|
|
|
if (object->animFrame != nextAnimFrame) {
|
|
|
|
if (nextAnimFrame == object->numAnimFrames - 1) {
|
|
|
|
object->field62++;
|
2018-05-15 21:36:11 +00:00
|
|
|
if (object->walkingIntoRoom != 0) {
|
|
|
|
addCommand(Command(COMMAND_FINISHED_BEAMING_IN, object->field66, 0, 0));
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object->animFrame = nextAnimFrame;
|
|
|
|
if (object->animFrame >= object->numAnimFrames) {
|
|
|
|
if (object->animationString[0] == '\0')
|
|
|
|
removeObjectFromScreen(i);
|
2018-05-14 03:58:58 +00:00
|
|
|
else
|
2018-05-11 06:17:57 +00:00
|
|
|
initStandAnim(i);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Sprite *sprite = &object->sprite;
|
|
|
|
|
|
|
|
object->animFile->seek(object->animFrame * 22, SEEK_SET);
|
|
|
|
char animFrameFilename[16];
|
|
|
|
object->animFile->read(animFrameFilename, 16);
|
|
|
|
sprite->setBitmap(loadAnimationFrame(animFrameFilename, object->scale));
|
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
memset(object->animationString4, 0, 10);
|
|
|
|
strncpy(object->animationString4, animFrameFilename, 9);
|
2018-05-11 06:17:57 +00:00
|
|
|
|
2018-05-13 00:48:39 +00:00
|
|
|
object->animFile->seek(10 + object->animFrame * 22, SEEK_SET);
|
2018-05-11 06:17:57 +00:00
|
|
|
uint16 xOffset = object->animFile->readUint16();
|
|
|
|
uint16 yOffset = object->animFile->readUint16();
|
|
|
|
uint16 basePriority = object->animFile->readUint16();
|
2018-05-13 00:48:39 +00:00
|
|
|
uint16 frames = object->animFile->readUint16();
|
2018-05-11 06:17:57 +00:00
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
sprite->pos.x = xOffset + object->pos.x;
|
|
|
|
sprite->pos.y = yOffset + object->pos.y;
|
|
|
|
sprite->drawPriority = _gfx->getPriValue(0, yOffset + object->pos.y) + basePriority;
|
2018-05-11 06:17:57 +00:00
|
|
|
sprite->bitmapChanged = true;
|
|
|
|
|
2018-05-13 00:48:39 +00:00
|
|
|
object->frameToStartNextAnim = frames + _frameIndex;
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-05-14 17:26:47 +00:00
|
|
|
case 1: // Walking
|
|
|
|
if (_frameIndex < object->frameToStartNextAnim)
|
|
|
|
break;
|
2018-05-15 21:36:11 +00:00
|
|
|
if (i == 0) // Kirk only
|
|
|
|
checkTouchedLoadingZone(object->pos.x, object->pos.y);
|
2018-05-14 17:26:47 +00:00
|
|
|
if (object->field90 != 0) {
|
|
|
|
Sprite *sprite = &object->sprite;
|
|
|
|
int loops;
|
|
|
|
if (getObjectScaleAtPosition((object->granularPosY + 0x8000) >> 16) < 0xa0)
|
|
|
|
loops = 1;
|
|
|
|
else
|
|
|
|
loops = 2;
|
|
|
|
for (int k = 0; k < loops; k++) {
|
|
|
|
if (object->field90 == 0)
|
|
|
|
break;
|
|
|
|
object->field90--;
|
|
|
|
uint32 newX = object->granularPosX + object->speedX;
|
|
|
|
uint32 newY = object->granularPosY + object->speedY;
|
|
|
|
if ((object->field90 & 3) == 0) {
|
|
|
|
sprite->bitmap.reset();
|
|
|
|
updateObjectPositionWhileWalking(object, (newX + 0x8000) >> 16, (newY + 0x8000) >> 16);
|
|
|
|
object->field92++;
|
|
|
|
}
|
|
|
|
|
|
|
|
object->granularPosX = newX;
|
|
|
|
object->granularPosY = newY;
|
|
|
|
object->frameToStartNextAnim = _frameIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // object->field90 == 0
|
|
|
|
if (object->iwSrcPosition == -1) {
|
2018-05-15 21:36:11 +00:00
|
|
|
if (object->walkingIntoRoom != 0) {
|
|
|
|
object->walkingIntoRoom = 0;
|
|
|
|
addCommand(Command(FINISHED_ENTERING_ROOM, object->field66 & 0xff, 0, 0));
|
2018-05-14 17:26:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
object->sprite.bitmap.reset();
|
|
|
|
updateObjectPositionWhileWalking(object, (object->granularPosX + 0x8000) >> 16, (object->granularPosY + 0x8000) >> 16);
|
|
|
|
initStandAnim(i);
|
|
|
|
}
|
|
|
|
else { // object->iwSrcPosition != -1
|
|
|
|
if (object->iwSrcPosition == object->iwDestPosition) {
|
|
|
|
object->animationString2[strlen(object->animationString2) - 1] = '\0';
|
|
|
|
object->iwDestPosition = -1;
|
|
|
|
object->iwSrcPosition = -1;
|
2018-05-14 22:25:47 +00:00
|
|
|
chooseObjectDirectionForWalking(object, object->pos.x, object->pos.y, object->dest.x, object->dest.y);
|
2018-05-14 17:26:47 +00:00
|
|
|
}
|
|
|
|
else {
|
2018-05-14 22:25:47 +00:00
|
|
|
int index = _iwFile->_iwEntries[object->iwSrcPosition][object->iwDestPosition];
|
|
|
|
object->iwSrcPosition = index;
|
|
|
|
Common::Point dest = _iwFile->_keyPositions[object->iwSrcPosition];
|
|
|
|
object->animationString2[strlen(object->animationString2) - 1] = '\0';
|
|
|
|
chooseObjectDirectionForWalking(object, object->pos.x, object->pos.y, dest.x, dest.y);
|
2018-05-14 17:26:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("Invalid anim type.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::removeObjectFromScreen(int objectIndex) {
|
|
|
|
Object *object = &_objectList[objectIndex];
|
|
|
|
|
|
|
|
if (object->spriteDrawn != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
debugC(6, kDebugGraphics, "Stop drawing object %d", objectIndex);
|
|
|
|
|
|
|
|
Sprite *sprite = &object->sprite;
|
|
|
|
sprite->field16 = true;
|
|
|
|
sprite->bitmapChanged = true;
|
|
|
|
_gfx->drawAllSprites();
|
|
|
|
_gfx->delSprite(sprite);
|
|
|
|
releaseAnim(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::objectFunc1() {
|
|
|
|
for (int i = 0; i < MAX_OBJECTS; i++) {
|
|
|
|
if (_objectList[i].spriteDrawn == 1) {
|
|
|
|
removeObjectFromScreen(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_OBJECTS / 2; i++) {
|
|
|
|
_objectBanFiles[i].reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-19 18:18:57 +00:00
|
|
|
void StarTrekEngine::drawObjectToScreen(Object *object, const Common::String &_animName, int16 x, int16 y, Fixed16 scale, bool addSprite) {
|
2018-05-11 06:17:57 +00:00
|
|
|
Common::String animFilename = _animName;
|
|
|
|
if (_animName.hasPrefixIgnoreCase("stnd") /* && word_45d20 == -1 */) // TODO
|
|
|
|
animFilename += 'j';
|
|
|
|
memcpy(object->animationString3, _animName.c_str(), sizeof(object->animationString3));
|
|
|
|
|
|
|
|
object->animType = 2;
|
|
|
|
object->animFile = loadFile(animFilename + ".anm");
|
|
|
|
object->numAnimFrames = object->animFile->size() / 22;
|
|
|
|
object->animFrame = 0;
|
2018-05-14 03:58:58 +00:00
|
|
|
object->pos.x = x;
|
|
|
|
object->pos.y = y;
|
2018-05-11 06:17:57 +00:00
|
|
|
object->field62 = 0;
|
2018-05-13 05:40:31 +00:00
|
|
|
object->scale = scale;
|
2018-05-11 06:17:57 +00:00
|
|
|
|
|
|
|
object->animFile->seek(16, SEEK_SET);
|
|
|
|
object->frameToStartNextAnim = object->animFile->readUint16() + _frameIndex;
|
|
|
|
|
|
|
|
char firstFrameFilename[10];
|
|
|
|
object->animFile->seek(0, SEEK_SET);
|
|
|
|
object->animFile->read(firstFrameFilename, 10);
|
|
|
|
|
|
|
|
Sprite *sprite = &object->sprite;
|
|
|
|
if (addSprite)
|
|
|
|
_gfx->addSprite(sprite);
|
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
sprite->setBitmap(loadAnimationFrame(firstFrameFilename, scale));
|
2018-05-11 06:17:57 +00:00
|
|
|
memset(object->animationString4, 0, sizeof(char) * 10);
|
|
|
|
strncpy(object->animationString4, firstFrameFilename, sizeof(char) * 9);
|
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
object->scale = scale;
|
2018-05-11 06:17:57 +00:00
|
|
|
|
|
|
|
object->animFile->seek(10, SEEK_SET);
|
|
|
|
uint16 xOffset = object->animFile->readUint16();
|
|
|
|
uint16 yOffset = object->animFile->readUint16();
|
|
|
|
uint16 basePriority = object->animFile->readUint16();
|
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
sprite->pos.x = xOffset + object->pos.x;
|
|
|
|
sprite->pos.y = yOffset + object->pos.y;
|
|
|
|
sprite->drawPriority = _gfx->getPriValue(0, yOffset + object->pos.y) + basePriority;
|
2018-05-11 06:17:57 +00:00
|
|
|
sprite->bitmapChanged = true;
|
|
|
|
|
|
|
|
object->spriteDrawn = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::releaseAnim(Object *object) {
|
|
|
|
switch (object->animType) {
|
|
|
|
case 0:
|
|
|
|
case 2:
|
|
|
|
object->sprite.bitmap.reset();
|
|
|
|
object->animFile.reset();
|
|
|
|
break;
|
2018-05-14 22:25:47 +00:00
|
|
|
case 1:
|
|
|
|
object->sprite.bitmap.reset();
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-14 03:58:58 +00:00
|
|
|
error("Invalid anim type");
|
2018-05-11 06:17:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
object->spriteDrawn = 0;
|
|
|
|
}
|
|
|
|
|
2018-05-14 03:58:58 +00:00
|
|
|
void StarTrekEngine::initStandAnim(int objectIndex) {
|
|
|
|
Object *object = &_objectList[objectIndex];
|
|
|
|
|
|
|
|
if (!object->spriteDrawn)
|
|
|
|
error("initStandAnim: dead anim");
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
// sub_239d2
|
|
|
|
const char *directions = "nsew";
|
|
|
|
|
|
|
|
if (objectIndex >= 0 && objectIndex <= 3) {
|
|
|
|
int8 dir = _awayMission.field25[objectIndex];
|
|
|
|
if (dir != -1) {
|
|
|
|
object->direction = directions[dir];
|
|
|
|
_awayMission.field25[objectIndex] = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// end of sub_239d2
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
Common::String animName;
|
|
|
|
if (object->direction != 0)
|
|
|
|
animName = Common::String(object->animationString) + (char)object->direction;
|
|
|
|
else // Default to facing south
|
|
|
|
animName = Common::String(object->animationString) + 's';
|
|
|
|
|
|
|
|
uint16 scale = getObjectScaleAtPosition(object->pos.y);
|
|
|
|
loadObjectAnim(objectIndex, animName, object->pos.x, object->pos.y, scale);
|
|
|
|
object->animType = 0;
|
|
|
|
}
|
|
|
|
|
2018-05-14 17:26:47 +00:00
|
|
|
void StarTrekEngine::updateObjectPositionWhileWalking(Object *object, int16 x, int16 y) {
|
|
|
|
object->scale = getObjectScaleAtPosition(y);
|
|
|
|
Common::String animName = Common::String::format("%s%02d", object->animationString2, object->field92 & 7);
|
|
|
|
object->sprite.setBitmap(loadAnimationFrame(animName, object->scale));
|
|
|
|
|
|
|
|
memset(object->animationString4, 0, 10);
|
|
|
|
strncpy(object->animationString4, animName.c_str(), 9);
|
|
|
|
|
|
|
|
Sprite *sprite = &object->sprite;
|
|
|
|
sprite->drawPriority = _gfx->getPriValue(0, y);
|
|
|
|
sprite->pos.x = x;
|
|
|
|
sprite->pos.y = y;
|
|
|
|
sprite->bitmapChanged = true;
|
|
|
|
|
|
|
|
object->frameToStartNextAnim = _frameIndex;
|
|
|
|
object->pos.x = x;
|
|
|
|
object->pos.y = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Chooses a value for the object's speed and direction, based on a source position and
|
|
|
|
* a destination position it's walking to.
|
|
|
|
*/
|
|
|
|
void StarTrekEngine::chooseObjectDirectionForWalking(Object *object, int16 srcX, int16 srcY, int16 destX, int16 destY) {
|
|
|
|
object->granularPosX = srcX << 16;
|
|
|
|
object->granularPosY = srcY << 16;
|
|
|
|
|
|
|
|
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
|
|
|
|
object->animationString2[strlen(object->animationString2) + 1] = '\0';
|
|
|
|
object->animationString2[strlen(object->animationString2)] = d;
|
|
|
|
|
|
|
|
object->direction = d;
|
|
|
|
object->field90 = absDistX;
|
|
|
|
|
|
|
|
if (distX != 0) {
|
|
|
|
if (distX > 0)
|
|
|
|
object->speedX = 1 << 16;
|
|
|
|
else
|
|
|
|
object->speedX = -1 << 16; // 0xffff0000
|
|
|
|
|
|
|
|
object->speedY = (distY << 16) / absDistX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char d;
|
|
|
|
if (distY > 0)
|
|
|
|
d = 'S';
|
|
|
|
else
|
|
|
|
d = 'N';
|
|
|
|
|
|
|
|
// Append direction to animation string
|
|
|
|
object->animationString2[strlen(object->animationString2) + 1] = '\0';
|
|
|
|
object->animationString2[strlen(object->animationString2)] = d;
|
|
|
|
|
|
|
|
object->direction = d;
|
|
|
|
object->field90 = absDistY;
|
|
|
|
|
|
|
|
if (distY != 0) {
|
|
|
|
if (distY > 0)
|
|
|
|
object->speedY = 1 << 16;
|
|
|
|
else
|
|
|
|
object->speedY = -1 << 16; // 0xffff0000
|
|
|
|
|
|
|
|
object->speedX = (distX << 16) / absDistY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 22:25:47 +00:00
|
|
|
/**
|
|
|
|
* Returns true if an object can walk directly from a source position to a destination
|
|
|
|
* position without running into unwalkable terrain.
|
|
|
|
*/
|
|
|
|
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;
|
2018-05-19 18:18:57 +00:00
|
|
|
Fixed32 speedX, speedY;
|
2018-05-14 22:25:47 +00:00
|
|
|
|
|
|
|
if (absDistX > absDistY) {
|
|
|
|
distCounter = absDistX;
|
|
|
|
|
|
|
|
if (distCounter == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
speedY = (distY << 16) / absDistX;
|
|
|
|
|
|
|
|
if (distX > 0)
|
|
|
|
speedX = 1 << 16;
|
|
|
|
else
|
|
|
|
speedX = -1 << 16;
|
|
|
|
}
|
|
|
|
else { // absDistX <= absDistY
|
|
|
|
distCounter = absDistY;
|
|
|
|
|
|
|
|
if (distCounter == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
speedX = (distX << 16) / absDistY;
|
|
|
|
|
|
|
|
if (distY > 0)
|
|
|
|
speedY = 1 << 16;
|
|
|
|
else
|
|
|
|
speedY = -1 << 16;
|
|
|
|
}
|
|
|
|
|
2018-05-19 18:18:57 +00:00
|
|
|
Fixed32 fixedX = srcX << 16;
|
|
|
|
Fixed32 fixedY = srcY << 16;
|
2018-05-14 22:25:47 +00:00
|
|
|
|
|
|
|
if (isPositionSolid((fixedX + 0x8000) >> 16, (fixedY + 0x8000) >> 16))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
while (distCounter-- > 0) {
|
|
|
|
fixedX += speedX;
|
|
|
|
fixedY += speedY;
|
|
|
|
|
|
|
|
if (isPositionSolid((fixedX + 0x8000) >> 16, (fixedY + 0x8000) >> 16))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-17 23:35:31 +00:00
|
|
|
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.activeItem;
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_OBJECTS; i++) {
|
|
|
|
Object *object = &_objectList[i];
|
|
|
|
if (sprite == &object->sprite)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
error("findObject: Clicked on an unknown sprite");
|
|
|
|
}
|
|
|
|
|
|
|
|
// word_4b418 = 0;
|
|
|
|
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 objectIndex = _room->readRdfWord(offset + 6);
|
|
|
|
// word_4b418 = 1;
|
|
|
|
// word_4a792 = _room->readRdfWord(offset + 2);
|
2018-05-19 18:18:57 +00:00
|
|
|
// word_4a796 = _room->readRdfWord(offset + 4); // TODO
|
2018-05-17 23:35:31 +00:00
|
|
|
return objectIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
int numVertices = _room->readRdfWord(offset + 8);
|
|
|
|
offset = offset + 10 + numVertices * 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (isPointInPolygon((int16 *)(_room->_rdfData + offset), x, y)) {
|
|
|
|
int objectIndex = _room->readRdfWord(offset);
|
|
|
|
return objectIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
int numVertices = _room->readRdfWord(offset + 2);
|
|
|
|
offset = offset + 4 + numVertices * 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-05-14 22:25:47 +00:00
|
|
|
/**
|
|
|
|
* Loads a bitmap for the animation frame with the given scale.
|
|
|
|
*/
|
2018-05-19 18:18:57 +00:00
|
|
|
SharedPtr<Bitmap> StarTrekEngine::loadAnimationFrame(const Common::String &filename, Fixed16 scale) {
|
2018-05-11 06:17:57 +00:00
|
|
|
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') {
|
2018-05-13 02:18:36 +00:00
|
|
|
// Mccoy has the "base" animations for all crewmen
|
2018-05-11 06:17:57 +00:00
|
|
|
bitmapToReturn = _gfx->loadBitmap(filename);
|
|
|
|
}
|
|
|
|
else {
|
2018-05-13 02:18:36 +00:00
|
|
|
// 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");
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
if (bitmapToReturn == nullptr) {
|
2018-05-13 02:18:36 +00:00
|
|
|
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) {
|
2018-05-14 03:58:58 +00:00
|
|
|
case 'k': // Kirk
|
2018-05-13 02:18:36 +00:00
|
|
|
colorShift = 8;
|
|
|
|
break;
|
2018-05-14 03:58:58 +00:00
|
|
|
case 'r': // Redshirt
|
2018-05-13 02:18:36 +00:00
|
|
|
colorShift = -8;
|
|
|
|
break;
|
2018-05-14 03:58:58 +00:00
|
|
|
case 's': // Spock
|
2018-05-13 02:18:36 +00:00
|
|
|
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);
|
|
|
|
}
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
if (scale != 256) {
|
|
|
|
bitmapToReturn = scaleBitmap(bitmapToReturn, scale);
|
2018-05-11 06:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return bitmapToReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::String StarTrekEngine::getCrewmanAnimFilename(int objectIndex, const Common::String &basename) {
|
|
|
|
const char *crewmanChars = "ksmr";
|
|
|
|
assert(objectIndex >= 0 && objectIndex < 4);
|
|
|
|
return crewmanChars[objectIndex] + basename;
|
|
|
|
}
|
|
|
|
|
2018-05-17 23:35:31 +00:00
|
|
|
/**
|
|
|
|
* Checks whether to change the mouse bitmap to have the red outline.
|
|
|
|
*/
|
|
|
|
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 worksOnObjects[] = { // 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 < MAX_OBJECTS && worksOnObjects[action - 1])
|
|
|
|
withRedOutline = true;
|
|
|
|
else if (selected >= MAX_OBJECTS && selected < MAX_OBJECTS_2 && worksOnHotspots[action - 1])
|
|
|
|
withRedOutline = true;
|
|
|
|
else
|
|
|
|
withRedOutline = false;
|
|
|
|
|
|
|
|
chooseMouseBitmapForAction(action, withRedOutline);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::showInventoryIcons(bool showItem) {
|
|
|
|
const char *crewmanFilenames[] = {
|
|
|
|
"ikirk",
|
|
|
|
"ispock",
|
|
|
|
"imccoy",
|
|
|
|
"iredshir"
|
|
|
|
};
|
|
|
|
|
|
|
|
Common::String itemFilename;
|
|
|
|
|
|
|
|
if (showItem) {
|
|
|
|
int i = _awayMission.activeItem;
|
|
|
|
if (i >= 0 && i <= 3)
|
|
|
|
itemFilename = crewmanFilenames[i];
|
|
|
|
else {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = 15;
|
|
|
|
_inventoryIconSprite.setBitmap(_gfx->loadBitmap("inv00"));
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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_ITEMS][10];
|
|
|
|
Common::Point itemPositions[NUM_ITEMS];
|
|
|
|
int16 itemIndices[NUM_ITEMS];
|
|
|
|
|
|
|
|
while (itemIndex < NUM_ITEMS) {
|
|
|
|
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_ITEMS];
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
/**
|
|
|
|
* A scale of 256 is the baseline.
|
|
|
|
*/
|
2018-05-19 18:18:57 +00:00
|
|
|
SharedPtr<Bitmap> StarTrekEngine::scaleBitmap(SharedPtr<Bitmap> bitmap, Fixed16 scale) {
|
2018-05-13 05:40:31 +00:00
|
|
|
int scaledWidth = (bitmap->width * scale) >> 8;
|
|
|
|
int scaledHeight = (bitmap->height * scale) >> 8;
|
|
|
|
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 = (bitmap->xoffset * scale) >> 8;
|
|
|
|
scaledBitmap->yoffset = (bitmap->yoffset * scale) >> 8;
|
|
|
|
|
|
|
|
// sub_344a5(scaledWidth, origWidth);
|
|
|
|
|
|
|
|
origHeight--;
|
|
|
|
scaledHeight--;
|
|
|
|
|
|
|
|
byte *src = bitmap->pixels;
|
|
|
|
byte *dest = scaledBitmap->pixels;
|
|
|
|
|
|
|
|
if (scale <= 256) {
|
|
|
|
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;
|
|
|
|
}
|
2018-03-11 23:22:24 +00:00
|
|
|
|
2018-05-13 05:40:31 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-05-14 03:58:58 +00:00
|
|
|
* This takes a row of an unscaled bitmap, and copies it to a row of a scaled bitmap.
|
2018-05-13 05:40:31 +00:00
|
|
|
* This was heavily optimized in the original game (manually constructed an unrolled
|
|
|
|
* loop).
|
|
|
|
*/
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
2018-03-11 03:47:52 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
/**
|
|
|
|
* TODO:
|
|
|
|
* - Should return nullptr on failure to open a file?
|
|
|
|
* - This is supposed to cache results, return same FileStream on multiple accesses.
|
|
|
|
* - This is supposed to read from a "patches" folder which overrides files in the
|
|
|
|
* packed blob.
|
|
|
|
*/
|
|
|
|
SharedPtr<FileStream> StarTrekEngine::loadFile(Common::String filename, int fileIndex) {
|
2018-01-19 07:50:51 +00:00
|
|
|
filename.toUppercase();
|
2018-05-11 06:17:57 +00:00
|
|
|
|
2018-05-05 00:32:08 +00:00
|
|
|
Common::String basename, extension;
|
|
|
|
|
2018-02-22 02:06:57 +00:00
|
|
|
bool bigEndian = getPlatform() == Common::kPlatformAmiga;
|
|
|
|
|
2018-05-05 00:32:08 +00:00
|
|
|
for (int i=filename.size()-1; ; i--) {
|
|
|
|
if (filename[i] == '.') {
|
|
|
|
basename = filename;
|
|
|
|
extension = filename;
|
|
|
|
basename.replace(i, filename.size()-i, "");
|
|
|
|
extension.replace(0, i+1, "");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 06:17:57 +00:00
|
|
|
// FIXME: don't know if this is right, or if it goes here
|
|
|
|
while (!basename.empty() && basename.lastChar() == ' ') {
|
|
|
|
basename.erase(basename.size() - 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = basename + '.' + extension;
|
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
// The Judgment Rites demo has its files not in the standard archive
|
|
|
|
if (getGameType() == GType_STJR && (getFeatures() & GF_DEMO)) {
|
|
|
|
Common::File *file = new Common::File();
|
|
|
|
if (!file->open(filename.c_str()))
|
2018-05-11 06:17:57 +00:00
|
|
|
error("Could not find file \'%s\'", filename.c_str());
|
2018-02-24 18:59:08 +00:00
|
|
|
return SharedPtr<FileStream>(new FileStream(file, bigEndian));
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Common::SeekableReadStream *indexFile = 0;
|
|
|
|
|
|
|
|
if (getPlatform() == Common::kPlatformAmiga) {
|
|
|
|
indexFile = SearchMan.createReadStreamForMember("data000.dir");
|
|
|
|
if (!indexFile)
|
2018-05-11 06:17:57 +00:00
|
|
|
error("Could not open data000.dir");
|
2011-02-20 07:12:35 +00:00
|
|
|
} else if (getPlatform() == Common::kPlatformMacintosh) {
|
|
|
|
indexFile = _macResFork->getResource("Directory");
|
|
|
|
if (!indexFile)
|
|
|
|
error("Could not find 'Directory' resource in 'Star Trek Data'");
|
|
|
|
} else {
|
|
|
|
indexFile = SearchMan.createReadStreamForMember("data.dir");
|
|
|
|
if (!indexFile)
|
2018-05-11 06:17:57 +00:00
|
|
|
error("Could not open data.dir");
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 indexOffset = 0;
|
|
|
|
bool foundData = false;
|
|
|
|
uint16 fileCount = 1;
|
|
|
|
uint16 uncompressedSize = 0;
|
|
|
|
|
|
|
|
while (!indexFile->eos() && !indexFile->err()) {
|
|
|
|
Common::String testfile;
|
|
|
|
for (byte i = 0; i < 8; i++) {
|
|
|
|
char c = indexFile->readByte();
|
|
|
|
if (c)
|
|
|
|
testfile += c;
|
|
|
|
}
|
|
|
|
testfile += '.';
|
2018-05-05 00:32:08 +00:00
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
for (byte i = 0; i < 3; i++)
|
|
|
|
testfile += indexFile->readByte();
|
2018-05-05 00:32:08 +00:00
|
|
|
|
|
|
|
if (getFeatures() & GF_DEMO && getPlatform() == Common::kPlatformDOS) {
|
2011-02-20 07:12:35 +00:00
|
|
|
indexFile->readByte(); // Always 0?
|
|
|
|
fileCount = indexFile->readUint16LE(); // Always 1
|
|
|
|
indexOffset = indexFile->readUint32LE();
|
|
|
|
uncompressedSize = indexFile->readUint16LE();
|
|
|
|
} else {
|
|
|
|
if (getPlatform() == Common::kPlatformAmiga)
|
|
|
|
indexOffset = (indexFile->readByte() << 16) + (indexFile->readByte() << 8) + indexFile->readByte();
|
|
|
|
else
|
|
|
|
indexOffset = indexFile->readByte() + (indexFile->readByte() << 8) + (indexFile->readByte() << 16);
|
|
|
|
|
|
|
|
if (indexOffset & (1 << 23)) {
|
|
|
|
fileCount = (indexOffset >> 16) & 0x7F;
|
|
|
|
indexOffset = indexOffset & 0xFFFF;
|
2018-05-05 00:32:08 +00:00
|
|
|
assert(fileCount > 1);
|
2011-02-20 07:12:35 +00:00
|
|
|
} else {
|
|
|
|
fileCount = 1;
|
|
|
|
}
|
|
|
|
}
|
2018-05-05 00:32:08 +00:00
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
if (filename.matchString(testfile)) {
|
|
|
|
foundData = true;
|
|
|
|
break;
|
2018-02-22 02:06:57 +00:00
|
|
|
}
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
2018-05-05 00:32:08 +00:00
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
delete indexFile;
|
2018-05-05 00:32:08 +00:00
|
|
|
|
|
|
|
if (!foundData) {
|
2018-01-19 07:50:51 +00:00
|
|
|
// Files can be accessed "sequentially" if their filenames are the same except for
|
|
|
|
// the last character being incremented by one.
|
|
|
|
if ((basename.lastChar() >= '1' && basename.lastChar() <= '9') ||
|
|
|
|
(basename.lastChar() >= 'B' && basename.lastChar() <= 'Z')) {
|
2018-05-05 00:32:08 +00:00
|
|
|
basename.setChar(basename.lastChar()-1, basename.size()-1);
|
2018-05-11 06:17:57 +00:00
|
|
|
return loadFile(basename + "." + extension, fileIndex+1);
|
2018-05-05 00:32:08 +00:00
|
|
|
} else
|
2018-05-11 06:17:57 +00:00
|
|
|
error("Could not find file \'%s\'", filename.c_str());
|
2018-05-05 00:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fileIndex >= fileCount)
|
|
|
|
error("Tried to access file index %d for file '%s' which doesn't exist.", fileIndex, filename.c_str());
|
2011-02-20 07:12:35 +00:00
|
|
|
|
|
|
|
Common::SeekableReadStream *dataFile = 0;
|
2018-02-22 02:06:57 +00:00
|
|
|
Common::SeekableReadStream *dataRunFile = 0; // FIXME: Amiga & Mac need this implemented
|
2011-02-20 07:12:35 +00:00
|
|
|
|
|
|
|
if (getPlatform() == Common::kPlatformAmiga) {
|
|
|
|
dataFile = SearchMan.createReadStreamForMember("data.000");
|
|
|
|
if (!dataFile)
|
|
|
|
error("Could not open data.000");
|
|
|
|
} else if (getPlatform() == Common::kPlatformMacintosh) {
|
|
|
|
dataFile = _macResFork->getDataFork();
|
|
|
|
if (!dataFile)
|
|
|
|
error("Could not get 'Star Trek Data' data fork");
|
|
|
|
} else {
|
|
|
|
dataFile = SearchMan.createReadStreamForMember("data.001");
|
|
|
|
if (!dataFile)
|
|
|
|
error("Could not open data.001");
|
2018-05-05 00:32:08 +00:00
|
|
|
dataRunFile = SearchMan.createReadStreamForMember("data.run");
|
|
|
|
if (!dataFile)
|
|
|
|
error("Could not open data.run");
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
2018-05-05 00:32:08 +00:00
|
|
|
|
|
|
|
if (getFeatures() & GF_DEMO && getPlatform() == Common::kPlatformDOS) {
|
2011-02-20 07:12:35 +00:00
|
|
|
assert(fileCount == 1); // Sanity check...
|
|
|
|
Common::SeekableReadStream *stream = dataFile->readStream(uncompressedSize);
|
|
|
|
delete dataFile;
|
2018-01-19 07:50:51 +00:00
|
|
|
delete dataRunFile;
|
2018-02-24 18:59:08 +00:00
|
|
|
return SharedPtr<FileStream>(new FileStream(stream, bigEndian));
|
2011-02-20 07:12:35 +00:00
|
|
|
} else {
|
2018-05-05 00:32:08 +00:00
|
|
|
if (fileCount != 1) {
|
|
|
|
dataRunFile->seek(indexOffset);
|
|
|
|
|
|
|
|
indexOffset = dataRunFile->readByte() + (dataRunFile->readByte() << 8) + (dataRunFile->readByte() << 16);
|
|
|
|
//indexOffset &= 0xFFFFFE;
|
|
|
|
|
|
|
|
for (uint16 i = 0; i < fileIndex; i++) {
|
|
|
|
uint16 size = dataRunFile->readUint16LE();
|
|
|
|
indexOffset += size;
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-05 00:32:08 +00:00
|
|
|
dataFile->seek(indexOffset);
|
|
|
|
|
|
|
|
uncompressedSize = (getPlatform() == Common::kPlatformAmiga) ? dataFile->readUint16BE() : dataFile->readUint16LE();
|
|
|
|
uint16 compressedSize = (getPlatform() == Common::kPlatformAmiga) ? dataFile->readUint16BE() : dataFile->readUint16LE();
|
|
|
|
|
|
|
|
Common::SeekableReadStream *stream = decodeLZSS(dataFile->readStream(compressedSize), uncompressedSize);
|
2018-01-19 07:50:51 +00:00
|
|
|
|
2018-05-05 00:32:08 +00:00
|
|
|
delete dataFile;
|
2018-01-19 07:50:51 +00:00
|
|
|
delete dataRunFile;
|
2018-02-24 18:59:08 +00:00
|
|
|
return SharedPtr<FileStream>(new FileStream(stream, bigEndian));
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
2018-02-22 02:06:57 +00:00
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
// We should not get to this point...
|
|
|
|
error("Could not find data for \'%s\'", filename.c_str());
|
2018-02-22 02:06:57 +00:00
|
|
|
|
2018-02-24 18:59:08 +00:00
|
|
|
return SharedPtr<FileStream>();
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::playMovie(Common::String filename) {
|
|
|
|
if (getPlatform() == Common::kPlatformMacintosh)
|
|
|
|
playMovieMac(filename);
|
|
|
|
else
|
|
|
|
error("Interplay MVE not yet supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
void StarTrekEngine::playMovieMac(Common::String filename) {
|
|
|
|
// Swap to 16bpp mode
|
|
|
|
initGraphics(512, 384, NULL);
|
|
|
|
|
|
|
|
Video::QuickTimeDecoder *qtDecoder = new Video::QuickTimeDecoder();
|
|
|
|
|
|
|
|
if (!qtDecoder->loadFile(filename))
|
|
|
|
error("Could not open '%s'", filename.c_str());
|
|
|
|
|
|
|
|
bool continuePlaying = true;
|
|
|
|
|
|
|
|
qtDecoder->start();
|
|
|
|
|
|
|
|
while (!qtDecoder->endOfVideo() && !shouldQuit() && continuePlaying) {
|
|
|
|
if (qtDecoder->needsUpdate()) {
|
|
|
|
const ::Graphics::Surface *frame = qtDecoder->decodeNextFrame();
|
|
|
|
|
|
|
|
if (frame) {
|
|
|
|
::Graphics::Surface *convertedFrame = frame->convertTo(_system->getScreenFormat());
|
|
|
|
_system->copyRectToScreen(convertedFrame->getPixels(), convertedFrame->pitch, 0, 0, convertedFrame->w, convertedFrame->h);
|
|
|
|
_system->updateScreen();
|
|
|
|
convertedFrame->free();
|
|
|
|
delete convertedFrame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Event event;
|
|
|
|
while (g_system->getEventManager()->pollEvent(event))
|
|
|
|
if (event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE)
|
|
|
|
continuePlaying = false;
|
|
|
|
|
|
|
|
g_system->delayMillis(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete qtDecoder;
|
|
|
|
|
|
|
|
// Swap back to 8bpp mode
|
2018-05-15 21:36:11 +00:00
|
|
|
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
|
2011-02-20 07:12:35 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 22:21:34 +00:00
|
|
|
uint16 StarTrekEngine::getRandomWord() {
|
|
|
|
return _randomSource.getRandomNumber(0xffff);
|
|
|
|
}
|
|
|
|
|
2011-02-20 07:12:35 +00:00
|
|
|
} // End of namespace StarTrek
|