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 // Verb and hitarea handling
24 
25 #include "common/system.h"
26 
27 #include "graphics/surface.h"
28 
29 #include "agos/agos.h"
30 #include "agos/intern.h"
31 
32 namespace AGOS {
33 
34 static const char *const russian_verb_names[] = {
35 	"Ietj _",
36 	"Qnotrft< pa",
37 	"Nt_r[t<",
38 	"Ecjdat<",
39 	"Q=fst<",
40 	"C^]t<",
41 	"Ha_r[t<",
42 	"Isqom<^ocat<",
43 	"Docorjt<",
44 	"Qp]t<",
45 	"Neft<",
46 	"Eat<"
47 };
48 
49 static const char *const hebrew_verb_names[] = {
50 	"LJ @L",
51 	"DQZKL RL",
52 	"TZG",
53 	"DFF",
54 	"@KEL",
55 	"DXM",
56 	"QBEX",
57 	"DYZNY",
58 	"CAX @L",
59 	"DQX",
60 	"LAY",
61 	"ZO"
62 };
63 
64 static const char *const spanish_verb_names[] = {
65 	"Caminar",
66 	"Mirar",
67 	"Abrir",
68 	"Mover",
69 	"Consumir",
70 	"Coger",
71 	"Cerrar",
72 	"Usar",
73 	"Hablar",
74 	"Quitar",
75 	"Llevar",
76 	"Dar"
77 };
78 
79 static const char *const italian_verb_names[] = {
80 	"Vai verso",
81 	"Osserva",
82 	"Apri",
83 	"Sposta",
84 	"Mangia",
85 	"Raccogli",
86 	"Chiudi",
87 	"Usa",
88 	"Parla a",
89 	"Togli",
90 	"Indossa",
91 	"Dai"
92 };
93 
94 static const char *const french_verb_names[] = {
95 	"Aller vers",
96 	"Regarder",
97 	"Ouvrir",
98 	"D/placer",
99 	"Consommer",
100 	"Prendre",
101 	"Fermer",
102 	"Utiliser",
103 	"Parler ;",
104 	"Enlever",
105 	"Mettre",
106 	"Donner"
107 };
108 
109 static const char *const german_verb_names[] = {
110 	"Gehe zu",
111 	"Schau an",
112 	";ffne",
113 	"Bewege",
114 	"Verzehre",
115 	"Nimm",
116 	"Schlie+e",
117 	"Benutze",
118 	"Rede mit",
119 	"Entferne",
120 	"Trage",
121 	"Gib"
122 };
123 
124 static const char *const english_verb_names[] = {
125 	"Walk to",
126 	"Look at",
127 	"Open",
128 	"Move",
129 	"Consume",
130 	"Pick up",
131 	"Close",
132 	"Use",
133 	"Talk to",
134 	"Remove",
135 	"Wear",
136 	"Give"
137 };
138 
139 static const char *const czech_verb_names[] = {
140 	"Jit",
141 	"Podivat se",
142 	"Otevrit",
143 	"Pohnout s",
144 	"Snist",
145 	"Sebrat",
146 	"Zavrit",
147 	"Pouzit",
148 	"Mluvit s",
149 	"Odstranit",
150 	"Oblect",
151 	"Dat"
152 };
153 
154 static const char *const russian_verb_prep_names[] = {
155 	"", "", "", "",
156 	"", "", "", "s yfn?",
157 	"", "", "", "_onu ?"
158 };
159 
160 static const char *const hebrew_verb_prep_names[] = {
161 	"", "", "", "",
162 	"", "", "", "RM ND ?",
163 	"", "", "", "LNI ?"
164 };
165 
166 static const char *const spanish_verb_prep_names[] = {
167 	"", "", "", "",
168 	"", "", "", "^con qu/?",
169 	"", "", "", "^a qui/n?"
170 };
171 
172 static const char *const italian_verb_prep_names[] = {
173 	"", "", "", "",
174 	"", "", "", "con cosa ?",
175 	"", "", "", "a chi ?"
176 };
177 
178 static const char *const french_verb_prep_names[] = {
179 	"", "", "", "",
180 	"", "", "", "avec quoi ?",
181 	"", "", "", "; qui ?"
182 };
183 
184 static const char *const german_verb_prep_names[] = {
185 	"", "", "", "",
186 	"", "", "", "mit was ?",
187 	"", "", "", "zu wem ?"
188 };
189 
190 static const char *const english_verb_prep_names[] = {
191 	"", "", "", "",
192 	"", "", "", "with what ?",
193 	"", "", "", "to whom ?"
194 };
195 
196 static const char *const czech_verb_prep_names[] = {
197 	"", "", "", "",
198 	"", "", "", "s cim ?",
199 	"", "", "", "komu ?"
200 };
201 
202 #ifdef ENABLE_AGOS2
clearName()203 void AGOSEngine_Feeble::clearName() {
204 	stopAnimateSimon2(2, 6);
205 	_lastNameOn = NULL;
206 	_animatePointer = false;
207 	_mouseAnim = 1;
208 	return;
209 }
210 #endif
211 
clearName()212 void AGOSEngine_Simon2::clearName() {
213 	if (getBitFlag(79)) {
214 		sendSync(202);
215 		_lastNameOn = NULL;
216 		return;
217 	}
218 
219 	if (_currentVerbBox == _lastVerbOn)
220 		return;
221 
222 	resetNameWindow();
223 	_lastVerbOn = _currentVerbBox;
224 
225 	if (_currentVerbBox != NULL && !(_currentVerbBox->flags & kBFBoxDead))
226 		printVerbOf(_currentVerbBox->id);
227 }
228 
clearName()229 void AGOSEngine_Simon1::clearName() {
230 	HitArea *ha;
231 
232 	if (_currentVerbBox == _lastVerbOn)
233 		return;
234 
235 	resetNameWindow();
236 	_lastVerbOn = _currentVerbBox;
237 
238 	if (_currentVerbBox != NULL && (ha = findBox(200)) && (ha->flags & kBFBoxDead) && !(_currentVerbBox->flags & kBFBoxDead))
239 		printVerbOf(_currentVerbBox->id);
240 }
241 
clearName()242 void AGOSEngine::clearName() {
243 	if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2)
244 		return;
245 
246 	if (_nameLocked || !_lastNameOn)
247 		return;
248 
249 	resetNameWindow();
250 }
251 
252 static const byte convertVerbID[9] = {
253 	0, 1, 5, 11, 8, 7, 10, 3, 2
254 };
255 
printVerbOf(uint hitarea_id)256 void AGOSEngine::printVerbOf(uint hitarea_id) {
257 	const char *txt;
258 	const char * const *verb_names;
259 	const char * const *verb_prep_names;
260 
261 	hitarea_id -= 101;
262 	if (getGameType() == GType_SIMON2)
263 		hitarea_id = convertVerbID[hitarea_id];
264 
265 	if (_showPreposition) {
266 		switch (_language) {
267 		case Common::RU_RUS:
268 			verb_prep_names = russian_verb_prep_names;
269 			break;
270 		case Common::HE_ISR:
271 			verb_prep_names = hebrew_verb_prep_names;
272 			break;
273 		case Common::ES_ESP:
274 			verb_prep_names = spanish_verb_prep_names;
275 			break;
276 		case Common::IT_ITA:
277 			verb_prep_names = italian_verb_prep_names;
278 			break;
279 		case Common::FR_FRA:
280 			verb_prep_names = french_verb_prep_names;
281 			break;
282 		case Common::DE_DEU:
283 			verb_prep_names = german_verb_prep_names;
284 			break;
285 		case Common::CZ_CZE:
286 			verb_prep_names = czech_verb_prep_names;
287 			break;
288 		default:
289 			verb_prep_names = english_verb_prep_names;
290 			break;
291 		}
292 		CHECK_BOUNDS(hitarea_id, english_verb_prep_names);
293 		txt = verb_prep_names[hitarea_id];
294 	} else {
295 		switch (_language) {
296 		case Common::RU_RUS:
297 			verb_names = russian_verb_names;
298 			break;
299 		case Common::HE_ISR:
300 			verb_names = hebrew_verb_names;
301 			break;
302 		case Common::ES_ESP:
303 			verb_names = spanish_verb_names;
304 			break;
305 		case Common::IT_ITA:
306 			verb_names = italian_verb_names;
307 			break;
308 		case Common::FR_FRA:
309 			verb_names = french_verb_names;
310 			break;
311 		case Common::DE_DEU:
312 			verb_names = german_verb_names;
313 			break;
314 		case Common::CZ_CZE:
315 			verb_names = czech_verb_names;
316 			break;
317 		default:
318 			verb_names = english_verb_names;
319 			break;
320 		}
321 		CHECK_BOUNDS(hitarea_id, english_verb_names);
322 		txt = verb_names[hitarea_id];
323 	}
324 	showActionString((const byte *)txt);
325 }
326 
showActionString(const byte * string)327 void AGOSEngine::showActionString(const byte *string) {
328 	WindowBlock *window;
329 	uint x;
330 	const uint len = (getGameType() == GType_WW) ? 29 : 53;
331 
332 	window = _windowArray[1];
333 	if (window == NULL || window->textColor == 0)
334 		return;
335 
336 	// Arisme : hack for long strings in the French version
337 	if ((strlen((const char*)string) - 1) <= len)
338 		x = (len - (strlen((const char *)string) - 1)) * 3;
339 	else
340 		x = 0;
341 
342 	window->textColumn = x / 8;
343 	window->textColumnOffset = x & 7;
344 	if (_language == Common::HE_ISR && window->textColumnOffset != 0) {
345 		window->textColumnOffset = 8 - window->textColumnOffset;
346 		window->textColumn++;
347 	}
348 
349 	for (; *string; string++)
350 		windowPutChar(window, *string);
351 }
352 
handleVerbClicked(uint verb)353 void AGOSEngine::handleVerbClicked(uint verb) {
354 	Subroutine *sub;
355 	int result;
356 
357 	if (shouldQuit())
358 		return;
359 
360 	_objectItem = _hitAreaObjectItem;
361 	if (_objectItem == _dummyItem2) {
362 		_objectItem = me();
363 	}
364 	if (_objectItem == _dummyItem3) {
365 		_objectItem = derefItem(me()->parent);
366 	}
367 
368 	_subjectItem = _hitAreaSubjectItem;
369 	if (_subjectItem == _dummyItem2) {
370 		_subjectItem = me();
371 	}
372 	if (_subjectItem == _dummyItem3) {
373 		_subjectItem = derefItem(me()->parent);
374 	}
375 
376 	if (_subjectItem) {
377 		_scriptNoun1 = _subjectItem->noun;
378 		_scriptAdj1 = _subjectItem->adjective;
379 	} else {
380 		_scriptNoun1 = -1;
381 		_scriptAdj1 = -1;
382 	}
383 
384 	if (_objectItem) {
385 		_scriptNoun2 = _objectItem->noun;
386 		_scriptAdj2 = _objectItem->adjective;
387 	} else {
388 		_scriptNoun2 = -1;
389 		_scriptAdj2 = -1;
390 	}
391 
392 	_scriptVerb = _verbHitArea;
393 
394 	sub = getSubroutineByID(0);
395 	if (sub == NULL)
396 		return;
397 
398 	result = startSubroutine(sub);
399 	if (result == -1)
400 		showMessageFormat("I don't understand");
401 
402 	_runScriptReturn1 = false;
403 
404 	sub = getSubroutineByID(100);
405 	if (sub)
406 		startSubroutine(sub);
407 
408 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP)
409 		_runScriptReturn1 = false;
410 
411 	permitInput();
412 }
413 
resetNameWindow()414 void AGOSEngine::resetNameWindow() {
415 	WindowBlock *window;
416 
417 	if (getGameType() == GType_SIMON2 && getBitFlag(79))
418 		return;
419 
420 	window = _windowArray[1];
421 	if (window != NULL && window->textColor != 0)
422 		clearWindow(window);
423 
424 	_lastNameOn = NULL;
425 	_lastVerbOn = NULL;
426 }
427 
findBox(uint hitarea_id)428 HitArea *AGOSEngine::findBox(uint hitarea_id) {
429 	HitArea *ha = _hitAreas;
430 	uint count = ARRAYSIZE(_hitAreas);
431 
432 	do {
433 		if (getGameType() == GType_FF || getGameType() == GType_PP) {
434 			if (ha->id == hitarea_id && ha->flags != 0)
435 				return ha;
436 		} else {
437 			if (ha->id == hitarea_id)
438 				return ha;
439 		}
440 	} while (ha++, --count);
441 	return NULL;
442 }
443 
findEmptyHitArea()444 HitArea *AGOSEngine::findEmptyHitArea() {
445 	HitArea *ha = _hitAreas;
446 	uint count = ARRAYSIZE(_hitAreas) - 1;
447 
448 	do {
449 		if (ha->flags == 0)
450 			return ha;
451 	} while (ha++, --count);
452 
453 	// The last box is overwritten, if too many boxes are allocated.
454 	return ha;
455 }
456 
freeBox(uint index)457 void AGOSEngine::freeBox(uint index) {
458 	CHECK_BOUNDS(index, _hitAreas);
459 	_hitAreas[index].flags = 0;
460 }
461 
enableBox(uint hitarea)462 void AGOSEngine::enableBox(uint hitarea) {
463 	HitArea *ha = findBox(hitarea);
464 	if (ha != NULL)
465 		ha->flags &= ~kBFBoxDead;
466 }
467 
disableBox(uint hitarea)468 void AGOSEngine::disableBox(uint hitarea) {
469 	HitArea *ha = findBox(hitarea);
470 	if (ha != NULL) {
471 		ha->flags |= kBFBoxDead;
472 		ha->flags &= ~kBFBoxSelected;
473 		if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) &&
474 			hitarea == 102) {
475 			resetVerbs();
476 		}
477 	}
478 }
479 
moveBox(uint hitarea,int x,int y)480 void AGOSEngine::moveBox(uint hitarea, int x, int y) {
481 	HitArea *ha = findBox(hitarea);
482 	if (ha != NULL) {
483 		if (getGameType() == GType_FF || getGameType() == GType_PP) {
484 			ha->x += x;
485 			ha->y += y;
486 		} else {
487 			ha->x = x;
488 			ha->y = y;
489 		}
490 	}
491 }
492 
undefineBox(uint hitarea)493 void AGOSEngine::undefineBox(uint hitarea) {
494 	HitArea *ha = findBox(hitarea);
495 	if (ha != NULL) {
496 		ha->flags = 0;
497 		if (ha == _lastNameOn)
498 			clearName();
499 		_needHitAreaRecalc++;
500 	}
501 }
502 
isBoxDead(uint hitarea)503 bool AGOSEngine::isBoxDead(uint hitarea) {
504 	HitArea *ha = findBox(hitarea);
505 	if (ha == NULL)
506 		return false;
507 	return (ha->flags & kBFBoxDead) == 0;
508 }
509 
defineBox(uint16 id,uint16 x,uint16 y,uint16 height,uint16 width,uint16 msg1,uint16 msg2,uint16 flags)510 void AGOSEngine::defineBox(uint16 id, uint16 x, uint16 y, uint16 height, uint16 width, uint16 msg1, uint16 msg2, uint16 flags) {
511 	HitArea *ha = _hitAreaList + id;
512 	ha->x = x;
513 	ha->y = y;
514 	ha->width = width;
515 	ha->height = height;
516 	ha->msg1 = msg1;
517 	ha->msg2 = msg2;
518 	ha->flags = flags;
519 	ha->id = ha->priority = id;
520 }
521 
defineBox(int id,int x,int y,int width,int height,int flags,int verb,Item * itemPtr)522 void AGOSEngine::defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *itemPtr) {
523 	HitArea *ha;
524 	undefineBox(id);
525 
526 	ha = findEmptyHitArea();
527 	ha->x = x;
528 	ha->y = y;
529 	ha->width = width;
530 	ha->height = height;
531 	ha->flags = flags | kBFBoxInUse;
532 	ha->id = ha->priority = id;
533 	ha->verb = verb;
534 	ha->itemPtr = itemPtr;
535 
536 	if (getGameType() == GType_FF && (ha->flags & kBFHyperBox)) {
537 		ha->data = _hyperLink;
538 		ha->priority = 50;
539 	}
540 
541 	_needHitAreaRecalc++;
542 }
543 
544 #ifdef ENABLE_AGOS2
resetVerbs()545 void AGOSEngine_PuzzlePack::resetVerbs() {
546 	_verbHitArea = 300;
547 }
548 
resetVerbs()549 void AGOSEngine_Feeble::resetVerbs() {
550 	_verbHitArea = 300;
551 	int cursor = 0;
552 	int animMax = 16;
553 
554 	if (getBitFlag(203)) {
555 		cursor = 14;
556 		animMax = 9;
557 	} else if (getBitFlag(204)) {
558 		cursor = 15;
559 		animMax = 9;
560 	} else if (getBitFlag(207)) {
561 		cursor = 26;
562 		animMax = 2;
563 	}
564 
565 	_mouseCursor = cursor;
566 	_mouseAnimMax = animMax;
567 	_mouseAnim = 1;
568 	_needHitAreaRecalc++;
569 
570 	if (getBitFlag(99)) {
571 		setVerb(NULL);
572 	}
573 }
574 #endif
575 
resetVerbs()576 void AGOSEngine::resetVerbs() {
577 	if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2)
578 		return;
579 
580 	uint id;
581 	HitArea *ha;
582 
583 	if (getGameType() == GType_SIMON2) {
584 		id = 2;
585 		if (!getBitFlag(79))
586 		id = (_mouse.y >= 136) ? 102 : 101;
587 	} else {
588 		id = (_mouse.y >= 136) ? 102 : 101;
589 	}
590 
591 	_defaultVerb = id;
592 
593 	ha = findBox(id);
594 	if (ha == NULL)
595 		return;
596 
597 	if (ha->flags & kBFBoxDead) {
598 		_defaultVerb = 999;
599 		_currentVerbBox = NULL;
600 	} else {
601 		_verbHitArea = ha->verb;
602 		setVerb(ha);
603 	}
604 }
605 
606 #ifdef ENABLE_AGOS2
setVerb(HitArea * ha)607 void AGOSEngine_Feeble::setVerb(HitArea *ha) {
608 	int cursor = _mouseCursor;
609 	if (_noRightClick)
610 		return;
611 
612 	if (cursor > 13)
613 		cursor = 0;
614 	cursor++;
615 	if (cursor == 5)
616 		cursor = 1;
617 	if (cursor == 4) {
618 		if (getBitFlag(72)) {
619 			cursor = 1;
620 		}
621 	} else if (cursor == 2) {
622 		if (getBitFlag(99)) {
623 			cursor = 3;
624 		}
625 	}
626 
627 	_mouseCursor = cursor;
628 	_mouseAnimMax = (cursor == 4) ? 14: 16;
629 	_mouseAnim = 1;
630 	_needHitAreaRecalc++;
631 	_verbHitArea = cursor + 300;
632 }
633 #endif
634 
setVerb(HitArea * ha)635 void AGOSEngine::setVerb(HitArea *ha) {
636 	HitArea *tmp = _currentVerbBox;
637 
638 	if (ha == tmp)
639 		return;
640 
641 	if (getGameType() == GType_SIMON1) {
642 		if (tmp != NULL) {
643 			tmp->flags |= kBFInvertTouch;
644 			if (getFeatures() & GF_32COLOR)
645 				invertBox(tmp, 212, 208, 212, 8);
646 			else
647 				invertBox(tmp, 213, 208, 213, 10);
648 		}
649 
650 		if (ha->flags & kBFBoxSelected) {
651 			if (getFeatures() & GF_32COLOR)
652 				invertBox(ha, 216, 212, 212, 4);
653 			else
654 				invertBox(ha, 218, 213, 213, 5);
655 		} else {
656 			if (getFeatures() & GF_32COLOR)
657 				invertBox(ha, 220, 216, 216, 8);
658 			else
659 				invertBox(ha, 223, 218, 218, 10);
660 		}
661 
662 		ha->flags &= ~(kBFBoxSelected + kBFInvertTouch);
663 	} else {
664 		if (ha->id < 101)
665 			return;
666 		_mouseCursor = ha->id - 101;
667 		_needHitAreaRecalc++;
668 	}
669 	_currentVerbBox = ha;
670 }
671 
672 #ifdef ENABLE_AGOS2
hitarea_leave(HitArea * ha,bool state)673 void AGOSEngine_Feeble::hitarea_leave(HitArea *ha, bool state) {
674 	invertBox(ha, state);
675 }
676 #endif
677 
hitarea_leave(HitArea * ha,bool state)678 void AGOSEngine::hitarea_leave(HitArea *ha, bool state) {
679 	if (getGameType() == GType_SIMON2) {
680 		invertBox(ha, 231, 229, 230, 1);
681 	} else {
682 		if (getFeatures() & GF_32COLOR)
683 			invertBox(ha, 220, 212, 216, 4);
684 		else
685 			invertBox(ha, 223, 213, 218, 5);
686 	}
687 }
688 
leaveHitAreaById(uint hitarea_id)689 void AGOSEngine::leaveHitAreaById(uint hitarea_id) {
690 	HitArea *ha = findBox(hitarea_id);
691 	if (ha)
692 		hitarea_leave(ha);
693 }
694 
inventoryUp(WindowBlock * window)695 void AGOSEngine::inventoryUp(WindowBlock *window) {
696 	if (window->iconPtr->line == 0)
697 		return;
698 
699 	mouseOff();
700 	uint index = getWindowNum(window);
701 	drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line - 1, window->iconPtr->classMask);
702 	mouseOn();
703 }
704 
inventoryDown(WindowBlock * window)705 void AGOSEngine::inventoryDown(WindowBlock *window) {
706 	mouseOff();
707 	uint index = getWindowNum(window);
708 	drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line + 1, window->iconPtr->classMask);
709 	mouseOn();
710 }
711 
boxController(uint x,uint y,uint mode)712 void AGOSEngine::boxController(uint x, uint y, uint mode) {
713 	HitArea *best_ha;
714 	HitArea *ha = _hitAreas;
715 	uint count = ARRAYSIZE(_hitAreas);
716 	uint16 priority = 0;
717 
718 	best_ha = NULL;
719 
720 	do {
721 		if (ha->flags & kBFBoxInUse) {
722 			if (!(ha->flags & kBFBoxDead)) {
723 				if (x >= ha->x && y >= ha->y &&
724 						x - ha->x < ha->width && y - ha->y < ha->height && priority <= ha->priority) {
725 					priority = ha->priority;
726 					best_ha = ha;
727 				} else {
728 					if (ha->flags & kBFBoxSelected) {
729 						hitarea_leave(ha , true);
730 						ha->flags &= ~kBFBoxSelected;
731 					}
732 				}
733 			} else {
734 				ha->flags &= ~kBFBoxSelected;
735 			}
736 		}
737 	} while (ha++, --count);
738 
739 	_currentBoxNum = 0;
740 	_currentBox = best_ha;
741 
742 	if (best_ha == NULL)
743 		return;
744 
745 	_currentBoxNum = best_ha->id;
746 
747 	if (mode != 0) {
748 		if (mode == 3) {
749 			if (best_ha->verb & 0x4000) {
750 				if (getGameType() == GType_ELVIRA1 && _variableArray[500] == 0) {
751 					_variableArray[500] = best_ha->verb & 0xBFFF;
752 				}
753 
754 				if (_clickOnly && best_ha->id < 8) {
755 					uint id = best_ha->id;
756 					if (id >= 4)
757 						id -= 4;
758 
759 					invertBox(findBox(id), 0, 0, 0, 0);
760 					_clickOnly = false;
761 					return;
762 				}
763 			}
764 
765 			if (best_ha->flags & kBFDragBox)
766 				_lastClickRem = best_ha;
767 		} else {
768 			_lastHitArea = best_ha;
769 		}
770 	}
771 
772 	if (_clickOnly)
773 		return;
774 
775 	if (best_ha->flags & kBFInvertTouch) {
776 		if (!(best_ha->flags & kBFBoxSelected)) {
777 			hitarea_leave(best_ha, false);
778 			best_ha->flags |= kBFBoxSelected;
779 		}
780 	} else {
781 		if (mode == 0)
782 			return;
783 
784 		if (!(best_ha->flags & kBFInvertSelect))
785 			return;
786 
787 		if (best_ha->flags & kBFToggleBox) {
788 			hitarea_leave(best_ha, false);
789 			best_ha->flags ^= kBFInvertSelect;
790 		} else if (!(best_ha->flags & kBFBoxSelected)) {
791 			hitarea_leave(best_ha, false);
792 			best_ha->flags |= kBFBoxSelected;
793 		}
794 	}
795 }
796 
boxController(uint x,uint y,uint mode)797 void AGOSEngine_Waxworks::boxController(uint x, uint y, uint mode) {
798 	HitArea *best_ha;
799 	HitArea *ha = _hitAreas;
800 	uint count = ARRAYSIZE(_hitAreas);
801 	uint16 priority = 0;
802 	uint16 x_ = x;
803 	uint16 y_ = y;
804 
805 	if (getGameType() == GType_FF || getGameType() == GType_PP) {
806 		x_ += _scrollX;
807 		y_ += _scrollY;
808 	} else if (getGameType() == GType_SIMON2) {
809 		if (getBitFlag(79) || y < 134) {
810 			x_ += _scrollX * 8;
811 		}
812 	}
813 
814 	best_ha = NULL;
815 
816 	do {
817 		if (ha->flags & kBFBoxInUse) {
818 			if (!(ha->flags & kBFBoxDead)) {
819 				if (x_ >= ha->x && y_ >= ha->y &&
820 						x_ - ha->x < ha->width && y_ - ha->y < ha->height && priority <= ha->priority) {
821 					priority = ha->priority;
822 					best_ha = ha;
823 				} else {
824 					if (ha->flags & kBFBoxSelected) {
825 						hitarea_leave(ha , true);
826 						ha->flags &= ~kBFBoxSelected;
827 					}
828 				}
829 			} else {
830 				ha->flags &= ~kBFBoxSelected;
831 			}
832 		}
833 	} while (ha++, --count);
834 
835 	_currentBoxNum = 0;
836 	_currentBox = best_ha;
837 
838 	if (best_ha == NULL) {
839 		clearName();
840 		if (getGameType() == GType_WW && _mouseCursor >= 4) {
841 			_mouseCursor = 0;
842 			_needHitAreaRecalc++;
843 		}
844 		return;
845 	}
846 
847 	_currentBoxNum = best_ha->id;
848 
849 	if (mode != 0) {
850 		if (mode == 3) {
851 			if (best_ha->flags & kBFDragBox) {
852 				_lastClickRem = best_ha;
853 			}
854 		} else {
855 			_lastHitArea = best_ha;
856 			if (getGameType() == GType_PP) {
857 				_variableArray[400] = x;
858 				_variableArray[401] = y;
859 			} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2 ||
860 				getGameType() == GType_FF) {
861 				_variableArray[1] = x;
862 				_variableArray[2] = y;
863 			}
864 		}
865 	}
866 
867 	if ((getGameType() == GType_WW) && (_mouseCursor == 0 || _mouseCursor >= 4)) {
868 		uint verb = best_ha->verb & 0x3FFF;
869 		if (verb >= 239 && verb <= 242) {
870 			uint cursor = verb - 235;
871 			if (_mouseCursor != cursor) {
872 				_mouseCursor = cursor;
873 				_needHitAreaRecalc++;
874 			}
875 		}
876 	}
877 
878 	if (getGameType() != GType_WW || !_nameLocked) {
879 		if (best_ha->flags & kBFNoTouchName) {
880 			clearName();
881 		} else if (best_ha != _lastNameOn) {
882 			displayName(best_ha);
883 		}
884 	}
885 
886 	if (best_ha->flags & kBFInvertTouch && !(best_ha->flags & kBFBoxSelected)) {
887 		hitarea_leave(best_ha, false);
888 		best_ha->flags |= kBFBoxSelected;
889 	}
890 }
891 
displayName(HitArea * ha)892 void AGOSEngine::displayName(HitArea *ha) {
893 	if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_PP)
894 		return;
895 
896 	bool result;
897 	int x = 0, y = 0;
898 
899 	if (getGameType() == GType_FF) {
900 		if (ha->flags & kBFHyperBox) {
901 			_lastNameOn = ha;
902 			return;
903 		}
904 		if (findBox(50))
905 			return;
906 
907 		if (getBitFlag(99))
908 			_animatePointer = ((ha->flags & kBFTextBox) == 0);
909 		else
910 			_animatePointer = true;
911 
912 		if (!getBitFlag(73))
913 			return;
914 
915 		y = ha->y;
916 		if (getBitFlag(99) && y > 288)
917 			y = 288;
918 		y -= 17;
919 		if (y < 0)
920 			y = 0;
921 		y += 2;
922 		x = ha->width / 2 + ha->x;
923 	} else {
924 		resetNameWindow();
925 	}
926 
927 	if (ha->flags & kBFTextBox) {
928 		result = printTextOf(ha->flags / 256, x, y);
929 	} else {
930 		result = printNameOf(ha->itemPtr, x, y);
931 	}
932 
933 	if (result)
934 		_lastNameOn = ha;
935 }
936 
937 #ifdef ENABLE_AGOS2
invertBox(HitArea * ha,bool state)938 void AGOSEngine_Feeble::invertBox(HitArea *ha, bool state) {
939 	if (getBitFlag(205) || getBitFlag(206)) {
940 		if (state != 0) {
941 			_mouseAnimMax = _oldMouseAnimMax;
942 			_mouseCursor = _oldMouseCursor;
943 		} else if (_mouseCursor != 18) {
944 			_oldMouseCursor = _mouseCursor;
945 			_animatePointer = false;
946 			_oldMouseAnimMax = _mouseAnimMax;
947 			_mouseAnimMax = 2;
948 			_mouseCursor = 18;
949 		}
950 	} else {
951 		if (getBitFlag(207)) {
952 			if (state != 0) {
953 				_noRightClick = 0;
954 				resetVerbs();
955 			} else {
956 				int cursor = ha->id + 9;
957 				if (cursor >= 23)
958 					cursor = 21;
959 				_mouseCursor = cursor;
960 				_mouseAnimMax = 8;
961 				_noRightClick = 1;
962 			}
963 		} else {
964 			VgaSprite *vsp = _vgaSprites;
965 
966 			int id = ha->id - 43;
967 			while (vsp->id) {
968 				if (vsp->id == id && vsp->zoneNum == 2) {
969 					if (state == 0)
970 						vsp->flags |= kDFShaded;
971 					else
972 						vsp->flags &= ~kDFShaded;
973 					break;
974 				}
975 				vsp++;
976 			}
977 		}
978 	}
979 }
980 #endif
981 
invertBox(HitArea * ha,byte a,byte b,byte c,byte d)982 void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) {
983 	byte *src, color;
984 	int w, h, i;
985 
986 	_videoLockOut |= 0x8000;
987 
988 	Graphics::Surface *screen = _system->lockScreen();
989 	src = (byte *)screen->getBasePtr(ha->x, ha->y);
990 
991 	// WORKAROUND: Hitareas for saved game names aren't adjusted for scrolling locations
992 	if (getGameType() == GType_SIMON2 && ha->id >= 208 && ha->id <= 213) {
993 		src -= _scrollX * 8;
994 	}
995 
996 	_litBoxFlag = true;
997 
998 	w = ha->width;
999 	h = ha->height;
1000 
1001 	do {
1002 		for (i = 0; i != w; ++i) {
1003 			color = src[i];
1004 			if (getGameType() == GType_WW) {
1005 				if (!(color & 0xF) || (color & 0xF) == 10) {
1006 					color ^= 10;
1007 					src[i] = color;
1008 				}
1009 			} else if (getGameType() == GType_ELVIRA2) {
1010 				if (!(color & 1)) {
1011 					color ^= 2;
1012 					src[i] = color;
1013 				}
1014 			} else if (getGameType() == GType_ELVIRA1) {
1015 				if (color & 1) {
1016 					color ^= 2;
1017 					src[i] = color;
1018 				}
1019 			} else if (getGameType() == GType_PN) {
1020 				if (getPlatform() == Common::kPlatformDOS) {
1021 					if (color != 15) {
1022 						color ^= 7;
1023 						src[i] = color;
1024 					}
1025 				} else {
1026 					if (color != 14) {
1027 						color ^= 15;
1028 						src[i] = color;
1029 					}
1030 				}
1031 			} else {
1032 				if (a >= color && b < color) {
1033 					if (c >= color)
1034 						color += d;
1035 					else
1036 						color -= d;
1037 					src[i] = color;
1038 				}
1039 			}
1040 		}
1041 		src += screen->pitch;
1042 	} while (--h);
1043 
1044 	_system->unlockScreen();
1045 
1046 	_videoLockOut &= ~0x8000;
1047 }
1048 
1049 } // End of namespace AGOS
1050