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/blitters.h"
29 #include "saga2/objects.h"
30 #include "saga2/contain.h"
31 #include "saga2/intrface.h"
32 #include "saga2/grabinfo.h"
33 #include "saga2/uidialog.h"
34 #include "saga2/motion.h"
35 #include "saga2/enchant.h"
36 #include "saga2/display.h"
37 #include "saga2/localize.h"
38 #include "saga2/imagcach.h"
39 #include "saga2/fontlib.h"
40 #include "saga2/uimetrcs.h"
41 
42 namespace Saga2 {
43 
44 /* ===================================================================== *
45    External delarations
46  * ===================================================================== */
47 extern ReadyContainerView   *TrioCviews[kNumViews];
48 extern ReadyContainerView   *indivCviewTop, *indivCviewBot;
49 extern gPanelList           *trioControls, *indivControls;
50 extern gPanelList           *playControls;
51 extern const uint32         imageGroupID;
52 extern gPanelList           *tileControls;
53 extern BackWindow           *mainWindow;
54 extern uint8                fixedColors[16];
55 
56 
57 /* ===================================================================== *
58    Classes
59  * ===================================================================== */
60 
61 //	Private subclass of GfxCompImage for armor display
62 
63 class gArmorIndicator : public GfxCompImage {
64 public:
65 	ArmorAttributes  attr;
66 	void drawClipped(gPort &,
67 	                 const   Point16 &,
68 	                 const   Rect16 &);
69 
70 	void setValue(PlayerActorID pID);
71 
gArmorIndicator(gPanelList & list,const Rect16 & box,void * img,uint16 ident,AppFunc * cmd=nullptr)72 	gArmorIndicator(gPanelList &list, const Rect16 &box, void *img, uint16 ident, AppFunc *cmd = nullptr)
73 		: GfxCompImage(list, box, img, ident, cmd) {
74 		attr.damageAbsorbtion = 0;
75 		attr.damageDivider = 1;
76 		attr.defenseBonus = 0;
77 	}
78 };
79 
80 //	Private subclass of gControl for enchantment display
81 //	REM: Some of this should probably be moved to a header file, even though no-one
82 //	is ever going to need it other than this file.
83 
84 enum enchantmentIcons {
85 	iconInvisible = 0,
86 	iconDetectPoison,
87 	iconSoulSight,
88 	iconPoisoned,
89 	iconParalysed,
90 	iconClumsy,
91 	iconFrozen,
92 	iconConstrained,
93 	iconAfraid,
94 	iconSeawalk,
95 	iconHaste,
96 	iconFirewalk,
97 	iconSurestrike,
98 	iconAdrenalFervor,
99 	iconInnerBalance,
100 	iconShadowWalk,
101 	iconBattleFever,
102 	iconSunWard,
103 	iconNetherWard,
104 	iconSpellBarrier,
105 	iconIronskin,
106 	iconNumbscent,
107 	iconProtectEvil,
108 	iconProtectUndead,
109 	iconProtectGhosts,
110 	iconCushionAir,
111 	iconResistImpact,
112 	iconResistSlash,
113 	iconResistProjectile,
114 	iconResistFire,
115 	iconResistAcid,
116 	iconResistHeat,
117 	iconResistCold,
118 	iconResistLightning,
119 	iconResistPoison,
120 	iconResistPsionic,
121 	iconResistDirectMagic,
122 	iconImmuneFire,
123 	iconImmuneAcid,
124 	iconImmuneHeat,
125 	iconImmuneCold,
126 	iconImmuneLightning,
127 	iconImmunePoison,
128 	iconImmunePsionic,
129 
130 	iconCount
131 };
132 
133 const char *enchantmentNames[] = {
134 	"Invisible",
135 	"Detect Poison",
136 	"Soul Sight",
137 	"Poisoned",
138 	"Paralysed",
139 	"Clumsy",
140 	"Frozen",
141 	"Constrained",
142 	"Afraid",
143 	"Seawalk",
144 	"Haste",
145 	"Fire Walk",
146 	"Surestrike",
147 	"Adrenal Fervor",
148 	"Inner Balance",
149 	"Shadow Walk",
150 	"Battle Fever",
151 	"Sun Ward",
152 	"Nether Ward",
153 	"Spell Barrier",
154 	"Ironskin",
155 	"Numbscent",
156 	"Protection from Evil",
157 	"Protection from Undead",
158 	"Protection from Ghosts",
159 	"Cushion of Air",
160 	"Resist impact damage",
161 	"Resist slashing damage",
162 	"Resist projectile damage",
163 	"Resist Fire / Yellow Magic",
164 	"Resist Acid / Violet Magic",
165 	"Resist Heat / Red Magic",
166 	"Resist Cold / Blue Magic",
167 	"Resist Lightning / Orange Magic",
168 	"Resist Poison / Green Magic",
169 	"Resist Mental Damage",
170 	"Resist Direct Magic",
171 	"Immunity to Fire / Yellow Magic",
172 	"Immunity to Acid / Violet Magic",
173 	"Immunity to Heat / Red Magic",
174 	"Immunity to Cold / Blue Magic",
175 	"Immunity to Lightning / Orange Magic",
176 	"Immunity to Poison / Green Magic",
177 	"Immunity to Mental Damage",
178 
179 	nullptr
180 };
181 
182 class gEnchantmentDisplay : public gControl {
183 
184 	uint8       iconFlags[iconCount];
185 
186 	void drawClipped(gPort &, const Point16 &, const Rect16 &);
187 	void pointerMove(gPanelMessage &msg);
188 public:
189 	void setValue(PlayerActorID pID);
190 
gEnchantmentDisplay(gPanelList & list,uint16 ident,AppFunc * cmd=nullptr)191 	gEnchantmentDisplay(gPanelList &list, uint16 ident, AppFunc *cmd = nullptr)
192 		: gControl(list, Rect16(0, 0, 630, 18), nullptr, ident, cmd) {
193 		memset(iconFlags, 0, sizeof iconFlags);
194 	}
195 };
196 
197 /* ===================================================================== *
198    Imports
199  * ===================================================================== */
200 
201 #ifndef FTAASM_H
202 extern void compositePixels(
203     gPixelMap       *compMap,
204     gPixelMap       *sprMap,
205     int32           xpos,
206     int32           ypos,
207     uint8           *lookup);
208 
209 extern void unpackImage(gPixelMap *map,
210                                   int32 width,
211                                   int32 rowCount,
212                                   int8 *srcData);
213 #endif
214 
215 // this is a redeclare of the struct in playmode.cpp
216 typedef struct {
217 	Point16     size;
218 	int16       compress;
219 	int8        data[2];
220 } ImageHeader;
221 
222 
223 // external appfuncs
224 APPFUNC(cmdBrain);
225 
226 /* ===================================================================== *
227    User interface application functions
228  * ===================================================================== */
229 APPFUNC(cmdPortrait);
230 APPFUNC(cmdAggressive);
231 //APPFUNC( cmdJump );
232 APPFUNC(cmdArmor);
233 APPFUNC(cmdCenter);
234 APPFUNC(cmdBand);
235 APPFUNC(cmdOptions);
236 APPFUNC(cmdBroChange);
237 APPFUNC(cmdHealthStar);
238 APPFUNC(cmdMassInd);
239 APPFUNC(cmdBulkInd);
240 APPFUNC(cmdManaInd);
241 
242 /* ===================================================================== *
243    User control metrics
244  * ===================================================================== */
245 
246 // position arrays for all buttons on the individual panels
247 static const StaticRect topBox[numButtons] = {
248 	/* portrait          */ { 489, 22 + (yContOffset * 0), 65, 72 },
249 	/* agress            */ { 559, 86 + (yContOffset * 0), 28, 27 },
250 	/* jump              */ { 592, 86 + (yContOffset * 0), 28, 27 },
251 	/* center            */ { 559, 56 + (yContOffset * 0), 28, 27 },
252 	/* banding           */ { 592, 56 + (yContOffset * 0), 28, 27 },
253 	/* namePlates        */ { 488, 94 + (yFaceOffset * 0), 65, 15 },
254 	/* namePlateFrames   */ { 487, 20 + (yFaceOffset * 0), 69, 92 }
255 };
256 
257 
258 static const StaticRect midBox[numButtons] = {
259 	{ 489, 22 + (yContOffset * 1), 65, 72 },
260 	{ 559, 86 + (yContOffset * 1), 28, 27 },
261 	{ 592, 86 + (yContOffset * 1), 28, 27 },
262 	{ 559, 56 + (yContOffset * 1), 28, 27 },
263 	{ 592, 56 + (yContOffset * 1), 28, 27 },
264 	{ 488, 94 + (yFaceOffset * 1), 65, 15 },
265 	{ 487, 20 + (yFaceOffset * 1), 69, 92 }
266 };
267 
268 
269 
270 static const StaticRect botBox[numButtons] = {
271 	{ 489, 22 + (yContOffset * 2), 65, 72 },
272 	{ 559, 86 + (yContOffset * 2), 28, 27 },
273 	{ 592, 86 + (yContOffset * 2), 28, 27 },
274 	{ 559, 56 + (yContOffset * 2), 28, 27 },
275 	{ 592, 56 + (yContOffset * 2), 28, 27 },
276 	{ 488, 94 + (yFaceOffset * 2), 65, 15 },
277 	{ 487, 20 + (yFaceOffset * 2), 69, 92 }
278 };
279 
280 
281 
282 // options button
283 GfxCompButton         *optBtn;
284 gEnchantmentDisplay *enchDisp;
285 
286 // brother buttons
287 GfxOwnerSelCompButton *julBtn;
288 GfxOwnerSelCompButton *phiBtn;
289 GfxOwnerSelCompButton *kevBtn;
290 GfxCompImage          *broBtnFrame;
291 
292 
293 // trio controls
294 GfxMultCompButton     *portBtns[kNumViews];
295 GfxOwnerSelCompButton *aggressBtns[kNumViews];
296 //GfxCompButton           *jumpBtns[kNumViews];
297 GfxOwnerSelCompButton *centerBtns[kNumViews];
298 GfxOwnerSelCompButton *bandingBtns[kNumViews];
299 GfxCompImage          *namePlates[kNumViews];
300 GfxCompImage          *namePlateFrames[kNumViews];
301 gArmorIndicator     *armorInd[kNumViews];
302 
303 // individual
304 GfxMultCompButton     *indivPortBtn;
305 GfxOwnerSelCompButton *indivAggressBtn;
306 //GfxCompButton           *indivJumpBtn;
307 GfxOwnerSelCompButton *indivCenterBtn;
308 GfxOwnerSelCompButton *indivBandingBtn;
309 GfxCompImage          *indivNamePlate;
310 GfxCompImage          *indivNamePlateFrame;
311 gArmorIndicator     *indivArmorInd;
312 
313 // mental button/indicators
314 GfxCompButton         *menConBtn;
315 
316 
317 // [brother panels] compressed image non-allocated pointer arrays
318 void **portImag[kNumViews];
319 void **aggressImag;
320 //void **jumpImag;
321 void **centerImag;
322 void **bandingImag;
323 void **menConBtnImag;
324 void **optBtnImag;
325 void **julBtnImag;
326 void **phiBtnImag;
327 void **kevBtnImag;
328 
329 // portrait name plate things
330 void *namePlateImages[kNumViews];
331 void *namePlateFrameImag;
332 void *armorImag;
333 
334 // brother selection button frame
335 void *broBtnFrameImag;
336 
337 // name plate resource indexes
338 int16   namePlateResNum[] = { 8, 9, 10 };
339 
340 // number of plate images
341 int16   numNamePlateRes[] = { 1, 1, 1 };
342 
343 // brother resource names
344 ResName broNames[] = {
345 	{ 'J', 'U', 'L', 0 },
346 	{ 'P', 'H', 'I', 0 },
347 	{ 'K', 'E', 'V', 0 }
348 };
349 
350 const uint32 containerGroupID = MKTAG('C', 'O', 'N', 'T');
351 
352 
353 // button position views
354 // topBox, midBox, and botBox are defined in uimetrcs.h
355 static const StaticRect *views[] = {
356 	topBox,
357 	midBox,
358 	botBox
359 };
360 
361 // individual indicators/buttons
362 static const StaticRect menConBtnRect = {485, 265, 44, 43};
363 
364 // options button
365 static const StaticRect optBtnRect = {20, 445, 26, 15};
366 
367 // brother buttons and frame
368 static const StaticRect broBtnRect = {481, 450, 144, 11};
369 static const StaticRect julBtnRect = {482, 451, 44, 9};
370 static const StaticRect phiBtnRect = {531, 451, 44, 9};
371 static const StaticRect kevBtnRect = {580, 451, 44, 9};
372 
373 
374 StaticTextPallete genericTextPal = {9 + 15, 20, 14, 11, 23, 17};
375 /*  uint8   dlPen;
376     uint8   urPen;
377     uint8   inPen;
378     uint8   dlHilitePen;
379     uint8   urHilitePen;
380     uint8   inHilitePen; */
381 
382 //  [Added here by Talin to fix problem in Win32. I don't know if it's the right place]
383 class CPortrait *Portrait;
384 
385 /* ===================================================================== *
386    ui controls
387  * ===================================================================== */
388 
389 // which brother is set in the individual controls
390 uint16  indivBrother;
391 
392 bool isBrotherDead(PlayerActorID brotherID);
393 
394 /* ===================================================================== *
395    Global class declarations
396  * ===================================================================== */
397 
398 // these objhets have to be initialized after resource are initialized
399 CStatusLine             *StatusLine          = nullptr;
400 CMassWeightIndicator    *MassWeightIndicator = nullptr;
401 CHealthIndicator        *HealthIndicator     = nullptr;
402 CManaIndicator          *ManaIndicator       = nullptr;
403 
404 /* ===================================================================== *
405    Plaq writing class
406  * ===================================================================== */
407 
CPlaqText(gPanelList & list,const Rect16 & box,const char * msg,gFont * font,int16 textPos,textPallete & pal,int16 ident,AppFunc * cmd)408 CPlaqText::CPlaqText(gPanelList     &list,
409                      const Rect16  &box,
410                      const char    *msg,
411                      gFont         *font,
412                      int16         textPos,
413                      textPallete   &pal,
414                      int16         ident,
415                      AppFunc       *cmd)
416 	: gControl(list, box, msg, ident, cmd) {
417 	if (strlen(msg) <= bufSize) {
418 		strcpy(lineBuf, msg);
419 	} else {
420 		*lineBuf = '\0';
421 	}
422 
423 	textFacePal     = pal;
424 	buttonFont      = font;
425 	textRect        = box;
426 	textPosition    = textPos;
427 	oldFont         = nullptr;
428 }
429 
enable(bool abled)430 void CPlaqText::enable(bool abled) {
431 	gPanel::enable(abled);
432 }
433 
invalidate(Rect16 *)434 void CPlaqText::invalidate(Rect16 *) {
435 	window.update(_extent);
436 }
437 
draw(void)438 void CPlaqText::draw(void) {
439 	gPort           &port = window.windowPort;
440 	Rect16          rect = window.getExtent();
441 
442 
443 	// save pen color, etc.
444 	SAVE_GPORT_STATE(port);
445 	oldFont = port.font;
446 
447 	// setup the port
448 	port.setMode(drawModeMatte);
449 	port.setFont(buttonFont);
450 
451 	g_vm->_pointer->hide(port, _extent);              // hide mouse pointer
452 	drawClipped(port, Point16(0, 0), Rect16(0, 0, rect.width, rect.height));
453 	g_vm->_pointer->show(port, _extent);              // show mouse pointer
454 
455 	// reset the old font
456 	port.setFont(oldFont);
457 }
458 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)459 void CPlaqText::drawClipped(gPort &port,
460                             const Point16 &offset,
461                             const Rect16 &r) {
462 	if (_extent.overlap(r)) {
463 		if (*lineBuf) {
464 			textRect = _extent;
465 			textRect.x -= offset.x;
466 			textRect.y -= offset.y;
467 
468 
469 			writePlaqText(port, textRect, buttonFont, textPosition, textFacePal, selected,  lineBuf);
470 		}
471 	}
472 }
473 
474 /* ===================================================================== *
475     Portrait control class
476  * ===================================================================== */
477 
CPortrait(GfxMultCompButton ** portraits,GfxMultCompButton * indivPort,const uint16 numPorts,uint16 numBrothers)478 CPortrait::CPortrait(GfxMultCompButton **portraits,
479                      GfxMultCompButton *indivPort,
480                      const uint16 numPorts,
481                      uint16 numBrothers) {     // numBrothers = post 1
482 	// do some checking
483 	assert(portraits);
484 	assert(indivPort);
485 
486 	for (uint16 i = 0; i < numBrothers; i++) {
487 		assert(portraits[i]);
488 	};
489 
490 	buttons     = portraits;    // set the pointer for class
491 	indivButton = indivPort;    // set the individual portrait
492 	numButtons  = numPorts;     // number of buttons per pointer
493 	_numViews    = numBrothers;  // number of pointers for whole array
494 
495 	// start off in a normal facial state
496 	for (uint16 i = 0; i < _numViews + 1; i++) {
497 		currentState[i] = kPortraitNormal;
498 	}
499 }
500 
setPortrait(uint16 brotherID)501 void CPortrait::setPortrait(uint16 brotherID) {
502 	assert(brotherID < _numViews + 1);
503 
504 	// tell button to select and display new image
505 	if (brotherID == uiIndiv) {
506 		WriteStatusF(4, " Brother id %d", brotherID);
507 
508 		indivButton->setCurrent(currentState[brotherID]);
509 		indivButton->invalidate();
510 	} else {
511 		buttons[brotherID]->setCurrent(currentState[brotherID]);
512 		buttons[brotherID]->invalidate();
513 	}
514 }
515 
set(uint16 brotherID,PortraitType type)516 void CPortrait::set(uint16 brotherID, PortraitType type) {
517 	currentState[brotherID] = type;
518 
519 	setPortrait(brotherID);
520 }
521 
ORset(uint16 brotherID,PortraitType type)522 void CPortrait::ORset(uint16 brotherID, PortraitType type) { // brotherID = post 0
523 	assert(brotherID < _numViews + 1);
524 
525 	if (type == currentState[brotherID]) {
526 		currentState[brotherID] = kPortraitNormal;
527 	} else {
528 		currentState[brotherID] = type;
529 	}
530 
531 	// set this button to the new state
532 	setPortrait(brotherID);
533 }
534 
getStateString(char buf[],int8 size,uint16 brotherID)535 void CPortrait::getStateString(char buf[], int8 size, uint16 brotherID) {
536 	static char asleepStr[]         = ASLEEP_STATE;
537 	static char paralysedStr[]      = PARALY_STATE;
538 	static char blindStr[]          = BLIND_STATE ;
539 	static char afraidStr[]         = AFRAID_STATE;
540 	static char angryStr[]          = ANGRY_STATE ;
541 	static char badlyWoundedStr[]   = BADWND_STATE;
542 	static char hurtStr[]           = HURT_STATE  ;
543 	static char poisonedStr[]       = POISON_STATE;
544 	static char diseasedStr[]       = DISEAS_STATE;
545 	static char normalStr[]         = NORMAL_STATE;
546 	static char commaStr[]          = ", ";
547 
548 	PlayerActor     *pa = getPlayerActorAddress(brotherID);
549 	Actor           *a = pa->getActor();
550 	ActorAttributes &stats = pa->getBaseStats();
551 
552 	buf[size - 1] = '\0';
553 
554 	if (a->isDead()) {
555 		Common::strlcpy(buf, DEAD_STATE, size);
556 		return;
557 	}
558 
559 	buf[0] = '\0';
560 
561 	if (a->_enchantmentFlags & (1 << actorAsleep)) {
562 		Common::strlcat(buf, asleepStr, size);
563 	} else if (a->_enchantmentFlags & (1 << actorParalyzed)) {
564 		Common::strlcat(buf, paralysedStr, size);
565 	} else if (a->_enchantmentFlags & (1 << actorBlind)) {
566 		Common::strlcat(buf, blindStr, size);
567 	} else if (a->_enchantmentFlags & (1 << actorFear)) {
568 		Common::strlcat(buf, afraidStr, size);
569 	} else if (pa->isAggressive()) {
570 		Common::strlcat(buf, angryStr, size);
571 	}
572 
573 	if (stats.vitality >= a->_effectiveStats.vitality * 3) {
574 		if (buf[0] != '\0')	// strlen(buf) > 0
575 			Common::strlcat(buf, commaStr, size);
576 
577 		Common::strlcat(buf, badlyWoundedStr, size);
578 	} else if (stats.vitality * 2 > a->_effectiveStats.vitality * 3) {
579 		if (buf[0] != '\0')	// strlen(buf) > 0
580 			Common::strlcat(buf, commaStr, size);
581 
582 		Common::strlcat(buf, hurtStr, size);
583 	}
584 
585 	if (a->_enchantmentFlags & (1 << actorPoisoned)) {
586 		if (buf[0] != '\0')	// strlen(buf) > 0
587 			Common::strlcat(buf, commaStr, size);
588 
589 		Common::strlcat(buf, poisonedStr, size);
590 	} else if (a->_enchantmentFlags & (1 << actorDiseased)) {
591 		if (buf[0] != '\0')	// strlen(buf) > 0
592 			Common::strlcat(buf, commaStr, size);
593 
594 		Common::strlcat(buf, diseasedStr, size);
595 	}
596 
597 	if (buf[0] == '\0')	// strlen(buf) == 0
598 		Common::strlcat(buf, normalStr, size);
599 }
600 
601 /* ===================================================================== *
602    status line gadget
603  * ===================================================================== */
604 
605 // status line class
CStatusLine(gPanelList & list,const Rect16 & box,const char * msg,gFont * font,int16 textPos,textPallete pal,int32,int16 ident,AppFunc * cmd)606 CStatusLine::CStatusLine(gPanelList         &list,
607                          const Rect16    &box,
608                          const char            *msg,
609                          gFont           *font,
610                          int16           textPos,
611                          textPallete     pal,
612                          int32           /*frameTime*/,
613                          int16           ident,
614                          AppFunc         *cmd) :
615 	CPlaqText(list, box, msg, font, textPos, pal, ident, cmd) {
616 
617 	lineDisplayed = false;
618 	queueHead = queueTail = 0;
619 
620 	for (int i = 0; i < ARRAYSIZE(lineQueue); i++) {
621 		lineQueue[i].text = nullptr;
622 		lineQueue[i].frameTime = 0;
623 	}
624 	waitAlarm.basetime = waitAlarm.duration = 0;
625 	minWaitAlarm.basetime = minWaitAlarm.duration = 0;
626 }
627 
~CStatusLine(void)628 CStatusLine::~CStatusLine(void) {
629 	while (queueTail != queueHead) {
630 		assert(lineQueue[queueTail].text != nullptr);
631 
632 		delete[] lineQueue[queueTail].text;
633 		queueTail = bump(queueTail);
634 	}
635 }
636 
setLine(char * msg,uint32 frameTime)637 void CStatusLine::setLine(char *msg, uint32 frameTime) { // frametime def
638 	uint8       newHead = bump(queueHead);
639 
640 	if (newHead != queueTail) {
641 		size_t      msgLen = strlen(msg);
642 
643 		if ((lineQueue[queueHead].text = new char[msgLen + 1]()) !=  nullptr) {
644 			strcpy(lineQueue[queueHead].text, msg);
645 			lineQueue[queueHead].frameTime = frameTime;
646 			queueHead = newHead;
647 		}
648 	}
649 }
650 
experationCheck(void)651 void CStatusLine::experationCheck(void) {
652 	if (lineDisplayed
653 	        && (waitAlarm.check()
654 	            || (queueTail != queueHead && minWaitAlarm.check()))) {
655 		enable(false);
656 		window.update(_extent);
657 
658 		lineDisplayed = false;
659 	}
660 
661 	if (!lineDisplayed && queueTail != queueHead) {
662 		// enable the control
663 		enable(true);
664 
665 		// set up the time for this message
666 		waitAlarm.set(lineQueue[queueTail].frameTime);
667 		minWaitAlarm.set(lineQueue[queueTail].frameTime / 5);
668 
669 		// copy upto the buffer's size in chars
670 		Common::strlcpy(lineBuf, lineQueue[queueTail].text, bufSize);
671 		lineBuf[bufSize - 1] = '\0';
672 
673 		//  free the queue text buffer
674 		delete[] lineQueue[queueTail].text;
675 		lineQueue[queueTail].text = nullptr;
676 
677 		//  bump the queue tail
678 		queueTail = bump(queueTail);
679 
680 		// draw the new textline
681 		window.update(_extent);
682 
683 		lineDisplayed = true;
684 	}
685 }
686 
clear(void)687 void CStatusLine::clear(void) {
688 	enable(false);
689 	window.update(_extent);
690 	lineDisplayed = false;
691 
692 	queueHead = queueTail = 0;
693 }
694 
695 /* ===================================================================== *
696     CMassWeightInterface: mass and weight allowence indicators
697  * ===================================================================== */
698 
699 bool CMassWeightIndicator::bRedraw;
700 
CMassWeightIndicator(gPanelList * panel,const Point16 & pos,uint16 type,bool death)701 CMassWeightIndicator::CMassWeightIndicator(gPanelList *panel, const Point16 &pos, uint16 type, bool death) {
702 
703 	// set up the position of this indicator
704 	backImagePos    = pos;
705 	massPiePos      = backImagePos;
706 	bulkPiePos      = backImagePos;
707 
708 	massPiePos.x    += massPieXOffset;
709 	massPiePos.y    += massPieYOffset;
710 	bulkPiePos.x    += bulkPieXOffset;
711 	bulkPiePos.y    += bulkPieYOffset;
712 
713 	bRedraw         = true; // this MUST be true or the indicators will not draw the first time
714 
715 	// attach the resource context
716 	containerRes = resFile->newContext(containerGroupID, "container context");
717 
718 	// setup mass/bulk indicator imagery
719 	if (death) {
720 		massBulkImag = g_vm->_imageCache->requestImage(containerRes, MKTAG('D', 'J', 'B', massBulkResNum));
721 
722 		pieIndImag = loadImageRes(containerRes, pieIndResNum, numPieIndImages, 'D', 'A', 'J');
723 	} else {
724 
725 		massBulkImag = g_vm->_imageCache->requestImage(containerRes, MKTAG('G', 'J', 'B', massBulkResNum));
726 
727 		pieIndImag = loadImageRes(containerRes, pieIndResNum, numPieIndImages, 'G', 'A', 'J');
728 	}
729 
730 	// attach controls to the indivControls panel
731 	// these butttons will get deactivated along with the panel
732 	pieMass = new GfxCompImage(*panel,
733 	                                       Rect16(massPiePos.x, massPiePos.y, pieXSize, pieYSize),
734 	                                       pieIndImag,
735 	                                       numPieIndImages,
736 	                                       0,
737 	                                       type,
738 	                                       cmdMassInd);
739 
740 	pieBulk = new GfxCompImage(*panel,
741 	                                       Rect16(bulkPiePos.x, bulkPiePos.y, pieXSize, pieYSize),
742 	                                       pieIndImag,
743 	                                       numPieIndImages,
744 	                                       0,
745 	                                       type,
746 	                                       cmdBulkInd);
747 
748 	// mass/bulk back image
749 	new GfxCompImage(*panel,
750 	                             Rect16(backImagePos.x, backImagePos.y, backImageXSize, backImageYSize),
751 	                             massBulkImag,
752 	                             uiIndiv,
753 	                             nullptr);
754 
755 	// release resource context
756 	if (containerRes) {
757 		resFile->disposeContext(containerRes);
758 		containerRes = nullptr;
759 	}
760 
761 	currentMass = 0;
762 	currentBulk = 0;
763 
764 	// if this is something other then the ready containers
765 	if (type > 1) {
766 		containerObject = (GameObject *)panel->userData;
767 	} else {
768 		containerObject = nullptr;
769 	}
770 
771 	g_vm->_indList.push_back(this);
772 }
773 
~CMassWeightIndicator(void)774 CMassWeightIndicator::~CMassWeightIndicator(void) {
775 	g_vm->_indList.remove(this);
776 
777 	unloadImageRes(pieIndImag, numPieIndImages);
778 	g_vm->_imageCache->releaseImage(massBulkImag);
779 }
780 
781 /*****************************************************************************
782 ** Method: recalculate()
783 ** Description: This will recalculate the values to be displayed for the
784 **              mass and bulk of the current ( single mode ) player
785 **/
recalculate(void)786 void CMassWeightIndicator::recalculate(void) {
787 	assert(pieMass);
788 	assert(pieBulk);
789 
790 	uint16 mass = getMassPieDiv();
791 	uint16 bulk = getBulkPieDiv();
792 	uint16 retMass, retBulk;
793 
794 
795 	if (containerObject) {
796 		setMassPie(retMass = getWeightRatio(containerObject, mass, false));
797 		setBulkPie(retBulk = getBulkRatio(containerObject, bulk, false));
798 	} else {
799 		setMassPie(retMass = getWeightRatio(g_vm->_playerList[getCenterActorPlayerID()]->getActor(), mass, false));
800 		setBulkPie(retBulk = getBulkRatio(g_vm->_playerList[getCenterActorPlayerID()]->getActor(), bulk, false));
801 	}
802 }
803 
804 /*****************************************************************************
805 ** Method: update()
806 ** Description: This will call recalculate and then invalidate the entire
807 **              weight/bulk control ( so it refreshes )
808 **/
update(void)809 void CMassWeightIndicator::update(void) {
810 	if (bRedraw == true) {
811 		for (Common::List<CMassWeightIndicator *>::iterator it = g_vm->_indList.begin(); it != g_vm->_indList.end(); ++it) {
812 			(*it)->recalculate();
813 			(*it)->invalidate();
814 		}
815 
816 		bRedraw = false;
817 	}
818 }
819 
820 /* ===================================================================== *
821     CManaIndicator: magic stuff indicator
822  * ===================================================================== */
823 
824 // setup the mana color tables
825 static uint8 manaColorMap[CManaIndicator::numManaTypes][CManaIndicator::numManaColors + 9] = {
826 	// each row has 9 leading zero's to acount for windows colors
827 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x74, 0x73, 0x72, 0x80, 0x84, 0x83, 0x82, 0x82, 0x81, 0x81, 0xF4, 0xE9 }, // Red
828 
829 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x74, 0x73, 0x72, 0x78, 0x77, 0x76, 0x75, 0x6B, 0x6A, 0x69, 0x3A, 0x39 }, // Orange
830 
831 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59 }, // Yellow
832 
833 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2, 0xE1, 0xE0, 0xDF, 0xDE, 0xC9 }, // Green
834 
835 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA0, 0x9F, 0x9E, 0xB2, 0xB1, 0xB0, 0xAF, 0xAE, 0xAD, 0xAC, 0xAA, 0xA9 }, // Blue
836 
837 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA0, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x93, 0x92, 0x92, 0x91, 0x0C }  // Violet
838 };
839 
840 
CManaIndicator(gPanelList & list)841 CManaIndicator::CManaIndicator(gPanelList &list) : GfxCompImage(list,
842 	        Rect16(x, y, xSize, ySize),
843 	        nullptr,
844 	        0,
845 	        cmdManaInd) {
846 	assert(resFile);
847 
848 	// init the resource handle with the mana resource group
849 	resContext  = resFile->newContext(MKTAG('M', 'A', 'N', 'A'), "mana context");
850 
851 	// load star images
852 	starImages = loadImageRes(resContext, starResNum, numStars, 'S', 'T', 'A');
853 
854 	// load in the ring images
855 	ringImages = loadImageRes(resContext, ringResNum, numRings, 'R', 'N', 'G');
856 
857 	backImage = g_vm->_imageCache->requestImage(resContext, MKTAG('B', 'A', 'C', 'K'));
858 
859 	wellImage = g_vm->_imageCache->requestImage(resContext, MKTAG('W', 'E', 'L', 'L'));
860 
861 	// hmm this could be cleaner...
862 	starRingEndPos[0] = Point16(redEndX,    redEndY);
863 	starRingEndPos[1] = Point16(orangeEndX, orangeEndY);
864 	starRingEndPos[2] = Point16(yellowEndX, yellowEndY);
865 	starRingEndPos[3] = Point16(greenEndX,  greenEndY);
866 	starRingEndPos[4] = Point16(blueEndX,   blueEndY);
867 	starRingEndPos[5] = Point16(violetEndX, violetEndY);
868 
869 	starSizes[0] = Point16(star1XSize, star1YSize);
870 	starSizes[1] = Point16(star2XSize, star2YSize);
871 	starSizes[2] = Point16(star3XSize, star3YSize);
872 	starSizes[3] = Point16(star4XSize, star4YSize);
873 	starSizes[4] = Point16(star5XSize, star5YSize);
874 	starSizes[5] = Point16(star6XSize, star6YSize);
875 	starSizes[6] = Point16(star7XSize, star7YSize);
876 
877 	ringSizes[0] = Point16(ring1XSize, ring1YSize);
878 	ringSizes[1] = Point16(ring2XSize, ring2YSize);
879 	ringSizes[2] = Point16(ring3XSize, ring3YSize);
880 	ringSizes[3] = Point16(ring4XSize, ring4YSize);
881 	ringSizes[4] = Point16(ring5XSize, ring5YSize);
882 	ringSizes[5] = Point16(ring6XSize, ring6YSize);
883 	ringSizes[6] = Point16(ring7XSize, ring7YSize);
884 
885 
886 	// get rid of resource context
887 	resFile->disposeContext(resContext);
888 	resContext = nullptr;
889 
890 	// set update checks to nominal values
891 	for (uint16 i = 0; i < numManaTypes; i++) {
892 		currentMana[i]        = -1;
893 		currentBaseMana[i]    = -1;
894 	}
895 
896 	// init the save map
897 	savedMap.size = Extent16(xSize, ySize);
898 	savedMap.data = new uint8[savedMap.bytes()];
899 }
900 
~CManaIndicator(void)901 CManaIndicator::~CManaIndicator(void) {
902 	// release images
903 	unloadImageRes(starImages, numStars);
904 	unloadImageRes(ringImages, numRings);
905 
906 	// release back image
907 	g_vm->_imageCache->releaseImage(backImage);
908 	g_vm->_imageCache->releaseImage(wellImage);
909 
910 	// release the saved map
911 	if (savedMap.data)
912 		delete[] savedMap.data;
913 }
914 
915 // this method provides a rect for any of the six mana regions of the control
916 // region numbers:
917 //  -------
918 //  |0 1 2|
919 //  |3 4 5|
920 //  -------
921 
getManaRegionRect(int8 nRegion)922 Rect16 CManaIndicator::getManaRegionRect(int8 nRegion) {
923 	assert(nRegion >= 0 && nRegion < numManaRegions);
924 
925 	int boxSizeX = xSize / 3;
926 	int boxSizeY = ySize / 2;
927 
928 	static Rect16 manaRegionRects[numManaRegions] = {
929 		Rect16(x, y, boxSizeX, boxSizeY),
930 		Rect16(x + boxSizeX, y, boxSizeX, boxSizeY),
931 		Rect16(x + boxSizeX * 2, y, boxSizeX, boxSizeY),
932 		Rect16(x, y + boxSizeY, boxSizeX, boxSizeY),
933 		Rect16(x + boxSizeX, y + boxSizeY, boxSizeX, boxSizeY),
934 		Rect16(x + boxSizeX * 2, y + boxSizeY, boxSizeX, boxSizeY)
935 	};
936 
937 	return manaRegionRects[nRegion];
938 }
939 
draw(void)940 void CManaIndicator::draw(void) {
941 
942 	gPort           &port = window.windowPort;
943 
944 
945 	// save pen color, etc.
946 	SAVE_GPORT_STATE(port);
947 
948 	// setup the port
949 	port.setMode(drawModeMatte);
950 
951 	g_vm->_pointer->hide(port, _extent);              // hide mouse pointer
952 	drawClipped(port, Point16(0, 0), Rect16(0, 0, xSize, ySize));
953 	g_vm->_pointer->show(port, _extent);              // show mouse pointer
954 
955 
956 }
957 
958 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & clipRect)959 void CManaIndicator::drawClipped(gPort &port,
960                                  const Point16 &offset,
961                                  const Rect16 &clipRect) {
962 	bool calcDraw;
963 
964 	// Do an update to the mana star info if needed,
965 	// if not, do not draw stuff
966 	calcDraw = update(g_vm->_playerList[getCenterActorPlayerID()]);
967 
968 	if (!calcDraw) {
969 		if (!_extent.overlap(clipRect)) return;
970 
971 		// draw the saved image to the port
972 		port.setMode(drawModeMatte);
973 		port.bltPixels(savedMap, 0, 0,
974 		               _extent.x - offset.x, _extent.y - offset.y,
975 		               xSize, ySize);
976 
977 		// draw the frame
978 		drawCompressedImage(port, Point16(_extent.x - offset.x, _extent.y - offset.y), backImage);
979 
980 		// and finish
981 		return;
982 	}
983 
984 	// otherwise continue with the update
985 	g_vm->_pointer->hide();
986 
987 	// create a temporary gPort to blit stuff to
988 	gPort       tempPort;
989 	gPixelMap   ringMap, starMap, mixMap, tempMap;
990 
991 
992 	if (!NewTempPort(tempPort, xSize, ySize))
993 		return;
994 
995 	// set the blit surface to a flat black
996 	memset(tempPort.map->data, 24, tempPort.map->bytes());
997 
998 	// draw the well
999 	drawCompressedImage(tempPort, Point16(wellX, wellY), wellImage);
1000 
1001 	// make a mixing plane and blank it
1002 	mixMap.size = Extent16(xSize, ySize);
1003 	mixMap.data = new uint8[mixMap.bytes()]();
1004 	// make a temp plane and blank it
1005 	tempMap.size = Extent16(xSize, ySize);
1006 	tempMap.data = new uint8[tempMap.bytes()]();
1007 
1008 	// clear out the blit surfaces
1009 	memset(mixMap.data, 0, mixMap.bytes());
1010 	memset(tempMap.data, 0, tempMap.bytes());
1011 
1012 	// draw as glyph
1013 	tempPort.setMode(drawModeMatte);
1014 
1015 	// draw each star and ring with color remap
1016 	for (uint16 i = 0; i < numManaTypes; i++) {
1017 		// get the header for the image pointer passed
1018 		ImageHeader *starHdr = (ImageHeader *)starImages[manaLines[i].starImageIndex];
1019 		ImageHeader *ringHdr = (ImageHeader *)ringImages[manaLines[i].ringImageIndex];
1020 
1021 		// set the buffer blit area to the image size
1022 		starMap.size = starHdr->size;
1023 		ringMap.size = ringHdr->size;
1024 
1025 		// see if it's compressed
1026 		if (starHdr->compress) {
1027 			// allocation of the temp buffer
1028 			starMap.data = new uint8[starMap.bytes()]();
1029 
1030 			// if it is then upack it to spec'ed coords.
1031 			unpackImage(&starMap, starMap.size.x, starMap.size.y, starHdr->data);
1032 		} else starMap.data = (uint8 *)starHdr->data;
1033 
1034 		// see if it's compressed
1035 		if (ringHdr->compress) {
1036 			// allocation of the temp buffer
1037 			ringMap.data = new uint8[ringMap.bytes()]();
1038 
1039 			// if it is then upack it to spec'ed coords.
1040 			unpackImage(&ringMap, ringMap.size.x, ringMap.size.y, ringHdr->data);
1041 		} else ringMap.data = (uint8 *)ringHdr->data;
1042 
1043 		// now blit the rings to the mixing surface
1044 		TBlit(&mixMap, &ringMap, manaLines[i].ringPos.x, manaLines[i].ringPos.y);
1045 		TBlit(&tempMap, &starMap, manaLines[i].starPos.x, manaLines[i].starPos.y);
1046 
1047 		// now do a peusdo-log additive thing to the images
1048 		uint8   *dst    = (uint8 *)mixMap.data;
1049 		uint8   *src    = (uint8 *)tempMap.data;
1050 
1051 		// get the least common dinominator for size ( should be equal )
1052 		uint16  bufferSize  = MIN(mixMap.bytes(), tempMap.bytes());
1053 
1054 		for (uint16 j = 0; j < bufferSize; j++) {
1055 			// image bug fix/kludge
1056 			if (dst[j] > 21) dst[j] = 10;
1057 			if (src[j] > 21) src[j] = 10;
1058 
1059 			// if the tempMap pixel is greater then zero
1060 			if (src[j] != 0 && src[j] > dst[j]) {
1061 				dst[j] += src[j] - dst[j];
1062 			}
1063 
1064 		}
1065 
1066 		// for each color index possible, match correct color value
1067 		// at dest buffer
1068 		compositePixels(
1069 		    tempPort.map,
1070 		    &mixMap,
1071 		    0,
1072 		    0,
1073 		    manaColorMap[i]);
1074 
1075 		// clear out the mixing surfaces
1076 		memset(mixMap.data, 0, mixMap.bytes());
1077 		memset(tempMap.data, 0, tempMap.bytes());
1078 
1079 		// dispose the temporary gPixelMap
1080 		if (starHdr->compress)
1081 			delete[] starMap.data;
1082 		if (ringHdr->compress)
1083 			delete[] ringMap.data;
1084 	}
1085 
1086 	// save this frame
1087 	TBlit(&savedMap, tempPort.map, 0, 0);
1088 
1089 	//  Blit the pixelmap to the main screen
1090 	port.setMode(drawModeMatte);
1091 	port.bltPixels(*tempPort.map, 0, 0,
1092 	               _extent.x - offset.x, _extent.y - offset.y,
1093 	               xSize, ySize);
1094 
1095 	// now blit the frame on top of it all.
1096 	drawCompressedImage(port, Point16(_extent.x - offset.x, _extent.y - offset.y), backImage);
1097 
1098 	// dispose of temporary pixelmap
1099 	DisposeTempPort(tempPort);
1100 	if (mixMap.data)
1101 		delete[] mixMap.data;
1102 	if (tempMap.data)
1103 		delete[] tempMap.data;
1104 
1105 	g_vm->_pointer->show();
1106 }
1107 
needUpdate(PlayerActor * player)1108 bool CManaIndicator::needUpdate(PlayerActor *player) {
1109 	assert(player);
1110 
1111 	// get the ability scores for this character
1112 	ActorAttributes *stats          = player->getEffStats();
1113 	ActorAttributes baseStatsRef    = player->getBaseStats();
1114 	int16           manaAmount;
1115 	int16           baseManaAmount;
1116 	uint16          i;
1117 
1118 	// this could do more array checking, but
1119 	// the number of mana type should remain stable
1120 	for (i = 0; i < numManaTypes; i++) {
1121 		manaAmount      = stats->mana(i);
1122 		baseManaAmount  = baseStatsRef.mana(i);
1123 
1124 		// check for new data
1125 		if (manaAmount != currentMana[i] || baseManaAmount != currentBaseMana[i]) {
1126 			return true;
1127 		}
1128 	}
1129 
1130 	return false;
1131 }
1132 
1133 
update(PlayerActor * player)1134 bool CManaIndicator::update(PlayerActor *player) {
1135 	assert(player);
1136 
1137 	// get the ability scores for this character
1138 	ActorAttributes *stats          = player->getEffStats();
1139 	ActorAttributes baseStatsRef    = player->getBaseStats();
1140 	int16           manaAmount;
1141 	int16           baseManaAmount;
1142 	uint16          i;
1143 	bool            newData = false;
1144 
1145 	// this could do more array checking, but
1146 	// the number of mana type should remain stable
1147 	for (i = 0; i < numManaTypes; i++) {
1148 		manaAmount      = stats->mana(i);
1149 		baseManaAmount  = baseStatsRef.mana(i);
1150 
1151 		// check for new data
1152 		if (manaAmount != currentMana[i] || baseManaAmount != currentBaseMana[i]) {
1153 			newData = true;
1154 
1155 			currentMana[i]        = manaAmount;
1156 			currentBaseMana[i]    = baseManaAmount;
1157 		}
1158 
1159 		// get manaLine info ( which star/ring image, and position on screen )
1160 		// from getStarInfo which takes the mana type index ( i ),
1161 		// current mana total, and the player base mana
1162 		if (newData == true) {
1163 			getManaLineInfo(i, manaAmount, baseManaAmount, &manaLines[i]);
1164 		}
1165 	}
1166 
1167 	// return the state of data change
1168 	if (newData  == false) {
1169 		return false;
1170 	} else {
1171 		return true;
1172 	}
1173 }
1174 
1175 //  A generalized interpolation template
LERP(T p1,T p2,int32 steps,int32 stepNum)1176 template<class T> inline T LERP(T p1, T p2, int32 steps, int32 stepNum) {
1177 	return p1 + (((p2 - p1) * stepNum) / steps);
1178 }
1179 
getManaLineInfo(uint16 index,int16 manaAmount,int16 baseManaAmount,manaLineInfo * info)1180 void CManaIndicator::getManaLineInfo(uint16 index,
1181                                      int16 manaAmount,
1182                                      int16 baseManaAmount,
1183                                      manaLineInfo *info) {
1184 	Point16 basePos = Point16(xSize / 2, ySize / 2);
1185 
1186 	// div zero prevention
1187 	if (manaAmount == 0) manaAmount = 1;
1188 	if (baseManaAmount == 0) baseManaAmount = 1;
1189 
1190 	manaLineInfo manaInfo;
1191 
1192 	//  Calculate the positions of the mana stars, and which images to use.
1193 	manaInfo.starPos        = LERP(basePos,
1194 	                               starRingEndPos[index],
1195 	                               (int32)maxLevel,
1196 	                               (int32)manaAmount);
1197 
1198 	manaInfo.ringPos        = LERP(basePos,
1199 	                               starRingEndPos[index],
1200 	                               (int32)maxLevel,
1201 	                               (int32)baseManaAmount);
1202 
1203 	manaInfo.starImageIndex = clamp(0,     manaAmount * numStars / maxLevel, numStars - 1);
1204 	manaInfo.ringImageIndex = clamp(0, baseManaAmount * numStars / maxLevel, numRings - 1);
1205 
1206 	// now do centering correct for images
1207 	manaInfo.starPos.x -= starSizes[manaInfo.starImageIndex].x / 2;
1208 	manaInfo.starPos.y -= starSizes[manaInfo.starImageIndex].y / 2;
1209 	manaInfo.ringPos.x -= ringSizes[manaInfo.ringImageIndex].x / 2;
1210 	manaInfo.ringPos.y -= ringSizes[manaInfo.ringImageIndex].y / 2;
1211 
1212 	// return the manaLineInfo struct info about mana star ring
1213 	*info = manaInfo;
1214 }
1215 
1216 /* ===================================================================== *
1217     CHealthIndicator: Health star indicator
1218  * ===================================================================== */
1219 
CHealthIndicator(AppFunc * cmd)1220 CHealthIndicator::CHealthIndicator(AppFunc *cmd) {
1221 	uint16 i;
1222 
1223 	// init the resource handle with the image group context
1224 	healthRes = resFile->newContext(imageGroupID, "health imagery context");
1225 
1226 	// load in health star imagery
1227 	starImag = loadButtonRes(healthRes, starStart, starNum, 'S', 'T', 'A');
1228 
1229 	// load in the health star border
1230 	starFrameImag    = g_vm->_imageCache->requestImage(healthRes, MKTAG('B', 'T', 'N', starFrameResNum));
1231 
1232 	// set the image indexes to nominal startup values
1233 	for (i = 0; i < numControls + 1; i++) {
1234 		imageIndexMemory[i] = -1;
1235 	}
1236 
1237 	// setup the id's for each of the stars
1238 	starIDs[0] = uiJulian;
1239 	starIDs[1] = uiPhillip;
1240 	starIDs[2] = uiKevin;
1241 
1242 
1243 	// health controls for the trio view
1244 	// deallocated with panel
1245 	for (i = 0; i < numControls; i++) {
1246 		starBtns[i] = new GfxCompImage(*trioControls,
1247 		                           Rect16(starXPos,
1248 		                                  starYPos + starYOffset * i,
1249 		                                  starXSize,
1250 		                                  starYSize),
1251 		                           starImag,
1252 		                           starNum,
1253 		                           starInitial,
1254 		                           starIDs[i],
1255 		                           cmd);
1256 
1257 
1258 		// image control for the star border/frame trio mode
1259 		new GfxCompImage(*trioControls,
1260 		                             Rect16(frameXPos,
1261 		                                    frameYPos + starYOffset * i,
1262 		                                    frameXSize,
1263 		                                    frameYSize),
1264 		                             starFrameImag,
1265 		                             0,
1266 		                             nullptr);
1267 
1268 
1269 	}
1270 	// health control for individual mode
1271 	// deallocated with panel
1272 	indivStarBtn = new GfxCompImage(*indivControls,
1273 	                          Rect16(starXPos,
1274 	                                 starYPos,
1275 	                                 starXSize,
1276 	                                 starYSize),
1277 	                          starImag,
1278 	                          starNum,
1279 	                          starInitial,
1280 	                          uiIndiv,
1281 	                          cmd);
1282 
1283 	// image control for the star border/frame indiv mode
1284 	new GfxCompImage(*indivControls,
1285 	                             Rect16(frameXPos,
1286 	                                    frameYPos,
1287 	                                    frameXSize,
1288 	                                    frameYSize),
1289 	                             starFrameImag,
1290 	                             0,
1291 	                             nullptr);
1292 
1293 	// release resource context
1294 	if (healthRes) {
1295 		resFile->disposeContext(healthRes);
1296 		healthRes = nullptr;
1297 	}
1298 }
1299 
1300 
~CHealthIndicator(void)1301 CHealthIndicator::~CHealthIndicator(void) {
1302 	// release star imagery
1303 	unloadImageRes(starImag, starNum);
1304 
1305 	// release star frame imagery
1306 	g_vm->_imageCache->releaseImage(starFrameImag);
1307 }
1308 
1309 //  Recalculate and update the health star for a particular brother
1310 
updateStar(GfxCompImage * starCtl,int32 bro,int32 baseVitality,int32 curVitality)1311 void CHealthIndicator::updateStar(GfxCompImage *starCtl, int32 bro, int32 baseVitality, int32 curVitality) {
1312 	assert(baseVitality >= 0);
1313 
1314 	int16 maxStar, imageIndex;
1315 
1316 	// prevent div zero
1317 	if (baseVitality == 0) baseVitality = 1;
1318 
1319 	maxStar = clamp(0, baseVitality / 10 + 14, starLevels - 1);
1320 //	imageIndex = (int16)( sqrt( sqrt( (double)curVitality ) ) * maxStar) / sqrt( sqrt( (double)baseVitality ) );
1321 	imageIndex = (int16)(sqrt((double)MAX((int32)0, curVitality)) * maxStar) / sqrt((double)baseVitality);
1322 
1323 	// prevent needless draws
1324 	if (imageIndexMemory[bro] != imageIndex) {
1325 		starCtl->setCurrent(imageIndex);
1326 		starCtl->invalidate();
1327 
1328 		imageIndexMemory[bro] = imageIndex;
1329 	}
1330 }
1331 
update(void)1332 void CHealthIndicator::update(void) {
1333 	if (g_vm->_indivControlsFlag) {
1334 		// get the stats for the selected brother
1335 		int16 baseVitality  = g_vm->_playerList[translatePanID(uiIndiv)]->getBaseStats().vitality;
1336 		int16 currVitality  = g_vm->_playerList[translatePanID(uiIndiv)]->getEffStats()->vitality;
1337 
1338 		updateStar(indivStarBtn, uiIndiv, baseVitality, currVitality);
1339 	} else {
1340 
1341 		for (uint16 i = 0; i < numControls; i++) {
1342 			// get the stats for the selected brother
1343 			int16 baseVitality  = g_vm->_playerList[i]->getBaseStats().vitality;
1344 			int16 currVitality  = g_vm->_playerList[i]->getEffStats()->vitality;
1345 
1346 			updateStar(starBtns[i], i, baseVitality, currVitality);
1347 		}
1348 	}
1349 }
1350 /* ===================================================================== *
1351    Plaq style text writing function
1352  * ===================================================================== */
1353 
writePlaqText(gPort & port,const Rect16 & r,gFont * font,int16 textPos,textPallete & pal,bool hiLite,const char * msg,...)1354 void writePlaqText(gPort            &port,
1355                    const Rect16    &r,
1356                    gFont           *font,
1357                    int16           textPos,
1358                    textPallete     &pal,
1359                    bool            hiLite,
1360                    const char      *msg, ...) {
1361 	char            lineBuf[128];
1362 	va_list         argptr;
1363 	Rect16          workRect;
1364 	int16 cnt;
1365 	gFont           *oldFont = port.font;
1366 
1367 	va_start(argptr, msg);
1368 	cnt = vsprintf(lineBuf, msg, argptr);
1369 	va_end(argptr);
1370 
1371 	SAVE_GPORT_STATE(port);
1372 	port.setMode(drawModeMatte);
1373 	port.setFont(font);
1374 
1375 	workRect = r;
1376 
1377 	port.setColor(hiLite ? pal.dlHilitePen : pal.dlPen);
1378 	workRect.x--;
1379 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0,  0));
1380 
1381 	workRect.y++;
1382 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
1383 
1384 	workRect.x++;
1385 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
1386 
1387 	port.setColor(hiLite ? pal.urHilitePen : pal.urPen);
1388 	workRect.x++;
1389 	workRect.y--;
1390 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0,  0));
1391 
1392 	workRect.y--;
1393 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0,  0));
1394 
1395 	workRect.x--;
1396 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0,  0));
1397 
1398 
1399 	port.setColor(hiLite ? pal.inHilitePen : pal.inPen);
1400 	workRect.y++;
1401 	port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0,  0));
1402 
1403 	port.setFont(oldFont);
1404 }
1405 
writePlaqTextPos(gPort & port,const Point16 & pos,gFont * font,int16,textPallete & pal,bool hiLite,const char * msg,...)1406 void writePlaqTextPos(gPort         &port,
1407                       const Point16    &pos,
1408                       gFont            *font,
1409                       int16            /*textPos*/,
1410                       textPallete      &pal,
1411                       bool             hiLite,
1412                       const char       *msg, ...) {
1413 	char            lineBuf[128];
1414 	va_list         argptr;
1415 	Point16         drawPos;
1416 	gFont           *oldFont = port.font;
1417 
1418 	va_start(argptr, msg);
1419 	vsprintf(lineBuf, msg, argptr);
1420 	va_end(argptr);
1421 
1422 	SAVE_GPORT_STATE(port);
1423 	port.setMode(drawModeMatte);
1424 	port.setFont(font);
1425 
1426 	drawPos = pos;
1427 
1428 	port.setColor(hiLite ? pal.dlHilitePen : pal.dlPen);
1429 	drawPos.x--;
1430 	port.moveTo(drawPos);
1431 
1432 	port.drawText(lineBuf, -1);
1433 
1434 	drawPos.y++;
1435 	port.moveTo(drawPos);
1436 
1437 	port.drawText(lineBuf, -1);
1438 
1439 	drawPos.x++;
1440 	port.moveTo(drawPos);
1441 
1442 	port.drawText(lineBuf, -1);
1443 
1444 	port.setColor(hiLite ? pal.urHilitePen : pal.urPen);
1445 	drawPos.x++;
1446 	drawPos.y--;
1447 	port.moveTo(drawPos);
1448 
1449 	port.drawText(lineBuf, -1);
1450 
1451 	drawPos.y--;
1452 	port.moveTo(drawPos);
1453 
1454 	port.drawText(lineBuf, -1);
1455 
1456 	drawPos.x--;
1457 	port.moveTo(drawPos);
1458 
1459 	port.drawText(lineBuf, -1);
1460 
1461 
1462 	port.setColor(hiLite ? pal.inHilitePen : pal.inPen);
1463 	drawPos.y++;
1464 	port.moveTo(drawPos);
1465 
1466 	port.drawText(lineBuf, -1);
1467 
1468 	port.setFont(oldFont);
1469 }
1470 
1471 
1472 
1473 ///////////////////////////////////////////////////////////////////
1474 /* functions for loading and unloading sets of compressed images */
1475 
1476 
1477 // creates an array of button images
1478 // passed a resource context, resource ref ID and the number of images
1479 // to sequentially load in
loadButtonRes(hResContext * con,int16 resID,int16 numRes)1480 void **loadButtonRes(hResContext *con, int16 resID, int16 numRes) {
1481 	int16   i, k;
1482 
1483 	void **images = (void **)malloc(sizeof(void *)*numRes);
1484 
1485 	for (i = 0, k = resID; i < numRes; i++, k++) {
1486 		// get an image from the image cache
1487 		images[i] = g_vm->_imageCache->requestImage(con, MKTAG('B', 'T', 'N', k));
1488 	}
1489 
1490 	return images;
1491 }
1492 
1493 // creates an array of images
1494 // passed a resource context, resource ref ID, the number of images
1495 // to sequentially load in, and the context id's
loadButtonRes(hResContext * con,int16 resID,int16 numRes,char a,char b,char c)1496 void **loadButtonRes(hResContext *con, int16 resID, int16 numRes, char a, char b, char c) {
1497 	int16   i, k;
1498 
1499 
1500 	void **images = (void **)malloc(sizeof(void *)*numRes);
1501 
1502 	for (i = 0, k = resID; i < numRes; i++, k++) {
1503 		images[i] = g_vm->_imageCache->requestImage(con, MKTAG(a, b, c, k));
1504 	}
1505 
1506 	return images;
1507 }
1508 
1509 // see LoadButtonRes
loadImageRes(hResContext * con,int16 resID,int16 numRes,char a,char b,char c)1510 void **loadImageRes(hResContext *con, int16 resID, int16 numRes, char a, char b, char c) {
1511 	return loadButtonRes(con, resID, numRes, a, b, c);
1512 }
1513 
1514 // correctly deletes any image arrays allocated with the LoadButtonRes or
1515 // LoadImageRes sets
unloadImageRes(void ** images,int16 numRes)1516 void unloadImageRes(void **images, int16 numRes) {
1517 	int16   i;
1518 
1519 	if (images) {
1520 		for (i = 0; i < numRes; i++) {
1521 			g_vm->_imageCache->releaseImage(images[i]);
1522 		}
1523 
1524 		free(images);
1525 	}
1526 }
1527 
1528 // defined for setup off all button based user controls
SetupUserControls(void)1529 void SetupUserControls(void) {
1530 	// index variables
1531 	uint16  n;
1532 	uint8   index = 0;
1533 
1534 	// resource handle
1535 	hResContext *imageRes;
1536 
1537 	// brother panel id's
1538 	uint16  brotherIDs[kNumViews]   =  { uiJulian, uiPhillip, uiKevin };
1539 
1540 	// portrait resource indexes
1541 	int16   portResNum[]    = { 0, 0, 0 };
1542 
1543 
1544 	// image setup
1545 
1546 	// init the resource handle with the image group context
1547 	imageRes        = resFile->newContext(imageGroupID, "image context");
1548 
1549 
1550 	// set up the control button images
1551 	aggressImag      = loadButtonRes(imageRes, aggressResNum, numBtnImages);
1552 
1553 	centerImag       = loadButtonRes(imageRes, centerResNum, numBtnImages);
1554 	bandingImag      = loadButtonRes(imageRes, bandingResNum, numBtnImages);
1555 	menConBtnImag    = loadButtonRes(imageRes, menConBtnResNum, numBtnImages);
1556 
1557 	// setup the options button imagery
1558 	optBtnImag       = loadButtonRes(imageRes, optBtnResNum, numBtnImages);
1559 
1560 	// setup the brother selector button imagery and button frame
1561 	julBtnImag       = loadButtonRes(imageRes, julBtnResNum, numBtnImages);
1562 	phiBtnImag       = loadButtonRes(imageRes, phiBtnResNum, numBtnImages);
1563 	kevBtnImag       = loadButtonRes(imageRes, kevBtnResNum, numBtnImages);
1564 	broBtnFrameImag  = g_vm->_imageCache->requestImage(imageRes, MKTAG('F', 'R', 'A', 'M'));
1565 
1566 
1567 	// set up the portrait name plates
1568 	for (n = 0; n < kNumViews; n++) {
1569 		namePlateImages[n] = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', namePlateResNum[n]));
1570 	}
1571 
1572 	// get the frame image
1573 	namePlateFrameImag = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', 15));
1574 	armorImag = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', 34));
1575 
1576 	// clean out the old context
1577 	if (imageRes) resFile->disposeContext(imageRes);
1578 	imageRes = nullptr;
1579 
1580 	// init the resource handle with the face group context
1581 	imageRes = resFile->newContext(faceGroupID, "face resources");
1582 
1583 	// set up the portrait button images
1584 	for (n = 0; n < kNumViews; n++) {
1585 		portImag[n]    = loadButtonRes(imageRes, portResNum[n], numPortImages, broNames[n].a, broNames[n].b, broNames[n].c);
1586 	}
1587 
1588 	// setup stand alone controls
1589 
1590 	optBtn = new GfxCompButton(*playControls, optBtnRect, optBtnImag, numBtnImages, 0, cmdOptions);
1591 
1592 	enchDisp = new gEnchantmentDisplay(*playControls, 0);
1593 
1594 	// setup the trio user cntl buttons
1595 	for (n = 0; n < kNumViews; n++) {
1596 		// portrait button
1597 		portBtns[n]        = new GfxMultCompButton(*trioControls, views[n][index++],
1598 		                                  portImag[n], numPortImages, 0, false, brotherIDs[n], cmdPortrait);
1599 
1600 		portBtns[n]->setMousePoll(true);
1601 
1602 		// aggressive button
1603 		aggressBtns[n]     = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
1604 		                                  aggressImag, numBtnImages, brotherIDs[n], cmdAggressive);
1605 
1606 		// name plates that go under the portraits
1607 		armorInd[n]        = new gArmorIndicator(*trioControls, views[n][index++],
1608 		                                  armorImag, brotherIDs[n], cmdArmor);
1609 
1610 		// center on brother
1611 		centerBtns[n]      = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
1612 		                                  centerImag, numBtnImages, brotherIDs[n], cmdCenter);
1613 
1614 		// banding
1615 		bandingBtns[n] = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
1616 		                              bandingImag, numBtnImages, brotherIDs[n], cmdBand);
1617 
1618 		// name plates that go under the portraits
1619 		namePlates[n]  = new GfxCompImage(*trioControls, views[n][index++],
1620 		                              namePlateImages[n], 0, nullptr);
1621 
1622 		// the frames for the name plates
1623 		namePlateFrames[n] = new GfxCompImage(*trioControls, views[n][index++],
1624 		                                  namePlateFrameImag, 0, nullptr);
1625 
1626 		index = 0;
1627 	}
1628 
1629 	// individual control buttons
1630 
1631 	// portrait button
1632 	indivPortBtn = new GfxMultCompButton(*indivControls, views[0][index++],
1633 	                          portImag[0], numPortImages, 0, false, uiIndiv, cmdPortrait);
1634 	indivPortBtn->setMousePoll(true);
1635 
1636 	// aggressive button
1637 	indivAggressBtn  = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
1638 	                              aggressImag, numBtnImages, uiIndiv, cmdAggressive);
1639 
1640 	indivArmorInd    = new gArmorIndicator(*indivControls, views[0][index++],
1641 	                              armorImag, uiIndiv, cmdArmor);
1642 	// center on brother
1643 	indivCenterBtn   = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
1644 	                              centerImag, numBtnImages, uiIndiv, cmdCenter);
1645 
1646 	// banding
1647 	indivBandingBtn  = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
1648 	                              bandingImag, numBtnImages, uiIndiv, cmdBand);
1649 
1650 	// name plates that go under the portraits
1651 	indivNamePlate  = new GfxCompImage(*indivControls, views[0][index++],
1652 	                             namePlateImages[0], 0, nullptr);
1653 
1654 	// the frames for the name plates
1655 	indivNamePlateFrame = new GfxCompImage(*indivControls, views[0][index++],
1656 	                                 namePlateFrameImag, 0, nullptr);
1657 
1658 	// setup the portrait object
1659 	Portrait = new CPortrait(portBtns,      // portrait buttons
1660 	                                       indivPortBtn,
1661 	                                       numPortImages,// num of images per button
1662 	                                       kNumViews);   // number of brothers
1663 
1664 
1665 	// mental container button
1666 	menConBtn = new GfxCompButton(*indivControls, menConBtnRect, menConBtnImag, numBtnImages, uiIndiv, cmdBrain);
1667 
1668 	// brother selection buttons >>> need to replace these with sticky buttons
1669 	julBtn = new GfxOwnerSelCompButton(*indivControls, julBtnRect, julBtnImag, numBtnImages, uiJulian, cmdBroChange);
1670 	phiBtn = new GfxOwnerSelCompButton(*indivControls, phiBtnRect, phiBtnImag, numBtnImages, uiPhillip, cmdBroChange);
1671 	kevBtn = new GfxOwnerSelCompButton(*indivControls, kevBtnRect, kevBtnImag, numBtnImages, uiKevin, cmdBroChange);
1672 
1673 	// frame for brother buttons
1674 	broBtnFrame = new GfxCompImage(*indivControls, broBtnRect, broBtnFrameImag, uiIndiv, nullptr);
1675 
1676 	// make the mana indicator
1677 	ManaIndicator = new CManaIndicator(*indivControls);
1678 	ManaIndicator->setMousePoll(true);
1679 
1680 	// get rid of the resource contexts
1681 	if (imageRes) {
1682 		resFile->disposeContext(imageRes);
1683 		imageRes = nullptr;
1684 	}
1685 
1686 	//The controls need to be enabled but undrawn at startup
1687 	//if ( displayEnabled() )
1688 	//  g_vm->_userControlsSetup = true;
1689 	updateAllUserControls();
1690 }
1691 
enableUserControls(void)1692 void enableUserControls(void) {
1693 	g_vm->_userControlsSetup = true;
1694 }
1695 
disableUserControls(void)1696 void disableUserControls(void) {
1697 	g_vm->_userControlsSetup = false;
1698 }
1699 
1700 // defines the cleanup for ALL user interface controls
CleanupUserControls(void)1701 void CleanupUserControls(void) {
1702 	g_vm->_userControlsSetup = false;
1703 	CleanupButtonImages();
1704 }
1705 
1706 // defines the cleaup for the global button image array
CleanupButtonImages(void)1707 void CleanupButtonImages(void) {
1708 	int16 i;
1709 
1710 	// deallocates the images in the array and the arrays themselves
1711 	unloadImageRes(aggressImag, numBtnImages);
1712 //	unloadImageRes( jumpImag   , numBtnImages );
1713 	unloadImageRes(centerImag, numBtnImages);
1714 	unloadImageRes(bandingImag, numBtnImages);
1715 	unloadImageRes(menConBtnImag, numBtnImages);
1716 
1717 
1718 	// dealloc the imag for the option button
1719 	unloadImageRes(optBtnImag, numBtnImages);
1720 
1721 	// dealloc brother's indiv mode buttons
1722 	unloadImageRes(julBtnImag, numBtnImages);
1723 	unloadImageRes(phiBtnImag, numBtnImages);
1724 	unloadImageRes(kevBtnImag, numBtnImages);
1725 
1726 
1727 	// portraits
1728 	for (i = 0; i < kNumViews; i++) {
1729 		unloadImageRes(portImag[i], numPortImages);
1730 	}
1731 
1732 	// name plate frame
1733 	g_vm->_imageCache->releaseImage(namePlateFrameImag);
1734 	g_vm->_imageCache->releaseImage(armorImag);
1735 
1736 	// release name frames
1737 	g_vm->_imageCache->releaseImage(broBtnFrameImag);
1738 
1739 	// name plates
1740 	for (i = 0; i < kNumViews; i++) {
1741 		g_vm->_imageCache->releaseImage(namePlateImages[i]);
1742 	}
1743 }
1744 
updateIndicators(void)1745 void updateIndicators(void) {
1746 	HealthIndicator->update();
1747 	MassWeightIndicator->update();
1748 
1749 	// mana indicator update check
1750 	if (isIndivMode()) {
1751 		if (ManaIndicator->needUpdate(g_vm->_playerList[getCenterActorPlayerID()])) {
1752 			// redraw the region that is not covered by any other window
1753 			ManaIndicator->invalidate();
1754 		}
1755 	}
1756 }
1757 
1758 
1759 //>>>
1760 /* ===================================================================== *
1761    These should become methods of the playerActor class
1762  * ===================================================================== */
1763 
1764 template< class T >
GetRatio(T curUnits,T maxUnits,T ratio)1765 inline T GetRatio(T curUnits, T maxUnits, T ratio) {
1766 	uint16 delt;
1767 
1768 	if (maxUnits < ratio) {
1769 		delt = ratio / maxUnits;
1770 
1771 		curUnits *= delt;
1772 	} else {
1773 		delt = maxUnits / ratio;
1774 
1775 		curUnits /= delt;
1776 	}
1777 
1778 	return clamp(0, curUnits, ratio);
1779 }
1780 
getWeightRatio(GameObject * obj,uint16 & maxRatio,bool bReturnMaxRatio=true)1781 uint16 getWeightRatio(GameObject *obj, uint16 &maxRatio, bool bReturnMaxRatio = true) {
1782 	assert(isObject(obj) || isActor(obj));
1783 
1784 	uint16 weight;
1785 	uint16 maxWeight;
1786 
1787 	// get the mass capacity for this container
1788 	maxWeight = obj->massCapacity();;
1789 
1790 	// get the total mass this container is holding
1791 	weight = obj->totalContainedMass();
1792 
1793 
1794 	if (bReturnMaxRatio) {
1795 		maxRatio = maxWeight;
1796 		return weight;
1797 	} else {
1798 		return  maxWeight != unlimitedCapacity
1799 		        ?   GetRatio(weight, maxWeight, maxRatio)
1800 		        :   0;
1801 	}
1802 }
1803 
1804 
getBulkRatio(GameObject * obj,uint16 & maxRatio,bool bReturnMaxRatio=true)1805 uint16 getBulkRatio(GameObject *obj, uint16 &maxRatio, bool bReturnMaxRatio = true) {
1806 	assert(isObject(obj) || isActor(obj));
1807 
1808 	uint16 maxBulk;
1809 	uint16 bulk;
1810 
1811 	//  get the bulk capacity for this container
1812 	maxBulk = obj->bulkCapacity();
1813 
1814 	// get the total bulk this container is holding
1815 	bulk = obj->totalContainedBulk();
1816 
1817 	if (bReturnMaxRatio) {
1818 		maxRatio = maxBulk;
1819 		return bulk;
1820 	} else {
1821 		return  maxBulk != unlimitedCapacity
1822 		        ?   GetRatio(bulk, maxBulk, maxRatio)
1823 		        :   0;
1824 	}
1825 }
1826 
1827 /* ===================================================================== *
1828    Application functions
1829  * ===================================================================== */
1830 
updateReadyContainers(void)1831 void updateReadyContainers(void) {
1832 	// if in individual mode
1833 	if (g_vm->_indivControlsFlag) {
1834 		indivCviewTop->invalidate();
1835 		indivCviewBot->invalidate();
1836 	} else if (TrioCviews[getCenterActorPlayerID()]) {
1837 		TrioCviews[getCenterActorPlayerID()]->invalidate();
1838 	}
1839 }
1840 
setEnchantmentDisplay(void)1841 void setEnchantmentDisplay(void) {
1842 	if (enchDisp) enchDisp->setValue(getCenterActorPlayerID());
1843 }
1844 
1845 // sets the individual brother control state buttons
setIndivBtns(uint16 brotherID)1846 void setIndivBtns(uint16 brotherID) {    // top = 0, mid = 1, bot = 2
1847 	g_vm->_indivControlsFlag = true;
1848 
1849 	// set the indiv bro
1850 	indivBrother = brotherID;
1851 
1852 	// set all the individual brother buttons to the correct states
1853 	indivCenterBtn->select(centerBtns[brotherID]->isSelected());
1854 	indivCenterBtn->ghost(centerBtns[brotherID]->isGhosted());
1855 	//indivStarBtn->setCurrent( ( uint16 )starBtns[brotherID]->getCurrent() );
1856 	indivPortBtn->setImages(portImag[brotherID]);
1857 	indivNamePlate->setImage(namePlateImages[brotherID]);
1858 	Portrait->set(uiIndiv, Portrait->getCurrentState(brotherID));
1859 	indivBandingBtn->select(bandingBtns[brotherID]->isSelected());
1860 	indivBandingBtn->ghost(bandingBtns[brotherID]->isGhosted());
1861 	indivAggressBtn->select(aggressBtns[brotherID]->isSelected());
1862 	indivAggressBtn->ghost(aggressBtns[brotherID]->isGhosted());
1863 	indivArmorInd->setValue(brotherID);
1864 	indivArmorInd->ghost(armorInd[brotherID]->isGhosted());
1865 	setEnchantmentDisplay();
1866 
1867 	// point the read containers to the correct brother
1868 	if (brotherID >= kPlayerActors)
1869 		brotherID = kPlayerActors - 1;
1870 
1871 	indivCviewTop->setContainer(GameObject::objectAddress(ActorBaseID + brotherID));
1872 	indivCviewTop->ghost(TrioCviews[brotherID]->isGhosted());
1873 	indivCviewBot->setContainer(GameObject::objectAddress(ActorBaseID + brotherID));
1874 	indivCviewBot->ghost(TrioCviews[brotherID]->isGhosted());
1875 
1876 	// now set the indicators for mass and bulk
1877 	uint16 pieWeightRatio   = MassWeightIndicator->getMassPieDiv();
1878 	uint16 pieBulkRatio     = MassWeightIndicator->getBulkPieDiv();
1879 	PlayerActor *brother    = g_vm->_playerList[brotherID];
1880 
1881 	MassWeightIndicator->setMassPie(getWeightRatio(brother->getActor(), pieWeightRatio, false));
1882 	MassWeightIndicator->setBulkPie(getBulkRatio(brother->getActor(), pieBulkRatio, false));
1883 }
1884 
1885 // sets the trio brothers control state buttons
setTrioBtns(void)1886 void setTrioBtns(void) {
1887 	g_vm->_indivControlsFlag = false;
1888 
1889 	// reset any value that might have changed in idividual mode
1890 	centerBtns[indivBrother]->select(indivCenterBtn->isSelected());
1891 	bandingBtns[indivBrother]->select(indivBandingBtn->isSelected());
1892 	Portrait->set(indivBrother, Portrait->getCurrentState(uiIndiv));
1893 	aggressBtns[indivBrother]->select(indivAggressBtn->isSelected());
1894 
1895 	armorInd[indivBrother]->setValue(indivBrother);
1896 	setEnchantmentDisplay();
1897 }
1898 
setControlPanelsToIndividualMode(uint16 brotherID)1899 void setControlPanelsToIndividualMode(uint16 brotherID) {
1900 	// copy the button/indicator states to the indiv buttons
1901 	setIndivBtns(brotherID);
1902 
1903 	// set the mode controls
1904 	trioControls->show(false, false);
1905 	indivControls->show(true, true);
1906 	trioControls->show(false, true);
1907 }
1908 
setControlPanelsToTrioMode(void)1909 void setControlPanelsToTrioMode(void) {
1910 	setTrioBtns();
1911 	indivControls->show(false, false);
1912 	trioControls->show(true, true);
1913 	indivControls->show(false, true);
1914 }
1915 
toggleIndivMode(void)1916 void toggleIndivMode(void) {
1917 	if (g_vm->_indivControlsFlag) setControlPanelsToTrioMode();
1918 	else setControlPanelsToIndividualMode(getCenterActorPlayerID());
1919 }
1920 
setCenterBrother(uint16 whichBrother)1921 void setCenterBrother(uint16 whichBrother) {
1922 	//  If we picked up anything, then put it back.
1923 	g_vm->_mouseInfo->replaceObject();
1924 
1925 	// set the new center actor
1926 	setCenterActor(g_vm->_playerList[whichBrother]);
1927 }
1928 
translatePanID(uint16 panID)1929 uint16 translatePanID(uint16 panID) {
1930 	// individual mode brother id translation
1931 	if (panID == uiIndiv) {
1932 		panID = indivBrother;
1933 	}
1934 
1935 	return panID;
1936 }
1937 
updateBrotherPortrait(uint16 brotherID,int16 pType)1938 void updateBrotherPortrait(uint16 brotherID, int16 pType) {
1939 	if (g_vm->_userControlsSetup) {
1940 		Portrait->set(brotherID, (PortraitType)pType);
1941 
1942 		if (brotherID == indivBrother)
1943 			Portrait->set(uiIndiv, (PortraitType)pType);
1944 	}
1945 }
1946 
updateBrotherAggressionButton(uint16 brotherID,bool aggressive)1947 void updateBrotherAggressionButton(uint16 brotherID, bool aggressive) {
1948 	if (g_vm->_userControlsSetup) {
1949 		aggressBtns[brotherID]->select(aggressive);
1950 		aggressBtns[brotherID]->ghost(isBrotherDead(brotherID));
1951 
1952 		if (brotherID == indivBrother) {
1953 			indivAggressBtn->select(aggressive);
1954 			indivAggressBtn->ghost(isBrotherDead(brotherID));
1955 		}
1956 
1957 		//  possibly change portrait type
1958 		recalcPortraitType(brotherID);
1959 	}
1960 }
1961 
updateBrotherBandingButton(uint16 brotherID,bool banded)1962 void updateBrotherBandingButton(uint16 brotherID, bool banded) {
1963 	if (g_vm->_userControlsSetup) {
1964 		bandingBtns[brotherID]->select(banded);
1965 		bandingBtns[brotherID]->ghost(isBrotherDead(brotherID));
1966 
1967 		if (brotherID == indivBrother) {
1968 			indivBandingBtn->select(banded);
1969 			indivBandingBtn->ghost(isBrotherDead(brotherID));
1970 		}
1971 	}
1972 }
1973 
updateBrotherRadioButtons(uint16 brotherID)1974 void updateBrotherRadioButtons(uint16 brotherID) {
1975 	if (g_vm->_userControlsSetup) {
1976 		bool    jul = (uiJulian == brotherID);
1977 		bool    phi = (uiPhillip == brotherID);
1978 		bool    kev = (uiKevin == brotherID);
1979 
1980 		// set the selection buttons to the correct states
1981 		julBtn->select(jul);
1982 		phiBtn->select(phi);
1983 		kevBtn->select(kev);
1984 
1985 		julBtn->ghost(isBrotherDead(uiJulian));
1986 		phiBtn->ghost(isBrotherDead(uiPhillip));
1987 		kevBtn->ghost(isBrotherDead(uiKevin));
1988 
1989 		// set the center brother buttons
1990 		centerBtns[uiJulian]->select(jul);
1991 		centerBtns[uiPhillip]->select(phi);
1992 		centerBtns[uiKevin]->select(kev);
1993 
1994 		centerBtns[uiJulian]->ghost(isBrotherDead(uiJulian));
1995 		centerBtns[uiPhillip]->ghost(isBrotherDead(uiPhillip));
1996 		centerBtns[uiKevin]->ghost(isBrotherDead(uiKevin));
1997 
1998 		if (brotherID == indivBrother) {
1999 			indivCenterBtn->select(true);
2000 			indivCenterBtn->ghost(isBrotherDead(brotherID));
2001 		}
2002 
2003 		if (g_vm->_indivControlsFlag)
2004 			setControlPanelsToIndividualMode(brotherID);
2005 	}
2006 }
2007 
updateBrotherArmor(uint16 brotherID)2008 void updateBrotherArmor(uint16 brotherID) {
2009 	if (g_vm->_userControlsSetup) {
2010 		armorInd[brotherID]->setValue(brotherID);
2011 		armorInd[brotherID]->ghost(isBrotherDead(brotherID));
2012 
2013 		if (brotherID == indivBrother) {
2014 			indivArmorInd->setValue(brotherID);
2015 			indivArmorInd->ghost(isBrotherDead(brotherID));
2016 		}
2017 	}
2018 }
2019 
updateAllUserControls(void)2020 void updateAllUserControls(void) {
2021 	if (displayEnabled()) {
2022 		if (g_vm->_userControlsSetup) {
2023 			uint16      centerBrotherID = getCenterActorPlayerID(),
2024 			            brotherID;
2025 
2026 			if (g_vm->_indivControlsFlag)
2027 				setControlPanelsToIndividualMode(indivBrother);
2028 			else
2029 				setControlPanelsToTrioMode();
2030 
2031 			updateBrotherRadioButtons(centerBrotherID);
2032 			for (brotherID = 0; brotherID < kNumViews; brotherID++) {
2033 				bool    dead = isBrotherDead(brotherID);
2034 
2035 				updateBrotherBandingButton(brotherID, isBanded(brotherID));
2036 				updateBrotherAggressionButton(brotherID, isAggressive(brotherID));
2037 				updateBrotherPortrait(brotherID, getPortraitType(brotherID));
2038 				updateBrotherArmor(brotherID);
2039 
2040 				// if in individual mode, ghost containers if he's dead
2041 				if (brotherID == indivBrother) {
2042 					indivCviewTop->ghost(dead);
2043 					indivCviewBot->ghost(dead);
2044 				}
2045 
2046 				//  Ghost the ready container if he's dead.
2047 				TrioCviews[brotherID]->ghost(dead);
2048 			}
2049 		}
2050 	} else {
2051 		reDrawScreen();
2052 	}
2053 }
2054 
updateBrotherControls(PlayerActorID brotherID)2055 void updateBrotherControls(PlayerActorID brotherID) {
2056 	if (g_vm->_userControlsSetup) {
2057 		bool    dead = isBrotherDead(brotherID);
2058 
2059 		updateBrotherRadioButtons(getCenterActorPlayerID());
2060 		updateBrotherBandingButton(brotherID, isBanded(brotherID));
2061 		updateBrotherAggressionButton(brotherID, isAggressive(brotherID));
2062 		updateBrotherPortrait(brotherID, getPortraitType(brotherID));
2063 		updateBrotherArmor(brotherID);
2064 
2065 		// if in individual mode, ghost containers if he's dead
2066 		if (brotherID == indivBrother) {
2067 			indivCviewTop->ghost(dead);
2068 			indivCviewBot->ghost(dead);
2069 		}
2070 
2071 		//  Ghost the ready container if he's dead.
2072 		TrioCviews[brotherID]->ghost(dead);
2073 	}
2074 }
2075 
2076 // button call backs
APPFUNC(cmdPortrait)2077 APPFUNC(cmdPortrait) {
2078 	const int bufSize = 80;
2079 	const int stateBufSize = 60;
2080 
2081 	uint16  panID = ev.panel->id;
2082 	GameObject      *mouseObject = g_vm->_mouseInfo->getObject();   // object being dragged
2083 
2084 	switch (ev.eventType) {
2085 
2086 	case gEventNewValue:
2087 
2088 		if (mouseObject != nullptr) {
2089 			PlayerActor     *pa = getPlayerActorAddress(translatePanID(panID));
2090 			Actor           *centerActorPtr = getCenterActor();
2091 
2092 			//  we dropped the object onto another object
2093 			if (g_vm->_mouseInfo->getDoable()) {
2094 				int16   intent = g_vm->_mouseInfo->getIntent();
2095 
2096 				g_vm->_mouseInfo->replaceObject();
2097 				if (intent == GrabInfo::Use) {
2098 					//  If we are using an intangible object (spell) then consider
2099 					//  the owner of the spell to be the center actor for the rest
2100 					//  of this action.
2101 					if (mouseObject->proto()->containmentSet() & ProtoObj::isIntangible) {
2102 						ObjectID    possessor = mouseObject->possessor();
2103 
2104 						if (possessor != Nothing) {
2105 							centerActorPtr = (Actor *)GameObject::objectAddress(possessor);
2106 						}
2107 					}
2108 
2109 					MotionTask::useObjectOnObject(
2110 					    *centerActorPtr,
2111 					    *mouseObject,
2112 					    *pa->getActor());
2113 				} else if (intent == GrabInfo::Drop) {
2114 					MotionTask::dropObjectOnObject(
2115 					    *centerActorPtr,
2116 					    *mouseObject,
2117 					    *pa->getActor(),
2118 					    g_vm->_mouseInfo->getMoveCount());
2119 				}
2120 
2121 //				( ( gGenericControl * )ev.panel )->disableDblClick();
2122 				//  clickActionDone = true;
2123 			} else if (g_vm->_mouseInfo->getIntent() == GrabInfo::Use) {
2124 				g_vm->_mouseInfo->replaceObject();
2125 //				clickActionDone = true;
2126 			}
2127 		} else if (panID != uiIndiv) {
2128 			if (!isBrotherDead(panID)) {
2129 				setCenterBrother(panID);
2130 				setControlPanelsToIndividualMode(panID);
2131 			}
2132 		} else if (panID == uiIndiv) {
2133 			setControlPanelsToTrioMode();
2134 		}
2135 		break;
2136 
2137 	case gEventMouseMove:
2138 
2139 		if (ev.value == GfxCompImage::leave) {
2140 			g_vm->_mouseInfo->setText(nullptr);
2141 			g_vm->_mouseInfo->setDoable(true);
2142 			break;
2143 		}
2144 
2145 //		if (ev.value == gCompImage::enter)
2146 		{
2147 			if (mouseObject != nullptr) {
2148 				PlayerActor     *pa = getPlayerActorAddress(translatePanID(panID));
2149 				Actor           *targetActor = pa->getActor(),
2150 				                 *enactor = getCenterActor();
2151 
2152 				g_vm->_mouseInfo->setText(nullptr);
2153 
2154 				if ((enactor->getLocation() - targetActor->getLocation()).quickHDistance() > 96) {
2155 					g_vm->_mouseInfo->setDoable(false);
2156 				} else {
2157 					g_vm->_mouseInfo->setDoable(true);
2158 				}
2159 			} else {
2160 
2161 				// working buffer
2162 				char buf[bufSize];
2163 				char state[stateBufSize];
2164 				uint16 brotherID = translatePanID(panID);
2165 
2166 				Portrait->getStateString(state, stateBufSize, brotherID);
2167 
2168 				switch (brotherID) {
2169 				case uiJulian:
2170 					sprintf(buf, "%s %s", JULIAN_BROSTATE, state);
2171 					break;
2172 				case uiPhillip:
2173 					sprintf(buf, "%s %s", PHILLIP_BROSTATE, state);
2174 					break;
2175 				case uiKevin:
2176 					sprintf(buf, "%s %s", KEVIN_BROSTATE, state);
2177 					break;
2178 				}
2179 				// set the text in the cursor
2180 				g_vm->_mouseInfo->setText(buf);
2181 			}
2182 		}
2183 		break;
2184 
2185 	default:
2186 		break;
2187 	}
2188 }
2189 
toggleAgression(PlayerActorID bro,bool all)2190 void toggleAgression(PlayerActorID bro, bool all) {
2191 	int16   wasAggressive = isAggressive(bro);
2192 
2193 	if (all) {
2194 		for (int i = 0; i < kPlayerActors; i++)
2195 			setAggression(i, !wasAggressive);
2196 	} else setAggression(bro, !wasAggressive);
2197 }
2198 
APPFUNC(cmdAggressive)2199 APPFUNC(cmdAggressive) {
2200 	uint16 transBroID = translatePanID(ev.panel->id);
2201 
2202 	// check for message update stuff
2203 	// and aggression update
2204 	if (ev.eventType == gEventNewValue) {
2205 		toggleAgression(transBroID, rightButtonState());
2206 //		int16   wasAggressive = isAggressive( transBroID );
2207 
2208 //		if (rightButtonState())
2209 //		{
2210 //			for (int i = 0; i < kPlayerActors; i++)
2211 //				setAggression( i, !wasAggressive );
2212 //		}
2213 //		else setAggression( transBroID, !wasAggressive );
2214 	} else if (ev.eventType == gEventMouseMove) {
2215 		if (ev.value == GfxCompImage::enter) {
2216 			// set the text in the cursor
2217 			g_vm->_mouseInfo->setText(isAggressive(transBroID)
2218 			                  ? ON_AGRESS
2219 			                  : OFF_AGRESS);
2220 		} else if (ev.value == GfxCompImage::leave) {
2221 			g_vm->_mouseInfo->setText(nullptr);
2222 		}
2223 	}
2224 }
2225 
2226 /*
2227 APPFUNC( cmdJump )
2228 {
2229     if( ev.eventType == gEventNewValue )
2230     {
2231         jump = !jump;
2232 
2233         if( jump )
2234         {
2235             setMouseImage( CloseBx1Image, 0, 0 );
2236         }
2237         else
2238         {
2239             setMouseImage( ArrowImage, 0, 0 );
2240         }
2241     }
2242 }
2243 */
2244 
APPFUNC(cmdArmor)2245 APPFUNC(cmdArmor) {
2246 	if (ev.eventType == gEventMouseMove) {
2247 
2248 		if (ev.value == GfxCompImage::enter) {
2249 			gArmorIndicator *gai = (gArmorIndicator *)ev.panel;
2250 			char    buf[128];
2251 
2252 			if (gai->attr.damageAbsorbtion == 0
2253 			        &&  gai->attr.defenseBonus == 0) {
2254 				g_vm->_mouseInfo->setText(NO_ARMOR);
2255 			} else {
2256 				sprintf(buf,
2257 				        DESC_ARMOR,
2258 				        gai->attr.damageAbsorbtion,
2259 				        gai->attr.damageDivider,
2260 				        gai->attr.defenseBonus);
2261 
2262 				// set the text in the cursor
2263 				g_vm->_mouseInfo->setText(buf);
2264 			}
2265 		} else if (ev.value == GfxCompImage::leave) {
2266 			g_vm->_mouseInfo->setText(nullptr);
2267 		}
2268 	}
2269 }
2270 
APPFUNC(cmdCenter)2271 APPFUNC(cmdCenter) {
2272 	uint16 transBroID = translatePanID(ev.panel->id);
2273 
2274 	if (ev.eventType == gEventNewValue) {
2275 		if (rightButtonState())
2276 			setCenterBrother((transBroID + 1) % kPlayerActors);
2277 		else setCenterBrother(transBroID);
2278 	}
2279 	if (ev.eventType == gEventMouseMove) {
2280 		if (ev.value == GfxCompImage::enter) {
2281 			// set the text in the cursor
2282 			g_vm->_mouseInfo->setText(getCenterActorPlayerID() == transBroID
2283 			                  ? ON_CENTER
2284 			                  : OFF_CENTER);
2285 		} else if (ev.value == GfxCompImage::leave) {
2286 			g_vm->_mouseInfo->setText(nullptr);
2287 		}
2288 	}
2289 }
2290 
toggleBanding(PlayerActorID bro,bool all)2291 void toggleBanding(PlayerActorID bro, bool all) {
2292 	int16   wasBanded = isBanded(bro);
2293 
2294 	if (all) {
2295 		for (int i = 0; i < kPlayerActors; i++)
2296 			setBanded(i, !wasBanded);
2297 	} else setBanded(bro, !wasBanded);
2298 }
2299 
APPFUNC(cmdBand)2300 APPFUNC(cmdBand) {
2301 	uint16 transBroID = translatePanID(ev.panel->id);
2302 
2303 	if (ev.eventType == gEventNewValue) {
2304 		toggleBanding(transBroID, rightButtonState());
2305 //		int16   wasBanded = isBanded( transBroID );
2306 //
2307 //		if (rightButtonState())
2308 //		{
2309 //			for (int i = 0; i < kPlayerActors; i++)
2310 //				setBanded( i, !wasBanded );
2311 //		}
2312 //		else setBanded( transBroID, !wasBanded );
2313 	} else if (ev.eventType == gEventMouseMove) {
2314 		if (ev.value == GfxCompImage::enter) {
2315 			// set the text in the cursor
2316 			g_vm->_mouseInfo->setText(isBanded(transBroID)
2317 			                  ? ON_BANDED
2318 			                  : OFF_BANDED);
2319 		} else if (ev.value == GfxCompImage::leave) {
2320 			g_vm->_mouseInfo->setText(nullptr);
2321 		}
2322 	}
2323 }
2324 
APPFUNC(cmdOptions)2325 APPFUNC(cmdOptions) {
2326 	if (ev.eventType == gEventNewValue) {
2327 		OptionsDialog();
2328 		//openOptionsPanel();
2329 	} else if (ev.eventType == gEventMouseMove) {
2330 		if (ev.value == GfxCompImage::enter)        g_vm->_mouseInfo->setText(OPTIONS_PANEL);
2331 		else if (ev.value == GfxCompImage::leave) g_vm->_mouseInfo->setText(nullptr);
2332 	}
2333 }
2334 
APPFUNC(cmdBroChange)2335 APPFUNC(cmdBroChange) {
2336 	if (ev.eventType == gEventNewValue) {
2337 		if (!isBrotherDead(ev.panel->id)) {
2338 			setCenterBrother(ev.panel->id);
2339 			// this sets up the buttons in trio mode to the correct
2340 			// state ( must be called before indiv mode switchtes )
2341 			setTrioBtns();
2342 			setControlPanelsToIndividualMode(ev.panel->id);
2343 		}
2344 	} else if (ev.eventType == gEventMouseMove) {
2345 		const int bufSize = 80;
2346 		const int stateBufSize = 60;
2347 
2348 		uint16  panID = ev.panel->id;
2349 
2350 		if (ev.value == GfxCompImage::enter) {
2351 			// working buffer
2352 			char buf[bufSize];
2353 			char state[stateBufSize];
2354 			uint16 brotherID = translatePanID(panID);
2355 
2356 			Portrait->getStateString(state, stateBufSize, brotherID);
2357 
2358 			switch (brotherID) {
2359 			case uiJulian:
2360 				sprintf(buf, "%s %s", JULIAN_BROSTATE, state);
2361 				break;
2362 			case uiPhillip:
2363 				sprintf(buf, "%s %s", PHILLIP_BROSTATE, state);
2364 				break;
2365 			case uiKevin:
2366 				sprintf(buf, "%s %s", KEVIN_BROSTATE, state);
2367 				break;
2368 			}
2369 			// set the text in the cursor
2370 			g_vm->_mouseInfo->setText(buf);
2371 		} else if (ev.value == GfxCompImage::leave) {
2372 			g_vm->_mouseInfo->setText(nullptr);
2373 		}
2374 	}
2375 }
2376 
APPFUNC(cmdHealthStar)2377 APPFUNC(cmdHealthStar) {
2378 	uint16 transBroID = translatePanID(ev.panel->id);
2379 
2380 	if (ev.eventType == gEventMouseMove) {
2381 		if (ev.value == GfxCompImage::leave) {
2382 			g_vm->_mouseInfo->setText(nullptr);
2383 			return;
2384 		}
2385 
2386 		if (ev.value == GfxCompImage::enter) {
2387 			ev.panel->setMousePoll(true);
2388 		}
2389 
2390 		// get the stats for the selected brother
2391 		int16 baseVitality = g_vm->_playerList[transBroID]->getBaseStats().vitality;
2392 		int16 currVitality = g_vm->_playerList[transBroID]->getEffStats()->vitality;
2393 
2394 		char buf[40];
2395 
2396 		sprintf(buf, "%s %d/%d", HEALTH_HINT, currVitality, baseVitality);
2397 		g_vm->_mouseInfo->setText(buf);
2398 	}
2399 }
2400 
APPFUNC(cmdMassInd)2401 APPFUNC(cmdMassInd) {
2402 	gWindow         *win = nullptr;
2403 	GameObject      *containerObject = nullptr;
2404 
2405 	if (ev.eventType == gEventMouseMove) {
2406 		if (ev.value == GfxCompImage::enter) {
2407 			const   int bufSize     = 40;
2408 			int     curWeight;
2409 			uint16  baseWeight;
2410 			char    buf[bufSize];
2411 
2412 			win = ev.panel->getWindow();        // get the window pointer
2413 
2414 			assert(win);
2415 
2416 			// is it something other than the brother's indicators?
2417 			if (ev.panel->id > 1) {
2418 				containerObject = (GameObject *)win->userData;
2419 			} else {
2420 				containerObject = (GameObject *)g_vm->_playerList[getCenterActorPlayerID()]->getActor();
2421 			}
2422 
2423 			assert(containerObject);
2424 
2425 			curWeight = getWeightRatio(containerObject, baseWeight);
2426 
2427 			if (baseWeight != unlimitedCapacity) {
2428 				sprintf(buf, "%s %d/%d", WEIGHT_HINT, curWeight, baseWeight);
2429 				g_vm->_mouseInfo->setText(buf);
2430 			} else
2431 				g_vm->_mouseInfo->setText(UNK_WEIGHT_HINT);
2432 		} else if (ev.value == GfxCompImage::leave) {
2433 			g_vm->_mouseInfo->setText(nullptr);
2434 		}
2435 	}
2436 }
2437 
APPFUNC(cmdBulkInd)2438 APPFUNC(cmdBulkInd) {
2439 	gWindow         *win = nullptr;
2440 	GameObject      *containerObject = nullptr;
2441 
2442 
2443 	if (ev.eventType == gEventMouseMove) {
2444 		if (ev.value == GfxCompImage::enter) {
2445 			const   int bufSize     = 40;
2446 			uint16  baseBulk    = 100;
2447 			char    buf[bufSize];
2448 			int     curBulk;
2449 
2450 			win = ev.panel->getWindow();        // get the window pointer
2451 
2452 			assert(win);
2453 
2454 			// is it something other than the brother's indicators?
2455 			if (ev.panel->id > 1) {
2456 				containerObject = (GameObject *)win->userData;
2457 			} else {
2458 				containerObject = (GameObject *)g_vm->_playerList[getCenterActorPlayerID()]->getActor();
2459 			}
2460 
2461 			assert(containerObject);
2462 
2463 			curBulk = getBulkRatio(containerObject, baseBulk);
2464 
2465 			if (baseBulk != unlimitedCapacity) {
2466 				sprintf(buf, "%s %d/%d", BULK_HINT, curBulk, baseBulk);
2467 				g_vm->_mouseInfo->setText(buf);
2468 			} else
2469 				g_vm->_mouseInfo->setText(UNK_BULK_HINT);
2470 		} else if (ev.value == GfxCompImage::leave) {
2471 			g_vm->_mouseInfo->setText(nullptr);
2472 		}
2473 	}
2474 }
2475 
APPFUNC(cmdManaInd)2476 APPFUNC(cmdManaInd) {
2477 	if (ev.eventType == gEventMouseMove) {
2478 		if (ev.value != GfxCompImage::leave) {
2479 			const   int BUF_SIZE = 64;
2480 			char    textBuffer[BUF_SIZE];
2481 			int     manaType = -1;
2482 			int     numManaRegions = ManaIndicator->getNumManaRegions();
2483 			int     i;
2484 			int     curMana = 0, baseMana = 0;
2485 			PlayerActor *player             = g_vm->_playerList[getCenterActorPlayerID()];
2486 			ActorAttributes *stats          = player->getEffStats();
2487 			ActorAttributes baseStatsRef    = player->getBaseStats();
2488 			Point16 pos = ev.mouse;
2489 
2490 			pos.x += ManaIndicator->getExtent().x;
2491 			pos.y += ManaIndicator->getExtent().y;
2492 
2493 			for (i = 0; i < numManaRegions; i++) {
2494 				Rect16 regionRect = ManaIndicator->getManaRegionRect(i);
2495 
2496 				if (regionRect.ptInside(pos)) {
2497 					manaType = i;
2498 					break;
2499 				}
2500 			}
2501 
2502 			if (manaType != -1) {
2503 				curMana     = stats->mana(clamp(0, manaType, numManaRegions));
2504 				baseMana    = baseStatsRef.mana(clamp(0, manaType, numManaRegions));
2505 			}
2506 
2507 			switch (manaType) {
2508 			case 0:
2509 				sprintf(textBuffer, "%s %d/%d", "Red Mana:", curMana, baseMana);
2510 				break;
2511 
2512 			case 1:
2513 				sprintf(textBuffer, "%s %d/%d", "Orange Mana:", curMana, baseMana);
2514 				break;
2515 
2516 			case 2:
2517 				sprintf(textBuffer, "%s %d/%d", "Yellow Mana:", curMana, baseMana);
2518 				break;
2519 
2520 			case 3:
2521 				sprintf(textBuffer, "%s %d/%d", "Green Mana:", curMana, baseMana);
2522 				break;
2523 
2524 			case 4:
2525 				sprintf(textBuffer, "%s %d/%d", "Blue Mana:", curMana, baseMana);
2526 				break;
2527 
2528 			case 5:
2529 				sprintf(textBuffer, "%s %d/%d", "Purple Mana:", curMana, baseMana);
2530 				break;
2531 
2532 			case -1:
2533 				textBuffer[0] = 0;
2534 				textBuffer[1] = 0;
2535 				break;
2536 
2537 			default:
2538 				assert(false);   // should never get here
2539 				break;
2540 			}
2541 
2542 			// set the text in the cursor
2543 			g_vm->_mouseInfo->setText(textBuffer);
2544 		} else
2545 			g_vm->_mouseInfo->setText(nullptr);
2546 	}
2547 }
2548 
isIndivMode(void)2549 bool isIndivMode(void) {
2550 	return g_vm->_indivControlsFlag;
2551 }
2552 
initUIState(void)2553 void initUIState(void) {
2554 	g_vm->_indivControlsFlag = false;
2555 	indivBrother = 0;
2556 
2557 	//updateAllUserControls();
2558 }
2559 
saveUIState(Common::OutSaveFile * outS)2560 void saveUIState(Common::OutSaveFile *outS) {
2561 	debugC(2, kDebugSaveload, "Saving UIState");
2562 
2563 	outS->write("UIST", 4);
2564 
2565 	CHUNK_BEGIN;
2566 	out->writeUint16LE(g_vm->_indivControlsFlag);
2567 	out->writeUint16LE(indivBrother);
2568 	CHUNK_END;
2569 
2570 	debugC(3, kDebugSaveload, "..._indivControlsFlag = %d", g_vm->_indivControlsFlag);
2571 	debugC(3, kDebugSaveload, "... indivBrother = %d", indivBrother);
2572 }
2573 
loadUIState(Common::InSaveFile * in)2574 void loadUIState(Common::InSaveFile *in) {
2575 	debugC(2, kDebugSaveload, "Loading UIState");
2576 
2577 	g_vm->_indivControlsFlag = in->readUint16LE();
2578 	indivBrother = in->readUint16LE();
2579 
2580 	debugC(3, kDebugSaveload, "... _indivControlsFlag = %d", g_vm->_indivControlsFlag);
2581 	debugC(3, kDebugSaveload, "... indivBrother = %d", indivBrother);
2582 
2583 	updateAllUserControls();
2584 }
2585 
cleanupUIState(void)2586 void cleanupUIState(void) {
2587 	if (StatusLine != nullptr)
2588 		StatusLine->clear();
2589 }
2590 
setValue(PlayerActorID brotherID)2591 void gArmorIndicator::setValue(PlayerActorID brotherID) {
2592 	Actor *bro = g_vm->_playerList[brotherID]->getActor();
2593 	bro->totalArmorAttributes(attr);
2594 	invalidate();
2595 }
2596 
2597 // getCurrentCompImage() is virtual function that should return
2598 // the current image to be displayed (to be used across all sub-classes)
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)2599 void gArmorIndicator::drawClipped(gPort &port,
2600                                   const Point16 &offset,
2601                                   const Rect16 &r) {
2602 	if (!_extent.overlap(r))    return;
2603 
2604 	SAVE_GPORT_STATE(port);
2605 
2606 	// get the current image
2607 	void *dispImage = getCurrentCompImage();
2608 
2609 	// make sure the image is valid
2610 	if (dispImage) {
2611 		// will part of this be drawn on screen?
2612 		if (_extent.overlap(r)) {
2613 			char buf[8];
2614 
2615 			// offset the image?
2616 			Point16 pos(_extent.x - offset.x,
2617 			            _extent.y - offset.y
2618 			           );
2619 			// draw the compressed image
2620 			if (isGhosted()) {
2621 				drawCompressedImageGhosted(port, pos, dispImage);
2622 				return;
2623 			} else drawCompressedImage(port, pos, dispImage);
2624 
2625 			// draw the armor numebrs
2626 			port.setFont(&Helv11Font);
2627 			port.setColor(11);                   // set color to white
2628 			port.setStyle(textStyleThickOutline);
2629 			port.setOutlineColor(24);                // set outline color to black
2630 			port.setMode(drawModeMatte);
2631 
2632 			if (attr.damageAbsorbtion == 0 && attr.defenseBonus == 0)
2633 				sprintf(buf, "-");
2634 			else if (attr.damageDivider > 1)
2635 				sprintf(buf, "%d/%d", attr.damageAbsorbtion, attr.damageDivider);
2636 			else sprintf(buf, "%d", attr.damageAbsorbtion);
2637 
2638 			port.drawTextInBox(buf, -1, Rect16(pos.x, pos.y, _extent.width, _extent.height),
2639 			                   textPosRight | textPosHigh, Point16(0,  2));
2640 
2641 			if (attr.damageAbsorbtion == 0 && attr.defenseBonus == 0)
2642 				sprintf(buf, "-");
2643 			else sprintf(buf, "%d", attr.defenseBonus);
2644 			port.drawTextInBox(buf, -1, Rect16(pos.x, pos.y, _extent.width, _extent.height),
2645 			                   textPosRight | textPosLow, Point16(0,  2));
2646 		}
2647 	}
2648 }
2649 
drawClipped(gPort & port,const Point16 & offset,const Rect16 & r)2650 void gEnchantmentDisplay::drawClipped(gPort &port, const    Point16 &offset, const Rect16 &r) {
2651 	Point16     pos(_extent.x + _extent.width - 10, _extent.y + 1);
2652 
2653 	pos += offset;
2654 
2655 	if (!_extent.overlap(r)) return;
2656 
2657 	for (int i = 0; i < iconCount; i++) {
2658 		if (iconFlags[i]) {
2659 			Sprite      *sp = mentalSprites->sprite(i + 162);
2660 
2661 			pos.x -= sp->size.x + 2;
2662 			DrawSprite(port, pos, sp);
2663 		}
2664 	}
2665 }
2666 
pointerMove(gPanelMessage & msg)2667 void gEnchantmentDisplay::pointerMove(gPanelMessage &msg) {
2668 	if (msg.pointerLeave) {
2669 		g_vm->_mouseInfo->setText(nullptr);
2670 	} else {
2671 		int16       x = _extent.width - 10;
2672 
2673 		setMousePoll(true);
2674 		setValue(getCenterActorPlayerID());
2675 
2676 		for (int i = 0; i < iconCount; i++) {
2677 			if (iconFlags[i]) {
2678 				Sprite      *sp = mentalSprites->sprite(i + 162);
2679 
2680 				x -= sp->size.x + 2;
2681 				if (msg.pickPos.x >= x) {
2682 					// set the text in the cursor
2683 					char    buf[128];
2684 
2685 					if (iconFlags[i] == 255)
2686 						sprintf(buf, "%s", enchantmentNames[i]);
2687 					else sprintf(buf, "%s : %d", enchantmentNames[i], iconFlags[i]);
2688 					g_vm->_mouseInfo->setText(buf);
2689 					return;
2690 				}
2691 			}
2692 		}
2693 	}
2694 }
2695 
setValue(PlayerActorID pID)2696 void gEnchantmentDisplay::setValue(PlayerActorID pID) {
2697 	Actor           *a = g_vm->_playerList[pID]->getActor();
2698 	uint8           newIconFlags[iconCount];
2699 	EnchantmentIterator iter(a);
2700 	ContainerIterator   cIter(a);
2701 
2702 	GameObject *obj = nullptr;
2703 
2704 	memset(newIconFlags, 0, sizeof newIconFlags);
2705 
2706 	/*
2707 	x   iconHaste,
2708 	    iconFirewalk,
2709 	x   iconAdrenalFervor,
2710 	x   iconShadowWalk,
2711 	    iconSunWard,
2712 	    iconSpellBarrier,
2713 	*/
2714 
2715 	for (ObjectID id1 = iter.first(&obj); id1 != Nothing; id1 = iter.next(&obj)) {
2716 		uint16 enchantmentID = obj->getExtra();
2717 		uint16 eType = getEnchantmentType(enchantmentID);
2718 		uint16 eSubType = getEnchantmentSubType(enchantmentID);
2719 		int16  eAmount = getEnchantmentAmount(enchantmentID);
2720 		uint8   duration = obj->getHitPoints();             // get hitpoints of enchant
2721 
2722 		switch (eType) {
2723 		case effectAttrib:
2724 			switch (eSubType) {
2725 //			case skillIDArchery:
2726 //			case skillIDShieldcraft:
2727 
2728 			case skillIDBludgeon:
2729 			case skillIDSwordcraft:
2730 				if (eAmount > 0) newIconFlags[iconSurestrike] = duration;
2731 				else newIconFlags[iconClumsy] = duration;
2732 				break;
2733 
2734 			case skillIDAgility:
2735 				if (eAmount > 0) newIconFlags[iconInnerBalance] = duration;
2736 				else newIconFlags[iconClumsy] = duration;
2737 				break;
2738 
2739 			case skillIDBrawn:
2740 				newIconFlags[iconBattleFever] = duration;
2741 				break;
2742 			}
2743 			break;
2744 
2745 		case effectResist:
2746 
2747 			switch (eSubType) {
2748 			case kDamageImpact:
2749 				newIconFlags[iconResistImpact] = duration;
2750 				break;
2751 			case kDamageSlash:
2752 				newIconFlags[iconResistSlash] = duration;
2753 				break;
2754 			case kDamageProjectile:
2755 				newIconFlags[iconResistProjectile] = duration;
2756 				break;
2757 			case kDamageFire:
2758 				newIconFlags[iconResistFire] = duration;
2759 				break;
2760 			case kDamageAcid:
2761 				newIconFlags[iconResistAcid] = duration;
2762 				break;
2763 			case kDamageHeat:
2764 				newIconFlags[iconResistHeat] = duration;
2765 				break;
2766 			case kDamageCold:
2767 				newIconFlags[iconResistCold] = duration;
2768 				break;
2769 			case kDamageLightning:
2770 				newIconFlags[iconResistLightning] = duration;
2771 				break;
2772 			case kDamagePoison:
2773 				newIconFlags[iconResistPoison] = duration;
2774 				break;
2775 			case kDamageMental:
2776 				newIconFlags[iconResistPsionic] = duration;
2777 				break;
2778 			case kDamageDirMagic:
2779 				newIconFlags[iconResistDirectMagic] = duration;
2780 				break;
2781 			}
2782 			break;
2783 
2784 		case effectImmune:
2785 			switch (eSubType) {
2786 			case kDamageImpact:
2787 				newIconFlags[iconIronskin] = duration;
2788 				break;
2789 			case kDamageSlash:
2790 				newIconFlags[iconIronskin] = duration;
2791 				break;
2792 			case kDamageFire:
2793 				newIconFlags[iconImmuneFire] = duration;
2794 				break;
2795 			case kDamageAcid:
2796 				newIconFlags[iconImmuneAcid] = duration;
2797 				break;
2798 			case kDamageHeat:
2799 				newIconFlags[iconImmuneHeat] = duration;
2800 				break;
2801 			case kDamageCold:
2802 				newIconFlags[iconImmuneCold] = duration;
2803 				break;
2804 			case kDamageLightning:
2805 				newIconFlags[iconImmuneLightning] = duration;
2806 				break;
2807 			case kDamagePoison:
2808 				newIconFlags[iconImmunePoison] = duration;
2809 				break;
2810 			case kDamageMental:
2811 				newIconFlags[iconImmunePsionic] = duration;
2812 				break;
2813 			}
2814 			break;
2815 
2816 		case effectOthers:
2817 			switch (eSubType) {
2818 			case actorPoisoned:
2819 			case actorDiseased:
2820 				newIconFlags[iconPoisoned] = duration;
2821 				break;
2822 
2823 			case actorFear:
2824 				newIconFlags[iconAfraid] = duration;
2825 				break;
2826 			case actorParalyzed:
2827 				newIconFlags[iconParalysed] = duration;
2828 				break; // iconFrozen ??
2829 			case actorSlowFall:
2830 				newIconFlags[iconCushionAir] = duration;
2831 				break;
2832 			case actorImmobile:
2833 				newIconFlags[iconConstrained] = duration;
2834 				break;
2835 			case actorSeeInvis:
2836 				newIconFlags[iconSoulSight] = duration;
2837 				break;
2838 			case actorInvisible:
2839 				newIconFlags[iconInvisible] = duration;
2840 				break;
2841 			case actorUndetectable:
2842 				newIconFlags[iconNumbscent] = duration;
2843 				break;
2844 			case actorDetPoison:
2845 				newIconFlags[iconDetectPoison] = duration;
2846 				break;
2847 			case actorNoDrain:
2848 				newIconFlags[iconNetherWard] = duration;
2849 				break;
2850 			case actorWaterBreathe:
2851 				newIconFlags[iconSeawalk] = duration;
2852 				break;
2853 			case actorRepelEvil:
2854 				newIconFlags[iconProtectEvil] = duration;
2855 				break;
2856 //			case actorRepelUndead: newIconFlags[iconProtectUndead] = duration; break;
2857 //			case actorRepelGhosts: newIconFlags[iconProtectGhosts] = duration; break;
2858 			}
2859 		}
2860 	}
2861 
2862 	while (cIter.next(&obj)) {
2863 		ProtoObj        *proto = obj->proto();
2864 		uint16          cSet = proto->containmentSet();
2865 
2866 		if ((cSet & (ProtoObj::isArmor | ProtoObj::isWeapon | ProtoObj::isWearable))
2867 		        &&  proto->isObjectBeingUsed(obj)) {
2868 			if (proto->immunity & (1 << resistImpact))            newIconFlags[iconResistImpact] = 255;
2869 			else if (proto->resistance & (1 << resistImpact)) newIconFlags[iconResistImpact] = 255;
2870 
2871 			if (proto->immunity & (1 << resistSlash))         newIconFlags[iconResistSlash] = 255;
2872 			else if (proto->resistance & (1 << resistSlash))  newIconFlags[iconResistSlash] = 255;
2873 
2874 			if (proto->immunity & (1 << resistProjectile))        newIconFlags[iconResistProjectile] = 255;
2875 			else if (proto->resistance & (1 << resistProjectile)) newIconFlags[iconResistProjectile] = 255;
2876 
2877 			if (proto->immunity & (1 << immuneFire))          newIconFlags[iconImmuneFire] = 255;
2878 			else if (proto->resistance & (1 << resistFire))   newIconFlags[iconResistFire] = 255;
2879 
2880 			if (proto->immunity & (1 << immuneAcid))          newIconFlags[iconImmuneAcid] = 255;
2881 			else if (proto->resistance & (1 << resistAcid))   newIconFlags[iconResistAcid] = 255;
2882 
2883 			if (proto->immunity & (1 << immuneHeat))          newIconFlags[iconImmuneHeat] = 255;
2884 			else if (proto->resistance & (1 << resistHeat))   newIconFlags[iconResistHeat] = 255;
2885 
2886 			if (proto->immunity & (1 << immuneCold))          newIconFlags[iconImmuneCold] = 255;
2887 			else if (proto->resistance & (1 << resistCold))   newIconFlags[iconResistCold] = 255;
2888 
2889 			if (proto->immunity & (1 << immuneLightning))     newIconFlags[iconImmuneLightning] = 255;
2890 			else if (proto->resistance & (1 << resistLightning)) newIconFlags[iconResistLightning] = 255;
2891 
2892 			if (proto->immunity & (1 << immunePoison))        newIconFlags[iconImmunePoison] = 255;
2893 			else if (proto->resistance & (1 << resistPoison)) newIconFlags[iconResistPoison] = 255;
2894 
2895 			if (proto->immunity & (1 << immuneMental))        newIconFlags[iconImmunePsionic] = 255;
2896 			else if (proto->resistance & (1 << resistMental)) newIconFlags[iconResistPsionic] = 255;
2897 
2898 			if (proto->immunity & (1 << resistDirMagic))      newIconFlags[iconResistDirectMagic] = 255;
2899 			else if (proto->resistance & (1 << resistDirMagic))newIconFlags[iconResistDirectMagic] = 255;
2900 		}
2901 	}
2902 
2903 	//  Compute icon flags for resistances and immunities
2904 
2905 #if 0
2906 	enum effectOthersTypes {
2907 		// Movement flags
2908 		o   actorNoncorporeal   = 1,    // The creature can walk through things
2909 		x   actorWaterBreathe   = 2,    // death spell
2910 		x   actorSlowFall       = 3,    // the creature is not harmed by falling (but falls none the less)
2911 		-   actorLevitate       = 4,    // flying with no height control ?
2912 		-   actorFlying         = 5,    // the creature flys
2913 		// speed flags
2914 		-   actorFastMove       = 6,    //
2915 		-   actorFastAttack     = 7,    //
2916 		actorSlowAttack     = 8,    // come... back... here... lit... tle... bun... ny...
2917 		x   actorSlowMove       = 9,    // I thought I told you to leave the piano at home
2918 		// ill effects
2919 		-   actorAsleep         = 10,   // Zzzzzzzzzzz
2920 		x   actorParalyzed      = 11,   // the creature can't move an inch
2921 		x   actorFear           = 12,   // run away! run away
2922 		x   actorDiseased       = 13,   // cannot heal
2923 		x   actorPoisoned       = 14,   // death spell
2924 		// perception & perceivability flags
2925 		x   actorBlind          = 15,   // can't see
2926 		x   actorSeeInvis       = 16,   // can see invisible
2927 		-   actorClairvoyant    = 17,  // unknown effects
2928 		x   actorInvisible      = 18,   // is invisible
2929 		x   actorUndetectable   = 19,   // can't be seen, smelled
2930 		x   actorDetPoison      = 20,   // poison things glow green
2931 		// flags preventing changes to other flags
2932 		actorNoEnchant      = 21,   // no bad enchantments
2933 		x   actorNoDrain        = 22,   // no mana / food drains
2934 		// flags that make things run away
2935 		x   actorRepelEvil      = 23,   // death spell
2936 		x   actorRepelGood      = 24,   // death spell
2937 		x   actorRepelUndead    = 25,   // death spell
2938 		// dead or moved flags
2939 //		actorMapping        =15,    //
2940 //		actorLandWalk       =0 ,    // someone else had this I have no idea what it is
2941 //		actorFloat          =2 ,    // the creature can travel through malts shakes & sundaes
2942 		actorWaterWalk,          // can walk on water (same as float ?)
2943 //		actorPanic          =13,    // creature takes off randomly
2944 //		actorSpotHidden     =17,    // can see hidden
2945 //		actorDetTraps       =22,    // traps glow green
2946 //		actorFlameAura      =23,    // has a flaming aura
2947 //		actorDead           =25,    // death spell
2948 	};
2949 #endif
2950 
2951 	//  If icon flags changed, then redraw the control.
2952 
2953 	if (memcmp(iconFlags, newIconFlags, sizeof iconFlags)) {
2954 		memcpy(iconFlags, newIconFlags, sizeof iconFlags);
2955 		invalidate();
2956 	}
2957 }
2958 
isBrotherDead(PlayerActorID brotherID)2959 bool isBrotherDead(PlayerActorID brotherID) {
2960 	return (getPlayerActorAddress(brotherID)->getActor()->isDead());
2961 }
2962 
StatusMsg(const char * msg,...)2963 void StatusMsg(const char *msg, ...) { // frametime def
2964 	va_list         argptr;
2965 	char            buffer[128];
2966 
2967 	if (StatusLine) {
2968 		va_start(argptr, msg);
2969 		vsprintf(buffer, msg, argptr);
2970 		va_end(argptr);
2971 
2972 		StatusLine->setLine(buffer, 500);
2973 	}
2974 }
2975 
2976 } // end of namespace Saga2
2977