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