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 "sherlock/tattoo/widget_inventory.h"
24 #include "sherlock/tattoo/tattoo_fixed_text.h"
25 #include "sherlock/tattoo/tattoo_people.h"
26 #include "sherlock/tattoo/tattoo_scene.h"
27 #include "sherlock/tattoo/tattoo_user_interface.h"
28 #include "sherlock/tattoo/tattoo.h"
29
30 namespace Sherlock {
31
32 namespace Tattoo {
33
34 #define INVENTORY_XSIZE 70 // Width of the box that surrounds inventory items
35 #define INVENTORY_YSIZE 70 // Height of the box that surrounds inventory items
36 #define MAX_INV_COMMANDS 10 // Maximum elements in dialog
37 #define NUM_INV_PER_LINE 4 // Number of inentory items per line in the dialog
38
WidgetInventoryTooltip(SherlockEngine * vm,WidgetInventory * owner)39 WidgetInventoryTooltip::WidgetInventoryTooltip(SherlockEngine *vm, WidgetInventory *owner) :
40 WidgetTooltipBase(vm), _owner(owner) {
41 }
42
setText(const Common::String & str)43 void WidgetInventoryTooltip::setText(const Common::String &str) {
44 // If no text specified, erase any previously displayed tooltip and free its surface
45 if (str.empty()) {
46 erase();
47 _surface.free();
48 return;
49 }
50
51 int width = _surface.stringWidth(str) + 2;
52 int height = 0;
53 Common::String line1 = str, line2;
54
55 // See if we need to split it into two lines
56 if (width > 150) {
57 // Yes, we do
58 const char *s = str.c_str();
59 const char *space = nullptr;
60 int dif = 10000;
61
62 while (*s) {
63 s = strchr(s, ' ');
64
65 if (!s) {
66 if (!space) {
67 height = _surface.stringHeight(str) + 2;
68 } else {
69 line1 = Common::String(str.c_str(), space);
70 line2 = Common::String(space + 1);
71 height = _surface.stringHeight(line1) + _surface.stringHeight(line2) + 4;
72 }
73 break;
74 } else {
75 line1 = Common::String(str.c_str(), s);
76 line2 = Common::String(s + 1);
77 int width1 = _surface.stringWidth(line1);
78 int width2 = _surface.stringWidth(line2);
79
80 if (ABS(width1 - width2) < dif) {
81 // Found a split point that results in less overall width
82 space = s;
83 dif = ABS(width1 - width2);
84 width = MAX(width1, width2);
85 }
86
87 s++;
88 }
89 }
90 } else {
91 height = _surface.stringHeight(str) + 2;
92 }
93
94 // Allocate a fresh surface for the new string
95 _bounds = Common::Rect(width, height);
96 _surface.create(width, height);
97 _surface.clear(TRANSPARENCY);
98
99 if (line2.empty()) {
100 _surface.writeFancyString(str, Common::Point(0, 0), BLACK, INFO_TOP);
101 } else {
102 int xp, yp;
103
104 xp = (_bounds.width() - _surface.stringWidth(line1) - 2) / 2;
105 _surface.writeFancyString(line1, Common::Point(xp, 0), BLACK, INFO_TOP);
106
107 xp = (_bounds.width() - _surface.stringWidth(line2) - 2) / 2;
108 yp = _surface.stringHeight(line2) + 2;
109 _surface.writeFancyString(line2, Common::Point(xp, yp), BLACK, INFO_TOP);
110 }
111 }
112
handleEvents()113 void WidgetInventoryTooltip::handleEvents() {
114 Events &events = *_vm->_events;
115 FixedText &fixedText = *_vm->_fixedText;
116 Inventory &inv = *_vm->_inventory;
117 TattooPeople &people = *(TattooPeople *)_vm->_people;
118 Scene &scene = *_vm->_scene;
119 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
120 Common::Point mousePos = events.mousePos();
121 Common::String str;
122 int select = -1, oldSelect = 999;
123 Common::String strWith = fixedText.getText(kFixedText_With);
124 Common::String strUse = fixedText.getText(kFixedText_Use);
125
126 // Register the tooltip for requiring post-rendering drawing, since we draw directly to the screen if a scene
127 // mask is active, since the initial draw to the screen will be covered by the mask rendering
128 if (ui._mask) {
129 ui._postRenderWidgets.push_back(this);
130 }
131
132 // If we are using an inventory item on an object in the room, display the appropriate text above the mouse cursor
133 if (_owner->_invVerbMode == 3) {
134 select = ui._bgFound;
135 oldSelect = ui._oldBgFound;
136
137 if (select != -1 && (select != oldSelect || (select != -1 && _surface.empty()))) {
138 // See if we're pointing at a shape or a sprite
139 if (select < 1000) {
140 Object &obj = scene._bgShapes[select];
141
142 if (!obj._description.empty() && !obj._description.hasPrefix(" ")) {
143 if (_vm->getLanguage() == Common::GR_GRE) {
144
145 if (!_owner->_swapItems)
146 str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), obj._description.c_str(),
147 inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str());
148 else
149 str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(),
150 obj._description.c_str(), _owner->_verb.c_str());
151 } else {
152 if (_owner->_swapItems)
153 str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), obj._description.c_str(), _owner->_action.c_str(),
154 inv[_owner->_invSelect]._name.c_str());
155 else
156 str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), inv[_owner->_invSelect]._name.c_str(),
157 _owner->_action.c_str(), obj._description.c_str());
158 }
159 }
160 } else {
161 Person &person = people[ui._bgFound - 1000];
162
163 if (!person._description.empty() && !person._description.hasPrefix(" ")) {
164 if (_vm->getLanguage() == Common::GR_GRE) {
165 if (!_owner->_swapItems)
166 str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), person._description.c_str(),
167 inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str());
168 else
169 str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(),
170 person._description.c_str(), _owner->_verb.c_str());
171 } else {
172
173 if (_owner->_swapItems)
174 str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), person._description.c_str(),
175 _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str());
176 else
177 str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(),
178 inv[_owner->_invSelect]._name.c_str(), _owner->_action.c_str(), person._description.c_str());
179 }
180 }
181 }
182 }
183 } else {
184 const Common::Rect &b = _owner->_bounds;
185 Common::Rect r(b.left + 3, b.top + 3, b.right - 3 - BUTTON_SIZE, b.bottom - 3);
186
187 if (r.contains(mousePos)) {
188 select = (mousePos.x - r.left) / (INVENTORY_XSIZE + 3) + NUM_INVENTORY_SHOWN / 2 *
189 ((mousePos.y - r.top) / (INVENTORY_YSIZE + 3)) + inv._invIndex;
190
191 if (select >= inv._holdings) {
192 select = -1;
193 } else {
194 oldSelect = _owner->_invSelect;
195
196 if (select != _owner->_invSelect || _surface.empty()) {
197
198 if (_owner->_invMode == 1) {
199 // See if we were pointing at a shapre or sprite
200 if (ui._activeObj < 1000) {
201 Object &obj = scene._bgShapes[ui._activeObj];
202
203 if (!obj._description.empty() && !obj._description.hasPrefix(" "))
204 str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[select]._name.c_str(),
205 strWith.c_str(), obj._description.c_str());
206 } else {
207 Person &person = people[ui._activeObj - 1000];
208
209 if (!person._description.empty() && !person._description.hasPrefix(" "))
210 str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[select]._name.c_str(),
211 strWith.c_str(), person._description.c_str());
212 }
213 } else {
214 if (_owner->_invVerbMode == 2)
215 str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[_owner->_invSelect]._name.c_str(),
216 strWith.c_str(), inv[select]._name.c_str());
217 else
218 str = inv[select]._description.c_str();
219 }
220 }
221 }
222 }
223 }
224
225 // See if they are pointing at a different inventory object and we need to
226 // change the graphics of the Text Tag
227 if (select != oldSelect || (select != -1 && _surface.empty())) {
228 // Set the text
229 setText(str);
230
231 if (_owner->_invVerbMode != 3)
232 _owner->_invSelect = select;
233 else
234 ui._oldBgFound = select;
235 } else if (select == -1 && oldSelect != -1) {
236 setText(Common::String());
237 return;
238 }
239
240 if (_owner->_invVerbMode == 3)
241 // Adjust tooltip to be above the inventory item being shown above the standard cursor
242 mousePos.y -= events._hotspotPos.y;
243
244 // Update the position of the tooltip
245 int xs = CLIP(mousePos.x - _bounds.width() / 2, 0, SHERLOCK_SCENE_WIDTH - _bounds.width());
246 int ys = CLIP(mousePos.y - _bounds.height(), 0, SHERLOCK_SCREEN_HEIGHT - _bounds.height());
247 _bounds.moveTo(xs, ys);
248 }
249
250 /*----------------------------------------------------------------*/
251
WidgetInventoryVerbs(SherlockEngine * vm,WidgetInventory * owner)252 WidgetInventoryVerbs::WidgetInventoryVerbs(SherlockEngine *vm, WidgetInventory *owner) :
253 WidgetBase(vm), _owner(owner) {
254 _invVerbSelect = _oldInvVerbSelect = -1;
255 }
256
load()257 void WidgetInventoryVerbs::load() {
258 Events &events = *_vm->_events;
259 Inventory &inv = *_vm->_inventory;
260 People &people = *_vm->_people;
261 Scene &scene = *_vm->_scene;
262 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
263 Common::Point mousePos = events.mousePos();
264
265 // Make the Verb List for this Inventory Item
266 _inventCommands.clear();
267 _inventCommands.push_back(FIXED(Look));
268
269 // Default the Action word to "with"
270 _owner->_action = _vm->getLanguage() == Common::GR_GRE ? "" : FIXED(With);
271
272 // Search all the bgshapes for any matching Target Fields
273 for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
274 Object &obj = scene._bgShapes[idx];
275
276 if (obj._type != INVALID && obj._type != HIDDEN) {
277 for (int useNum = 0; useNum < 6; ++useNum) {
278 if (!obj._use[useNum]._verb.hasPrefix("*") &&
279 !obj._use[useNum]._target.compareToIgnoreCase(inv[_owner->_invSelect]._name)) {
280 // Make sure the Verb is not already in the list
281 bool found1 = false;
282 for (uint cmdNum = 0; cmdNum < _inventCommands.size() && !found1; ++cmdNum) {
283 if (!_inventCommands[cmdNum].compareToIgnoreCase(obj._use[useNum]._verb))
284 found1 = true;
285 }
286
287 if (!found1) {
288 _inventCommands.push_back(obj._use[useNum]._verb);
289
290 // Check for any Special Action commands
291 for (int nameNum = 0; nameNum < 4; ++nameNum) {
292 if (!scumm_strnicmp(obj._use[useNum]._names[nameNum].c_str(), "*V", 2)) {
293 if (!scumm_strnicmp(obj._use[useNum]._names[nameNum].c_str(), "*VSWAP", 6))
294 _owner->_swapItems = true;
295 else
296 _owner->_action = Common::String(obj._use[useNum]._names[nameNum].c_str() + 2);
297 }
298 }
299 }
300 }
301 }
302 }
303 }
304
305 // Search the NPCs for matches as well
306 for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
307 for (int useNum = 0; useNum < 2; ++useNum) {
308 if (!people[idx]._use[useNum]._target.compareToIgnoreCase(inv[_owner->_invSelect]._name) &&
309 !people[idx]._use[useNum]._verb.empty() && !people[idx]._use[useNum]._verb.hasPrefix(" ")) {
310 bool found1 = false;
311 for (uint cmdNum = 0; cmdNum < _inventCommands.size() && !found1; ++cmdNum) {
312 if (!_inventCommands[cmdNum].compareToIgnoreCase(people[idx]._use[cmdNum]._verb))
313 found1 = true;
314 }
315
316 if (!found1)
317 _inventCommands.push_back(people[idx]._use[useNum]._verb);
318 }
319 }
320 }
321
322 // Finally see if the item itself has a verb
323 if (!inv[_owner->_invSelect]._verb._verb.empty()) {
324 // Don't add "Solve" to the Foolscap if it's already been "Solved"
325 if (inv[_owner->_invSelect]._verb._verb.compareToIgnoreCase(FIXED(Solve)) || !_vm->readFlags(299))
326 _inventCommands.push_back(inv[_owner->_invSelect]._verb._verb);
327 }
328
329 // Now find the widest command in the _inventCommands array
330 int width = 0;
331 for (uint idx = 0; idx < _inventCommands.size(); ++idx)
332 width = MAX(width, _surface.stringWidth(_inventCommands[idx]));
333
334 // Set up bounds for the menu
335 _bounds = Common::Rect(width + _surface.widestChar() * 2 + 6,
336 (_surface.fontHeight() + 7) * _inventCommands.size() + 3);
337 _bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
338
339 // Create the surface
340 _surface.create(_bounds.width(), _bounds.height());
341 _surface.clear(TRANSPARENCY);
342 makeInfoArea();
343
344 // Draw the Verb commands and the lines separating them
345 ImageFile &images = *ui._interfaceImages;
346 for (int idx = 0; idx < (int)_inventCommands.size(); ++idx) {
347 _surface.writeString(_inventCommands[idx], Common::Point((_bounds.width() -
348 _surface.stringWidth(_inventCommands[idx])) / 2, (_surface.fontHeight() + 7) * idx + 5), INFO_TOP);
349
350 if (idx < (int)_inventCommands.size() - 1) {
351 _surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1), _bounds.right - 4, INFO_TOP);
352 _surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.right - 4, INFO_MIDDLE);
353 _surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.right - 4, INFO_BOTTOM);
354
355 _surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1)));
356 _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
357 (_surface.fontHeight() + 7) * (idx + 1) - 1));
358 }
359 }
360
361 summonWindow();
362 }
363
handleEvents()364 void WidgetInventoryVerbs::handleEvents() {
365 Events &events = *_vm->_events;
366 Inventory &inv = *_vm->_inventory;
367 TattooScene &scene = *(TattooScene *)_vm->_scene;
368 Common::Point mousePos = events.mousePos();
369 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
370 TattooEngine &vm = *(TattooEngine *)_vm;
371
372 // Handle changing highlighted verb entry
373 highlightControls();
374
375 // See if they want to close the menu (by clicking outside the menu)
376 Common::Rect innerBounds = _bounds;
377 innerBounds.grow(-3);
378
379 // Flag is they are pressing outside of the menu
380 if (!innerBounds.contains(mousePos))
381 _outsideMenu = true;
382
383 if (events._released || events._rightReleased || ui._keyState.keycode == Common::KEYCODE_ESCAPE) {
384 ui._scrollHighlight = SH_NONE;
385 banishWindow();
386
387 if (_outsideMenu || ui._keyState.keycode == Common::KEYCODE_ESCAPE) {
388 _owner->_invVerbMode = 0;
389 } else if (innerBounds.contains(mousePos)) {
390 _outsideMenu = false;
391
392 // Check if they are trying to solve the Foolscap puzzle, or looking at the completed puzzle
393 bool doFoolscap = !inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) &&
394 !_inventCommands[_invVerbSelect].compareToIgnoreCase(FIXED(Solve));
395 doFoolscap |= (!inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) || !inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv7)))
396 && !_inventCommands[_invVerbSelect].compareToIgnoreCase(FIXED(Look)) && vm.readFlags(299);
397
398 if (doFoolscap) {
399 // Close the entire Inventory and return to Standard Mode
400 _owner->_invVerbMode = 0;
401
402 _owner->_tooltipWidget.banishWindow();
403 _owner->banishWindow();
404 inv.freeInv();
405
406 events.clearEvents();
407 vm.doFoolscapPuzzle();
408 } else if (_invVerbSelect == 0) {
409 // They have released the mouse on the Look Verb command, so Look at the inventory item
410 ui._invLookFlag = true;
411 inv.freeInv();
412 ui._windowOpen = false;
413 ui._lookPos = mousePos;
414 ui.printObjectDesc(inv[_owner->_invSelect]._examine, true);
415 } else {
416 _owner->_invVerbMode = 3;
417 ui._oldBgFound = -1;
418
419 // See if the selected Verb with the selected Iventory Item, is to be used by itself
420 if (!_inventCommands[_invVerbSelect].compareToIgnoreCase(inv[_owner->_invSelect]._verb._verb) ||
421 !inv[_owner->_invSelect]._verb._target.compareToIgnoreCase("*SELF")) {
422 inv.freeInv();
423
424 ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
425 events.clearEvents();
426 ui.checkAction(inv[_owner->_invSelect]._verb, 2000);
427 } else {
428 _owner->_verb = _inventCommands[_invVerbSelect];
429 }
430
431 // If we are still in Inventory Mode, setup the graphic to float in front of the mouse cursor
432 if (ui._menuMode == INV_MODE) {
433 // Add the inventory item to the cursor
434 ImageFrame &imgFrame = (*inv._invShapes[_owner->_invSelect - inv._invIndex])[0];
435 events.setCursor(ARROW, Common::Point(-100, imgFrame._height), imgFrame._frame);
436
437 // Close the inventory dialog without banishing it, so it can keep getting events
438 // to handle tooltips and actually making the selection of what object to use them item on
439 inv.freeInv();
440 _owner->_surface.free();
441 }
442 }
443 }
444 }
445 }
446
highlightControls()447 void WidgetInventoryVerbs::highlightControls() {
448 Events &events = *_vm->_events;
449 Common::Point mousePos = events.mousePos();
450
451 Common::Rect innerBounds = _bounds;
452 innerBounds.grow(-3);
453
454 // Set the highlighted verb
455 _invVerbSelect = -1;
456 if (innerBounds.contains(mousePos))
457 _invVerbSelect = (mousePos.y - _bounds.top - 3) / (_surface.fontHeight() + 7);
458
459 // See if the highlighted verb has changed
460 if (_invVerbSelect != _oldInvVerbSelect) {
461 // Draw the list again, with the new highlighting
462 for (int idx = 0; idx < (int)_inventCommands.size(); ++idx) {
463 byte color = (idx == _invVerbSelect) ? COMMAND_HIGHLIGHTED : INFO_TOP;
464 _surface.writeString(_inventCommands[idx], Common::Point(
465 (_bounds.width() - _surface.stringWidth(_inventCommands[idx])) / 2,
466 (_surface.fontHeight() + 7) * idx + 5), color);
467 }
468
469 _oldInvVerbSelect = _invVerbSelect;
470 }
471 }
472
473 /*----------------------------------------------------------------*/
474
WidgetInventory(SherlockEngine * vm)475 WidgetInventory::WidgetInventory(SherlockEngine *vm) : WidgetBase(vm),
476 _tooltipWidget(vm, this), _verbList(vm, this) {
477 _invMode = 0;
478 _invVerbMode = 0;
479 _invSelect = _oldInvSelect = -1;
480 _selector = _oldSelector = -1;
481 _swapItems = false;
482 }
483
load(int mode)484 void WidgetInventory::load(int mode) {
485 Events &events = *_vm->_events;
486 Inventory &inv = *_vm->_inventory;
487 Screen &screen = *_vm->_screen;
488 Common::Point mousePos = events.mousePos();
489
490 if (mode == 3) {
491 mode = 2;
492 mousePos = Common::Point(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2);
493 }
494
495 if (mode != 0)
496 _invMode = mode;
497 _invVerbMode = 0;
498 _invSelect = _oldInvSelect = -1;
499 _selector = _oldSelector = -1;
500 _scroll = true;
501
502 if (mode == 0) {
503 banishWindow();
504 } else {
505 _bounds = Common::Rect((INVENTORY_XSIZE + 3) * NUM_INVENTORY_SHOWN / 2 + BUTTON_SIZE + 6,
506 (INVENTORY_YSIZE + 3) * 2 + 3);
507 _bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
508 }
509
510 // Ensure menu will be on-screen
511 restrictToScreen();
512
513 // Load the inventory data
514 inv.loadInv();
515
516 // Redraw the inventory menu on the widget surface
517 _surface.create(_bounds.width(), _bounds.height());
518 _surface.clear(TRANSPARENCY);
519
520 // Draw the window background and then the inventory on top of it
521 makeInfoArea(_surface);
522 drawBars();
523 drawInventory();
524 }
525
drawBars()526 void WidgetInventory::drawBars() {
527 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
528 ImageFile &images = *ui._interfaceImages;
529 int x;
530
531 _surface.hLine(3, INVENTORY_YSIZE + 3, _bounds.width() - 4, INFO_TOP);
532 _surface.hLine(3, INVENTORY_YSIZE + 4, _bounds.width() - 4, INFO_MIDDLE);
533 _surface.hLine(3, INVENTORY_YSIZE + 5, _bounds.width() - 4, INFO_BOTTOM);
534 _surface.SHtransBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2));
535
536 for (int idx = 1; idx <= NUM_INVENTORY_SHOWN / 2; ++idx) {
537 x = idx * (INVENTORY_XSIZE + 3);
538
539 _surface.vLine(x, 3, _bounds.height() - 4, INFO_TOP);
540 _surface.vLine(x + 1, 3, _bounds.height() - 4, INFO_MIDDLE);
541 _surface.vLine(x + 2, 3, _bounds.height() - 4, INFO_BOTTOM);
542
543 _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, 1));
544 _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4));
545 _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5));
546 _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
547 }
548
549 _surface.vLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
550 }
551
drawInventory()552 void WidgetInventory::drawInventory() {
553 Inventory &inv = *_vm->_inventory;
554
555 // TODO: Refactor _invIndex into this widget class
556 for (int idx = 0, itemId = inv._invIndex; idx < NUM_INVENTORY_SHOWN; ++idx, ++itemId) {
557 // Figure out the drawing position
558 Common::Point pt(3 + (INVENTORY_XSIZE + 3) * (idx % (NUM_INVENTORY_SHOWN / 2)),
559 3 + (INVENTORY_YSIZE + 3) * (idx / (NUM_INVENTORY_SHOWN / 2)));
560
561 // Draw the box to serve as the background for the item
562 _surface.hLine(pt.x + 1, pt.y, pt.x + INVENTORY_XSIZE - 2, TRANSPARENCY);
563 _surface.fillRect(Common::Rect(pt.x, pt.y + 1, pt.x + INVENTORY_XSIZE, pt.y + INVENTORY_YSIZE - 1), TRANSPARENCY);
564 _surface.hLine(pt.x + 1, pt.y + INVENTORY_YSIZE - 1, pt.x + INVENTORY_XSIZE - 2, TRANSPARENCY);
565
566 // Draw the item
567 if (itemId < inv._holdings) {
568 ImageFrame &img = (*inv._invShapes[idx])[0];
569 _surface.SHtransBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2,
570 pt.y + (INVENTORY_YSIZE - img._height) / 2));
571 }
572 }
573
574 drawScrollBar(inv._invIndex / NUM_INV_PER_LINE, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
575 (inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
576 }
577
handleEvents()578 void WidgetInventory::handleEvents() {
579 TattooEngine &vm = *(TattooEngine *)_vm;
580 Events &events = *_vm->_events;
581 Inventory &inv = *_vm->_inventory;
582 People &people = *_vm->_people;
583 TattooScene &scene = *(TattooScene *)_vm->_scene;
584 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
585 Common::Point mousePos = events.mousePos();
586
587 if (_invVerbMode == 1) {
588 checkTabbingKeys(MAX_INV_COMMANDS);
589 } else if (_invVerbMode == 0) {
590 checkInvTabbingKeys();
591
592 // Handle scrollbar events
593 int oldScrollIndex = inv._invIndex / NUM_INV_PER_LINE;
594 int invIndex = inv._invIndex / NUM_INV_PER_LINE;
595
596 ScrollHighlight oldHighlight = ui._scrollHighlight;
597 handleScrollbarEvents(invIndex, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
598 (inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
599
600 handleScrolling(invIndex, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
601 (inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
602
603 if (oldScrollIndex != invIndex) {
604 // Starting visible item index has changed, so set the index and reload inventory graphics
605 inv._invIndex = invIndex * NUM_INV_PER_LINE;
606 inv.freeGraphics();
607 inv.loadGraphics();
608 }
609
610 if (ui._scrollHighlight != oldHighlight || oldScrollIndex != invIndex) {
611 drawInventory();
612 return;
613 }
614 }
615
616 if (_invVerbMode != 1)
617 _tooltipWidget.handleEvents();
618
619 // Flag is they started pressing outside of the menu
620 if (events._firstPress && !_bounds.contains(mousePos))
621 _outsideMenu = true;
622
623 if (_invVerbMode != 3)
624 highlightControls();
625
626 // See if they released a mouse button button
627 if (events._released || events._rightReleased || ui._keyState.keycode == Common::KEYCODE_ESCAPE) {
628 ui._scrollHighlight = SH_NONE;
629
630 // See if they have a Verb List open for an Inventry Item
631 if (_invVerbMode == 1)
632 return;
633
634 if (_invVerbMode == 3) {
635 // Selecting object after inventory verb has been selected
636 _tooltipWidget.banishWindow();
637 close();
638
639 if (ui._keyState.keycode != Common::KEYCODE_ESCAPE) {
640 // If user pointed at an item, use the selected inventory item with this item
641 bool found = false;
642 if (ui._bgFound != -1) {
643 if (ui._personFound) {
644 Person &person = people[ui._bgFound - 1000];
645
646 for (int idx = 0; idx < 2; ++idx) {
647 if (!person._use[idx]._verb.compareToIgnoreCase(_verb) &&
648 !person._use[idx]._target.compareToIgnoreCase(_invTarget)) {
649 ui.checkAction(person._use[idx], ui._bgFound);
650 found = true;
651 }
652 }
653 } else {
654 for (int idx = 0; idx < 6; ++idx) {
655 if (!ui._bgShape->_use[idx]._verb.compareToIgnoreCase(_verb) &&
656 !ui._bgShape->_use[idx]._target.compareToIgnoreCase(_invTarget)) {
657 ui.checkAction(ui._bgShape->_use[idx], ui._bgFound);
658 found = true;
659 }
660 }
661 }
662 }
663
664 if (!found)
665 ui.putMessage("%s", FIXED(NoEffect));
666 }
667 } else if ((_outsideMenu && !_bounds.contains(mousePos)) || ui._keyState.keycode == Common::KEYCODE_ESCAPE) {
668 // Want to close the window (clicked outside of it). So close the window and return to Standard
669 close();
670
671 } else if (_bounds.contains(mousePos)) {
672 // Mouse button was released inside the inventory window
673 _outsideMenu = false;
674
675 // See if they are pointing at one of the inventory items
676 if (_invSelect != -1) {
677 // See if they are in Use Obj with Inv. Mode (they right clicked on an item
678 // in the room and selected "Use with Inv.")
679 if (_invMode == 1) {
680 _tooltipWidget.banishWindow();
681 banishWindow();
682
683 // See if the item in the room that they started with was a person
684 bool found = false;
685 if (ui._activeObj >= 1000) {
686 // Object was a person, activate anything in his two verb fields
687 for (int idx = 0; idx < 2; ++idx) {
688 if (!people[ui._activeObj - 1000]._use[idx]._target.compareToIgnoreCase(inv[_invSelect]._name)) {
689 ui.checkAction(people[ui._activeObj - 1000]._use[idx], ui._activeObj);
690 found = true;
691 }
692 }
693 } else {
694 // Object was a regular object, activate anything in its verb fields
695 for (int idx = 0; idx < 6; ++idx) {
696 if (!scene._bgShapes[ui._activeObj]._use[idx]._target.compareToIgnoreCase(inv[_invSelect]._name)) {
697 ui.checkAction(scene._bgShapes[ui._activeObj]._use[idx], ui._activeObj);
698 found = true;
699 }
700 }
701 }
702 if (!found)
703 ui.putMessage("%s", FIXED(NoEffect));
704
705 } else {
706 // See if they right clicked on an item
707 if (events._rightReleased) {
708 _invVerbMode = 1;
709 _verbList._oldInvVerbSelect = -1;
710 _tooltipWidget.banishWindow();
711
712 // Keep track of the name of the inventory object so we can check it against the target fields
713 // of verbs when we activate it
714 _invTarget = inv[_invSelect]._name;
715 _swapItems = false;
716
717 _verbList.load();
718 } else {
719 // They left clicked on an inventory item, so Look at it
720
721 // Check if they are looking at the solved Foolscap
722 if ((!inv[_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) || !inv[_invSelect]._name.compareToIgnoreCase(FIXED(Inv7)))
723 && vm.readFlags(299)) {
724 banishWindow();
725 _tooltipWidget.erase();
726
727 _invVerbMode = 0;
728 inv.freeInv();
729
730 events.clearEvents();
731 events.setCursor(ARROW);
732 ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
733
734 scene.doBgAnim();
735 vm.doFoolscapPuzzle();
736 } else {
737 ui._invLookFlag = true;
738 inv.freeInv();
739
740 _tooltipWidget.banishWindow();
741 ui._windowOpen = false;
742 ui._lookPos = mousePos;
743 ui.printObjectDesc(inv[_invSelect]._examine, true);
744 }
745 }
746 }
747 }
748 }
749 }
750 }
751
checkInvTabbingKeys()752 void WidgetInventory::checkInvTabbingKeys() {
753 }
754
highlightControls()755 void WidgetInventory::highlightControls() {
756 // TODO
757 }
758
banishWindow()759 void WidgetInventory::banishWindow() {
760 WidgetBase::banishWindow();
761
762 _verbList.banishWindow();
763 }
764
draw()765 void WidgetInventory::draw() {
766 WidgetBase::draw();
767 _tooltipWidget.draw();
768 }
769
erase()770 void WidgetInventory::erase() {
771 WidgetBase::erase();
772 _tooltipWidget.erase();
773 }
774
close()775 void WidgetInventory::close() {
776 Events &events = *_vm->_events;
777 Inventory &inv = *_vm->_inventory;
778 TattooScene &scene = *(TattooScene *)_vm->_scene;
779 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
780
781 banishWindow();
782 inv.freeInv();
783 events.clearEvents();
784
785 events.setCursor(ARROW);
786 ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
787 }
788
789 } // End of namespace Tattoo
790
791 } // End of namespace Sherlock
792