openspades/Sources/Client/Client_Draw.cpp
Chameleonhider cae574d972 Configure-able player name drawing
Currently, hottracked player names are drawn on top of crosshair
together with distance to them. Center of screen is important region and
thus it should be configure-able.  This allows you to disable drawing
name on the whole or just disable drawing the distance. Also possible to
position the name wherever you want.
2016-02-10 18:35:55 +02:00

932 lines
25 KiB
C++

/*
Copyright (c) 2013 yvt
based on code of pysnip (c) Mathias Kaerlev 2011-2012.
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 "Client.h"
#include <cstdlib>
#include <Core/ConcurrentDispatch.h>
#include <Core/Settings.h>
#include <Core/Strings.h>
#include <Core/Bitmap.h>
#include <Core/FileManager.h>
#include "IAudioChunk.h"
#include "IAudioDevice.h"
#include "ClientUI.h"
#include "PaletteView.h"
#include "LimboView.h"
#include "MapView.h"
#include "Corpse.h"
#include "ClientPlayer.h"
#include "ILocalEntity.h"
#include "ChatWindow.h"
#include "CenterMessageView.h"
#include "Tracer.h"
#include "FallingBlock.h"
#include "HurtRingView.h"
#include "ParticleSpriteEntity.h"
#include "SmokeSpriteEntity.h"
#include "IFont.h"
#include "ScoreboardView.h"
#include "TCProgressView.h"
#include "World.h"
#include "Weapon.h"
#include "GameMap.h"
#include "Grenade.h"
#include "NetClient.h"
SPADES_SETTING(cg_hitIndicator, "1");
SPADES_SETTING(cg_debugAim, "0");
SPADES_SETTING(cg_keyReloadWeapon, "");
SPADES_SETTING(cg_screenshotFormat, "jpeg");
SPADES_SETTING(cg_stats, "0");
SPADES_SETTING(cg_hideHud, "0");
SPADES_SETTING(cg_playerNames, "2");
SPADES_SETTING(cg_playerNameX, "0");
SPADES_SETTING(cg_playerNameY, "0");
namespace spades {
namespace client {
enum class ScreenshotFormat {
Jpeg, Targa, Png
};
namespace {
ScreenshotFormat GetScreenshotFormat() {
if(EqualsIgnoringCase(cg_screenshotFormat, "jpeg")) {
return ScreenshotFormat::Jpeg;
}else if(EqualsIgnoringCase(cg_screenshotFormat, "targa")) {
return ScreenshotFormat::Targa;
}else if(EqualsIgnoringCase(cg_screenshotFormat, "png")) {
return ScreenshotFormat::Png;
}else{
SPRaise("Invalid screenshot format: %s", cg_screenshotFormat.CString());
}
}
}
void Client::TakeScreenShot(bool sceneOnly){
SceneDefinition sceneDef = CreateSceneDefinition();
lastSceneDef = sceneDef;
// render scene
flashDlights = flashDlightsOld;
DrawScene();
// draw 2d
if(!sceneOnly)
Draw2D();
// Well done!
renderer->FrameDone();
Handle<Bitmap> bmp(renderer->ReadBitmap(), false);
// force 100% opacity
uint32_t *pixels = bmp->GetPixels();
for(size_t i = bmp->GetWidth() * bmp->GetHeight(); i > 0; i--) {
*(pixels++) |= 0xff000000UL;
}
try{
std::string name = ScreenShotPath();
bmp->Save(name);
std::string msg;
if(sceneOnly)
msg = _Tr("Client", "Sceneshot saved: {0}", name);
else
msg = _Tr("Client", "Screenshot saved: {0}", name);
ShowAlert(msg, AlertType::Notice);
}catch(const Exception& ex){
std::string msg;
msg = _Tr("Client", "Screenshot failed: ");
msg += ex.GetShortMessage();
ShowAlert(msg, AlertType::Error);
SPLog("Screenshot failed: %s", ex.what());
}catch(const std::exception& ex){
std::string msg;
msg = _Tr("Client", "Screenshot failed: ");
msg += ex.what();
ShowAlert(msg, AlertType::Error);
SPLog("Screenshot failed: %s", ex.what());
}
}
std::string Client::ScreenShotPath() {
char bufJpeg[256];
char bufTarga[256];
char bufPng[256];
for(int i = 0; i < 10000;i++){
sprintf(bufJpeg, "Screenshots/shot%04d.jpg", nextScreenShotIndex);
sprintf(bufTarga, "Screenshots/shot%04d.tga", nextScreenShotIndex);
sprintf(bufPng, "Screenshots/shot%04d.png", nextScreenShotIndex);
if(FileManager::FileExists(bufJpeg) ||
FileManager::FileExists(bufTarga) ||
FileManager::FileExists(bufPng)){
nextScreenShotIndex++;
if(nextScreenShotIndex >= 10000)
nextScreenShotIndex = 0;
continue;
}
switch(GetScreenshotFormat()) {
case ScreenshotFormat::Jpeg:
return bufJpeg;
case ScreenshotFormat::Targa:
return bufTarga;
case ScreenshotFormat::Png:
return bufPng;
}
SPAssert(false);
}
SPRaise("No free file name");
}
#pragma mark - HUD Drawings
void Client::DrawSplash() {
Handle<IImage> img;
Vector2 siz;
Vector2 scrSize = {renderer->ScreenWidth(),
renderer->ScreenHeight()};
renderer->SetColorAlphaPremultiplied(MakeVector4(0, 0, 0, 1));
img = renderer->RegisterImage("Gfx/White.tga");
renderer->DrawImage(img, AABB2(0, 0, scrSize.x, scrSize.y));
renderer->SetColorAlphaPremultiplied(MakeVector4(1, 1, 1, 1.));
img = renderer->RegisterImage("Gfx/Title/Logo.png");
siz = MakeVector2(img->GetWidth(), img->GetHeight());
siz *= std::min(1.f, scrSize.x / siz.x * 0.5f);
siz *= std::min(1.f, scrSize.y / siz.y);
renderer->DrawImage(img, AABB2((scrSize.x - siz.x) * .5f,
(scrSize.y - siz.y) * .5f,
siz.x, siz.y));
}
void Client::DrawStartupScreen() {
Handle<IImage> img;
Vector2 scrSize = {renderer->ScreenWidth(),
renderer->ScreenHeight()};
renderer->SetColorAlphaPremultiplied(MakeVector4(0, 0, 0, 1.));
img = renderer->RegisterImage("Gfx/White.tga");
renderer->DrawImage(img, AABB2(0, 0,
scrSize.x, scrSize.y));
DrawSplash();
IFont *font = textFont;
std::string str = _Tr("Client", "NOW LOADING");
Vector2 size = font->Measure(str);
Vector2 pos = MakeVector2(scrSize.x - 16.f, scrSize.y - 16.f);
pos -= size;
font->DrawShadow(str, pos, 1.f, MakeVector4(1,1,1,1), MakeVector4(0,0,0,0.5));
renderer->FrameDone();
renderer->Flip();
}
void Client::DrawDisconnectScreen() {
Handle<client::IImage> img;
Vector2 scrSize = { renderer->ScreenWidth(),
renderer->ScreenHeight() };
renderer->SetColorAlphaPremultiplied(MakeVector4(0, 0, 0, 1.));
img = renderer->RegisterImage("Gfx/White.tga");
renderer->DrawImage(img, AABB2(0, 0,
scrSize.x, scrSize.y));
DrawSplash();
IFont *font = textFont;
std::string str = _Tr("Client", "Disconnecting...");
Vector2 size = font->Measure(str);
Vector2 pos = MakeVector2(scrSize.x - 16.f, scrSize.y - 16.f);
pos -= size;
font->DrawShadow(str, pos, 1.f, MakeVector4(1, 1, 1, 1), MakeVector4(0, 0, 0, 0.5));
renderer->FrameDone();
renderer->Flip();
}
void Client::DrawHurtSprites() {
float per = (world->GetTime() - lastHurtTime) / 1.5f;
if(per > 1.f) return;
if(per < 0.f) return;
Handle<IImage> img = renderer->RegisterImage("Gfx/HurtSprite.png");
Vector2 scrSize = {renderer->ScreenWidth(), renderer->ScreenHeight()};
Vector2 scrCenter = scrSize * .5f;
float radius = scrSize.GetLength() * .5f;
for(size_t i = 0 ; i < hurtSprites.size(); i++) {
HurtSprite& spr = hurtSprites[i];
float alpha = spr.strength - per;
if(alpha < 0.f) continue;
if(alpha > 1.f) alpha = 1.f;
Vector2 radDir = {
cosf(spr.angle), sinf(spr.angle)
};
Vector2 angDir = {
-sinf(spr.angle), cosf(spr.angle)
};
float siz = spr.scale * radius;
Vector2 base = radDir * radius + scrCenter;
Vector2 centVect = radDir * (-siz);
Vector2 sideVect1 = angDir * (siz * 4.f * (spr.horzShift));
Vector2 sideVect2 = angDir * (siz * 4.f * (spr.horzShift - 1.f));
Vector2 v1 = base + centVect + sideVect1;
Vector2 v2 = base + centVect + sideVect2;
Vector2 v3 = base + sideVect1;
renderer->SetColorAlphaPremultiplied(MakeVector4(0.f, 0.f, 0.f, alpha));
renderer->DrawImage(img,
v1, v2, v3,
AABB2(0, 8.f, img->GetWidth(), img->GetHeight()));
}
}
void Client::DrawHurtScreenEffect() {
SPADES_MARK_FUNCTION();
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
float wTime = world->GetTime();
Player *p = GetWorld()->GetLocalPlayer();
if(wTime < lastHurtTime + .35f &&
wTime >= lastHurtTime){
float per = (wTime - lastHurtTime) / .35f;
per = 1.f - per;
per *= .3f + (1.f - p->GetHealth() / 100.f) * .7f;
per = std::min(per, 0.9f);
per = 1.f - per;
Vector3 color = {1.f, per, per};
renderer->MultiplyScreenColor(color);
renderer->SetColorAlphaPremultiplied(MakeVector4((1.f - per) * .1f,0,0,(1.f - per) * .1f));
renderer->DrawImage(renderer->RegisterImage("Gfx/White.tga"),
AABB2(0, 0, scrWidth, scrHeight));
}
}
void Client::DrawHottrackedPlayerName() {
SPADES_MARK_FUNCTION();
if ((int)cg_playerNames == 0)
return;
Player *p = GetWorld()->GetLocalPlayer();
hitTag_t tag = hit_None;
Player *hottracked = HotTrackedPlayer( &tag );
if(hottracked){
Vector3 posxyz = Project(hottracked->GetEye());
Vector2 pos = {posxyz.x, posxyz.y};
char buf[64];
if ((int)cg_playerNames == 1){
float dist = (hottracked->GetEye() - p->GetEye()).GetLength();
int idist = (int)floorf(dist + .5f);
sprintf(buf, "%s [%d%s]", hottracked->GetName().c_str(), idist, (idist == 1) ? "block" : "blocks");
}
else
sprintf(buf, "%s", hottracked->GetName().c_str());
pos.y += (int)cg_playerNameY;
pos.x += (int)cg_playerNameX;
IFont *font = textFont;
Vector2 size = font->Measure(buf);
pos.x -= size.x * .5f;
pos.y -= size.y;
font->DrawShadow(buf, pos, 1.f, MakeVector4(1,1,1,1), MakeVector4(0,0,0,0.5));
}
}
void Client::DrawDebugAim() {
SPADES_MARK_FUNCTION();
//float scrWidth = renderer->ScreenWidth();
//float scrHeight = renderer->ScreenHeight();
//float wTime = world->GetTime();
Player *p = GetWorld()->GetLocalPlayer();
//IFont *font;
Weapon *w = p->GetWeapon();
float spread = w->GetSpread();
AABB2 boundary(0,0,0,0);
for(int i = 0; i < 8; i++){
Vector3 vec = p->GetFront();
if(i & 1) vec.x += spread;
else vec.x -= spread;
if(i & 2) vec.y += spread;
else vec.y -= spread;
if(i & 4) vec.z += spread;
else vec.z -= spread;
Vector3 viewPos;
viewPos.x = Vector3::Dot(vec, p->GetRight());
viewPos.y = Vector3::Dot(vec, p->GetUp());
viewPos.z = Vector3::Dot(vec, p->GetFront());
Vector2 p;
p.x = viewPos.x / viewPos.z;
p.y = viewPos.y / viewPos.z;
boundary.min.x = std::min(boundary.min.x, p.x);
boundary.min.y = std::min(boundary.min.y, p.y);
boundary.max.x = std::max(boundary.max.x, p.x);
boundary.max.y = std::max(boundary.max.y, p.y);
}
Handle<IImage> img = renderer->RegisterImage("Gfx/White.tga");
boundary.min *= renderer->ScreenHeight() * .5f;
boundary.max *= renderer->ScreenHeight() * .5f;
boundary.min /= tanf(lastSceneDef.fovY * .5f);
boundary.max /= tanf(lastSceneDef.fovY * .5f);
IntVector3 cent;
cent.x = (int)(renderer->ScreenWidth() * .5f);
cent.y = (int)(renderer->ScreenHeight() * .5f);
IntVector3 p1 = cent;
IntVector3 p2 = cent;
p1.x += (int)floorf(boundary.min.x);
p1.y += (int)floorf(boundary.min.y);
p2.x += (int)ceilf(boundary.max.x);
p2.y += (int)ceilf(boundary.max.y);
renderer->SetColorAlphaPremultiplied(MakeVector4(0,0,0,1));
renderer->DrawImage(img, AABB2(p1.x - 2, p1.y - 2,
p2.x - p1.x + 4, 1));
renderer->DrawImage(img, AABB2(p1.x - 2, p1.y - 2,
1, p2.y - p1.y + 4));
renderer->DrawImage(img, AABB2(p1.x - 2, p2.y + 1,
p2.x - p1.x + 4, 1));
renderer->DrawImage(img, AABB2(p2.x + 1, p1.y - 2,
1, p2.y - p1.y + 4));
renderer->SetColorAlphaPremultiplied(MakeVector4(1,1,1,1));
renderer->DrawImage(img, AABB2(p1.x - 1, p1.y - 1,
p2.x - p1.x + 2, 1));
renderer->DrawImage(img, AABB2(p1.x - 1, p1.y - 1,
1, p2.y - p1.y + 2));
renderer->DrawImage(img, AABB2(p1.x - 1, p2.y,
p2.x - p1.x + 2, 1));
renderer->DrawImage(img, AABB2(p2.x, p1.y - 1,
1, p2.y - p1.y + 2));
}
void Client::DrawJoinedAlivePlayerHUD() {
SPADES_MARK_FUNCTION();
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
//float wTime = world->GetTime();
Player *p = GetWorld()->GetLocalPlayer();
IFont *font;
// draw local weapon's 2d things
clientPlayers[p->GetId()]->Draw2D();
// draw damage ring
if(!cg_hideHud)
hurtRingView->Draw();
if(cg_hitIndicator && hitFeedbackIconState > 0.f &&
!cg_hideHud) {
Handle<IImage> img(renderer->RegisterImage("Gfx/HitFeedback.png"), false);
Vector2 pos = {scrWidth * .5f, scrHeight * .5f};
pos.x -= img->GetWidth() * .5f;
pos.y -= img->GetHeight() * .5f;
float op = hitFeedbackIconState;
Vector4 color;
if(hitFeedbackFriendly) {
color = MakeVector4(0.02f, 1.f, 0.02f, 1.f);
}else{
color = MakeVector4(1.f, 0.02f, 0.04f, 1.f);
}
color *= op;
renderer->SetColorAlphaPremultiplied(color);
renderer->DrawImage(img, pos);
}
if(cg_debugAim && p->GetTool() == Player::ToolWeapon) {
DrawDebugAim();
}
if(!cg_hideHud) {
// draw ammo
Weapon *weap = p->GetWeapon();
Handle<IImage> ammoIcon;
float iconWidth, iconHeight;
float spacing = 2.f;
int stockNum;
int warnLevel;
if(p->IsToolWeapon()){
switch(weap->GetWeaponType()){
case RIFLE_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/7.62mm.tga");
iconWidth = 6.f;
iconHeight = iconWidth * 4.f;
break;
case SMG_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/9mm.tga");
iconWidth = 4.f;
iconHeight = iconWidth * 4.f;
break;
case SHOTGUN_WEAPON:
ammoIcon = renderer->RegisterImage("Gfx/Bullet/12gauge.tga");
iconWidth = 30.f;
iconHeight = iconWidth / 4.f;
spacing = -6.f;
break;
default:
SPInvalidEnum("weap->GetWeaponType()", weap->GetWeaponType());
}
int clipSize = weap->GetClipSize();
int clip = weap->GetAmmo();
clipSize = std::max(clipSize, clip);
for(int i = 0; i < clipSize; i++){
float x = scrWidth - 16.f - (float)(i+1) *
(iconWidth + spacing);
float y = scrHeight - 16.f - iconHeight;
if(clip >= i + 1){
renderer->SetColorAlphaPremultiplied(MakeVector4(1,1,1,1));
}else{
renderer->SetColorAlphaPremultiplied(MakeVector4(0.4,0.4,0.4,1));
}
renderer->DrawImage(ammoIcon,
AABB2(x,y,iconWidth,iconHeight));
}
stockNum = weap->GetStock();
warnLevel = weap->GetMaxStock() / 3;
}else{
iconHeight = 0.f;
warnLevel = 0;
switch(p->GetTool()){
case Player::ToolSpade:
case Player::ToolBlock:
stockNum = p->GetNumBlocks();
break;
case Player::ToolGrenade:
stockNum = p->GetNumGrenades();
break;
default:
SPInvalidEnum("p->GetTool()", p->GetTool());
}
}
Vector4 numberColor = {1, 1, 1, 1};
if(stockNum == 0){
numberColor.y = 0.3f;
numberColor.z = 0.3f;
}else if(stockNum <= warnLevel){
numberColor.z = 0.3f;
}
char buf[64];
sprintf(buf, "%d", stockNum);
font = designFont;
std::string stockStr = buf;
Vector2 size = font->Measure(stockStr);
Vector2 pos = MakeVector2(scrWidth - 16.f, scrHeight - 16.f - iconHeight);
pos -= size;
font->DrawShadow(stockStr, pos, 1.f, numberColor, MakeVector4(0,0,0,0.5));
// draw "press ... to reload"
{
std::string msg = "";
switch(p->GetTool()){
case Player::ToolBlock:
if(p->GetNumBlocks() == 0){
msg = _Tr("Client", "Out of Block");
}
break;
case Player::ToolGrenade:
if(p->GetNumGrenades() == 0){
msg = _Tr("Client", "Out of Grenade");
}
break;
case Player::ToolWeapon:
{
Weapon *weap = p->GetWeapon();
if(weap->IsReloading() ||
p->IsAwaitingReloadCompletion()){
msg = _Tr("Client", "Reloading");
}else if(weap->GetAmmo() == 0 &&
weap->GetStock() == 0){
msg = _Tr("Client", "Out of Ammo");
}else if(weap->GetStock() > 0 &&
weap->GetAmmo() < weap->GetClipSize() / 4){
msg = _Tr("Client", "Press [{0}] to Reload", (std::string)cg_keyReloadWeapon);
}
}
break;
default:;
// no message
}
if(!msg.empty()){
font = textFont;
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2((scrWidth - size.x) * .5f,
scrHeight * 2.f / 3.f);
font->DrawShadow(msg, pos, 1.f, MakeVector4(1,1,1,1), MakeVector4(0,0,0,0.5));
}
}
if(p->GetTool() == Player::ToolBlock) {
paletteView->Draw();
}
// draw map
mapView->Draw();
DrawHealth();
}
}
void Client::DrawDeadPlayerHUD() {
SPADES_MARK_FUNCTION();
Player *p = GetWorld()->GetLocalPlayer();
IFont *font;
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
if(!cg_hideHud) {
// draw respawn tme
if(!p->IsAlive()){
std::string msg;
float secs = p->GetRespawnTime() - world->GetTime();
if(secs > 0.f)
msg = _Tr("Client", "You will respawn in: {0}", (int)ceilf(secs));
else
msg = _Tr("Client", "Waiting for respawn");
if(!msg.empty()){
font = textFont;
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2((scrWidth - size.x) * .5f, scrHeight / 3.f);
font->DrawShadow(msg, pos, 1.f, MakeVector4(1,1,1,1), MakeVector4(0,0,0,0.5));
}
}
// draw map
mapView->Draw();
}
}
void Client::DrawAlert() {
SPADES_MARK_FUNCTION();
IFont *font = textFont;
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
auto& r = renderer;
const float fadeOutTime = 1.f;
float fade = 1.f - (time - alertDisappearTime) / fadeOutTime;
fade = std::min(fade, 1.f);
if(fade <= 0.f) {
return;
}
float borderFade = 1.f - (time - alertAppearTime) * 1.5f;
borderFade = std::max(std::min(borderFade, 1.f), 0.f);
borderFade *= fade;
Handle<IImage> alertIcon(renderer->RegisterImage("Gfx/AlertIcon.png"), false);
Vector2 textSize = font->Measure(alertContents);
Vector2 contentsSize = textSize;
contentsSize.y = std::max(contentsSize.y, 16.f);
if(alertType != AlertType::Notice) {
contentsSize.x += 22.f;
}
// add margin
const float margin = 8.f;
contentsSize.x += margin * 2.f;
contentsSize.y += margin * 2.f;
contentsSize.x = floorf(contentsSize.x);
contentsSize.y = floorf(contentsSize.y);
Vector2 pos = (Vector2(scrWidth, scrHeight) - contentsSize) * Vector2(0.5f, 0.7f);
pos.y += 40.f;
pos.x = floorf(pos.x);
pos.y = floorf(pos.y);
Vector4 color;
// draw border
switch(alertType) {
case AlertType::Notice:
color = Vector4(0.f, 0.f, 0.f, 0.f);
break;
case AlertType::Warning:
color = Vector4(1.f, 1.f, 0.f, .7f);
break;
case AlertType::Error:
color = Vector4(1.f, 0.f, 0.f, .7f);
break;
}
color *= borderFade;
r->SetColorAlphaPremultiplied(color);
const float border = 1.f;
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y-border, contentsSize.x+border*2.f, border));
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y+contentsSize.y, contentsSize.x+border*2.f, border));
r->DrawImage(nullptr, AABB2(pos.x-border, pos.y, border, contentsSize.y));
r->DrawImage(nullptr, AABB2(pos.x+contentsSize.x, pos.y, border, contentsSize.y));
// fill background
color = Vector4(0.f, 0.f, 0.f, fade * 0.5f);
r->SetColorAlphaPremultiplied(color);
r->DrawImage(nullptr, AABB2(pos.x, pos.y, contentsSize.x, contentsSize.y));
// draw icon
switch(alertType) {
case AlertType::Notice:
color = Vector4(0.f, 0.f, 0.f, 0.f);
break;
case AlertType::Warning:
color = Vector4(1.f, 1.f, 0.f, 1.f);
break;
case AlertType::Error:
color = Vector4(1.f, 0.f, 0.f, 1.f);
break;
}
color *= fade;
r->SetColorAlphaPremultiplied(color);
r->DrawImage(alertIcon, Vector2(pos.x + margin, pos.y + (contentsSize.y - 16.f) * 0.5f));
// draw text
color = Vector4(1.f, 1.f, 1.f, 1.f);
color *= fade;
font->DrawShadow(alertContents,
Vector2(pos.x + contentsSize.x - textSize.x - margin,
pos.y + (contentsSize.y - textSize.y) * 0.5f),
1.f, color,
Vector4(0.f, 0.f, 0.f, fade * 0.5f));
}
void Client::DrawHealth() {
SPADES_MARK_FUNCTION();
Player *p = GetWorld()->GetLocalPlayer();
IFont *font;
//float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
std::string str = std::to_string(p->GetHealth());
Vector4 numberColor = {1, 1, 1, 1};
if(p->GetHealth() == 0){
numberColor.y = 0.3f;
numberColor.z = 0.3f;
}else if(p->GetHealth() <= 50){
numberColor.z = 0.3f;
}
font = designFont;
Vector2 size = font->Measure(str);
Vector2 pos = MakeVector2(16.f, scrHeight - 16.f);
pos.y -= size.y;
font->DrawShadow(str, pos, 1.f, numberColor, MakeVector4(0,0,0,0.5));
}
void Client::Draw2DWithWorld() {
SPADES_MARK_FUNCTION();
float scrWidth = renderer->ScreenWidth();
//float scrHeight = renderer->ScreenHeight();
IFont *font;
//float wTime = world->GetTime();
for(auto& ent: localEntities){
ent->Render2D();
}
Player *p = GetWorld()->GetLocalPlayer();
if(p){
DrawHurtSprites();
DrawHurtScreenEffect();
DrawHottrackedPlayerName();
if(!cg_hideHud)
tcView->Draw();
if(p->GetTeamId() < 2) {
// player is not spectator
if(p->IsAlive()){
DrawJoinedAlivePlayerHUD();
}else{
DrawDeadPlayerHUD();
}
}
if(IsFollowing() && !cg_hideHud){
if(followingPlayerId == p->GetId()){
// just spectating
}else{
font = textFont;
std::string msg = _Tr("Client", "Following {0}", world->GetPlayerPersistent(followingPlayerId).name);
Vector2 size = font->Measure(msg);
Vector2 pos = MakeVector2(scrWidth - 8.f, 256.f + 32.f);
pos.x -= size.x;
font->DrawShadow(msg, pos, 1.f, MakeVector4(1, 1, 1, 1), MakeVector4(0,0,0,0.5));
}
}
if(!cg_hideHud) {
DrawAlert();
chatWindow->Draw();
killfeedWindow->Draw();
}
// large map view should come in front
largeMapView->Draw();
if(scoreboardVisible)
scoreboard->Draw();
// --- end "player is there" render
}else{
// world exists, but no local player: not joined
scoreboard->Draw();
DrawAlert();
}
if(IsLimboViewActive())
limbo->Draw();
}
void Client::Draw2DWithoutWorld() {
SPADES_MARK_FUNCTION();
// no world; loading?
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
IFont *font;
DrawSplash();
Handle<IImage> img;
std::string msg = net->GetStatusString();
font = textFont;
Vector2 textSize = font->Measure(msg);
font->Draw(msg, MakeVector2(scrWidth - 16.f, scrHeight - 24.f) - textSize, 1.f, MakeVector4(1,1,1,0.95f));
img = renderer->RegisterImage("Gfx/White.tga");
float pos = timeSinceInit / 3.6f;
pos -= floorf(pos);
pos = 1.f - pos * 2.0f;
for(float v = 0; v < 0.6f; v += 0.14f) {
float p = pos + v;
if(p < 0.01f || p > .99f) continue;
p = asin(p * 2.f - 1.f);
p = p / (float)M_PI + 0.5f;
float op = p * (1.f - p) * 4.f;
renderer->SetColorAlphaPremultiplied(MakeVector4(op, op, op, op));
renderer->DrawImage(img, AABB2(scrWidth - 236.f + p * 234.f, scrHeight - 18.f, 4.f, 4.f));
}
DrawAlert();
}
void Client::DrawStats() {
SPADES_MARK_FUNCTION();
if(!cg_stats) return;
char buf[256];
std::string str;
{
auto fps = fpsCounter.GetFps();
if(fps == 0.0)
str += "--.-- fps";
else {
sprintf(buf, "%.02f fps", fps);
str += buf;
}
}
if(net) {
auto ping = net->GetPing();
auto upbps = net->GetUplinkBps();
auto downbps = net->GetDownlinkBps();
sprintf(buf, ", ping: %dms, up/down: %.02f/%.02fkbps",
ping, upbps / 1000.0, downbps / 1000.0);
str += buf;
}
float scrWidth = renderer->ScreenWidth();
float scrHeight = renderer->ScreenHeight();
IFont *font = textFont;
float margin = 5.f;
IRenderer *r = renderer;
auto size = font->Measure(str);
size += Vector2(margin * 2.f, margin * 2.f);
auto pos =
(Vector2(scrWidth, scrHeight) - size) * Vector2(0.5f, 1.f);
r->SetColorAlphaPremultiplied(Vector4(0.f, 0.f, 0.f, 0.5f));
r->DrawImage(nullptr, AABB2(pos.x, pos.y, size.x, size.y));
font->DrawShadow(str, pos + Vector2(margin, margin), 1.f,
Vector4(1.f, 1.f, 1.f, 1.f),
Vector4(0.f, 0.f, 0.f, 0.5f));
}
void Client::Draw2D(){
SPADES_MARK_FUNCTION();
if(GetWorld()){
Draw2DWithWorld();
}else{
Draw2DWithoutWorld();
}
if(!cg_hideHud)
centerMessageView->Draw();
DrawStats();
}
}
}