1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/str.h"
24
25 #include "gob/hotspots.h"
26 #include "gob/global.h"
27 #include "gob/draw.h"
28 #include "gob/game.h"
29 #include "gob/script.h"
30 #include "gob/inter.h"
31
32 namespace Gob {
33
Hotspot()34 Hotspots::Hotspot::Hotspot() {
35 clear();
36 }
37
Hotspot(uint16 i,uint16 l,uint16 t,uint16 r,uint16 b,uint16 f,uint16 k,uint16 enter,uint16 leave,uint16 pos)38 Hotspots::Hotspot::Hotspot(uint16 i,
39 uint16 l, uint16 t, uint16 r, uint16 b, uint16 f, uint16 k,
40 uint16 enter, uint16 leave, uint16 pos) {
41
42 id = i;
43 left = l;
44 top = t;
45 right = r;
46 bottom = b;
47 flags = f;
48 key = k;
49 funcEnter = enter;
50 funcLeave = leave;
51 funcPos = pos;
52 script = 0;
53 }
54
clear()55 void Hotspots::Hotspot::clear() {
56 id = 0;
57 left = 0xFFFF;
58 top = 0;
59 right = 0;
60 bottom = 0;
61 flags = 0;
62 key = 0;
63 funcEnter = 0;
64 funcLeave = 0;
65 funcPos = 0;
66 script = 0;
67 }
68
getType() const69 Hotspots::Type Hotspots::Hotspot::getType() const {
70 return (Type) (flags & 0xF);
71 }
72
getButton() const73 MouseButtons Hotspots::Hotspot::getButton() const {
74 uint8 buttonBits = ((flags & 0x70) >> 4);
75
76 if (buttonBits == 0)
77 return kMouseButtonsLeft;
78 if (buttonBits == 1)
79 return kMouseButtonsRight;
80 if (buttonBits == 2)
81 return kMouseButtonsAny;
82
83 return kMouseButtonsNone;
84 }
85
getWindow() const86 uint16 Hotspots::Hotspot::getWindow() const {
87 return (flags & 0x0F00);
88 }
89
getCursor() const90 uint8 Hotspots::Hotspot::getCursor() const {
91 return (flags & 0xF000) >> 12;
92 }
93
getState(uint16 id)94 uint8 Hotspots::Hotspot::getState(uint16 id) {
95 return (id & 0xF000) >> 12;
96 }
97
getState() const98 uint8 Hotspots::Hotspot::getState() const {
99 return getState(id);
100 }
101
isEnd() const102 bool Hotspots::Hotspot::isEnd() const {
103 return (left == 0xFFFF);
104 }
105
isInput() const106 bool Hotspots::Hotspot::isInput() const {
107 if (getType() < kTypeInput1NoLeave)
108 return false;
109
110 if (getType() > kTypeInputFloatLeave)
111 return false;
112
113 return true;
114 }
115
isActiveInput() const116 bool Hotspots::Hotspot::isActiveInput() const {
117 if (isEnd())
118 return false;
119
120 if (!isFilledEnabled())
121 return false;
122
123 if (!isInput())
124 return false;
125
126 return true;
127 }
128
isInputLeave() const129 bool Hotspots::Hotspot::isInputLeave() const {
130 if (!isInput())
131 return false;
132
133 if (!(getType() & 1))
134 return true;
135
136 return false;
137 }
138
isFilled() const139 bool Hotspots::Hotspot::isFilled() const {
140 return getState() & kStateFilled;
141 }
142
isFilledEnabled() const143 bool Hotspots::Hotspot::isFilledEnabled() const {
144 return (getState() & kStateFilledDisabled) == kStateFilled;
145 }
146
isFilledNew() const147 bool Hotspots::Hotspot::isFilledNew() const {
148 return getState() == kStateFilled;
149 }
150
isDisabled() const151 bool Hotspots::Hotspot::isDisabled() const {
152 return getState() & kStateDisabled;
153 }
154
isIn(uint16 x,uint16 y) const155 bool Hotspots::Hotspot::isIn(uint16 x, uint16 y) const {
156 // FIXME: the cast to int16 is a hack, to fix handling of Gob2 problems related to
157 // hotspots with negative offset (to temporary disable them).
158 if ((int16) x < (int16) left)
159 return false;
160 if ((int16) x > (int16) right)
161 return false;
162 if ((int16) y < (int16) top)
163 return false;
164 if ((int16) y > (int16) bottom)
165 return false;
166
167 return true;
168 }
169
buttonMatch(MouseButtons button) const170 bool Hotspots::Hotspot::buttonMatch(MouseButtons button) const {
171 MouseButtons myButton = getButton();
172
173 if (myButton == kMouseButtonsAny)
174 // Any button allowed
175 return true;
176
177 if (myButton == kMouseButtonsNone)
178 // No button allowed
179 return false;
180
181 if (myButton == button)
182 // Exact match
183 return true;
184
185 return false;
186 }
187
disable()188 void Hotspots::Hotspot::disable() {
189 id |= (kStateDisabled << 12);
190 }
191
enable()192 void Hotspots::Hotspot::enable() {
193 id &= ~(kStateDisabled << 12);
194 }
195
196
Hotspots(GobEngine * vm)197 Hotspots::Hotspots(GobEngine *vm) : _vm(vm) {
198 _hotspots = new Hotspot[kHotspotCount];
199
200 _shouldPush = false;
201
202 _currentKey = 0;
203 _currentIndex = 0;
204 _currentId = 0;
205 _currentX = 0;
206 _currentY = 0;
207 }
208
~Hotspots()209 Hotspots::~Hotspots() {
210 delete[] _hotspots;
211
212 // Pop the whole stack and free each element's memory
213 while (!_stack.empty()) {
214
215 StackEntry backup = _stack.pop();
216
217 delete[] backup.hotspots;
218 }
219 }
220
clear()221 void Hotspots::clear() {
222 _currentKey = 0;
223
224 for (int i = 0; i < kHotspotCount; i++)
225 _hotspots[i].clear();
226 }
227
add(uint16 id,uint16 left,uint16 top,uint16 right,uint16 bottom,uint16 flags,uint16 key,uint16 funcEnter,uint16 funcLeave,uint16 funcPos)228 uint16 Hotspots::add(uint16 id,
229 uint16 left, uint16 top, uint16 right, uint16 bottom,
230 uint16 flags, uint16 key,
231 uint16 funcEnter, uint16 funcLeave, uint16 funcPos) {
232
233 Hotspot hotspot(id, left, top, right, bottom,
234 flags, key, funcEnter, funcLeave, funcPos);
235
236 return add(hotspot);
237 }
238
add(const Hotspot & hotspot)239 uint16 Hotspots::add(const Hotspot &hotspot) {
240 for (int i = 0; i < kHotspotCount; i++) {
241 Hotspot &spot = _hotspots[i];
242
243 // free space => add same id => update
244 if (! (spot.isEnd() || (spot.id == hotspot.id)))
245 continue;
246
247 // When updating, keep disabled state intact
248 uint16 id = hotspot.id;
249 if ((spot.id & ~(kStateDisabled << 12)) ==
250 (hotspot.id & ~(kStateDisabled << 12)))
251 id = spot.id;
252
253 // Set
254 spot = hotspot;
255 spot.id = id;
256
257 // Remember the current script
258 spot.script = _vm->_game->_script;
259
260 debugC(1, kDebugHotspots, "Adding hotspot %03d: Coord:%3d+%3d+%3d+%3d - id:%04X, key:%04X, flag:%04X - fcts:%5d, %5d, %5d",
261 i, spot.left, spot.top, spot.right, spot.bottom,
262 spot.id, spot.key, spot.flags, spot.funcEnter, spot.funcLeave, spot.funcPos);
263
264 return i;
265 }
266
267 error("Hotspots::add(): Hotspot array full");
268 return 0xFFFF; // for compilers that don't support NORETURN
269 }
270
remove(uint16 id)271 void Hotspots::remove(uint16 id) {
272 for (int i = 0; i < kHotspotCount; i++) {
273 if (_hotspots[i].id == id) {
274 debugC(1, kDebugHotspots, "Removing hotspot %d: %X", i, id);
275 _hotspots[i].clear();
276 }
277 }
278 }
279
removeState(uint8 state)280 void Hotspots::removeState(uint8 state) {
281 for (int i = 0; i < kHotspotCount; i++) {
282 Hotspot &spot = _hotspots[i];
283
284 if (spot.getState() == state) {
285 debugC(1, kDebugHotspots, "Removing hotspot %d: %X (by state %X)", i, spot.id, state);
286 spot.clear();
287 }
288 }
289 }
290
recalculate(bool force)291 void Hotspots::recalculate(bool force) {
292 debugC(5, kDebugHotspots, "Recalculating hotspots");
293
294 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
295 Hotspot &spot = _hotspots[i];
296
297 if (!force && ((spot.flags & 0x80) != 0))
298 // Not forcing a special hotspot
299 continue;
300
301 if (spot.funcPos == 0)
302 // Simple coordinates don't need update
303 continue;
304
305 // Setting the needed script
306 Script *curScript = _vm->_game->_script;
307
308 _vm->_game->_script = spot.script;
309 if (!_vm->_game->_script)
310 _vm->_game->_script = curScript;
311
312 // Calling the function that contains the positions
313 _vm->_game->_script->call(spot.funcPos);
314
315 // Calculate positions
316 int16 left = _vm->_game->_script->readValExpr();
317 int16 top = _vm->_game->_script->readValExpr();
318 int16 width = _vm->_game->_script->readValExpr();
319 int16 height = _vm->_game->_script->readValExpr();
320
321 // Re-read the flags too, if applicable
322 uint16 flags = 0;
323 if (spot.getState() == (kStateFilled | kStateType2))
324 flags = _vm->_game->_script->readValExpr();
325
326 // Apply backDelta, if needed
327 if ((_vm->_draw->_renderFlags & RENDERFLAG_CAPTUREPOP) && (left != -1)) {
328 left += _vm->_draw->_backDeltaX;
329 top += _vm->_draw->_backDeltaY;
330 }
331
332 // Clamping
333 if (left < 0) {
334 width += left;
335 left = 0;
336 }
337 if (top < 0) {
338 height += top;
339 top = 0;
340 }
341
342 // Set the updated position
343 spot.left = left;
344 spot.top = top;
345 spot.right = left + width - 1;
346 spot.bottom = top + height - 1;
347
348 if (spot.getState() == (kStateFilled | kStateType2))
349 spot.flags = flags;
350
351 // Return
352 _vm->_game->_script->pop();
353
354 _vm->_game->_script = curScript;
355 }
356 }
357
push(uint8 all,bool force)358 void Hotspots::push(uint8 all, bool force) {
359 debugC(1, kDebugHotspots, "Pushing hotspots (%d, %d)", all, force);
360
361 // Should we push at all?
362 if (!_shouldPush && !force)
363 return;
364
365 // Count the hotspots
366 uint32 size = 0;
367 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
368 Hotspot &spot = _hotspots[i];
369
370 // Save all of them
371 if ( (all == 1) ||
372 // Don't save the global ones
373 ((all == 0) && (spot.id >= 20)) ||
374 // Only save disabled ones
375 ((all == 2) && ((spot.getState() == (kStateFilledDisabled | kStateType1)) ||
376 (spot.getState() == (kStateDisabled)) ||
377 (spot.getState() == (kStateFilledDisabled | kStateType2))))) {
378 size++;
379 }
380
381 }
382
383 StackEntry backup;
384
385 backup.shouldPush = _shouldPush;
386 backup.size = size;
387 backup.key = _currentKey;
388 backup.id = _currentId;
389 backup.index = _currentIndex;
390 backup.x = _currentX;
391 backup.y = _currentY;
392
393 backup.hotspots = new Hotspot[size];
394
395 // Copy the hotspots
396 Hotspot *destPtr = backup.hotspots;
397 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
398 Hotspot &spot = _hotspots[i];
399
400 // Save all of them
401 if ( (all == 1) ||
402 // Don't save the global ones
403 ((all == 0) && (spot.id >= 20)) ||
404 // Only save disabled ones
405 ((all == 2) && ((spot.getState() == (kStateFilledDisabled | kStateType1)) ||
406 (spot.getState() == (kStateDisabled)) ||
407 (spot.getState() == (kStateFilledDisabled | kStateType2))))) {
408
409 memcpy(destPtr, &spot, sizeof(Hotspot));
410 destPtr++;
411
412 spot.clear();
413 }
414
415 }
416
417 // Reset current state
418 _shouldPush = false;
419 _currentKey = 0;
420 _currentId = 0;
421 _currentIndex = 0;
422 _currentX = 0;
423 _currentY = 0;
424
425 _stack.push(backup);
426 }
427
pop()428 void Hotspots::pop() {
429 debugC(1, kDebugHotspots, "Popping hotspots");
430
431 assert(!_stack.empty());
432
433 StackEntry backup = _stack.pop();
434
435 // Find the end of the filled hotspot space
436 int i;
437 Hotspot *destPtr = _hotspots;
438 for (i = 0; i < kHotspotCount; i++, destPtr++) {
439 if (destPtr->isEnd())
440 break;
441 }
442
443 if (((uint32) (kHotspotCount - i)) < backup.size)
444 error("Hotspots::pop(): Not enough free space in the current Hotspot "
445 "array to pop %d elements (got %d)", backup.size, kHotspotCount - i);
446
447 // Copy
448 memcpy(destPtr, backup.hotspots, backup.size * sizeof(Hotspot));
449
450 _shouldPush = backup.shouldPush;
451 _currentKey = backup.key;
452 _currentId = backup.id;
453 _currentIndex = backup.index;
454 _currentX = backup.x;
455 _currentY = backup.y;
456
457 delete[] backup.hotspots;
458 }
459
isValid(uint16 key,uint16 id,uint16 index) const460 bool Hotspots::isValid(uint16 key, uint16 id, uint16 index) const {
461 if (index >= kHotspotCount)
462 return false;
463
464 if (key == 0)
465 return false;
466
467 if (!(Hotspot::getState(id) & kStateFilled))
468 return false;
469
470 return true;
471 }
472
call(uint16 offset)473 void Hotspots::call(uint16 offset) {
474 debugC(4, kDebugHotspots, "Calling hotspot function %d", offset);
475
476 _vm->_game->_script->call(offset);
477
478 _shouldPush = true;
479
480 Common::Stack<StackEntry>::size_type stackSize = _stack.size();
481
482 _vm->_inter->funcBlock(0);
483
484 while (stackSize != _stack.size())
485 pop();
486
487 _shouldPush = false;
488
489 _vm->_game->_script->pop();
490
491 recalculate(false);
492 }
493
enter(uint16 index)494 void Hotspots::enter(uint16 index) {
495 debugC(2, kDebugHotspots, "Entering hotspot %d", index);
496
497 if (index >= kHotspotCount) {
498 warning("Hotspots::enter(): Index %d out of range", index);
499 return;
500 }
501
502 Hotspot &spot = _hotspots[index];
503
504 // If requested, write the ID into a variable
505 if ((spot.getState() == (kStateFilled | kStateType1)) ||
506 (spot.getState() == (kStateFilled | kStateType2)))
507 WRITE_VAR(17, -(spot.id & 0x0FFF));
508
509 _currentX = _vm->_global->_inter_mouseX;
510 _currentY = _vm->_global->_inter_mouseY;
511
512 if (spot.funcEnter != 0)
513 call(spot.funcEnter);
514 }
515
leave(uint16 index)516 void Hotspots::leave(uint16 index) {
517 debugC(2, kDebugHotspots, "Leaving hotspot %d", index);
518
519 if (index >= kHotspotCount) {
520 warning("Hotspots::leave(): Index %d out of range", index);
521 return;
522 }
523
524 Hotspot &spot = _hotspots[index];
525
526 // If requested, write the ID into a variable
527 if ((spot.getState() == (kStateFilled | kStateType1)) ||
528 (spot.getState() == (kStateFilled | kStateType2)))
529 WRITE_VAR(17, spot.id & 0x0FFF);
530
531 if (spot.funcLeave != 0)
532 call(spot.funcLeave);
533 }
534
windowCursor(int16 & dx,int16 & dy) const535 int16 Hotspots::windowCursor(int16 &dx, int16 &dy) const {
536 if (!(_vm->_draw->_renderFlags & RENDERFLAG_HASWINDOWS))
537 return 0;
538
539 for (int i = 0; i < 10; i++) {
540 if (_vm->_draw->_fascinWin[i].id == -1)
541 // No such windows
542 continue;
543
544 const int left = _vm->_draw->_fascinWin[i].left;
545 const int top = _vm->_draw->_fascinWin[i].top;
546 const int right = _vm->_draw->_fascinWin[i].left + _vm->_draw->_fascinWin[i].width - 1;
547 const int bottom = _vm->_draw->_fascinWin[i].top + _vm->_draw->_fascinWin[i].height - 1;
548
549 if ((_vm->_global->_inter_mouseX < left) || (_vm->_global->_inter_mouseX > right) ||
550 (_vm->_global->_inter_mouseY < top ) || (_vm->_global->_inter_mouseY > bottom))
551 // We're not inside that window
552 continue;
553
554 if (_vm->_draw->_fascinWin[i].id != (_vm->_draw->_winCount - 1))
555 // Only consider the top-most window
556 continue;
557
558 dx = _vm->_draw->_fascinWin[i].left;
559 dy = _vm->_draw->_fascinWin[i].top;
560
561 if ((_vm->_global->_inter_mouseX < (left + 12)) && (_vm->_global->_inter_mouseY < (top + 12)) &&
562 (VAR((_vm->_draw->_winVarArrayStatus / 4) + i) & 2))
563 // Cursor on 'Close Window'
564 return 5;
565
566 if ((_vm->_global->_inter_mouseX > (right - 12)) & (_vm->_global->_inter_mouseY < (top + 12)) &&
567 (VAR((_vm->_draw->_winVarArrayStatus / 4) + i) & 4))
568 // Cursor on 'Move Window'
569 return 6;
570
571 return -1;
572 }
573
574 return 0;
575 }
576
checkMouse(Type type,uint16 & id,uint16 & index) const577 uint16 Hotspots::checkMouse(Type type, uint16 &id, uint16 &index) const {
578 id = 0;
579 index = 0;
580
581 int16 dx = 0;
582 int16 dy = 0;
583 int16 winId = _vm->_draw->getWinFromCoord(dx, dy);
584
585 if (winId < 0) {
586 winId = 0;
587 dx = 0;
588 dy = 0;
589 } else
590 winId *= 256;
591
592 if (type == kTypeMove) {
593 // Check where the mouse was moved to
594
595 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
596 const Hotspot &spot = _hotspots[i];
597
598 if (spot.isDisabled())
599 // Only consider enabled hotspots
600 continue;
601
602 if (spot.getType() > kTypeMove)
603 // Only consider click and move hotspots
604 continue;
605
606 if (spot.getWindow() != winId)
607 // Only check the current window
608 continue;
609
610 if (!spot.isIn(_vm->_global->_inter_mouseX - dx, _vm->_global->_inter_mouseY - dy))
611 // If we're not in it, ignore it
612 continue;
613
614 id = spot.id;
615 index = i;
616
617 return spot.key;
618 }
619
620 return 0;
621
622 } else if (type == kTypeClick) {
623 // Check if something was clicked
624
625 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
626 const Hotspot &spot = _hotspots[i];
627
628 if (spot.isDisabled())
629 // Only consider enabled hotspots
630 continue;
631
632 if (spot.getWindow() != winId)
633 // Only check the active window
634 continue;
635
636 if (spot.getType() < kTypeMove)
637 // Only consider hotspots that can be clicked
638 continue;
639
640 if (!spot.isIn(_vm->_global->_inter_mouseX - dx, _vm->_global->_inter_mouseY - dy))
641 // If we're not in it, ignore it
642 continue;
643
644 if (!spot.buttonMatch(_vm->_game->_mouseButtons))
645 // Don't follow hotspots with button requirements we don't meet
646 continue;
647
648 id = spot.id;
649 index = i;
650
651 if ((spot.getType() == kTypeMove) || (spot.getType() == kTypeClick))
652 // It's a move or click => return the key
653 return spot.key;
654
655 // Otherwise, the key has a different meaning, so ignore it
656 return 0;
657 }
658
659 if (_vm->_game->_mouseButtons != kMouseButtonsLeft)
660 // Let the right mouse button act as an escape key
661 return kKeyEscape;
662
663 return 0;
664 }
665
666 return 0;
667 }
668
checkHotspotChanged()669 bool Hotspots::checkHotspotChanged() {
670 uint16 key, id, index;
671
672 // Get the current hotspot
673 key = checkMouse(kTypeMove, id, index);
674
675 uint16 mouseX = _vm->_global->_inter_mouseX;
676 uint16 mouseY = _vm->_global->_inter_mouseY;
677
678 if (key == _currentKey) {
679 // Still the same hotspot, just update the mouse position
680
681 _currentX = mouseX;
682 _currentY = mouseY;
683 return false;
684 }
685
686 // In Geisha, no move hotspot changes should occur when
687 // we didn't actually move the mouse
688 if (_vm->getGameType() == kGameTypeGeisha)
689 if ((mouseX == _currentX) && (mouseY == _currentY))
690 return false;
691
692 // Leave the old area
693 if (isValid(_currentKey, _currentId,_currentIndex))
694 leave(_currentIndex);
695
696 _currentKey = key;
697 _currentId = id;
698 _currentIndex = index;
699 _currentX = mouseX;
700 _currentY = mouseY;
701
702 // Enter the new one
703 if (isValid(key, id, index))
704 enter(index);
705
706 return true;
707 }
708
check(uint8 handleMouse,int16 delay,uint16 & id,uint16 & index)709 uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index) {
710 if (delay >= -1) {
711 _currentKey = 0;
712 _currentId = 0;
713 _currentIndex = 0;
714 }
715
716 id = 0;
717 index = 0;
718
719 if (handleMouse) {
720 if ((_vm->_draw->_cursorIndex == -1) && (_currentKey == 0)) {
721 // Last know state: No hotspot hit. Look if that changed
722
723 _currentKey = checkMouse(kTypeMove, _currentId, _currentIndex);
724
725 if (isValid(_currentKey, _currentId, _currentIndex))
726 enter(_currentIndex);
727 }
728 _vm->_draw->animateCursor(-1);
729 }
730
731 uint32 startTime = _vm->_util->getTimeKey();
732
733 // Update display
734 _vm->_draw->blitInvalidated();
735 _vm->_video->waitRetrace();
736
737 uint16 key = 0;
738 while (key == 0) {
739
740 if (_vm->_inter->_terminate || _vm->shouldQuit()) {
741 if (handleMouse)
742 _vm->_draw->blitCursor();
743 return 0;
744 }
745
746 // Anything changed?
747 checkHotspotChanged();
748
749 // Update display
750 if (!_vm->_draw->_noInvalidated) {
751 if (handleMouse)
752 _vm->_draw->animateCursor(-1);
753 else
754 _vm->_draw->blitInvalidated();
755 _vm->_video->waitRetrace();
756 }
757
758 if (handleMouse)
759 _vm->_game->evaluateScroll();
760
761 // Update keyboard and mouse state
762 key = _vm->_game->checkKeys(&_vm->_global->_inter_mouseX,
763 &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, handleMouse);
764
765 if (!handleMouse && (_vm->_game->_mouseButtons != kMouseButtonsNone)) {
766 // We don't want any mouse input but got one => Wait till it went away
767
768 _vm->_util->waitMouseRelease(0);
769 key = 3;
770 }
771
772 if (key != 0) {
773 // Got a key press
774
775 if (handleMouse & 1)
776 _vm->_draw->blitCursor();
777
778 id = 0;
779 index = 0;
780
781 // Leave the current hotspot
782 if (isValid(_currentKey, _currentId, _currentIndex))
783 leave(_currentIndex);
784
785 _currentKey = 0;
786 break;
787 }
788
789 if (handleMouse) {
790
791 if (_vm->_game->_mouseButtons != kMouseButtonsNone) {
792 // Mouse button pressed
793 int i = _vm->_draw->handleCurWin();
794
795 if (!i) {
796 _vm->_draw->animateCursor(2);
797 if (delay > 0) {
798 // If a delay was requested, wait the specified time
799 _vm->_util->delay(delay);
800 } else if (handleMouse & 1)
801 _vm->_util->waitMouseRelease(1);
802
803 _vm->_draw->animateCursor(-1);
804
805 // Which region was clicked?
806 key = checkMouse(kTypeClick, id, index);
807
808 if ((key != 0) || (id != 0)) {
809 // Got a valid region
810
811 if ( (handleMouse & 1) &&
812 ((delay <= 0) || (_vm->_game->_mouseButtons == kMouseButtonsNone)))
813 _vm->_draw->blitCursor();
814
815
816 if ((key != _currentKey) && (_vm->getGameType() != kGameTypeFascination) &&
817 (_vm->getGameType() != kGameTypeGeisha))
818 // If the hotspot changed, leave the old one
819 // Code not present in Fascination executables
820 leave(_currentIndex);
821
822 _currentKey = 0;
823 break;
824 }
825
826 if (handleMouse & 4)
827 // Nothing further than one simple check was requested => return
828 return 0;
829
830 // Leave the current area
831 if (_currentKey != 0)
832 leave(_currentIndex);
833
834 // No click, but do we have a move event? If so, enter that hotspot
835 _currentKey = checkMouse(kTypeMove, _currentId, _currentIndex);
836 if (isValid(_currentKey, _currentId, _currentIndex))
837 enter(_currentIndex);
838 } else {
839 WRITE_VAR(16, (int32)i);
840 id = 0;
841 index = 0;
842 return 0;
843 }
844 } else
845 // No mouse button pressed, check whether the position changed at least
846 checkHotspotChanged();
847 }
848
849 if ((delay == -2) && (key == 0) &&
850 (_vm->_game->_mouseButtons == kMouseButtonsNone)) {
851 // Nothing found and no further handling requested. Return.
852
853 id = 0;
854 index = 0;
855 break;
856 }
857
858 if (handleMouse)
859 _vm->_draw->animateCursor(-1);
860
861 if ((delay < 0) && (key == 0) &&
862 (_vm->_game->_mouseButtons == kMouseButtonsNone)) {
863
864 // Look if we've maybe reached the timeout
865
866 uint32 curTime = _vm->_util->getTimeKey();
867 if ((curTime + delay) > startTime) {
868 // If so, return
869
870 id = 0;
871 index = 0;
872 break;
873 }
874
875 }
876
877 // Sleep for a short amount of time
878 _vm->_util->delay(10);
879
880 }
881
882 return key;
883 }
884
check(uint8 handleMouse,int16 delay)885 uint16 Hotspots::check(uint8 handleMouse, int16 delay) {
886 uint16 id, index;
887
888 // Check and ignore the id and index
889 return Hotspots::check(handleMouse, delay, id, index);
890 }
891
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)892 uint16 Hotspots::updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
893 uint16 backColor, uint16 frontColor, char *str, uint16 fontIndex,
894 Type type, int16 &duration, uint16 &id, uint16 &index) {
895
896 if ((fontIndex >= Draw::kFontCount) || !_vm->_draw->_fonts[fontIndex]) {
897 warning("Hotspots::updateInput(): Invalid font specified: %d", fontIndex);
898 return 0;
899 }
900
901 // Check if we need to consider mouse events
902 bool handleMouse = false;
903 if ( (_vm->_game->_handleMouse != 0) &&
904 ((_vm->_global->_useMouse != 0) || (_vm->_game->_forceHandleMouse != 0)))
905 handleMouse = true;
906
907 const Font &font = *_vm->_draw->_fonts[fontIndex];
908
909 // Current position in the string, preset to the end
910 uint32 pos = strlen(str);
911 /* Size of input field in characters.
912 * If the font is not monospaced, we can't know that */
913 uint32 editSize = font.isMonospaced() ? (width / font.getCharWidth()) : 0;
914
915 uint16 key = 0;
916 char tempStr[256];
917
918 while (1) {
919 // If we the edit field has enough space, add a space for the new character
920 Common::strlcpy(tempStr, str, 255);
921 strcat(tempStr, " ");
922 if ((editSize != 0) && strlen(tempStr) > editSize)
923 Common::strlcpy(tempStr, str, 256);
924
925 // Clear input area
926 fillRect(xPos, yPos,
927 font.isMonospaced() ? (editSize * font.getCharWidth()) : width, height,
928 backColor);
929
930 // Print the current string, vertically centered
931 printText(xPos, yPos + (height - font.getCharHeight()) / 2,
932 tempStr, fontIndex, frontColor);
933
934 // If we've reached the end of the input field, set the cursor to the last character
935 if ((editSize != 0) && (pos == editSize))
936 pos--;
937
938 // The character under the cursor
939 char curSym = tempStr[pos];
940
941 if (_vm->_inter->_variables)
942 WRITE_VAR(56, pos);
943
944 bool first = true;
945 while (1) {
946 tempStr[0] = curSym;
947 tempStr[1] = 0;
948
949 // Draw cursor
950 uint16 cursorX, cursorY, cursorWidth, cursorHeight;
951 getTextCursorPos(font, str, pos, xPos, yPos, width, height,
952 cursorX, cursorY, cursorWidth, cursorHeight);
953 fillRect(cursorX, cursorY, cursorWidth, cursorHeight, frontColor);
954
955 if (first) {
956 // The first time, purge old information too
957 key = check(handleMouse, -1, id, index);
958
959 if (key == 0)
960 // We didn't catch any input, let's try again with a real timeout
961 key = check(handleMouse, -300, id, index);
962
963 first = false;
964 } else
965 // Try to catch a character
966 key = check(handleMouse, -300, id, index);
967
968 tempStr[0] = curSym;
969 tempStr[1] = 0;
970
971 // Clear cursor
972 getTextCursorPos(font, str, pos, xPos, yPos, width, height,
973 cursorX, cursorY, cursorWidth, cursorHeight);
974 fillRect(cursorX, cursorY, cursorWidth, cursorHeight, backColor);
975
976 // Print the current string, vertically centered
977 printText(cursorX, yPos + (height - font.getCharHeight()) / 2,
978 tempStr, fontIndex, frontColor);
979
980 if ((key != 0) || (id != 0))
981 // We did get a key, stop looking
982 break;
983
984 // Try again
985 key = check(handleMouse, -300, id, index);
986
987 if ((key != 0) || (id != 0) ||
988 _vm->_inter->_terminate || _vm->shouldQuit())
989 // We did get a key, stop looking
990 break;
991
992 if (duration > 0) {
993 // Look if we reached the time limit
994 duration -= 600;
995 if (duration <= 1) {
996 // If so, abort
997 key = 0;
998 id = 0;
999 break;
1000 }
1001 }
1002 }
1003
1004 if ((key == 0) || (id != 0) ||
1005 _vm->_inter->_terminate || _vm->shouldQuit())
1006 // Got no key, or a region ID instead, return
1007 return 0;
1008
1009 switch (key) {
1010 case kKeyRight:
1011 // If possible, move the cursor right
1012 if (((editSize != 0) && ((pos > strlen(str)) || (pos > (editSize - 1)))) ||
1013 ((editSize == 0) && (pos > strlen(str)))) {
1014 pos++;
1015 continue;
1016 }
1017 // Continue downwards instead
1018 return kKeyDown;
1019
1020 case kKeyLeft:
1021 // If possible, move the cursor left
1022 if (pos > 0) {
1023 pos--;
1024 continue;
1025 }
1026 // Continue upwards instead
1027 return kKeyUp;
1028
1029 case kKeyBackspace:
1030 if (pos > 0) {
1031 // Delete the character to the left
1032 _vm->_util->cutFromStr(str, pos - 1, 1);
1033 pos--;
1034 } else {
1035 if (pos < strlen(str))
1036 // Delete the character to the right
1037 _vm->_util->cutFromStr(str, pos, 1);
1038 }
1039 continue;
1040
1041 case kKeyDelete:
1042 if (pos >= strlen(str))
1043 continue;
1044
1045 // Delete the character to the right
1046 _vm->_util->cutFromStr(str, pos, 1);
1047 continue;
1048
1049 case kKeyReturn:
1050 case kKeyF1:
1051 case kKeyF2:
1052 case kKeyF3:
1053 case kKeyF4:
1054 case kKeyF5:
1055 case kKeyF6:
1056 case kKeyF7:
1057 case kKeyF8:
1058 case kKeyF9:
1059 case kKeyF10:
1060 case kKeyUp:
1061 case kKeyDown:
1062 return key;
1063
1064 case kKeyEscape:
1065 // If we got an escape event, wait until the mouse buttons have been released
1066 if (_vm->_global->_useMouse != 0)
1067 continue;
1068
1069 _vm->_game->_forceHandleMouse = !_vm->_game->_forceHandleMouse;
1070
1071 handleMouse = false;
1072 if ( (_vm->_game->_handleMouse != 0) &&
1073 ((_vm->_global->_useMouse != 0) || (_vm->_game->_forceHandleMouse != 0)))
1074 handleMouse = true;
1075
1076 while (_vm->_global->_pressedKeys[1] != 0)
1077 ;
1078 continue;
1079
1080 default:
1081 // Got a "normal" key
1082
1083 uint16 savedKey = key;
1084
1085 key &= 0xFF;
1086
1087 if (((type == kTypeInputFloatNoLeave) || (type == kTypeInputFloatLeave)) &&
1088 (key >= ' ') && (key <= 0xFF)) {
1089
1090 // Only allow character found in numerical floating values
1091
1092 const char *str1 = "0123456789-.,+ ";
1093 const char *str2 = "0123456789-,,+ ";
1094
1095 if ((((savedKey >> 8) > 1) && ((savedKey >> 8) < 12)) &&
1096 ((_vm->_global->_pressedKeys[42] != 0) ||
1097 (_vm->_global->_pressedKeys[56] != 0)))
1098 key = ((savedKey >> 8) - 1) % 10 + '0';
1099
1100 int i;
1101 for (i = 0; str1[i] != 0; i++) {
1102 if (key == str1[i]) {
1103 key = str2[i];
1104 break;
1105 }
1106 }
1107
1108 if (i == (int16) strlen(str1))
1109 key = 0;
1110 }
1111
1112 if ((key >= ' ') && (key <= 0xFF)) {
1113 if (editSize == 0) {
1114 // Length of the string + current character + next one
1115 int length = _vm->_draw->stringLength(str, fontIndex) +
1116 font.getCharWidth(' ') + font.getCharWidth(key);
1117
1118 if (length > width)
1119 // We're above the limit, ignore the key
1120 continue;
1121
1122 if (((int32) strlen(str)) >= (_vm->_global->_inter_animDataSize * 4 - 1))
1123 // Above the limit of character allowed in a string, ignore the key
1124 continue;
1125
1126 } else {
1127 if (strlen(str) > editSize)
1128 // We're over the upper character limit for this field
1129 continue;
1130 else if (editSize == strlen(str))
1131 // We've reached the upper limit, overwrite the last character
1132 _vm->_util->cutFromStr(str, strlen(str) - 1, 1);
1133 }
1134
1135 // Advance cursor
1136 pos++;
1137 tempStr[0] = key;
1138 tempStr[1] = 0;
1139
1140 // Add character
1141 _vm->_util->insertStr(tempStr, str, pos - 1);
1142 }
1143 }
1144 }
1145 }
1146
handleInputs(int16 time,uint16 inputCount,uint16 & curInput,InputDesc * inputs,uint16 & id,uint16 & index)1147 uint16 Hotspots::handleInputs(int16 time, uint16 inputCount, uint16 &curInput,
1148 InputDesc *inputs, uint16 &id, uint16 &index) {
1149
1150 // Redraw all texts in all inputs we currently manage
1151 updateAllTexts(inputs);
1152
1153 for (int i = 0; i < 40; i++)
1154 WRITE_VAR(17 + i, 0);
1155
1156 while (1) {
1157 // Find the hotspot index to our current input
1158 uint16 hotspotIndex = inputToHotspot(curInput);
1159
1160 assert(hotspotIndex != 0xFFFF);
1161
1162 Hotspot inputSpot = _hotspots[hotspotIndex];
1163
1164 // Handle input events from that input field
1165 uint16 key = updateInput(inputSpot.left, inputSpot.top,
1166 inputSpot.right - inputSpot.left + 1,
1167 inputSpot.bottom - inputSpot.top + 1,
1168 inputs[curInput].backColor, inputs[curInput].frontColor,
1169 GET_VARO_STR(inputSpot.key), inputs[curInput].fontIndex,
1170 inputSpot.getType(), time, id, index);
1171
1172 if (_vm->_inter->_terminate)
1173 return 0;
1174
1175 switch (key) {
1176 case kKeyNone:
1177 if (id == 0)
1178 // No key and no hotspot => return
1179 return 0;
1180
1181 if (_vm->_game->_mouseButtons != kMouseButtonsNone)
1182 // Clicked something, get the hotspot index
1183 index = findClickedInput(index);
1184
1185 if (!_hotspots[index].isInput())
1186 // It's no input, return
1187 return 0;
1188
1189 // Get the associated input index
1190 curInput = hotspotToInput(index);
1191 break;
1192
1193 case kKeyF1:
1194 case kKeyF2:
1195 case kKeyF3:
1196 case kKeyF4:
1197 case kKeyF5:
1198 case kKeyF6:
1199 case kKeyF7:
1200 case kKeyF8:
1201 case kKeyF9:
1202 case kKeyF10:
1203 return key;
1204
1205 case kKeyReturn:
1206 // Just one input => return
1207 if (inputCount == 1)
1208 return kKeyReturn;
1209
1210 // End of input chain reached => wrap
1211 if (curInput == (inputCount - 1)) {
1212 curInput = 0;
1213 break;
1214 }
1215
1216 // Next input
1217 curInput++;
1218 break;
1219
1220 case kKeyDown:
1221 // Next input
1222 if ((inputCount - 1) > curInput)
1223 curInput++;
1224 break;
1225
1226 case kKeyUp:
1227 // Previous input
1228 if (curInput > 0)
1229 curInput--;
1230 break;
1231 }
1232 }
1233 }
1234
evaluateNew(uint16 i,uint16 * ids,InputDesc * inputs,uint16 & inputId,bool & hasInput,uint16 & inputCount)1235 void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
1236 uint16 &inputId, bool &hasInput, uint16 &inputCount) {
1237
1238 ids[i] = 0;
1239
1240 // Type and window
1241 byte type = _vm->_game->_script->readByte();
1242 byte windowNum = 0;
1243
1244 if ((type & 0x40) != 0) {
1245 // Got a window ID
1246
1247 type -= 0x40;
1248 windowNum = _vm->_game->_script->readByte();
1249 }
1250
1251 // Coordinates
1252 uint16 left, top, width, height, right, bottom;
1253 uint32 funcPos = 0;
1254 if ((type & 0x80) != 0) {
1255 // Complex coordinate expressions
1256 funcPos = _vm->_game->_script->pos();
1257 left = _vm->_game->_script->readValExpr();
1258 top = _vm->_game->_script->readValExpr();
1259 width = _vm->_game->_script->readValExpr();
1260 height = _vm->_game->_script->readValExpr();
1261 } else {
1262 // Immediate values
1263 funcPos = 0;
1264 left = _vm->_game->_script->readUint16();
1265 top = _vm->_game->_script->readUint16();
1266 width = _vm->_game->_script->readUint16();
1267 height = _vm->_game->_script->readUint16();
1268 }
1269 type &= 0x7F;
1270
1271 // Draw a border around the hotspot
1272 if (_vm->_draw->_renderFlags & RENDERFLAG_BORDERHOTSPOTS) {
1273 Surface &surface = *_vm->_draw->_spritesArray[_vm->_draw->_destSurface];
1274
1275 _vm->_video->dirtyRectsAll();
1276
1277 if (windowNum == 0) {
1278 // The hotspot is not inside a window, just draw border it
1279 surface.drawRect(left, top, left + width - 1, top + height - 1, 0);
1280
1281 } else {
1282 // The hotspot is inside a window, only draw it if it's the topmost window
1283
1284 if ((_vm->_draw->_fascinWin[windowNum].id != -1) &&
1285 (_vm->_draw->_fascinWin[windowNum].id == (_vm->_draw->_winCount - 1))) {
1286
1287 const uint16 wLeft = left + _vm->_draw->_fascinWin[windowNum].left;
1288 const uint16 wTop = top + _vm->_draw->_fascinWin[windowNum].top;
1289
1290 surface.drawRect(wLeft, wTop, wLeft + width - 1, wTop + height - 1, 0);
1291 }
1292 }
1293 }
1294
1295 // Apply global drawing offset
1296 if ((_vm->_draw->_renderFlags & RENDERFLAG_CAPTUREPOP) && (left != 0xFFFF)) {
1297 left += _vm->_draw->_backDeltaX;
1298 top += _vm->_draw->_backDeltaY;
1299 }
1300
1301 right = left + width - 1;
1302 bottom = top + height - 1;
1303
1304 // Enabling the hotspots again
1305 if ((type == kTypeEnable2) || (type == kTypeEnable1)) {
1306 uint8 wantedState = 0;
1307 if (type == kTypeEnable2)
1308 wantedState = kStateFilledDisabled | kStateType2;
1309 else
1310 wantedState = kStateFilledDisabled | kStateType1;
1311
1312 _vm->_game->_script->skip(6);
1313
1314 for (int j = 0; j < kHotspotCount; j++) {
1315 Hotspot &spot = _hotspots[j];
1316
1317 if (spot.getState() == wantedState) {
1318 spot.enable();
1319 spot.funcEnter = _vm->_game->_script->pos();
1320 spot.funcLeave = _vm->_game->_script->pos();
1321 }
1322 }
1323
1324 _vm->_game->_script->skipBlock();
1325
1326 return;
1327 }
1328
1329 int16 key = 0;
1330 int16 flags = 0;
1331 Font *font = 0;
1332 uint32 funcEnter = 0, funcLeave = 0;
1333
1334 if ((windowNum != 0) && (type != 0) && (type != 2))
1335 debugC(0, kDebugHotspots, "evaluateNew - type %d, win %d",type, windowNum);
1336
1337 // Evaluate parameters for the new hotspot
1338 switch (type) {
1339 case kTypeNone:
1340 _vm->_game->_script->skip(6);
1341
1342 funcEnter = _vm->_game->_script->pos();
1343 _vm->_game->_script->skipBlock();
1344
1345 funcLeave = _vm->_game->_script->pos();
1346 _vm->_game->_script->skipBlock();
1347
1348 key = i + ((kStateFilled | kStateType2) << 12);
1349 flags = type + (windowNum << 8);
1350 break;
1351
1352 case kTypeMove:
1353 key = _vm->_game->_script->readInt16();
1354 ids[i] = _vm->_game->_script->readInt16();
1355 flags = _vm->_game->_script->readInt16();
1356
1357 funcEnter = _vm->_game->_script->pos();
1358 _vm->_game->_script->skipBlock();
1359
1360 funcLeave = _vm->_game->_script->pos();
1361 _vm->_game->_script->skipBlock();
1362
1363 if (key == 0)
1364 key = i + ((kStateFilled | kStateType2) << 12);
1365
1366 flags = type + (windowNum << 8) + (flags << 4);
1367 break;
1368
1369 case kTypeInput1NoLeave:
1370 case kTypeInput1Leave:
1371 case kTypeInput2NoLeave:
1372 case kTypeInput2Leave:
1373 case kTypeInput3NoLeave:
1374 case kTypeInput3Leave:
1375 case kTypeInputFloatNoLeave:
1376 case kTypeInputFloatLeave:
1377 hasInput = true;
1378
1379 _vm->_util->clearKeyBuf();
1380
1381 // Input text parameters
1382 key = _vm->_game->_script->readVarIndex();
1383 inputs[inputCount].fontIndex = _vm->_game->_script->readInt16();
1384 inputs[inputCount].backColor = _vm->_game->_script->readByte();
1385 inputs[inputCount].frontColor = _vm->_game->_script->readByte();
1386 inputs[inputCount].length = 0;
1387 inputs[inputCount].str = 0;
1388
1389 if ((type >= kTypeInput2NoLeave) && (type <= kTypeInput3Leave)) {
1390 inputs[inputCount].length = _vm->_game->_script->readUint16();
1391
1392 inputs[inputCount].str =
1393 (const char *)(_vm->_game->_script->getData() + _vm->_game->_script->pos());
1394
1395 _vm->_game->_script->skip(inputs[inputCount].length);
1396 }
1397
1398 if (left == 0xFFFF) {
1399 if (!(type & 1))
1400 // No coordinates but a leave block => skip it
1401 _vm->_game->_script->skipBlock();
1402 break;
1403 }
1404
1405 font = _vm->_draw->_fonts[inputs[inputCount].fontIndex];
1406 if (font->isMonospaced())
1407 right = left + width * font->getCharWidth() - 1;
1408
1409 funcEnter = 0;
1410 funcPos = 0;
1411 funcLeave = 0;
1412 if (!(type & 1)) {
1413 // Got a leave
1414 funcLeave = _vm->_game->_script->pos();
1415 _vm->_game->_script->skipBlock();
1416 }
1417
1418 flags = type;
1419
1420 inputCount++;
1421 break;
1422
1423 case 20:
1424 inputId = i;
1425 // fall through
1426 case kTypeClick:
1427 key = _vm->_game->_script->readInt16();
1428 ids[i] = _vm->_game->_script->readInt16();
1429 flags = _vm->_game->_script->readInt16();
1430
1431 if (flags > 3)
1432 warning("evaluateNew: Warning, use of type 2 or 20. flags = %d, should be %d", flags, flags&3);
1433
1434 funcEnter = 0;
1435
1436 funcLeave = _vm->_game->_script->pos();
1437 _vm->_game->_script->skipBlock();
1438
1439 flags = ((uint16) kTypeClick) + (windowNum << 8) + (flags << 4);
1440 break;
1441
1442 case kTypeClickEnter:
1443 key = _vm->_game->_script->readInt16();
1444 ids[i] = _vm->_game->_script->readInt16();
1445 flags = _vm->_game->_script->readInt16() & 3;
1446
1447 funcEnter = _vm->_game->_script->pos();
1448 _vm->_game->_script->skipBlock();
1449
1450 funcLeave = 0;
1451
1452 flags = ((uint16) kTypeClick) + (windowNum << 8) + (flags << 4);
1453 break;
1454 }
1455
1456 // Add the new hotspot
1457 add(i | (kStateFilled << 12), left, top, right, bottom,
1458 flags, key, funcEnter, funcLeave, funcPos);
1459 }
1460
evaluateFind(uint16 key,int16 timeVal,const uint16 * ids,uint16 leaveWindowIndex,uint16 hotspotIndex1,uint16 hotspotIndex2,uint16 endIndex,int16 & duration,uint16 & id,uint16 & index,bool & finished)1461 bool Hotspots::evaluateFind(uint16 key, int16 timeVal, const uint16 *ids,
1462 uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
1463 uint16 endIndex, int16 &duration, uint16 &id, uint16 &index, bool &finished) {
1464
1465 bool fascinCheck = false;
1466
1467 if (id != 0)
1468 // We already found a hotspot, nothing to do
1469 return true;
1470
1471 if (key != 0) {
1472 // We've got a key
1473
1474 // Find the hotspot with that key associated
1475 findKey(key, id, index);
1476 if (id != 0)
1477 // Found it
1478 return true;
1479
1480 // Try it case insensitively
1481 findKeyCaseInsensitive(key, id, index);
1482 if (id != 0)
1483 // Found it
1484 return true;
1485
1486 return false;
1487 }
1488 if ((_vm->getGameType() == kGameTypeFascination) && (getCurrentHotspot()))
1489 fascinCheck = true;
1490
1491 if ((duration != 0) && (!fascinCheck)) {
1492 // We've got a time duration
1493
1494 if (hotspotIndex1 != 0) {
1495 finished =
1496 leaveNthPlain(hotspotIndex1, endIndex, timeVal, ids, id, index, duration);
1497 } else if (hotspotIndex2 != 0) {
1498 findNthPlain(hotspotIndex2, endIndex, id, index);
1499 } else {
1500 // Enter the first hotspot
1501 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1502 Hotspot &spot = _hotspots[i];
1503 if (spot.isFilledNew()) {
1504 id = spot.id;
1505 index = i;
1506 break;
1507 }
1508 }
1509
1510 // Leave the current hotspot
1511 if ((_currentKey != 0) && (_hotspots[_currentIndex].funcLeave != 0))
1512 call(_hotspots[_currentIndex].funcLeave);
1513
1514 _currentKey = 0;
1515 }
1516
1517 if (id != 0)
1518 return true;
1519
1520 return false;
1521 } else {
1522 if (leaveWindowIndex != 0)
1523 findNthPlain(leaveWindowIndex, endIndex, id, index);
1524
1525 if (id != 0)
1526 return true;
1527 }
1528
1529 return false;
1530 }
1531
evaluate()1532 void Hotspots::evaluate() {
1533 InputDesc inputs[20];
1534 uint16 ids[kHotspotCount];
1535
1536 // Push all local hotspots
1537 push(0);
1538
1539 // Find the current end of the hotspot block
1540 uint16 endIndex = 0;
1541 while (!_hotspots[endIndex].isEnd())
1542 endIndex++;
1543
1544 _shouldPush = false;
1545
1546 _vm->_game->_script->skip(1);
1547
1548 // Number of new hotspots
1549 byte count = _vm->_game->_script->readByte();
1550
1551 // Parameters of this block
1552 _vm->_game->_handleMouse = _vm->_game->_script->peekByte(0);
1553 int16 duration = _vm->_game->_script->peekByte(1);
1554
1555 byte leaveWindowIndex = 0;
1556 if (_vm->getGameType() == kGameTypeFascination)
1557 leaveWindowIndex = _vm->_game->_script->peekByte(2);
1558
1559 byte hotspotIndex1 = _vm->_game->_script->peekByte(3);
1560 byte hotspotIndex2 = _vm->_game->_script->peekByte(4);
1561 bool needRecalculation = _vm->_game->_script->peekByte(5) != 0;
1562
1563 // Seconds -> Milliseconds
1564 duration *= 1000;
1565
1566 if ((hotspotIndex1 != 0) || (hotspotIndex2 != 0)) {
1567 duration /= 100;
1568 if (_vm->_game->_script->peekByte(1) == 100)
1569 duration = 2;
1570 }
1571
1572 int16 timeVal = duration;
1573
1574 _vm->_game->_script->skip(6);
1575
1576 setCurrentHotspot(0, 0);
1577
1578 bool finishedDuration = false;
1579
1580 uint16 id = 0;
1581 uint16 inputId = 0xFFFF;
1582 uint16 index = 0;
1583
1584 bool hasInput = false;
1585 uint16 inputCount = 0;
1586
1587 // Adding new hotspots
1588 for (uint16 i = 0; i < count; i++)
1589 evaluateNew(i, ids, inputs, inputId, hasInput, inputCount);
1590
1591 // Recalculate all hotspots if requested
1592 if (needRecalculation)
1593 recalculate(true);
1594
1595 _vm->_game->_forceHandleMouse = 0;
1596 _vm->_util->clearKeyBuf();
1597
1598 while ((id == 0) && !_vm->_inter->_terminate && !_vm->shouldQuit()) {
1599 uint16 key = 0;
1600 if (hasInput) {
1601 // Input
1602
1603 uint16 curInput = 0;
1604
1605 key = handleInputs(duration, inputCount, curInput, inputs, id, index);
1606
1607 // Notify the script of the current input index
1608 WRITE_VAR(17 + 38, curInput);
1609 if (key == kKeyReturn) {
1610 // Return pressed, invoke input leave
1611 findFirstInputLeave(id, inputId, index);
1612 break;
1613 }
1614 } else
1615 // Normal move or click check
1616 key = check(_vm->_game->_handleMouse, -duration, id, index);
1617
1618 key = convertSpecialKey(key);
1619
1620 // Try to find a fitting hotspot
1621 evaluateFind(key, timeVal, ids, leaveWindowIndex, hotspotIndex1, hotspotIndex2, endIndex,
1622 duration, id, index, finishedDuration);
1623
1624 if (finishedDuration)
1625 break;
1626
1627 if ((id == 0) || (_hotspots[index].funcLeave != 0))
1628 // We don't have a new ID, but haven't yet handled the leave function
1629 continue;
1630
1631 _vm->_inter->storeMouse();
1632
1633 setCurrentHotspot(ids, id);
1634
1635 // Enter it
1636 if (_hotspots[index].funcEnter != 0)
1637 call(_hotspots[index].funcEnter);
1638
1639 setCurrentHotspot(0, 0);
1640 id = 0;
1641 }
1642
1643 if ((id & 0xFFF) == inputId)
1644 matchInputStrings(inputs);
1645
1646 if (_vm->_game->_handleMouse == 1)
1647 _vm->_draw->blitCursor();
1648
1649 if (!_vm->_inter->_terminate && (!finishedDuration)) {
1650 _vm->_game->_script->seek(_hotspots[index].funcLeave);
1651
1652 _vm->_inter->storeMouse();
1653 if (getCurrentHotspot() == 0) {
1654 // No hotspot currently handled, now we'll handle the newly found one
1655
1656 setCurrentHotspot(ids, id);
1657 }
1658 } else
1659 _vm->_game->_script->setFinished(true);
1660
1661 for (int i = 0; i < count; i++)
1662 // Remove all local hotspots
1663 remove(i + (kStateFilled << 12));
1664
1665 for (int i = 0; i < kHotspotCount; i++) {
1666 Hotspot &spot = _hotspots[i];
1667
1668 // Disable the ones still there
1669 if ((spot.getState() == (kStateFilled | kStateType1)) ||
1670 (spot.getState() == (kStateFilled | kStateType2)))
1671 spot.disable();
1672 }
1673
1674 }
1675
findCursor(uint16 x,uint16 y) const1676 int16 Hotspots::findCursor(uint16 x, uint16 y) const {
1677 int16 cursor = 0;
1678
1679 int16 deltax = 0;
1680 int16 deltay = 0;
1681
1682 // Fascination uses hard-coded windows
1683 if (_vm->getGameType() == kGameTypeFascination) {
1684 cursor = windowCursor(deltax, deltay);
1685
1686 // We're in a window and in an area that forces a specific cursor
1687 if (cursor > 0)
1688 return cursor;
1689
1690 // We're somewhere else inside a window
1691 if (cursor < 0) {
1692 int16 curType = -cursor * 256;
1693 cursor = 0;
1694
1695 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1696 const Hotspot &spot = _hotspots[i];
1697 // this check is /really/ Fascination specific.
1698 // It's illogical, so if it's to be reused in Adi games... Be careful!
1699 if ((spot.flags & 0xFF00) == curType)
1700 if (spot.isIn(x - deltax, y - deltay)) {
1701 if (spot.getType() < kTypeInput1NoLeave)
1702 cursor = 1;
1703 else
1704 cursor = 3;
1705 break;
1706 }
1707 }
1708
1709 if (_vm->_draw->_cursorAnimLow[cursor] == -1)
1710 // If the cursor is invalid... there's a generic "click" cursor
1711 cursor = 1;
1712
1713 return cursor;
1714 }
1715
1716 }
1717
1718 // Normal, non-window cursor handling
1719 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1720 const Hotspot &spot = _hotspots[i];
1721
1722 if ((spot.getWindow() != 0) || spot.isDisabled())
1723 // Ignore disabled and non-main-windowed hotspots
1724 continue;
1725
1726 if (!spot.isIn(x, y))
1727 // We're not in that hotspot, ignore it
1728 continue;
1729
1730 if (spot.getCursor() == 0) {
1731 // Hotspot doesn't itself specify a cursor...
1732 if (spot.getType() >= kTypeInput1NoLeave) {
1733 // ...but the type has a generic one
1734 cursor = 3;
1735 break;
1736 } else if ((spot.getButton() != kMouseButtonsRight) && (cursor == 0))
1737 // ...but there's a generic "click" cursor
1738 cursor = 1;
1739 } else if (cursor == 0)
1740 // Hotspot had an attached cursor index
1741 cursor = spot.getCursor();
1742 }
1743
1744 return cursor;
1745 }
1746
oPlaytoons_F_1B()1747 void Hotspots::oPlaytoons_F_1B() {
1748 int16 shortId;
1749 int16 longId;
1750 int16 var2;
1751 int16 fontIndex;
1752 int16 var4;
1753
1754 uint16 left;
1755 uint16 top;
1756 uint16 right;
1757 uint16 bottom;
1758
1759 shortId = _vm->_game->_script->readValExpr();
1760 var2 = _vm->_game->_script->readValExpr();
1761
1762 _vm->_game->_script->evalExpr(0);
1763
1764 fontIndex = _vm->_game->_script->readValExpr();
1765 var4 = _vm->_game->_script->readValExpr();
1766
1767 // this variable is always set to 0 in Playtoons
1768 // var_4 += unk_var;
1769
1770 for (int i = 0; i < kHotspotCount; i++) {
1771 if (_hotspots[i].isEnd())
1772 return;
1773
1774 if ((_hotspots[i].id == 0xD000 + shortId) || (_hotspots[i].id == 0xB000 + shortId) ||
1775 (_hotspots[i].id == 0x4000 + shortId)) {
1776 longId = _hotspots[i].id;
1777 warning("oPlaytoons_F_1B: shortId %d, var2 %d fontIndex %d var4 %d - longId %d", shortId, var2, fontIndex, var4, longId);
1778
1779 left = _hotspots[i].left;
1780 top = _hotspots[i].top;
1781 right = _hotspots[i].right;
1782 bottom = _hotspots[i].bottom;
1783
1784 left += 2;
1785 top += 2;
1786 right -= 2;
1787 bottom -= 2;
1788 if ((_vm->_draw->_needAdjust != 2) && (_vm->_draw->_needAdjust != 10)) {
1789 left += 2;
1790 top += 2;
1791 right -= 2;
1792 bottom -= 2;
1793 }
1794 _vm->_draw->oPlaytoons_sub_F_1B(0x8000+ var2, left, top, right, bottom, _vm->_game->_script->getResultStr(), fontIndex, var4, shortId);
1795 return;
1796 }
1797 }
1798 warning("shortId not found %d", shortId);
1799 return;
1800 }
1801
inputToHotspot(uint16 input) const1802 uint16 Hotspots::inputToHotspot(uint16 input) const {
1803 uint16 inputIndex = 0;
1804 for (int i = 0; i < kHotspotCount; i++) {
1805 const Hotspot &spot = _hotspots[i];
1806
1807 if (!spot.isActiveInput())
1808 // Not an active input
1809 continue;
1810
1811 if (inputIndex == input)
1812 // We've found our input
1813 return i;
1814
1815 // Next one
1816 inputIndex++;
1817 }
1818
1819 // None found
1820 return 0xFFFF;
1821 }
1822
hotspotToInput(uint16 hotspot) const1823 uint16 Hotspots::hotspotToInput(uint16 hotspot) const {
1824 uint16 input = 0;
1825
1826 for (int i = 0; i < kHotspotCount; i++) {
1827 const Hotspot &spot = _hotspots[i];
1828
1829 if (!spot.isActiveInput())
1830 // Not an active input
1831 continue;
1832
1833 if (i == hotspot)
1834 // We've found our hotspot
1835 break;
1836
1837 // Next one
1838 input++;
1839 }
1840
1841 return input;
1842 }
1843
findClickedInput(uint16 index) const1844 uint16 Hotspots::findClickedInput(uint16 index) const {
1845 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1846 const Hotspot &spot = _hotspots[i];
1847
1848 if (spot.getWindow() != 0)
1849 // Ignore other windows
1850 continue;
1851
1852 if (spot.isDisabled())
1853 // Ignore disabled hotspots
1854 continue;
1855
1856 if (!spot.isIn(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY))
1857 // This one wasn't it
1858 continue;
1859
1860 if (spot.getCursor() != 0)
1861 // This one specifies a cursor, so we don't want it
1862 continue;
1863
1864 if (!spot.isInput())
1865 // It's no input
1866 continue;
1867
1868 index = i;
1869 break;
1870 }
1871
1872 return index;
1873 }
1874
findFirstInputLeave(uint16 & id,uint16 & inputId,uint16 & index) const1875 bool Hotspots::findFirstInputLeave(uint16 &id, uint16 &inputId, uint16 &index) const {
1876 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1877 const Hotspot &spot = _hotspots[i];
1878
1879 if (!spot.isFilledEnabled())
1880 // Not filled or disabled
1881 continue;
1882
1883 if (!spot.isInputLeave())
1884 // Not an input with a leave function
1885 continue;
1886
1887 id = spot.id;
1888 inputId = spot.id & 0x7FFF;
1889 index = i;
1890 return true;
1891 }
1892
1893 return false;
1894 }
1895
findKey(uint16 key,uint16 & id,uint16 & index) const1896 bool Hotspots::findKey(uint16 key, uint16 &id, uint16 &index) const {
1897 id = 0;
1898 index = 0;
1899
1900 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1901 const Hotspot &spot = _hotspots[i];
1902
1903 if (!spot.isFilledEnabled())
1904 // Not filled or disabled
1905 continue;
1906
1907 // Key match Catch all
1908 if ((spot.key == key) || (spot.key == 0x7FFF)) {
1909 id = spot.id;
1910 index = i;
1911 return true;
1912 }
1913 }
1914
1915 return false;
1916 }
1917
findKeyCaseInsensitive(uint16 key,uint16 & id,uint16 & index) const1918 bool Hotspots::findKeyCaseInsensitive(uint16 key, uint16 &id, uint16 &index) const {
1919 id = 0;
1920 index = 0;
1921
1922 for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1923 const Hotspot &spot = _hotspots[i];
1924
1925 if (!spot.isFilledEnabled())
1926 // Not filled or disabled, ignore
1927 continue;
1928
1929 if ((spot.key & 0xFF00) != 0)
1930 continue;
1931
1932 if (spot.key == 0)
1933 // No associated key, ignore
1934 continue;
1935
1936 // Compare
1937 if (toupper(key & 0xFF) == toupper(spot.key)) {
1938 id = spot.id;
1939 index = i;
1940 return true;
1941 }
1942 }
1943
1944 return false;
1945 }
1946
findNthPlain(uint16 n,uint16 startIndex,uint16 & id,uint16 & index) const1947 bool Hotspots::findNthPlain(uint16 n, uint16 startIndex, uint16 &id, uint16 &index) const {
1948 id = 0;
1949 index = 0;
1950
1951 uint16 counter = 0;
1952 for (int i = startIndex; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
1953 const Hotspot &spot = _hotspots[i];
1954
1955 if (!spot.isFilledNew())
1956 // Not filled, ignore
1957 continue;
1958
1959 if (++counter != n)
1960 // Not yet the one we want
1961 continue;
1962
1963 id = spot.id;
1964 index = i;
1965 return true;
1966 }
1967
1968 return false;
1969 }
1970
leaveNthPlain(uint16 n,uint16 startIndex,int16 timeVal,const uint16 * ids,uint16 & id,uint16 & index,int16 & duration)1971 bool Hotspots::leaveNthPlain(uint16 n, uint16 startIndex, int16 timeVal, const uint16 *ids,
1972 uint16 &id, uint16 &index, int16 &duration) {
1973
1974 id = 0;
1975 index = 0;
1976
1977 if (!findNthPlain(n, startIndex, id, index))
1978 // Doesn't exist
1979 return false;
1980
1981 _vm->_inter->storeMouse();
1982
1983 if (getCurrentHotspot() != 0)
1984 // We already handle a hotspot
1985 return false;
1986
1987 setCurrentHotspot(ids, id);
1988
1989 const Hotspot &spot = _hotspots[index];
1990 if (spot.funcLeave != 0) {
1991 // It has a leave function
1992
1993 uint32 startTime, callTime;
1994
1995 // Call the leave and time it
1996 startTime = _vm->_util->getTimeKey();
1997 call(spot.funcLeave);
1998 _vm->_inter->animPalette();
1999 callTime = _vm->_util->getTimeKey() - startTime;
2000
2001 // Remove the time it took from the time we have available
2002 duration = CLIP<int>(timeVal - callTime, 2, timeVal);
2003 }
2004
2005 if (getCurrentHotspot() == 0) {
2006 id = 0;
2007 index = 0;
2008 }
2009
2010 return getCurrentHotspot() != 0;
2011 }
2012
setCurrentHotspot(const uint16 * ids,uint16 id) const2013 void Hotspots::setCurrentHotspot(const uint16 *ids, uint16 id) const {
2014 if (!ids) {
2015 WRITE_VAR(16, 0);
2016 return;
2017 }
2018
2019 if (Hotspot::getState(id) == kStateFilled)
2020 WRITE_VAR(16, ids[id & 0xFFF]);
2021 else
2022 WRITE_VAR(16, id & 0xFFF);
2023 }
2024
getCurrentHotspot() const2025 uint32 Hotspots::getCurrentHotspot() const {
2026 return VAR(16);
2027 }
2028
cleanFloatString(const Hotspot & spot) const2029 void Hotspots::cleanFloatString(const Hotspot &spot) const {
2030 char *to, *from;
2031
2032 to = from = GET_VARO_STR(spot.key);
2033 for (int i = 0; (i < 257) && (*from != '\0'); i++, from++) {
2034 char c = *from;
2035
2036 // Skip spaces
2037 if (c == ' ')
2038 continue;
2039
2040 // Convert decimal separator if necessary
2041 if ((_vm->_global->_language == kLanguageBritish) && (c == '.'))
2042 c = ',';
2043
2044 *to++ = c;
2045 }
2046
2047 *to = '\0';
2048 }
2049
checkStringMatch(const Hotspot & spot,const InputDesc & input,uint16 inputPos) const2050 void Hotspots::checkStringMatch(const Hotspot &spot, const InputDesc &input,
2051 uint16 inputPos) const {
2052
2053 const char *str = input.str;
2054
2055 char tempStr[256];
2056 char spotStr[256];
2057
2058 Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
2059
2060 if (spot.getType() < kTypeInput3NoLeave)
2061 _vm->_util->cleanupStr(tempStr);
2062
2063 uint16 pos = 0;
2064 do {
2065 Common::strlcpy(spotStr, str, 256);
2066
2067 pos += strlen(str) + 1;
2068 str += strlen(str) + 1;
2069
2070 if (spot.getType() < kTypeInput3NoLeave)
2071 _vm->_util->cleanupStr(spotStr);
2072
2073 // Compare the entered string with the string we wanted
2074 if (strcmp(tempStr, spotStr) == 0) {
2075 WRITE_VAR(17, VAR(17) + 1);
2076 WRITE_VAR(17 + inputPos, 1);
2077 break;
2078 }
2079 } while (input.length > pos);
2080 }
2081
matchInputStrings(const InputDesc * inputs) const2082 void Hotspots::matchInputStrings(const InputDesc *inputs) const {
2083 uint16 strInputCount = 0;
2084 uint16 inputIndex = 0;
2085 uint16 inputPos = 1;
2086
2087 for (int i = 0; i < kHotspotCount; i++) {
2088 const Hotspot &spot = _hotspots[i];
2089
2090 // Looking for all enabled inputs
2091 if (spot.isEnd())
2092 continue;
2093 if (!spot.isFilledEnabled())
2094 continue;
2095 if (!spot.isInput())
2096 continue;
2097
2098 if (spot.getType() >= kTypeInputFloatNoLeave)
2099 cleanFloatString(spot);
2100
2101 if ((spot.getType() >= kTypeInput2NoLeave) && (spot.getType() <= kTypeInput3Leave)) {
2102 // Look if we find a match between the wanted and the typed string
2103 checkStringMatch(spot, inputs[inputIndex], inputPos);
2104 strInputCount++;
2105 } else
2106 WRITE_VAR(17 + inputPos, 2);
2107
2108 inputIndex++;
2109 inputPos++;
2110 }
2111
2112 // Notify the scripts if we reached the requested hotspot
2113 WRITE_VAR(17, (uint32) (strInputCount == ((uint16) VAR(17))));
2114 }
2115
convertSpecialKey(uint16 key) const2116 uint16 Hotspots::convertSpecialKey(uint16 key) const {
2117 if (((key & 0xFF) >= ' ') && ((key & 0xFF) <= 0xFF) &&
2118 ((key >> 8) > 1) && ((key >> 8) < 12))
2119 key = '0' + (((key >> 8) - 1) % 10) + (key & 0xFF00);
2120
2121 return key;
2122 }
2123
getTextCursorPos(const Font & font,const char * str,uint32 pos,uint16 x,uint16 y,uint16 width,uint16 height,uint16 & cursorX,uint16 & cursorY,uint16 & cursorWidth,uint16 & cursorHeight) const2124 void Hotspots::getTextCursorPos(const Font &font, const char *str,
2125 uint32 pos, uint16 x, uint16 y, uint16 width, uint16 height,
2126 uint16 &cursorX, uint16 &cursorY, uint16 &cursorWidth, uint16 &cursorHeight) const {
2127
2128 if (!font.isMonospaced()) {
2129 // Cursor to the right of the current character
2130
2131 cursorX = x;
2132 cursorY = y;
2133 cursorWidth = 1;
2134 cursorHeight = height;
2135
2136 // Iterate through the string and add each character's width
2137 for (uint32 i = 0; i < pos; i++)
2138 cursorX += font.getCharWidth(str[i]);
2139
2140 } else {
2141 // Cursor underlining the current character
2142
2143 cursorX = x + font.getCharWidth() * pos;
2144 cursorY = y + height - 1;
2145 cursorWidth = font.getCharWidth();
2146 cursorHeight = 1;
2147 }
2148 }
2149
fillRect(uint16 x,uint16 y,uint16 width,uint16 height,uint16 color) const2150 void Hotspots::fillRect(uint16 x, uint16 y, uint16 width, uint16 height, uint16 color) const {
2151 _vm->_draw->_destSurface = Draw::kBackSurface;
2152 _vm->_draw->_destSpriteX = x;
2153 _vm->_draw->_destSpriteY = y;
2154 _vm->_draw->_spriteRight = width;
2155 _vm->_draw->_spriteBottom = height;
2156 _vm->_draw->_backColor = color;
2157
2158 _vm->_draw->spriteOperation(DRAW_FILLRECT | 0x10);
2159 }
2160
printText(uint16 x,uint16 y,const char * str,uint16 fontIndex,uint16 color) const2161 void Hotspots::printText(uint16 x, uint16 y, const char *str, uint16 fontIndex, uint16 color) const {
2162 _vm->_draw->_destSpriteX = x;
2163 _vm->_draw->_destSpriteY = y;
2164 _vm->_draw->_frontColor = color;
2165 _vm->_draw->_fontIndex = fontIndex;
2166 _vm->_draw->_textToPrint = str;
2167 _vm->_draw->_transparency = 1;
2168
2169 _vm->_draw->spriteOperation(DRAW_PRINTTEXT | 0x10);
2170 }
2171
updateAllTexts(const InputDesc * inputs) const2172 void Hotspots::updateAllTexts(const InputDesc *inputs) const {
2173 uint16 input = 0;
2174
2175 for (int i = 0; i < kHotspotCount; i++) {
2176 const Hotspot &spot = _hotspots[i];
2177
2178 if (spot.isEnd())
2179 // It's an end, we don't want it
2180 continue;
2181
2182 if (!spot.isFilledEnabled())
2183 // This one's either not used or disabled
2184 continue;
2185
2186 if (!spot.isInput())
2187 // Not an input
2188 continue;
2189
2190 // Get its text
2191 char tempStr[256];
2192 Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
2193
2194 // Coordinates
2195 uint16 x = spot.left;
2196 uint16 y = spot.top;
2197 uint16 width = spot.right - spot.left + 1;
2198 uint16 height = spot.bottom - spot.top + 1;
2199 // Clear the background
2200 fillRect(x, y, width, height, inputs[input].backColor);
2201
2202 // Center the text vertically
2203 y += (height - _vm->_draw->_fonts[_vm->_draw->_fontIndex]->getCharHeight()) / 2;
2204
2205 // Draw it
2206 printText(x, y, tempStr, inputs[input].fontIndex, inputs[input].frontColor);
2207
2208 input++;
2209 }
2210 }
2211 } // End of namespace Gob
2212