scummvm/engines/saga2/intrface.cpp
2021-12-26 18:48:43 +01:00

2982 lines
86 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
#include "saga2/saga2.h"
#include "saga2/detection.h"
#include "saga2/blitters.h"
#include "saga2/objects.h"
#include "saga2/contain.h"
#include "saga2/intrface.h"
#include "saga2/grabinfo.h"
#include "saga2/uidialog.h"
#include "saga2/motion.h"
#include "saga2/enchant.h"
#include "saga2/display.h"
#include "saga2/localize.h"
#include "saga2/imagcach.h"
#include "saga2/fontlib.h"
#include "saga2/uimetrcs.h"
namespace Saga2 {
/* ===================================================================== *
External delarations
* ===================================================================== */
extern ReadyContainerView *TrioCviews[kNumViews];
extern ReadyContainerView *indivCviewTop, *indivCviewBot;
extern gPanelList *trioControls, *indivControls;
extern gPanelList *playControls;
extern const uint32 imageGroupID;
extern gPanelList *tileControls;
extern BackWindow *mainWindow;
extern uint8 fixedColors[16];
/* ===================================================================== *
Classes
* ===================================================================== */
// Private subclass of GfxCompImage for armor display
class gArmorIndicator : public GfxCompImage {
public:
ArmorAttributes attr;
void drawClipped(gPort &,
const Point16 &,
const Rect16 &) override;
void setValue(PlayerActorID pID);
gArmorIndicator(gPanelList &list, const Rect16 &box, void *img, uint16 ident, AppFunc *cmd = nullptr)
: GfxCompImage(list, box, img, ident, cmd) {
attr.damageAbsorbtion = 0;
attr.damageDivider = 1;
attr.defenseBonus = 0;
}
};
// Private subclass of gControl for enchantment display
// REM: Some of this should probably be moved to a header file, even though no-one
// is ever going to need it other than this file.
enum enchantmentIcons {
iconInvisible = 0,
iconDetectPoison,
iconSoulSight,
iconPoisoned,
iconParalysed,
iconClumsy,
iconFrozen,
iconConstrained,
iconAfraid,
iconSeawalk,
iconHaste,
iconFirewalk,
iconSurestrike,
iconAdrenalFervor,
iconInnerBalance,
iconShadowWalk,
iconBattleFever,
iconSunWard,
iconNetherWard,
iconSpellBarrier,
iconIronskin,
iconNumbscent,
iconProtectEvil,
iconProtectUndead,
iconProtectGhosts,
iconCushionAir,
iconResistImpact,
iconResistSlash,
iconResistProjectile,
iconResistFire,
iconResistAcid,
iconResistHeat,
iconResistCold,
iconResistLightning,
iconResistPoison,
iconResistPsionic,
iconResistDirectMagic,
iconImmuneFire,
iconImmuneAcid,
iconImmuneHeat,
iconImmuneCold,
iconImmuneLightning,
iconImmunePoison,
iconImmunePsionic,
iconCount
};
const char *enchantmentNames[] = {
"Invisible",
"Detect Poison",
"Soul Sight",
"Poisoned",
"Paralysed",
"Clumsy",
"Frozen",
"Constrained",
"Afraid",
"Seawalk",
"Haste",
"Fire Walk",
"Surestrike",
"Adrenal Fervor",
"Inner Balance",
"Shadow Walk",
"Battle Fever",
"Sun Ward",
"Nether Ward",
"Spell Barrier",
"Ironskin",
"Numbscent",
"Protection from Evil",
"Protection from Undead",
"Protection from Ghosts",
"Cushion of Air",
"Resist impact damage",
"Resist slashing damage",
"Resist projectile damage",
"Resist Fire / Yellow Magic",
"Resist Acid / Violet Magic",
"Resist Heat / Red Magic",
"Resist Cold / Blue Magic",
"Resist Lightning / Orange Magic",
"Resist Poison / Green Magic",
"Resist Mental Damage",
"Resist Direct Magic",
"Immunity to Fire / Yellow Magic",
"Immunity to Acid / Violet Magic",
"Immunity to Heat / Red Magic",
"Immunity to Cold / Blue Magic",
"Immunity to Lightning / Orange Magic",
"Immunity to Poison / Green Magic",
"Immunity to Mental Damage",
nullptr
};
class gEnchantmentDisplay : public gControl {
uint8 iconFlags[iconCount];
void drawClipped(gPort &, const Point16 &, const Rect16 &) override;
void pointerMove(gPanelMessage &msg) override;
public:
void setValue(PlayerActorID pID);
gEnchantmentDisplay(gPanelList &list, uint16 ident, AppFunc *cmd = nullptr)
: gControl(list, Rect16(0, 0, 630, 18), nullptr, ident, cmd) {
memset(iconFlags, 0, sizeof iconFlags);
}
};
/* ===================================================================== *
Imports
* ===================================================================== */
#ifndef FTAASM_H
extern void compositePixels(
gPixelMap *compMap,
gPixelMap *sprMap,
int32 xpos,
int32 ypos,
uint8 *lookup);
extern void unpackImage(gPixelMap *map,
int32 width,
int32 rowCount,
int8 *srcData);
#endif
// this is a redeclare of the struct in playmode.cpp
typedef struct {
Point16 size;
int16 compress;
int8 data[2];
} ImageHeader;
// external appfuncs
APPFUNC(cmdBrain);
/* ===================================================================== *
User interface application functions
* ===================================================================== */
APPFUNC(cmdPortrait);
APPFUNC(cmdAggressive);
//APPFUNC( cmdJump );
APPFUNC(cmdArmor);
APPFUNC(cmdCenter);
APPFUNC(cmdBand);
APPFUNC(cmdOptions);
APPFUNC(cmdBroChange);
APPFUNC(cmdHealthStar);
APPFUNC(cmdMassInd);
APPFUNC(cmdBulkInd);
APPFUNC(cmdManaInd);
/* ===================================================================== *
User control metrics
* ===================================================================== */
// position arrays for all buttons on the individual panels
static const StaticRect topBox[numButtons] = {
/* portrait */ { 489, 22 + (yContOffset * 0), 65, 72 },
/* agress */ { 559, 86 + (yContOffset * 0), 28, 27 },
/* jump */ { 592, 86 + (yContOffset * 0), 28, 27 },
/* center */ { 559, 56 + (yContOffset * 0), 28, 27 },
/* banding */ { 592, 56 + (yContOffset * 0), 28, 27 },
/* namePlates */ { 488, 94 + (yFaceOffset * 0), 65, 15 },
/* namePlateFrames */ { 487, 20 + (yFaceOffset * 0), 69, 92 }
};
static const StaticRect midBox[numButtons] = {
{ 489, 22 + (yContOffset * 1), 65, 72 },
{ 559, 86 + (yContOffset * 1), 28, 27 },
{ 592, 86 + (yContOffset * 1), 28, 27 },
{ 559, 56 + (yContOffset * 1), 28, 27 },
{ 592, 56 + (yContOffset * 1), 28, 27 },
{ 488, 94 + (yFaceOffset * 1), 65, 15 },
{ 487, 20 + (yFaceOffset * 1), 69, 92 }
};
static const StaticRect botBox[numButtons] = {
{ 489, 22 + (yContOffset * 2), 65, 72 },
{ 559, 86 + (yContOffset * 2), 28, 27 },
{ 592, 86 + (yContOffset * 2), 28, 27 },
{ 559, 56 + (yContOffset * 2), 28, 27 },
{ 592, 56 + (yContOffset * 2), 28, 27 },
{ 488, 94 + (yFaceOffset * 2), 65, 15 },
{ 487, 20 + (yFaceOffset * 2), 69, 92 }
};
// options button
GfxCompButton *optBtn;
gEnchantmentDisplay *enchDisp;
// brother buttons
GfxOwnerSelCompButton *julBtn;
GfxOwnerSelCompButton *phiBtn;
GfxOwnerSelCompButton *kevBtn;
GfxCompImage *broBtnFrame;
// trio controls
GfxMultCompButton *portBtns[kNumViews];
GfxOwnerSelCompButton *aggressBtns[kNumViews];
//GfxCompButton *jumpBtns[kNumViews];
GfxOwnerSelCompButton *centerBtns[kNumViews];
GfxOwnerSelCompButton *bandingBtns[kNumViews];
GfxCompImage *namePlates[kNumViews];
GfxCompImage *namePlateFrames[kNumViews];
gArmorIndicator *armorInd[kNumViews];
// individual
GfxMultCompButton *indivPortBtn;
GfxOwnerSelCompButton *indivAggressBtn;
//GfxCompButton *indivJumpBtn;
GfxOwnerSelCompButton *indivCenterBtn;
GfxOwnerSelCompButton *indivBandingBtn;
GfxCompImage *indivNamePlate;
GfxCompImage *indivNamePlateFrame;
gArmorIndicator *indivArmorInd;
// mental button/indicators
GfxCompButton *menConBtn;
// [brother panels] compressed image non-allocated pointer arrays
void **portImag[kNumViews];
void **aggressImag;
//void **jumpImag;
void **centerImag;
void **bandingImag;
void **menConBtnImag;
void **optBtnImag;
void **julBtnImag;
void **phiBtnImag;
void **kevBtnImag;
// portrait name plate things
void *namePlateImages[kNumViews];
void *namePlateFrameImag;
void *armorImag;
// brother selection button frame
void *broBtnFrameImag;
// name plate resource indexes
int16 namePlateResNum[] = { 8, 9, 10 };
// number of plate images
int16 numNamePlateRes[] = { 1, 1, 1 };
// brother resource names
ResName broNames[] = {
{ 'J', 'U', 'L', 0 },
{ 'P', 'H', 'I', 0 },
{ 'K', 'E', 'V', 0 }
};
const uint32 containerGroupID = MKTAG('C', 'O', 'N', 'T');
// button position views
// topBox, midBox, and botBox are defined in uimetrcs.h
static const StaticRect *views[] = {
topBox,
midBox,
botBox
};
// individual indicators/buttons
static const StaticRect menConBtnRect = {485, 265, 44, 43};
// options button
static const StaticRect optBtnRect = {20, 445, 26, 15};
// brother buttons and frame
static const StaticRect broBtnRect = {481, 450, 144, 11};
static const StaticRect julBtnRect = {482, 451, 44, 9};
static const StaticRect phiBtnRect = {531, 451, 44, 9};
static const StaticRect kevBtnRect = {580, 451, 44, 9};
StaticTextPallete genericTextPal = {9 + 15, 20, 14, 11, 23, 17};
/* uint8 dlPen;
uint8 urPen;
uint8 inPen;
uint8 dlHilitePen;
uint8 urHilitePen;
uint8 inHilitePen; */
// [Added here by Talin to fix problem in Win32. I don't know if it's the right place]
class CPortrait *Portrait;
/* ===================================================================== *
ui controls
* ===================================================================== */
// which brother is set in the individual controls
uint16 indivBrother;
bool isBrotherDead(PlayerActorID brotherID);
/* ===================================================================== *
Global class declarations
* ===================================================================== */
// these objhets have to be initialized after resource are initialized
CStatusLine *StatusLine = nullptr;
CMassWeightIndicator *MassWeightIndicator = nullptr;
CHealthIndicator *HealthIndicator = nullptr;
CManaIndicator *ManaIndicator = nullptr;
/* ===================================================================== *
Plaq writing class
* ===================================================================== */
CPlaqText::CPlaqText(gPanelList &list,
const Rect16 &box,
const char *msg,
gFont *font,
int16 textPos,
textPallete &pal,
int16 ident,
AppFunc *cmd)
: gControl(list, box, msg, ident, cmd) {
if (strlen(msg) <= bufSize) {
strcpy(lineBuf, msg);
} else {
*lineBuf = '\0';
}
textFacePal = pal;
buttonFont = font;
textRect = box;
textPosition = textPos;
oldFont = nullptr;
}
void CPlaqText::enable(bool abled) {
gPanel::enable(abled);
}
void CPlaqText::invalidate(Rect16 *) {
window.update(_extent);
}
void CPlaqText::draw() {
gPort &port = window.windowPort;
Rect16 rect = window.getExtent();
// save pen color, etc.
SAVE_GPORT_STATE(port);
oldFont = port.font;
// setup the port
port.setMode(drawModeMatte);
port.setFont(buttonFont);
g_vm->_pointer->hide(port, _extent); // hide mouse pointer
drawClipped(port, Point16(0, 0), Rect16(0, 0, rect.width, rect.height));
g_vm->_pointer->show(port, _extent); // show mouse pointer
// reset the old font
port.setFont(oldFont);
}
void CPlaqText::drawClipped(gPort &port,
const Point16 &offset,
const Rect16 &r) {
if (_extent.overlap(r)) {
if (*lineBuf) {
textRect = _extent;
textRect.x -= offset.x;
textRect.y -= offset.y;
writePlaqText(port, textRect, buttonFont, textPosition, textFacePal, selected, lineBuf);
}
}
}
/* ===================================================================== *
Portrait control class
* ===================================================================== */
CPortrait::CPortrait(GfxMultCompButton **portraits,
GfxMultCompButton *indivPort,
const uint16 numPorts,
uint16 numBrothers) { // numBrothers = post 1
// do some checking
assert(portraits);
assert(indivPort);
for (uint16 i = 0; i < numBrothers; i++) {
assert(portraits[i]);
};
buttons = portraits; // set the pointer for class
indivButton = indivPort; // set the individual portrait
numButtons = numPorts; // number of buttons per pointer
_numViews = numBrothers; // number of pointers for whole array
// start off in a normal facial state
for (uint16 i = 0; i < _numViews + 1; i++) {
currentState[i] = kPortraitNormal;
}
}
void CPortrait::setPortrait(uint16 brotherID) {
assert(brotherID < _numViews + 1);
// tell button to select and display new image
if (brotherID == uiIndiv) {
WriteStatusF(4, " Brother id %d", brotherID);
indivButton->setCurrent(currentState[brotherID]);
indivButton->invalidate();
} else {
buttons[brotherID]->setCurrent(currentState[brotherID]);
buttons[brotherID]->invalidate();
}
}
void CPortrait::set(uint16 brotherID, PortraitType type) {
currentState[brotherID] = type;
setPortrait(brotherID);
}
void CPortrait::ORset(uint16 brotherID, PortraitType type) { // brotherID = post 0
assert(brotherID < _numViews + 1);
if (type == currentState[brotherID]) {
currentState[brotherID] = kPortraitNormal;
} else {
currentState[brotherID] = type;
}
// set this button to the new state
setPortrait(brotherID);
}
void CPortrait::getStateString(char buf[], int8 size, uint16 brotherID) {
static char asleepStr[] = ASLEEP_STATE;
static char paralysedStr[] = PARALY_STATE;
static char blindStr[] = BLIND_STATE ;
static char afraidStr[] = AFRAID_STATE;
static char angryStr[] = ANGRY_STATE ;
static char badlyWoundedStr[] = BADWND_STATE;
static char hurtStr[] = HURT_STATE ;
static char poisonedStr[] = POISON_STATE;
static char diseasedStr[] = DISEAS_STATE;
static char normalStr[] = NORMAL_STATE;
static char commaStr[] = ", ";
PlayerActor *pa = getPlayerActorAddress(brotherID);
Actor *a = pa->getActor();
ActorAttributes &stats = pa->getBaseStats();
buf[size - 1] = '\0';
if (a->isDead()) {
Common::strlcpy(buf, DEAD_STATE, size);
return;
}
buf[0] = '\0';
if (a->_enchantmentFlags & (1 << actorAsleep)) {
Common::strlcat(buf, asleepStr, size);
} else if (a->_enchantmentFlags & (1 << actorParalyzed)) {
Common::strlcat(buf, paralysedStr, size);
} else if (a->_enchantmentFlags & (1 << actorBlind)) {
Common::strlcat(buf, blindStr, size);
} else if (a->_enchantmentFlags & (1 << actorFear)) {
Common::strlcat(buf, afraidStr, size);
} else if (pa->isAggressive()) {
Common::strlcat(buf, angryStr, size);
}
if (stats.vitality >= a->_effectiveStats.vitality * 3) {
if (buf[0] != '\0') // strlen(buf) > 0
Common::strlcat(buf, commaStr, size);
Common::strlcat(buf, badlyWoundedStr, size);
} else if (stats.vitality * 2 > a->_effectiveStats.vitality * 3) {
if (buf[0] != '\0') // strlen(buf) > 0
Common::strlcat(buf, commaStr, size);
Common::strlcat(buf, hurtStr, size);
}
if (a->_enchantmentFlags & (1 << actorPoisoned)) {
if (buf[0] != '\0') // strlen(buf) > 0
Common::strlcat(buf, commaStr, size);
Common::strlcat(buf, poisonedStr, size);
} else if (a->_enchantmentFlags & (1 << actorDiseased)) {
if (buf[0] != '\0') // strlen(buf) > 0
Common::strlcat(buf, commaStr, size);
Common::strlcat(buf, diseasedStr, size);
}
if (buf[0] == '\0') // strlen(buf) == 0
Common::strlcat(buf, normalStr, size);
}
/* ===================================================================== *
status line gadget
* ===================================================================== */
// status line class
CStatusLine::CStatusLine(gPanelList &list,
const Rect16 &box,
const char *msg,
gFont *font,
int16 textPos,
textPallete pal,
int32 /*frameTime*/,
int16 ident,
AppFunc *cmd) :
CPlaqText(list, box, msg, font, textPos, pal, ident, cmd) {
lineDisplayed = false;
queueHead = queueTail = 0;
for (int i = 0; i < ARRAYSIZE(lineQueue); i++) {
lineQueue[i].text = nullptr;
lineQueue[i].frameTime = 0;
}
waitAlarm.basetime = waitAlarm.duration = 0;
minWaitAlarm.basetime = minWaitAlarm.duration = 0;
}
CStatusLine::~CStatusLine() {
while (queueTail != queueHead) {
assert(lineQueue[queueTail].text != nullptr);
delete[] lineQueue[queueTail].text;
queueTail = bump(queueTail);
}
}
void CStatusLine::setLine(char *msg, uint32 frameTime) { // frametime def
uint8 newHead = bump(queueHead);
if (newHead != queueTail) {
size_t msgLen = strlen(msg);
if ((lineQueue[queueHead].text = new char[msgLen + 1]()) != nullptr) {
strcpy(lineQueue[queueHead].text, msg);
lineQueue[queueHead].frameTime = frameTime;
queueHead = newHead;
}
}
}
void CStatusLine::experationCheck() {
if (lineDisplayed
&& (waitAlarm.check()
|| (queueTail != queueHead && minWaitAlarm.check()))) {
enable(false);
window.update(_extent);
lineDisplayed = false;
}
if (!lineDisplayed && queueTail != queueHead) {
// enable the control
enable(true);
// set up the time for this message
waitAlarm.set(lineQueue[queueTail].frameTime);
minWaitAlarm.set(lineQueue[queueTail].frameTime / 5);
// copy upto the buffer's size in chars
Common::strlcpy(lineBuf, lineQueue[queueTail].text, bufSize);
lineBuf[bufSize - 1] = '\0';
// free the queue text buffer
delete[] lineQueue[queueTail].text;
lineQueue[queueTail].text = nullptr;
// bump the queue tail
queueTail = bump(queueTail);
// draw the new textline
window.update(_extent);
lineDisplayed = true;
}
}
void CStatusLine::clear() {
enable(false);
window.update(_extent);
lineDisplayed = false;
queueHead = queueTail = 0;
}
/* ===================================================================== *
CMassWeightInterface: mass and weight allowence indicators
* ===================================================================== */
bool CMassWeightIndicator::bRedraw;
CMassWeightIndicator::CMassWeightIndicator(gPanelList *panel, const Point16 &pos, uint16 type, bool death) {
// set up the position of this indicator
backImagePos = pos;
massPiePos = backImagePos;
bulkPiePos = backImagePos;
massPiePos.x += massPieXOffset;
massPiePos.y += massPieYOffset;
bulkPiePos.x += bulkPieXOffset;
bulkPiePos.y += bulkPieYOffset;
bRedraw = true; // this MUST be true or the indicators will not draw the first time
// attach the resource context
containerRes = resFile->newContext(containerGroupID, "container context");
// setup mass/bulk indicator imagery
if (death) {
massBulkImag = g_vm->_imageCache->requestImage(containerRes, MKTAG('D', 'J', 'B', massBulkResNum));
pieIndImag = loadImageRes(containerRes, pieIndResNum, numPieIndImages, 'D', 'A', 'J');
} else {
massBulkImag = g_vm->_imageCache->requestImage(containerRes, MKTAG('G', 'J', 'B', massBulkResNum));
pieIndImag = loadImageRes(containerRes, pieIndResNum, numPieIndImages, 'G', 'A', 'J');
}
// attach controls to the indivControls panel
// these butttons will get deactivated along with the panel
pieMass = new GfxCompImage(*panel,
Rect16(massPiePos.x, massPiePos.y, pieXSize, pieYSize),
pieIndImag,
numPieIndImages,
0,
type,
cmdMassInd);
pieBulk = new GfxCompImage(*panel,
Rect16(bulkPiePos.x, bulkPiePos.y, pieXSize, pieYSize),
pieIndImag,
numPieIndImages,
0,
type,
cmdBulkInd);
// mass/bulk back image
new GfxCompImage(*panel,
Rect16(backImagePos.x, backImagePos.y, backImageXSize, backImageYSize),
massBulkImag,
uiIndiv,
nullptr);
// release resource context
if (containerRes) {
resFile->disposeContext(containerRes);
containerRes = nullptr;
}
currentMass = 0;
currentBulk = 0;
// if this is something other then the ready containers
if (type > 1) {
containerObject = (GameObject *)panel->userData;
} else {
containerObject = nullptr;
}
g_vm->_indList.push_back(this);
}
CMassWeightIndicator::~CMassWeightIndicator() {
g_vm->_indList.remove(this);
unloadImageRes(pieIndImag, numPieIndImages);
g_vm->_imageCache->releaseImage(massBulkImag);
}
/*****************************************************************************
** Method: recalculate()
** Description: This will recalculate the values to be displayed for the
** mass and bulk of the current ( single mode ) player
**/
void CMassWeightIndicator::recalculate() {
assert(pieMass);
assert(pieBulk);
uint16 mass = getMassPieDiv();
uint16 bulk = getBulkPieDiv();
uint16 retMass, retBulk;
if (containerObject) {
setMassPie(retMass = getWeightRatio(containerObject, mass, false));
setBulkPie(retBulk = getBulkRatio(containerObject, bulk, false));
} else {
setMassPie(retMass = getWeightRatio(g_vm->_playerList[getCenterActorPlayerID()]->getActor(), mass, false));
setBulkPie(retBulk = getBulkRatio(g_vm->_playerList[getCenterActorPlayerID()]->getActor(), bulk, false));
}
}
/*****************************************************************************
** Method: update()
** Description: This will call recalculate and then invalidate the entire
** weight/bulk control ( so it refreshes )
**/
void CMassWeightIndicator::update() {
if (bRedraw == true) {
for (Common::List<CMassWeightIndicator *>::iterator it = g_vm->_indList.begin(); it != g_vm->_indList.end(); ++it) {
(*it)->recalculate();
(*it)->invalidate();
}
bRedraw = false;
}
}
/* ===================================================================== *
CManaIndicator: magic stuff indicator
* ===================================================================== */
// setup the mana color tables
static uint8 manaColorMap[CManaIndicator::numManaTypes][CManaIndicator::numManaColors + 9] = {
// each row has 9 leading zero's to acount for windows colors
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x74, 0x73, 0x72, 0x80, 0x84, 0x83, 0x82, 0x82, 0x81, 0x81, 0xF4, 0xE9 }, // Red
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x74, 0x73, 0x72, 0x78, 0x77, 0x76, 0x75, 0x6B, 0x6A, 0x69, 0x3A, 0x39 }, // Orange
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59 }, // Yellow
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2, 0xE1, 0xE0, 0xDF, 0xDE, 0xC9 }, // Green
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA0, 0x9F, 0x9E, 0xB2, 0xB1, 0xB0, 0xAF, 0xAE, 0xAD, 0xAC, 0xAA, 0xA9 }, // Blue
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA0, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x93, 0x92, 0x92, 0x91, 0x0C } // Violet
};
CManaIndicator::CManaIndicator(gPanelList &list) : GfxCompImage(list,
Rect16(x, y, xSize, ySize),
nullptr,
0,
cmdManaInd) {
assert(resFile);
// init the resource handle with the mana resource group
resContext = resFile->newContext(MKTAG('M', 'A', 'N', 'A'), "mana context");
// load star images
starImages = loadImageRes(resContext, starResNum, numStars, 'S', 'T', 'A');
// load in the ring images
ringImages = loadImageRes(resContext, ringResNum, numRings, 'R', 'N', 'G');
backImage = g_vm->_imageCache->requestImage(resContext, MKTAG('B', 'A', 'C', 'K'));
wellImage = g_vm->_imageCache->requestImage(resContext, MKTAG('W', 'E', 'L', 'L'));
// hmm this could be cleaner...
starRingEndPos[0] = Point16(redEndX, redEndY);
starRingEndPos[1] = Point16(orangeEndX, orangeEndY);
starRingEndPos[2] = Point16(yellowEndX, yellowEndY);
starRingEndPos[3] = Point16(greenEndX, greenEndY);
starRingEndPos[4] = Point16(blueEndX, blueEndY);
starRingEndPos[5] = Point16(violetEndX, violetEndY);
starSizes[0] = Point16(star1XSize, star1YSize);
starSizes[1] = Point16(star2XSize, star2YSize);
starSizes[2] = Point16(star3XSize, star3YSize);
starSizes[3] = Point16(star4XSize, star4YSize);
starSizes[4] = Point16(star5XSize, star5YSize);
starSizes[5] = Point16(star6XSize, star6YSize);
starSizes[6] = Point16(star7XSize, star7YSize);
ringSizes[0] = Point16(ring1XSize, ring1YSize);
ringSizes[1] = Point16(ring2XSize, ring2YSize);
ringSizes[2] = Point16(ring3XSize, ring3YSize);
ringSizes[3] = Point16(ring4XSize, ring4YSize);
ringSizes[4] = Point16(ring5XSize, ring5YSize);
ringSizes[5] = Point16(ring6XSize, ring6YSize);
ringSizes[6] = Point16(ring7XSize, ring7YSize);
// get rid of resource context
resFile->disposeContext(resContext);
resContext = nullptr;
// set update checks to nominal values
for (uint16 i = 0; i < numManaTypes; i++) {
currentMana[i] = -1;
currentBaseMana[i] = -1;
}
// init the save map
savedMap.size = Extent16(xSize, ySize);
savedMap.data = new uint8[savedMap.bytes()];
}
CManaIndicator::~CManaIndicator() {
// release images
unloadImageRes(starImages, numStars);
unloadImageRes(ringImages, numRings);
// release back image
g_vm->_imageCache->releaseImage(backImage);
g_vm->_imageCache->releaseImage(wellImage);
// release the saved map
if (savedMap.data)
delete[] savedMap.data;
}
// this method provides a rect for any of the six mana regions of the control
// region numbers:
// -------
// |0 1 2|
// |3 4 5|
// -------
Rect16 CManaIndicator::getManaRegionRect(int8 nRegion) {
assert(nRegion >= 0 && nRegion < numManaRegions);
int boxSizeX = xSize / 3;
int boxSizeY = ySize / 2;
static Rect16 manaRegionRects[numManaRegions] = {
Rect16(x, y, boxSizeX, boxSizeY),
Rect16(x + boxSizeX, y, boxSizeX, boxSizeY),
Rect16(x + boxSizeX * 2, y, boxSizeX, boxSizeY),
Rect16(x, y + boxSizeY, boxSizeX, boxSizeY),
Rect16(x + boxSizeX, y + boxSizeY, boxSizeX, boxSizeY),
Rect16(x + boxSizeX * 2, y + boxSizeY, boxSizeX, boxSizeY)
};
return manaRegionRects[nRegion];
}
void CManaIndicator::draw() {
gPort &port = window.windowPort;
// save pen color, etc.
SAVE_GPORT_STATE(port);
// setup the port
port.setMode(drawModeMatte);
g_vm->_pointer->hide(port, _extent); // hide mouse pointer
drawClipped(port, Point16(0, 0), Rect16(0, 0, xSize, ySize));
g_vm->_pointer->show(port, _extent); // show mouse pointer
}
void CManaIndicator::drawClipped(gPort &port,
const Point16 &offset,
const Rect16 &clipRect) {
bool calcDraw;
// Do an update to the mana star info if needed,
// if not, do not draw stuff
calcDraw = update(g_vm->_playerList[getCenterActorPlayerID()]);
if (!calcDraw) {
if (!_extent.overlap(clipRect)) return;
// draw the saved image to the port
port.setMode(drawModeMatte);
port.bltPixels(savedMap, 0, 0,
_extent.x - offset.x, _extent.y - offset.y,
xSize, ySize);
// draw the frame
drawCompressedImage(port, Point16(_extent.x - offset.x, _extent.y - offset.y), backImage);
// and finish
return;
}
// otherwise continue with the update
g_vm->_pointer->hide();
// create a temporary gPort to blit stuff to
gPort tempPort;
gPixelMap ringMap, starMap, mixMap, tempMap;
if (!NewTempPort(tempPort, xSize, ySize))
return;
// set the blit surface to a flat black
memset(tempPort.map->data, 24, tempPort.map->bytes());
// draw the well
drawCompressedImage(tempPort, Point16(wellX, wellY), wellImage);
// make a mixing plane and blank it
mixMap.size = Extent16(xSize, ySize);
mixMap.data = new uint8[mixMap.bytes()]();
// make a temp plane and blank it
tempMap.size = Extent16(xSize, ySize);
tempMap.data = new uint8[tempMap.bytes()]();
// clear out the blit surfaces
memset(mixMap.data, 0, mixMap.bytes());
memset(tempMap.data, 0, tempMap.bytes());
// draw as glyph
tempPort.setMode(drawModeMatte);
// draw each star and ring with color remap
for (uint16 i = 0; i < numManaTypes; i++) {
// get the header for the image pointer passed
ImageHeader *starHdr = (ImageHeader *)starImages[manaLines[i].starImageIndex];
ImageHeader *ringHdr = (ImageHeader *)ringImages[manaLines[i].ringImageIndex];
// set the buffer blit area to the image size
starMap.size = starHdr->size;
ringMap.size = ringHdr->size;
// see if it's compressed
if (starHdr->compress) {
// allocation of the temp buffer
starMap.data = new uint8[starMap.bytes()]();
// if it is then upack it to spec'ed coords.
unpackImage(&starMap, starMap.size.x, starMap.size.y, starHdr->data);
} else starMap.data = (uint8 *)starHdr->data;
// see if it's compressed
if (ringHdr->compress) {
// allocation of the temp buffer
ringMap.data = new uint8[ringMap.bytes()]();
// if it is then upack it to spec'ed coords.
unpackImage(&ringMap, ringMap.size.x, ringMap.size.y, ringHdr->data);
} else ringMap.data = (uint8 *)ringHdr->data;
// now blit the rings to the mixing surface
TBlit(&mixMap, &ringMap, manaLines[i].ringPos.x, manaLines[i].ringPos.y);
TBlit(&tempMap, &starMap, manaLines[i].starPos.x, manaLines[i].starPos.y);
// now do a peusdo-log additive thing to the images
uint8 *dst = (uint8 *)mixMap.data;
uint8 *src = (uint8 *)tempMap.data;
// get the least common dinominator for size ( should be equal )
uint16 bufferSize = MIN(mixMap.bytes(), tempMap.bytes());
for (uint16 j = 0; j < bufferSize; j++) {
// image bug fix/kludge
if (dst[j] > 21) dst[j] = 10;
if (src[j] > 21) src[j] = 10;
// if the tempMap pixel is greater then zero
if (src[j] != 0 && src[j] > dst[j]) {
dst[j] += src[j] - dst[j];
}
}
// for each color index possible, match correct color value
// at dest buffer
compositePixels(
tempPort.map,
&mixMap,
0,
0,
manaColorMap[i]);
// clear out the mixing surfaces
memset(mixMap.data, 0, mixMap.bytes());
memset(tempMap.data, 0, tempMap.bytes());
// dispose the temporary gPixelMap
if (starHdr->compress)
delete[] starMap.data;
if (ringHdr->compress)
delete[] ringMap.data;
}
// save this frame
TBlit(&savedMap, tempPort.map, 0, 0);
// Blit the pixelmap to the main screen
port.setMode(drawModeMatte);
port.bltPixels(*tempPort.map, 0, 0,
_extent.x - offset.x, _extent.y - offset.y,
xSize, ySize);
// now blit the frame on top of it all.
drawCompressedImage(port, Point16(_extent.x - offset.x, _extent.y - offset.y), backImage);
// dispose of temporary pixelmap
DisposeTempPort(tempPort);
if (mixMap.data)
delete[] mixMap.data;
if (tempMap.data)
delete[] tempMap.data;
g_vm->_pointer->show();
}
bool CManaIndicator::needUpdate(PlayerActor *player) {
assert(player);
// get the ability scores for this character
ActorAttributes *stats = player->getEffStats();
ActorAttributes baseStatsRef = player->getBaseStats();
int16 manaAmount;
int16 baseManaAmount;
uint16 i;
// this could do more array checking, but
// the number of mana type should remain stable
for (i = 0; i < numManaTypes; i++) {
manaAmount = stats->mana(i);
baseManaAmount = baseStatsRef.mana(i);
// check for new data
if (manaAmount != currentMana[i] || baseManaAmount != currentBaseMana[i]) {
return true;
}
}
return false;
}
bool CManaIndicator::update(PlayerActor *player) {
assert(player);
// get the ability scores for this character
ActorAttributes *stats = player->getEffStats();
ActorAttributes baseStatsRef = player->getBaseStats();
int16 manaAmount;
int16 baseManaAmount;
uint16 i;
bool newData = false;
// this could do more array checking, but
// the number of mana type should remain stable
for (i = 0; i < numManaTypes; i++) {
manaAmount = stats->mana(i);
baseManaAmount = baseStatsRef.mana(i);
// check for new data
if (manaAmount != currentMana[i] || baseManaAmount != currentBaseMana[i]) {
newData = true;
currentMana[i] = manaAmount;
currentBaseMana[i] = baseManaAmount;
}
// get manaLine info ( which star/ring image, and position on screen )
// from getStarInfo which takes the mana type index ( i ),
// current mana total, and the player base mana
if (newData == true) {
getManaLineInfo(i, manaAmount, baseManaAmount, &manaLines[i]);
}
}
// return the state of data change
if (newData == false) {
return false;
} else {
return true;
}
}
// A generalized interpolation template
template<class T> inline T LERP(T p1, T p2, int32 steps, int32 stepNum) {
return p1 + (((p2 - p1) * stepNum) / steps);
}
void CManaIndicator::getManaLineInfo(uint16 index,
int16 manaAmount,
int16 baseManaAmount,
manaLineInfo *info) {
Point16 basePos = Point16(xSize / 2, ySize / 2);
// div zero prevention
if (manaAmount == 0) manaAmount = 1;
if (baseManaAmount == 0) baseManaAmount = 1;
manaLineInfo manaInfo;
// Calculate the positions of the mana stars, and which images to use.
manaInfo.starPos = LERP(basePos,
starRingEndPos[index],
(int32)maxLevel,
(int32)manaAmount);
manaInfo.ringPos = LERP(basePos,
starRingEndPos[index],
(int32)maxLevel,
(int32)baseManaAmount);
manaInfo.starImageIndex = clamp(0, manaAmount * numStars / maxLevel, numStars - 1);
manaInfo.ringImageIndex = clamp(0, baseManaAmount * numStars / maxLevel, numRings - 1);
// now do centering correct for images
manaInfo.starPos.x -= starSizes[manaInfo.starImageIndex].x / 2;
manaInfo.starPos.y -= starSizes[manaInfo.starImageIndex].y / 2;
manaInfo.ringPos.x -= ringSizes[manaInfo.ringImageIndex].x / 2;
manaInfo.ringPos.y -= ringSizes[manaInfo.ringImageIndex].y / 2;
// return the manaLineInfo struct info about mana star ring
*info = manaInfo;
}
/* ===================================================================== *
CHealthIndicator: Health star indicator
* ===================================================================== */
CHealthIndicator::CHealthIndicator(AppFunc *cmd) {
uint16 i;
// init the resource handle with the image group context
healthRes = resFile->newContext(imageGroupID, "health imagery context");
// load in health star imagery
starImag = loadButtonRes(healthRes, starStart, starNum, 'S', 'T', 'A');
// load in the health star border
starFrameImag = g_vm->_imageCache->requestImage(healthRes, MKTAG('B', 'T', 'N', starFrameResNum));
// set the image indexes to nominal startup values
for (i = 0; i < numControls + 1; i++) {
imageIndexMemory[i] = -1;
}
// setup the id's for each of the stars
starIDs[0] = uiJulian;
starIDs[1] = uiPhillip;
starIDs[2] = uiKevin;
// health controls for the trio view
// deallocated with panel
for (i = 0; i < numControls; i++) {
starBtns[i] = new GfxCompImage(*trioControls,
Rect16(starXPos,
starYPos + starYOffset * i,
starXSize,
starYSize),
starImag,
starNum,
starInitial,
starIDs[i],
cmd);
// image control for the star border/frame trio mode
new GfxCompImage(*trioControls,
Rect16(frameXPos,
frameYPos + starYOffset * i,
frameXSize,
frameYSize),
starFrameImag,
0,
nullptr);
}
// health control for individual mode
// deallocated with panel
indivStarBtn = new GfxCompImage(*indivControls,
Rect16(starXPos,
starYPos,
starXSize,
starYSize),
starImag,
starNum,
starInitial,
uiIndiv,
cmd);
// image control for the star border/frame indiv mode
new GfxCompImage(*indivControls,
Rect16(frameXPos,
frameYPos,
frameXSize,
frameYSize),
starFrameImag,
0,
nullptr);
// release resource context
if (healthRes) {
resFile->disposeContext(healthRes);
healthRes = nullptr;
}
}
CHealthIndicator::~CHealthIndicator() {
// release star imagery
unloadImageRes(starImag, starNum);
// release star frame imagery
g_vm->_imageCache->releaseImage(starFrameImag);
}
// Recalculate and update the health star for a particular brother
void CHealthIndicator::updateStar(GfxCompImage *starCtl, int32 bro, int32 baseVitality, int32 curVitality) {
assert(baseVitality >= 0);
int16 maxStar, imageIndex;
// prevent div zero
if (baseVitality == 0) baseVitality = 1;
maxStar = clamp(0, baseVitality / 10 + 14, starLevels - 1);
// imageIndex = (int16)( sqrt( sqrt( (double)curVitality ) ) * maxStar) / sqrt( sqrt( (double)baseVitality ) );
imageIndex = (int16)(sqrt((double)MAX((int32)0, curVitality)) * maxStar) / sqrt((double)baseVitality);
// prevent needless draws
if (imageIndexMemory[bro] != imageIndex) {
starCtl->setCurrent(imageIndex);
starCtl->invalidate();
imageIndexMemory[bro] = imageIndex;
}
}
void CHealthIndicator::update() {
if (g_vm->_indivControlsFlag) {
// get the stats for the selected brother
int16 baseVitality = g_vm->_playerList[translatePanID(uiIndiv)]->getBaseStats().vitality;
int16 currVitality = g_vm->_playerList[translatePanID(uiIndiv)]->getEffStats()->vitality;
updateStar(indivStarBtn, uiIndiv, baseVitality, currVitality);
} else {
for (uint16 i = 0; i < numControls; i++) {
// get the stats for the selected brother
int16 baseVitality = g_vm->_playerList[i]->getBaseStats().vitality;
int16 currVitality = g_vm->_playerList[i]->getEffStats()->vitality;
updateStar(starBtns[i], i, baseVitality, currVitality);
}
}
}
/* ===================================================================== *
Plaq style text writing function
* ===================================================================== */
void writePlaqText(gPort &port,
const Rect16 &r,
gFont *font,
int16 textPos,
textPallete &pal,
bool hiLite,
const char *msg, ...) {
char lineBuf[128];
va_list argptr;
Rect16 workRect;
int16 cnt;
gFont *oldFont = port.font;
va_start(argptr, msg);
cnt = vsprintf(lineBuf, msg, argptr);
va_end(argptr);
SAVE_GPORT_STATE(port);
port.setMode(drawModeMatte);
port.setFont(font);
workRect = r;
port.setColor(hiLite ? pal.dlHilitePen : pal.dlPen);
workRect.x--;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
workRect.y++;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
workRect.x++;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
port.setColor(hiLite ? pal.urHilitePen : pal.urPen);
workRect.x++;
workRect.y--;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
workRect.y--;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
workRect.x--;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
port.setColor(hiLite ? pal.inHilitePen : pal.inPen);
workRect.y++;
port.drawTextInBox(lineBuf, cnt, workRect, textPos, Point16(0, 0));
port.setFont(oldFont);
}
void writePlaqTextPos(gPort &port,
const Point16 &pos,
gFont *font,
int16 /*textPos*/,
textPallete &pal,
bool hiLite,
const char *msg, ...) {
char lineBuf[128];
va_list argptr;
Point16 drawPos;
gFont *oldFont = port.font;
va_start(argptr, msg);
vsprintf(lineBuf, msg, argptr);
va_end(argptr);
SAVE_GPORT_STATE(port);
port.setMode(drawModeMatte);
port.setFont(font);
drawPos = pos;
port.setColor(hiLite ? pal.dlHilitePen : pal.dlPen);
drawPos.x--;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
drawPos.y++;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
drawPos.x++;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
port.setColor(hiLite ? pal.urHilitePen : pal.urPen);
drawPos.x++;
drawPos.y--;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
drawPos.y--;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
drawPos.x--;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
port.setColor(hiLite ? pal.inHilitePen : pal.inPen);
drawPos.y++;
port.moveTo(drawPos);
port.drawText(lineBuf, -1);
port.setFont(oldFont);
}
///////////////////////////////////////////////////////////////////
/* functions for loading and unloading sets of compressed images */
// creates an array of button images
// passed a resource context, resource ref ID and the number of images
// to sequentially load in
void **loadButtonRes(hResContext *con, int16 resID, int16 numRes) {
int16 i, k;
void **images = (void **)malloc(sizeof(void *)*numRes);
for (i = 0, k = resID; i < numRes; i++, k++) {
// get an image from the image cache
images[i] = g_vm->_imageCache->requestImage(con, MKTAG('B', 'T', 'N', k));
}
return images;
}
// creates an array of images
// passed a resource context, resource ref ID, the number of images
// to sequentially load in, and the context id's
void **loadButtonRes(hResContext *con, int16 resID, int16 numRes, char a, char b, char c) {
int16 i, k;
void **images = (void **)malloc(sizeof(void *)*numRes);
for (i = 0, k = resID; i < numRes; i++, k++) {
images[i] = g_vm->_imageCache->requestImage(con, MKTAG(a, b, c, k));
}
return images;
}
// see LoadButtonRes
void **loadImageRes(hResContext *con, int16 resID, int16 numRes, char a, char b, char c) {
return loadButtonRes(con, resID, numRes, a, b, c);
}
// correctly deletes any image arrays allocated with the LoadButtonRes or
// LoadImageRes sets
void unloadImageRes(void **images, int16 numRes) {
int16 i;
if (images) {
for (i = 0; i < numRes; i++) {
g_vm->_imageCache->releaseImage(images[i]);
}
free(images);
}
}
// defined for setup off all button based user controls
void SetupUserControls() {
if (g_vm->getGameId() == GID_DINO) {
warning("TODO: SetupUserControls() for Dino");
return;
}
// index variables
uint16 n;
uint8 index = 0;
// resource handle
hResContext *imageRes;
// brother panel id's
uint16 brotherIDs[kNumViews] = { uiJulian, uiPhillip, uiKevin };
// portrait resource indexes
int16 portResNum[] = { 0, 0, 0 };
// image setup
// init the resource handle with the image group context
imageRes = resFile->newContext(imageGroupID, "image context");
// set up the control button images
aggressImag = loadButtonRes(imageRes, aggressResNum, numBtnImages);
centerImag = loadButtonRes(imageRes, centerResNum, numBtnImages);
bandingImag = loadButtonRes(imageRes, bandingResNum, numBtnImages);
menConBtnImag = loadButtonRes(imageRes, menConBtnResNum, numBtnImages);
// setup the options button imagery
optBtnImag = loadButtonRes(imageRes, optBtnResNum, numBtnImages);
// setup the brother selector button imagery and button frame
julBtnImag = loadButtonRes(imageRes, julBtnResNum, numBtnImages);
phiBtnImag = loadButtonRes(imageRes, phiBtnResNum, numBtnImages);
kevBtnImag = loadButtonRes(imageRes, kevBtnResNum, numBtnImages);
broBtnFrameImag = g_vm->_imageCache->requestImage(imageRes, MKTAG('F', 'R', 'A', 'M'));
// set up the portrait name plates
for (n = 0; n < kNumViews; n++) {
namePlateImages[n] = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', namePlateResNum[n]));
}
// get the frame image
namePlateFrameImag = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', 15));
armorImag = g_vm->_imageCache->requestImage(imageRes, MKTAG('B', 'T', 'N', 34));
// clean out the old context
if (imageRes) resFile->disposeContext(imageRes);
imageRes = nullptr;
// init the resource handle with the face group context
imageRes = resFile->newContext(faceGroupID, "face resources");
// set up the portrait button images
for (n = 0; n < kNumViews; n++) {
portImag[n] = loadButtonRes(imageRes, portResNum[n], numPortImages, broNames[n].a, broNames[n].b, broNames[n].c);
}
// setup stand alone controls
optBtn = new GfxCompButton(*playControls, optBtnRect, optBtnImag, numBtnImages, 0, cmdOptions);
enchDisp = new gEnchantmentDisplay(*playControls, 0);
// setup the trio user cntl buttons
for (n = 0; n < kNumViews; n++) {
// portrait button
portBtns[n] = new GfxMultCompButton(*trioControls, views[n][index++],
portImag[n], numPortImages, 0, false, brotherIDs[n], cmdPortrait);
portBtns[n]->setMousePoll(true);
// aggressive button
aggressBtns[n] = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
aggressImag, numBtnImages, brotherIDs[n], cmdAggressive);
// name plates that go under the portraits
armorInd[n] = new gArmorIndicator(*trioControls, views[n][index++],
armorImag, brotherIDs[n], cmdArmor);
// center on brother
centerBtns[n] = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
centerImag, numBtnImages, brotherIDs[n], cmdCenter);
// banding
bandingBtns[n] = new GfxOwnerSelCompButton(*trioControls, views[n][index++],
bandingImag, numBtnImages, brotherIDs[n], cmdBand);
// name plates that go under the portraits
namePlates[n] = new GfxCompImage(*trioControls, views[n][index++],
namePlateImages[n], 0, nullptr);
// the frames for the name plates
namePlateFrames[n] = new GfxCompImage(*trioControls, views[n][index++],
namePlateFrameImag, 0, nullptr);
index = 0;
}
// individual control buttons
// portrait button
indivPortBtn = new GfxMultCompButton(*indivControls, views[0][index++],
portImag[0], numPortImages, 0, false, uiIndiv, cmdPortrait);
indivPortBtn->setMousePoll(true);
// aggressive button
indivAggressBtn = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
aggressImag, numBtnImages, uiIndiv, cmdAggressive);
indivArmorInd = new gArmorIndicator(*indivControls, views[0][index++],
armorImag, uiIndiv, cmdArmor);
// center on brother
indivCenterBtn = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
centerImag, numBtnImages, uiIndiv, cmdCenter);
// banding
indivBandingBtn = new GfxOwnerSelCompButton(*indivControls, views[0][index++],
bandingImag, numBtnImages, uiIndiv, cmdBand);
// name plates that go under the portraits
indivNamePlate = new GfxCompImage(*indivControls, views[0][index++],
namePlateImages[0], 0, nullptr);
// the frames for the name plates
indivNamePlateFrame = new GfxCompImage(*indivControls, views[0][index++],
namePlateFrameImag, 0, nullptr);
// setup the portrait object
Portrait = new CPortrait(portBtns, // portrait buttons
indivPortBtn,
numPortImages,// num of images per button
kNumViews); // number of brothers
// mental container button
menConBtn = new GfxCompButton(*indivControls, menConBtnRect, menConBtnImag, numBtnImages, uiIndiv, cmdBrain);
// brother selection buttons >>> need to replace these with sticky buttons
julBtn = new GfxOwnerSelCompButton(*indivControls, julBtnRect, julBtnImag, numBtnImages, uiJulian, cmdBroChange);
phiBtn = new GfxOwnerSelCompButton(*indivControls, phiBtnRect, phiBtnImag, numBtnImages, uiPhillip, cmdBroChange);
kevBtn = new GfxOwnerSelCompButton(*indivControls, kevBtnRect, kevBtnImag, numBtnImages, uiKevin, cmdBroChange);
// frame for brother buttons
broBtnFrame = new GfxCompImage(*indivControls, broBtnRect, broBtnFrameImag, uiIndiv, nullptr);
// make the mana indicator
ManaIndicator = new CManaIndicator(*indivControls);
ManaIndicator->setMousePoll(true);
// get rid of the resource contexts
if (imageRes) {
resFile->disposeContext(imageRes);
imageRes = nullptr;
}
//The controls need to be enabled but undrawn at startup
//if ( displayEnabled() )
// g_vm->_userControlsSetup = true;
updateAllUserControls();
}
void enableUserControls() {
g_vm->_userControlsSetup = true;
}
void disableUserControls() {
g_vm->_userControlsSetup = false;
}
// defines the cleanup for ALL user interface controls
void CleanupUserControls() {
g_vm->_userControlsSetup = false;
CleanupButtonImages();
}
// defines the cleaup for the global button image array
void CleanupButtonImages() {
int16 i;
// deallocates the images in the array and the arrays themselves
unloadImageRes(aggressImag, numBtnImages);
// unloadImageRes( jumpImag , numBtnImages );
unloadImageRes(centerImag, numBtnImages);
unloadImageRes(bandingImag, numBtnImages);
unloadImageRes(menConBtnImag, numBtnImages);
// dealloc the imag for the option button
unloadImageRes(optBtnImag, numBtnImages);
// dealloc brother's indiv mode buttons
unloadImageRes(julBtnImag, numBtnImages);
unloadImageRes(phiBtnImag, numBtnImages);
unloadImageRes(kevBtnImag, numBtnImages);
// portraits
for (i = 0; i < kNumViews; i++) {
unloadImageRes(portImag[i], numPortImages);
}
// name plate frame
g_vm->_imageCache->releaseImage(namePlateFrameImag);
g_vm->_imageCache->releaseImage(armorImag);
// release name frames
g_vm->_imageCache->releaseImage(broBtnFrameImag);
// name plates
for (i = 0; i < kNumViews; i++) {
g_vm->_imageCache->releaseImage(namePlateImages[i]);
}
}
void updateIndicators() {
HealthIndicator->update();
MassWeightIndicator->update();
// mana indicator update check
if (isIndivMode()) {
if (ManaIndicator->needUpdate(g_vm->_playerList[getCenterActorPlayerID()])) {
// redraw the region that is not covered by any other window
ManaIndicator->invalidate();
}
}
}
//>>>
/* ===================================================================== *
These should become methods of the playerActor class
* ===================================================================== */
template< class T >
inline T GetRatio(T curUnits, T maxUnits, T ratio) {
uint16 delt;
if (maxUnits < ratio) {
delt = ratio / maxUnits;
curUnits *= delt;
} else {
delt = maxUnits / ratio;
curUnits /= delt;
}
return clamp(0, curUnits, ratio);
}
uint16 getWeightRatio(GameObject *obj, uint16 &maxRatio, bool bReturnMaxRatio = true) {
assert(isObject(obj) || isActor(obj));
uint16 weight;
uint16 maxWeight;
// get the mass capacity for this container
maxWeight = obj->massCapacity();;
// get the total mass this container is holding
weight = obj->totalContainedMass();
if (bReturnMaxRatio) {
maxRatio = maxWeight;
return weight;
} else {
return maxWeight != unlimitedCapacity
? GetRatio(weight, maxWeight, maxRatio)
: 0;
}
}
uint16 getBulkRatio(GameObject *obj, uint16 &maxRatio, bool bReturnMaxRatio = true) {
assert(isObject(obj) || isActor(obj));
uint16 maxBulk;
uint16 bulk;
// get the bulk capacity for this container
maxBulk = obj->bulkCapacity();
// get the total bulk this container is holding
bulk = obj->totalContainedBulk();
if (bReturnMaxRatio) {
maxRatio = maxBulk;
return bulk;
} else {
return maxBulk != unlimitedCapacity
? GetRatio(bulk, maxBulk, maxRatio)
: 0;
}
}
/* ===================================================================== *
Application functions
* ===================================================================== */
void updateReadyContainers() {
// if in individual mode
if (g_vm->_indivControlsFlag) {
indivCviewTop->invalidate();
indivCviewBot->invalidate();
} else if (TrioCviews[getCenterActorPlayerID()]) {
TrioCviews[getCenterActorPlayerID()]->invalidate();
}
}
void setEnchantmentDisplay() {
if (enchDisp) enchDisp->setValue(getCenterActorPlayerID());
}
// sets the individual brother control state buttons
void setIndivBtns(uint16 brotherID) { // top = 0, mid = 1, bot = 2
g_vm->_indivControlsFlag = true;
// set the indiv bro
indivBrother = brotherID;
// set all the individual brother buttons to the correct states
indivCenterBtn->select(centerBtns[brotherID]->isSelected());
indivCenterBtn->ghost(centerBtns[brotherID]->isGhosted());
//indivStarBtn->setCurrent( ( uint16 )starBtns[brotherID]->getCurrent() );
indivPortBtn->setImages(portImag[brotherID]);
indivNamePlate->setImage(namePlateImages[brotherID]);
Portrait->set(uiIndiv, Portrait->getCurrentState(brotherID));
indivBandingBtn->select(bandingBtns[brotherID]->isSelected());
indivBandingBtn->ghost(bandingBtns[brotherID]->isGhosted());
indivAggressBtn->select(aggressBtns[brotherID]->isSelected());
indivAggressBtn->ghost(aggressBtns[brotherID]->isGhosted());
indivArmorInd->setValue(brotherID);
indivArmorInd->ghost(armorInd[brotherID]->isGhosted());
setEnchantmentDisplay();
// point the read containers to the correct brother
if (brotherID >= kPlayerActors)
brotherID = kPlayerActors - 1;
indivCviewTop->setContainer(GameObject::objectAddress(ActorBaseID + brotherID));
indivCviewTop->ghost(TrioCviews[brotherID]->isGhosted());
indivCviewBot->setContainer(GameObject::objectAddress(ActorBaseID + brotherID));
indivCviewBot->ghost(TrioCviews[brotherID]->isGhosted());
// now set the indicators for mass and bulk
uint16 pieWeightRatio = MassWeightIndicator->getMassPieDiv();
uint16 pieBulkRatio = MassWeightIndicator->getBulkPieDiv();
PlayerActor *brother = g_vm->_playerList[brotherID];
MassWeightIndicator->setMassPie(getWeightRatio(brother->getActor(), pieWeightRatio, false));
MassWeightIndicator->setBulkPie(getBulkRatio(brother->getActor(), pieBulkRatio, false));
}
// sets the trio brothers control state buttons
void setTrioBtns() {
g_vm->_indivControlsFlag = false;
// reset any value that might have changed in idividual mode
centerBtns[indivBrother]->select(indivCenterBtn->isSelected());
bandingBtns[indivBrother]->select(indivBandingBtn->isSelected());
Portrait->set(indivBrother, Portrait->getCurrentState(uiIndiv));
aggressBtns[indivBrother]->select(indivAggressBtn->isSelected());
armorInd[indivBrother]->setValue(indivBrother);
setEnchantmentDisplay();
}
void setControlPanelsToIndividualMode(uint16 brotherID) {
// copy the button/indicator states to the indiv buttons
setIndivBtns(brotherID);
// set the mode controls
trioControls->show(false, false);
indivControls->show(true, true);
trioControls->show(false, true);
}
void setControlPanelsToTrioMode() {
setTrioBtns();
indivControls->show(false, false);
trioControls->show(true, true);
indivControls->show(false, true);
}
void toggleIndivMode() {
if (g_vm->_indivControlsFlag) setControlPanelsToTrioMode();
else setControlPanelsToIndividualMode(getCenterActorPlayerID());
}
void setCenterBrother(uint16 whichBrother) {
// If we picked up anything, then put it back.
g_vm->_mouseInfo->replaceObject();
// set the new center actor
setCenterActor(g_vm->_playerList[whichBrother]);
}
uint16 translatePanID(uint16 panID) {
// individual mode brother id translation
if (panID == uiIndiv) {
panID = indivBrother;
}
return panID;
}
void updateBrotherPortrait(uint16 brotherID, int16 pType) {
if (g_vm->_userControlsSetup) {
Portrait->set(brotherID, (PortraitType)pType);
if (brotherID == indivBrother)
Portrait->set(uiIndiv, (PortraitType)pType);
}
}
void updateBrotherAggressionButton(uint16 brotherID, bool aggressive) {
if (g_vm->_userControlsSetup) {
aggressBtns[brotherID]->select(aggressive);
aggressBtns[brotherID]->ghost(isBrotherDead(brotherID));
if (brotherID == indivBrother) {
indivAggressBtn->select(aggressive);
indivAggressBtn->ghost(isBrotherDead(brotherID));
}
// possibly change portrait type
recalcPortraitType(brotherID);
}
}
void updateBrotherBandingButton(uint16 brotherID, bool banded) {
if (g_vm->_userControlsSetup) {
bandingBtns[brotherID]->select(banded);
bandingBtns[brotherID]->ghost(isBrotherDead(brotherID));
if (brotherID == indivBrother) {
indivBandingBtn->select(banded);
indivBandingBtn->ghost(isBrotherDead(brotherID));
}
}
}
void updateBrotherRadioButtons(uint16 brotherID) {
if (g_vm->_userControlsSetup) {
bool jul = (uiJulian == brotherID);
bool phi = (uiPhillip == brotherID);
bool kev = (uiKevin == brotherID);
// set the selection buttons to the correct states
julBtn->select(jul);
phiBtn->select(phi);
kevBtn->select(kev);
julBtn->ghost(isBrotherDead(uiJulian));
phiBtn->ghost(isBrotherDead(uiPhillip));
kevBtn->ghost(isBrotherDead(uiKevin));
// set the center brother buttons
centerBtns[uiJulian]->select(jul);
centerBtns[uiPhillip]->select(phi);
centerBtns[uiKevin]->select(kev);
centerBtns[uiJulian]->ghost(isBrotherDead(uiJulian));
centerBtns[uiPhillip]->ghost(isBrotherDead(uiPhillip));
centerBtns[uiKevin]->ghost(isBrotherDead(uiKevin));
if (brotherID == indivBrother) {
indivCenterBtn->select(true);
indivCenterBtn->ghost(isBrotherDead(brotherID));
}
if (g_vm->_indivControlsFlag)
setControlPanelsToIndividualMode(brotherID);
}
}
void updateBrotherArmor(uint16 brotherID) {
if (g_vm->_userControlsSetup) {
armorInd[brotherID]->setValue(brotherID);
armorInd[brotherID]->ghost(isBrotherDead(brotherID));
if (brotherID == indivBrother) {
indivArmorInd->setValue(brotherID);
indivArmorInd->ghost(isBrotherDead(brotherID));
}
}
}
void updateAllUserControls() {
if (displayEnabled()) {
if (g_vm->_userControlsSetup && g_vm->getGameId() == GID_FTA2) {
uint16 centerBrotherID = getCenterActorPlayerID(),
brotherID;
if (g_vm->_indivControlsFlag)
setControlPanelsToIndividualMode(indivBrother);
else
setControlPanelsToTrioMode();
updateBrotherRadioButtons(centerBrotherID);
for (brotherID = 0; brotherID < kNumViews; brotherID++) {
bool dead = isBrotherDead(brotherID);
updateBrotherBandingButton(brotherID, isBanded(brotherID));
updateBrotherAggressionButton(brotherID, isAggressive(brotherID));
updateBrotherPortrait(brotherID, getPortraitType(brotherID));
updateBrotherArmor(brotherID);
// if in individual mode, ghost containers if he's dead
if (brotherID == indivBrother) {
indivCviewTop->ghost(dead);
indivCviewBot->ghost(dead);
}
// Ghost the ready container if he's dead.
TrioCviews[brotherID]->ghost(dead);
}
}
} else {
reDrawScreen();
}
}
void updateBrotherControls(PlayerActorID brotherID) {
if (g_vm->_userControlsSetup) {
bool dead = isBrotherDead(brotherID);
updateBrotherRadioButtons(getCenterActorPlayerID());
updateBrotherBandingButton(brotherID, isBanded(brotherID));
updateBrotherAggressionButton(brotherID, isAggressive(brotherID));
updateBrotherPortrait(brotherID, getPortraitType(brotherID));
updateBrotherArmor(brotherID);
// if in individual mode, ghost containers if he's dead
if (brotherID == indivBrother) {
indivCviewTop->ghost(dead);
indivCviewBot->ghost(dead);
}
// Ghost the ready container if he's dead.
TrioCviews[brotherID]->ghost(dead);
}
}
// button call backs
APPFUNC(cmdPortrait) {
const int bufSize = 80;
const int stateBufSize = 60;
uint16 panID = ev.panel->id;
GameObject *mouseObject = g_vm->_mouseInfo->getObject(); // object being dragged
switch (ev.eventType) {
case gEventNewValue:
if (mouseObject != nullptr) {
PlayerActor *pa = getPlayerActorAddress(translatePanID(panID));
Actor *centerActorPtr = getCenterActor();
// we dropped the object onto another object
if (g_vm->_mouseInfo->getDoable()) {
int16 intent = g_vm->_mouseInfo->getIntent();
g_vm->_mouseInfo->replaceObject();
if (intent == GrabInfo::Use) {
// If we are using an intangible object (spell) then consider
// the owner of the spell to be the center actor for the rest
// of this action.
if (mouseObject->proto()->containmentSet() & ProtoObj::isIntangible) {
ObjectID possessor = mouseObject->possessor();
if (possessor != Nothing) {
centerActorPtr = (Actor *)GameObject::objectAddress(possessor);
}
}
MotionTask::useObjectOnObject(
*centerActorPtr,
*mouseObject,
*pa->getActor());
} else if (intent == GrabInfo::Drop) {
MotionTask::dropObjectOnObject(
*centerActorPtr,
*mouseObject,
*pa->getActor(),
g_vm->_mouseInfo->getMoveCount());
}
// ( ( gGenericControl * )ev.panel )->disableDblClick();
// clickActionDone = true;
} else if (g_vm->_mouseInfo->getIntent() == GrabInfo::Use) {
g_vm->_mouseInfo->replaceObject();
// clickActionDone = true;
}
} else if (panID != uiIndiv) {
if (!isBrotherDead(panID)) {
setCenterBrother(panID);
setControlPanelsToIndividualMode(panID);
}
} else if (panID == uiIndiv) {
setControlPanelsToTrioMode();
}
break;
case gEventMouseMove:
if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
g_vm->_mouseInfo->setDoable(true);
break;
}
// if (ev.value == gCompImage::enter)
{
if (mouseObject != nullptr) {
PlayerActor *pa = getPlayerActorAddress(translatePanID(panID));
Actor *targetActor = pa->getActor(),
*enactor = getCenterActor();
g_vm->_mouseInfo->setText(nullptr);
if ((enactor->getLocation() - targetActor->getLocation()).quickHDistance() > 96) {
g_vm->_mouseInfo->setDoable(false);
} else {
g_vm->_mouseInfo->setDoable(true);
}
} else {
// working buffer
char buf[bufSize];
char state[stateBufSize];
uint16 brotherID = translatePanID(panID);
Portrait->getStateString(state, stateBufSize, brotherID);
switch (brotherID) {
case uiJulian:
sprintf(buf, "%s %s", JULIAN_BROSTATE, state);
break;
case uiPhillip:
sprintf(buf, "%s %s", PHILLIP_BROSTATE, state);
break;
case uiKevin:
sprintf(buf, "%s %s", KEVIN_BROSTATE, state);
break;
}
// set the text in the cursor
g_vm->_mouseInfo->setText(buf);
}
}
break;
default:
break;
}
}
void toggleAgression(PlayerActorID bro, bool all) {
int16 wasAggressive = isAggressive(bro);
if (all) {
for (int i = 0; i < kPlayerActors; i++)
setAggression(i, !wasAggressive);
} else setAggression(bro, !wasAggressive);
}
APPFUNC(cmdAggressive) {
uint16 transBroID = translatePanID(ev.panel->id);
// check for message update stuff
// and aggression update
if (ev.eventType == gEventNewValue) {
toggleAgression(transBroID, rightButtonState());
// int16 wasAggressive = isAggressive( transBroID );
// if (rightButtonState())
// {
// for (int i = 0; i < kPlayerActors; i++)
// setAggression( i, !wasAggressive );
// }
// else setAggression( transBroID, !wasAggressive );
} else if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
// set the text in the cursor
g_vm->_mouseInfo->setText(isAggressive(transBroID)
? ON_AGRESS
: OFF_AGRESS);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
/*
APPFUNC( cmdJump )
{
if( ev.eventType == gEventNewValue )
{
jump = !jump;
if( jump )
{
setMouseImage( CloseBx1Image, 0, 0 );
}
else
{
setMouseImage( ArrowImage, 0, 0 );
}
}
}
*/
APPFUNC(cmdArmor) {
if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
gArmorIndicator *gai = (gArmorIndicator *)ev.panel;
char buf[128];
if (gai->attr.damageAbsorbtion == 0
&& gai->attr.defenseBonus == 0) {
g_vm->_mouseInfo->setText(NO_ARMOR);
} else {
sprintf(buf,
DESC_ARMOR,
gai->attr.damageAbsorbtion,
gai->attr.damageDivider,
gai->attr.defenseBonus);
// set the text in the cursor
g_vm->_mouseInfo->setText(buf);
}
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
APPFUNC(cmdCenter) {
uint16 transBroID = translatePanID(ev.panel->id);
if (ev.eventType == gEventNewValue) {
if (rightButtonState())
setCenterBrother((transBroID + 1) % kPlayerActors);
else setCenterBrother(transBroID);
}
if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
// set the text in the cursor
g_vm->_mouseInfo->setText(getCenterActorPlayerID() == transBroID
? ON_CENTER
: OFF_CENTER);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
void toggleBanding(PlayerActorID bro, bool all) {
int16 wasBanded = isBanded(bro);
if (all) {
for (int i = 0; i < kPlayerActors; i++)
setBanded(i, !wasBanded);
} else setBanded(bro, !wasBanded);
}
APPFUNC(cmdBand) {
uint16 transBroID = translatePanID(ev.panel->id);
if (ev.eventType == gEventNewValue) {
toggleBanding(transBroID, rightButtonState());
// int16 wasBanded = isBanded( transBroID );
//
// if (rightButtonState())
// {
// for (int i = 0; i < kPlayerActors; i++)
// setBanded( i, !wasBanded );
// }
// else setBanded( transBroID, !wasBanded );
} else if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
// set the text in the cursor
g_vm->_mouseInfo->setText(isBanded(transBroID)
? ON_BANDED
: OFF_BANDED);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
APPFUNC(cmdOptions) {
if (ev.eventType == gEventNewValue) {
OptionsDialog();
//openOptionsPanel();
} else if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) g_vm->_mouseInfo->setText(OPTIONS_PANEL);
else if (ev.value == GfxCompImage::leave) g_vm->_mouseInfo->setText(nullptr);
}
}
APPFUNC(cmdBroChange) {
if (ev.eventType == gEventNewValue) {
if (!isBrotherDead(ev.panel->id)) {
setCenterBrother(ev.panel->id);
// this sets up the buttons in trio mode to the correct
// state ( must be called before indiv mode switchtes )
setTrioBtns();
setControlPanelsToIndividualMode(ev.panel->id);
}
} else if (ev.eventType == gEventMouseMove) {
const int bufSize = 80;
const int stateBufSize = 60;
uint16 panID = ev.panel->id;
if (ev.value == GfxCompImage::enter) {
// working buffer
char buf[bufSize];
char state[stateBufSize];
uint16 brotherID = translatePanID(panID);
Portrait->getStateString(state, stateBufSize, brotherID);
switch (brotherID) {
case uiJulian:
sprintf(buf, "%s %s", JULIAN_BROSTATE, state);
break;
case uiPhillip:
sprintf(buf, "%s %s", PHILLIP_BROSTATE, state);
break;
case uiKevin:
sprintf(buf, "%s %s", KEVIN_BROSTATE, state);
break;
}
// set the text in the cursor
g_vm->_mouseInfo->setText(buf);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
APPFUNC(cmdHealthStar) {
uint16 transBroID = translatePanID(ev.panel->id);
if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
return;
}
if (ev.value == GfxCompImage::enter) {
ev.panel->setMousePoll(true);
}
// get the stats for the selected brother
int16 baseVitality = g_vm->_playerList[transBroID]->getBaseStats().vitality;
int16 currVitality = g_vm->_playerList[transBroID]->getEffStats()->vitality;
char buf[40];
sprintf(buf, "%s %d/%d", HEALTH_HINT, currVitality, baseVitality);
g_vm->_mouseInfo->setText(buf);
}
}
APPFUNC(cmdMassInd) {
gWindow *win = nullptr;
GameObject *containerObject = nullptr;
if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
const int bufSize = 40;
int curWeight;
uint16 baseWeight;
char buf[bufSize];
win = ev.panel->getWindow(); // get the window pointer
assert(win);
// is it something other than the brother's indicators?
if (ev.panel->id > 1) {
containerObject = (GameObject *)win->userData;
} else {
containerObject = (GameObject *)g_vm->_playerList[getCenterActorPlayerID()]->getActor();
}
assert(containerObject);
curWeight = getWeightRatio(containerObject, baseWeight);
if (baseWeight != unlimitedCapacity) {
sprintf(buf, "%s %d/%d", WEIGHT_HINT, curWeight, baseWeight);
g_vm->_mouseInfo->setText(buf);
} else
g_vm->_mouseInfo->setText(UNK_WEIGHT_HINT);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
APPFUNC(cmdBulkInd) {
gWindow *win = nullptr;
GameObject *containerObject = nullptr;
if (ev.eventType == gEventMouseMove) {
if (ev.value == GfxCompImage::enter) {
const int bufSize = 40;
uint16 baseBulk = 100;
char buf[bufSize];
int curBulk;
win = ev.panel->getWindow(); // get the window pointer
assert(win);
// is it something other than the brother's indicators?
if (ev.panel->id > 1) {
containerObject = (GameObject *)win->userData;
} else {
containerObject = (GameObject *)g_vm->_playerList[getCenterActorPlayerID()]->getActor();
}
assert(containerObject);
curBulk = getBulkRatio(containerObject, baseBulk);
if (baseBulk != unlimitedCapacity) {
sprintf(buf, "%s %d/%d", BULK_HINT, curBulk, baseBulk);
g_vm->_mouseInfo->setText(buf);
} else
g_vm->_mouseInfo->setText(UNK_BULK_HINT);
} else if (ev.value == GfxCompImage::leave) {
g_vm->_mouseInfo->setText(nullptr);
}
}
}
APPFUNC(cmdManaInd) {
if (ev.eventType == gEventMouseMove) {
if (ev.value != GfxCompImage::leave) {
const int BUF_SIZE = 64;
char textBuffer[BUF_SIZE];
int manaType = -1;
int numManaRegions = ManaIndicator->getNumManaRegions();
int i;
int curMana = 0, baseMana = 0;
PlayerActor *player = g_vm->_playerList[getCenterActorPlayerID()];
ActorAttributes *stats = player->getEffStats();
ActorAttributes baseStatsRef = player->getBaseStats();
Point16 pos = ev.mouse;
pos.x += ManaIndicator->getExtent().x;
pos.y += ManaIndicator->getExtent().y;
for (i = 0; i < numManaRegions; i++) {
Rect16 regionRect = ManaIndicator->getManaRegionRect(i);
if (regionRect.ptInside(pos)) {
manaType = i;
break;
}
}
if (manaType != -1) {
curMana = stats->mana(clamp(0, manaType, numManaRegions));
baseMana = baseStatsRef.mana(clamp(0, manaType, numManaRegions));
}
switch (manaType) {
case 0:
sprintf(textBuffer, "%s %d/%d", "Red Mana:", curMana, baseMana);
break;
case 1:
sprintf(textBuffer, "%s %d/%d", "Orange Mana:", curMana, baseMana);
break;
case 2:
sprintf(textBuffer, "%s %d/%d", "Yellow Mana:", curMana, baseMana);
break;
case 3:
sprintf(textBuffer, "%s %d/%d", "Green Mana:", curMana, baseMana);
break;
case 4:
sprintf(textBuffer, "%s %d/%d", "Blue Mana:", curMana, baseMana);
break;
case 5:
sprintf(textBuffer, "%s %d/%d", "Purple Mana:", curMana, baseMana);
break;
case -1:
textBuffer[0] = 0;
textBuffer[1] = 0;
break;
default:
assert(false); // should never get here
break;
}
// set the text in the cursor
g_vm->_mouseInfo->setText(textBuffer);
} else
g_vm->_mouseInfo->setText(nullptr);
}
}
bool isIndivMode() {
return g_vm->_indivControlsFlag;
}
void initUIState() {
g_vm->_indivControlsFlag = false;
indivBrother = 0;
//updateAllUserControls();
}
void saveUIState(Common::OutSaveFile *outS) {
debugC(2, kDebugSaveload, "Saving UIState");
outS->write("UIST", 4);
CHUNK_BEGIN;
out->writeUint16LE(g_vm->_indivControlsFlag);
out->writeUint16LE(indivBrother);
CHUNK_END;
debugC(3, kDebugSaveload, "..._indivControlsFlag = %d", g_vm->_indivControlsFlag);
debugC(3, kDebugSaveload, "... indivBrother = %d", indivBrother);
}
void loadUIState(Common::InSaveFile *in) {
debugC(2, kDebugSaveload, "Loading UIState");
g_vm->_indivControlsFlag = in->readUint16LE();
indivBrother = in->readUint16LE();
debugC(3, kDebugSaveload, "... _indivControlsFlag = %d", g_vm->_indivControlsFlag);
debugC(3, kDebugSaveload, "... indivBrother = %d", indivBrother);
updateAllUserControls();
}
void cleanupUIState() {
if (StatusLine != nullptr)
StatusLine->clear();
}
void gArmorIndicator::setValue(PlayerActorID brotherID) {
Actor *bro = g_vm->_playerList[brotherID]->getActor();
bro->totalArmorAttributes(attr);
invalidate();
}
// getCurrentCompImage() is virtual function that should return
// the current image to be displayed (to be used across all sub-classes)
void gArmorIndicator::drawClipped(gPort &port,
const Point16 &offset,
const Rect16 &r) {
if (!_extent.overlap(r)) return;
SAVE_GPORT_STATE(port);
// get the current image
void *dispImage = getCurrentCompImage();
// make sure the image is valid
if (dispImage) {
// will part of this be drawn on screen?
if (_extent.overlap(r)) {
char buf[8];
// offset the image?
Point16 pos(_extent.x - offset.x,
_extent.y - offset.y
);
// draw the compressed image
if (isGhosted()) {
drawCompressedImageGhosted(port, pos, dispImage);
return;
} else drawCompressedImage(port, pos, dispImage);
// draw the armor numebrs
port.setFont(&Helv11Font);
port.setColor(11); // set color to white
port.setStyle(textStyleThickOutline);
port.setOutlineColor(24); // set outline color to black
port.setMode(drawModeMatte);
if (attr.damageAbsorbtion == 0 && attr.defenseBonus == 0)
sprintf(buf, "-");
else if (attr.damageDivider > 1)
sprintf(buf, "%d/%d", attr.damageAbsorbtion, attr.damageDivider);
else sprintf(buf, "%d", attr.damageAbsorbtion);
port.drawTextInBox(buf, -1, Rect16(pos.x, pos.y, _extent.width, _extent.height),
textPosRight | textPosHigh, Point16(0, 2));
if (attr.damageAbsorbtion == 0 && attr.defenseBonus == 0)
sprintf(buf, "-");
else sprintf(buf, "%d", attr.defenseBonus);
port.drawTextInBox(buf, -1, Rect16(pos.x, pos.y, _extent.width, _extent.height),
textPosRight | textPosLow, Point16(0, 2));
}
}
}
void gEnchantmentDisplay::drawClipped(gPort &port, const Point16 &offset, const Rect16 &r) {
Point16 pos(_extent.x + _extent.width - 10, _extent.y + 1);
pos += offset;
if (!_extent.overlap(r)) return;
for (int i = 0; i < iconCount; i++) {
if (iconFlags[i]) {
Sprite *sp = mentalSprites->sprite(i + 162);
pos.x -= sp->size.x + 2;
DrawSprite(port, pos, sp);
}
}
}
void gEnchantmentDisplay::pointerMove(gPanelMessage &msg) {
if (msg.pointerLeave) {
g_vm->_mouseInfo->setText(nullptr);
} else {
int16 x = _extent.width - 10;
setMousePoll(true);
setValue(getCenterActorPlayerID());
for (int i = 0; i < iconCount; i++) {
if (iconFlags[i]) {
Sprite *sp = mentalSprites->sprite(i + 162);
x -= sp->size.x + 2;
if (msg.pickPos.x >= x) {
// set the text in the cursor
char buf[128];
if (iconFlags[i] == 255)
sprintf(buf, "%s", enchantmentNames[i]);
else sprintf(buf, "%s : %d", enchantmentNames[i], iconFlags[i]);
g_vm->_mouseInfo->setText(buf);
return;
}
}
}
}
}
void gEnchantmentDisplay::setValue(PlayerActorID pID) {
Actor *a = g_vm->_playerList[pID]->getActor();
uint8 newIconFlags[iconCount];
EnchantmentIterator iter(a);
ContainerIterator cIter(a);
GameObject *obj = nullptr;
memset(newIconFlags, 0, sizeof newIconFlags);
/*
x iconHaste,
iconFirewalk,
x iconAdrenalFervor,
x iconShadowWalk,
iconSunWard,
iconSpellBarrier,
*/
for (ObjectID id1 = iter.first(&obj); id1 != Nothing; id1 = iter.next(&obj)) {
uint16 enchantmentID = obj->getExtra();
uint16 eType = getEnchantmentType(enchantmentID);
uint16 eSubType = getEnchantmentSubType(enchantmentID);
int16 eAmount = getEnchantmentAmount(enchantmentID);
uint8 duration = obj->getHitPoints(); // get hitpoints of enchant
switch (eType) {
case effectAttrib:
switch (eSubType) {
// case skillIDArchery:
// case skillIDShieldcraft:
case skillIDBludgeon:
case skillIDSwordcraft:
if (eAmount > 0) newIconFlags[iconSurestrike] = duration;
else newIconFlags[iconClumsy] = duration;
break;
case skillIDAgility:
if (eAmount > 0) newIconFlags[iconInnerBalance] = duration;
else newIconFlags[iconClumsy] = duration;
break;
case skillIDBrawn:
newIconFlags[iconBattleFever] = duration;
break;
}
break;
case effectResist:
switch (eSubType) {
case kDamageImpact:
newIconFlags[iconResistImpact] = duration;
break;
case kDamageSlash:
newIconFlags[iconResistSlash] = duration;
break;
case kDamageProjectile:
newIconFlags[iconResistProjectile] = duration;
break;
case kDamageFire:
newIconFlags[iconResistFire] = duration;
break;
case kDamageAcid:
newIconFlags[iconResistAcid] = duration;
break;
case kDamageHeat:
newIconFlags[iconResistHeat] = duration;
break;
case kDamageCold:
newIconFlags[iconResistCold] = duration;
break;
case kDamageLightning:
newIconFlags[iconResistLightning] = duration;
break;
case kDamagePoison:
newIconFlags[iconResistPoison] = duration;
break;
case kDamageMental:
newIconFlags[iconResistPsionic] = duration;
break;
case kDamageDirMagic:
newIconFlags[iconResistDirectMagic] = duration;
break;
}
break;
case effectImmune:
switch (eSubType) {
case kDamageImpact:
newIconFlags[iconIronskin] = duration;
break;
case kDamageSlash:
newIconFlags[iconIronskin] = duration;
break;
case kDamageFire:
newIconFlags[iconImmuneFire] = duration;
break;
case kDamageAcid:
newIconFlags[iconImmuneAcid] = duration;
break;
case kDamageHeat:
newIconFlags[iconImmuneHeat] = duration;
break;
case kDamageCold:
newIconFlags[iconImmuneCold] = duration;
break;
case kDamageLightning:
newIconFlags[iconImmuneLightning] = duration;
break;
case kDamagePoison:
newIconFlags[iconImmunePoison] = duration;
break;
case kDamageMental:
newIconFlags[iconImmunePsionic] = duration;
break;
}
break;
case effectOthers:
switch (eSubType) {
case actorPoisoned:
case actorDiseased:
newIconFlags[iconPoisoned] = duration;
break;
case actorFear:
newIconFlags[iconAfraid] = duration;
break;
case actorParalyzed:
newIconFlags[iconParalysed] = duration;
break; // iconFrozen ??
case actorSlowFall:
newIconFlags[iconCushionAir] = duration;
break;
case actorImmobile:
newIconFlags[iconConstrained] = duration;
break;
case actorSeeInvis:
newIconFlags[iconSoulSight] = duration;
break;
case actorInvisible:
newIconFlags[iconInvisible] = duration;
break;
case actorUndetectable:
newIconFlags[iconNumbscent] = duration;
break;
case actorDetPoison:
newIconFlags[iconDetectPoison] = duration;
break;
case actorNoDrain:
newIconFlags[iconNetherWard] = duration;
break;
case actorWaterBreathe:
newIconFlags[iconSeawalk] = duration;
break;
case actorRepelEvil:
newIconFlags[iconProtectEvil] = duration;
break;
// case actorRepelUndead: newIconFlags[iconProtectUndead] = duration; break;
// case actorRepelGhosts: newIconFlags[iconProtectGhosts] = duration; break;
}
}
}
while (cIter.next(&obj)) {
ProtoObj *proto = obj->proto();
uint16 cSet = proto->containmentSet();
if ((cSet & (ProtoObj::isArmor | ProtoObj::isWeapon | ProtoObj::isWearable))
&& proto->isObjectBeingUsed(obj)) {
if (proto->immunity & (1 << resistImpact)) newIconFlags[iconResistImpact] = 255;
else if (proto->resistance & (1 << resistImpact)) newIconFlags[iconResistImpact] = 255;
if (proto->immunity & (1 << resistSlash)) newIconFlags[iconResistSlash] = 255;
else if (proto->resistance & (1 << resistSlash)) newIconFlags[iconResistSlash] = 255;
if (proto->immunity & (1 << resistProjectile)) newIconFlags[iconResistProjectile] = 255;
else if (proto->resistance & (1 << resistProjectile)) newIconFlags[iconResistProjectile] = 255;
if (proto->immunity & (1 << immuneFire)) newIconFlags[iconImmuneFire] = 255;
else if (proto->resistance & (1 << resistFire)) newIconFlags[iconResistFire] = 255;
if (proto->immunity & (1 << immuneAcid)) newIconFlags[iconImmuneAcid] = 255;
else if (proto->resistance & (1 << resistAcid)) newIconFlags[iconResistAcid] = 255;
if (proto->immunity & (1 << immuneHeat)) newIconFlags[iconImmuneHeat] = 255;
else if (proto->resistance & (1 << resistHeat)) newIconFlags[iconResistHeat] = 255;
if (proto->immunity & (1 << immuneCold)) newIconFlags[iconImmuneCold] = 255;
else if (proto->resistance & (1 << resistCold)) newIconFlags[iconResistCold] = 255;
if (proto->immunity & (1 << immuneLightning)) newIconFlags[iconImmuneLightning] = 255;
else if (proto->resistance & (1 << resistLightning)) newIconFlags[iconResistLightning] = 255;
if (proto->immunity & (1 << immunePoison)) newIconFlags[iconImmunePoison] = 255;
else if (proto->resistance & (1 << resistPoison)) newIconFlags[iconResistPoison] = 255;
if (proto->immunity & (1 << immuneMental)) newIconFlags[iconImmunePsionic] = 255;
else if (proto->resistance & (1 << resistMental)) newIconFlags[iconResistPsionic] = 255;
if (proto->immunity & (1 << resistDirMagic)) newIconFlags[iconResistDirectMagic] = 255;
else if (proto->resistance & (1 << resistDirMagic))newIconFlags[iconResistDirectMagic] = 255;
}
}
// Compute icon flags for resistances and immunities
#if 0
enum effectOthersTypes {
// Movement flags
o actorNoncorporeal = 1, // The creature can walk through things
x actorWaterBreathe = 2, // death spell
x actorSlowFall = 3, // the creature is not harmed by falling (but falls none the less)
- actorLevitate = 4, // flying with no height control ?
- actorFlying = 5, // the creature flys
// speed flags
- actorFastMove = 6, //
- actorFastAttack = 7, //
actorSlowAttack = 8, // come... back... here... lit... tle... bun... ny...
x actorSlowMove = 9, // I thought I told you to leave the piano at home
// ill effects
- actorAsleep = 10, // Zzzzzzzzzzz
x actorParalyzed = 11, // the creature can't move an inch
x actorFear = 12, // run away! run away
x actorDiseased = 13, // cannot heal
x actorPoisoned = 14, // death spell
// perception & perceivability flags
x actorBlind = 15, // can't see
x actorSeeInvis = 16, // can see invisible
- actorClairvoyant = 17, // unknown effects
x actorInvisible = 18, // is invisible
x actorUndetectable = 19, // can't be seen, smelled
x actorDetPoison = 20, // poison things glow green
// flags preventing changes to other flags
actorNoEnchant = 21, // no bad enchantments
x actorNoDrain = 22, // no mana / food drains
// flags that make things run away
x actorRepelEvil = 23, // death spell
x actorRepelGood = 24, // death spell
x actorRepelUndead = 25, // death spell
// dead or moved flags
// actorMapping =15, //
// actorLandWalk =0 , // someone else had this I have no idea what it is
// actorFloat =2 , // the creature can travel through malts shakes & sundaes
actorWaterWalk, // can walk on water (same as float ?)
// actorPanic =13, // creature takes off randomly
// actorSpotHidden =17, // can see hidden
// actorDetTraps =22, // traps glow green
// actorFlameAura =23, // has a flaming aura
// actorDead =25, // death spell
};
#endif
// If icon flags changed, then redraw the control.
if (memcmp(iconFlags, newIconFlags, sizeof iconFlags)) {
memcpy(iconFlags, newIconFlags, sizeof iconFlags);
invalidate();
}
}
bool isBrotherDead(PlayerActorID brotherID) {
return (getPlayerActorAddress(brotherID)->getActor()->isDead());
}
void StatusMsg(const char *msg, ...) { // frametime def
va_list argptr;
char buffer[128];
if (StatusLine) {
va_start(argptr, msg);
vsprintf(buffer, msg, argptr);
va_end(argptr);
StatusLine->setLine(buffer, 500);
}
}
} // end of namespace Saga2