1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 09:04:31 +02:00

fix multi-line text centering, level stats screen (debug on 0 key)

This commit is contained in:
XProger
2018-10-17 10:37:18 +03:00
parent 92a9f9e941
commit ccc270ee2b
8 changed files with 113 additions and 42 deletions

View File

@@ -128,7 +128,7 @@ struct Character : Controller {
virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) { virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
if (getEntity().isEnemy() && health > 0.0f && health <= damage) if (getEntity().isEnemy() && health > 0.0f && health <= damage)
level->levelStats.kills++; level->stats.kills++;
health = max(0.0f, health - damage); health = max(0.0f, health - damage);
} }

View File

@@ -711,7 +711,7 @@ namespace Debug {
sprintf(buf, "floor = %d, roomBelow = %d, roomAbove = %d, roomNext = %d, height = %d", info.floorIndex, info.roomBelow, info.roomAbove, info.roomNext, int(info.floor - info.ceiling)); sprintf(buf, "floor = %d, roomBelow = %d, roomAbove = %d, roomNext = %d, height = %d", info.floorIndex, info.roomBelow, info.roomAbove, info.roomNext, int(info.floor - info.ceiling));
Debug::Draw::text(vec2(16, y += 16), vec4(1.0f), buf); Debug::Draw::text(vec2(16, y += 16), vec4(1.0f), buf);
const SaveProgress &stats = game->getLevel()->levelStats; const SaveProgress &stats = game->getLevel()->stats;
sprintf(buf, "stats: time = %d, distance = %d, secrets = %c%c%c, pickups = %d, mediUsed = %d, ammoUsed = %d, kills = %d", stats.time, stats.distance, sprintf(buf, "stats: time = %d, distance = %d, secrets = %c%c%c, pickups = %d, mediUsed = %d, ammoUsed = %d, kills = %d", stats.time, stats.distance,
(stats.secrets & 4) ? '1' : '0', (stats.secrets & 4) ? '1' : '0',
(stats.secrets & 2) ? '1' : '0', (stats.secrets & 2) ? '1' : '0',

View File

@@ -2344,8 +2344,7 @@ namespace TR {
} }
}; };
SaveProgress gameStats; SaveProgress stats;
SaveProgress levelStats;
SaveState state; SaveState state;
int cutEntity; int cutEntity;
@@ -2839,9 +2838,8 @@ namespace TR {
initRoomMeshes(); initRoomMeshes();
initAnimTex(); initAnimTex();
memset(&gameStats, 0, sizeof(gameStats)); memset(&stats, 0, sizeof(stats));
memset(&levelStats, 0, sizeof(levelStats)); memset(&state, 0, sizeof(state));
memset(&state, 0, sizeof(state));
initExtra(); initExtra();
initCutscene(); initCutscene();

View File

@@ -186,6 +186,11 @@ namespace Game {
if (level->isEnded) if (level->isEnded)
return true; return true;
if (Input::down[ik0] && !level->inventory->isActive()) {
level->inventory->toggle(0, Inventory::PAGE_LEVEL_STATS);
Input::down[ik0] = false;
}
if (Input::down[ik5] && !level->inventory->isActive()) { if (Input::down[ik5] && !level->inventory->isActive()) {
if (level->players[0]->canSaveGame()) if (level->players[0]->canSaveGame())
level->saveGame(true, false); level->saveGame(true, false);

View File

@@ -75,8 +75,8 @@ struct OptionItem {
UI::textOut(vec2(x, y), vStr, UI::aCenter, w, alpha, UI::SHADE_GRAY); // color as StringID UI::textOut(vec2(x, y), vStr, UI::aCenter, w, alpha, UI::SHADE_GRAY); // color as StringID
if (type == TYPE_PARAM && active) { if (type == TYPE_PARAM && active) {
float maxWidth = UI::getTextSize(STR[color + value]).x; int maxWidth = UI::getTextSize(STR[color + value]).x;
maxWidth = maxWidth * 0.5f + 8.0f; maxWidth = maxWidth / 2 + 8;
x += w * 0.5f; x += w * 0.5f;
if (checkValue(value - 1)) UI::specOut(vec2(x - maxWidth - 16.0f, y), 108); if (checkValue(value - 1)) UI::specOut(vec2(x - maxWidth - 16.0f, y), 108);
if (checkValue(value + 1)) UI::specOut(vec2(x + maxWidth, y), 109); if (checkValue(value + 1)) UI::specOut(vec2(x + maxWidth, y), 109);
@@ -191,6 +191,7 @@ struct Inventory {
PAGE_INVENTORY, PAGE_INVENTORY,
PAGE_ITEMS, PAGE_ITEMS,
PAGE_SAVEGAME, PAGE_SAVEGAME,
PAGE_LEVEL_STATS,
PAGE_MAX PAGE_MAX
}; };
@@ -787,7 +788,8 @@ struct Inventory {
phaseRing = active ? 1.0f : 0.0f; phaseRing = active ? 1.0f : 0.0f;
slot = 1; slot = 1;
} else { } else {
game->playSound(active ? TR::SND_INV_SHOW : TR::SND_INV_HIDE, p); if (curPage != PAGE_LEVEL_STATS)
game->playSound(active ? TR::SND_INV_SHOW : TR::SND_INV_HIDE, p);
} }
chosen = false; chosen = false;
@@ -1085,7 +1087,11 @@ struct Inventory {
Item *item = items[getGlobalIndex(page, index)]; Item *item = items[getGlobalIndex(page, index)];
if (page == PAGE_SAVEGAME) { if (page == PAGE_LEVEL_STATS) {
if (Input::lastState[playerIndex] != cMAX) {
toggle(playerIndex, targetPage);
}
} else if (page == PAGE_SAVEGAME) {
if (Input::lastState[playerIndex] == cLeft || Input::lastState[playerIndex] == cRight) if (Input::lastState[playerIndex] == cLeft || Input::lastState[playerIndex] == cRight)
slot ^= 1; slot ^= 1;
@@ -1345,7 +1351,7 @@ struct Inventory {
} }
UI::textOut(vec2(0, 480 - 32), str, UI::aCenter, UI::width); UI::textOut(vec2(0, 480 - 32), str, UI::aCenter, UI::width);
float tw = UI::getTextSize(STR[str]).x; int tw = UI::getTextSize(STR[str]).x;
if (item->value > 0) UI::specOut(vec2((UI::width - tw) * 0.5f - 32.0f, 480 - 32), 108); if (item->value > 0) UI::specOut(vec2((UI::width - tw) * 0.5f - 32.0f, 480 - 32), 108);
if (item->value < 2) UI::specOut(vec2((UI::width + tw) * 0.5f + 16.0f, 480 - 32), 109); if (item->value < 2) UI::specOut(vec2((UI::width + tw) * 0.5f + 16.0f, 480 - 32), 109);
@@ -1716,10 +1722,39 @@ struct Inventory {
renderPage(targetPage); renderPage(targetPage);
} }
void showLevelStats(const vec2 &pos) {
char buf[256];
char time[16];
TR::Level *level = game->getLevel();
SaveProgress &stats = level->stats;
int secretsMax = 3;
int secrets = ((stats.secrets & 1) != 0) +
((stats.secrets & 2) != 0) +
((stats.secrets & 4) != 0);
int s = stats.time % 60;
int m = stats.time / 60 % 60;
int h = stats.time / 3600;
if (h)
sprintf(time, "%d:%02d:%02d", h, m, s);
else
sprintf(time, "%d:%02d", m, s);
sprintf(buf, STR[STR_LEVEL_STATS],
TR::LEVEL_INFO[level->id].title,
stats.kills,
stats.pickups,
secrets, secretsMax, time);
UI::textOut(pos, buf, UI::aCenter, UI::width);
}
void renderUI() { void renderUI() {
if (!active || phaseRing < 1.0f) return; if (!active || phaseRing < 1.0f) return;
static const StringID pageTitle[PAGE_MAX] = { STR_OPTION, STR_INVENTORY, STR_ITEMS, STR_SAVEGAME }; static const StringID pageTitle[PAGE_MAX] = { STR_OPTION, STR_INVENTORY, STR_ITEMS, STR_SAVEGAME, STR_LEVEL_STATS };
float eye = UI::width * Core::eye * 0.01f; float eye = UI::width * Core::eye * 0.01f;
@@ -1738,6 +1773,11 @@ struct Inventory {
return; return;
} }
if (page == PAGE_LEVEL_STATS) {
showLevelStats(vec2(-eye, 180));
return;
}
if (!game->getLevel()->isTitle()) if (!game->getLevel()->isTitle())
UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width); UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width);

View File

@@ -1020,7 +1020,7 @@ struct Lara : Character {
} }
if (shots) { if (shots) {
level->levelStats.ammoUsed += ((wpnCurrent == TR::Entity::SHOTGUN) ? 1 : 2); level->stats.ammoUsed += ((wpnCurrent == TR::Entity::SHOTGUN) ? 1 : 2);
game->playSound(wpnGetSound(), pos, Sound::PAN); game->playSound(wpnGetSound(), pos, Sound::PAN);
game->playSound(TR::SND_RICOCHET, nearPos, Sound::PAN); game->playSound(TR::SND_RICOCHET, nearPos, Sound::PAN);
@@ -1635,7 +1635,7 @@ struct Lara : Character {
case TR::Entity::INV_UZIS : wpnChange(TR::Entity::UZIS); break; case TR::Entity::INV_UZIS : wpnChange(TR::Entity::UZIS); break;
case TR::Entity::INV_MEDIKIT_SMALL : case TR::Entity::INV_MEDIKIT_SMALL :
case TR::Entity::INV_MEDIKIT_BIG : case TR::Entity::INV_MEDIKIT_BIG :
level->levelStats.mediUsed += (item == TR::Entity::INV_MEDIKIT_SMALL) ? 1 : 2; level->stats.mediUsed += (item == TR::Entity::INV_MEDIKIT_SMALL) ? 1 : 2;
damageTime = LARA_DAMAGE_TIME; damageTime = LARA_DAMAGE_TIME;
health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH)); health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH));
game->playSound(TR::SND_HEALTH, pos, Sound::PAN); game->playSound(TR::SND_HEALTH, pos, Sound::PAN);
@@ -2107,8 +2107,8 @@ struct Lara : Character {
effect = TR::Effect::Type(cmd.args); effect = TR::Effect::Type(cmd.args);
break; break;
case TR::Action::SECRET : case TR::Action::SECRET :
if (!(level->levelStats.secrets & (1 << cmd.args))) { if (!(level->stats.secrets & (1 << cmd.args))) {
level->levelStats.secrets |= 1 << cmd.args; level->stats.secrets |= 1 << cmd.args;
if (!game->playSound(TR::SND_SECRET, pos)) if (!game->playSound(TR::SND_SECRET, pos))
game->playTrack(TR::TRACK_TR1_SECRET, true); game->playTrack(TR::TRACK_TR1_SECRET, true);
} }
@@ -2839,7 +2839,7 @@ struct Lara : Character {
pickupList[i]->deactivate(); pickupList[i]->deactivate();
pickupList[i]->flags.invisible = true; pickupList[i]->flags.invisible = true;
game->invAdd(pickupList[i]->getEntity().type, 1); game->invAdd(pickupList[i]->getEntity().type, 1);
level->levelStats.pickups++; level->stats.pickups++;
} }
pickupListCount = 0; pickupListCount = 0;
} }
@@ -3123,7 +3123,7 @@ struct Lara : Character {
statsDistDelta += (pos - oldPos).length(); statsDistDelta += (pos - oldPos).length();
while (statsDistDelta >= UNITS_PER_METER) { while (statsDistDelta >= UNITS_PER_METER) {
statsDistDelta -= UNITS_PER_METER; statsDistDelta -= UNITS_PER_METER;
level->levelStats.distance++; level->stats.distance++;
} }
} }
} }

View File

@@ -82,7 +82,7 @@ struct Level : IGame {
// save next level // save next level
level.id = TR::getNextSaveLevel(level.id); // get next not cutscene level level.id = TR::getNextSaveLevel(level.id); // get next not cutscene level
if (level.id != TR::LVL_MAX && !level.isTitle()) { if (level.id != TR::LVL_MAX && !level.isTitle()) {
memset(&level.levelStats, 0, sizeof(level.levelStats)); memset(&level.stats, 0, sizeof(level.stats));
saveGame(false, false); saveGame(false, false);
loadSlot = getSaveSlot(level.id, false); loadSlot = getSaveSlot(level.id, false);
} }
@@ -123,7 +123,7 @@ struct Level : IGame {
if (dummy) if (dummy)
memset(levelStats, 0, sizeof(*levelStats)); memset(levelStats, 0, sizeof(*levelStats));
else else
*levelStats = level.levelStats; *levelStats = level.stats;
// inventory items // inventory items
int32 *itemsCount = (int32*)ptr; int32 *itemsCount = (int32*)ptr;
@@ -191,8 +191,8 @@ struct Level : IGame {
uint8 *ptr = data; uint8 *ptr = data;
// level progress stats // level progress stats
level.levelStats = *(SaveProgress*)ptr; level.stats = *(SaveProgress*)ptr;
ptr += sizeof(level.levelStats); ptr += sizeof(level.stats);
// inventory items // inventory items
int32 itemsCount = *(int32*)ptr; int32 itemsCount = *(int32*)ptr;
@@ -247,7 +247,7 @@ struct Level : IGame {
level.state.flags.track = 0; level.state.flags.track = 0;
playTrack(track); playTrack(track);
} else } else
memset(&level.levelStats, 0, sizeof(level.levelStats)); memset(&level.stats, 0, sizeof(level.stats));
statsTimeDelta = 0.0f; statsTimeDelta = 0.0f;
} }
@@ -284,7 +284,7 @@ struct Level : IGame {
} else } else
slot = saveSlots[index]; slot = saveSlots[index];
SaveProgress *levelStats = (SaveProgress*)slot.data; SaveProgress *levelStats = (SaveProgress*)slot.data;
*levelStats = level.levelStats; *levelStats = level.stats;
} else { } else {
removeSaveSlot(id, checkpoint); // remove checkpoints and level saves removeSaveSlot(id, checkpoint); // remove checkpoints and level saves
saveSlots.push(createSaveSlot(id, checkpoint)); saveSlots.push(createSaveSlot(id, checkpoint));
@@ -1771,7 +1771,7 @@ struct Level : IGame {
statsTimeDelta += Core::deltaTime; statsTimeDelta += Core::deltaTime;
while (statsTimeDelta >= 1.0f) { while (statsTimeDelta >= 1.0f) {
statsTimeDelta -= 1.0f; statsTimeDelta -= 1.0f;
level.levelStats.time++; level.stats.time++;
} }
params->time += Core::deltaTime; params->time += Core::deltaTime;

View File

@@ -11,6 +11,7 @@ enum StringID {
, STR_LOADING , STR_LOADING
, STR_HELP_PRESS , STR_HELP_PRESS
, STR_HELP_TEXT , STR_HELP_TEXT
, STR_LEVEL_STATS
, STR_HINT_SAVING , STR_HINT_SAVING
, STR_HINT_SAVING_DONE , STR_HINT_SAVING_DONE
, STR_HINT_SAVING_ERROR , STR_HINT_SAVING_ERROR
@@ -122,6 +123,12 @@ const char *helpText =
"DOZY on - Look + Duck + Action + Jump@" "DOZY on - Look + Duck + Action + Jump@"
"DOZY off - Walk"; "DOZY off - Walk";
const char *levelStats =
"%s@@@"
"KILLS %d@@"
"PICKUPS %d@@"
"SECRETS %d of %d@@"
"TIME TAKEN %s";
const char *STR[STR_MAX] = { const char *STR[STR_MAX] = {
"Not implemented yet!" "Not implemented yet!"
@@ -129,6 +136,7 @@ const char *STR[STR_MAX] = {
, "Loading..." , "Loading..."
, "Press H for help" , "Press H for help"
, helpText , helpText
, levelStats
, "Saving game..." , "Saving game..."
, "Saving done!" , "Saving done!"
, "SAVING ERROR!" , "SAVING ERROR!"
@@ -256,7 +264,21 @@ namespace UI {
return char_map[c - 32]; return char_map[c - 32];
} }
vec2 getTextSize(const char *text) { short2 getLineSize(const char *text) {
int x = 0;
while (char c = *text++) {
if (c == ' ' || c == '_') {
x += 6;
} else if (c == '@') {
break;
} else
x += char_width[charRemap(c)] + 1;
}
return short2(x, 16);
}
short2 getTextSize(const char *text) {
int x = 0, w = 0, h = 16; int x = 0, w = 0, h = 16;
while (char c = *text++) { while (char c = *text++) {
@@ -271,7 +293,7 @@ namespace UI {
} }
w = max(w, x); w = max(w, x);
return vec2(float(w), float(h)); return short2(w, h);
} }
#define MAX_CHARS DYN_MESH_QUADS #define MAX_CHARS DYN_MESH_QUADS
@@ -347,6 +369,19 @@ namespace UI {
SHADE_GRAY = 2, SHADE_GRAY = 2,
}; };
int getLeftOffset(const char *text, Align align, int width) {
if (align != aLeft) {
int lineWidth = getLineSize(text).x;
if (align == aCenter)
return (width - lineWidth) / 2;
if (align == aRight)
return width - lineWidth;
}
return 0;
}
void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0, uint8 alpha = 255, ShadeType shade = SHADE_ORANGE, bool isShadow = false) { void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0, uint8 alpha = 255, ShadeType shade = SHADE_ORANGE, bool isShadow = false) {
if (!text) return; if (!text) return;
@@ -358,26 +393,19 @@ namespace UI {
MeshBuilder *mesh = game->getMesh(); MeshBuilder *mesh = game->getMesh();
int seq = level->extra.glyphs; int seq = level->extra.glyphs;
int x = int(pos.x); int x = int(pos.x) + getLeftOffset(text, align, width);
int y = int(pos.y); int y = int(pos.y);
if (align == aCenter)
x += int((width - getTextSize(text).x) / 2);
if (align == aRight)
x += int(width - getTextSize(text).x);
int left = x;
while (char c = *text++) { while (char c = *text++) {
if (c == ' ' || c == '_') {
x += 6; if (c == '@') {
x = int(pos.x) + getLeftOffset(text, align, width);
y += 16;
continue; continue;
} }
if (c == '@') { if (c == ' ' || c == '_') {
x = left; x += 6;
y += 16;
continue; continue;
} }