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 /*
24 * This code is based on original Tony Tough source code
25 *
26 * Copyright (c) 1997-2003 Nayma Software
27 */
28
29 #include "common/textconsole.h"
30 #include "tony/mpal/mpalutils.h"
31 #include "tony/inventory.h"
32 #include "tony/game.h"
33 #include "tony/tony.h"
34
35 namespace Tony {
36
37 /****************************************************************************\
38 * RMInventory Methods
39 \****************************************************************************/
40
RMInventory()41 RMInventory::RMInventory() {
42 _items = NULL;
43 _state = CLOSED;
44 _bCombining = false;
45 _nItems = 0;
46
47 Common::fill(_inv, _inv + 256, 0);
48 _nInv = 0;
49 _curPutY = 0;
50 _curPutTime = 0;
51 _curPos = 0;
52 _bHasFocus = false;
53 _nSelectObj = 0;
54 _nCombine = 0;
55 _bBlinkingRight = false;
56 _bBlinkingLeft = false;
57 _miniAction = 0;
58 }
59
~RMInventory()60 RMInventory::~RMInventory() {
61 close();
62 }
63
checkPointInside(const RMPoint & pt)64 bool RMInventory::checkPointInside(const RMPoint &pt) {
65 if (!GLOBALS._bCfgInvUp)
66 return pt._y > RM_SY - 70;
67 else
68 return pt._y < 70;
69 }
70
init()71 void RMInventory::init() {
72 // Create the main buffer
73 create(RM_SX, 68);
74 setPriority(185);
75
76 // Setup the inventory
77 _nInv = 0;
78 _curPos = 0;
79 _bCombining = false;
80
81 // New items
82 _nItems = 78; // @@@ Number of takeable items
83 _items = new RMInventoryItem[_nItems + 1];
84
85 int curres = 10500;
86
87 // Loop through the items
88 for (int i = 0; i <= _nItems; i++) {
89 // Load the items from the resource
90 RMRes res(curres);
91 assert(res.isValid());
92 Common::SeekableReadStream *ds = res.getReadStream();
93
94 // Initialize the MPAL inventory item by reading it in.
95 _items[i]._icon.setInitCurPattern(false);
96 _items[i]._icon.readFromStream(*ds);
97 delete ds;
98
99 // Puts in the default pattern 1
100 _items[i]._pointer = NULL;
101 _items[i]._status = 1;
102 _items[i]._icon.setPattern(1);
103 _items[i]._icon.doFrame(this, false);
104
105 curres++;
106 if (i == 0 || i == 28 || i == 29)
107 continue;
108
109 _items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()];
110
111 for (int j = 0; j < _items[i]._icon.numPattern(); j++) {
112 RMResRaw raw(curres);
113
114 assert(raw.isValid());
115
116 _items[i]._pointer[j].init((const byte *)raw, raw.width(), raw.height(), true);
117 curres++;
118 }
119 }
120
121 _items[28]._icon.setPattern(1);
122 _items[29]._icon.setPattern(1);
123
124 // Download interface
125 RMRes res(RES_I_MINIINTER);
126 assert(res.isValid());
127 Common::SeekableReadStream *ds = res.getReadStream();
128 _miniInterface.readFromStream(*ds);
129 _miniInterface.setPattern(1);
130 delete ds;
131
132 // Create the text for hints on the mini interface
133 _hints[0].setAlignType(RMText::HCENTER, RMText::VTOP);
134 _hints[1].setAlignType(RMText::HCENTER, RMText::VTOP);
135 _hints[2].setAlignType(RMText::HCENTER, RMText::VTOP);
136
137 // The text is taken from MPAL for translation
138 RMMessage msg1(15);
139 RMMessage msg2(13);
140 RMMessage msg3(14);
141
142 _hints[0].writeText(msg1[0], 1); // Examine
143 _hints[1].writeText(msg2[0], 1); // Take
144 _hints[2].writeText(msg3[0], 1); // Use
145
146 // Prepare initial inventory
147 prepare();
148 drawOT(Common::nullContext);
149 clearOT();
150 }
151
close()152 void RMInventory::close() {
153 // Has memory
154 if (_items != NULL) {
155 // Delete the item pointers
156 for (int i = 0; i <= _nItems; i++)
157 delete[] _items[i]._pointer;
158
159 // Delete the items array
160 delete[] _items;
161 _items = NULL;
162 }
163
164 destroy();
165 }
166
reset()167 void RMInventory::reset() {
168 _state = CLOSED;
169 endCombine();
170 }
171
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)172 void RMInventory::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
173 CORO_BEGIN_CONTEXT;
174 RMPoint pos;
175 RMPoint pos2;
176 RMGfxPrimitive *p;
177 RMGfxPrimitive *p2;
178 CORO_END_CONTEXT(_ctx);
179
180 CORO_BEGIN_CODE(_ctx);
181
182 prim->setDst(RMPoint(0, _curPutY));
183 _csModifyInterface.lock();
184 CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
185 _csModifyInterface.unlock();
186
187 if (_state == SELECTING) {
188
189 if (!GLOBALS._bCfgInvUp) {
190 _ctx->pos.set((_nSelectObj + 1) * 64 - 20, RM_SY - 113);
191 _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, RM_SY - 150);
192 } else {
193 _ctx->pos.set((_nSelectObj + 1) * 64 - 20, 72 - 4); // The brown part is at the top :(
194 _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, 119 - 4);
195 }
196
197 _ctx->p = new RMGfxPrimitive(prim->_task, _ctx->pos);
198 _ctx->p2 = new RMGfxPrimitive(prim->_task, _ctx->pos2);
199
200 // Draw the mini interface
201 CORO_INVOKE_2(_miniInterface.draw, bigBuf, _ctx->p);
202
203 if (GLOBALS._bCfgInterTips) {
204 if (_miniAction == 1) // Examine
205 CORO_INVOKE_2(_hints[0].draw, bigBuf, _ctx->p2);
206 else if (_miniAction == 2) // Talk
207 CORO_INVOKE_2(_hints[1].draw, bigBuf, _ctx->p2);
208 else if (_miniAction == 3) // Use
209 CORO_INVOKE_2(_hints[2].draw, bigBuf, _ctx->p2);
210 }
211
212 delete _ctx->p;
213 delete _ctx->p2;
214 }
215
216 CORO_END_CODE;
217 }
218
removeThis(CORO_PARAM,bool & result)219 void RMInventory::removeThis(CORO_PARAM, bool &result) {
220 if (_state == CLOSED)
221 result = true;
222 else
223 result = false;
224 }
225
removeItem(int code)226 void RMInventory::removeItem(int code) {
227 for (int i = 0; i < _nInv; i++) {
228 if (_inv[i] == code - 10000) {
229 _csModifyInterface.lock();
230
231 Common::copy(&_inv[i + 1], &_inv[i + 1] + (_nInv - i), &_inv[i]);
232 _nInv--;
233
234 prepare();
235 drawOT(Common::nullContext);
236 clearOT();
237 _csModifyInterface.unlock();
238 return;
239 }
240 }
241 }
242
addItem(int code)243 void RMInventory::addItem(int code) {
244 if (code <= 10000 || code >= 10101) {
245 // If we are here, it means that we are adding an item that should not be in the inventory
246 warning("RMInventory::addItem(%d) - Cannot find a valid icon for this item, and then it will not be added to the inventory", code);
247 } else {
248 _csModifyInterface.lock();
249 if (_curPos + 8 == _nInv) {
250 // Break through the inventory! On the flashing pattern
251 _items[28]._icon.setPattern(2);
252 }
253
254 _inv[_nInv++] = code - 10000;
255
256 prepare();
257 drawOT(Common::nullContext);
258 clearOT();
259 _csModifyInterface.unlock();
260 }
261 }
262
changeItemStatus(uint32 code,uint32 dwStatus)263 void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) {
264 if (code <= 10000 || code >= 10101) {
265 error("RMInventory::changeItemStatus(%d) - Specified object code is not valid", code);
266 } else {
267 _csModifyInterface.lock();
268 _items[code - 10000]._icon.setPattern(dwStatus);
269 _items[code - 10000]._status = dwStatus;
270
271 prepare();
272 drawOT(Common::nullContext);
273 clearOT();
274 _csModifyInterface.unlock();
275 }
276 }
277
prepare()278 void RMInventory::prepare() {
279 for (int i = 1; i < RM_SX / 64 - 1; i++) {
280 if (i - 1 + _curPos < _nInv)
281 addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0)));
282 else
283 addPrim(new RMGfxPrimitive(&_items[0]._icon, RMPoint(i * 64, 0)));
284 }
285
286 // Frecce
287 addPrim(new RMGfxPrimitive(&_items[29]._icon, RMPoint(0, 0)));
288 addPrim(new RMGfxPrimitive(&_items[28]._icon, RMPoint(640 - 64, 0)));
289 }
290
miniActive()291 bool RMInventory::miniActive() {
292 return _state == SELECTING;
293 }
294
haveFocus(const RMPoint & mpos)295 bool RMInventory::haveFocus(const RMPoint &mpos) {
296 // When we combine, have the focus only if we are on an arrow (to scroll)
297 if (_state == OPENED && _bCombining && checkPointInside(mpos) && (mpos._x < 64 || mpos._x > RM_SX - 64))
298 return true;
299
300 // If the inventory is open, focus we we go over it
301 if (_state == OPENED && !_bCombining && checkPointInside(mpos))
302 return true;
303
304 // If we are selecting a verb (and then right down), we always focus
305 if (_state == SELECTING)
306 return true;
307
308 return false;
309 }
310
endCombine()311 void RMInventory::endCombine() {
312 _bCombining = false;
313 }
314
leftClick(const RMPoint & mpos,int & nCombineObj)315 bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) {
316 // The left click picks an item from your inventory to use it with the background
317 int n = mpos._x / 64;
318
319 if (_state == OPENED) {
320 if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
321 _bCombining = true; //m_state = COMBINING;
322 _nCombine = _inv[n - 1 + _curPos];
323 nCombineObj = _nCombine + 10000;
324
325 g_vm->playUtilSFX(1);
326 return true;
327 }
328 }
329
330 // Click the right arrow
331 if ((_state == OPENED) && _bBlinkingRight) {
332 _csModifyInterface.lock();
333 _curPos++;
334
335 if (_curPos + 8 >= _nInv) {
336 _bBlinkingRight = false;
337 _items[28]._icon.setPattern(1);
338 }
339
340 if (_curPos > 0) {
341 _bBlinkingLeft = true;
342 _items[29]._icon.setPattern(2);
343 }
344
345 prepare();
346 drawOT(Common::nullContext);
347 clearOT();
348 _csModifyInterface.unlock();
349 }
350
351 // Click the left arrow
352 else if ((_state == OPENED) && _bBlinkingLeft) {
353 assert(_curPos > 0);
354 _csModifyInterface.lock();
355 _curPos--;
356
357 if (_curPos == 0) {
358 _bBlinkingLeft = false;
359 _items[29]._icon.setPattern(1);
360 }
361
362 if (_curPos + 8 < _nInv) {
363 _bBlinkingRight = true;
364 _items[28]._icon.setPattern(2);
365 }
366
367 prepare();
368 drawOT(Common::nullContext);
369 clearOT();
370 _csModifyInterface.unlock();
371 }
372
373 return false;
374 }
375
rightClick(const RMPoint & mpos)376 void RMInventory::rightClick(const RMPoint &mpos) {
377 assert(checkPointInside(mpos));
378
379 if (_state == OPENED && !_bCombining) {
380 // Open the context interface
381 int n = mpos._x / 64;
382
383 if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) {
384 _state = SELECTING;
385 _miniAction = 0;
386 _nSelectObj = n - 1;
387
388 g_vm->playUtilSFX(0);
389 }
390 }
391
392 if ((_state == OPENED) && _bBlinkingRight) {
393 _csModifyInterface.lock();
394 _curPos += 7;
395 if (_curPos + 8 > _nInv)
396 _curPos = _nInv - 8;
397
398 if (_curPos + 8 <= _nInv) {
399 _bBlinkingRight = false;
400 _items[28]._icon.setPattern(1);
401 }
402
403 if (_curPos > 0) {
404 _bBlinkingLeft = true;
405 _items[29]._icon.setPattern(2);
406 }
407
408 prepare();
409 drawOT(Common::nullContext);
410 clearOT();
411 _csModifyInterface.unlock();
412 } else if ((_state == OPENED) && _bBlinkingLeft) {
413 assert(_curPos > 0);
414 _csModifyInterface.lock();
415 _curPos -= 7;
416 if (_curPos < 0)
417 _curPos = 0;
418
419 if (_curPos == 0) {
420 _bBlinkingLeft = false;
421 _items[29]._icon.setPattern(1);
422 }
423
424 if (_curPos + 8 < _nInv) {
425 _bBlinkingRight = true;
426 _items[28]._icon.setPattern(2);
427 }
428
429 prepare();
430 drawOT(Common::nullContext);
431 clearOT();
432 _csModifyInterface.unlock();
433 }
434 }
435
rightRelease(const RMPoint & mpos,RMTonyAction & curAction)436 bool RMInventory::rightRelease(const RMPoint &mpos, RMTonyAction &curAction) {
437 if (_state == SELECTING) {
438 _state = OPENED;
439
440 if (_miniAction == 1) { // Examine
441 curAction = TA_EXAMINE;
442 return true;
443 } else if (_miniAction == 2) { // Talk
444 curAction = TA_TALK;
445 return true;
446 } else if (_miniAction == 3) { // Use
447 curAction = TA_USE;
448 return true;
449 }
450 }
451
452 return false;
453 }
454
455 #define INVSPEED 20
456
doFrame(RMGfxTargetBuffer & bigBuf,RMPointer & ptr,RMPoint mpos,bool bCanOpen)457 void RMInventory::doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen) {
458 if (_state != CLOSED) {
459 // Clean up the OT list
460 _csModifyInterface.lock();
461 clearOT();
462
463 // DoFrame makes all the objects currently in the inventory be displayed
464 // @@@ Maybe we should do all takeable objects? Please does not help
465 bool bNeedRedraw = false;
466
467 for (int i = 0; i < _nInv; i++) {
468 if (_items[_inv[i]]._icon.doFrame(this, false) && (i >= _curPos && i <= _curPos + 7))
469 bNeedRedraw = true;
470 }
471
472 if ((_state == CLOSING || _state == OPENING || _state == OPENED) && checkPointInside(mpos)) {
473 if (mpos._x > RM_SX - 64) {
474 if (_curPos + 8 < _nInv && !_bBlinkingRight) {
475 _items[28]._icon.setPattern(3);
476 _bBlinkingRight = true;
477 bNeedRedraw = true;
478 }
479 } else if (_bBlinkingRight) {
480 _items[28]._icon.setPattern(2);
481 _bBlinkingRight = false;
482 bNeedRedraw = true;
483 }
484
485 if (mpos._x < 64) {
486 if (_curPos > 0 && !_bBlinkingLeft) {
487 _items[29]._icon.setPattern(3);
488 _bBlinkingLeft = true;
489 bNeedRedraw = true;
490 }
491 } else if (_bBlinkingLeft) {
492 _items[29]._icon.setPattern(2);
493 _bBlinkingLeft = false;
494 bNeedRedraw = true;
495 }
496 }
497
498 if (_items[28]._icon.doFrame(this, false))
499 bNeedRedraw = true;
500
501 if (_items[29]._icon.doFrame(this, false))
502 bNeedRedraw = true;
503
504 if (bNeedRedraw)
505 prepare();
506
507 _csModifyInterface.unlock();
508 }
509
510 if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_i)) {
511 GLOBALS._bCfgInvLocked = !GLOBALS._bCfgInvLocked;
512 }
513
514 if (_bCombining) { // m_state == COMBINING)
515 ptr.setCustomPointer(&_items[_nCombine]._pointer[_items[_nCombine]._status - 1]);
516 ptr.setSpecialPointer(RMPointer::PTR_CUSTOM);
517 }
518
519 if (!GLOBALS._bCfgInvUp) {
520 if ((_state == CLOSED) && (mpos._y > RM_SY - 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
521 if (!GLOBALS._bCfgInvNoScroll) {
522 _state = OPENING;
523 _curPutY = RM_SY - 1;
524 _curPutTime = g_vm->getTime();
525 } else {
526 _state = OPENED;
527 _curPutY = RM_SY - 68;
528 }
529 } else if (_state == OPENED) {
530 if ((mpos._y < RM_SY - 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
531 if (!GLOBALS._bCfgInvNoScroll) {
532 _state = CLOSING;
533 _curPutY = RM_SY - 68;
534 _curPutTime = g_vm->getTime();
535 } else {
536 _state = CLOSED;
537 }
538 }
539 } else if (_state == OPENING) {
540 while (_curPutTime + INVSPEED < g_vm->getTime()) {
541 _curPutY -= 3;
542 _curPutTime += INVSPEED;
543 }
544
545 if (_curPutY <= RM_SY - 68) {
546 _state = OPENED;
547 _curPutY = RM_SY - 68;
548 }
549
550 } else if (_state == CLOSING) {
551 while (_curPutTime + INVSPEED < g_vm->getTime()) {
552 _curPutY += 3;
553 _curPutTime += INVSPEED;
554 }
555
556 if (_curPutY > 480)
557 _state = CLOSED;
558 }
559 } else {
560 if ((_state == CLOSED) && (mpos._y < 10 || GLOBALS._bCfgInvLocked) && bCanOpen) {
561 if (!GLOBALS._bCfgInvNoScroll) {
562 _state = OPENING;
563 _curPutY = - 68;
564 _curPutTime = g_vm->getTime();
565 } else {
566 _state = OPENED;
567 _curPutY = 0;
568 }
569 } else if (_state == OPENED) {
570 if ((mpos._y > 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) {
571 if (!GLOBALS._bCfgInvNoScroll) {
572 _state = CLOSING;
573 _curPutY = -2;
574 _curPutTime = g_vm->getTime();
575 } else {
576 _state = CLOSED;
577 }
578 }
579 } else if (_state == OPENING) {
580 while (_curPutTime + INVSPEED < g_vm->getTime()) {
581 _curPutY += 3;
582 _curPutTime += INVSPEED;
583 }
584
585 if (_curPutY >= 0) {
586 _state = OPENED;
587 _curPutY = 0;
588 }
589 } else if (_state == CLOSING) {
590 while (_curPutTime + INVSPEED < g_vm->getTime()) {
591 _curPutY -= 3;
592 _curPutTime += INVSPEED;
593 }
594
595 if (_curPutY < -68)
596 _state = CLOSED;
597 }
598 }
599
600 if (_state == SELECTING) {
601 int startx = (_nSelectObj + 1) * 64 - 20;
602 int starty;
603
604 if (!GLOBALS._bCfgInvUp)
605 starty = RM_SY - 109;
606 else
607 starty = 70;
608
609 // Make sure it is on one of the verbs
610 if (mpos._y > starty && mpos._y < starty + 45) {
611 if (mpos._x > startx && mpos._x < startx + 40) {
612 if (_miniAction != 1) {
613 _miniInterface.setPattern(2);
614 _miniAction = 1;
615 g_vm->playUtilSFX(1);
616 }
617 } else if (mpos._x >= startx + 40 && mpos._x < startx + 80) {
618 if (_miniAction != 2) {
619 _miniInterface.setPattern(3);
620 _miniAction = 2;
621 g_vm->playUtilSFX(1);
622 }
623 } else if (mpos._x >= startx + 80 && mpos._x < startx + 108) {
624 if (_miniAction != 3) {
625 _miniInterface.setPattern(4);
626 _miniAction = 3;
627 g_vm->playUtilSFX(1);
628 }
629 } else {
630 _miniInterface.setPattern(1);
631 _miniAction = 0;
632 }
633 } else {
634 _miniInterface.setPattern(1);
635 _miniAction = 0;
636 }
637
638 // Update the mini-interface
639 _miniInterface.doFrame(&bigBuf, false);
640 }
641
642 if ((_state != CLOSED) && !_nInList) {
643 bigBuf.addPrim(new RMGfxPrimitive(this));
644 }
645 }
646
itemInFocus(const RMPoint & mpt)647 bool RMInventory::itemInFocus(const RMPoint &mpt) {
648 if ((_state == OPENED || _state == OPENING) && checkPointInside(mpt))
649 return true;
650 else
651 return false;
652 }
653
whichItemIsIn(const RMPoint & mpt)654 RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) {
655 if (_state == OPENED) {
656 if (checkPointInside(mpt)) {
657 int n = mpt._x / 64;
658 if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0 && (!_bCombining || _inv[n - 1 + _curPos] != _nCombine))
659 return &_items[_inv[n - 1 + _curPos]]._icon;
660 }
661 }
662
663 return NULL;
664 }
665
getSaveStateSize()666 int RMInventory::getSaveStateSize() {
667 // m_inv pattern m_nInv
668 return 256 * 4 + 256 * 4 + 4;
669 }
670
saveState(byte * state)671 void RMInventory::saveState(byte *state) {
672 WRITE_LE_UINT32(state, _nInv);
673 state += 4;
674 for (int i = 0; i < 256; ++i) {
675 WRITE_LE_UINT32(state, _inv[i]);
676 state += 4;
677 }
678
679 int x;
680 for (int i = 0; i < 256; i++) {
681 if (i < _nItems)
682 x = _items[i]._status;
683 else
684 x = 0;
685
686 WRITE_LE_UINT32(state, x);
687 state += 4;
688 }
689 }
690
loadState(byte * state)691 int RMInventory::loadState(byte *state) {
692 _nInv = READ_LE_UINT32(state);
693 state += 4;
694 for (int i = 0; i < 256; ++i) {
695 _inv[i] = READ_LE_UINT32(state);
696 state += 4;
697 }
698
699 for (int i = 0; i < 256; i++) {
700 int x = READ_LE_UINT32(state);
701 state += 4;
702
703 if (i < _nItems) {
704 _items[i]._status = x;
705 _items[i]._icon.setPattern(x);
706 }
707 }
708
709 _curPos = 0;
710 _bCombining = false;
711
712 _items[29]._icon.setPattern(1);
713
714 if (_nInv > 8)
715 _items[28]._icon.setPattern(2);
716 else
717 _items[28]._icon.setPattern(1);
718
719 prepare();
720 drawOT(Common::nullContext);
721 clearOT();
722
723 return getSaveStateSize();
724 }
725
operator +=(RMItem * item)726 RMInventory &RMInventory::operator+=(RMItem *item) {
727 addItem(item->mpalCode());
728 return *this;
729 }
730
operator +=(RMItem & item)731 RMInventory &RMInventory::operator+=(RMItem &item) {
732 addItem(item.mpalCode());
733 return *this;
734 }
735
operator +=(int code)736 RMInventory &RMInventory::operator+=(int code) {
737 addItem(code);
738 return *this;
739 }
740
741 /****************************************************************************\
742 * RMInterface methods
743 \****************************************************************************/
744
RMInterface()745 RMInterface::RMInterface() : RMGfxSourceBuffer8RLEByte() {
746 _bActive = _bPerorate = false;
747 _lastHotZone = -1;
748 }
749
~RMInterface()750 RMInterface::~RMInterface() {
751 }
752
active()753 bool RMInterface::active() {
754 return _bActive;
755 }
756
onWhichBox(RMPoint pt)757 int RMInterface::onWhichBox(RMPoint pt) {
758 pt -= _openStart;
759
760 // Check how many verbs you have to consider
761 int max = 4;
762 if (_bPerorate)
763 max = 5;
764
765 // Find the verb
766 for (int i = 0; i < max; i++) {
767 if (_hotbbox[i].ptInRect(pt))
768 return i;
769 }
770
771 // Found no verb
772 return -1;
773 }
774
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)775 void RMInterface::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
776 CORO_BEGIN_CONTEXT;
777 int h;
778 CORO_END_CONTEXT(_ctx);
779
780 CORO_BEGIN_CODE(_ctx);
781
782 prim->getDst().topLeft() = _openStart;
783 CORO_INVOKE_2(RMGfxSourceBuffer8RLEByte::draw, bigBuf, prim);
784
785 // Check if there is a draw hot zone
786 _ctx->h = onWhichBox(_mpos);
787 if (_ctx->h != -1) {
788 prim->getDst().topLeft() = _openStart;
789 CORO_INVOKE_2(_hotzone[_ctx->h].draw, bigBuf, prim);
790
791 if (_lastHotZone != _ctx->h) {
792 _lastHotZone = _ctx->h;
793 g_vm->playUtilSFX(1);
794 }
795
796 if (GLOBALS._bCfgInterTips) {
797 prim->getDst().topLeft() = _openStart + RMPoint(70, 177);
798 CORO_INVOKE_2(_hints[_ctx->h].draw, bigBuf, prim);
799 }
800 } else
801 _lastHotZone = -1;
802
803 CORO_END_CODE;
804 }
805
doFrame(RMGfxTargetBuffer & bigBuf,RMPoint mousepos)806 void RMInterface::doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos) {
807 // If needed, add to the OT schedule list
808 if (!_nInList && _bActive)
809 bigBuf.addPrim(new RMGfxPrimitive(this));
810
811 _mpos = mousepos;
812 }
813
clicked(const RMPoint & mousepos)814 void RMInterface::clicked(const RMPoint &mousepos) {
815 _bActive = true;
816 _openPos = mousepos;
817
818 // Calculate the top left corner of the interface
819 _openStart = _openPos - RMPoint(_dimx / 2, _dimy / 2);
820 _lastHotZone = -1;
821
822 // Keep it inside the screen
823 if (_openStart._x < 0)
824 _openStart._x = 0;
825 if (_openStart._y < 0)
826 _openStart._y = 0;
827 if (_openStart._x + _dimx > RM_SX)
828 _openStart._x = RM_SX - _dimx;
829 if (_openStart._y + _dimy > RM_SY)
830 _openStart._y = RM_SY - _dimy;
831
832 // Play the sound effect
833 g_vm->playUtilSFX(0);
834 }
835
released(const RMPoint & mousepos,RMTonyAction & action)836 bool RMInterface::released(const RMPoint &mousepos, RMTonyAction &action) {
837 if (!_bActive)
838 return false;
839
840 _bActive = false;
841
842 switch (onWhichBox(mousepos)) {
843 case 0:
844 action = TA_TAKE;
845 break;
846
847 case 1:
848 action = TA_TALK;
849 break;
850
851 case 2:
852 action = TA_USE;
853 break;
854
855 case 3:
856 action = TA_EXAMINE;
857 break;
858
859 case 4:
860 action = TA_PERORATE;
861 break;
862
863 default: // No verb
864 return false;
865 }
866
867 return true;
868 }
869
reset()870 void RMInterface::reset() {
871 _bActive = false;
872 }
873
setPerorate(bool bOn)874 void RMInterface::setPerorate(bool bOn) {
875 _bPerorate = bOn;
876 }
877
getPerorate()878 bool RMInterface::getPerorate() {
879 return _bPerorate;
880 }
881
init()882 void RMInterface::init() {
883 RMResRaw inter(RES_I_INTERFACE);
884 RMRes pal(RES_I_INTERPPAL);
885
886 setPriority(191);
887
888 RMGfxSourceBuffer::init(inter, inter.width(), inter.height());
889 loadPaletteWA(RES_I_INTERPAL);
890
891 for (int i = 0; i < 5; i++) {
892 RMResRaw part(RES_I_INTERP1 + i);
893
894 _hotzone[i].init(part, part.width(), part.height());
895 _hotzone[i].loadPaletteWA(pal);
896 }
897
898 _hotbbox[0].setRect(126, 123, 159, 208); // Take
899 _hotbbox[1].setRect(90, 130, 125, 186); // About
900 _hotbbox[2].setRect(110, 60, 152, 125);
901 _hotbbox[3].setRect(56, 51, 93, 99);
902 _hotbbox[4].setRect(51, 105, 82, 172);
903
904 _hints[0].setAlignType(RMText::HRIGHT, RMText::VTOP);
905 _hints[1].setAlignType(RMText::HRIGHT, RMText::VTOP);
906 _hints[2].setAlignType(RMText::HRIGHT, RMText::VTOP);
907 _hints[3].setAlignType(RMText::HRIGHT, RMText::VTOP);
908 _hints[4].setAlignType(RMText::HRIGHT, RMText::VTOP);
909
910 // The text is taken from MPAL for translation
911 RMMessage msg0(12);
912 RMMessage msg1(13);
913 RMMessage msg2(14);
914 RMMessage msg3(15);
915 RMMessage msg4(16);
916
917 _hints[0].writeText(msg0[0], 1); // Take
918 _hints[1].writeText(msg1[0], 1); // Talk
919 _hints[2].writeText(msg2[0], 1); // Use
920 _hints[3].writeText(msg3[0], 1); // Examine
921 _hints[4].writeText(msg4[0], 1); // Show Yourself
922
923 _bActive = false;
924 _bPerorate = false;
925 _lastHotZone = 0;
926 }
927
close()928 void RMInterface::close() {
929 destroy();
930
931 for (int i = 0; i < 5; i++)
932 _hotzone[i].destroy();
933 }
934
935 } // End of namespace Tony
936