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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "saga2/saga2.h"
28 #include "saga2/objects.h"
29 #include "saga2/contain.h"
30 #include "saga2/grabinfo.h"
31 #include "saga2/motion.h"
32 #include "saga2/uimetrcs.h"
33 #include "saga2/localize.h"
34 #include "saga2/intrface.h"
35 #include "saga2/spellbuk.h"
36 #include "saga2/imagcach.h"
37 #include "saga2/hresmgr.h"
38 #include "saga2/fontlib.h"
39 
40 #include "saga2/pclass.r"
41 
42 namespace Saga2 {
43 
44 enum {
45 	kMaxOpenDistance = 32
46 };
47 
48 // selector image pointer
49 static void *selImage;
50 
51 /* ===================================================================== *
52    Imports
53  * ===================================================================== */
54 
55 extern ReadyContainerView   *TrioCviews[kNumViews];
56 extern ReadyContainerView   *indivCviewTop, *indivCviewBot;
57 extern SpriteSet    *objectSprites;        // object sprites
58 extern Alarm        containerObjTextAlarm;
59 extern bool         gameSetupComplete;
60 
61 extern hResContext  *imageRes;              // image resource handle
62 hResContext         *containerRes;          // image resource handle
63 
64 extern APPFUNC(cmdWindowFunc);
65 
66 //  Temporary...
67 void grabObject(ObjectID pickedObject);      // turn object into mouse ptr
68 void releaseObject(void);                    // restore mouse pointer
69 
70 /* Reference Types
71 ProtoObj::isTangible
72 ProtoObj::isContainer
73 ProtoObj::isBottle
74 ProtoObj::isFood
75 ProtoObj::isWearable
76 ProtoObj::isWeapon
77 ProtoObj::isDocument
78 ProtoObj::isIntangible
79 ProtoObj::isConcept
80 ProtoObj::isMemory
81 ProtoObj::isPsych
82 ProtoObj::isSpell
83 ProtoObj::isEnchantment
84 */
85 
86 // used to ignore doubleClick when doubleClick == singleClick
87 static bool alreadyDone;
88 //  ID of the last object that the mouse moved over
89 
90 /* ===================================================================== *
91    ContainerView member functions
92  * ===================================================================== */
93 
94 // static mouse info variables
95 ObjectID    ContainerView::lastPickedObjectID = Nothing;
96 int32       ContainerView::lastPickedObjectQuantity = - 1;
97 bool        ContainerView::objTextAlarm = false;
98 char        ContainerView::mouseText[ContainerView::bufSize] = { "" };
99 bool        ContainerView::mouseInView = false;
100 uint16      ContainerView::numPicked = 1;
101 GameObject  *ContainerView::objToGet;
102 int32       ContainerView::amountAccumulator = 0;
103 int16       ContainerView::amountIndY = -1;
104 
105 //-----------------------------------------------------------------------
106 //	Physical container appearance
107 
108 static ContainerAppearanceDef physicalContainerAppearance = {
109 	{250, 60, 268, 304 + 16},
110 	{17 + 4, 87, 268 - 2, 304 - 87},
111 	{13 + 8, 37, 44, 42},
112 	{13 + 8 + 44, 37, 44, 42},
113 	{13 + 118, 50, 36, 36},
114 	{13 + 139, 37, 88, 43},
115 	{ MKTAG('P', 'C', 'L', 0), MKTAG('P', 'C', 'L', 1) },
116 	{ MKTAG('P', 'S', 'L', 0), MKTAG('P', 'S', 'L', 1) },
117 	{13, 8},
118 	{22, 22},
119 	0, 0,
120 	0
121 };
122 
123 static const StaticWindow brassDecorations[] = {
124 	{{0,  0, 268,  86},   nullptr, 3},
125 	{{13, 86, 242, 109},  nullptr, 4},
126 	{{13, 195, 242, 121}, nullptr, 5}
127 };
128 
129 static const StaticWindow clothDecorations[] = {
130 	{{0,  0, 268,  86},   nullptr, 6},
131 	{{13, 86, 242, 109},  nullptr, 7},
132 	{{13, 195, 242, 121}, nullptr, 8}
133 };
134 
135 static const StaticWindow steelDecorations[] = {
136 	{{0,  0, 268,  86},   nullptr, 9},
137 	{{13, 86, 242, 109},  nullptr, 10},
138 	{{13, 195, 242, 121}, nullptr, 11}
139 };
140 
141 static const StaticWindow woodDecorations[] = {
142 	{{0,  0, 268,  86},   nullptr, 12},
143 	{{13, 86, 242, 109},  nullptr, 13},
144 	{{13, 195, 242, 121}, nullptr, 14}
145 };
146 
147 //-----------------------------------------------------------------------
148 //	Death container appearance
149 
150 static ContainerAppearanceDef deathContainerAppearance = {
151 	{260, 60, 206, 250},
152 	{2, 87, 206 - 22, 250 - 87 - 32},
153 	{16,  24, 44, 42},
154 	{120 + 18, 24, 44, 42},
155 	{0, 0, 0, 0},
156 	{0, 0, 0, 0},
157 	{ MKTAG('D', 'C', 'L', 0), MKTAG('D', 'C', 'L', 1) },
158 	{ MKTAG('D', 'S', 'L', 0), MKTAG('D', 'S', 'L', 1) },
159 	{27, -4},
160 	{22, 22},
161 	0, 0,
162 	0
163 };
164 
165 // physal dialog window decorations
166 static const StaticWindow deathDecorations[] = {
167 	{{0,  0, 206,  250}, nullptr, 15}
168 };
169 
170 //-----------------------------------------------------------------------
171 //	ReadyContainer appearance
172 
173 static const ContainerAppearanceDef readyContainerAppearance = {
174 	{0, 0, 0, 0},
175 	{476, 105, 0, 0},
176 	{0, 0, 0, 0},
177 	{0, 0, 0, 0},
178 	{0, 0, 0, 0},
179 	{0, 0, 0, 0},
180 	{ 0, 0 },
181 	{ 0, 0 },
182 	{iconOriginX - 1, iconOriginY - 1 - 8},
183 	{iconSpacingX, iconSpacingY},
184 	1, 3,
185 	3
186 };
187 
188 //-----------------------------------------------------------------------
189 //	Mental Container appearance
190 
191 static const ContainerAppearanceDef mentalContainerAppearance = {
192 	{478, 168 - 54, 158, 215},
193 	{2, 86 - 18 - 4, 158 - 2, 215 - 66},
194 	{2, 19, 44, 44},
195 	{103, 40 - 18 - 4, 44, 44},
196 	{0, 0, 0, 0},
197 	{0, 0, 0, 0},
198 	{ MKTAG('C', 'L', 'S', 0), MKTAG('C', 'L', 'S', 1) },
199 	{ MKTAG('S', 'E', 'L', 0), MKTAG('S', 'E', 'L', 1) },
200 	{3, 0},
201 	{4, 4},
202 	4, 4,
203 	20
204 };
205 
206 static const StaticWindow mentalDecorations[] = {
207 	{{0,  0, 158,  215}, nullptr, 0}      //  Bottom decoration panel
208 };
209 //-----------------------------------------------------------------------
210 //	Enchantment container appearance
211 
212 static const ContainerAppearanceDef enchantmentContainerAppearance = {
213 	{262, 92, 116, 202},
214 	{2, 87, 116 - 2, 202 - 87},
215 	{7, 50, 44, 43},
216 	{57, 50, 44, 43},
217 	{38, 7, 32, 32},
218 	{0, 0, 0, 0},
219 	{ MKTAG('A', 'A', 'A', 0), MKTAG('A', 'A', 'A', 0) },
220 	{ MKTAG('A', 'A', 'A', 0), MKTAG('A', 'A', 'A', 0) },
221 	{12, 98},
222 	{16, 13},
223 	2, 2,
224 	2
225 };
226 
227 //-----------------------------------------------------------------------
228 //	ContainerView class
229 
230 //  Constructor
231 /* ContainerView::ContainerView(
232     gPanelList      &list,
233     const Rect16    &box,
234     ContainerNode   &nd,
235     Point16         org,
236     Point16         space,
237     int16           numRows,
238     int16           numCols,
239     int16           totRows,
240     uint16          ident,
241     AppFunc         *cmd ) */
242 
ContainerView(gPanelList & list,const Rect16 & rect,ContainerNode & nd,const ContainerAppearanceDef & app,AppFunc * cmd)243 ContainerView::ContainerView(
244     gPanelList      &list,
245     const Rect16    &rect,
246     ContainerNode   &nd,
247     const ContainerAppearanceDef &app,
248     AppFunc         *cmd)
249 	: gControl(list, rect, NULL, 0, cmd),
250 	  iconOrigin(app.iconOrigin),
251 	  iconSpacing(app.iconSpacing),
252 	  visibleRows(app.rows),
253 	  visibleCols(app.cols),
254 	  node(nd) {
255 	containerObject = GameObject::objectAddress(nd.object);
256 	scrollPosition  = 0;
257 	totalRows       = app.totRows;
258 	setMousePoll(true);
259 	totalMass = 0;
260 	totalBulk = 0;
261 	numObjects = 0;
262 }
263 
264 //  Destructor
~ContainerView()265 ContainerView::~ContainerView() {
266 }
267 
268 /****** contain.cpp/ContainerView::findPane ***********************
269 *
270 *   NAME
271 *             findPane - find a ContainerView control that is
272 *                        viewing the contents of the target object
273 *
274 *   SYNOPSIS
275 *             If pane is NULL, start search at first ContainerView
276 *             else, start search at pane->next.
277 *
278 *             Iterate through ContainerView list until a
279 *             ContainerView is found that is viewing the
280 *             target object (obj).
281 *
282 *   INPUTS
283 *             obj   Target Game Object
284 *             pane  Previously found pane, or NULL if search start
285 *
286 *   RESULT
287 *             Returns a pointer to a ContainterPanel if one is found
288 *             or NULL if no more found.
289 *
290 ********************************************************************
291 */
292 
293 //  returns true if the object is visible for this type of
294 //  container.
isVisible(GameObject * item)295 bool ContainerView::isVisible(GameObject *item) {
296 	ProtoObj *proto = item->proto();
297 
298 	if (proto->containmentSet() & ProtoObj::isEnchantment)
299 		return false;
300 
301 	//  If Intangible Container then don't show it.
302 	if ((proto->containmentSet() & (ProtoObj::isContainer | ProtoObj::isIntangible)) == (ProtoObj::isContainer | ProtoObj::isIntangible))
303 		return true;
304 
305 	return true;
306 }
307 
308 //  total the mass, bulk, and number of all objects in container.
totalObjects(void)309 void ContainerView::totalObjects(void) {
310 	ObjectID objID;
311 	GameObject *item = nullptr;
312 
313 	totalMass   = 0;
314 	totalBulk   = 0;
315 	numObjects  = 0;
316 
317 	if (containerObject == NULL) return;
318 
319 	RecursiveContainerIterator  iter(containerObject);
320 
321 	//  See if he has enough currency
322 	for (objID = iter.first(&item); objID != Nothing; objID = iter.next(&item)) {
323 		//  If the object is visible, then add to total mass and
324 		//  bulk.
325 		if (isVisible(item)) {
326 			uint16  numItems;
327 
328 			ProtoObj *proto = item->proto();
329 
330 			numObjects++;
331 
332 			// if it's mergeable calc using the getExtra method of
333 			// quantity calculation
334 			// if not, then use the objLoc.z method
335 			if (proto->flags & ResourceObjectPrototype::objPropMergeable)
336 				numItems = item->getExtra();
337 			else numItems = 1;
338 
339 			totalMass += proto->mass * numItems;
340 			totalBulk += proto->bulk * numItems;
341 		}
342 	}
343 
344 //	ContainerView *viewToUpdate = ContainerView::findPane( containerObject );
345 
346 //	viewToUpdate->getWindow()->update( viewToUpdate->getExtent() );
347 }
348 
349 //  Return the Nth visible object from this container.
getObject(int16 slotNum)350 ObjectID ContainerView::getObject(int16 slotNum) {
351 	ObjectID        objID;
352 	GameObject      *item;
353 
354 	if (containerObject == NULL) return Nothing;
355 
356 	ContainerIterator   iter(containerObject);
357 
358 	//  Iterate through all the objects in the container.
359 	while ((objID = iter.next(&item)) != Nothing) {
360 		//  If the object is visible, then check which # it is,
361 		//  and return the object if slotnum has been decremented
362 		//  to zero.
363 		if (isVisible(item)) {
364 			if (slotNum == 0) return objID;
365 			slotNum--;
366 		}
367 	}
368 	return Nothing;
369 }
370 
371 //  REM: We need to handle the case of a NULL container here...
372 //  Draw the contents of the container.
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)373 void ContainerView::drawClipped(
374     gPort           &port,
375     const Point16   &offset,
376     const Rect16    &r) {
377 	//  Coordinates to draw the inventory icon at.
378 	int16           x,
379 	                y;
380 
381 	//  Coordinates for slot 0,0.
382 	int16           originX = _extent.x - offset.x + iconOrigin.x,
383 	                originY = _extent.y - offset.y + iconOrigin.y;
384 
385 	ObjectID        objID;
386 	GameObject      *item;
387 
388 	//  Iterator class for the container.
389 	ContainerIterator   iter(containerObject);
390 
391 	//  Color set to draw the object.
392 	ColorTable      objColors;
393 
394 	//  If there's no overlap between extent and clip, then return.
395 	if (!_extent.overlap(r)) return;
396 
397 	//  Iterate through each item within the container.
398 	while ((objID = iter.next(&item)) != Nothing) {
399 		TilePoint   objLoc;
400 		ProtoObj    *objProto = item->proto();
401 
402 		objLoc = item->getLocation();
403 
404 		if (objLoc.z == 0) continue;
405 
406 		//  Draw object only if visible and in a visible row & col.
407 		if (objLoc.u >= scrollPosition
408 		        &&  objLoc.u < scrollPosition + visibleRows
409 		        &&  objLoc.v < visibleCols
410 		        &&  isVisible(item)) {
411 			Sprite      *spr;
412 			ProtoObj    *proto = item->proto();
413 			Point16     sprPos;
414 
415 			y =     originY
416 			        + (objLoc.u - scrollPosition)
417 			        * (iconSpacing.y + iconHeight);
418 			x =     originX
419 			        +       objLoc.v
420 			        * (iconSpacing.x + iconWidth);
421 
422 			//  Get the address of the sprite.
423 			spr = proto->getSprite(item, ProtoObj::objInContainerView).sp;
424 
425 			sprPos.x = x + ((iconWidth - spr->size.x) >> 1)
426 			           - spr->offset.x;
427 			sprPos.y = y + ((iconHeight - spr->size.y) >> 1)
428 			           - spr->offset.y;
429 
430 			//  Build the color table.
431 			item->getColorTranslation(objColors);
432 			//  Draw the sprite into the port
433 
434 			DrawColorMappedSprite(
435 			    port,
436 			    sprPos,
437 			    spr,
438 			    objColors);
439 
440 			// check to see if selecting amount for this objec
441 			if (objToGet == item) {
442 				Point16 selectorPos = Point16(x + ((iconWidth - selectorX) >> 1),
443 				                              y + ((iconHeight - selectorY) >> 1));
444 
445 				// draw the selector thingy
446 				drawSelector(port, selectorPos);
447 
448 				// set the position of the inc center
449 				amountIndY = y - (selectorY >> 1) - 12;
450 			} else drawQuantity(port, item, objProto, x, y);
451 		}
452 	}
453 }
454 
455 // draws the mereged object multi-item selector
drawSelector(gPort & port,Point16 & pos)456 void ContainerView::drawSelector(gPort &port, Point16 &pos) {
457 	char buf[20];
458 	uint8   num;
459 
460 	SAVE_GPORT_STATE(port);
461 
462 	// draw the arrow images
463 	drawCompressedImage(port, pos, selImage);
464 
465 	// draw the number of items selected thus far
466 	num = sprintf(buf, " %d ", numPicked);
467 
468 	port.moveTo(Point16(pos.x - ((3 * (num - 3)) + 1),  pos.y + 7));
469 	port.setFont(&Helv11Font);
470 	port.setColor(11);                   // set color to white
471 	port.setStyle(textStyleThickOutline);
472 	port.setOutlineColor(24);                // set outline color to black
473 	port.setMode(drawModeMatte);
474 
475 	port.drawText(buf);
476 }
477 
drawQuantity(gPort & port,GameObject * item,ProtoObj * objProto,int16 x,int16 y)478 void ContainerView::drawQuantity(
479     gPort           &port,
480     GameObject      *item,
481     ProtoObj        *objProto,
482     int16           x,
483     int16           y) {
484 	int16       quantity;
485 
486 	quantity = (objProto->flags & ResourceObjectPrototype::objPropMergeable)
487 	           ? item->getExtra()
488 	           : item->getLocation().z;
489 
490 	if (quantity > 1) {
491 		SAVE_GPORT_STATE(port);
492 		char buf[8];
493 
494 		// draw the number of items selected thus far
495 		sprintf(buf, "%d", quantity);
496 
497 		port.moveTo(x - 1,  y + 22);
498 		port.setFont(&Helv11Font);
499 		port.setColor(11);                   // set color to white
500 		port.setStyle(textStyleThickOutline);
501 		port.setOutlineColor(24);                // set outline color to black
502 		port.setMode(drawModeMatte);
503 
504 		port.drawText(buf);
505 	}
506 }
507 
setContainer(GameObject * containerObj)508 void ContainerView::setContainer(GameObject *containerObj) {
509 	containerObject = containerObj;
510 	totalObjects();
511 }
512 
513 
514 //	Get the slot that the point is over
pickObjectSlot(const Point16 & pickPos)515 TilePoint ContainerView::pickObjectSlot(const Point16 &pickPos) {
516 	TilePoint   slot;
517 	Point16     temp;
518 
519 	//  Compute the u/v of the slot that they clicked on.
520 	temp   = pickPos + iconSpacing / 2 - iconOrigin;
521 	slot.v = clamp(0, temp.x / (iconWidth  + iconSpacing.x), visibleCols - 1);
522 	slot.u = clamp(0, temp.y / (iconHeight + iconSpacing.y), visibleRows - 1) + scrollPosition;
523 	slot.z = 1;
524 	return slot;
525 }
526 
527 //	Get the object that the pointer is over
getObject(const TilePoint & slot)528 GameObject *ContainerView::getObject(const TilePoint &slot) {
529 	GameObject *item;
530 	TilePoint   loc;
531 
532 	item = containerObject->child();
533 
534 	while (item != NULL) {
535 		//  Skip objects that are stacked behind other objects
536 		if (item->getLocation().z != 0) {
537 			ProtoObj *proto = item->proto();
538 
539 			loc = item->getLocation();
540 
541 			if (
542 			    (loc.u == slot.u) &&
543 			    (loc.v == slot.v) &&
544 			    //Skip The Enchantments
545 			    (!(proto->containmentSet() & ProtoObj::isEnchantment))
546 			) {
547 				return item;
548 			}
549 		}
550 
551 		item = item->next();
552 	}
553 
554 	return NULL;
555 
556 }
557 
558 //	Get the object ID that the point is over
pickObjectID(const Point16 & pickPos)559 ObjectID ContainerView::pickObjectID(const Point16 &pickPos) {
560 	TilePoint       slot;
561 	GameObject      *item;
562 
563 	slot = pickObjectSlot(pickPos);
564 	item = getObject(slot);
565 
566 	if (item != NULL) {
567 		return item->thisID();
568 	} else {
569 		return 0;
570 	}
571 }
572 
573 //	Get the object ID that the point is over
pickObject(const Point16 & pickPos)574 GameObject *ContainerView::pickObject(const Point16 &pickPos) {
575 	TilePoint       slot;
576 	GameObject      *item;
577 
578 	slot = pickObjectSlot(pickPos);
579 	item = getObject(slot);
580 
581 	return item;
582 }
583 
activate(gEventType why)584 bool ContainerView::activate(gEventType why) {
585 	gPanel::activate(why);
586 
587 	return true;
588 }
589 
deactivate(void)590 void ContainerView::deactivate(void) {
591 }
592 
pointerMove(gPanelMessage & msg)593 void ContainerView::pointerMove(gPanelMessage &msg) {
594 	if (msg.pointerLeave) {
595 		lastPickedObjectID = Nothing;
596 		lastPickedObjectQuantity = -1;
597 		g_vm->_mouseInfo->setText(NULL);
598 		mouseText[0] = 0;
599 
600 		// static bool that tells if the mouse cursor
601 		// is in a panel
602 		mouseInView = false;
603 		g_vm->_mouseInfo->setDoable(true);
604 	} else {
605 //		if( msg.pointerEnter )
606 		{
607 			// static bool that tells if the mouse cursor
608 			// is in a panel
609 			mouseInView = true;
610 
611 			GameObject *mouseObject;
612 			mouseObject = g_vm->_mouseInfo->getObject();
613 
614 			if (!node.isAccessable(getCenterActorID())) {
615 				g_vm->_mouseInfo->setDoable(false);
616 			} else if (mouseObject == NULL) {
617 				g_vm->_mouseInfo->setDoable(true);
618 			} else {
619 				g_vm->_mouseInfo->setDoable(containerObject->canContain(mouseObject->thisID()));
620 			}
621 		}
622 
623 		//  Determine if mouse is pointing at a new object
624 		updateMouseText(msg.pickPos);
625 	}
626 }
627 
pointerHit(gPanelMessage & msg)628 bool ContainerView::pointerHit(gPanelMessage &msg) {
629 	GameObject  *mouseObject;
630 	GameObject  *slotObject;
631 	uint16       mouseSet;
632 
633 	slotObject  = pickObject(msg.pickPos);
634 	mouseObject = g_vm->_mouseInfo->getObject();
635 	mouseSet    = mouseObject ? mouseObject->containmentSet() : 0;
636 
637 	if (!g_vm->_mouseInfo->getDoable()) return false;
638 
639 	if (msg.doubleClick && !alreadyDone) {
640 		dblClick(mouseObject, slotObject, msg);
641 	} else { // single click
642 		if (mouseObject != NULL) {
643 			alreadyDone = true;    // if object then no doubleClick
644 
645 			if (g_vm->_mouseInfo->getIntent() == GrabInfo::Drop) {
646 				if (mouseSet & ProtoObj::isTangible) {
647 					dropPhysical(msg, mouseObject, slotObject, g_vm->_mouseInfo->getMoveCount());
648 				}
649 
650 				//  intangibles are used by dropping them
651 				else if ((mouseSet & ProtoObj::isConcept) ||
652 				         (mouseSet & ProtoObj::isPsych) ||
653 				         (mouseSet & ProtoObj::isSpell) ||
654 				         (mouseSet & ProtoObj::isSkill)) {
655 					useConcept(msg, mouseObject, slotObject);
656 				} else {
657 					// !!!! bad state, reset cursor
658 					g_vm->_mouseInfo->replaceObject();
659 				}
660 			} else if (g_vm->_mouseInfo->getIntent() == GrabInfo::Use) {
661 				if (mouseSet & ProtoObj::isTangible) {
662 					usePhysical(msg, mouseObject, slotObject);
663 				} else if ((mouseSet & ProtoObj::isSpell) ||
664 				           (mouseSet & ProtoObj::isSkill)) {
665 					g_vm->_mouseInfo->replaceObject();
666 				} else {
667 					useConcept(msg, mouseObject, slotObject);
668 				}
669 			} else {
670 				// !!!! bad state, reset cursor
671 				g_vm->_mouseInfo->replaceObject();
672 			}
673 		} else {
674 			// default to doubleClick active
675 			alreadyDone = false;
676 			clickOn(msg, mouseObject, slotObject);
677 		}
678 	}
679 
680 	// total the mass and bulk of all the objects in this container
681 	totalObjects();
682 	window.update(_extent);
683 
684 	return activate(gEventMouseDown);
685 }
686 
pointerRelease(gPanelMessage &)687 void ContainerView::pointerRelease(gPanelMessage &) {
688 	// see if in multi-item get mode
689 	if (objToGet) {
690 		objToGet->take(getCenterActorID(), numPicked);
691 
692 		// reset the flags and pointer dealing with merged object movement
693 		objToGet            = NULL;
694 		numPicked           = 1;
695 		amountIndY          = -1;
696 	}
697 
698 	gPanel::deactivate();
699 }
700 
timerTick(gPanelMessage & msg)701 void ContainerView::timerTick(gPanelMessage &msg) {
702 	// validate objToGet and make sure that the number selected for move
703 	// is less then or equal to the number of items present in the merged object
704 	if (objToGet && amountIndY != -1) {
705 		int32   rate = (amountIndY - msg.pickAbsPos.y);
706 
707 		rate = rate * ((rate > 0) ? rate : -rate);
708 
709 		//  Add to the amount accumulator based on the mouse position
710 		amountAccumulator += rate / 4 /* * accelSpeed */;
711 
712 		//  Take the top bits of the amount accumulator and add to
713 		//  the mergeable amount.
714 		numPicked = clamp(1,
715 		                  numPicked + (amountAccumulator >> 8),
716 		                  objToGet->getExtra());
717 
718 		//  Now remove the bits that we added to the grab amount
719 		//  keep the remaining bits to accumulate for next time
720 		amountAccumulator &= 0x00ff;
721 	}
722 }
723 
dblClick(GameObject * mouseObject,GameObject * slotObject,gPanelMessage & msg)724 void ContainerView::dblClick(GameObject *mouseObject, GameObject *slotObject, gPanelMessage &msg) {
725 	alreadyDone = true;
726 
727 	// double click stuff
728 	dblClickOn(msg, mouseObject, slotObject);
729 }
730 
731 // activate the click function
clickOn(gPanelMessage &,GameObject *,GameObject * cObj)732 void ContainerView::clickOn(
733     gPanelMessage &,
734     GameObject *,
735     GameObject *cObj) {
736 	if (cObj != NULL) {
737 		if (cObj->proto()->flags & ResourceObjectPrototype::objPropMergeable) {
738 			if (!rightButtonState()) {
739 				//  just get the object into the cursor
740 				cObj->take(getCenterActorID(), cObj->getExtra());
741 			} else {
742 				// activate multi-object get interface if a mergeable object
743 				getMerged(cObj);
744 				g_vm->_mouseInfo->setText(NULL);
745 				mouseText[0] = 0;
746 			}
747 		} else {
748 			//  just get the object into the cursor
749 			cObj->take(getCenterActorID(), numPicked);
750 		}
751 	}
752 }
753 
getMerged(GameObject * obj)754 void ContainerView::getMerged(GameObject *obj) {
755 	// reset the number picked.
756 	numPicked = 1;
757 
758 	// set the object to be gotten
759 	objToGet = obj;
760 }
761 
762 // Activate the double click function
dblClickOn(gPanelMessage &,GameObject * mObj,GameObject *)763 void ContainerView::dblClickOn(
764     gPanelMessage &,
765     GameObject *mObj,
766     GameObject *) {
767 	if (mObj != NULL) {
768 		ObjectID        possessor = mObj->possessor();
769 		ProtoObj        *proto = mObj->proto();
770 		PlayerActorID   pID;
771 
772 		//  Only player actors can be possessors as far as the UI is concerned
773 		if (actorIDToPlayerID(possessor, pID) == false) possessor = Nothing;
774 
775 		g_vm->_mouseInfo->replaceObject(); //Put Object Back
776 		if (!(proto->setUseCursor(mObj->thisID()))) {
777 			MotionTask::useObject(
778 			    possessor == Nothing ? *getCenterActor() : * (Actor *)GameObject::objectAddress(possessor),
779 			    *mObj);
780 		}
781 	}
782 }
783 
784 // drop a physical object
dropPhysical(gPanelMessage & msg,GameObject * mObj,GameObject * cObj,int16 num)785 void ContainerView::dropPhysical(
786     gPanelMessage   &msg,
787     GameObject      *mObj,
788     GameObject      *cObj,
789     int16           num) {
790 	assert(g_vm->_mouseInfo->getObject() == mObj);
791 	assert(mObj->containmentSet() & ProtoObj::isTangible);
792 
793 	//  Place object back where it came from, temporarily
794 	g_vm->_mouseInfo->replaceObject();
795 
796 	//  test to check if item is accepted by container
797 	if (containerObject->canContain(mObj->thisID())) {
798 		Actor       *centerActor = getCenterActor();
799 		Location    loc(pickObjectSlot(msg.pickPos),
800 		                containerObject->thisID());
801 
802 		//  check if no object in the current slot
803 		if (cObj == NULL) {
804 			MotionTask::dropObject(*centerActor, *mObj, loc, num);
805 
806 			WriteStatusF(6, "No object state");
807 		} else {
808 			//  Try dropping this object on the object in the container
809 			MotionTask::dropObjectOnObject(*centerActor, *mObj, *cObj, num);
810 		}
811 
812 		alreadyDone = true;
813 	}
814 }
815 
816 // use a physical object
usePhysical(gPanelMessage & msg,GameObject * mObj,GameObject * cObj)817 void ContainerView::usePhysical(
818     gPanelMessage   &msg,
819     GameObject      *mObj,
820     GameObject      *cObj) {
821 	assert(g_vm->_mouseInfo->getObject() == mObj);
822 	assert(mObj->containmentSet() & ProtoObj::isTangible);
823 
824 	if (cObj == NULL) {
825 		dropPhysical(msg, mObj, cObj);
826 	} else {
827 		g_vm->_mouseInfo->replaceObject();
828 		//  Use mouse object on container object
829 		MotionTask::useObjectOnObject(*getCenterActor(), *mObj, *cObj);
830 	}
831 }
832 
833 // Use Concept or Psycological state
useConcept(gPanelMessage & msg,GameObject * mObj,GameObject * cObj)834 void ContainerView::useConcept(
835     gPanelMessage   &msg,
836     GameObject      *mObj,
837     GameObject      *cObj) {
838 	assert(g_vm->_mouseInfo->getObject() == mObj);
839 	assert(mObj->containmentSet() & ProtoObj::isIntangible);
840 
841 	g_vm->_mouseInfo->replaceObject();
842 
843 	//  Determine if this object can go into this container
844 	if (containerObject->canContain(mObj->thisID())) {
845 		ObjectID    centerActorID = getCenterActorID();
846 
847 		if (cObj == NULL) {
848 			//  If there is no object already in this slot drop the
849 			//  mouse object here
850 
851 			Location    loc(pickObjectSlot(msg.pickPos),
852 			                containerObject->thisID());
853 
854 			mObj->drop(centerActorID, loc);
855 		} else
856 			//  If there is an object here drop the mouse object onto it
857 			mObj->dropOn(centerActorID, cObj->thisID());
858 
859 		alreadyDone = true;
860 	}
861 }
862 
863 // Use Spell or Skill
useSpell(gPanelMessage & msg,GameObject * mObj,GameObject * cObj)864 void ContainerView::useSpell(
865     gPanelMessage   &msg,
866     GameObject      *mObj,
867     GameObject      *cObj) {
868 	useConcept(msg, mObj, cObj);
869 }
870 
871 //  Determine if the mouse is pointing at a new object, and if so,
872 //  adjust the mouse text
updateMouseText(Point16 & pickPos)873 void ContainerView::updateMouseText(Point16 &pickPos) {
874 	ObjectID slotID = pickObjectID(pickPos);
875 
876 	// set the mouse text to null if there is no object to get hints about
877 	if (slotID == Nothing) {
878 		// clear out the mouse text
879 		g_vm->_mouseInfo->setText(NULL);
880 		mouseText[0] = 0;
881 
882 		// reset the last picked thingy
883 		lastPickedObjectID          = Nothing;
884 		lastPickedObjectQuantity    = -1;
885 
886 		// set the display alarm to false
887 		objTextAlarm = false;
888 
889 		return;
890 	}
891 
892 	// get handles to the object in question
893 	GameObject  *slotObject = GameObject::objectAddress(slotID);
894 
895 	if (slotID == lastPickedObjectID && slotObject->getExtra() == lastPickedObjectQuantity) {
896 		return; // same object, bug out
897 	} else {
898 		// was not same, but is now.
899 		lastPickedObjectID          = slotID;
900 		lastPickedObjectQuantity    = slotObject->getExtra();
901 
902 		// clear out the mouse text
903 		g_vm->_mouseInfo->setText(NULL);
904 		mouseText[0] = 0;
905 
906 		// reset the alarm flag
907 		objTextAlarm = false;
908 
909 		// set the hint alarm
910 		containerObjTextAlarm.set(ticksPerSecond / 2);
911 
912 		// put the normalized text into mouseText
913 		slotObject->objCursorText(mouseText, bufSize);
914 	}
915 }
916 
917 #if 0
918 //  Functions do not appear to be called
919 
920 void ContainerView::setCursorText(GameObject *obj) {
921 	assert(obj);
922 
923 	const   bufSize = 40;
924 	char cursorText[bufSize];
925 
926 	// put the normalized text into cursorText
927 	obj->objCursorText(cursorText, bufSize);
928 
929 	g_vm->_mouseInfo->setText(cursorText);
930 }
931 
932 void ContainerView::setDelayedCursorText(GameObject *obj) {
933 	// clear out the mouse text
934 	g_vm->_mouseInfo->setText(NULL);
935 	mouseText[0] = 0;
936 
937 	// reset the alarm flag
938 	objTextAlarm = false;
939 
940 	// set the hint alarm
941 	containerObjTextAlarm.set(ticksPerSecond / 2);
942 
943 	// put the normalized text into mouseText
944 	obj->objCursorText(mouseText, bufSize);
945 }
946 #endif
947 
948 /* ===================================================================== *
949     EnchantmentContainerView member functions
950  * ===================================================================== */
951 
EnchantmentContainerView(gPanelList & list,ContainerNode & nd,const ContainerAppearanceDef & app,AppFunc * cmd)952 EnchantmentContainerView::EnchantmentContainerView(
953     gPanelList      &list,
954     ContainerNode   &nd,
955     const ContainerAppearanceDef &app,
956     AppFunc         *cmd)
957 	: ContainerView(list, app.viewRect, nd, app, cmd) {
958 }
959 
960 //  Check If Sprite Should Be Shown
pointerHit(gPanelMessage &)961 bool EnchantmentContainerView::pointerHit(gPanelMessage &) {
962 	return true;
963 }
964 
pointerMove(gPanelMessage &)965 void EnchantmentContainerView::pointerMove(gPanelMessage &) {}
966 
967 /* ===================================================================== *
968     ReadyContainerView member functions
969  * ===================================================================== */
970 
ReadyContainerView(gPanelList & list,const Rect16 & box,ContainerNode & nd,void ** backgrounds,int16 numRes,int16 numRows,int16 numCols,int16 totRows,AppFunc * cmd)971 ReadyContainerView::ReadyContainerView(
972     gPanelList      &list,
973     const Rect16    &box,
974     ContainerNode   &nd,
975     void            **backgrounds,
976     int16           numRes,
977     int16           numRows,
978     int16           numCols,
979     int16           totRows,
980     AppFunc         *cmd)
981 	: ContainerView(list, box, nd, readyContainerAppearance, cmd) {
982 	//  Over-ride row and column info in appearance record.
983 	visibleRows = numRows;
984 	visibleCols = numCols;
985 	totalRows   = totRows;
986 
987 	if (backgrounds) {
988 		backImages  = backgrounds;
989 		numIm       = numRes;
990 	} else {
991 		backImages  = NULL;
992 		numIm       = 0;
993 	}
994 }
995 
setScrollOffset(int8 num)996 void ReadyContainerView::setScrollOffset(int8 num) {
997 	if (num > 0) {
998 		scrollPosition = num;
999 	}
1000 }
1001 
timerTick(gPanelMessage & msg)1002 void ReadyContainerView::timerTick(gPanelMessage &msg) {
1003 	// validate objToGet and make sure that the number selected for move
1004 	// is less then or equal to the number of items present in the merged object
1005 	if (objToGet && amountIndY != -1) {
1006 		ContainerView::timerTick(msg);
1007 
1008 		// redraw the container to draw the amount indicator
1009 		draw();
1010 	}
1011 }
1012 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)1013 void ReadyContainerView::drawClipped(
1014     gPort           &port,
1015     const Point16   &offset,
1016     const Rect16    &r) {
1017 	//  Coordinates to draw the inventory icon at.
1018 	int16           x,
1019 	                y;
1020 
1021 	//  Coordinates for slot 0,0.
1022 	int16           originX = _extent.x - offset.x + iconOrigin.x,
1023 	                originY = _extent.y - offset.y + iconOrigin.y;
1024 
1025 	//  Row an column number of the inventory slot.
1026 	int16           col,
1027 	                row;
1028 
1029 	ObjectID        objID;
1030 	GameObject      *item;
1031 
1032 	//  Iterator class for the container.
1033 	ContainerIterator   iter(containerObject);
1034 
1035 	//  Color set to draw the object.
1036 	ColorTable      objColors;
1037 
1038 	//  If there's no overlap between extent and clip, then return.
1039 	if (!_extent.overlap(r)) return;
1040 
1041 	//  Draw the boxes for visible rows and cols.
1042 
1043 	if (backImages) {
1044 		int16   i;
1045 		Point16 drawOrg(_extent.x - offset.x + backOriginX,
1046 		                _extent.y - offset.y + backOriginY);
1047 
1048 		for (y = drawOrg.y, row = 0;
1049 		        row < visibleRows;
1050 		        row++, y += iconSpacing.y + iconHeight) {
1051 			// reset y for background image stuff
1052 			//y = drawOrg.y;
1053 
1054 			for (i = 0, x = drawOrg.x, col = 0;
1055 			        col < visibleCols;
1056 			        i++, col++, x += iconSpacing.x + iconWidth) {
1057 				Point16 pos(x, y);
1058 
1059 				if (isGhosted()) drawCompressedImageGhosted(port, pos, backImages[i % numIm]);
1060 				else drawCompressedImage(port, pos, backImages[i % numIm]);
1061 			}
1062 
1063 		}
1064 	} else {
1065 		for (y = originY, row = 0;
1066 		        row < visibleRows;
1067 		        row++, y += iconSpacing.y + iconHeight) {
1068 
1069 			for (x = originX, col = 0;
1070 			        col < visibleCols;
1071 			        col++, x += iconSpacing.x + iconWidth) {
1072 				//  REM: We need to come up with some way of
1073 				//  indicating how to render the pattern data which
1074 				//  is behind the object...
1075 				port.setColor(14);
1076 				port.fillRect(x, y, iconWidth, iconHeight);
1077 
1078 			}
1079 
1080 		}
1081 
1082 	}
1083 
1084 	//  Iterate through each item within the container.
1085 	while ((objID = iter.next(&item)) != Nothing) {
1086 
1087 		TilePoint   objLoc;
1088 		ProtoObj    *objProto = item->proto();
1089 
1090 		//  If Intangible Container then don't show it.
1091 		if ((objProto->containmentSet() & (ProtoObj::isContainer | ProtoObj::isIntangible)) == (ProtoObj::isContainer | ProtoObj::isIntangible))
1092 			continue;
1093 
1094 		objLoc = item->getLocation();
1095 
1096 		if (objLoc.z == 0) continue;
1097 
1098 		//  Draw object only if visible and in a visible row & col.
1099 		if (objLoc.u >= scrollPosition &&
1100 		        objLoc.u < scrollPosition + visibleRows &&
1101 		        objLoc.v < visibleCols &&
1102 		        isVisible(item)) {
1103 			Sprite      *spr;
1104 			ProtoObj    *proto = item->proto();
1105 			Point16     sprPos;
1106 
1107 			y = originY + (objLoc.u - scrollPosition) * (iconSpacing.y + iconHeight);
1108 			x = originX + objLoc.v * (iconSpacing.x + iconWidth);
1109 
1110 			//  Get the address of the sprite.
1111 			spr = proto->getSprite(item, ProtoObj::objInContainerView).sp;
1112 
1113 			sprPos.x = x + ((iconWidth - spr->size.x) >> 1)
1114 			           - spr->offset.x;
1115 			sprPos.y = y + ((iconHeight - spr->size.y) >> 1)
1116 			           - spr->offset.y;
1117 
1118 			if (isGhosted()) return;
1119 
1120 			//  Draw the "in use" indicator.
1121 			if (backImages && proto->isObjectBeingUsed(item)) {
1122 				drawCompressedImage(port,
1123 				                    Point16(x - 4, y - 4), backImages[3]);
1124 			}
1125 
1126 			//  Build the color table.
1127 			item->getColorTranslation(objColors);
1128 
1129 			//  Draw the sprite into the port
1130 			DrawColorMappedSprite(
1131 			    port,
1132 			    sprPos,
1133 			    spr,
1134 			    objColors);
1135 
1136 			// check to see if selecting amount for this objec
1137 			if (objToGet == item) {
1138 				Point16 selectorPos = Point16(x + ((iconWidth - selectorX) >> 1),
1139 				                              y + ((iconHeight - selectorY) >> 1));
1140 
1141 				// draw the selector thingy
1142 				drawSelector(port, selectorPos);
1143 
1144 				// set the position of the inc center
1145 				amountIndY = y - (selectorY >> 1) + 28;   // extent.y;
1146 			} else drawQuantity(port, item, objProto, x, y);
1147 		}
1148 	}
1149 }
1150 
1151 /* ===================================================================== *
1152     ContainerWindow member functions
1153  * ===================================================================== */
1154 
1155 //  ContainerWindow class
1156 
ContainerWindow(ContainerNode & nd,const ContainerAppearanceDef & app,const char saveas[])1157 ContainerWindow::ContainerWindow(ContainerNode &nd,
1158                                  const ContainerAppearanceDef &app,
1159                                  const char saveas[])
1160 	: FloatingWindow(nd.position, 0, saveas, cmdWindowFunc) {
1161 	//  Initialize view to NULL.
1162 	view = NULL;
1163 
1164 	// create the close button for this window
1165 	closeCompButton = new GfxCompButton(
1166 	                      *this,
1167 	                      app.closeRect,              // rect for button
1168 	                      containerRes,               // resource context
1169 	                      app.closeResID[0],
1170 	                      app.closeResID[1],
1171 	                      0,
1172 	                      cmdCloseButtonFunc);        // mind app func
1173 }
1174 
1175 //  Virtual destructor (base does nothing)
~ContainerWindow(void)1176 ContainerWindow::~ContainerWindow(void) {}
1177 
getView(void)1178 ContainerView &ContainerWindow::getView(void) {
1179 	return *view;
1180 }
1181 
1182 /* ===================================================================== *
1183     ScrollableContainerWindow member functions
1184  * ===================================================================== */
1185 
ScrollableContainerWindow(ContainerNode & nd,const ContainerAppearanceDef & app,const char saveas[])1186 ScrollableContainerWindow::ScrollableContainerWindow(
1187     ContainerNode &nd, const ContainerAppearanceDef &app, const char saveas[])
1188 	: ContainerWindow(nd, app, saveas) {
1189 	view = new ContainerView(*this, app.viewRect, nd, app);
1190 
1191 	// make the button conected to this window
1192 	scrollCompButton = new GfxCompButton(
1193 	                       *this,
1194 	                       app.scrollRect,                 // rect for button
1195 	                       containerRes,                   // resource context
1196 	                       app.scrollResID[0],           // resource handle name
1197 	                       app.scrollResID[1],
1198 	                       0,
1199 	                       cmdScrollFunc);                 // mind app func
1200 
1201 	assert(view != NULL);
1202 	assert(scrollCompButton != NULL);
1203 }
1204 
1205 /* ===================================================================== *
1206     TangibleContainerWindow member functions
1207  * ===================================================================== */
1208 
TangibleContainerWindow(ContainerNode & nd,const ContainerAppearanceDef & app)1209 TangibleContainerWindow::TangibleContainerWindow(
1210     ContainerNode &nd, const ContainerAppearanceDef &app)
1211 	: ScrollableContainerWindow(nd, app, "ObjectWindow") {
1212 #if DEBUG
1213 	assert(view->containerObject);
1214 	assert(view->containerObject->proto());
1215 #endif
1216 
1217 	const int weightIndicatorType = 2;
1218 	objRect = app.iconRect;
1219 	deathFlag = nd.getType() == ContainerNode::deadType;
1220 	containerSpriteImg = NULL;
1221 
1222 	// setup the mass and weight indicator
1223 	if (deathFlag) {
1224 		// set the decorations for this window
1225 		setDecorations(deathDecorations,
1226 		               ARRAYSIZE(deathDecorations),
1227 		               containerRes, 'F', 'R', 'M');
1228 		massWeightIndicator = NULL;
1229 	} else {
1230 		const StaticWindow *winDecs[] =  {
1231 			brassDecorations,
1232 		    clothDecorations,
1233 		    steelDecorations,
1234 		    woodDecorations
1235 		};
1236 		uint16      bgndType = view->containerObject->proto()->appearanceType;
1237 
1238 		assert(bgndType < 4);
1239 
1240 		setContainerSprite();               // show at the top of the box
1241 
1242 		// set the decorations for this window
1243 		setDecorations(winDecs[bgndType],
1244 		               ARRAYSIZE(brassDecorations),    // brass was arb, all should have same
1245 		               containerRes, 'F', 'R', 'M');
1246 
1247 		// set the userdata such that we can extract the container object later
1248 		// through an appfunc.
1249 		this->userData = view->containerObject;
1250 
1251 		massWeightIndicator = new CMassWeightIndicator(
1252 		                          this,
1253 		                          Point16(app.massRect.x, app.massRect.y),
1254 		                          weightIndicatorType,
1255 		                          deathFlag);
1256 	}
1257 }
1258 
~TangibleContainerWindow(void)1259 TangibleContainerWindow::~TangibleContainerWindow(void) {
1260 	if (massWeightIndicator)    delete massWeightIndicator;
1261 	if (containerSpriteImg)     delete containerSpriteImg;
1262 }
1263 
setContainerSprite(void)1264 void TangibleContainerWindow::setContainerSprite(void) {
1265 	// pointer to sprite data that will be drawn
1266 	Sprite              *spr;
1267 	ProtoObj            *proto = view->containerObject->proto();
1268 	Point16             sprPos;
1269 	char                dummy = '\0';
1270 
1271 	//  Get the address of the sprite.
1272 	spr = proto->getSprite(view->containerObject, ProtoObj::objInContainerView).sp;
1273 
1274 	sprPos.x = objRect.x - (spr->size.x >> 1);  //objRect.x + ( spr->size.x >> 1 );
1275 	sprPos.y = objRect.y - (spr->size.y >> 1);
1276 
1277 	containerSpriteImg = new GfxSpriteImage(
1278 	                         *this,
1279 	                         Rect16(sprPos.x,
1280 	                                sprPos.y,
1281 	                                objRect.height,
1282 	                                objRect.width),
1283 	                         view->containerObject,
1284 	                         dummy,
1285 	                         0,
1286 	                         NULL);
1287 }
1288 
massBulkUpdate()1289 void TangibleContainerWindow::massBulkUpdate() {
1290 	if (massWeightIndicator) {      //  Death container doesn't have MW indicator
1291 		// set the indicators to the correct mass and bulk
1292 		massWeightIndicator->setMassPie(view->totalMass);
1293 		massWeightIndicator->setBulkPie(view->totalBulk);
1294 	}
1295 }
1296 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & clip)1297 void TangibleContainerWindow::drawClipped(
1298     gPort &port,
1299     const Point16 &offset,
1300     const Rect16 &clip) {
1301 	if (!_extent.overlap(clip)) return;
1302 
1303 	// draw the decorations
1304 	ScrollableContainerWindow::drawClipped(port, offset, clip);
1305 }
1306 
1307 /* ===================================================================== *
1308     IntangibleContainerWindow member functions
1309  * ===================================================================== */
1310 
IntangibleContainerWindow(ContainerNode & nd,const ContainerAppearanceDef & app)1311 IntangibleContainerWindow::IntangibleContainerWindow(
1312     ContainerNode &nd, const ContainerAppearanceDef &app)
1313 	: ScrollableContainerWindow(nd, app, "MentalWindow") {
1314 	// make the button conected to this window
1315 	mindSelectorCompButton = new GfxMultCompButton(
1316 	                             *this,
1317 	                             Rect16(49, 15 - 13, 52, 67),
1318 	                             containerRes,
1319 	                             'H', 'E', 'D', 1, 3, 1,
1320 	                             0,
1321 	                             cmdMindContainerFunc);          // mind app func
1322 
1323 	assert(mindSelectorCompButton != NULL);
1324 
1325 	mindSelectorCompButton->setResponse(false);
1326 
1327 	// set the decorations for this window
1328 	setDecorations(mentalDecorations,
1329 	               ARRAYSIZE(mentalDecorations),
1330 	               containerRes, 'F', 'R', 'M');
1331 
1332 	setMindContainer(nd.mindType, *this);
1333 }
1334 
1335 /* ===================================================================== *
1336     EnchantmentContainerWindow member functions
1337  * ===================================================================== */
1338 
EnchantmentContainerWindow(ContainerNode & nd,const ContainerAppearanceDef & app)1339 EnchantmentContainerWindow::EnchantmentContainerWindow(
1340     ContainerNode &nd, const ContainerAppearanceDef &app)
1341 	: ContainerWindow(nd, app, "EnchantmentWindow") {
1342 	view = new EnchantmentContainerView(*this, nd, app);
1343 
1344 	// make the button conected to this window
1345 	scrollCompButton = new GfxCompButton(
1346 	                       *this,
1347 	                       app.scrollRect,                 // rect for button
1348 	                       containerRes,                   // resource context
1349 	                       app.scrollResID[0],           // resource handle name
1350 	                       app.scrollResID[1],
1351 	                       0,
1352 	                       cmdScrollFunc);                 // mind app func
1353 
1354 	assert(view != NULL);
1355 	assert(scrollCompButton != NULL);
1356 }
1357 
1358 /* ===================================================================== *
1359    ContainerNode functions
1360  * ===================================================================== */
1361 
ContainerNode(ContainerManager & cl,ObjectID id,int typ)1362 ContainerNode::ContainerNode(ContainerManager &cl, ObjectID id, int typ) {
1363 	GameObject      *obj = GameObject::objectAddress(id);
1364 	PlayerActorID   ownerID;
1365 
1366 	//  Convert the possessor() of the object to a player actor ID,
1367 	//  if it is indeed a player actor; Else set to "nobody".
1368 	if (isActor(id)) {
1369 		if (actorIDToPlayerID(id, ownerID) == false)
1370 			ownerID = ContainerNode::nobody;
1371 	} else {
1372 		ObjectID        possessor = obj->possessor();
1373 
1374 		if (possessor == Nothing || actorIDToPlayerID(possessor, ownerID) == false)
1375 			ownerID = ContainerNode::nobody;
1376 	}
1377 
1378 	//  Compute the initial position of the container window
1379 	switch (typ) {
1380 	case readyType:
1381 		break;
1382 
1383 	case deadType:
1384 		position = deathContainerAppearance.defaultWindowPos;
1385 		break;
1386 
1387 	case mentalType:
1388 		mindType = 0; //protoClassIdeaContainer;
1389 		position = mentalContainerAppearance.defaultWindowPos;
1390 		break;
1391 
1392 	case physicalType:
1393 		position = physicalContainerAppearance.defaultWindowPos;
1394 		break;
1395 
1396 	case enchantType:
1397 		position = enchantmentContainerAppearance.defaultWindowPos;
1398 		break;
1399 	}
1400 
1401 	//  Fill in the initial values.
1402 	window      = NULL;
1403 	type        = typ;
1404 	object      = id;
1405 	owner       = ownerID;
1406 	action      = 0;
1407 	mindType    = 0;
1408 
1409 	//  Add to container list.
1410 	cl.add(this);
1411 }
1412 
1413 //  Return the container window for a container node, if it is visible
getWindow(void)1414 ContainerWindow *ContainerNode::getWindow(void) {
1415 	return window;
1416 }
1417 
1418 //  Return the container view for a container node, if it is visible
getView(void)1419 ContainerView   *ContainerNode::getView(void) {
1420 	return window ? &window->getView() : NULL;
1421 }
1422 
1423 //  Destructor
~ContainerNode()1424 ContainerNode::~ContainerNode() {
1425 	//  Close the container window.
1426 	hide();
1427 
1428 	//  Remove from container list
1429 	g_vm->_cnm->remove(this);
1430 }
1431 
read(Common::InSaveFile * in)1432 void ContainerNode::read(Common::InSaveFile *in) {
1433 	//  Restore fields
1434 	object = in->readUint16LE();
1435 	type = in->readByte();
1436 	owner = in->readByte();
1437 	position.read(in);
1438 	mindType = in->readByte();
1439 	window = NULL;
1440 	action = 0;
1441 
1442 	bool shown = in->readUint16LE();
1443 
1444 	//  If this container was shown, re-show it
1445 	if (shown)
1446 		markForShow();
1447 
1448 	debugC(4, kDebugSaveload, "... object = %d", object);
1449 	debugC(4, kDebugSaveload, "... type = %d", type);
1450 	debugC(4, kDebugSaveload, "... owner = %d", owner);
1451 	debugC(4, kDebugSaveload, "... position = (%d, %d, %d, %d)", position.x, position.y, position.width, position.height);
1452 	debugC(4, kDebugSaveload, "... mindType = %d", mindType);
1453 	debugC(4, kDebugSaveload, "... shown = %d", shown);
1454 }
1455 
write(Common::MemoryWriteStreamDynamic * out)1456 void ContainerNode::write(Common::MemoryWriteStreamDynamic *out) {
1457 	//  Store fields
1458 	out->writeUint16LE(object);
1459 	out->writeByte(type);
1460 	out->writeByte(owner);
1461 	position.write(out);
1462 	out->writeByte(mindType);
1463 	out->writeUint16LE(window != NULL);
1464 
1465 	debugC(4, kDebugSaveload, "... object = %d", object);
1466 	debugC(4, kDebugSaveload, "... type = %d", type);
1467 	debugC(4, kDebugSaveload, "... owner = %d", owner);
1468 	debugC(4, kDebugSaveload, "... position = (%d, %d, %d, %d)", position.x, position.y, position.width, position.height);
1469 	debugC(4, kDebugSaveload, "... mindType = %d", mindType);
1470 	debugC(4, kDebugSaveload, "... shown = %d", window != NULL);
1471 }
1472 
1473 //  Close the container window, but leave the node.
hide(void)1474 void ContainerNode::hide(void) {
1475 	//  close the window, but don't close the object.
1476 	if (type != readyType && window != NULL) {
1477 		position = window->getExtent();     //  Save old window position
1478 		window->close();
1479 		delete window;
1480 		window = NULL;
1481 	}
1482 }
1483 
1484 //  Open the cotainer window, given the node info.
show(void)1485 void ContainerNode::show(void) {
1486 	ProtoObj        *proto = GameObject::protoAddress(object);
1487 
1488 	assert(proto);
1489 
1490 	//  open the window; Object should already be "open"
1491 	if (window == NULL) {
1492 		switch (type) {
1493 		case physicalType:
1494 			physicalContainerAppearance.rows    = proto->getViewableRows();
1495 			physicalContainerAppearance.cols    = proto->getViewableCols();
1496 			physicalContainerAppearance.totRows = proto->getMaxRows();
1497 			window = new TangibleContainerWindow(*this, physicalContainerAppearance);
1498 			break;
1499 
1500 		case deadType:
1501 			deathContainerAppearance.rows       = proto->getViewableRows();
1502 			deathContainerAppearance.cols       = proto->getViewableCols();
1503 			deathContainerAppearance.totRows    = proto->getMaxRows();
1504 			window = new TangibleContainerWindow(*this, deathContainerAppearance);
1505 			break;
1506 
1507 		case mentalType:
1508 			window = new IntangibleContainerWindow(*this, mentalContainerAppearance);
1509 			break;
1510 
1511 		case enchantType:
1512 			window = new EnchantmentContainerWindow(*this, enchantmentContainerAppearance);
1513 			break;
1514 
1515 		case readyType:
1516 		default:
1517 			return;
1518 		}
1519 	}
1520 
1521 	window->open();
1522 }
1523 
update(void)1524 void ContainerNode::update(void) {
1525 	if (type == readyType) {
1526 		//  Update ready containers if they are enabled
1527 		if (TrioCviews[owner]->getEnabled())  TrioCviews[owner]->invalidate();
1528 		if (indivCviewTop->getEnabled())        indivCviewTop->invalidate();
1529 		if (indivCviewBot->getEnabled())        indivCviewBot->invalidate();
1530 
1531 		//  If the container to update is the center brother's ready container.
1532 		if (isIndivMode() && owner == getCenterActorPlayerID()) {
1533 			//  Update player's mass & weight indicator...
1534 			MassWeightIndicator->update();
1535 		}
1536 	} else if (window) {
1537 		getView()->invalidate();
1538 		window->massBulkUpdate();
1539 	}
1540 }
1541 
1542 //  Find a container node, given a specific object
find(ObjectID id)1543 ContainerNode *ContainerManager::find(ObjectID id) {
1544 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it)
1545 		if ((*it)->object == id)
1546 			return *it;
1547 
1548 	return NULL;
1549 }
1550 
find(ObjectID id,int16 type)1551 ContainerNode *ContainerManager::find(ObjectID id, int16 type) {
1552 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it)
1553 		if ((*it)->object == id && (*it)->type == type)
1554 			return *it;
1555 
1556 	return NULL;
1557 }
1558 
1559 //  returns true if the object represented by the container can be
1560 //  accessed by the player.
isAccessable(ObjectID enactor)1561 bool ContainerNode::isAccessable(ObjectID enactor) {
1562 	Actor       *a = (Actor *)GameObject::objectAddress(enactor);
1563 	ObjectID    holder;
1564 	GameObject  *obj = GameObject::objectAddress(object);
1565 	int32       dist;
1566 
1567 	//  REM: We really ought to do a line-of-sight test here.
1568 
1569 	//  Calculate distance between actor and container.
1570 	dist = (a->getLocation() - obj->getWorldLocation()).quickHDistance();
1571 
1572 	//  If the container object is too far away we can't access any containers.
1573 	//  Note: Actors are not considered to be in possession of themselves...
1574 	holder = obj->possessor();
1575 	if (holder != Nothing || isActor(object)) {
1576 		//  "Reach" for other players is further than for other objects
1577 		if (holder != a->thisID() && dist > 96)
1578 			return false;
1579 	} else if (dist > kMaxOpenDistance)
1580 		return false;
1581 
1582 	return true;
1583 }
1584 
1585 //  Change the owner of a ready container (for indiv mode)
changeOwner(int16 newOwner)1586 void ContainerNode::changeOwner(int16 newOwner) {
1587 	owner = newOwner;
1588 	object = getPlayerActorAddress(newOwner)->getActorID();
1589 }
1590 
1591 /* ===================================================================== *
1592    ContainerManager functions
1593  * ===================================================================== */
1594 
setPlayerNum(PlayerActorID playerNum)1595 void ContainerManager::setPlayerNum(PlayerActorID playerNum) {
1596 	//  Close all containers which are not on the ground and not owned
1597 	//  by the current protagonist.
1598 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
1599 		ContainerNode *n = *it;
1600 
1601 		if (n->owner != ContainerNode::nobody && n->owner != playerNum)
1602 			n->hide();
1603 	}
1604 
1605 	//  Open any containers which belong to the new protagonist.
1606 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
1607 		ContainerNode *n = *it;
1608 
1609 		if (n->owner == playerNum)
1610 			n->markForShow();
1611 	}
1612 }
1613 
doDeferredActions(void)1614 void ContainerManager::doDeferredActions(void) {
1615 	Common::List<ContainerNode *>::iterator nextIt;
1616 	Actor           *a = getCenterActor();
1617 	TilePoint       tp = a->getLocation();
1618 	GameObject      *world = a->parent();
1619 
1620 	//  Close all containers which are not on the ground and not owned
1621 	//  by the current protagonist.
1622 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); it = nextIt) {
1623 		nextIt = it;
1624 		nextIt++;
1625 		ContainerNode *n = *it;
1626 
1627 		//  If the object is not in a player inventory (i.e. on the ground)
1628 		if (n->owner == ContainerNode::nobody) {
1629 			//  If the object is in a different world, or too far away
1630 			//  from the protagonist, then quietly close the object.
1631 			GameObject  *obj = GameObject::objectAddress(n->object);
1632 			if (obj->world() != world
1633 			        || (obj->getWorldLocation() - tp).quickHDistance() > kMaxOpenDistance) {
1634 				//  Close object image and window (silently)
1635 				obj->setFlags(0, objectOpen);
1636 				delete n;
1637 				continue;
1638 			}
1639 		}
1640 
1641 		if (n->action & ContainerNode::actionDelete) {
1642 			delete n;
1643 			continue;
1644 		}
1645 
1646 		if (n->action & ContainerNode::actionHide) {
1647 			n->hide();
1648 		} else {
1649 			if (n->action & ContainerNode::actionShow) n->show();
1650 			if (n->action & ContainerNode::actionUpdate) n->update();
1651 		}
1652 
1653 		n->action = 0;
1654 	}
1655 }
1656 
setUpdate(ObjectID id)1657 void ContainerManager::setUpdate(ObjectID id) {
1658 	//  Close all containers which are not on the ground and not owned
1659 	//  by the current protagonist.
1660 	for (Common::List<ContainerNode *>::iterator it = _list.begin(); it != _list.end(); ++it) {
1661 		ContainerNode *n = *it;
1662 
1663 		if (n->object == id)
1664 			n->update();
1665 		else if (n->type == ContainerNode::mentalType    //  Special case for mind containers
1666 		         &&  n->object == GameObject::objectAddress(id)->IDParent())
1667 			n->update();
1668 	}
1669 }
1670 
1671 extern int16 openMindType;
1672 
1673 //  General function to create a container "node". This determines what
1674 //  kind of container is appropriate, and also if a container of that
1675 //  type is already open.
CreateContainerNode(ObjectID id,bool open,int16)1676 ContainerNode *CreateContainerNode(ObjectID id, bool open, int16) {
1677 	ContainerNode   *cn = NULL;
1678 	GameObject      *obj = GameObject::objectAddress(id);
1679 	PlayerActorID   owner;
1680 
1681 	if (isActor(id)) {
1682 		if (actorIDToPlayerID(id, owner) == false)
1683 			owner = ContainerNode::nobody;
1684 
1685 		if (((Actor *)obj)->isDead()) {
1686 			//  Open dead container for dead actor
1687 			if (!(cn = g_vm->_cnm->find(owner, ContainerNode::deadType)))
1688 				cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::deadType);
1689 		} else if (owner != ContainerNode::nobody) {
1690 			return OpenMindContainer(owner, open, /*mType*/ openMindType);
1691 		}
1692 #if DEBUG
1693 		else fatal("Attempt to open non-dead actor as a container.\n");
1694 #endif
1695 	} else {
1696 		if (actorIDToPlayerID(obj->possessor(), owner) == false)
1697 			owner = ContainerNode::nobody;
1698 
1699 		if (!(cn = g_vm->_cnm->find(id, ContainerNode::physicalType)))
1700 			cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::physicalType);
1701 	}
1702 
1703 	//  If node was successfull created, and we wanted it open, and the owner
1704 	//  is the center actor or no-actor then make the container window visible.
1705 	if (cn != NULL
1706 	        &&  open
1707 	        && (owner == getCenterActorID() || owner == ContainerNode::nobody)) {
1708 		cn->show();
1709 	}
1710 
1711 	return cn;
1712 }
1713 
CreateReadyContainerNode(PlayerActorID player)1714 ContainerNode *CreateReadyContainerNode(PlayerActorID player) {
1715 	return new ContainerNode(*g_vm->_cnm,
1716 	                            getPlayerActorAddress(player)->getActorID(),
1717 	                            ContainerNode::readyType);
1718 }
1719 
OpenMindContainer(PlayerActorID player,int16 open,int16 type)1720 ContainerNode *OpenMindContainer(PlayerActorID player, int16 open, int16 type) {
1721 	ContainerNode   *cn;
1722 	ObjectID        id = getPlayerActorAddress(player)->getActorID();
1723 
1724 	if (!(cn = g_vm->_cnm->find(id, ContainerNode::mentalType))) {
1725 		cn = new ContainerNode(*g_vm->_cnm, id, ContainerNode::mentalType);
1726 		cn->mindType = type;
1727 
1728 		//  If node was successfull created, and we wanted it open, and the owner
1729 		//  is the center actor or no-actor then make the container window visible.
1730 		if (open && id == getCenterActorID()) {
1731 			cn->show();
1732 		}
1733 	} else {
1734 		IntangibleContainerWindow   *cw = (IntangibleContainerWindow *)cn->getWindow();
1735 
1736 		if (cw && (type != cn->mindType)) {
1737 			cn->mindType = type;
1738 			setMindContainer(cn->mindType, *cw);
1739 			cw->update(cw->getView().getExtent());
1740 		}
1741 	}
1742 	return cn;
1743 }
1744 
1745 /* ===================================================================== *
1746     Misc. functions
1747  * ===================================================================== */
1748 
initContainers(void)1749 void initContainers(void) {
1750 	if (containerRes == NULL)
1751 		containerRes = resFile->newContext(MKTAG('C', 'O', 'N', 'T'), "cont.resources");
1752 
1753 	selImage = g_vm->_imageCache->requestImage(imageRes, MKTAG('A', 'M', 'N', 'T'));
1754 }
1755 
cleanupContainers(void)1756 void cleanupContainers(void) {
1757 	if (selImage)
1758 		g_vm->_imageCache->releaseImage(selImage);
1759 	if (containerRes)
1760 		resFile->disposeContext(containerRes);
1761 
1762 	selImage = NULL;
1763 	containerRes = NULL;
1764 }
1765 
initContainerNodes(void)1766 void initContainerNodes(void) {
1767 #if DEBUG
1768 	//  Verify the globalContainerList only has ready ContainerNodes
1769 
1770 	ContainerNode   *node;
1771 	bool            onlyReady = true;
1772 
1773 	for (node = g_vm->_cnm->first(); node != NULL; node = node->next()) {
1774 		if (node->getType() != ContainerNode::readyType) {
1775 			onlyReady = false;
1776 			break;
1777 		}
1778 	}
1779 
1780 	assert(onlyReady);
1781 #endif
1782 }
1783 
saveContainerNodes(Common::OutSaveFile * outS)1784 void saveContainerNodes(Common::OutSaveFile *outS) {
1785 	debugC(2, kDebugSaveload, "Saving Container Nodes");
1786 
1787 	int i = 0;
1788 	int16 numNodes = 0;
1789 
1790 	//  Make sure there are no pending container view actions
1791 	g_vm->_cnm->doDeferredActions();
1792 
1793 	//  Count the number of nodes to save
1794 	for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
1795 		ContainerNode *n = *it;
1796 
1797 		if (n->getType() != ContainerNode::readyType)
1798 			numNodes++;
1799 	}
1800 
1801 	outS->write("CONT", 4);
1802 	CHUNK_BEGIN;
1803 	//  Store the number of nodes to save
1804 	out->writeSint16LE(numNodes);
1805 
1806 	debugC(3, kDebugSaveload, "... numNodes = %d", numNodes);
1807 
1808 	//  Store the nodes
1809 	for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
1810 		ContainerNode *n = *it;
1811 
1812 		if (n->getType() != ContainerNode::readyType) {
1813 			debugC(3, kDebugSaveload, "Saving ContainerNode %d", i++);
1814 			n->write(out);
1815 		}
1816 	}
1817 	CHUNK_END;
1818 }
1819 
loadContainerNodes(Common::InSaveFile * in)1820 void loadContainerNodes(Common::InSaveFile *in) {
1821 	debugC(2, kDebugSaveload, "Loading Container Nodes");
1822 
1823 	ContainerNode *node;
1824 	Common::List<ContainerNode *> tempList;
1825 	int16 numNodes;
1826 
1827 	//  Read in the number of container nodes to restore
1828 	numNodes = in->readSint16LE();
1829 	debugC(3, kDebugSaveload, "... numNodes = %d", numNodes);
1830 
1831 	for (int i = 0; i < numNodes; i++) {
1832 		debugC(3, kDebugSaveload, "Loading ContainerNode %d", i);
1833 
1834 		node = new ContainerNode;
1835 
1836 		//  Restore the state of the node
1837 		node->read(in);
1838 
1839 		//  Add it back to the container list
1840 		g_vm->_cnm->add(node);
1841 	}
1842 
1843 	assert(tempList.empty());
1844 }
1845 
cleanupContainerNodes(void)1846 void cleanupContainerNodes(void) {
1847 	if (g_vm->_cnm == nullptr)
1848 		return;
1849 
1850 	Common::Array<ContainerNode *> deletionArray;
1851 
1852 	for (Common::List<ContainerNode *>::iterator it = g_vm->_cnm->_list.begin(); it != g_vm->_cnm->_list.end(); ++it) {
1853 		ContainerNode *n = *it;
1854 
1855 		if (n->getType() != ContainerNode::readyType)
1856 			deletionArray.push_back(*it);
1857 	}
1858 
1859 	for (uint i = 0; i < deletionArray.size(); ++i)
1860 		delete deletionArray[i];
1861 }
1862 
updateContainerWindows(void)1863 void updateContainerWindows(void) {
1864 	g_vm->_cnm->doDeferredActions();
1865 }
1866 
setMindContainer(int index,IntangibleContainerWindow & cw)1867 void setMindContainer(int index, IntangibleContainerWindow &cw) {
1868 	const int classTable[] = {
1869 		protoClassIdeaContainer,
1870 		protoClassSkillContainer,
1871 		protoClassMemoryContainer,
1872 		protoClassPsychContainer    // Not used anymore
1873 	};
1874 
1875 	ObjectID        ownerID = cw.getView().node.getObject();
1876 	GameObject      *object = GameObject::objectAddress(ownerID);
1877 	ContainerIterator iter(object);
1878 	GameObject      *item;
1879 	ObjectID        id;
1880 
1881 	assert(index >= 0);
1882 	assert(index < ARRAYSIZE(classTable));
1883 
1884 	int             containerClass = classTable[index];
1885 
1886 	cw.mindSelectorCompButton->setCurrent(index);
1887 	cw.mindSelectorCompButton->invalidate();
1888 
1889 	while ((id = iter.next(&item)) != Nothing) {
1890 		if (item->proto()->classType == containerClass) {
1891 			cw.view->setContainer(item);
1892 			return;
1893 		}
1894 	}
1895 }
1896 
APPFUNC(cmdMindContainerFunc)1897 APPFUNC(cmdMindContainerFunc) {
1898 	if (ev.panel && ev.eventType == gEventNewValue /* && ev.value */) {
1899 		IntangibleContainerWindow   *cw = (IntangibleContainerWindow *)ev.window;
1900 		ContainerNode   &nd = cw->getView().node;
1901 		int             newMindType = nd.mindType;
1902 
1903 		const Rect16 idea(0, 0, 22, 67),      // idea button click area
1904 		             skill(22, 0, 11, 67),    // skill area
1905 		             memory(33, 0,  9, 67),   // memory area
1906 		             psych(42, 0, 10, 67);    // psych(ic?) area
1907 
1908 		if (idea.ptInside(ev.mouse))    newMindType = 0; //protoClassIdeaContainer;
1909 		if (skill.ptInside(ev.mouse))   newMindType = 1; //protoClassSkillContainer;
1910 		if (memory.ptInside(ev.mouse))  newMindType = 2; //protoClassMemoryContainer;
1911 //		if (psych.ptInside(ev.mouse))   newMindType = protoClassPsychContainer;
1912 
1913 		if (newMindType != nd.mindType) {
1914 			nd.mindType = newMindType;
1915 			setMindContainer(nd.mindType, *cw);
1916 			cw->update(cw->getView().getExtent());
1917 		}
1918 	} else if (ev.eventType == gEventMouseMove) {
1919 		//if (ev.value == gCompImage::enter)
1920 		{
1921 			const Rect16 idea(0, 0, 22, 67),      // idea button click area
1922 			             skill(22, 0, 11, 67),    // skill area
1923 			             memory(33, 0,  9, 67);   // memory area
1924 
1925 
1926 			const int BUF_SIZE = 64;
1927 			char    textBuffer[BUF_SIZE];
1928 			int     mindType = -1;
1929 
1930 
1931 			if (idea.ptInside(ev.mouse))       mindType = 0;    //protoClassIdeaContainer;
1932 			if (skill.ptInside(ev.mouse))  mindType = 1;    //protoClassSkillContainer;
1933 			if (memory.ptInside(ev.mouse)) mindType = 2;    //protoClassMemoryContainer;
1934 
1935 			switch (mindType) {
1936 			case 0:
1937 				sprintf(textBuffer, IDEAS_MENTAL);
1938 				break;
1939 
1940 			case 1:
1941 				sprintf(textBuffer, SPELL_MENTAL);
1942 				break;
1943 
1944 			case 2:
1945 				sprintf(textBuffer, SKILL_MENTAL);
1946 				break;
1947 
1948 			case -1:
1949 				textBuffer[0] = 0;
1950 				break;
1951 
1952 			default:
1953 				assert(false);   // should never get here
1954 				break;
1955 			}
1956 
1957 			// set the text in the cursor
1958 			g_vm->_mouseInfo->setText(textBuffer);
1959 		}
1960 
1961 		if (ev.value == GfxCompImage::leave) {
1962 			g_vm->_mouseInfo->setText(NULL);
1963 		}
1964 	}
1965 }
1966 
APPFUNC(cmdCloseButtonFunc)1967 APPFUNC(cmdCloseButtonFunc) {
1968 	if (ev.eventType == gEventNewValue && ev.value == 1) {
1969 		ContainerWindow     *win = (ContainerWindow *)ev.window;
1970 
1971 		if (win->getView().node.getType() == ContainerNode::mentalType) {
1972 			win->getView().node.markForDelete();
1973 		} else {
1974 			win->containerObject()->close(getCenterActorID());
1975 		}
1976 		updateContainerWindows();
1977 
1978 		// make sure the hint text goes away
1979 		if (g_vm->_mouseInfo->getObject() == NULL) {
1980 			g_vm->_mouseInfo->setText(NULL);
1981 		}
1982 	} else if (ev.eventType == gEventMouseMove) {
1983 		if (ev.value == GfxCompImage::enter) {
1984 			g_vm->_mouseInfo->setText(CLOSE_MOUSE);
1985 		} else if (ev.value == GfxCompImage::leave) {
1986 			g_vm->_mouseInfo->setText(NULL);
1987 		}
1988 	}
1989 }
1990 
APPFUNC(cmdScrollFunc)1991 APPFUNC(cmdScrollFunc) {
1992 	if (ev.panel && ev.eventType == gEventNewValue && ev.value) {
1993 		ScrollableContainerWindow *cw;
1994 		const Rect16 upArea(0, 0, 44, 22);
1995 
1996 		cw = (ScrollableContainerWindow *)ev.window;
1997 		if (upArea.ptInside(ev.mouse))
1998 			cw->scrollUp();
1999 		else
2000 			cw->scrollDown();
2001 		ev.window->update(cw->getView().getExtent());
2002 	} else if (ev.eventType == gEventMouseMove) {
2003 		if (ev.value == GfxCompImage::enter) {
2004 			g_vm->_mouseInfo->setText(SCROLL_MOUSE);
2005 		} else if (ev.value == GfxCompImage::leave) {
2006 			g_vm->_mouseInfo->setText(NULL);
2007 		}
2008 	}
2009 }
2010 
2011 } // end of namespace Saga2
2012