1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 06:52:34 +02:00

Overhaul script text display with new text logic system.

Logic for displaying text now belongs in ScreenText, which will
handle type specific text display.
This commit is contained in:
Daniel Evans 2016-05-07 18:29:08 +01:00
parent e7faa60926
commit 5721333fb0
13 changed files with 582 additions and 155 deletions

View File

@ -7,6 +7,7 @@
#include <map>
#include <vector>
#include <objects/ObjectTypes.hpp>
#include <engine/ScreenText.hpp>
class GameWorld;
class GameObject;
@ -159,42 +160,6 @@ struct TextDisplayData
glm::vec4 colourBG;
};
struct OnscreenText
{
std::string id;
std::string osTextString;
float osTextStart;
float osTextTime;
unsigned short osTextStyle;
std::string osTextVar;
enum /*TextStyle*/
{
/// Used for subtitles
HighPriority = 0,
/// Mission completed message
CenterBig = 1,
/// Right aligned mission names
MissionName = 2,
/// Help text (top left, black background)
Help = 12
};
OnscreenText(const std::string& id,
const std::string& string,
float start,
float time,
unsigned short style,
const std::string& var = "")
: id (id)
, osTextString(string)
, osTextStart(start)
, osTextTime(time)
, osTextStyle(style)
, osTextVar(var)
{ }
};
/**
* Stores information about where the game can generate vehicles.
*/
@ -353,11 +318,9 @@ struct GameState
std::map<unsigned short, std::string> specialCharacters;
std::map<unsigned short, std::string> specialModels;
/**
* Stores long-lasting on screen messages
*/
std::vector<OnscreenText> text;
/// Handles on screen text behaviour
ScreenText text;
TextDisplayData nextText;
/**
* Stores temporary, one-tick messages

View File

@ -0,0 +1,160 @@
#ifndef _RWENGINE_SCREENTEXT_HPP_
#define _RWENGINE_SCREENTEXT_HPP_
#include <rw/types.hpp>
#include <string>
#include <vector>
#include <array>
#include <algorithm>
#include <utility>
enum class ScreenTextType
{
/// Big text will be rendered according to the proscribed style.
/// Adding a 2nd big text will cause the first to terminate.
Big = 0,
/// See Big, will wait for any Big text to finish.
BigLowPriority = 1,
/// Will be cleared by the clear help opcode
Help = 2,
/// Automatically cleared after each tick, for generic text.
Immediate = 3,
/// High priority cutscene text
HighPriority = 4,
///
_Count = 5
};
constexpr unsigned int ScreenTypeTextCount = static_cast<unsigned int>(ScreenTextType::_Count);
/**
* @brief The ScreenTextEntry struct
*
* Text string and fading information.
*/
struct ScreenTextEntry
{
/// After processing numbers
std::string text;
/// in the virtual 640x480 screen space
glm::vec2 position;
/// Font number
int font;
/// Font size
int size;
/// Background colour (or, if a == 0, shadow offset)
glm::u8vec4 colourBG;
/// Foreground colour
glm::u8vec3 colourFG;
/// Alignment (left = 0, center = 1, right = 2)
unsigned char alignment;
/// Onscreen duration
int durationMS;
/// The amount of time onscreen so far
int displayedMS;
/// Wrap width
int wrapX;
/// ID used to reference the text.
std::string id;
static ScreenTextEntry makeBig(const std::string& id,
const std::string& str,
int style,
int durationMS);
static ScreenTextEntry makeHighPriority(const std::string& id,
const std::string& str,
int durationMS);
static ScreenTextEntry makeHelp(const std::string& id,
const std::string& str);
};
/**
* @brief The ScreenText class
*
* Logic for on-screen game text rendering
*
* There are 4 text pool types:
* - Big Text
* - (low priority) Big Text
* - Help Text
* - Immediate Text
*
* Only one Big Text can be drawn at a time, adding a second will
* cause the first to end prematurely. Low priority big text will
* only display if there is no regular big text.
*
* Help text.
*
* Immediate text is only rendered once, the text is removed at the
* start of each tick and must be re-added to keep it on screen.
*/
class ScreenText
{
using EntryList =
std::vector<ScreenTextEntry>;
using EntryQueues =
std::array<EntryList, ScreenTypeTextCount>;
public:
///
/// \brief tick Apply display and fading rules to the text
/// \param dt
///
void tick(float dt);
template <ScreenTextType Q, class... Args>
void addText(Args&&...args)
{
static_assert(static_cast<size_t>(Q) < ScreenTypeTextCount, "Queue out of range");
m_textQueues[static_cast<size_t>(Q)].emplace_back(std::forward<Args...>(args...));
}
template <ScreenTextType Q>
const EntryList& getText() const
{
static_assert(static_cast<size_t>(Q) < ScreenTypeTextCount, "Queue out of range");
return m_textQueues[static_cast<size_t>(Q)];
}
template <ScreenTextType Q>
void clear()
{
static_assert(static_cast<size_t>(Q) < ScreenTypeTextCount, "Queue out of range");
m_textQueues[static_cast<size_t>(Q)].clear();
}
template <ScreenTextType Q>
void remove(const std::string& id)
{
static_assert(static_cast<size_t>(Q) < ScreenTypeTextCount, "Queue out of range");
auto& list = m_textQueues[static_cast<size_t>(Q)];
list.erase(
std::remove_if(list.begin(), list.end(),
[&id](const ScreenTextEntry& e){ return e.id == id; }),
list.end()
);
}
const EntryQueues& getAllText() const
{
return m_textQueues;
}
template<class...Args>
static std::string format(std::string format, Args&&...args)
{
const std::array<std::string, 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())
{
format = format.substr(0, x) + vals[val++] + format.substr(x + 3);
}
return format;
}
private:
EntryQueues m_textQueues;
};
#endif

View File

@ -32,17 +32,19 @@ public:
int font;
/// Message to be displayed (including markup)
std::string text;
/// Extra text parameter
std::string varText;
/// On screen position
glm::vec2 screenPosition;
/// font size
float size;
/// Base colour
glm::vec3 baseColour;
glm::u8vec3 baseColour;
/// Background colour
glm::u8vec4 backgroundColour;
/// Horizontal Alignment
TextAlignemnt align;
/// Wrap width
int wrapX;
TextInfo();
};
@ -70,4 +72,4 @@ private:
GeometryBuffer gb;
DrawBuffer db;
};
};

View File

@ -0,0 +1,112 @@
#include <engine/ScreenText.hpp>
void ScreenText::tick(float dt)
{
int millis = dt * 1000;
// Remove all the immedate text
m_textQueues[static_cast<size_t>(ScreenTextType::Immediate)].clear();
for (unsigned int t = 0; t < m_textQueues.size(); ++t)
{
for (unsigned int i = 0; i < m_textQueues[t].size();)
{
auto& big = m_textQueues[t][i];
big.displayedMS += millis;
if (big.displayedMS >= big.durationMS)
{
m_textQueues[t].erase(m_textQueues[t].begin()+i);
}
else
{
++i;
}
}
}
}
ScreenTextEntry ScreenTextEntry::makeBig(const std::string& id, const std::string& str, int style, int durationMS)
{
switch(style) {
case 1:
return {
str,
{320.f, 400.f},
1,
50,
{ 3, 3, 0, 0},
{20, 20, 200},
1,
durationMS,
0,
600,
id
};
case 2:
return {
str,
{620.f, 380.f},
1,
30,
{ 3, 3, 0, 0},
{205, 162, 7},
2,
durationMS,
0,
600,
id
};
default:
RW_ERROR("Unhandled text style");
break;
}
return {
"Error",
{320.f, 400.f},
1,
50,
{20, 20, 0, 0},
{20, 20, 200},
1,
durationMS,
0,
600,
id
};
}
ScreenTextEntry ScreenTextEntry::makeHighPriority(const std::string& id, const std::string& str, int durationMS)
{
return {
str,
{320.f, 420.f},
2,
18,
{0, 0, 0, 0},
{255, 255, 255},
1,
durationMS,
0,
50,
id
};
}
ScreenTextEntry ScreenTextEntry::makeHelp(const std::string& id, const std::string& str)
{
return {
str,
{20.f, 20.f},
2,
18,
{ 0, 0, 0, 255},
{255, 255, 255},
0,
5000,
0,
35,
id
};
}

View File

@ -187,13 +187,17 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
glm::vec2 coord( 0.f, 0.f );
glm::vec2 alignment = ti.screenPosition;
// We should track real size not just chars.
auto lineLength = 0;
glm::vec2 ss( ti.size );
glm::vec3 colour = ti.baseColour;
glm::vec3 colour = glm::vec3(ti.baseColour) * (1/255.f);
glm::vec4 colourBG = glm::vec4(ti.backgroundColour) * (1/255.f);
std::vector<TextVertex> geo;
float maxWidth = 0.f;
float maxHeight = 0.f;
auto text = ti.text;
@ -206,11 +210,6 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
{
switch( text[i+1] )
{
case '1':
case 'a':
text.erase(text.begin()+i, text.begin()+i+3);
text.insert(i, ti.varText);
break;
case 'k': {
text.erase(text.begin()+i, text.begin()+i+3);
// Extract the key name from the /next/ markup
@ -218,7 +217,7 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
auto keyname = text.substr(i+1, keyend-i-1);
// Since we don't have a key map yet, just print out the name
text.erase(text.begin()+i, text.begin()+keyend);
text.insert(i, "("+keyname+")");
text.insert(i, keyname);
} break;
case 'w':
text.erase(text.begin()+i, text.begin()+i+3);
@ -238,6 +237,26 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
{
continue;
}
// If we're not at the start of the column, check if the current word
// will need to be wrapped
if (ti.wrapX > 0 && coord.x > 0.f && !std::isspace(c))
{
auto wend = std::find_if(std::begin(text)+i,
std::end(text),
[](char x) { return std::isspace(x); });
if (wend != std::end(text))
{
auto word = std::distance(std::begin(text)+i, wend);
if (lineLength + word >= ti.wrapX)
{
coord.x = 0;
coord.y += ss.y;
maxHeight = coord.y + ss.y;
lineLength = 0;
}
}
}
auto& data = glyphData[glyph];
auto tex = indexToCoord(ti.font, glyph);
@ -248,11 +267,14 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
// Handle special chars.
if( c == '\n' )
{
coord.x = ti.screenPosition.x;
coord.x = 0.f;
coord.y += ss.y;
maxHeight = coord.y + ss.y;
lineLength = 0;
continue;
}
lineLength ++;
glm::vec2 p = coord;
coord.x += ss.x;
maxWidth = std::max(coord.x, maxWidth);
@ -277,6 +299,17 @@ void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
}
alignment.y -= ti.size * 0.2f;
// If we need to, draw the background.
if (colourBG.a > 0.f)
{
renderer->drawColour(
colourBG,
glm::vec4(
ti.screenPosition - (ss/3.f),
glm::vec2(maxWidth, maxHeight)+(ss/2.f)));
}
renderer->getRenderer()->setUniform(textShader, "proj", renderer->getRenderer()->get2DProjection());
renderer->getRenderer()->setUniformTexture(textShader, "fontTexture", 0);

View File

@ -19,45 +19,56 @@
#include <objects/CutsceneObject.hpp>
#include <objects/PickupObject.hpp>
#include <core/Logger.hpp>
#include <engine/ScreenText.hpp>
#include <glm/gtx/string_cast.hpp>
#include <iostream>
#include <algorithm>
// Helper function to format script numbers to strings
// for use in the text printing opcodes.
std::string formatValue(const SCMOpcodeParameter& p)
{
switch (p.type) {
case TFloat16:
return std::to_string(p.real);
default:
return std::to_string(p.integerValue());
}
return "";
}
void game_print_big(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
unsigned short time = args[1].integer;
unsigned short style = args[2].integer;
args.getWorld()->state->text.emplace_back(
id,
str,
args.getWorld()->getGameTime(),
args[1].integer / 1000.f,
style
);
args.getWorld()->state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
id, str, style, time
));
}
void game_print_now(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
int time = args[1].integer;
int flags = args[2].integer;
RW_UNUSED(flags);
RW_UNIMPLEMENTED("game_print_now(): flags");
args.getWorld()->state->text.emplace_back(
id,
str,
args.getWorld()->getGameTime(),
args[1].integer / 1000.f,
0
);
RW_UNIMPLEMENTED("Unclear what style should be used");
args.getWorld()->state->text.addText<ScreenTextType::HighPriority>(
ScreenTextEntry::makeHighPriority(
id, str, time
));
}
void game_clear_prints(const ScriptArguments& args)
{
args.getWorld()->state->text.clear();
args.getWorld()->state->text.clear<ScreenTextType::Big>();
}
void game_get_time(const ScriptArguments& args)
@ -326,20 +337,15 @@ 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 = args.getWorld()->data->texts.text(id);
int number = args[1].integer;
std::string str =
ScreenText::format(
args.getWorld()->data->texts.text(id),
formatValue(args[1]));
unsigned short style = args[3].integer;
args.getWorld()->state->text.push_back({
id,
str,
args.getWorld()->getGameTime(),
args[2].integer / 1000.f,
style,
std::to_string(number),
});
args.getWorld()->state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
id, str, style, 5000
));
}
void game_disable_roads(const ScriptArguments& args)
@ -821,17 +827,7 @@ void game_clear_print(const ScriptArguments& args)
{
std::string id(args[0].string);
for( size_t i = 0; i < args.getWorld()->state->text.size(); )
{
if( args.getWorld()->state->text[i].id == id )
{
args.getWorld()->state->text.erase(args.getWorld()->state->text.begin() + i);
}
else
{
i++;
}
}
args.getWorld()->state->text.remove<ScreenTextType::Big>(id);
}
bool game_did_game_save(const ScriptArguments& args)
@ -851,30 +847,16 @@ void game_display_help(const ScriptArguments& args)
{
std::string id(args[0].string);
std::string str = args.getWorld()->data->texts.text(id);
unsigned short style = OnscreenText::Help;
args.getWorld()->state->text.push_back({
id,
str,
args.getWorld()->getGameTime(),
2.5f,
style
});
args.getWorld()->state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeHelp(
id, str
));
}
void game_clear_help(const ScriptArguments& args)
{
for( size_t i = 0; i < args.getWorld()->state->text.size(); )
{
auto& texts = args.getWorld()->state->text;
if( texts[i].osTextStyle == OnscreenText::Help )
{
texts.erase(texts.begin() + i);
}
else
{
i++;
}
}
args.getWorld()->state->text.clear<ScreenTextType::Help>();
}
bool game_can_player_move(const ScriptArguments& args)

View File

@ -16,11 +16,11 @@ constexpr size_t ui_ammoSize = 14;
constexpr size_t ui_ammoHeight = 16;
constexpr size_t ui_armourOffset = ui_textSize * 3;
constexpr size_t ui_maxWantedLevel = 6;
#define RGB_COLOR(r,g,b) r/255.f, g/255.f, b/255.f
const glm::vec3 ui_timeColour(RGB_COLOR(196, 165, 119));
const glm::vec3 ui_moneyColour(RGB_COLOR(89, 113, 147));
const glm::vec3 ui_healthColour(RGB_COLOR(187, 102, 47));
const glm::vec3 ui_armourColour(RGB_COLOR(123, 136, 93));
const glm::u8vec3 ui_timeColour(196, 165, 119);
const glm::u8vec3 ui_moneyColour(89, 113, 147);
const glm::u8vec3 ui_healthColour(187, 102, 47);
const glm::u8vec3 ui_armourColour(123, 136, 93);
const glm::u8vec3 ui_shadowColour(0, 0, 0);
const float ui_mapSize = 150.f;
const float ui_worldSizeMin = 200.f;
const float ui_worldSizeMax = 300.f;
@ -75,7 +75,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
ti.text = ss.str();
}
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
render->text.renderText(ti);
@ -86,7 +86,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
infoTextY += ui_textHeight;
ti.text = "$" + std::to_string(world->state->playerInfo.money);
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
render->text.renderText(ti);
@ -102,7 +102,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
<< (int)player->getCharacter()->getCurrentState().health;
ti.text = ss.str();
}
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f, infoTextY+1.f);
render->text.renderText(ti);
@ -116,7 +116,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
ss << "[" << std::setw(3) << std::setfill('0')
<< (int)player->getCharacter()->getCurrentState().armour;
ti.text = ss.str();
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(infoTextX + 1.f - ui_armourOffset, infoTextY+1.f);
render->text.renderText(ti);
@ -130,7 +130,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
s += "]";
}
ti.text = s;
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.screenPosition = glm::vec2(wantedX + 1.f, wantedY + 1.f);
render->text.renderText(ti);
@ -183,7 +183,7 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
ti.text = std::to_string(slotInfo.bulletsClip) + "-"
+ std::to_string(slotInfo.bulletsTotal);
ti.baseColour = glm::vec3(0.f, 0.f, 0.f);
ti.baseColour = ui_shadowColour;
ti.font = 2;
ti.size = ui_ammoSize;
ti.align = TextRenderer::TextInfo::Center;
@ -204,13 +204,58 @@ void drawHUD(ViewCamera& currentView, PlayerController* player, GameWorld* world
void drawOnScreenText(GameWorld* world, GameRenderer* renderer)
{
const glm::ivec2& vp = renderer->getRenderer()->getViewport();
const auto vp = glm::vec2(renderer->getRenderer()->getViewport());
TextRenderer::TextInfo ti;
ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 20.f;
auto& alltext = world->state->text.getAllText();
for(auto& l : alltext)
{
for (auto& t : l)
{
ti.size = t.size;
ti.font = t.font;
ti.text = t.text;
ti.wrapX = t.wrapX;
ti.screenPosition = (t.position/glm::vec2(640.f, 480.f)) * vp;
switch(t.alignment) {
case 0:
ti.align = TextRenderer::TextInfo::Left;
break;
case 1:
ti.align = TextRenderer::TextInfo::Center;
break;
case 2:
ti.align = TextRenderer::TextInfo::Right;
break;
}
// Check for the background type
if (t.colourBG.a == 0)
{
ti.baseColour = glm::vec3(0.f);
ti.screenPosition += glm::vec2(t.colourBG.x, t.colourBG.y);
ti.backgroundColour = {0, 0, 0, 0};
renderer->text.renderText(ti);
ti.screenPosition -= glm::vec2(t.colourBG.x, t.colourBG.y);
}
else if(t.colourBG.a > 0)
{
ti.backgroundColour = t.colourBG;
}
ti.baseColour = t.colourFG;
renderer->text.renderText(ti);
}
}
#if 0
for(OnscreenText& t : world->state->text)
{
glm::vec2 shadowOffset( 0, 0 );
@ -271,15 +316,5 @@ void drawOnScreenText(GameWorld* world, GameRenderer* renderer)
renderer->text.renderText(ti);
}
for(auto& t : world->state->texts) {
ti.font = 2;
ti.screenPosition = t.position / glm::vec2(640, 480);
ti.screenPosition *= vp;
ti.baseColour = glm::vec3(t.colourFG);
ti.size = 20.f;
ti.text = t.text;
renderer->text.renderText(ti);
}
#endif
}

View File

@ -36,11 +36,11 @@ public:
ti.size = getHeight();
if( ! active )
{
ti.baseColour = glm::vec3(1.f, 1.f, 1.f);
ti.baseColour = glm::u8vec3(255);
}
else
{
ti.baseColour = glm::vec3(1.f, 1.f, 0.f);
ti.baseColour = glm::u8vec3(255, 255, 0);
}
r->text.renderText(ti);
basis.y += getHeight();

View File

@ -438,20 +438,8 @@ void RWGame::tick(float dt)
}
world->destroyQueuedObjects();
state->texts.clear();
for( int i = 0; i < state->text.size(); )
{
auto& text = state->text[i];
if( world->getGameTime() > text.osTextStart + text.osTextTime )
{
state->text.erase(state->text.begin() + i);
}
else
{
i++;
}
}
state->text.tick(dt);
world->dynamicsWorld->stepSimulation(dt, 2, dt);
@ -656,6 +644,7 @@ void RWGame::renderDebugStats(float time, Renderer::ProfileInfo& worldRenderTime
ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 15.f;
ti.baseColour = glm::u8vec3(255);
renderer->text.renderText(ti);
/*while( engine->log.size() > 0 && engine->log.front().time + 10.f < engine->gameTime ) {
@ -752,6 +741,7 @@ void RWGame::renderProfile()
ti.align = TextRenderer::TextInfo::Left;
ti.font = 2;
ti.size = lineHeight - 2.f;
ti.baseColour = glm::u8vec3(255);
std::function<void(const perf::ProfileEntry&,int)> renderEntry = [&](const perf::ProfileEntry& entry, int depth)
{
int g = 0;

View File

@ -195,6 +195,7 @@ void DebugState::draw(GameRenderer* r)
ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 15.f;
ti.baseColour = glm::u8vec3(255);
r->text.renderText(ti);
State::draw(r);

View File

@ -55,5 +55,6 @@ void LoadingState::draw(GameRenderer* r)
ti.size = 25.f;
ti.screenPosition = glm::vec2( 50.f, size.y - ti.size - 50.f );
ti.font = 2;
ti.baseColour = glm::u8vec3(255);
r->text.renderText(ti);
}

View File

@ -44,7 +44,8 @@ include_directories("${CMAKE_SOURCE_DIR}/tests")
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
include_directories("${CMAKE_SOURCE_DIR}/rwengine/include" "${CMAKE_SOURCE_DIR}/rwgame" ${BULLET_INCLUDE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/rwengine/include" "${CMAKE_SOURCE_DIR}/rwgame")
include_directories(${BULLET_INCLUDE_DIR} SYSTEM)
target_link_libraries(run_tests
rwengine

View File

@ -2,6 +2,7 @@
#include "test_globals.hpp"
#include <data/GameTexts.hpp>
#include <loaders/LoaderGXT.hpp>
#include <engine/ScreenText.hpp>
BOOST_AUTO_TEST_SUITE(TextTests)
@ -20,4 +21,150 @@ BOOST_AUTO_TEST_CASE(load_test)
}
}
BOOST_AUTO_TEST_CASE(big_test)
{
// Check that makeBig creates a text in the right place
{
auto big = ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
1,
5000);
BOOST_CHECK_EQUAL("TEST_1", big.id);
BOOST_CHECK_EQUAL("Test String", big.text);
BOOST_CHECK_EQUAL(5000, big.durationMS);
BOOST_CHECK_EQUAL(0, big.displayedMS);
BOOST_CHECK_EQUAL(1, big.alignment);
BOOST_CHECK_EQUAL(50, big.size);
}
{
auto big = ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
2,
5000);
BOOST_CHECK_EQUAL("Test String", big.text);
BOOST_CHECK_EQUAL(5000, big.durationMS);
BOOST_CHECK_EQUAL(0, big.displayedMS);
BOOST_CHECK_EQUAL(2, big.alignment);
BOOST_CHECK_EQUAL(30, big.size);
}
}
BOOST_AUTO_TEST_CASE(help_test)
{
auto help = ScreenTextEntry::makeHelp(
"TEST_1",
"Test Help");
BOOST_CHECK_EQUAL("Test Help", help.text);
BOOST_CHECK_EQUAL(5000, help.durationMS);
BOOST_CHECK_EQUAL(0, help.displayedMS);
BOOST_CHECK_EQUAL(18, help.size);
}
BOOST_AUTO_TEST_CASE(queue_test)
{
// Check that creating a test puts it on the right queue.
ScreenText st;
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
2,
5000));
st.addText<ScreenTextType::HighPriority>(
ScreenTextEntry::makeHighPriority(
"TEST_1",
"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(5000, st.getText<ScreenTextType::Big>()[0].durationMS);
BOOST_CHECK_EQUAL(0, st.getText<ScreenTextType::Big>()[0].displayedMS);
BOOST_CHECK_EQUAL(1, st.getText<ScreenTextType::HighPriority>().size());
st.tick(6.f);
BOOST_CHECK_EQUAL(0, st.getText<ScreenTextType::Big>().size());
BOOST_CHECK_EQUAL(0, st.getText<ScreenTextType::HighPriority>().size());
}
BOOST_AUTO_TEST_CASE(clear_test)
{
ScreenText st;
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
2,
5000));
BOOST_CHECK_EQUAL(1, st.getText<ScreenTextType::Big>().size());
st.clear<ScreenTextType::Big>();
BOOST_CHECK_EQUAL(0, st.getText<ScreenTextType::Big>().size());
}
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~";
auto f1 = ScreenText::format(codeStr1, "r");
BOOST_CHECK_EQUAL(f1, "Hello r world");
auto f2 = ScreenText::format(codeStr2, "k", "h");
BOOST_CHECK_EQUAL(f2, "kHello h world~1~");
auto f3 = ScreenText::format(codeStr3, "x");
BOOST_CHECK_EQUAL(f3, "Hello worldx");
auto f4 = ScreenText::format(codeStr3, "x", "k");
BOOST_CHECK_EQUAL(f4, "Hello worldx");
}
BOOST_AUTO_TEST_CASE(format_remove)
{
// Test removing an identified string from the list
ScreenText st;
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_2",
"Test String",
2,
5000));
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
2,
5000));
st.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(
"TEST_1",
"Test String",
2,
5000));
BOOST_CHECK_EQUAL(3, st.getText<ScreenTextType::Big>().size());
st.remove<ScreenTextType::Big>("TEST_1");
BOOST_CHECK_EQUAL(1, st.getText<ScreenTextType::Big>().size());
}
BOOST_AUTO_TEST_SUITE_END()