aboutsummaryrefslogtreecommitdiff
path: root/engines/agi
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi')
-rw-r--r--engines/agi/agi.h18
-rw-r--r--engines/agi/cycle.cpp8
-rw-r--r--engines/agi/detection.cpp10
-rw-r--r--engines/agi/detection_tables.h7
-rw-r--r--engines/agi/font.cpp1
-rw-r--r--engines/agi/systemui.cpp43
-rw-r--r--engines/agi/systemui.h4
-rw-r--r--engines/agi/text.cpp96
-rw-r--r--engines/agi/text.h4
-rw-r--r--engines/agi/words.cpp2
-rw-r--r--engines/agi/words.h2
11 files changed, 164 insertions, 31 deletions
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 6534331f6e..b288557f57 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -148,17 +148,17 @@ enum BooterDisks {
// position and position.v.
//
enum AgiGameFeatures {
- GF_AGIMOUSE = (1 << 0),
+ GF_AGIMOUSE = (1 << 0), // this disables "Click-to-walk mouse interface"
GF_AGDS = (1 << 1),
- GF_AGI256 = (1 << 2),
- GF_AGI256_2 = (1 << 3),
- GF_AGIPAL = (1 << 4),
- GF_MACGOLDRUSH = (1 << 5),
- GF_FANMADE = (1 << 6),
- GF_MENUS = (1 << 7),
- GF_ESCPAUSE = (1 << 8),
+ GF_AGI256 = (1 << 2), // marks fanmade AGI-256 games
+ GF_AGI256_2 = (1 << 3), // marks fanmade AGI-256-2 games
+ GF_AGIPAL = (1 << 4), // marks game using fanmade AGIPAL extension
+ GF_MACGOLDRUSH = (1 << 5), // use "grdir" instead of "dir" for volume loading
+ GF_FANMADE = (1 << 6), // marks fanmade games
+ GF_MENUS = (1 << 7), // not used anymore
+ GF_ESCPAUSE = (1 << 8), // not used anymore, we detect this internally
GF_OLDAMIGAV20 = (1 << 9),
- GF_CLIPCOORDS = (1 << 10),
+ GF_CLIPCOORDS = (1 << 10), // not used atm
GF_2GSOLDSOUND = (1 << 11)
};
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 211513ca36..19aca6f2c4 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -497,7 +497,13 @@ int AgiEngine::runGame() {
break;
case Common::kRenderHercA:
case Common::kRenderHercG:
- setVar(VM_VAR_MONITOR, kAgiMonitorHercules);
+ // Set EGA for now. Some games place text differently, when this is set to kAgiMonitorHercules.
+ // Text placement was different for Hercules rendering (16x12 instead of 16x16). There also was
+ // not enough space left for the prompt at the bottom. This was caused by the Hercules resolution.
+ // We don't have this restriction and we also support the regular prompt for Hercules mode.
+ // In theory Sierra could have had special Hercules code inside their games.
+ // TODO: check this.
+ setVar(VM_VAR_MONITOR, kAgiMonitorEga);
break;
// Don't know if Amiga AGI games use a different value than kAgiMonitorEga
// for vMonitor so I just use kAgiMonitorEga for them (As was done before too).
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 308489ad0b..7d5fd3d668 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -181,6 +181,16 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+ {
+ GAMEOPTION_COMMAND_PROMPT_WINDOW,
+ {
+ _s("Pause when entering commands"),
+ _s("Shows a command prompt window and pauses the game (like in SCI) instead of a real-time prompt."),
+ "commandpromptwindow",
+ false
+ }
+ },
+
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index 87da0c2c01..0938e9d4d6 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -26,11 +26,12 @@ namespace Agi {
#define GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE GUIO_GAMEOPTIONS2
#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3
#define GAMEOPTION_USE_HERCULES_FONT GUIO_GAMEOPTIONS4
+#define GAMEOPTION_COMMAND_PROMPT_WINDOW GUIO_GAMEOPTIONS5
// TODO: properly implement GAMEOPTIONs
-#define GAMEOPTIONS_DEFAULT GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE,GAMEOPTION_USE_HERCULES_FONT)
-#define GAMEOPTIONS_AMIGA GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT)
-#define GAMEOPTIONS_FANMADE_MOUSE GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_USE_HERCULES_FONT)
+#define GAMEOPTIONS_DEFAULT GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
+#define GAMEOPTIONS_AMIGA GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
+#define GAMEOPTIONS_FANMADE_MOUSE GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
#define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp,guioptions) { \
{ \
diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp
index 017167850e..f9605e4a3d 100644
--- a/engines/agi/font.cpp
+++ b/engines/agi/font.cpp
@@ -1232,6 +1232,7 @@ void GfxFont::loadFontHercules() {
fontData += 4; // skip the last 2 lines
}
+ free(rawData);
} else {
warning("Fontfile 'hgc_font': unexpected file size");
}
diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp
index 7aa26131c9..aeb1ded4a2 100644
--- a/engines/agi/systemui.cpp
+++ b/engines/agi/systemui.cpp
@@ -44,6 +44,8 @@ SystemUI::SystemUI(AgiEngine *vm, GfxMgr *gfx, TextMgr *text) {
_textStatusSoundOn = "Sound:on";
_textStatusSoundOff = "Sound:off";
+ _textEnterCommand = "Enter input\n\n";
+
_textPause = " Game paused.\nPress Enter to continue.";
_textPauseButton = nullptr;
@@ -214,6 +216,47 @@ const char *SystemUI::getInventoryTextReturnToGame() {
return _textInventoryReturnToGame;
}
+bool SystemUI::askForCommand(Common::String &commandText) {
+ // Let user enter the command (this was originally only available for Hercules rendering, we allow it everywhere)
+ bool previousEditState = _text->inputGetEditStatus();
+ byte previousEditCursor = _text->inputGetCursorChar();
+
+ _text->drawMessageBox(_textEnterCommand, 0, 36, true);
+
+ _text->inputEditOn();
+
+ _text->charPos_Push();
+ _text->charAttrib_Push();
+
+ _text->charPos_SetInsideWindow(2, 0);
+ _text->charAttrib_Set(15, 0);
+ _text->clearBlockInsideWindow(2, 0, 36, 0); // input line is supposed to be black
+ _text->inputSetCursorChar('_');
+
+ _text->stringSet(commandText.c_str()); // Set current command text (may be a command recall)
+
+ _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING);
+ _text->stringEdit(35); // only allow up to 35 characters
+
+ _text->charAttrib_Pop();
+ _text->charPos_Pop();
+ _text->inputSetCursorChar(previousEditCursor);
+ if (!previousEditState) {
+ _text->inputEditOff();
+ }
+
+ _text->closeWindow();
+
+ if (!_text->stringWasEntered()) {
+ // User cancelled? exit now
+ return false;
+ }
+
+ commandText.clear();
+ commandText += (char *)_text->_inputString;
+ return true;
+}
+
int16 SystemUI::figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription) {
int16 matchedGameSlotId = -1;
int16 freshGameSlotId = -1;
diff --git a/engines/agi/systemui.h b/engines/agi/systemui.h
index ffb1238ebe..283de8794c 100644
--- a/engines/agi/systemui.h
+++ b/engines/agi/systemui.h
@@ -77,6 +77,8 @@ public:
const char *getInventoryTextSelectItems();
const char *getInventoryTextReturnToGame();
+ bool askForCommand(Common::String &commandText);
+
int16 figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription);
int16 figureOutAutomaticRestoreGameSlot(const char *automaticSaveDescription);
@@ -130,6 +132,8 @@ private:
const char *_textStatusSoundOn;
const char *_textStatusSoundOff;
+ const char *_textEnterCommand;
+
const char *_textPause;
const char *_textPauseButton;
const char *_textRestart;
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 18254d88f8..0cacce2421 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/config-manager.h"
#include "agi/agi.h"
#include "agi/sprite.h" // for commit_both()
#include "agi/graphics.h"
@@ -74,6 +75,12 @@ TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) {
configureScreen(2);
_messageBoxCancelled = false;
+
+ _optionCommandPromptWindow = false;
+
+ if (ConfMan.getBool("commandpromptwindow")) {
+ _optionCommandPromptWindow = true;
+ }
}
TextMgr::~TextMgr() {
@@ -671,6 +678,30 @@ void TextMgr::promptKeyPress(uint16 newKey) {
int16 maxChars = 0;
int16 scriptsInputLen = _vm->getVar(VM_VAR_MAX_INPUT_CHARACTERS);
+ bool acceptableInput = false;
+
+ // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.)
+ // In text-mode this sort of worked at least with the DOS interpreter
+ // but as soon as invalid characters were used in graphics mode they weren't properly shown
+ switch (_vm->getLanguage()) {
+ case Common::RU_RUS:
+ if (newKey >= 0x20)
+ acceptableInput = true;
+ break;
+ default:
+ if ((newKey >= 0x20) && (newKey <= 0x7f))
+ acceptableInput = true;
+ break;
+ }
+
+ if (_optionCommandPromptWindow) {
+ // Forward to command prompt window, using last command
+ if (acceptableInput) {
+ promptCommandWindow(false, newKey);
+ }
+ return;
+ }
+
if (_messageState.dialogue_Open) {
maxChars = TEXT_STRING_MAX_SIZE - 4;
} else {
@@ -715,22 +746,6 @@ void TextMgr::promptKeyPress(uint16 newKey) {
}
default:
if (maxChars > _promptCursorPos) {
- bool acceptableInput = false;
-
- // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.)
- // In text-mode this sort of worked at least with the DOS interpreter
- // but as soon as invalid characters were used in graphics mode they weren't properly shown
- switch (_vm->getLanguage()) {
- case Common::RU_RUS:
- if (newKey >= 0x20)
- acceptableInput = true;
- break;
- default:
- if ((newKey >= 0x20) && (newKey <= 0x7f))
- acceptableInput = true;
- break;
- }
-
if (acceptableInput) {
_prompt[_promptCursorPos] = newKey;
_promptCursorPos++;
@@ -747,6 +762,11 @@ void TextMgr::promptKeyPress(uint16 newKey) {
}
void TextMgr::promptCancelLine() {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
+
while (_promptCursorPos) {
promptKeyPress(0x08); // Backspace until prompt is empty
}
@@ -755,6 +775,12 @@ void TextMgr::promptCancelLine() {
void TextMgr::promptEchoLine() {
int16 previousLen = strlen((char *)_promptPrevious);
+ if (_optionCommandPromptWindow) {
+ // Forward to command prompt window, using last command
+ promptCommandWindow(true, 0);
+ return;
+ }
+
if (_promptCursorPos < previousLen) {
inputEditOn();
@@ -771,6 +797,11 @@ void TextMgr::promptRedraw() {
char *textPtr = nullptr;
if (_promptEnabled) {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
+
inputEditOn();
clearLine(_promptRow, _textAttrib.background);
charPos_Set(_promptRow, 0);
@@ -788,6 +819,10 @@ void TextMgr::promptRedraw() {
// for AGI1
void TextMgr::promptClear() {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
clearLine(_promptRow, _textAttrib.background);
}
@@ -797,6 +832,35 @@ void TextMgr::promptRememberForAutoComplete(bool entered) {
#endif
}
+void TextMgr::promptCommandWindow(bool recallLastCommand, uint16 newKey) {
+ Common::String commandText;
+
+ if (recallLastCommand) {
+ commandText += Common::String((char *)_promptPrevious);
+ }
+ if (newKey) {
+ if (newKey != ' ') {
+ // Only add char, when it's not a space.
+ // Original AGI did not filter space, but it makes no sense to start with a space.
+ // Space would get filtered anyway during dictionary parsing.
+ commandText += newKey;
+ }
+ }
+
+ if (_systemUI->askForCommand(commandText)) {
+ if (commandText.size()) {
+ // Something actually was entered?
+ strncpy((char *)&_prompt, commandText.c_str(), sizeof(_prompt));
+ promptRememberForAutoComplete(true);
+ memcpy(&_promptPrevious, &_prompt, sizeof(_prompt));
+ // parse text
+ _vm->_words->parseUsingDictionary((char *)&_prompt);
+
+ _prompt[0] = 0;
+ }
+ }
+}
+
bool TextMgr::stringWasEntered() {
return _inputStringEntered;
}
diff --git a/engines/agi/text.h b/engines/agi/text.h
index 7e9228e30a..f0aeab7762 100644
--- a/engines/agi/text.h
+++ b/engines/agi/text.h
@@ -163,6 +163,8 @@ public:
bool _inputEditEnabled;
byte _inputCursorChar;
+ bool _optionCommandPromptWindow;
+
bool _promptEnabled;
int16 _promptRow;
int16 _promptCursorPos;
@@ -189,6 +191,8 @@ public:
void promptClear(); // for AGI1
void promptRememberForAutoComplete(bool entered = false); // for auto-completion
+ void promptCommandWindow(bool recallLastCommand, uint16 newKey);
+
int16 _inputStringRow;
int16 _inputStringColumn;
bool _inputStringEntered;
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 32fa4cbff4..7072f003c2 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -293,7 +293,7 @@ int16 Words::findWordInDictionary(const Common::String &userInputLowcased, uint1
return wordId;
}
-void Words::parseUsingDictionary(char *rawUserInput) {
+void Words::parseUsingDictionary(const char *rawUserInput) {
Common::String userInput;
Common::String userInputLowcased;
const char *userInputPtr = nullptr;
diff --git a/engines/agi/words.h b/engines/agi/words.h
index c7bf4829c3..96dafae275 100644
--- a/engines/agi/words.h
+++ b/engines/agi/words.h
@@ -57,7 +57,7 @@ public:
void unloadDictionary();
void clearEgoWords();
- void parseUsingDictionary(char *rawUserInput);
+ void parseUsingDictionary(const char *rawUserInput);
private:
void cleanUpInput(const char *userInput, Common::String &cleanInput);