413 lines
12 KiB
C++
413 lines
12 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/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/ultima4/ultima4.h"
|
|
#include "ultima/ultima4/core/config.h"
|
|
#include "ultima/ultima4/core/utils.h"
|
|
#include "ultima/ultima4/views/stats.h"
|
|
#include "ultima/ultima4/game/armor.h"
|
|
#include "ultima/ultima4/game/context.h"
|
|
#include "ultima/ultima4/views/menu.h"
|
|
#include "ultima/ultima4/game/names.h"
|
|
#include "ultima/ultima4/game/player.h"
|
|
#include "ultima/ultima4/filesys/savegame.h"
|
|
#include "ultima/ultima4/game/spell.h"
|
|
#include "ultima/ultima4/map/tile.h"
|
|
#include "ultima/ultima4/game/weapon.h"
|
|
|
|
namespace Ultima {
|
|
namespace Ultima4 {
|
|
|
|
/**
|
|
* StatsArea class implementation
|
|
*/
|
|
StatsArea::StatsArea() :
|
|
_title(STATS_AREA_X * CHAR_WIDTH, 0 * CHAR_HEIGHT, STATS_AREA_WIDTH, 1),
|
|
_mainArea(STATS_AREA_X * CHAR_WIDTH, STATS_AREA_Y * CHAR_HEIGHT, STATS_AREA_WIDTH, STATS_AREA_HEIGHT),
|
|
_summary(STATS_AREA_X * CHAR_WIDTH, (STATS_AREA_Y + STATS_AREA_HEIGHT + 1) * CHAR_HEIGHT, STATS_AREA_WIDTH, 1),
|
|
_view(STATS_PARTY_OVERVIEW) {
|
|
// Generate a formatted Common::String for each menu item,
|
|
// and then add the item to the menu. The Y value
|
|
// for each menu item will be filled in later.
|
|
for (int count = 0; count < 8; count++) {
|
|
char outputBuffer[16];
|
|
snprintf(outputBuffer, sizeof(outputBuffer), "-%-11s%%s", getReagentName((Reagent)count));
|
|
_reagentsMixMenu.add(count, new IntMenuItem(outputBuffer, 1, 0, -1, (int *)g_context->_party->getReagentPtr((Reagent)count), 0, 99, 1, MENU_OUTPUT_REAGENT));
|
|
}
|
|
|
|
_reagentsMixMenu.addObserver(this);
|
|
}
|
|
|
|
void StatsArea::setView(StatsView view) {
|
|
this->_view = view;
|
|
update();
|
|
}
|
|
|
|
void StatsArea::prevItem() {
|
|
_view = (StatsView)(_view - 1);
|
|
if (_view < STATS_CHAR1)
|
|
_view = STATS_MIXTURES;
|
|
if (_view <= STATS_CHAR8 && (_view - STATS_CHAR1 + 1) > g_context->_party->size())
|
|
_view = (StatsView)(STATS_CHAR1 - 1 + g_context->_party->size());
|
|
update();
|
|
}
|
|
|
|
void StatsArea::nextItem() {
|
|
_view = (StatsView)(_view + 1);
|
|
if (_view > STATS_MIXTURES)
|
|
_view = STATS_CHAR1;
|
|
if (_view <= STATS_CHAR8 && (_view - STATS_CHAR1 + 1) > g_context->_party->size())
|
|
_view = STATS_WEAPONS;
|
|
update();
|
|
}
|
|
|
|
void StatsArea::update(bool avatarOnly) {
|
|
clear();
|
|
|
|
/*
|
|
* update the upper stats box
|
|
*/
|
|
switch (_view) {
|
|
case STATS_PARTY_OVERVIEW:
|
|
showPartyView(avatarOnly);
|
|
break;
|
|
case STATS_CHAR1:
|
|
case STATS_CHAR2:
|
|
case STATS_CHAR3:
|
|
case STATS_CHAR4:
|
|
case STATS_CHAR5:
|
|
case STATS_CHAR6:
|
|
case STATS_CHAR7:
|
|
case STATS_CHAR8:
|
|
showPlayerDetails();
|
|
break;
|
|
case STATS_WEAPONS:
|
|
showWeapons();
|
|
break;
|
|
case STATS_ARMOR:
|
|
showArmor();
|
|
break;
|
|
case STATS_EQUIPMENT:
|
|
showEquipment();
|
|
break;
|
|
case STATS_ITEMS:
|
|
showItems();
|
|
break;
|
|
case STATS_REAGENTS:
|
|
showReagents();
|
|
break;
|
|
case STATS_MIXTURES:
|
|
showMixtures();
|
|
break;
|
|
case MIX_REAGENTS:
|
|
showReagents(true);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* update the lower stats box (food, gold, etc.)
|
|
*/
|
|
if (g_context->_transportContext == TRANSPORT_SHIP)
|
|
_summary.textAt(0, 0, "F:%04d SHP:%02d", g_ultima->_saveGame->_food / 100, g_ultima->_saveGame->_shipHull);
|
|
else
|
|
_summary.textAt(0, 0, "F:%04d G:%04d", g_ultima->_saveGame->_food / 100, g_ultima->_saveGame->_gold);
|
|
|
|
update(g_context->_aura);
|
|
|
|
redraw();
|
|
}
|
|
|
|
void StatsArea::update(Aura *observable, NoArg *arg) {
|
|
Observer<Aura *>::update(observable, arg);
|
|
}
|
|
|
|
void StatsArea::update(Aura *aura) {
|
|
byte mask = 0xff;
|
|
for (int i = 0; i < VIRT_MAX; i++) {
|
|
if (g_ultima->_saveGame->_karma[i] == 0)
|
|
mask &= ~(1 << i);
|
|
}
|
|
|
|
switch (aura->getType()) {
|
|
case Aura::NONE:
|
|
_summary.drawCharMasked(0, STATS_AREA_WIDTH / 2, 0, mask);
|
|
break;
|
|
case Aura::HORN:
|
|
_summary.drawChar(CHARSET_REDDOT, STATS_AREA_WIDTH / 2, 0);
|
|
break;
|
|
case Aura::JINX:
|
|
_summary.drawChar('J', STATS_AREA_WIDTH / 2, 0);
|
|
break;
|
|
case Aura::NEGATE:
|
|
_summary.drawChar('N', STATS_AREA_WIDTH / 2, 0);
|
|
break;
|
|
case Aura::PROTECTION:
|
|
_summary.drawChar('P', STATS_AREA_WIDTH / 2, 0);
|
|
break;
|
|
case Aura::QUICKNESS:
|
|
_summary.drawChar('Q', STATS_AREA_WIDTH / 2, 0);
|
|
break;
|
|
}
|
|
|
|
_summary.update();
|
|
}
|
|
|
|
void StatsArea::update(Party *party, PartyEvent &event) {
|
|
update(); // Do a full update
|
|
}
|
|
|
|
void StatsArea::update(Menu *menu, MenuEvent &event) {
|
|
update(); // Do a full update
|
|
}
|
|
|
|
void StatsArea::highlightPlayer(int player) {
|
|
assertMsg(player < g_context->_party->size(), "player number out of range: %d", player);
|
|
_mainArea.highlight(0, player * CHAR_HEIGHT, STATS_AREA_WIDTH * CHAR_WIDTH, CHAR_HEIGHT);
|
|
#ifdef IOS_ULTIMA4
|
|
U4IOS::updateActivePartyMember(player);
|
|
#endif
|
|
}
|
|
|
|
void StatsArea::clear() {
|
|
for (int i = 0; i < STATS_AREA_WIDTH; i++)
|
|
_title.drawChar(CHARSET_HORIZBAR, i, 0);
|
|
|
|
_mainArea.clear();
|
|
_summary.clear();
|
|
}
|
|
|
|
void StatsArea::redraw() {
|
|
_title.update();
|
|
_mainArea.update();
|
|
_summary.update();
|
|
}
|
|
|
|
void StatsArea::setTitle(const Common::String &s) {
|
|
int titleStart = (STATS_AREA_WIDTH / 2) - ((s.size() + 2) / 2);
|
|
_title.textAt(titleStart, 0, "%c%s%c", 16, s.c_str(), 17);
|
|
}
|
|
|
|
void StatsArea::showPartyView(bool avatarOnly) {
|
|
const char *format = "%d%c%-9.8s%3d%s";
|
|
|
|
PartyMember *p = nullptr;
|
|
int activePlayer = g_context->_party->getActivePlayer();
|
|
|
|
assertMsg(g_context->_party->size() <= 8, "party members out of range: %d", g_context->_party->size());
|
|
|
|
if (!avatarOnly) {
|
|
for (int i = 0; i < g_context->_party->size(); i++) {
|
|
p = g_context->_party->member(i);
|
|
_mainArea.textAt(0, i, format, i + 1, (i == activePlayer) ? CHARSET_BULLET : '-', p->getName().c_str(), p->getHp(), _mainArea.colorizeStatus(p->getStatus()).c_str());
|
|
}
|
|
} else {
|
|
p = g_context->_party->member(0);
|
|
_mainArea.textAt(0, 0, format, 1, (activePlayer == 0) ? CHARSET_BULLET : '-', p->getName().c_str(), p->getHp(), _mainArea.colorizeStatus(p->getStatus()).c_str());
|
|
}
|
|
}
|
|
|
|
void StatsArea::showPlayerDetails() {
|
|
int player = _view - STATS_CHAR1;
|
|
|
|
assertMsg(player < 8, "character number out of range: %d", player);
|
|
|
|
PartyMember *p = g_context->_party->member(player);
|
|
setTitle(p->getName());
|
|
_mainArea.textAt(0, 0, "%c %c", p->getSex(), p->getStatus());
|
|
Common::String classStr = getClassName(p->getClass());
|
|
int classStart = (STATS_AREA_WIDTH / 2) - (classStr.size() / 2);
|
|
_mainArea.textAt(classStart, 0, "%s", classStr.c_str());
|
|
_mainArea.textAt(0, 2, " MP:%02d LV:%d", p->getMp(), p->getRealLevel());
|
|
_mainArea.textAt(0, 3, "STR:%02d HP:%04d", p->getStr(), p->getHp());
|
|
_mainArea.textAt(0, 4, "DEX:%02d HM:%04d", p->getDex(), p->getMaxHp());
|
|
_mainArea.textAt(0, 5, "INT:%02d EX:%04d", p->getInt(), p->getExp());
|
|
_mainArea.textAt(0, 6, "W:%s", p->getWeapon()->getName().c_str());
|
|
_mainArea.textAt(0, 7, "A:%s", p->getArmor()->getName().c_str());
|
|
}
|
|
|
|
void StatsArea::showWeapons() {
|
|
setTitle("Weapons");
|
|
|
|
int line = 0;
|
|
int col = 0;
|
|
_mainArea.textAt(0, line++, "A-%s", g_weapons->get(WEAP_HANDS)->getName().c_str());
|
|
for (int w = WEAP_HANDS + 1; w < WEAP_MAX; w++) {
|
|
int n = g_ultima->_saveGame->_weapons[w];
|
|
if (n >= 100)
|
|
n = 99;
|
|
if (n >= 1) {
|
|
const char *format = (n >= 10) ? "%c%d-%s" : "%c-%d-%s";
|
|
|
|
_mainArea.textAt(col, line++, format, w - WEAP_HANDS + 'A', n, g_weapons->get((WeaponType) w)->getAbbrev().c_str());
|
|
if (line >= (STATS_AREA_HEIGHT)) {
|
|
line = 0;
|
|
col += 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StatsArea::showArmor() {
|
|
setTitle("Armour");
|
|
|
|
int line = 0;
|
|
_mainArea.textAt(0, line++, "A -No Armour");
|
|
for (int a = ARMR_NONE + 1; a < ARMR_MAX; a++) {
|
|
if (g_ultima->_saveGame->_armor[a] > 0) {
|
|
const char *format = (g_ultima->_saveGame->_armor[a] >= 10) ? "%c%d-%s" : "%c-%d-%s";
|
|
|
|
_mainArea.textAt(0, line++, format, a - ARMR_NONE + 'A', g_ultima->_saveGame->_armor[a], g_armors->get((ArmorType) a)->getName().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void StatsArea::showEquipment() {
|
|
setTitle("Equipment");
|
|
|
|
int line = 0;
|
|
_mainArea.textAt(0, line++, "%2d Torches", g_ultima->_saveGame->_torches);
|
|
_mainArea.textAt(0, line++, "%2d Gems", g_ultima->_saveGame->_gems);
|
|
_mainArea.textAt(0, line++, "%2d Keys", g_ultima->_saveGame->_keys);
|
|
if (g_ultima->_saveGame->_sextants > 0)
|
|
_mainArea.textAt(0, line++, "%2d Sextants", g_ultima->_saveGame->_sextants);
|
|
}
|
|
|
|
void StatsArea::showItems() {
|
|
int i, j;
|
|
char buffer[17];
|
|
|
|
setTitle("Items");
|
|
|
|
int line = 0;
|
|
if (g_ultima->_saveGame->_stones != 0) {
|
|
j = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
if (g_ultima->_saveGame->_stones & (1 << i))
|
|
buffer[j++] = getStoneName((Virtue) i)[0];
|
|
}
|
|
buffer[j] = '\0';
|
|
_mainArea.textAt(0, line++, "Stones:%s", buffer);
|
|
}
|
|
if (g_ultima->_saveGame->_runes != 0) {
|
|
j = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
if (g_ultima->_saveGame->_runes & (1 << i))
|
|
buffer[j++] = getVirtueName((Virtue) i)[0];
|
|
}
|
|
buffer[j] = '\0';
|
|
_mainArea.textAt(0, line++, "Runes:%s", buffer);
|
|
}
|
|
if (g_ultima->_saveGame->_items & (ITEM_CANDLE | ITEM_BOOK | ITEM_BELL)) {
|
|
buffer[0] = '\0';
|
|
if (g_ultima->_saveGame->_items & ITEM_BELL) {
|
|
Common::strcat_s(buffer, getItemName(ITEM_BELL));
|
|
Common::strcat_s(buffer, " ");
|
|
}
|
|
if (g_ultima->_saveGame->_items & ITEM_BOOK) {
|
|
Common::strcat_s(buffer, getItemName(ITEM_BOOK));
|
|
Common::strcat_s(buffer, " ");
|
|
}
|
|
if (g_ultima->_saveGame->_items & ITEM_CANDLE) {
|
|
Common::strcat_s(buffer, getItemName(ITEM_CANDLE));
|
|
buffer[15] = '\0';
|
|
}
|
|
_mainArea.textAt(0, line++, "%s", buffer);
|
|
}
|
|
if (g_ultima->_saveGame->_items & (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T)) {
|
|
j = 0;
|
|
if (g_ultima->_saveGame->_items & ITEM_KEY_T)
|
|
buffer[j++] = getItemName(ITEM_KEY_T)[0];
|
|
if (g_ultima->_saveGame->_items & ITEM_KEY_L)
|
|
buffer[j++] = getItemName(ITEM_KEY_L)[0];
|
|
if (g_ultima->_saveGame->_items & ITEM_KEY_C)
|
|
buffer[j++] = getItemName(ITEM_KEY_C)[0];
|
|
buffer[j] = '\0';
|
|
_mainArea.textAt(0, line++, "3 Part Key:%s", buffer);
|
|
}
|
|
if (g_ultima->_saveGame->_items & ITEM_HORN)
|
|
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_HORN));
|
|
if (g_ultima->_saveGame->_items & ITEM_WHEEL)
|
|
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_WHEEL));
|
|
if (g_ultima->_saveGame->_items & ITEM_SKULL)
|
|
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_SKULL));
|
|
}
|
|
|
|
void StatsArea::showReagents(bool active) {
|
|
setTitle("Reagents");
|
|
|
|
int line = 0,
|
|
r = REAG_ASH;
|
|
Common::String shortcut("A");
|
|
|
|
_reagentsMixMenu.show(&_mainArea);
|
|
|
|
for (const auto *item : _reagentsMixMenu) {
|
|
if (item->isVisible()) {
|
|
// Insert the reagent menu item shortcut character
|
|
shortcut.setChar('A' + r, 0);
|
|
if (active)
|
|
_mainArea.textAt(0, line++, "%s", _mainArea.colorizeString(shortcut, FG_YELLOW, 0, 1).c_str());
|
|
else
|
|
_mainArea.textAt(0, line++, "%s", shortcut.c_str());
|
|
}
|
|
r++;
|
|
}
|
|
}
|
|
|
|
void StatsArea::showMixtures() {
|
|
setTitle("Mixtures");
|
|
|
|
int line = 0;
|
|
int col = 0;
|
|
for (int s = 0; s < SPELL_MAX; s++) {
|
|
int n = g_ultima->_saveGame->_mixtures[s];
|
|
if (n >= 100)
|
|
n = 99;
|
|
if (n >= 1) {
|
|
_mainArea.textAt(col, line++, "%c-%02d", s + 'A', n);
|
|
if (line >= (STATS_AREA_HEIGHT)) {
|
|
if (col >= 10)
|
|
break;
|
|
line = 0;
|
|
col += 5;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StatsArea::resetReagentsMenu() {
|
|
int i = 0, row = 0;
|
|
|
|
for (auto *item : _reagentsMixMenu) {
|
|
if (g_ultima->_saveGame->_reagents[i++] > 0) {
|
|
item->setVisible(true);
|
|
item->setY(row++);
|
|
} else {
|
|
item->setVisible(false);
|
|
}
|
|
}
|
|
|
|
_reagentsMixMenu.reset(false);
|
|
}
|
|
|
|
} // End of namespace Ultima4
|
|
} // End of namespace Ultima
|