aboutsummaryrefslogtreecommitdiff
path: root/engines/pegasus/pegasus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/pegasus/pegasus.cpp')
-rw-r--r--engines/pegasus/pegasus.cpp156
1 files changed, 139 insertions, 17 deletions
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
index 81e8058628..463e81e52e 100644
--- a/engines/pegasus/pegasus.cpp
+++ b/engines/pegasus/pegasus.cpp
@@ -33,9 +33,11 @@
#include "common/textconsole.h"
#include "common/translation.h"
#include "common/random.h"
+#include "backends/keymapper/keymapper.h"
#include "base/plugins.h"
#include "base/version.h"
#include "gui/saveload.h"
+#include "video/theora_decoder.h"
#include "video/qt_decoder.h"
#include "pegasus/console.h"
@@ -151,6 +153,7 @@ Common::Error PegasusEngine::run() {
}
// Set up input
+ initKeymap();
InputHandler::setInputHandler(this);
allowInput(true);
@@ -237,6 +240,9 @@ bool PegasusEngine::detectOpeningClosingDirectory() {
void PegasusEngine::createItems() {
Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80);
+ if (!res)
+ error("Couldn't find neighborhood items resource");
+
uint16 entryCount = res->readUint16BE();
for (uint16 i = 0; i < entryCount; i++) {
@@ -614,7 +620,7 @@ void PegasusEngine::loadFromContinuePoint() {
// Failure to load a continue point is fatal
if (!_continuePoint)
- error("Attempting to load from non-existant continue point");
+ error("Attempting to load from non-existent continue point");
_continuePoint->seek(0);
@@ -637,9 +643,15 @@ void PegasusEngine::writeContinueStream(Common::WriteStream *stream) {
delete[] data;
}
+Common::StringArray PegasusEngine::listSaveFiles() {
+ Common::StringArray fileNames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav");
+ Common::sort(fileNames.begin(), fileNames.end());
+ return fileNames;
+}
+
Common::Error PegasusEngine::loadGameState(int slot) {
- Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav");
- Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]);
+ Common::StringArray fileNames = listSaveFiles();
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(fileNames[slot]);
if (!loadFile)
return Common::kUnknownError;
@@ -649,7 +661,23 @@ Common::Error PegasusEngine::loadGameState(int slot) {
return valid ? Common::kNoError : Common::kUnknownError;
}
+static bool isValidSaveFileChar(char c) {
+ // Limit it to letters, digits, and a few other characters that should be safe
+ return Common::isAlnum(c) || c == ' ' || c == '_' || c == '+' || c == '-' || c == '.';
+}
+
+static bool isValidSaveFileName(const Common::String &desc) {
+ for (uint32 i = 0; i < desc.size(); i++)
+ if (!isValidSaveFileChar(desc[i]))
+ return false;
+
+ return true;
+}
+
Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) {
+ if (!isValidSaveFileName(desc))
+ return Common::Error(Common::kCreatingFileFailed, _("Invalid save file name"));
+
Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str());
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output, false);
if (!saveFile)
@@ -667,19 +695,35 @@ void PegasusEngine::receiveNotification(Notification *notification, const Notifi
case kGameStartingFlag: {
useMenu(new MainMenu());
- if (!isDemo()) {
+ if (isDemo()) {
+ // Start playing the music earlier here
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+
+ // Show the intro splash screen
+ showTempScreen("Images/Demo/NGsplashScrn.pict");
+
+ if (shouldQuit()) {
+ useMenu(0);
+ return;
+ }
+
+ // Fade out and then back in with the main menu
+ _gfx->doFadeOutSync();
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+ } else {
+ // Display the intro
runIntro();
resetIntroTimer();
- } else {
- showTempScreen("Images/Demo/NGsplashScrn.pict");
- }
- if (shouldQuit())
- return;
+ if (shouldQuit())
+ return;
- _gfx->invalRect(Common::Rect(0, 0, 640, 480));
- _gfx->updateDisplay();
- ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ // Now display the main menu
+ _gfx->invalRect(Common::Rect(0, 0, 640, 480));
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ }
break;
}
case kPlayerDiedFlag:
@@ -818,7 +862,8 @@ void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) {
case kMenuCmdDeathQuitDemo:
if (isDemo())
showTempScreen("Images/Demo/NGquitScrn.pict");
- _system->quit();
+ _gfx->doFadeOutSync();
+ quitGame();
break;
case kMenuCmdOverview:
stopIntroTimer();
@@ -1132,10 +1177,15 @@ void PegasusEngine::doInterfaceOverview() {
controllerHighlight.hide();
}
- overviewText.setTime(time * 3 + 2, 15);
- overviewText.redrawMovieWorld();
+ // The original just constantly redraws the frame, but that
+ // doesn't actually need to be done.
+ if ((time * 3 + 2) * 40 != overviewText.getTime()) {
+ overviewText.setTime(time * 3 + 2, 15);
+ overviewText.redrawMovieWorld();
+ }
refreshDisplay();
+ _system->delayMillis(10);
}
if (shouldQuit())
@@ -1313,8 +1363,14 @@ bool PegasusEngine::playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16
if (video->needsUpdate()) {
const Graphics::Surface *frame = video->decodeNextFrame();
- if (frame)
- drawScaledFrame(frame, x, y);
+ if (frame) {
+ if (frame->w <= 320 && frame->h <= 240) {
+ drawScaledFrame(frame, x, y);
+ } else {
+ _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+ _system->updateScreen();
+ }
+ }
}
Input input;
@@ -1338,6 +1394,19 @@ void PegasusEngine::die(const DeathReason reason) {
}
void PegasusEngine::doDeath() {
+#ifdef USE_THEORADEC
+ // The updated demo has a new Theora video for the closing
+ if (isDVDDemo() && _deathReason == kPlayerWonGame) {
+ Video::TheoraDecoder decoder;
+
+ if (decoder.loadFile("Images/Demo TSA/DemoClosing.ogg")) {
+ throwAwayEverything();
+ decoder.start();
+ playMovieScaled(&decoder, 0, 0);
+ }
+ }
+#endif
+
_gfx->doFadeOutSync();
throwAwayEverything();
useMenu(new DeathMenu(_deathReason));
@@ -1420,6 +1489,10 @@ void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode oldMod
}
bool PegasusEngine::canSwitchGameMode(const GameMode newMode, const GameMode oldMode) {
+ // WORKAROUND: Don't allow game mode switches when the interface is not set up.
+ // Prevents segfaults when pressing 'i' when in the space chase.
+ if (!g_interface)
+ return false;
if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick)
return false;
if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick)
@@ -1538,6 +1611,18 @@ void PegasusEngine::startNewGame() {
GameState.setPrehistoricSeenFlyer2(false);
GameState.setPrehistoricSeenBridgeZoom(false);
GameState.setPrehistoricBreakerThrown(false);
+
+#ifdef USE_THEORADEC
+ if (isDVD()) {
+ // The updated demo has a new Theora video for the closing
+ Video::TheoraDecoder decoder;
+
+ if (decoder.loadFile("Images/Demo TSA/DemoOpening.ogg")) {
+ decoder.start();
+ playMovieScaled(&decoder, 0, 0);
+ }
+ }
+#endif
} else {
jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast);
}
@@ -2343,4 +2428,41 @@ uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const {
return 1;
}
+void PegasusEngine::initKeymap() {
+#ifdef ENABLE_KEYMAPPER
+ static const char *const kKeymapName = "pegasus";
+ Common::Keymapper *const mapper = _eventMan->getKeymapper();
+
+ // Do not try to recreate same keymap over again
+ if (mapper->getKeymap(kKeymapName) != 0)
+ return;
+
+ Common::Keymap *const engineKeyMap = new Common::Keymap(kKeymapName);
+
+ // Since the game has multiple built-in keys for each of these anyway,
+ // this just attempts to remap one of them.
+ const Common::KeyActionEntry keyActionEntries[] = {
+ { Common::KEYCODE_UP, "UP", _("Up/Zoom In/Move Forward/Open Doors") },
+ { Common::KEYCODE_DOWN, "DWN", _("Down/Zoom Out") },
+ { Common::KEYCODE_LEFT, "TL", _("Turn Left") },
+ { Common::KEYCODE_RIGHT, "TR", _("Turn Right") },
+ { Common::KEYCODE_BACKQUOTE, "TIV", _("Display/Hide Inventory Tray") },
+ { Common::KEYCODE_BACKSPACE, "TBI", _("Display/Hide Biochip Tray") },
+ { Common::KEYCODE_RETURN, "ENT", _("Action/Select") },
+ { Common::KEYCODE_t, "TMA", _("Toggle Center Data Display") },
+ { Common::KEYCODE_i, "TIN", _("Display/Hide Info Screen") },
+ { Common::KEYCODE_ESCAPE, "PM", _("Display/Hide Pause Menu") },
+ { Common::KEYCODE_e, "WTF", _("???") } // easter egg key (without being completely upfront about it)
+ };
+
+ for (uint i = 0; i < ARRAYSIZE(keyActionEntries); i++) {
+ Common::Action *const act = new Common::Action(engineKeyMap, keyActionEntries[i].id, keyActionEntries[i].description);
+ act->addKeyEvent(keyActionEntries[i].ks);
+ }
+
+ mapper->addGameKeymap(engineKeyMap);
+ mapper->pushKeymap(kKeymapName, true);
+#endif
+}
+
} // End of namespace Pegasus