1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-26 12:22:41 +01:00

Merge pull request #210 from danhedron/overhaul-text

Overhaul GXT handling and menu text
This commit is contained in:
Daniel Evans 2016-08-23 00:20:15 +01:00 committed by GitHub
commit 268e8193c6
23 changed files with 212 additions and 221 deletions

View File

@ -39,7 +39,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
add_definitions(-DRW_NETBSD)
elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
add_definitions(-DRW_OPENBSD)
set(OPENRW_PLATFORM_LIBS iconv)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_definitions(-DRW_WINDOWS)
else ()
@ -82,10 +81,6 @@ if(NOT DEFINED DOC_DIR)
endif()
# Find dependancies
IF(APPLE)
set(OPENRW_PLATFORM_LIBS iconv)
ENDIF()
find_package(OpenGL REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED)

View File

@ -33,6 +33,7 @@ set(RWENGINE_SOURCES
src/data/Chase.hpp
src/data/CollisionModel.hpp
src/data/CutsceneData.hpp
src/data/GameTexts.cpp
src/data/GameTexts.hpp
src/data/InstanceData.hpp
src/data/ObjectData.cpp

View File

@ -0,0 +1,10 @@
#include "GameTexts.hpp"
GameString GameStringUtil::fromString(const std::string& str)
{
GameString s;
for (std::string::size_type i = 0u; i < str.size(); ++i) {
s += str[i];
}
return s;
}

View File

@ -1,24 +1,62 @@
#pragma once
#ifndef _GAMETEXTS_HPP_
#define _GAMETEXTS_HPP_
#ifndef RWENGINE_GAMETEXTS_HPP
#define RWENGINE_GAMETEXTS_HPP
#include <string>
#include <unordered_map>
/**
* Each GXT char is just a 16-bit index into the font map.
*/
using GameStringChar = uint16_t;
/**
* The game stores strings as 16-bit indexes into the font
* texture, which is something simllar to ASCII.
*/
using GameString = std::basic_string<GameStringChar>;
/**
* GXT keys are just 8 single byte chars.
* Keys are small so should be subject to SSO
*/
using GameStringKey = std::string;
namespace GameStringUtil
{
/**
* @brief fromString Converts a string to a GameString
*
* Encoding of GameStrings depends on the font, only simple ASCII chars will map well
*/
GameString fromString(const std::string& str);
}
/**
* Since the encoding of symbols is arbitrary, these constants should be used in
* hard-coded strings containing symbols outside of the ASCII-subset supported by
* all fonts
*/
namespace GameSymbols
{
static constexpr GameStringChar Money = '$';
static constexpr GameStringChar Heart = '{';
static constexpr GameStringChar Armour = '[';
static constexpr GameStringChar Star = ']';
}
class GameTexts
{
std::unordered_map<std::string, std::string> _textDB;
using StringTable = std::unordered_map<GameStringKey, GameString>;
StringTable m_strings;
public:
void addText(const std::string& id, const std::string& text) {
_textDB.insert({ id, text });
void addText(const GameStringKey& id, GameString&& text) {
m_strings.emplace(id, text);
}
std::string text(const std::string& id) {
auto a = _textDB.find(id);
if( a != _textDB.end() ) {
GameString text(const GameStringKey& id) {
auto a = m_strings.find(id);
if( a != m_strings.end() ) {
return a->second;
}
return id;
return GameStringUtil::fromString("MISSING: " + id);
}
};

View File

@ -1,7 +1,7 @@
#include <engine/GameState.hpp>
BasicState::BasicState()
: saveName { "" }
: saveName { 0 }
, saveTime { 0, 0, 0, 0, 0, 0, 0, 0 }
, islandNumber { 0 }
, cameraPosition { }

View File

@ -33,8 +33,7 @@ struct SystemTime
/** Block 0 State */
struct BasicState
{
/// /!\ This is wchar_t[24] in the original format /!\ we convert on load for convenience
char saveName[48];
GameStringChar saveName[24];
SystemTime saveTime;
uint32_t unknown;
uint16_t islandNumber;
@ -162,7 +161,7 @@ struct GameStats
struct TextDisplayData
{
// This is set by the final display text command.
std::string text;
GameString text;
glm::vec2 position;
glm::vec4 colourFG;

View File

@ -11,7 +11,6 @@
#include <ai/PlayerController.hpp>
#include <items/WeaponItem.hpp>
#include <cstring>
#include <iconv.h>
#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
@ -528,24 +527,6 @@ bool SaveGame::loadGame(GameState& state, const std::string& file)
static_assert(sizeof(BasicState) == 0xBC, "BasicState is not the right size");
READ_VALUE(state.basic)
// Convert utf-16 to utf-8
size_t bytes = 0;
for(;; bytes++ ) {
if(state.basic.saveName[bytes-1] == 0 && state.basic.saveName[bytes] == 0) break;
}
size_t outSize = 24;
char outBuff[48];
char* outCur = outBuff;
auto icv = iconv_open("UTF-8", "UTF-16");
char* saveName = (char*)state.basic.saveName;
#if defined(RW_NETBSD)
iconv(icv, (const char**)&saveName, &bytes, &outCur, &outSize);
#else
iconv(icv, &saveName, &bytes, &outCur, &outSize);
#endif
strcpy(state.basic.saveName, outBuff);
BlockDword scriptBlockSize;
READ_SIZE(scriptBlockSize)
@ -1244,24 +1225,6 @@ bool SaveGame::getSaveInfo(const std::string& file, BasicState *basicState)
std::fclose(loadFile);
size_t bytes = 0;
for(;; bytes++ ) {
if(basicState->saveName[bytes-1] == 0 && basicState->saveName[bytes] == 0) break;
}
size_t outSize = 24;
char outBuff[48];
char* outCur = outBuff;
auto icv = iconv_open("UTF-8", "UTF-16");
char* saveName = (char*)basicState->saveName;
// Convert to UTF-8 and copy back to the return struct
#if defined(RW_NETBSD)
iconv(icv, (const char**)&saveName, &bytes, &outCur, &outSize);
#else
iconv(icv, &saveName, &bytes, &outCur, &outSize);
#endif
strcpy(basicState->saveName, outBuff);
return true;
}

View File

@ -26,7 +26,7 @@ void ScreenText::tick(float dt)
}
}
ScreenTextEntry ScreenTextEntry::makeBig(const std::string& id, const std::string& str, int style, int durationMS)
ScreenTextEntry ScreenTextEntry::makeBig(const GameStringKey& id, const GameString& str, int style, int durationMS)
{
switch(style) {
@ -142,7 +142,7 @@ ScreenTextEntry ScreenTextEntry::makeBig(const std::string& id, const std::strin
}
return {
"Error, style " + std::to_string(style),
GameStringUtil::fromString("Error, style " + std::to_string(style)),
{320.f, 400.f},
2,
50,
@ -156,7 +156,7 @@ ScreenTextEntry ScreenTextEntry::makeBig(const std::string& id, const std::strin
};
}
ScreenTextEntry ScreenTextEntry::makeHighPriority(const std::string& id, const std::string& str, int durationMS)
ScreenTextEntry ScreenTextEntry::makeHighPriority(const GameStringKey& id, const GameString& str, int durationMS)
{
// Color: ?
// Font: Arial
@ -179,7 +179,7 @@ ScreenTextEntry ScreenTextEntry::makeHighPriority(const std::string& id, const s
};
}
ScreenTextEntry ScreenTextEntry::makeHelp(const std::string& id, const std::string& str)
ScreenTextEntry ScreenTextEntry::makeHelp(const GameStringKey& id, const GameString& str)
{
return {
str,

View File

@ -1,7 +1,7 @@
#ifndef _RWENGINE_SCREENTEXT_HPP_
#define _RWENGINE_SCREENTEXT_HPP_
#ifndef RWENGINE_SCREENTEXT_HPP
#define RWENGINE_SCREENTEXT_HPP
#include <rw/types.hpp>
#include <string>
#include <data/GameTexts.hpp>
#include <vector>
#include <array>
#include <algorithm>
@ -33,7 +33,7 @@ constexpr unsigned int ScreenTypeTextCount = static_cast<unsigned int>(ScreenTex
struct ScreenTextEntry
{
/// After processing numbers
std::string text;
GameString text;
/// in the virtual 640x480 screen space
glm::vec2 position;
/// Font number
@ -53,19 +53,19 @@ struct ScreenTextEntry
/// Wrap width
int wrapX;
/// ID used to reference the text.
std::string id;
GameStringKey id;
static ScreenTextEntry makeBig(const std::string& id,
const std::string& str,
static ScreenTextEntry makeBig(const GameStringKey& id,
const GameString& str,
int style,
int durationMS);
static ScreenTextEntry makeHighPriority(const std::string& id,
const std::string& str,
static ScreenTextEntry makeHighPriority(const GameStringKey& id,
const GameString& str,
int durationMS);
static ScreenTextEntry makeHelp(const std::string& id,
const std::string& str);
static ScreenTextEntry makeHelp(const GameStringKey& id,
const GameString& str);
};
/**
@ -141,12 +141,13 @@ public:
}
template<class...Args>
static std::string format(std::string format, Args&&...args)
static GameString format(GameString format, Args&&...args)
{
const std::array<std::string, sizeof...(args)> vals = { args... };
static auto kReplacementMarker = GameStringUtil::fromString("~1~");
const std::array<GameString, sizeof...(args)> vals = { args... };
size_t x = 0, val = 0;
// We're only looking for numerical replacement markers
while ((x = format.find("~1~")) != std::string::npos && val < vals.size())
while ((x = format.find(kReplacementMarker)) != GameString::npos && val < vals.size())
{
format = format.substr(0, x) + vals[val++] + format.substr(x + 3);
}

View File

@ -1,5 +1,4 @@
#include <loaders/LoaderGXT.hpp>
#include <iconv.h>
void LoaderGXT::load(GameTexts &texts, FileHandle &file)
{
@ -13,40 +12,12 @@ void LoaderGXT::load(GameTexts &texts, FileHandle &file)
auto tdata = data+blocksize+8;
// This is not supported in GCC 4.8.1
//std::wstring_convert<std::codecvt<char16_t,char,std::mbstate_t>,char16_t> convert;
auto icv = iconv_open("UTF-8", "UTF-16");
for( size_t t = 0; t < blocksize/12; ++t ) {
size_t offset = *(std::uint32_t*)(data+(t * 12 + 0));
std::string id(data+(t * 12 + 4));
// Find the terminating bytes
size_t bytes = 0;
for(;; bytes++ ) {
if(tdata[offset+bytes-1] == 0 && tdata[offset+bytes] == 0) break;
}
size_t len = bytes/2;
size_t outSize = 1024;
char u8buff[1024];
char *uwot = u8buff;
char* strbase = tdata+offset;
#if defined(RW_NETBSD)
iconv(icv, (const char**)&strbase, &bytes, &uwot, &outSize);
#else
iconv(icv, &strbase, &bytes, &uwot, &outSize);
#endif
u8buff[len] = '\0';
std::string message(u8buff);
texts.addText(id, message);
GameStringChar* stringSrc = reinterpret_cast<GameStringChar*>(tdata+offset);
GameString string(stringSrc);
texts.addText(id, std::move(string));
}
iconv_close(icv);
}

View File

@ -5,45 +5,11 @@
#include <algorithm>
#include <cctype>
/// @todo This is very rough
int charToIndex(char g)
int charToIndex(uint16_t g)
{
if( g >= '0' && g <= '9' )
{
return 16 + (g - '0');
}
else if( g >= 'A' && g <= 'Z' )
{
return 33 + (g - 'A');
}
else if( g >= 'a' && g <= 'z' )
{
return 65 + (g - 'a');
}
switch(g)
{
default: return 0;
case '!': return 1;
case '"': return 2;
case '#': return 3;
case '$': return 4;
case '%': return 5;
case '&': return 6;
case '\'': return 7;
case '(': return 8;
case ')': return 9;
case '*': return 10;
case '+': return 11;
case ',': return 12;
case '-': return 13;
case '.': return 14;
case '/': return 15;
case ':': return 26;
case '[': return 59;
case ']': return 61;
// This is a guess.
case '@': return 91;
}
// Correct for the default font maps
/// @todo confirm for JA / RU font maps
return g - 32;
}
glm::vec4 indexToCoord(int font, int index)
@ -204,7 +170,7 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti, bool forceColour
for (size_t i = 0; i < text.length(); ++i)
{
char c = text[i];
char16_t c = text[i];
// Handle any markup changes.
if( c == '~' && text.length() > i + 1 )

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef RWENGINE_TEXTRENDERER_HPP
#define RWENGINE_TEXTRENDERER_HPP
#include <engine/GameData.hpp>
#include "OpenGLRenderer.hpp"
@ -31,7 +32,7 @@ public:
/// Font index @see TextRenderer::setFontTexture
int font;
/// Message to be displayed (including markup)
std::string text;
GameString text;
/// On screen position
glm::vec2 screenPosition;
/// font size
@ -73,3 +74,4 @@ private:
GeometryBuffer gb;
DrawBuffer db;
};
#endif

View File

@ -29,21 +29,21 @@
// Helper function to format script numbers to strings
// for use in the text printing opcodes.
std::string formatValue(const SCMOpcodeParameter& p)
GameString formatValue(const SCMOpcodeParameter& p)
{
switch (p.type) {
case TFloat16:
return std::to_string(p.real);
return GameStringUtil::fromString(std::to_string(p.real));
default:
return std::to_string(p.integerValue());
return GameStringUtil::fromString(std::to_string(p.integerValue()));
}
return "";
return {0};
}
void game_print_big(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
auto str = args.getWorld()->data->texts.text(id);
unsigned short time = args[1].integer;
unsigned short style = args[2].integer;
args.getWorld()->state->text.addText<ScreenTextType::Big>(
@ -55,7 +55,7 @@ void game_print_big(const ScriptArguments& args)
void game_print_now(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
auto str = args.getWorld()->data->texts.text(id);
int time = args[1].integer;
int flags = args[2].integer;
RW_UNUSED(flags);
@ -390,7 +390,7 @@ void game_get_runtime(const ScriptArguments& args)
void game_print_big_with_number(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str =
auto str =
ScreenText::format(
args.getWorld()->data->texts.text(id),
formatValue(args[1]));
@ -873,8 +873,8 @@ bool game_has_respray_happened(const ScriptArguments& args)
void game_display_text(const ScriptArguments& args)
{
glm::vec2 pos(args[0].real, args[1].real);
std::string str(args[2].string);
str = args.getWorld()->data->texts.text(str);
std::string id(args[2].string);
auto str = args.getWorld()->data->texts.text(id);
args.getWorld()->state->nextText.text = str;
args.getWorld()->state->nextText.position = pos;
args.getWorld()->state->texts.push_back(args.getWorld()->state->nextText);
@ -946,7 +946,7 @@ void game_print_big_with_2_numbers(const ScriptArguments& args)
int time = args[3].integerValue();
unsigned short style = args[4].integerValue();
std::string str = ScreenText::format(world->data->texts.text(id),
auto str = ScreenText::format(world->data->texts.text(id),
formatValue(args[1]),
formatValue(args[2]));
@ -1113,7 +1113,7 @@ void game_get_found_hidden_packages(const ScriptArguments& args)
void game_display_help(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
auto str = args.getWorld()->data->texts.text(id);
args.getWorld()->state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeHelp(

View File

@ -52,7 +52,6 @@ target_link_libraries(rwgame
if(MINGW)
add_definitions(-D _USE_MATH_DEFINES)
target_link_libraries(rwgame
iconv
mman)
endif()

View File

@ -76,7 +76,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
<< std::setw(0) << ":"
<< std::setw(2) << world->getMinute();
ti.text = ss.str();
ti.text = GameStringUtil::fromString(ss.str());
}
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
@ -88,7 +88,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
infoTextY += ui_textHeight;
ti.text = "$" + std::to_string(world->state->playerInfo.money);
ti.text = GameSymbols::Money + GameStringUtil::fromString(std::to_string(world->state->playerInfo.money));
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
render->text.renderText(ti);
@ -101,9 +101,9 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
{
std::stringstream ss;
ss << "@" << std::setw(3) << std::setfill('0')
ss << std::setw(3) << std::setfill('0')
<< (int)player->getCharacter()->getCurrentState().health;
ti.text = ss.str();
ti.text = GameSymbols::Heart + GameStringUtil::fromString(ss.str());
}
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
@ -116,9 +116,9 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
if (player->getCharacter()->getCurrentState().armour > 0)
{
std::stringstream ss;
ss << "[" << std::setw(3) << std::setfill('0')
ss << std::setw(3) << std::setfill('0')
<< (int)player->getCharacter()->getCurrentState().armour;
ti.text = ss.str();
ti.text = GameSymbols::Armour + GameStringUtil::fromString(ss.str());
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f - ui_armourOffset, infoTextY+1.f);
render->text.renderText(ti);
@ -128,9 +128,9 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
render->text.renderText(ti);
}
std::string s;
GameString s;
for (size_t i = 0; i < ui_maxWantedLevel; ++i) {
s += "]";
s += GameSymbols::Star;
}
ti.text = s;
ti.baseColour = ui_shadowColour;
@ -183,8 +183,9 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
if (wep->getWeaponData()->fireType != WeaponData::MELEE) {
const CharacterState& cs = player->getCharacter()->getCurrentState();
const CharacterWeaponSlot& slotInfo = cs.weapons[cs.currentWeapon];
ti.text = std::to_string(slotInfo.bulletsClip) + "-"
+ std::to_string(slotInfo.bulletsTotal);
ti.text = GameStringUtil::fromString(
std::to_string(slotInfo.bulletsClip) + "-"
+ std::to_string(slotInfo.bulletsTotal));
ti.baseColour = ui_shadowColour;
ti.font = 2;

View File

@ -7,22 +7,38 @@
#include <functional>
#include <algorithm>
/**
* Default values for menus that should match the look and feel of the original
*/
namespace MenuDefaults
{
constexpr int kFont = 1;
constexpr const char* kStartGameId = "FET_SAN";
constexpr const char* kResumeGameId = "FEM_RES";
constexpr const char* kLoadGameId = "FET_LG";
constexpr const char* kDebugId = "FEM_DBG";
constexpr const char* kOptionsId = "FET_OPT";
constexpr const char* kQuitGameId = "FET_QG";
}
class Menu
{
int font;
public:
Menu(int font)
Menu(int font = MenuDefaults::kFont)
: font(font), activeEntry(-1) {}
struct MenuEntry
{
std::string name;
GameString text;
float _size;
MenuEntry(const std::string& n, float size = 30.f) : name(n), _size(size)
MenuEntry(const GameString& n, float size = 30.f)
: text(n)
, _size(size)
{
std::transform(name.begin(), name.end(), name.begin(), toupper);
}
float getHeight() { return _size; }
@ -32,7 +48,7 @@ public:
TextRenderer::TextInfo ti;
ti.font = font;
ti.screenPosition = basis;
ti.text = name;
ti.text = text;
ti.size = getHeight();
if( ! active )
{
@ -53,10 +69,11 @@ public:
{
std::function<void(void)> callback;
Entry(const std::string& title,
Entry(const GameString& title,
std::function<void(void)> cb,
float size)
: MenuEntry(title, size), callback(cb)
: MenuEntry(title, size)
, callback(cb)
{
}
@ -68,10 +85,15 @@ public:
}
};
static std::shared_ptr<MenuEntry> lambda(const std::string& n, std::function<void (void)> callback, float size = 30.f)
static std::shared_ptr<MenuEntry> lambda(const GameString& n, std::function<void (void)> callback, float size = 30.f)
{
return std::shared_ptr<MenuEntry>(new Entry(n, callback, size));
}
static std::shared_ptr<MenuEntry> lambda(const std::string& n, std::function<void (void)> callback, float size = 30.f)
{
return lambda(GameStringUtil::fromString(n), callback, size);
}
std::vector<std::shared_ptr<MenuEntry>> entries;

View File

@ -627,7 +627,7 @@ void RWGame::renderDebugStats(float time, Renderer::ProfileInfo& worldRenderTime
}
TextRenderer::TextInfo ti;
ti.text = ss.str();
ti.text = GameStringUtil::fromString(ss.str());
ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 15.f;

View File

@ -368,7 +368,7 @@ void DebugState::draw(GameRenderer* r)
ss << "Camera Position: " << glm::to_string(_debugCam.position);
TextRenderer::TextInfo ti;
ti.text = ss.str();
ti.text = GameStringUtil::fromString(ss.str());
ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 15.f;

View File

@ -50,9 +50,10 @@ void LoadingState::handleEvent(const SDL_Event& e)
void LoadingState::draw(GameRenderer* r)
{
static auto kLoadingString = GameStringUtil::fromString("Loading...");
// Display some manner of loading screen.
TextRenderer::TextInfo ti;
ti.text = "Loading...";
ti.text = kLoadingString;
auto size = r->getRenderer()->getViewport();
ti.size = 25.f;
ti.screenPosition = glm::vec2( 50.f, size.y - ti.size - 50.f );

View File

@ -14,34 +14,39 @@ MenuState::MenuState(RWGame* game)
void MenuState::enterMainMenu()
{
Menu *m = new Menu(2);
auto data = game->getGameData();
auto& t = data->texts;
Menu *m = new Menu;
m->offset = glm::vec2(200.f, 200.f);
m->addEntry(Menu::lambda("Start", [=] { StateManager::get().enter(new IngameState(game)); }));
m->addEntry(Menu::lambda("Load Game", [=] { enterLoadMenu(); }));
m->addEntry(Menu::lambda("Test", [=] { StateManager::get().enter(new IngameState(game, true, "test")); }));
m->addEntry(Menu::lambda("Options", [] { RW_UNIMPLEMENTED("Options Menu"); }));
m->addEntry(Menu::lambda("Exit", [] { StateManager::get().clear(); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kStartGameId), [=] { StateManager::get().enter(new IngameState(game)); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kLoadGameId), [=] { enterLoadMenu(); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kDebugId), [=] { StateManager::get().enter(new IngameState(game, true, "test")); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId), [] { RW_UNIMPLEMENTED("Options Menu"); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kQuitGameId), [] { StateManager::get().clear(); }));
this->enterMenu(m);
}
void MenuState::enterLoadMenu()
{
Menu *m = new Menu(2);
Menu *m = new Menu;
m->offset = glm::vec2(20.f, 30.f);
m->addEntry(Menu::lambda("Back", [=] { enterMainMenu(); }));
m->addEntry(Menu::lambda("BACK", [=] { enterMainMenu(); }));
auto saves = SaveGame::getAllSaveGameInfo();
for(SaveGameInfo& save : saves) {
if (save.valid) {
std::stringstream ss;
ss << save.basicState.saveTime.year << "/" << save.basicState.saveTime.month << "/" << save.basicState.saveTime.day
<< " " << save.basicState.saveTime.hour << ":" << save.basicState.saveTime.minute << " " << save.basicState.saveName;
m->addEntry(Menu::lambda(ss.str(), [=] {
ss << save.basicState.saveTime.year << " " << save.basicState.saveTime.month << " " << save.basicState.saveTime.day
<< " " << save.basicState.saveTime.hour << ":" << save.basicState.saveTime.minute << " ";
auto name = GameStringUtil::fromString(ss.str());
name += save.basicState.saveName;
m->addEntry(Menu::lambda(name, [=] {
StateManager::get().enter(new IngameState(game, false));
game->loadGame(save.savePath);
}, 20.f));
}
else {
m->addEntry(Menu::lambda("Corrupt", [=] { }));
m->addEntry(Menu::lambda("CORRUPT", [=] { }));
}
}
this->enterMenu(m);

View File

@ -6,11 +6,14 @@
PauseState::PauseState(RWGame* game)
: State(game)
{
Menu *m = new Menu(2);
auto data = game->getGameData();
auto& t = data->texts;
Menu *m = new Menu;
m->offset = glm::vec2( 200.f, 200.f );
m->addEntry(Menu::lambda("Continue", [] { StateManager::get().exit(); }));
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));
m->addEntry(Menu::lambda("Exit", [] { StateManager::get().clear(); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kResumeGameId), [] { StateManager::get().exit(); }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId), [] { std::cout << "Options" << std::endl; }));
m->addEntry(Menu::lambda(t.text(MenuDefaults::kQuitGameId), [] { StateManager::get().clear(); }));
this->enterMenu(m);
}

View File

@ -42,6 +42,18 @@ struct print_log_value<std::nullptr_t> {
};
}} BOOST_NS_MAGIC_CLOSING
namespace boost { namespace test_tools { BOOST_NS_MAGIC
template<>
struct print_log_value<GameString> {
void operator()( std::ostream& s , GameString const& v )
{
for (GameString::size_type i = 0u; i<v.size(); ++i) {
s << (char)v[i];
}
}
};
}} BOOST_NS_MAGIC_CLOSING
#undef BOOST_NS_MAGIC
#undef BOOST_NS_MAGIC_CLOSING

View File

@ -4,6 +4,8 @@
#include <loaders/LoaderGXT.hpp>
#include <engine/ScreenText.hpp>
#define T(x) GameStringUtil::fromString(x)
BOOST_AUTO_TEST_SUITE(TextTests)
#if RW_TEST_WITH_DATA
@ -18,7 +20,7 @@ BOOST_AUTO_TEST_CASE(load_test)
loader.load( texts, d );
BOOST_CHECK_EQUAL( texts.text("1008"), "BUSTED" );
BOOST_CHECK_EQUAL( texts.text("1008"), T("BUSTED") );
}
}
@ -28,12 +30,12 @@ BOOST_AUTO_TEST_CASE(big_test)
{
auto big = ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
1,
5000);
BOOST_CHECK_EQUAL("TEST_1", big.id);
BOOST_CHECK_EQUAL("Test String", big.text);
BOOST_CHECK_EQUAL(T("Test String"), big.text);
BOOST_CHECK_EQUAL(5000, big.durationMS);
BOOST_CHECK_EQUAL(0, big.displayedMS);
BOOST_CHECK_EQUAL(1, big.alignment);
@ -42,11 +44,11 @@ BOOST_AUTO_TEST_CASE(big_test)
{
auto big = ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
2,
5000);
BOOST_CHECK_EQUAL("Test String", big.text);
BOOST_CHECK_EQUAL(T("Test String"), big.text);
BOOST_CHECK_EQUAL(5000, big.durationMS);
BOOST_CHECK_EQUAL(0, big.displayedMS);
BOOST_CHECK_EQUAL(2, big.alignment);
@ -58,9 +60,9 @@ BOOST_AUTO_TEST_CASE(help_test)
{
auto help = ScreenTextEntry::makeHelp(
"TEST_1",
"Test Help");
T("Test Help"));
BOOST_CHECK_EQUAL("Test Help", help.text);
BOOST_CHECK_EQUAL(T("Test Help"), help.text);
BOOST_CHECK_EQUAL(5000, help.durationMS);
BOOST_CHECK_EQUAL(0, help.displayedMS);
BOOST_CHECK_EQUAL(18, help.size);
@ -75,17 +77,17 @@ BOOST_AUTO_TEST_CASE(queue_test)
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
2,
5000));
st.addText<ScreenTextType::HighPriority>(
ScreenTextEntry::makeHighPriority(
"TEST_1",
"Test String",
T("Test String"),
5000));
BOOST_REQUIRE(st.getText<ScreenTextType::Big>().size() == 1);
BOOST_CHECK_EQUAL("Test String", st.getText<ScreenTextType::Big>()[0].text);
BOOST_CHECK_EQUAL(T("Test String"), st.getText<ScreenTextType::Big>()[0].text);
BOOST_CHECK_EQUAL(5000, st.getText<ScreenTextType::Big>()[0].durationMS);
BOOST_CHECK_EQUAL(0, st.getText<ScreenTextType::Big>()[0].displayedMS);
@ -104,7 +106,7 @@ BOOST_AUTO_TEST_CASE(clear_test)
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
2,
5000));
@ -118,21 +120,21 @@ BOOST_AUTO_TEST_CASE(clear_test)
BOOST_AUTO_TEST_CASE(format_test)
{
// Test formating of string codes into strings.
const auto codeStr1 = "Hello ~1~ world";
const auto codeStr2 = "~1~Hello ~1~ world~1~";
const auto codeStr3 = "Hello world~1~";
const auto codeStr1 = T("Hello ~1~ world");
const auto codeStr2 = T("~1~Hello ~1~ world~1~");
const auto codeStr3 = T("Hello world~1~");
auto f1 = ScreenText::format(codeStr1, "r");
BOOST_CHECK_EQUAL(f1, "Hello r world");
auto f1 = ScreenText::format(codeStr1, T("r"));
BOOST_CHECK_EQUAL(f1, T("Hello r world"));
auto f2 = ScreenText::format(codeStr2, "k", "h");
BOOST_CHECK_EQUAL(f2, "kHello h world~1~");
auto f2 = ScreenText::format(codeStr2, T("k"), T("h"));
BOOST_CHECK_EQUAL(f2, T("kHello h world~1~"));
auto f3 = ScreenText::format(codeStr3, "x");
BOOST_CHECK_EQUAL(f3, "Hello worldx");
auto f3 = ScreenText::format(codeStr3, T("x"));
BOOST_CHECK_EQUAL(f3, T("Hello worldx"));
auto f4 = ScreenText::format(codeStr3, "x", "k");
BOOST_CHECK_EQUAL(f4, "Hello worldx");
auto f4 = ScreenText::format(codeStr3, T("x"), T("k"));
BOOST_CHECK_EQUAL(f4, T("Hello worldx"));
}
BOOST_AUTO_TEST_CASE(format_remove)
@ -143,21 +145,21 @@ BOOST_AUTO_TEST_CASE(format_remove)
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_2",
"Test String",
T("Test String"),
2,
5000));
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
2,
5000));
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
T("Test String"),
2,
5000));