MM: XEEN: add hp and sp bars for QoL

This commit is contained in:
Alexander Izmailov 2023-06-09 12:00:06 +03:00 committed by Paul Gilbert
parent e3343eb810
commit a3cfd1fabd
7 changed files with 195 additions and 36 deletions

View File

@ -49,6 +49,7 @@ struct MightAndMagicGameDescription {
#define GAMEOPTION_SHOW_ITEM_COSTS GUIO_GAMEOPTIONS1
#define GAMEOPTION_DURABLE_ARMOR GUIO_GAMEOPTIONS2
#define GAMEOPTION_SHOW_HP_SP_BARS GUIO_GAMEOPTIONS3
} // namespace MM

View File

@ -19,8 +19,13 @@
*
*/
#include "engines/advancedDetector.h"
#include "engines/mm/detection.h"
namespace MM {
#define GUIO_XEEN GUIO3(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR, GAMEOPTION_SHOW_HP_SP_BARS)
static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
#ifdef ENABLE_MM1
{
@ -82,7 +87,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -98,7 +103,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -118,7 +123,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -134,7 +139,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -150,7 +155,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -167,7 +172,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_WorldOfXeen,
0
@ -182,7 +187,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Clouds,
0
@ -197,7 +202,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Clouds,
0
@ -212,7 +217,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Clouds,
0
@ -227,7 +232,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::ZH_TWN,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Clouds,
0
@ -243,7 +248,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Clouds,
0
@ -258,7 +263,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_DarkSide,
0
@ -273,7 +278,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_DarkSide,
0
@ -289,7 +294,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_DarkSide,
0
@ -304,7 +309,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::ZH_TWN,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_DarkSide,
0
@ -320,7 +325,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_DarkSide,
0
@ -335,7 +340,7 @@ static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR)
GUIO_XEEN
},
GType_Swords,
0

View File

@ -39,6 +39,43 @@
#include "mm/xeen/swordsofxeen/swordsofxeen.h"
#endif
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_SHOW_ITEM_COSTS,
{
_s("Show item costs in standard inventory mode"),
_s("Shows item costs in standard inventory mode, allowing the value of items to be compared"),
"ShowItemCosts",
false,
0,
0
}
},
{
GAMEOPTION_DURABLE_ARMOR,
{
_s("More durable armor"),
_s("Armor won't break until character is at -80HP, rather than merely -10HP"),
"DurableArmor",
false,
0,
0
}
},
{
GAMEOPTION_SHOW_HP_SP_BARS,
{
_s("Hitpoint bars"),
_s("Replace a colored gem with bars for hit points and spell points."),
"ShowHpSpBars",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class MMMetaEngine : public AdvancedMetaEngine {
private:
/**
@ -60,6 +97,10 @@ public:
SaveStateList listSaves(const char *target) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
};
bool MMMetaEngine::hasFeature(MetaEngineFeature f) const {

View File

@ -51,12 +51,124 @@ PartyDrawer::PartyDrawer(XeenEngine *vm): _vm(vm) {
_hiliteChar = HILIGHT_CHAR_NONE;
}
static inline int clipToFaceWidth(int i) {
const int faceWidth = 32;
return CLIP(i, 0, faceWidth);
}
static inline int unzero(int i) {
if (i) return i;
return 1;
}
void PartyDrawer::drawHitPoints(int charIndex) {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
bool inCombat = _vm->_mode == MODE_COMBAT;
Window &win = windows[0];
Character &c = inCombat ? *combat._combatParty[charIndex] : party._activeParty[charIndex];
enum {
BLACK = 0,
STONE = 20,
BRIGHT_GREEN = 80,
GREEN = 85,
YELLOW = 55,
RED = 185,
DARK_BLUE = 78,
BRIGHT_BLUE = 70,
BLUE = 75
};
int hp = c._currentHp;
int sp = c._currentSp;
int maxHp = c.getMaxHP();
int maxSp = c.getMaxSP();
int gemFrame;
int hpColor = GREEN;
if (hp < 1) {
gemFrame = 4;
} else if (hp > maxHp) {
gemFrame = 3;
} else if (hp == maxHp) {
gemFrame = 0;
} else if (hp < (maxHp / 4)) {
gemFrame = 2;
hpColor = RED;
} else{
hpColor = YELLOW;
gemFrame = 1;
}
if (!g_vm->_extOptions._showHpSpBars) {
_hpSprites.draw(0, gemFrame, Common::Point(Res.HP_BARS_X[charIndex], 182));
} else {
const int faceWidth = 32;
const int barLeft = Res.CHAR_FACES_X[charIndex];
const int barRight = Res.CHAR_FACES_X[charIndex] + 32;
const int barHeight = 3;
const int frameTop = 183;
const int frameBottom = 190;
const int frameColor = STONE;
const int hpBarTop = 184;
const int hpBarBottom = hpBarTop + barHeight;
const int spBarTop = 188;
const int spBarBottom = spBarTop + barHeight;
int hpPart = clipToFaceWidth( (hp * faceWidth) / unzero(maxHp) );
int boostedHpPart = 0;
if (hp > maxHp) {
boostedHpPart = clipToFaceWidth(faceWidth * (hp - maxHp) / unzero(hp + maxHp) );
}
int spPart = clipToFaceWidth( (sp * faceWidth) / unzero(maxSp) );
int boostedSpPart = 0;
if (sp > maxSp) {
boostedSpPart = clipToFaceWidth( faceWidth * (sp - maxSp) / unzero(sp + maxSp) );
}
int negativeHpPart = 32;
if (hp < 0) {
negativeHpPart = faceWidth - clipToFaceWidth((-hp * faceWidth) / unzero(maxHp));
}
win.fillRect(Common::Rect(barLeft, frameTop, barRight, frameBottom), frameColor);
win.fillRect(Common::Rect(barLeft, hpBarTop, barRight, hpBarBottom), BLACK);
win.fillRect(Common::Rect(barLeft, hpBarTop, barLeft + hpPart, hpBarBottom), hpColor);
if (boostedHpPart != 0) {
win.fillRect(Common::Rect(barLeft, hpBarTop, barLeft + boostedHpPart, hpBarBottom), BRIGHT_GREEN);
}
if (negativeHpPart != 32) {
win.fillRect(Common::Rect(barLeft + negativeHpPart, hpBarTop, barLeft + faceWidth, hpBarBottom), DARK_BLUE);
}
if (maxSp != 0) {
win.fillRect(Common::Rect(barLeft, spBarTop, barRight, spBarBottom), BLACK);
win.fillRect(Common::Rect(barLeft, spBarTop, barLeft + spPart, spBarBottom), BLUE);
if (boostedSpPart) {
win.fillRect(Common::Rect(barLeft, spBarTop, barLeft + boostedSpPart, spBarBottom), BRIGHT_BLUE);
}
}
}
}
void PartyDrawer::drawParty(bool updateFlag) {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Resources &res = *_vm->_resources;
Windows &windows = *_vm->_windows;
bool inCombat = _vm->_mode == MODE_COMBAT;
_restoreSprites.draw(0, 0, Common::Point(8, 149));
// Handle drawing the party faces
@ -75,23 +187,7 @@ void PartyDrawer::drawParty(bool updateFlag) {
}
for (uint idx = 0; idx < partyCount; ++idx) {
Character &c = inCombat ? *combat._combatParty[idx] : party._activeParty[idx];
// Draw the Hp bar
int maxHp = c.getMaxHP();
int frame;
if (c._currentHp < 1)
frame = 4;
else if (c._currentHp > maxHp)
frame = 3;
else if (c._currentHp == maxHp)
frame = 0;
else if (c._currentHp < (maxHp / 4))
frame = 2;
else
frame = 1;
_hpSprites.draw(0, frame, Common::Point(Res.HP_BARS_X[idx], 182));
drawHitPoints(idx);
}
if (_hiliteChar != HILIGHT_CHAR_NONE)
@ -533,6 +629,10 @@ void Interface::perform() {
if (CastSpell::show(_vm) != -1) {
chargeStep();
doStepCode();
// update spell point bar
if (g_vm->_extOptions._showHpSpBars) {
drawParty(true);
}
}
break;
@ -1601,6 +1701,10 @@ void Interface::doCombat() {
// Cast spell
if (CastSpell::show(_vm) != -1) {
nextChar();
// update spell point bar
if (g_vm->_extOptions._showHpSpBars) {
drawParty(true);
}
} else {
highlightChar(combat._whosTurn);
}

View File

@ -86,6 +86,9 @@ public:
void unhighlightChar();
void resetHighlight();
private:
void drawHitPoints(int charIndex);
};
/**

View File

@ -125,6 +125,7 @@ void XeenEngine::loadSettings() {
_extOptions._showItemCosts = ConfMan.hasKey("ShowItemCosts") && ConfMan.getBool("ShowItemCosts");
_extOptions._durableArmor = ConfMan.hasKey("DurableArmor") && ConfMan.getBool("DurableArmor");
_extOptions._showHpSpBars = ConfMan.hasKey("ShowHpSpBars") && ConfMan.getBool("ShowHpSpBars");
// If requested, load a savegame instead of showing the intro
if (ConfMan.hasKey("save_slot")) {

View File

@ -97,9 +97,13 @@ class XeenEngine : public MMEngine {
struct ExtendedOptions {
bool _showItemCosts;
bool _durableArmor;
bool _showHpSpBars;
ExtendedOptions() : _showItemCosts(false), _durableArmor(false) {
}
ExtendedOptions() :
_showItemCosts(false),
_durableArmor(false),
_showHpSpBars(false)
{}
};
private:
/**