openspades/Sources/Client/ChatWindow.cpp
Chameleonhider 83b061fedf Makes local kills on killfeed black
Turns icons of local kills (both for kill and death) black. Black colour
stands out the most. Useful after having disabled center messages.
2016-02-10 20:02:43 +02:00

325 lines
8.6 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
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.
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.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatWindow.h"
#include "IRenderer.h"
#include "IFont.h"
#include <Core/Debug.h>
#include "Client.h"
#include "World.h"
#include <Core/Exception.h>
#include <ctype.h>
#include <Core/Math.h>
#include <Core/Settings.h>
SPADES_SETTING(cg_chatHeight, "30");
SPADES_SETTING(cg_killfeedHeight, "26");
namespace spades {
namespace client{
ChatWindow::ChatWindow(Client *cli, IRenderer* rend, IFont *fnt, bool killfeed):
client(cli), renderer(rend),
font(fnt), killfeed(killfeed){
firstY = 0.f;
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/a-Rifle.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/b-SMG.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/c-Shotgun.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/d-Headshot.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/e-Melee.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/f-Grenade.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/g-Falling.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/h-Teamchange.png") );
mKillImages.push_back( renderer->RegisterImage("Gfx/Killfeed/i-Classchange.png") );
for( size_t n = 0; n < mKillImages.size(); ++n ) {
if( mKillImages[n]->GetHeight() > GetLineHeight() ) {
SPRaise( "Kill image (%d) height too big ", n );
}
}
}
ChatWindow::~ChatWindow(){}
float ChatWindow::GetWidth() {
return renderer->ScreenWidth() / 2;
}
float ChatWindow::GetHeight() {
float prop = killfeed ?
(float)cg_killfeedHeight :
(float)cg_chatHeight;
return renderer->ScreenHeight() * prop * 0.01f;
}
float ChatWindow::GetLineHeight() {
return 20.f;
}
static bool isWordChar(char c){
return isalnum(c) || c == '\'';
}
std::string ChatWindow::killImage( int type, int weapon )
{
std::string tmp = "xx";
tmp[0] = 7;
switch( type ) {
case KillTypeWeapon:
switch( weapon ) {
case 0: case 1: case 2:
tmp[1] = 'a' + weapon;
break;
default:
return "";
}
break;
case KillTypeHeadshot: case KillTypeMelee: case KillTypeGrenade:
case KillTypeFall: case KillTypeTeamChange: case KillTypeClassChange:
tmp[1] = 'a' + 2 + type;
break;
default:
return "";
}
return tmp;
}
void ChatWindow::AddMessage(const std::string &msg){
SPADES_MARK_FUNCTION();
// get visible message string
std::string str;
float x = 0.f, maxW = GetWidth();
float lh = GetLineHeight(), h = lh;
size_t wordStart = std::string::npos;
size_t wordStartOutPos = 0;
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){
wordStart = msg.size();
wordStartOutPos = str.size();
}
}else{
wordStart = std::string::npos;
}
float w = font->Measure(std::string(&msg[i],1)).x;
if(x + w > maxW){
if(wordStart != std::string::npos && wordStart != str.size()){
// adding a part of word.
// do word wrapping
std::string s = msg.substr(wordStart, i - wordStart + 1);
float nw = font->Measure(s).x;
if(nw <= maxW){
// word wrap succeeds
w = nw;
x = w;
h += lh;
str.insert(wordStartOutPos, "\n");
goto didWordWrap;
}
}
x = 0; h += lh;
str += 13;
}
x += w;
str += msg[i];
didWordWrap:;
}else if(msg[i] == 13 || msg[i] == 10){
x = 0; h += lh;
str += 13;
}else{
str += msg[i];
}
}
entries.push_front( ChatEntry( msg, h, 0.f, 15.f ) );
firstY -= h;
}
std::string ChatWindow::ColoredMessage(const std::string & msg, char c){
SPADES_MARK_FUNCTION_DEBUG();
std::string s;
s += c;
s += msg;
s += MsgColorRestore;
return s;
}
std::string ChatWindow::TeamColorMessage(const std::string &msg, int team){
SPADES_MARK_FUNCTION_DEBUG();
switch( team ) {
case 0:
return ColoredMessage(msg, MsgColorTeam1);
case 1:
return ColoredMessage(msg, MsgColorTeam2);
case 2:
return ColoredMessage(msg, MsgColorTeam3);
default:
return msg;
}
}
static Vector4 ConvertColor(IntVector3 v){
return MakeVector4((float)v.x / 255.f, (float)v.y / 255.f, (float)v.z / 255.f, 1.f);
}
Vector4 ChatWindow::GetColor(char c){
World* w = client ? client->GetWorld() : NULL;
switch(c){
case MsgColorTeam1:
return w ? ConvertColor(w->GetTeam(0).color) : MakeVector4( 0, 1, 0, 1 );
case MsgColorTeam2:
return w ? ConvertColor(w->GetTeam(1).color) : MakeVector4( 0, 0, 1, 1 );
case MsgColorTeam3:
return w ? ConvertColor(w->GetTeam(2).color) : MakeVector4( 1, 1, 0, 1 );
case MsgColorRed:
return MakeVector4(1,0,0,1);
case MsgColorBlack:
return MakeVector4(0,0,0,1);
case MsgColorSysInfo:
return MakeVector4(0,1,0,1);
default:
return MakeVector4(1,1,1,1);
}
}
void ChatWindow::Update(float dt) {
if(firstY < 0.f){
firstY += dt * std::max(100.f, -firstY);
if(firstY > 0.f)
firstY = 0.f;
}
float height = GetHeight();
float y = firstY;
for(std::list<ChatEntry>::iterator it = entries.begin(); it != entries.end(); ){
ChatEntry& ent = *it;
if(y + ent.height > height){
// should fade out
ent.fade -= dt * 4.f;
if(ent.fade < 0.f){
ent.fade = 0.f;
std::list<ChatEntry>::iterator er = it++;
entries.erase( er );
continue;
}
}else if(y + ent.height > 0.f){
// should fade in
ent.fade += dt * 4.f;
if(ent.fade > 1.f)
ent.fade = 1.f;
}
ent.timeFade -= dt;
if(ent.timeFade < 0.f){
std::list<ChatEntry>::iterator er = it++;
entries.erase( er );
continue;
}
y += ent.height;
++it;
}
}
IImage* ChatWindow::imageForIndex( char index )
{
int real = index - 'a';
if( real >= 0 && real < (int)mKillImages.size() ) {
return mKillImages[real];
}
return NULL;
}
void ChatWindow::Draw() {
SPADES_MARK_FUNCTION();
float winX = 4.f;
float winY = killfeed ? 8.f :
renderer->ScreenHeight() - GetHeight() - 60.f;
std::list<ChatEntry>::iterator it;
float lHeight = GetLineHeight();
float y = firstY;
Vector4 shadowColor = {0, 0, 0, 0.8f};
for(it = entries.begin(); it != entries.end(); ++it){
ChatEntry& ent = *it;
std::string msg = ent.msg;
Vector4 color = GetColor(MsgColorRestore);
float tx = 0.f, ty = y;
float fade = ent.fade;
if(ent.timeFade < 1.f) { fade *= ent.timeFade; }
shadowColor.w = .8f * fade;
color.w = fade;
std::string ch = "aaaaaa"; //let's not make a new object for each character.
// note: UTF-8's longest character is 6 bytes
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){
if( msg[i] == MsgImage ) {
IImage* kill = NULL;
if( i+1 < msg.size() && (kill = imageForIndex(msg[i+1])) ) {
Vector4 colorpm = color;
colorpm.x *= colorpm.w;
colorpm.y *= colorpm.w;
colorpm.z *= colorpm.w;
renderer->SetColorAlphaPremultiplied(colorpm);
renderer->DrawImage( kill, MakeVector2( tx + winX, ty + winY ) );
tx += kill->GetWidth();
++i;
} else {
// just ignore invalid icon specifier
}
} else {
color = GetColor(msg[i]);
color.w = fade;
}
}else{
size_t ln = 0;
GetCodePointFromUTF8String(msg, i, &ln);
ch.resize(ln);
for(size_t k = 0; k < ln; k++)
ch[k] = msg[i + k];
i += ln - 1;
font->DrawShadow(ch, MakeVector2(tx + winX, ty + winY), 1.f, color, shadowColor);
tx += font->Measure(ch).x;
}
}
y += ent.height;
}
}
}
}