mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 03:42:48 +01: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:
parent
e7faa60926
commit
5721333fb0
@ -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
|
||||
|
160
rwengine/include/engine/ScreenText.hpp
Normal file
160
rwengine/include/engine/ScreenText.hpp
Normal 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
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
112
rwengine/src/engine/ScreenText.cpp
Normal file
112
rwengine/src/engine/ScreenText.cpp
Normal 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
|
||||
};
|
||||
}
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user