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