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