mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-13 16:44:50 +02:00
fix multi-line text centering, level stats screen (debug on 0 key)
This commit is contained in:
@@ -128,7 +128,7 @@ struct Character : Controller {
|
||||
|
||||
virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
|
||||
if (getEntity().isEnemy() && health > 0.0f && health <= damage)
|
||||
level->levelStats.kills++;
|
||||
level->stats.kills++;
|
||||
health = max(0.0f, health - damage);
|
||||
}
|
||||
|
||||
|
@@ -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));
|
||||
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,
|
||||
(stats.secrets & 4) ? '1' : '0',
|
||||
(stats.secrets & 2) ? '1' : '0',
|
||||
|
@@ -2344,8 +2344,7 @@ namespace TR {
|
||||
}
|
||||
};
|
||||
|
||||
SaveProgress gameStats;
|
||||
SaveProgress levelStats;
|
||||
SaveProgress stats;
|
||||
SaveState state;
|
||||
|
||||
int cutEntity;
|
||||
@@ -2839,9 +2838,8 @@ namespace TR {
|
||||
initRoomMeshes();
|
||||
initAnimTex();
|
||||
|
||||
memset(&gameStats, 0, sizeof(gameStats));
|
||||
memset(&levelStats, 0, sizeof(levelStats));
|
||||
memset(&state, 0, sizeof(state));
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
initExtra();
|
||||
initCutscene();
|
||||
|
@@ -186,6 +186,11 @@ namespace Game {
|
||||
if (level->isEnded)
|
||||
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 (level->players[0]->canSaveGame())
|
||||
level->saveGame(true, false);
|
||||
|
@@ -75,8 +75,8 @@ struct OptionItem {
|
||||
UI::textOut(vec2(x, y), vStr, UI::aCenter, w, alpha, UI::SHADE_GRAY); // color as StringID
|
||||
|
||||
if (type == TYPE_PARAM && active) {
|
||||
float maxWidth = UI::getTextSize(STR[color + value]).x;
|
||||
maxWidth = maxWidth * 0.5f + 8.0f;
|
||||
int maxWidth = UI::getTextSize(STR[color + value]).x;
|
||||
maxWidth = maxWidth / 2 + 8;
|
||||
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, y), 109);
|
||||
@@ -191,6 +191,7 @@ struct Inventory {
|
||||
PAGE_INVENTORY,
|
||||
PAGE_ITEMS,
|
||||
PAGE_SAVEGAME,
|
||||
PAGE_LEVEL_STATS,
|
||||
PAGE_MAX
|
||||
};
|
||||
|
||||
@@ -787,7 +788,8 @@ struct Inventory {
|
||||
phaseRing = active ? 1.0f : 0.0f;
|
||||
slot = 1;
|
||||
} 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;
|
||||
@@ -1085,7 +1087,11 @@ struct Inventory {
|
||||
|
||||
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)
|
||||
slot ^= 1;
|
||||
|
||||
@@ -1345,7 +1351,7 @@ struct Inventory {
|
||||
}
|
||||
|
||||
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 < 2) UI::specOut(vec2((UI::width + tw) * 0.5f + 16.0f, 480 - 32), 109);
|
||||
@@ -1716,10 +1722,39 @@ struct Inventory {
|
||||
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() {
|
||||
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;
|
||||
|
||||
@@ -1738,6 +1773,11 @@ struct Inventory {
|
||||
return;
|
||||
}
|
||||
|
||||
if (page == PAGE_LEVEL_STATS) {
|
||||
showLevelStats(vec2(-eye, 180));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!game->getLevel()->isTitle())
|
||||
UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width);
|
||||
|
||||
|
12
src/lara.h
12
src/lara.h
@@ -1020,7 +1020,7 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
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(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_MEDIKIT_SMALL :
|
||||
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;
|
||||
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);
|
||||
@@ -2107,8 +2107,8 @@ struct Lara : Character {
|
||||
effect = TR::Effect::Type(cmd.args);
|
||||
break;
|
||||
case TR::Action::SECRET :
|
||||
if (!(level->levelStats.secrets & (1 << cmd.args))) {
|
||||
level->levelStats.secrets |= 1 << cmd.args;
|
||||
if (!(level->stats.secrets & (1 << cmd.args))) {
|
||||
level->stats.secrets |= 1 << cmd.args;
|
||||
if (!game->playSound(TR::SND_SECRET, pos))
|
||||
game->playTrack(TR::TRACK_TR1_SECRET, true);
|
||||
}
|
||||
@@ -2839,7 +2839,7 @@ struct Lara : Character {
|
||||
pickupList[i]->deactivate();
|
||||
pickupList[i]->flags.invisible = true;
|
||||
game->invAdd(pickupList[i]->getEntity().type, 1);
|
||||
level->levelStats.pickups++;
|
||||
level->stats.pickups++;
|
||||
}
|
||||
pickupListCount = 0;
|
||||
}
|
||||
@@ -3123,7 +3123,7 @@ struct Lara : Character {
|
||||
statsDistDelta += (pos - oldPos).length();
|
||||
while (statsDistDelta >= UNITS_PER_METER) {
|
||||
statsDistDelta -= UNITS_PER_METER;
|
||||
level->levelStats.distance++;
|
||||
level->stats.distance++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
src/level.h
14
src/level.h
@@ -82,7 +82,7 @@ struct Level : IGame {
|
||||
// save next level
|
||||
level.id = TR::getNextSaveLevel(level.id); // get next not cutscene level
|
||||
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);
|
||||
loadSlot = getSaveSlot(level.id, false);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ struct Level : IGame {
|
||||
if (dummy)
|
||||
memset(levelStats, 0, sizeof(*levelStats));
|
||||
else
|
||||
*levelStats = level.levelStats;
|
||||
*levelStats = level.stats;
|
||||
|
||||
// inventory items
|
||||
int32 *itemsCount = (int32*)ptr;
|
||||
@@ -191,8 +191,8 @@ struct Level : IGame {
|
||||
uint8 *ptr = data;
|
||||
|
||||
// level progress stats
|
||||
level.levelStats = *(SaveProgress*)ptr;
|
||||
ptr += sizeof(level.levelStats);
|
||||
level.stats = *(SaveProgress*)ptr;
|
||||
ptr += sizeof(level.stats);
|
||||
|
||||
// inventory items
|
||||
int32 itemsCount = *(int32*)ptr;
|
||||
@@ -247,7 +247,7 @@ struct Level : IGame {
|
||||
level.state.flags.track = 0;
|
||||
playTrack(track);
|
||||
} else
|
||||
memset(&level.levelStats, 0, sizeof(level.levelStats));
|
||||
memset(&level.stats, 0, sizeof(level.stats));
|
||||
|
||||
statsTimeDelta = 0.0f;
|
||||
}
|
||||
@@ -284,7 +284,7 @@ struct Level : IGame {
|
||||
} else
|
||||
slot = saveSlots[index];
|
||||
SaveProgress *levelStats = (SaveProgress*)slot.data;
|
||||
*levelStats = level.levelStats;
|
||||
*levelStats = level.stats;
|
||||
} else {
|
||||
removeSaveSlot(id, checkpoint); // remove checkpoints and level saves
|
||||
saveSlots.push(createSaveSlot(id, checkpoint));
|
||||
@@ -1771,7 +1771,7 @@ struct Level : IGame {
|
||||
statsTimeDelta += Core::deltaTime;
|
||||
while (statsTimeDelta >= 1.0f) {
|
||||
statsTimeDelta -= 1.0f;
|
||||
level.levelStats.time++;
|
||||
level.stats.time++;
|
||||
}
|
||||
|
||||
params->time += Core::deltaTime;
|
||||
|
60
src/ui.h
60
src/ui.h
@@ -11,6 +11,7 @@ enum StringID {
|
||||
, STR_LOADING
|
||||
, STR_HELP_PRESS
|
||||
, STR_HELP_TEXT
|
||||
, STR_LEVEL_STATS
|
||||
, STR_HINT_SAVING
|
||||
, STR_HINT_SAVING_DONE
|
||||
, STR_HINT_SAVING_ERROR
|
||||
@@ -122,6 +123,12 @@ const char *helpText =
|
||||
"DOZY on - Look + Duck + Action + Jump@"
|
||||
"DOZY off - Walk";
|
||||
|
||||
const char *levelStats =
|
||||
"%s@@@"
|
||||
"KILLS %d@@"
|
||||
"PICKUPS %d@@"
|
||||
"SECRETS %d of %d@@"
|
||||
"TIME TAKEN %s";
|
||||
|
||||
const char *STR[STR_MAX] = {
|
||||
"Not implemented yet!"
|
||||
@@ -129,6 +136,7 @@ const char *STR[STR_MAX] = {
|
||||
, "Loading..."
|
||||
, "Press H for help"
|
||||
, helpText
|
||||
, levelStats
|
||||
, "Saving game..."
|
||||
, "Saving done!"
|
||||
, "SAVING ERROR!"
|
||||
@@ -256,7 +264,21 @@ namespace UI {
|
||||
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;
|
||||
|
||||
while (char c = *text++) {
|
||||
@@ -271,7 +293,7 @@ namespace UI {
|
||||
}
|
||||
w = max(w, x);
|
||||
|
||||
return vec2(float(w), float(h));
|
||||
return short2(w, h);
|
||||
}
|
||||
|
||||
#define MAX_CHARS DYN_MESH_QUADS
|
||||
@@ -347,6 +369,19 @@ namespace UI {
|
||||
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) {
|
||||
if (!text) return;
|
||||
|
||||
@@ -358,26 +393,19 @@ namespace UI {
|
||||
MeshBuilder *mesh = game->getMesh();
|
||||
int seq = level->extra.glyphs;
|
||||
|
||||
int x = int(pos.x);
|
||||
int x = int(pos.x) + getLeftOffset(text, align, width);
|
||||
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++) {
|
||||
if (c == ' ' || c == '_') {
|
||||
x += 6;
|
||||
|
||||
if (c == '@') {
|
||||
x = int(pos.x) + getLeftOffset(text, align, width);
|
||||
y += 16;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '@') {
|
||||
x = left;
|
||||
y += 16;
|
||||
if (c == ' ' || c == '_') {
|
||||
x += 6;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user