2013-08-29 11:45:22 +09:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 yvt
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
This file is part of OpenSpades.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
OpenSpades is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
OpenSpades is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-29 11:45:22 +09:00
|
|
|
*/
|
2013-08-18 16:18:06 +09:00
|
|
|
|
2016-12-03 19:04:58 +09:00
|
|
|
#include <cctype>
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "ChatWindow.h"
|
|
|
|
#include "Client.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include "IFont.h"
|
|
|
|
#include "IRenderer.h"
|
2013-08-18 16:18:06 +09:00
|
|
|
#include "World.h"
|
2016-12-03 18:23:47 +09:00
|
|
|
#include <Core/Debug.h>
|
2013-11-02 19:00:50 +01:00
|
|
|
#include <Core/Exception.h>
|
2013-12-10 01:30:15 +09:00
|
|
|
#include <Core/Math.h>
|
2014-03-31 22:11:09 +09:00
|
|
|
#include <Core/Settings.h>
|
2013-08-18 16:18:06 +09:00
|
|
|
|
2016-11-19 21:03:51 +09:00
|
|
|
DEFINE_SPADES_SETTING(cg_chatHeight, "30");
|
|
|
|
DEFINE_SPADES_SETTING(cg_killfeedHeight, "26");
|
2013-11-02 19:00:50 +01:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
namespace spades {
|
2016-12-03 18:23:47 +09:00
|
|
|
namespace client {
|
|
|
|
|
|
|
|
ChatWindow::ChatWindow(Client *cli, IRenderer *rend, IFont *fnt, bool killfeed)
|
|
|
|
: client(cli), renderer(rend), font(fnt), killfeed(killfeed) {
|
2013-08-18 16:18:06 +09:00
|
|
|
firstY = 0.f;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
ChatWindow::~ChatWindow() {}
|
|
|
|
|
|
|
|
float ChatWindow::GetWidth() { return renderer->ScreenWidth() / 2; }
|
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
float ChatWindow::GetNormalHeight() {
|
2016-12-03 18:23:47 +09:00
|
|
|
float prop = killfeed ? (float)cg_killfeedHeight : (float)cg_chatHeight;
|
|
|
|
|
2014-03-31 22:11:09 +09:00
|
|
|
return renderer->ScreenHeight() * prop * 0.01f;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2013-11-03 00:52:54 +01:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
float ChatWindow::GetBufferHeight() {
|
|
|
|
if (killfeed) {
|
|
|
|
return GetNormalHeight();
|
|
|
|
} else {
|
|
|
|
// Take up the remaining height
|
|
|
|
float prop = 100.0f - (float)cg_killfeedHeight;
|
|
|
|
|
|
|
|
return renderer->ScreenHeight() * prop * 0.01f - 100.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
float ChatWindow::GetLineHeight() { return 20.f; }
|
|
|
|
|
|
|
|
static bool isWordChar(char c) { return isalnum(c) || c == '\''; }
|
|
|
|
|
|
|
|
std::string ChatWindow::killImage(int type, int weapon) {
|
2013-11-03 00:52:54 +01:00
|
|
|
std::string tmp = "xx";
|
|
|
|
tmp[0] = 7;
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (type) {
|
2013-11-03 00:52:54 +01:00
|
|
|
case KillTypeWeapon:
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (weapon) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2: tmp[1] = 'a' + weapon; break;
|
|
|
|
default: return "";
|
2013-11-03 00:52:54 +01:00
|
|
|
}
|
2013-11-03 19:07:06 +01:00
|
|
|
break;
|
2016-12-03 18:23:47 +09:00
|
|
|
case KillTypeHeadshot:
|
|
|
|
case KillTypeMelee:
|
|
|
|
case KillTypeGrenade:
|
|
|
|
case KillTypeFall:
|
|
|
|
case KillTypeTeamChange:
|
|
|
|
case KillTypeClassChange: tmp[1] = 'a' + 2 + type; break;
|
|
|
|
default: return "";
|
2013-11-03 00:52:54 +01:00
|
|
|
}
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
void ChatWindow::AddMessage(const std::string &msg) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
// get visible message string
|
|
|
|
std::string str;
|
|
|
|
float x = 0.f, maxW = GetWidth();
|
|
|
|
float lh = GetLineHeight(), h = lh;
|
|
|
|
size_t wordStart = std::string::npos;
|
2014-04-06 22:42:17 +09:00
|
|
|
size_t wordStartOutPos = 0;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
for (size_t i = 0; i < msg.size(); i++) {
|
|
|
|
if (msg[i] > MsgColorMax && msg[i] != 13 && msg[i] != 10) {
|
|
|
|
if (isWordChar(msg[i])) {
|
|
|
|
if (wordStart == std::string::npos) {
|
2013-08-18 16:18:06 +09:00
|
|
|
wordStart = msg.size();
|
|
|
|
wordStartOutPos = str.size();
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-08-18 16:18:06 +09:00
|
|
|
wordStart = std::string::npos;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
float w = font->Measure(std::string(&msg[i], 1)).x;
|
|
|
|
if (x + w > maxW) {
|
|
|
|
if (wordStart != std::string::npos && wordStart != str.size()) {
|
2013-08-18 16:18:06 +09:00
|
|
|
// adding a part of word.
|
|
|
|
// do word wrapping
|
|
|
|
std::string s = msg.substr(wordStart, i - wordStart + 1);
|
|
|
|
float nw = font->Measure(s).x;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (nw <= maxW) {
|
2013-08-18 16:18:06 +09:00
|
|
|
// word wrap succeeds
|
|
|
|
w = nw;
|
|
|
|
x = w;
|
|
|
|
h += lh;
|
|
|
|
str.insert(wordStartOutPos, "\n");
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
goto didWordWrap;
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
x = 0;
|
|
|
|
h += lh;
|
2013-08-18 16:18:06 +09:00
|
|
|
str += 13;
|
|
|
|
}
|
|
|
|
x += w;
|
|
|
|
str += msg[i];
|
|
|
|
didWordWrap:;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else if (msg[i] == 13 || msg[i] == 10) {
|
|
|
|
x = 0;
|
|
|
|
h += lh;
|
2013-08-18 16:18:06 +09:00
|
|
|
str += 13;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-08-18 16:18:06 +09:00
|
|
|
str += msg[i];
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
entries.push_front(ChatEntry(msg, h, 15.f));
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
firstY -= h;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
std::string ChatWindow::ColoredMessage(const std::string &msg, char c) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
std::string s;
|
|
|
|
s += c;
|
|
|
|
s += msg;
|
|
|
|
s += MsgColorRestore;
|
|
|
|
return s;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
std::string ChatWindow::TeamColorMessage(const std::string &msg, int team) {
|
2013-08-18 16:18:06 +09:00
|
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
2016-12-03 18:23:47 +09:00
|
|
|
switch (team) {
|
|
|
|
case 0: return ColoredMessage(msg, MsgColorTeam1);
|
|
|
|
case 1: return ColoredMessage(msg, MsgColorTeam2);
|
|
|
|
case 2: return ColoredMessage(msg, MsgColorTeam3);
|
|
|
|
default: return msg;
|
2013-11-02 19:00:50 +01:00
|
|
|
}
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
static Vector4 ConvertColor(IntVector3 v) {
|
2013-11-02 19:00:50 +01:00
|
|
|
return MakeVector4((float)v.x / 255.f, (float)v.y / 255.f, (float)v.z / 255.f, 1.f);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
Vector4 ChatWindow::GetColor(char c) {
|
|
|
|
World *w = client ? client->GetWorld() : NULL;
|
|
|
|
switch (c) {
|
2013-08-18 16:18:06 +09:00
|
|
|
case MsgColorTeam1:
|
2016-12-03 18:23:47 +09:00
|
|
|
return w ? ConvertColor(w->GetTeam(0).color) : MakeVector4(0, 1, 0, 1);
|
2013-08-18 16:18:06 +09:00
|
|
|
case MsgColorTeam2:
|
2016-12-03 18:23:47 +09:00
|
|
|
return w ? ConvertColor(w->GetTeam(1).color) : MakeVector4(0, 0, 1, 1);
|
2013-08-18 16:18:06 +09:00
|
|
|
case MsgColorTeam3:
|
2016-12-03 18:23:47 +09:00
|
|
|
return w ? ConvertColor(w->GetTeam(2).color) : MakeVector4(1, 1, 0, 1);
|
|
|
|
case MsgColorRed: return MakeVector4(1, 0, 0, 1);
|
2017-01-09 22:56:55 +09:00
|
|
|
case MsgColorGray: return MakeVector4(0.5f, 0.5f, 0.5f, 1);
|
2016-12-03 18:23:47 +09:00
|
|
|
case MsgColorSysInfo: return MakeVector4(0, 1, 0, 1);
|
|
|
|
default: return MakeVector4(1, 1, 1, 1);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
void ChatWindow::Update(float dt) {
|
2016-12-03 18:23:47 +09:00
|
|
|
|
|
|
|
if (firstY < 0.f) {
|
2013-08-18 16:18:06 +09:00
|
|
|
firstY += dt * std::max(100.f, -firstY);
|
2016-12-03 18:23:47 +09:00
|
|
|
if (firstY > 0.f)
|
2013-08-18 16:18:06 +09:00
|
|
|
firstY = 0.f;
|
|
|
|
}
|
2013-11-02 19:00:50 +01:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
float normalHeight = GetNormalHeight();
|
|
|
|
float bufferHeight = GetBufferHeight();
|
2013-08-18 16:18:06 +09:00
|
|
|
float y = firstY;
|
2013-11-02 19:00:50 +01:00
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
for (std::list<ChatEntry>::iterator it = entries.begin(); it != entries.end();) {
|
|
|
|
ChatEntry &ent = *it;
|
2019-01-01 14:47:40 +09:00
|
|
|
if (y + ent.height > bufferHeight) {
|
|
|
|
ent.bufferFade -= dt * 4.f;
|
|
|
|
if (ent.bufferFade < 0.f) {
|
|
|
|
// evict from the buffer
|
2013-11-02 19:00:50 +01:00
|
|
|
std::list<ChatEntry>::iterator er = it++;
|
2016-12-03 18:23:47 +09:00
|
|
|
entries.erase(er);
|
2013-08-18 16:18:06 +09:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-01 14:47:40 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if (y + ent.height > normalHeight) {
|
|
|
|
ent.fade = std::max(ent.fade - dt * 4.f, 0.0f);
|
2016-12-03 18:23:47 +09:00
|
|
|
} else if (y + ent.height > 0.f) {
|
2019-01-01 14:47:40 +09:00
|
|
|
ent.fade = std::min(ent.fade + dt * 4.f, 1.0f);
|
|
|
|
ent.bufferFade = std::min(ent.bufferFade + dt * 4.f, 1.0f);
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
ent.timeFade -= dt;
|
2016-12-03 18:23:47 +09:00
|
|
|
if (ent.timeFade < 0.f) {
|
2019-01-01 14:47:40 +09:00
|
|
|
ent.timeFade = 0.f;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
y += ent.height;
|
2013-11-02 19:00:50 +01:00
|
|
|
++it;
|
2013-08-18 16:18:06 +09:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
void ChatWindow::Draw() {
|
|
|
|
SPADES_MARK_FUNCTION();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
float winH = expanded ? GetBufferHeight() : GetNormalHeight();
|
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
float winX = 4.f;
|
2019-01-01 14:47:40 +09:00
|
|
|
float winY = killfeed ? 8.f : renderer->ScreenHeight() - winH - 60.f;
|
2013-08-18 16:18:06 +09:00
|
|
|
std::list<ChatEntry>::iterator it;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
float lHeight = GetLineHeight();
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
float y = firstY;
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2014-03-09 14:32:45 +01:00
|
|
|
Vector4 shadowColor = {0, 0, 0, 0.8f};
|
2017-01-09 22:56:55 +09:00
|
|
|
Vector4 brightShadowColor = {1, 1, 1, 0.8f};
|
|
|
|
|
|
|
|
std::string ch = "aaaaaa"; // let's not make a new object for each character.
|
|
|
|
// note: UTF-8's longest character is 6 bytes
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
if (expanded) {
|
|
|
|
// Draw a box behind text when expanded
|
|
|
|
Handle<IImage> whiteImage = renderer->RegisterImage("Gfx/White.tga");
|
|
|
|
renderer->SetColorAlphaPremultiplied(MakeVector4(0, 0, 0, 0.2f));
|
|
|
|
renderer->DrawImage(whiteImage, AABB2(0, winY, GetWidth(), winH));
|
|
|
|
}
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
for (it = entries.begin(); it != entries.end(); ++it) {
|
|
|
|
ChatEntry &ent = *it;
|
|
|
|
|
2017-01-09 22:56:55 +09:00
|
|
|
const std::string &msg = ent.msg;
|
2013-08-18 16:18:06 +09:00
|
|
|
Vector4 color = GetColor(MsgColorRestore);
|
2017-01-09 22:56:55 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
float tx = 0.f, ty = y;
|
2017-01-09 22:56:55 +09:00
|
|
|
|
2013-08-18 16:18:06 +09:00
|
|
|
float fade = ent.fade;
|
2019-01-01 14:47:40 +09:00
|
|
|
|
|
|
|
if (expanded) {
|
|
|
|
// Display out-dated messages when expanded
|
|
|
|
fade = ent.bufferFade;
|
|
|
|
} else {
|
|
|
|
if (ent.timeFade < 1.f) {
|
|
|
|
fade *= ent.timeFade;
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
}
|
2019-01-01 14:47:40 +09:00
|
|
|
|
|
|
|
if (fade < 0.01f) {
|
|
|
|
// Skip rendering invisible messages
|
|
|
|
goto endDrawLine;
|
|
|
|
}
|
|
|
|
|
2017-01-09 22:56:55 +09:00
|
|
|
brightShadowColor.w = shadowColor.w = .8f * fade;
|
|
|
|
|
2013-12-10 01:47:22 +09:00
|
|
|
color.w = fade;
|
2016-12-03 18:23:47 +09:00
|
|
|
for (size_t i = 0; i < msg.size(); i++) {
|
|
|
|
if (msg[i] == 13 || msg[i] == 10) {
|
|
|
|
tx = 0.f;
|
|
|
|
ty += lHeight;
|
|
|
|
} else if (msg[i] <= MsgColorMax && msg[i] >= 1) {
|
2017-01-09 22:56:55 +09:00
|
|
|
color = GetColor(msg[i]);
|
|
|
|
color.w = fade;
|
2016-12-03 18:23:47 +09:00
|
|
|
} else {
|
2013-12-10 01:30:15 +09:00
|
|
|
size_t ln = 0;
|
|
|
|
GetCodePointFromUTF8String(msg, i, &ln);
|
|
|
|
ch.resize(ln);
|
2016-12-03 18:23:47 +09:00
|
|
|
for (size_t k = 0; k < ln; k++)
|
2013-12-10 01:30:15 +09:00
|
|
|
ch[k] = msg[i + k];
|
|
|
|
i += ln - 1;
|
2017-01-09 22:56:55 +09:00
|
|
|
|
|
|
|
float luminosity = color.x + color.y + color.z;
|
|
|
|
|
2016-12-03 18:23:47 +09:00
|
|
|
font->DrawShadow(ch, MakeVector2(tx + winX, ty + winY), 1.f, color,
|
2019-01-01 14:47:40 +09:00
|
|
|
luminosity > 0.9f ? shadowColor : brightShadowColor);
|
2013-08-18 16:18:06 +09:00
|
|
|
tx += font->Measure(ch).x;
|
|
|
|
}
|
|
|
|
}
|
2016-12-03 18:23:47 +09:00
|
|
|
|
2019-01-01 14:47:40 +09:00
|
|
|
endDrawLine:
|
2013-08-18 16:18:06 +09:00
|
|
|
y += ent.height;
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 23:51:05 +09:00
|
|
|
} // namespace client
|
|
|
|
} // namespace spades
|