aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/gob/hotspots.cpp317
-rw-r--r--engines/gob/hotspots.h41
2 files changed, 288 insertions, 70 deletions
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index f97e2a7f9c..b717bd76df 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -161,12 +161,15 @@ bool Hotspots::Hotspot::buttonMatch(MouseButtons button) const {
MouseButtons myButton = getButton();
if (myButton == kMouseButtonsAny)
+ // Any button allowed
return true;
if (myButton == kMouseButtonsNone)
+ // No button allowed
return false;
if (myButton == button)
+ // Exact match
return true;
return false;
@@ -194,7 +197,9 @@ Hotspots::Hotspots(GobEngine *vm) : _vm(vm) {
Hotspots::~Hotspots() {
delete[] _hotspots;
+ // Pop the whole stack and free each element's memory
while (!_stack.empty()) {
+
StackEntry backup = _stack.pop();
delete[] backup.hotspots;
@@ -278,9 +283,11 @@ void Hotspots::recalculate(bool force) {
Hotspot &spot = _hotspots[i];
if (!force && ((spot.flags & 0x80) != 0))
+ // Not forcing a special hotspot
continue;
if (spot.funcPos == 0)
+ // Simple coordinates don't need update
continue;
// Setting the needed script
@@ -420,6 +427,7 @@ void Hotspots::pop() {
error("Hotspots::pop(): Not enough free space in the current Hotspot "
"array to pop %d elements (got %d)", backup.size, kHotspotCount - i);
+ // Copy
memcpy(destPtr, backup.hotspots, backup.size * sizeof(Hotspot));
_shouldPush = backup.shouldPush;
@@ -474,6 +482,7 @@ void Hotspots::enter(uint16 index) {
Hotspot &spot = _hotspots[index];
+ // If requested, write the ID into a variable
if ((spot.getState() == (kStateFilled | kStateType1)) ||
(spot.getState() == (kStateFilled | kStateType2)))
WRITE_VAR(17, -(spot.id & 0x0FFF));
@@ -492,6 +501,7 @@ void Hotspots::leave(uint16 index) {
Hotspot &spot = _hotspots[index];
+ // If requested, write the ID into a variable
if ((spot.getState() == (kStateFilled | kStateType1)) ||
(spot.getState() == (kStateFilled | kStateType2)))
WRITE_VAR(17, spot.id & 0x0FFF);
@@ -504,21 +514,26 @@ uint16 Hotspots::checkMouse(Type type, uint16 &id, uint16 &index) const {
id = 0;
index = 0;
- if (type == kTypeMove) {
+ if (type == kTypeMove) {
+ // Check where the mouse was moved to
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
if (spot.isDisabled())
+ // Only consider enabled hotspots
continue;
if (spot.getType() > kTypeMove)
+ // Only consider click and move hotspots
continue;
if (spot.getWindow() != 0)
+ // Only check the main window
continue;
if (!spot.isIn(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY))
+ // If we're not in it, ignore it
continue;
id = spot.id;
@@ -530,35 +545,44 @@ uint16 Hotspots::checkMouse(Type type, uint16 &id, uint16 &index) const {
return 0;
} else if (type == kTypeClick) {
+ // Check if something was clicked
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
if (spot.isDisabled())
+ // Only consider enabled hotspots
continue;
if (spot.getWindow() != 0)
+ // Only check the main window
continue;
if (spot.getType() < kTypeMove)
+ // Only consider hotspots that can be clicked
continue;
if (!spot.isIn(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY))
+ // If we're not in it, ignore it
continue;
if (!spot.buttonMatch(_vm->_game->_mouseButtons))
+ // Don't follow hotspots with button requirements we don't meet
continue;
id = spot.id;
index = i;
if ((spot.getType() == kTypeMove) || (spot.getType() == kTypeClick))
+ // It's a move or click => return the key
return spot.key;
+ // Otherwise, the key has a different meaning, so ignore it
return 0;
}
if (_vm->_game->_mouseButtons != kMouseButtonsLeft)
+ // Let the right mouse button act as an escape key
return kKeyEscape;
return 0;
@@ -568,23 +592,29 @@ uint16 Hotspots::checkMouse(Type type, uint16 &id, uint16 &index) const {
return 0;
}
-void Hotspots::checkHotspotChanged() {
+bool Hotspots::checkHotspotChanged() {
uint16 key, id, index;
+ // Get the current hotspot
key = checkMouse(kTypeMove, id, index);
if (key == _currentKey)
- return;
+ // Nothing changed => nothing to do
+ return false;
- if (isValid(_currentKey, _currentId, _currentIndex))
+ // Leave the old area
+ if (isValid(_currentKey, _currentId,_currentIndex))
leave(_currentIndex);
_currentKey = key;
_currentId = id;
_currentIndex = index;
+ // Enter the new one
if (isValid(key, id, index))
enter(index);
+
+ return true;
}
uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index) {
@@ -601,6 +631,8 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
if (handleMouse) {
if ((_vm->_draw->_cursorIndex == -1) && (_currentKey == 0)) {
+ // Last know state: No hotspot hit. Look if that changed
+
_currentKey = checkMouse(kTypeMove, _currentId, _currentIndex);
if (isValid(_currentKey, _currentId, _currentIndex))
@@ -612,6 +644,7 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
uint32 startTime = _vm->_util->getTimeKey();
+ // Update display
_vm->_draw->blitInvalidated();
_vm->_video->retrace();
@@ -624,8 +657,10 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
return 0;
}
+ // Anything changed?
checkHotspotChanged();
+ // Update display
if (!_vm->_draw->_noInvalidated) {
if (handleMouse)
_vm->_draw->animateCursor(-1);
@@ -634,21 +669,27 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
_vm->_video->waitRetrace();
}
+ // Update keyboard and mouse state
key = _vm->_game->checkKeys(&_vm->_global->_inter_mouseX,
&_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, handleMouse);
if (!handleMouse && (_vm->_game->_mouseButtons != kMouseButtonsNone)) {
+ // We don't want any mouse input but got one => Wait till it went away
+
_vm->_util->waitMouseRelease(0);
key = 3;
}
if (key != 0) {
+ // Got a key press
+
if (handleMouse & 1)
_vm->_draw->blitCursor();
id = 0;
index = 0;
+ // Leave the current hotspot
if (isValid(_currentKey, _currentId, _currentIndex))
leave(_currentIndex);
@@ -659,8 +700,11 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
if (handleMouse) {
if (_vm->_game->_mouseButtons != kMouseButtonsNone) {
+ // Mouse button pressed
if (delay > 0) {
+ // If a delay was requested, wait the specified time
+
_vm->_draw->animateCursor(2);
_vm->_util->delay(delay);
} else if (handleMouse & 1)
@@ -668,13 +712,17 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
_vm->_draw->animateCursor(-1);
+ // Which region was clicked?
key = checkMouse(kTypeClick, id, index);
if ((key != 0) || (id != 0)) {
+ // Got a valid region
+
if ( (handleMouse & 1) &&
((delay <= 0) || (_vm->_game->_mouseButtons == kMouseButtonsNone)))
_vm->_draw->blitCursor();
+ // If the hotspot changed, leave the old one
if (key != _currentKey)
leave(_currentIndex);
@@ -683,22 +731,26 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
}
if (handleMouse & 4)
+ // Nothing further than one simple check was requested => return
return 0;
+ // Leave the current area
if (_currentKey != 0)
leave(_currentIndex);
+ // No click, but do we have a move event? If so, enter that hotspot
_currentKey = checkMouse(kTypeMove, _currentId, _currentIndex);
if (isValid(_currentKey, _currentId, _currentIndex))
enter(_currentIndex);
} else
+ // No mouse button pressed, check whether the position changed at least
checkHotspotChanged();
-
}
if ((delay == -2) && (key == 0) &&
(_vm->_game->_mouseButtons == kMouseButtonsNone)) {
+ // Nothing found and no further handling requested. Return.
id = 0;
index = 0;
@@ -711,9 +763,12 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
if ((delay < 0) && (key == 0) &&
(_vm->_game->_mouseButtons == kMouseButtonsNone)) {
+ // Look if we've maybe reached the timeout
+
uint32 curTime = _vm->_util->getTimeKey();
- // Timeout reached?
if ((curTime + delay) > startTime) {
+ // If so, return
+
id = 0;
index = 0;
break;
@@ -721,6 +776,7 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
}
+ // Sleep for a short amount of time
_vm->_util->delay(10);
}
@@ -731,16 +787,20 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
uint16 Hotspots::check(uint8 handleMouse, int16 delay) {
uint16 id, index;
+ // Check and ignore the id and index
return Hotspots::check(handleMouse, delay, id, index);
}
-uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
+uint16 Hotspots::updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
uint16 backColor, uint16 frontColor, char *str, uint16 fontIndex,
- Type type, int16 &duration, uint16 &id, uint16 index) {
+ Type type, int16 &duration, uint16 &id, uint16 &index) {
- if ((fontIndex >= 8) || !_vm->_draw->_fonts[fontIndex])
+ if ((fontIndex >= 8) || !_vm->_draw->_fonts[fontIndex]) {
+ warning("Hotspots::updateInput(): Invalid font specified: %d", fontIndex);
return 0;
+ }
+ // Check if we need to consider mouse events
bool handleMouse = false;
if ( (_vm->_game->_handleMouse != 0) &&
((_vm->_global->_useMouse != 0) || (_vm->_game->_forceHandleMouse != 0)))
@@ -748,15 +808,20 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
const Video::FontDesc &font = *_vm->_draw->_fonts[fontIndex];
+ // The font doesn't specify individual character widths => monospaced
bool monoSpaced = (font.charWidths == 0);
- uint32 pos = strlen(str);
- uint32 editSize = monoSpaced ? (width / font.itemWidth) : 0;
+ // Current position in the string, preset to the end
+ uint32 pos = strlen(str);
+ /* Size of input field in characters.
+ * If the font is not monospaced, we can't know that */
+ uint32 editSize = monoSpaced ? (width / font.itemWidth) : 0;
uint16 key = 0;
char tempStr[256];
while (1) {
+ // If we the edit field has enough space, add a space for the new character
strncpy0(tempStr, str, 254);
strcat(tempStr, " ");
if ((editSize != 0) && strlen(tempStr) > editSize)
@@ -767,12 +832,15 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
monoSpaced ? (editSize * font.itemWidth) : width, height,
backColor);
+ // Print the current string, vertically centered
printText(xPos, yPos + (height - font.itemHeight) / 2,
tempStr, fontIndex, frontColor);
+ // If we've reached the end of the input field, set the cursor to the last character
if ((editSize != 0) && (pos == editSize))
pos--;
+ // The character under the cursor
char curSym = tempStr[pos];
if (_vm->_inter->_variables)
@@ -790,13 +858,16 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
fillRect(cursorX, cursorY, cursorWidth, cursorHeight, frontColor);
if (first) {
+ // The first time, purge old information too
key = check(handleMouse, -1, id, index);
if (key == 0)
+ // We didn't catch any input, let's try again with a real timeout
key = check(handleMouse, -300, id, index);
first = false;
} else
+ // Try to catch a character
key = check(handleMouse, -300, id, index);
tempStr[0] = curSym;
@@ -807,21 +878,27 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
cursorX, cursorY, cursorWidth, cursorHeight);
fillRect(cursorX, cursorY, cursorWidth, cursorHeight, backColor);
+ // Print the current string, vertically centered
printText(cursorX, yPos + (height - font.itemHeight) / 2,
tempStr, fontIndex, frontColor);
if ((key != 0) || (id != 0))
+ // We did get a key, stop looking
break;
+ // Try again
key = check(handleMouse, -300, id, index);
if ((key != 0) || (id != 0) ||
_vm->_inter->_terminate || _vm->shouldQuit())
+ // We did get a key, stop looking
break;
if (duration > 0) {
+ // Look if we reached the time limit
duration -= 600;
if (duration <= 1) {
+ // If so, abort
key = 0;
id = 0;
break;
@@ -831,30 +908,37 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
if ((key == 0) || (id != 0) ||
_vm->_inter->_terminate || _vm->shouldQuit())
+ // Got no key, or a region ID instead, return
return 0;
switch (key) {
case kKeyRight:
+ // If possible, move the cursor right
if ((pos > strlen(str)) || (pos > (editSize - 1)) || (editSize == 0)) {
pos++;
continue;
}
+ // Continue downwards instead
return kKeyDown;
case kKeyLeft:
+ // If possible, move the cursor left
if (pos > 0) {
pos--;
continue;
}
+ // Continue upwards instead
return kKeyUp;
case kKeyBackspace:
if (pos > 0) {
+ // Delete the character to the left
_vm->_util->cutFromStr(str, pos - 1, 1);
pos--;
continue;
} else {
if (pos < strlen(str))
+ // Delete the character to the right
_vm->_util->cutFromStr(str, pos, 1);
}
@@ -862,6 +946,7 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
if (pos >= strlen(str))
continue;
+ // Delete the character to the right
_vm->_util->cutFromStr(str, pos, 1);
continue;
@@ -881,6 +966,7 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
return key;
case kKeyEscape:
+ // If we got an escape event, wait until the mouse buttons have been released
if (_vm->_global->_useMouse != 0)
continue;
@@ -891,17 +977,21 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
((_vm->_global->_useMouse != 0) || (_vm->_game->_forceHandleMouse != 0)))
handleMouse = true;
- while (_vm->_global->_pressedKeys[1] != 0)
- ;
+ while (_vm->_global->_pressedKeys[1] != 0);
continue;
default:
+ // Got a "normal" key
+
uint16 savedKey = key;
key &= 0xFF;
if (((type == kTypeInputFloatNoLeave) || (type == kTypeInputFloatLeave)) &&
(key >= ' ') && (key <= 0xFF)) {
+
+ // Only allow character found in numerical floating values
+
const char *str1 = "0123456789-.,+ ";
const char *str2 = "0123456789-,,+ ";
@@ -924,27 +1014,34 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
if ((key >= ' ') && (key <= 0xFF)) {
if (editSize == 0) {
+ // Length of the string + current character + next one
int length = _vm->_draw->stringLength(str, fontIndex) +
font.charWidths[' ' - font.startItem] +
font.charWidths[key - font.startItem];
if (length > width)
+ // We're above the limit, ignore the key
continue;
if (((int32) strlen(str)) >= (_vm->_global->_inter_animDataSize * 4 - 1))
+ // Above the limit of character allowed in a string, ignore the key
continue;
} else {
if (strlen(str) > editSize)
+ // We're over the upper character limit for this field
continue;
else if (editSize == strlen(str))
+ // We've reached the upper limit, overwrite the last character
_vm->_util->cutFromStr(str, strlen(str) - 1, 1);
}
+ // Advance cursor
pos++;
tempStr[0] = key;
tempStr[1] = 0;
+ // Add character
_vm->_util->insertStr(tempStr, str, pos - 1);
}
@@ -952,22 +1049,25 @@ uint16 Hotspots::readString(uint16 xPos, uint16 yPos, uint16 width, uint16 heigh
}
}
-uint16 Hotspots::handleInput(int16 time, uint16 inputCount, uint16 &curInput,
+uint16 Hotspots::handleInputs(int16 time, uint16 inputCount, uint16 &curInput,
InputDesc *inputs, uint16 &id, uint16 &index) {
+ // Redraw all texts in all inputs we currently manage
updateAllTexts(inputs);
for (int i = 0; i < 40; i++)
- WRITE_VAR_OFFSET(i * 4 + 0x44, 0);
+ WRITE_VAR(17 + i, 0);
while (1) {
- uint16 hotspotIndex = findInput(curInput);
+ // Find the hotspot index to our current input
+ uint16 hotspotIndex = inputToHotspot(curInput);
assert(hotspotIndex != 0xFFFF);
Hotspot inputSpot = _hotspots[hotspotIndex];
- uint16 key = readString(inputSpot.left, inputSpot.top,
+ // Handle input events from that input field
+ uint16 key = updateInput(inputSpot.left, inputSpot.top,
inputSpot.right - inputSpot.left + 1,
inputSpot.bottom - inputSpot.top + 1,
inputs[curInput].backColor, inputs[curInput].frontColor,
@@ -980,15 +1080,19 @@ uint16 Hotspots::handleInput(int16 time, uint16 inputCount, uint16 &curInput,
switch (key) {
case kKeyNone:
if (id == 0)
+ // No key and no hotspot => return
return 0;
if (_vm->_game->_mouseButtons != kMouseButtonsNone)
+ // Clicked something, get the hotspot index
index = findClickedInput(index);
if (!_hotspots[index].isInput())
+ // It's no input, return
return 0;
- curInput = findNthInput(index);
+ // Get the associated input index
+ curInput = hotspotToInput(index);
break;
case kKeyF1:
@@ -1004,12 +1108,11 @@ uint16 Hotspots::handleInput(int16 time, uint16 inputCount, uint16 &curInput,
return key;
case kKeyReturn:
-
// Just one input => return
if (inputCount == 1)
return kKeyReturn;
- // End of input chain reached => wap
+ // End of input chain reached => wrap
if (curInput == (inputCount - 1)) {
curInput = 0;
break;
@@ -1044,6 +1147,8 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
byte window = 0;
if ((type & 0x40) != 0) {
+ // Got a window ID
+
type -= 0x40;
window = _vm->_game->_script->readByte();
}
@@ -1052,12 +1157,14 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
uint16 left, top, width, height, right, bottom;
uint32 funcPos = 0;
if ((type & 0x80) != 0) {
+ // Complex coordinate expressions
funcPos = _vm->_game->_script->pos();
left = _vm->_game->_script->readValExpr();
top = _vm->_game->_script->readValExpr();
width = _vm->_game->_script->readValExpr();
height = _vm->_game->_script->readValExpr();
} else {
+ // Immediate values
funcPos = 0;
left = _vm->_game->_script->readUint16();
top = _vm->_game->_script->readUint16();
@@ -1154,28 +1261,35 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
inputs[inputCount].fontIndex = _vm->_game->_script->readInt16();
inputs[inputCount].backColor = _vm->_game->_script->readByte();
inputs[inputCount].frontColor = _vm->_game->_script->readByte();
+ inputs[inputCount].length = 0;
inputs[inputCount].str = 0;
if ((type >= kTypeInput2NoLeave) && (type <= kTypeInput3Leave)) {
+ uint16 length = _vm->_game->_script->readUint16();
+
inputs[inputCount].str =
- (const char *) (_vm->_game->_script->getData() + _vm->_game->_script->pos() + 2);
- _vm->_game->_script->skip(_vm->_game->_script->peekUint16() + 2);
+ (const char *) (_vm->_game->_script->getData() + _vm->_game->_script->pos());
+
+ _vm->_game->_script->skip(length);
}
if (left == 0xFFFF) {
- if ((type & 1) == 0)
+ if (!(type & 1))
+ // No coordinates but a leave block => skip it
_vm->_game->_script->skipBlock();
break;
}
font = _vm->_draw->_fonts[inputs[inputCount].fontIndex];
if (!font->charWidths)
+ // Monospaced font
right = left + width * font->itemWidth - 1;
funcEnter = 0;
funcPos = 0;
funcLeave = 0;
if (!(type & 1)) {
+ // Got a leave
funcLeave = _vm->_game->_script->pos();
_vm->_game->_script->skipBlock();
}
@@ -1215,6 +1329,7 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
break;
}
+ // Add the new hotspot
add(i | (kStateFilled << 12), left, top, right, bottom,
flags, key, funcEnter, funcLeave, funcPos);
}
@@ -1227,8 +1342,10 @@ void Hotspots::evaluate() {
int16 var_26;
int16 collStackPos;
+ // Push all local hotspots
push(0);
+ // Find the current end of the hotspot block
uint16 endIndex = 0;
while (!_hotspots[endIndex].isEnd())
endIndex++;
@@ -1237,15 +1354,19 @@ void Hotspots::evaluate() {
_vm->_game->_script->skip(1);
+ // Number of new hotspots
byte count = _vm->_game->_script->readByte();
- _vm->_game->_handleMouse = _vm->_game->_script->peekByte(0);
- int16 duration = _vm->_game->_script->peekByte(1);
- byte stackPos2 = _vm->_game->_script->peekByte(3);
- byte descIndex = _vm->_game->_script->peekByte(4);
- bool needRecalculation = _vm->_game->_script->peekByte(5) != 0;
+ // Parameters of this block
+ _vm->_game->_handleMouse = _vm->_game->_script->peekByte(0);
+ int16 duration = _vm->_game->_script->peekByte(1);
+ byte stackPos2 = _vm->_game->_script->peekByte(3);
+ byte descIndex = _vm->_game->_script->peekByte(4);
+ bool needRecalculation = _vm->_game->_script->peekByte(5) != 0;
+ // Seconds -> Milliseconds
duration *= 1000;
+
if ((stackPos2 != 0) || (descIndex != 0)) {
duration /= 100;
if (_vm->_game->_script->peekByte(1) == 100)
@@ -1256,6 +1377,7 @@ void Hotspots::evaluate() {
_vm->_game->_script->skip(6);
+ // Clear current ID
WRITE_VAR(16, 0);
byte var_41 = 0;
@@ -1267,9 +1389,12 @@ void Hotspots::evaluate() {
bool hasInput = false;
uint16 inputCount = 0;
+
+ // Adding new hotspots
for (uint16 i = 0; i < count; i++)
evaluateNew(i, ids, inputs, validId, hasInput, inputCount);
+ // Recalculate all hotspots if requested
if (needRecalculation)
recalculate(true);
@@ -1279,22 +1404,29 @@ void Hotspots::evaluate() {
do {
uint16 key = 0;
if (hasInput) {
+ // Input
+
uint16 curInput = 0;
- key = handleInput(duration, inputCount, curInput, inputs, id, index);
+ key = handleInputs(duration, inputCount, curInput, inputs, id, index);
+ // Notify the script of the current input index
WRITE_VAR(55, curInput);
if (key == kKeyReturn) {
+ // Return pressed, invoke input leave
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
if (!spot.isFilledEnabled())
+ // Not filled or disabled
continue;
if ((spot.getType() & 1) != 0)
+ // Not an input with a leave function
continue;
if (spot.getType() <= kTypeClick)
+ // Not an input
continue;
id = spot.id;
@@ -1305,20 +1437,29 @@ void Hotspots::evaluate() {
break;
}
} else
+ // Normal move or click check
key = check(_vm->_game->_handleMouse, -duration, id, index);
+ // Handle special number keys
if (((key & 0xFF) >= ' ') && ((key & 0xFF) <= 0xFF) &&
((key >> 8) > 1) && ((key >> 8) < 12))
key = '0' + (((key >> 8) - 1) % 10) + (key & 0xFF00);
if (id == 0) {
+ // No hotspot area
+
if (key != 0) {
+ // But a key
+
+ // Find the hotspot with that key associated
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
if (!spot.isFilledEnabled())
+ // Not filled or disabled
continue;
+ // Key match Catch all
if ((spot.key == key) || (spot.key == 0x7FFF)) {
id = spot.id;
index = i;
@@ -1327,6 +1468,9 @@ void Hotspots::evaluate() {
}
if (id == 0) {
+ // Didn't find such a hotspot
+
+ // Try it again, this time case insensitively
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
@@ -1358,26 +1502,33 @@ void Hotspots::evaluate() {
collStackPos++;
if (collStackPos != stackPos2)
+ // Isn't yet the one wanted
continue;
id = spot.id;
index = i;
_vm->_inter->storeMouse();
if (VAR(16) != 0)
+ // We already handle a hotspot
break;
+ // Notify the scripts that we now handle this hotspot
if (Hotspot::getState(id) == kStateFilled)
WRITE_VAR(16, ids[id & 0xFFF]);
else
WRITE_VAR(16, id & 0xFFF);
if (spot.funcLeave != 0) {
+ // It has a leave function
+
uint32 timeKey = _vm->_util->getTimeKey();
call(spot.funcLeave);
if (timeVal != 2) {
+ // Rest time we have left = time we had - time the leave took
duration = timeVal - (_vm->_util->getTimeKey() - timeKey);
+ // Remove the buffer time
if ((duration - var_46) < 3) {
var_46 -= (duration - 3);
duration = 3;
@@ -1386,6 +1537,7 @@ void Hotspots::evaluate() {
var_46 = 0;
}
+ // Clamp
if (duration > timeVal)
duration = timeVal;
@@ -1395,6 +1547,7 @@ void Hotspots::evaluate() {
}
if (VAR(16) == 0)
+ // Didn't find anything
id = 0;
else
var_41 = 1;
@@ -1406,6 +1559,7 @@ void Hotspots::evaluate() {
if (descIndex != 0) {
counter = 0;
+ // Enter the nth hotspot
for (int i = endIndex; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
@@ -1421,6 +1575,7 @@ void Hotspots::evaluate() {
} else {
+ // Enter the first hotspot
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
@@ -1431,6 +1586,7 @@ void Hotspots::evaluate() {
}
}
+ // Leave the current hotspot
if ((_currentKey != 0) && (_hotspots[_currentIndex].funcLeave != 0))
call(_hotspots[_currentIndex].funcLeave);
@@ -1445,15 +1601,18 @@ void Hotspots::evaluate() {
break;
if ((id == 0) || (_hotspots[index].funcLeave != 0))
+ // We don't have a new ID, but haven't yet handled the leave function
continue;
_vm->_inter->storeMouse();
+ // Notify the scripts of the currently handled hotspot
if (Hotspot::getState(id) == kStateFilled)
WRITE_VAR(16, ids[id & 0xFFF]);
else
WRITE_VAR(16, id & 0xFFF);
+ // Enter it
if (_hotspots[index].funcEnter != 0)
call(_hotspots[index].funcEnter);
@@ -1470,23 +1629,30 @@ void Hotspots::evaluate() {
for (int i = 0; i < kHotspotCount; i++) {
Hotspot &spot = _hotspots[i];
+ // Looking for all enabled inputs
if (spot.isEnd())
continue;
-
if (!spot.isFilledEnabled())
continue;
-
if (!spot.isInput())
continue;
- if (spot.getType() > kTypeInput3Leave) {
+ // Clean up numerical floating values
+ if (spot.getType() >= kTypeInputFloatNoLeave) {
+ // Get the string
char *ptr;
strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+
+ // Remove spaces
while ((ptr = strchr(tempStr, ' ')))
_vm->_util->cutFromStr(tempStr, (ptr - tempStr), 1);
+
+ // Exchange decimal separator if needed
if (_vm->_global->_language == kLanguageBritish)
while ((ptr = strchr(tempStr, '.')))
*ptr = ',';
+
+ // Write it back
WRITE_VARO_STR(spot.key, tempStr);
}
@@ -1498,6 +1664,7 @@ void Hotspots::evaluate() {
if (spot.getType() < kTypeInput3NoLeave)
_vm->_util->cleanupStr(tempStr);
+ // Look if we find a match between the wanted and the typed string
int16 pos = 0;
do {
char spotStr[256];
@@ -1510,20 +1677,23 @@ void Hotspots::evaluate() {
if (spot.getType() < kTypeInput3NoLeave)
_vm->_util->cleanupStr(spotStr);
+ // Compare the entered string with the string we wanted
if (strcmp(tempStr, spotStr) == 0) {
WRITE_VAR(17, VAR(17) + 1);
WRITE_VAR(17 + var_26, 1);
break;
}
- } while (READ_LE_UINT16(inputs[var_24].str - 2) > pos);
+ } while (inputs[var_24].length > pos);
+
collStackPos++;
- } else {
+ } else
WRITE_VAR(17 + var_26, 2);
- }
+
var_24++;
var_26++;
}
+ // Notify the scripts if we reached the requested hotspot
if (collStackPos != (int16) VAR(17))
WRITE_VAR(17, 0);
else
@@ -1538,6 +1708,8 @@ void Hotspots::evaluate() {
_vm->_inter->storeMouse();
if (VAR(16) == 0) {
+ // No hotspot currently handled, now we'll handle the newly found one
+
if (Hotspot::getState(id) == kStateFilled)
WRITE_VAR(16, ids[id & 0xFFF]);
else
@@ -1547,14 +1719,16 @@ void Hotspots::evaluate() {
_vm->_game->_script->setFinished(true);
for (int i = 0; i < count; i++)
+ // Remove all local hotspots
remove(i + (kStateFilled << 12));
for (int i = 0; i < kHotspotCount; i++) {
Hotspot &spot = _hotspots[i];
- if ((spot.getState() == (kStateFilled | kStateType1)) ||
- (spot.getState() == (kStateFilled | kStateType2)))
- spot.disable();
+ // Disable the ones still there
+ if ((spot.getState() == (kStateFilled | kStateType1)) ||
+ (spot.getState() == (kStateFilled | kStateType2)))
+ spot.disable();
}
}
@@ -1566,58 +1740,94 @@ int16 Hotspots::findCursor(uint16 x, uint16 y) const {
Hotspot &spot = _hotspots[i];
if ((spot.getWindow() != 0) || spot.isDisabled())
+ // Ignore disabled and non-main-windowed hotspots
continue;
if (!spot.isIn(x, y))
+ // We're not in that hotspot, ignore it
continue;
if (spot.getCursor() == 0) {
+ // Hotspot doesn't itself specify a cursor...
if (spot.getType() >= kTypeInput1NoLeave) {
+ // ...but the type has a generic one
cursor = 3;
break;
} else if ((spot.getButton() != kMouseButtonsRight) && (cursor == 0))
+ // ...but there's a generic "click" cursor
cursor = 1;
} else if (cursor == 0)
+ // Hotspot had an attached cursor index
cursor = spot.getCursor();
}
return cursor;
}
-uint16 Hotspots::findInput(uint16 input) const {
+uint16 Hotspots::inputToHotspot(uint16 input) const {
uint16 inputIndex = 0;
for (int i = 0; i < kHotspotCount; i++) {
Hotspot &spot = _hotspots[i];
if (!spot.isActiveInput())
+ // Not an active input
continue;
if (inputIndex == input)
+ // We've found our input
return i;
+ // Next one
inputIndex++;
}
+ // None found
return 0xFFFF;
}
+uint16 Hotspots::hotspotToInput(uint16 hotspot) const {
+ uint16 input = 0;
+
+ for (int i = 0; i < kHotspotCount; i++) {
+ Hotspot &spot = _hotspots[i];
+
+ if (!spot.isActiveInput())
+ // Not an active input
+ continue;
+
+ if (i == hotspot)
+ // We've found our hotspot
+ break;
+
+ // Next one
+ input++;
+ }
+
+ return input;
+}
+
uint16 Hotspots::findClickedInput(uint16 index) const {
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
Hotspot &spot = _hotspots[i];
if (spot.getWindow() != 0)
+ // Ignore other windows
continue;
if (spot.isDisabled())
+ // Ignore disabled hotspots
continue;
if (!spot.isIn(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY))
+ // This one wasn't it
continue;
if (spot.getCursor() != 0)
+ // This one specifies a cursor, so we don't want it
continue;
if (!spot.isInput())
+ // It's no input
continue;
index = i;
@@ -1627,24 +1837,6 @@ uint16 Hotspots::findClickedInput(uint16 index) const {
return index;
}
-uint16 Hotspots::findNthInput(uint16 n) const {
- uint16 input = 0;
-
- for (int i = 0; i < kHotspotCount; i++) {
- Hotspot &spot = _hotspots[i];
-
- if (!spot.isActiveInput())
- continue;
-
- if (i == n)
- break;
-
- input++;
- }
-
- return input;
-}
-
void Hotspots::getTextCursorPos(const Video::FontDesc &font, const char *str,
uint32 pos, uint16 x, uint16 y, uint16 width, uint16 height,
uint16 &cursorX, uint16 &cursorY, uint16 &cursorWidth, uint16 &cursorHeight) const {
@@ -1657,6 +1849,7 @@ void Hotspots::getTextCursorPos(const Video::FontDesc &font, const char *str,
cursorWidth = 1;
cursorHeight = height;
+ // Iterate through the string and add each character's width
for (uint32 i = 0; i < pos; i++)
cursorX += font.charWidths[str[i] - font.startItem];
@@ -1699,25 +1892,33 @@ void Hotspots::updateAllTexts(const InputDesc *inputs) const {
const Hotspot &spot = _hotspots[i];
if (spot.isEnd())
+ // It's an end, we don't want it
continue;
if (!spot.isFilledEnabled())
+ // This one's either not used or disabled
continue;
if (!spot.isInput())
+ // Not an input
continue;
+ // Get its text
char tempStr[256];
strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+ // Coordinates
uint16 x = spot.left;
uint16 y = spot.top;
uint16 width = spot.right - spot.left + 1;
uint16 height = spot.bottom - spot.top + 1;
+ // Clear the background
fillRect(x, y, width, height, inputs[input].backColor);
- y += (width - _vm->_draw->_fonts[_vm->_draw->_fontIndex]->itemHeight) / 2;
+ // Center the text vertically
+ y += (height - _vm->_draw->_fonts[_vm->_draw->_fontIndex]->itemHeight) / 2;
+ // Draw it
printText(x, y, tempStr, inputs[input].fontIndex, inputs[input].frontColor);
input++;
diff --git a/engines/gob/hotspots.h b/engines/gob/hotspots.h
index 79a80526de..893991bfbe 100644
--- a/engines/gob/hotspots.h
+++ b/engines/gob/hotspots.h
@@ -66,6 +66,7 @@ public:
Hotspots(GobEngine *vm);
~Hotspots();
+ /** Remove all hotspots. */
void clear();
/** Add a hotspot, returning the new index. */
@@ -74,7 +75,9 @@ public:
uint16 flags, uint16 key,
uint16 funcEnter, uint16 funcLeave, uint16 funcPos);
+ /** Remove a specific hotspot. */
void remove(uint16 id);
+ /** Remove all hotspots in this state. */
void removeState(uint8 state);
/** Push the current hotspots onto the stack.
@@ -86,11 +89,15 @@ public:
/** Pop hotspots from the stack. */
void pop();
+ /** Check the current hotspot. */
uint16 check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index);
+ /** Check the current hotspot. */
uint16 check(uint8 handleMouse, int16 delay);
+ /** Evaluate hotspot changes. */
void evaluate();
+ /** Return the cursor found in the hotspot to the coordinates. */
int16 findCursor(uint16 x, uint16 y) const;
private:
@@ -123,13 +130,13 @@ private:
/** Is this hotspot the block end marker? */
bool isEnd() const;
- bool isInput() const;
+ bool isInput() const;
bool isActiveInput() const;
- bool isFilled() const;
+ bool isFilled() const;
bool isFilledEnabled() const;
- bool isFilledNew() const;
- bool isDisabled() const;
+ bool isFilledNew() const;
+ bool isDisabled() const;
/** Are the specified coordinates in the hotspot? */
bool isIn(uint16 x, uint16 y) const;
@@ -155,6 +162,7 @@ private:
uint16 fontIndex;
uint16 backColor;
uint16 frontColor;
+ uint16 length;
const char *str;
};
@@ -178,6 +186,7 @@ private:
*/
void recalculate(bool force);
+ /** Is this a valid hotspot? */
bool isValid(uint16 key, uint16 id, uint16 index) const;
/** Call a hotspot subroutine. */
@@ -190,32 +199,40 @@ private:
/** Which hotspot is the mouse cursor currently at? */
uint16 checkMouse(Type type, uint16 &id, uint16 &index) const;
- void checkHotspotChanged();
+ /** Did the current hotspot change in the meantime? */
+ bool checkHotspotChanged();
- uint16 readString(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
+ /** Update events from a specific input. */
+ uint16 updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
uint16 backColor, uint16 frontColor, char *str, uint16 fontIndex,
- Type type, int16 &duration, uint16 &id, uint16 index);
+ Type type, int16 &duration, uint16 &id, uint16 &index);
- uint16 handleInput(int16 time, uint16 inputCount, uint16 &curInput,
+ /** Handle all inputs we currently manage. */
+ uint16 handleInputs(int16 time, uint16 inputCount, uint16 &curInput,
InputDesc *inputs, uint16 &id, uint16 &index);
+ /** Evaluate adding new hotspots script commands. */
void evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
uint16 &validId, bool &hasInput, uint16 &inputCount);
- // Finding certain inputs
- uint16 findInput(uint16 input) const;
+ /** Find the hotspot index that corresponds to the input index. */
+ uint16 inputToHotspot(uint16 input) const;
+ /** Find the input index that corresponds to the hotspot index. */
+ uint16 hotspotToInput(uint16 hotspot) const;
+ /** Find the input that was clicked on. */
uint16 findClickedInput(uint16 index) const;
- uint16 findNthInput(uint16 n) const;
/** Calculate the graphical cursor position. */
void getTextCursorPos(const Video::FontDesc &font, const char *str,
uint32 pos, uint16 x, uint16 y, uint16 width, uint16 height,
uint16 &cursorX, uint16 &cursorY, uint16 &cursorWidth, uint16 &cursorHeight) const;
- // Drawing functions
+ /** Fill that rectangle with the color. */
void fillRect(uint16 x, uint16 y, uint16 width, uint16 height, uint16 color) const;
+ /** Print the given text. */
void printText(uint16 x, uint16 y, const char *str, uint16 fontIndex, uint16 color) const;
+ /** Go through all inputs we manage and redraw their texts. */
void updateAllTexts(const InputDesc *inputs) const;
};