AGI: implement predictive dialog
This commit is contained in:
parent
73242aa5bc
commit
c28e101cdb
6 changed files with 193 additions and 127 deletions
|
@ -231,8 +231,7 @@ struct gameIdList {
|
|||
|
||||
struct Mouse {
|
||||
int button;
|
||||
int x;
|
||||
int y;
|
||||
Common::Point pos;
|
||||
};
|
||||
|
||||
// Used by AGI Mouse protocol 1.0 for v27 (i.e. button pressed -variable).
|
||||
|
@ -942,7 +941,12 @@ public:
|
|||
void cleanKeyboard();
|
||||
|
||||
int16 getSpecialMenuControllerSlot();
|
||||
|
||||
bool handleMouseClicks(uint16 &key);
|
||||
bool handleController(uint16 key);
|
||||
|
||||
bool showPredictiveDialog();
|
||||
|
||||
uint16 agiGetKeypress();
|
||||
int waitKey();
|
||||
int waitAnyKey();
|
||||
|
|
|
@ -206,8 +206,8 @@ int AgiEngine::mainCycle(bool onlyCheckForEvents) {
|
|||
//
|
||||
// We run AGIMOUSE always as a side effect
|
||||
//if (getFeatures() & GF_AGIMOUSE) {
|
||||
setVar(VM_VAR_MOUSE_X, _mouse.x / 2);
|
||||
setVar(VM_VAR_MOUSE_Y, _mouse.y);
|
||||
setVar(VM_VAR_MOUSE_X, _mouse.pos.x / 2);
|
||||
setVar(VM_VAR_MOUSE_Y, _mouse.pos.y);
|
||||
//}
|
||||
|
||||
switch (_game.inputMode) {
|
||||
|
@ -245,21 +245,27 @@ int AgiEngine::mainCycle(bool onlyCheckForEvents) {
|
|||
setVar(VM_VAR_KEY, keyAscii);
|
||||
}
|
||||
|
||||
handleMouseClicks(key);
|
||||
|
||||
if (!cycleInnerLoopIsActive()) {
|
||||
// no inner loop active at the moment, regular processing
|
||||
switch (_game.inputMode) {
|
||||
case INPUTMODE_NORMAL:
|
||||
if (!handleController(key)) {
|
||||
if (key == 0 || (!_text->promptIsEnabled()))
|
||||
break;
|
||||
|
||||
_text->promptCharPress(key);
|
||||
if (key) {
|
||||
if (!handleController(key)) {
|
||||
if ((key) && (_text->promptIsEnabled())) {
|
||||
_text->promptCharPress(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INPUTMODE_NONE:
|
||||
handleController(key);
|
||||
if (key)
|
||||
_game.keypress = key;
|
||||
if (key) {
|
||||
handleController(key);
|
||||
if (key) {
|
||||
_game.keypress = key;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -274,7 +280,6 @@ int AgiEngine::mainCycle(bool onlyCheckForEvents) {
|
|||
switch (_game.cycleInnerLoopType) {
|
||||
case CYCLE_INNERLOOP_GETSTRING: // loop called from TextMgr::stringEdit()
|
||||
case CYCLE_INNERLOOP_GETNUMBER:
|
||||
//handleController(key);
|
||||
if (key) {
|
||||
_text->stringCharPress(key);
|
||||
}
|
||||
|
|
|
@ -68,52 +68,16 @@ void AgiEngine::processEvents() {
|
|||
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_PREDICTIVE_DIALOG: {
|
||||
GUI::PredictiveDialog _predictiveDialog;
|
||||
_predictiveDialog.runModal();
|
||||
#if 0
|
||||
strcpy(_predictiveResult, _predictiveDialog.getResult());
|
||||
if (strcmp(_predictiveResult, "")) {
|
||||
if (_game.inputMode == INPUTMODE_NORMAL) {
|
||||
//strcpy((char *)_game.inputBuffer, _predictiveResult);
|
||||
//handleKeys(KEY_ENTER);
|
||||
// TODO: repair predictive
|
||||
} else if (_game.inputMode == INPUTMODE_GETSTRING) {
|
||||
strcpy(_game.strings[_stringdata.str], _predictiveResult);
|
||||
newInputMode(INPUTMODE_NORMAL);
|
||||
//_gfx->printCharacter(_stringdata.x + strlen(_game.strings[_stringdata.str]) + 1,
|
||||
// _stringdata.y, ' ', 15, 0);
|
||||
} else if (_game.inputMode == INPUTMODE_NONE) {
|
||||
for (int n = 0; _predictiveResult[n]; n++)
|
||||
keyEnqueue(_predictiveResult[n]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
if (predictiveDialog()) {
|
||||
if (_game.inputMode == INPUT_NORMAL) {
|
||||
strcpy((char *)_game.inputBuffer, _predictiveResult);
|
||||
handleKeys(KEY_ENTER);
|
||||
} else if (_game.inputMode == INPUT_GETSTRING) {
|
||||
strcpy(_game.strings[_stringdata.str], _predictiveResult);
|
||||
newInputMode(INPUT_NORMAL);
|
||||
_gfx->printCharacter(_stringdata.x + strlen(_game.strings[_stringdata.str]) + 1,
|
||||
_stringdata.y, ' ', _game.colorFg, _game.colorBg);
|
||||
} else if (_game.inputMode == INPUT_NONE) {
|
||||
for (int n = 0; _predictiveResult[n]; n++)
|
||||
keyEnqueue(_predictiveResult[n]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
case Common::EVENT_PREDICTIVE_DIALOG:
|
||||
showPredictiveDialog();
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
if (_game.mouseEnabled) {
|
||||
key = AGI_MOUSE_BUTTON_LEFT;
|
||||
_mouse.button = kAgiMouseButtonLeft;
|
||||
keyEnqueue(key);
|
||||
_mouse.x = event.mouse.x;
|
||||
_mouse.y = event.mouse.y;
|
||||
_mouse.pos.x = event.mouse.x;
|
||||
_mouse.pos.y = event.mouse.y;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
|
@ -121,8 +85,8 @@ void AgiEngine::processEvents() {
|
|||
key = AGI_MOUSE_BUTTON_RIGHT;
|
||||
_mouse.button = kAgiMouseButtonRight;
|
||||
keyEnqueue(key);
|
||||
_mouse.x = event.mouse.x;
|
||||
_mouse.y = event.mouse.y;
|
||||
_mouse.pos.x = event.mouse.x;
|
||||
_mouse.pos.y = event.mouse.y;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_WHEELUP:
|
||||
|
@ -139,20 +103,20 @@ void AgiEngine::processEvents() {
|
|||
break;
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
if (_game.mouseEnabled) {
|
||||
_mouse.x = event.mouse.x;
|
||||
_mouse.y = event.mouse.y;
|
||||
_mouse.pos.x = event.mouse.x;
|
||||
_mouse.pos.y = event.mouse.y;
|
||||
|
||||
if (!_game.mouseFence.isEmpty()) {
|
||||
if (_mouse.x < _game.mouseFence.left)
|
||||
_mouse.x = _game.mouseFence.left;
|
||||
if (_mouse.x > _game.mouseFence.right)
|
||||
_mouse.x = _game.mouseFence.right;
|
||||
if (_mouse.y < _game.mouseFence.top)
|
||||
_mouse.y = _game.mouseFence.top;
|
||||
if (_mouse.y > _game.mouseFence.bottom)
|
||||
_mouse.y = _game.mouseFence.bottom;
|
||||
if (_mouse.pos.x < _game.mouseFence.left)
|
||||
_mouse.pos.x = _game.mouseFence.left;
|
||||
if (_mouse.pos.x > _game.mouseFence.right)
|
||||
_mouse.pos.x = _game.mouseFence.right;
|
||||
if (_mouse.pos.y < _game.mouseFence.top)
|
||||
_mouse.pos.y = _game.mouseFence.top;
|
||||
if (_mouse.pos.y > _game.mouseFence.bottom)
|
||||
_mouse.pos.y = _game.mouseFence.bottom;
|
||||
|
||||
g_system->warpMouse(_mouse.x, _mouse.y);
|
||||
g_system->warpMouse(_mouse.pos.x, _mouse.pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,8 +125,8 @@ void AgiEngine::processEvents() {
|
|||
case Common::EVENT_RBUTTONUP:
|
||||
if (_game.mouseEnabled) {
|
||||
_mouse.button = kAgiMouseButtonUp;
|
||||
_mouse.x = event.mouse.x;
|
||||
_mouse.y = event.mouse.y;
|
||||
_mouse.pos.x = event.mouse.x;
|
||||
_mouse.pos.y = event.mouse.y;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
|
@ -347,6 +311,86 @@ int16 AgiEngine::getSpecialMenuControllerSlot() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool AgiEngine::handleMouseClicks(uint16 &key) {
|
||||
// No mouse click? -> exit
|
||||
if (key != AGI_MOUSE_BUTTON_LEFT)
|
||||
return false;
|
||||
|
||||
Common::Rect displayLineRect(DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT);
|
||||
int16 statusRow = _text->statusRow_Get();
|
||||
|
||||
displayLineRect.moveTo(0, statusRow * FONT_DISPLAY_HEIGHT);
|
||||
|
||||
if (displayLineRect.contains(_mouse.pos)) {
|
||||
if (getFlag(VM_FLAG_MENUS_WORK) && _menu->isAvailable()) {
|
||||
warning("click on status line -> menu TODO");
|
||||
// TODO: menu
|
||||
// This should be done in a better way as in simulate ESC key
|
||||
// Sierra seems to have hardcoded it in some way, but we would have to verify, what flags
|
||||
// they checked. The previous way wasn't accurate. Mouse support for menu is missing atm anyway.
|
||||
//if ((getflag(VM_FLAG_MENUS_WORK) || (getFeatures() & GF_MENUS)) && _mouse.y <= CHAR_LINES) {
|
||||
// newInputMode(INPUTMODE_MENU);
|
||||
// return true;
|
||||
//}
|
||||
key = 0; // eat event
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_text->promptIsEnabled() && (!cycleInnerLoopIsActive()) ) {
|
||||
// Prompt is currently enabled, but no inner loop is active
|
||||
int16 promptRow = _text->promptRow_Get();
|
||||
|
||||
displayLineRect.moveTo(0, promptRow * FONT_DISPLAY_HEIGHT);
|
||||
|
||||
if (displayLineRect.contains(_mouse.pos)) {
|
||||
// and user clicked within the line of the prompt
|
||||
showPredictiveDialog();
|
||||
|
||||
key = 0; // eat event
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cycleInnerLoopIsActive()) {
|
||||
// inner loop active, check what kind of loop it is. Then process / forward it
|
||||
switch (_game.cycleInnerLoopType) {
|
||||
case CYCLE_INNERLOOP_GETSTRING:
|
||||
case CYCLE_INNERLOOP_GETNUMBER: {
|
||||
// process in here
|
||||
int16 stringRow, stringColumn, stringMaxLen;
|
||||
|
||||
_text->stringPos_Get(stringRow, stringColumn);
|
||||
stringMaxLen = _text->stringGetMaxLen();
|
||||
|
||||
Common::Rect displayRect(stringMaxLen * FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT);
|
||||
displayRect.moveTo(stringColumn * FONT_DISPLAY_WIDTH, stringRow * FONT_DISPLAY_HEIGHT);
|
||||
|
||||
if (displayRect.contains(_mouse.pos)) {
|
||||
// user clicked inside the input space
|
||||
showPredictiveDialog();
|
||||
|
||||
key = 0; // eat event
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CYCLE_INNERLOOP_INVENTORY:
|
||||
// TODO: forward
|
||||
break;
|
||||
case CYCLE_INNERLOOP_MENU:
|
||||
// TODO: forward
|
||||
break;
|
||||
case CYCLE_INNERLOOP_SYSTEMUI_SELECTSAVEDGAMESLOT:
|
||||
// TODO: forward
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AgiEngine::handleController(uint16 key) {
|
||||
ScreenObjEntry *screenObjEgo = &_game.screenObjTable[SCREENOBJECTS_EGO_ENTRY];
|
||||
|
||||
|
@ -407,51 +451,6 @@ bool AgiEngine::handleController(uint16 key) {
|
|||
}
|
||||
}
|
||||
|
||||
if (key == AGI_MOUSE_BUTTON_LEFT) {
|
||||
// call mouse when click is done on status bar
|
||||
// TODO
|
||||
// This should be done in a better way as in simulate ESC key
|
||||
// Sierra seems to have hardcoded it in some way, but we would have to verify, what flags
|
||||
// they checked. The previous way wasn't accurate. Mouse support for menu is missing atm anyway.
|
||||
//if ((getflag(VM_FLAG_MENUS_WORK) || (getFeatures() & GF_MENUS)) && _mouse.y <= CHAR_LINES) {
|
||||
// newInputMode(INPUTMODE_MENU);
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
|
||||
// Show predictive dialog if the user clicks on input area
|
||||
if (key == AGI_MOUSE_BUTTON_LEFT &&
|
||||
(int)_mouse.y >= _text->promptRow_Get() * FONT_DISPLAY_HEIGHT &&
|
||||
(int)_mouse.y <= (_text->promptRow_Get() + 1) * FONT_DISPLAY_HEIGHT) {
|
||||
GUI::PredictiveDialog _predictiveDialog;
|
||||
_predictiveDialog.runModal();
|
||||
#if 0
|
||||
strcpy(_predictiveResult, _predictiveDialog.getResult());
|
||||
if (strcmp(_predictiveResult, "")) {
|
||||
if (_game.inputMode == INPUTMODE_NONE) {
|
||||
for (int n = 0; _predictiveResult[n]; n++)
|
||||
keyEnqueue(_predictiveResult[n]);
|
||||
} else {
|
||||
//strcpy((char *)_game.inputBuffer, _predictiveResult);
|
||||
//handleKeys(KEY_ENTER);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
if (predictiveDialog()) {
|
||||
if (_game.inputMode == INPUT_NONE) {
|
||||
for (int n = 0; _predictiveResult[n]; n++)
|
||||
keyEnqueue(_predictiveResult[n]);
|
||||
} else {
|
||||
strcpy((char *)_game.inputBuffer, _predictiveResult);
|
||||
handleKeys(KEY_ENTER);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 newDirection = 0;
|
||||
|
||||
switch (key) {
|
||||
|
@ -498,8 +497,8 @@ bool AgiEngine::handleController(uint16 key) {
|
|||
//v->flags |= fAdjEgoXY;
|
||||
// setting fAdjEgoXY here will at least break "climbing the log" in SQ2
|
||||
// in case you walked to the log by using the mouse, so don't!!!
|
||||
int16 egoDestinationX = _mouse.x;
|
||||
int16 egoDestinationY = _mouse.y;
|
||||
int16 egoDestinationX = _mouse.pos.x;
|
||||
int16 egoDestinationY = _mouse.pos.y;
|
||||
adjustPosToGameScreen(egoDestinationX, egoDestinationY);
|
||||
|
||||
screenObjEgo->motionType = kMotionEgo;
|
||||
|
@ -534,6 +533,41 @@ bool AgiEngine::handleController(uint16 key) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AgiEngine::showPredictiveDialog() {
|
||||
GUI::PredictiveDialog predictiveDialog;
|
||||
|
||||
inGameTimerPause();
|
||||
predictiveDialog.runModal();
|
||||
inGameTimerResume();
|
||||
|
||||
Common::String predictiveResult(predictiveDialog.getResult());
|
||||
uint16 predictiveResultLen = predictiveResult.size();
|
||||
if (predictiveResult.size()) {
|
||||
// User actually entered something
|
||||
for (int16 resultPos = 0; resultPos < predictiveResultLen; resultPos++) {
|
||||
keyEnqueue(predictiveResult[resultPos]);
|
||||
}
|
||||
if (!cycleInnerLoopIsActive()) {
|
||||
if (_text->promptIsEnabled()) {
|
||||
// add ENTER, when the input is probably meant for the prompt
|
||||
keyEnqueue(AGI_KEY_ENTER);
|
||||
}
|
||||
} else {
|
||||
switch (_game.cycleInnerLoopType) {
|
||||
case CYCLE_INNERLOOP_GETSTRING:
|
||||
case CYCLE_INNERLOOP_GETNUMBER:
|
||||
// add ENTER, when the input is probably meant for GetString/GetNumber
|
||||
keyEnqueue(AGI_KEY_ENTER);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int AgiEngine::waitKey() {
|
||||
int key = 0;
|
||||
|
||||
|
|
|
@ -1851,8 +1851,7 @@ void cmdConfigureScreen(AgiGame *state, uint8 *parameter) {
|
|||
uint16 promptRow = parameter[1];
|
||||
uint16 statusRow = parameter[2];
|
||||
|
||||
state->_vm->_text->configureScreen(lineMinPrint);
|
||||
|
||||
textMgr->configureScreen(lineMinPrint);
|
||||
textMgr->statusRow_Set(statusRow);
|
||||
textMgr->promptRow_Set(promptRow);
|
||||
}
|
||||
|
@ -2256,8 +2255,8 @@ void cmdPushScript(AgiGame *state, uint8 *parameter) {
|
|||
// We run AGIMOUSE always as a side effect
|
||||
//if (getFeatures() & GF_AGIMOUSE || true) {
|
||||
vm->setVar(VM_VAR_MOUSE_BUTTONSTATE, state->_vm->_mouse.button);
|
||||
vm->setVar(VM_VAR_MOUSE_X, state->_vm->_mouse.x / 2);
|
||||
vm->setVar(VM_VAR_MOUSE_Y, state->_vm->_mouse.y);
|
||||
vm->setVar(VM_VAR_MOUSE_X, vm->_mouse.pos.x / 2);
|
||||
vm->setVar(VM_VAR_MOUSE_Y, vm->_mouse.pos.y);
|
||||
/*} else {
|
||||
if (getVersion() >= 0x2915) {
|
||||
debug(0, "push.script");
|
||||
|
@ -2277,8 +2276,8 @@ void cmdMousePosn(AgiGame *state, uint8 *parameter) {
|
|||
AgiEngine *vm = state->_vm;
|
||||
uint16 destVarNr1 = parameter[0];
|
||||
uint16 destVarNr2 = parameter[1];
|
||||
int16 mouseX = state->_vm->_mouse.x;
|
||||
int16 mouseY = state->_vm->_mouse.y;
|
||||
int16 mouseX = vm->_mouse.pos.x;
|
||||
int16 mouseY = vm->_mouse.pos.y;
|
||||
|
||||
state->_vm->adjustPosToGameScreen(mouseX, mouseY);
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) {
|
|||
promptDisable();
|
||||
promptReset();
|
||||
|
||||
_inputStringRow = 0;
|
||||
_inputStringColumn = 0;
|
||||
_inputStringMaxLen = 0;
|
||||
_inputStringCursorPos = 0;
|
||||
_inputString[0] = 0;
|
||||
|
@ -107,16 +109,21 @@ void TextMgr::charPos_Set(int16 row, int16 column) {
|
|||
_textPos.column = column;
|
||||
}
|
||||
|
||||
void TextMgr::charPos_Get(TextPos_Struct *posPtr) {
|
||||
posPtr->row = _textPos.row;
|
||||
posPtr->column = _textPos.column;
|
||||
}
|
||||
|
||||
void TextMgr::charPos_Set(TextPos_Struct *posPtr) {
|
||||
_textPos.row = posPtr->row;
|
||||
_textPos.column = posPtr->column;
|
||||
}
|
||||
|
||||
void TextMgr::charPos_Get(int16 &row, int16 &column) {
|
||||
row = _textPos.row;
|
||||
column = _textPos.column;
|
||||
}
|
||||
|
||||
void TextMgr::charPos_Get(TextPos_Struct *posPtr) {
|
||||
posPtr->row = _textPos.row;
|
||||
posPtr->column = _textPos.column;
|
||||
}
|
||||
|
||||
void TextMgr::charPos_Push() {
|
||||
if (_textPosArrayCount < TEXTPOSARRAY_MAX) {
|
||||
charPos_Get(&_textPosArray[_textPosArrayCount]);
|
||||
|
@ -735,9 +742,21 @@ void TextMgr::stringSet(const char *text) {
|
|||
_inputString[sizeof(_inputString) - 1] = 0; // terminator
|
||||
}
|
||||
|
||||
void TextMgr::stringPos_Get(int16 &row, int16 &column) {
|
||||
row = _inputStringRow;
|
||||
column = _inputStringColumn;
|
||||
}
|
||||
int16 TextMgr::stringGetMaxLen() {
|
||||
return _inputStringMaxLen;
|
||||
}
|
||||
|
||||
void TextMgr::stringEdit(int16 stringMaxLen) {
|
||||
int16 inputStringLen = strlen((const char *)_inputString);
|
||||
|
||||
// Remember current position for predictive dialog
|
||||
_inputStringRow = _textPos.row;
|
||||
_inputStringColumn = _textPos.column;
|
||||
|
||||
// Caller can set the input string
|
||||
_inputStringCursorPos = 0;
|
||||
while (_inputStringCursorPos < inputStringLen) {
|
||||
|
|
|
@ -107,8 +107,9 @@ public:
|
|||
|
||||
void charPos_Clip(int16 &row, int16 &column);
|
||||
void charPos_Set(int16 row, int16 column);
|
||||
void charPos_Get(TextPos_Struct *posPtr);
|
||||
void charPos_Set(TextPos_Struct *posPtr);
|
||||
void charPos_Get(int16 &row, int16 &column);
|
||||
void charPos_Get(TextPos_Struct *posPtr);
|
||||
void charPos_Push();
|
||||
void charPos_Pop();
|
||||
void charPos_SetInsideWindow(int16 windowRow, int16 windowColumn);
|
||||
|
@ -181,12 +182,16 @@ public:
|
|||
void promptClear(); // for AGI1
|
||||
void promptRememberForAutoComplete(bool entered = false); // for auto-completion
|
||||
|
||||
int16 _inputStringRow;
|
||||
int16 _inputStringColumn;
|
||||
bool _inputStringEntered;
|
||||
int16 _inputStringMaxLen;
|
||||
int16 _inputStringCursorPos;
|
||||
byte _inputString[42];
|
||||
|
||||
bool stringWasEntered();
|
||||
void stringPos_Get(int16 &row, int16 &column);
|
||||
int16 stringGetMaxLen();
|
||||
void stringSet(const char *text);
|
||||
void stringEdit(int16 stringMaxLen);
|
||||
void stringCharPress(int16 newChar);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue