Merge pull request #7515 from furo321/racefinish-message

Add finish info messages from 0.7
This commit is contained in:
heinrich5991 2023-12-01 16:13:06 +00:00 committed by GitHub
commit 21cfb01ec3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 674 additions and 459 deletions

View file

@ -1543,6 +1543,7 @@ set(EXPECTED_DATA
menuimages/play_game.png
menuimages/settings.png
particles.png
race_flag.png
shader/pipeline.frag
shader/pipeline.vert
shader/prim.frag
@ -2194,10 +2195,10 @@ if(CLIENT)
components/ghost.h
components/hud.cpp
components/hud.h
components/infomessages.cpp
components/infomessages.h
components/items.cpp
components/items.h
components/killmessages.cpp
components/killmessages.h
components/mapimages.cpp
components/mapimages.h
components/maplayers.cpp

BIN
data/race_flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -255,6 +255,7 @@ container.images.Add(image_audio_source)
container.images.Add(image_strongweak)
container.images.Add(image_hud)
container.images.Add(image_extras)
container.images.Add(Image("raceflag", "race_flag.png"))
container.pickups.Add(Pickup("health"))
container.pickups.Add(Pickup("armor"))

View file

@ -558,4 +558,12 @@ Messages = [
NetMessageEx("Sv_YourVote", "yourvote@netmsg.ddnet.org", [
NetIntRange("m_Voted", -1, 1),
]),
NetMessageEx("Sv_RaceFinish", "racefinish@netmsg.ddnet.org", [
NetIntRange("m_ClientID", 0, 'MAX_CLIENTS-1'),
NetIntAny("m_Time"),
NetIntAny("m_Diff"),
NetBool("m_RecordPersonal"),
NetBool("m_RecordServer", default=False),
]),
]

View file

@ -3453,8 +3453,12 @@ int str_time(int64_t centisecs, int format, char *buffer, int buffer_size)
(centisecs % hour) / min, (centisecs % min) / sec, centisecs % sec);
[[fallthrough]];
case TIME_MINS_CENTISECS:
if(centisecs >= min)
return str_format(buffer, buffer_size, "%02" PRId64 ":%02" PRId64 ".%02" PRId64, centisecs / min,
(centisecs % min) / sec, centisecs % sec);
[[fallthrough]];
case TIME_SECS_CENTISECS:
return str_format(buffer, buffer_size, "%02" PRId64 ".%02" PRId64, (centisecs % min) / sec, centisecs % sec);
}
return -1;

View file

@ -1754,6 +1754,7 @@ enum
TIME_MINS,
TIME_HOURS_CENTISECS,
TIME_MINS_CENTISECS,
TIME_SECS_CENTISECS,
};
/*

View file

@ -64,6 +64,7 @@ MACRO_CONFIG_INT(ClShowChat, cl_showchat, 1, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE
MACRO_CONFIG_INT(ClShowChatFriends, cl_show_chat_friends, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show only chat messages from friends")
MACRO_CONFIG_INT(ClShowChatSystem, cl_show_chat_system, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show chat messages from the server")
MACRO_CONFIG_INT(ClShowKillMessages, cl_showkillmessages, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show kill messages")
MACRO_CONFIG_INT(ClShowFinishMessages, cl_show_finish_messages, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show finish messages")
MACRO_CONFIG_INT(ClShowVotesAfterVoting, cl_show_votes_after_voting, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show votes window after voting")
MACRO_CONFIG_INT(ClShowLocalTimeAlways, cl_show_local_time_always, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Always show local time")
MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show ingame FPS counter")

View file

@ -0,0 +1,566 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <engine/graphics.h>
#include <engine/shared/config.h>
#include <engine/shared/protocol.h>
#include <engine/textrender.h>
#include <game/generated/client_data.h>
#include <game/generated/protocol.h>
#include <game/localization.h>
#include "infomessages.h"
#include <game/client/animstate.h>
#include <game/client/gameclient.h>
#include <game/client/prediction/entities/character.h>
#include <game/client/prediction/gameworld.h>
void CInfoMessages::OnWindowResize()
{
for(auto &InfoMsg : m_aInfoMsgs)
{
DeleteTextContainers(&InfoMsg);
}
}
void CInfoMessages::OnReset()
{
m_InfoMsgCurrent = 0;
for(auto &InfoMsg : m_aInfoMsgs)
{
InfoMsg.m_Tick = -100000;
DeleteTextContainers(&InfoMsg);
}
}
void CInfoMessages::DeleteTextContainers(CInfoMsg *pInfoMsg)
{
TextRender()->DeleteTextContainer(pInfoMsg->m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(pInfoMsg->m_KillerTextContainerIndex);
TextRender()->DeleteTextContainer(pInfoMsg->m_DiffTextContainerIndex);
TextRender()->DeleteTextContainer(pInfoMsg->m_TimeTextContainerIndex);
}
void CInfoMessages::OnInit()
{
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
m_SpriteQuadContainerIndex = Graphics()->CreateQuadContainer(false);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(1, 0, 0, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(1, 0, 0, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
for(int i = 0; i < NUM_WEAPONS; ++i)
{
float ScaleX, ScaleY;
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->GetSpriteScale(g_pData->m_Weapons.m_aId[i].m_pSpriteBody, ScaleX, ScaleY);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 96.f * ScaleX, 96.f * ScaleY);
}
Graphics()->QuadContainerUpload(m_SpriteQuadContainerIndex);
}
void CInfoMessages::AddInfoMsg(EType Type, CInfoMsg NewMsg)
{
NewMsg.m_Type = Type;
NewMsg.m_Tick = Client()->GameTick(g_Config.m_ClDummy);
m_InfoMsgCurrent = (m_InfoMsgCurrent + 1) % MAX_INFOMSGS;
DeleteTextContainers(&m_aInfoMsgs[m_InfoMsgCurrent]);
m_aInfoMsgs[m_InfoMsgCurrent] = NewMsg;
}
void CInfoMessages::CreateNamesIfNotCreated(CInfoMsg *pInfoMsg)
{
const float FontSize = 36.0f;
if(!pInfoMsg->m_VictimTextContainerIndex.Valid() && pInfoMsg->m_aVictimName[0] != 0)
{
pInfoMsg->m_VictimTextWidth = TextRender()->TextWidth(FontSize, pInfoMsg->m_aVictimName);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
unsigned Color = g_Config.m_ClKillMessageNormalColor;
if(pInfoMsg->m_aVictimIds[0] == m_pClient->m_Snap.m_LocalClientID)
{
Color = g_Config.m_ClKillMessageHighlightColor;
}
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(Color)));
TextRender()->CreateTextContainer(pInfoMsg->m_VictimTextContainerIndex, &Cursor, pInfoMsg->m_aVictimName);
}
if(!pInfoMsg->m_KillerTextContainerIndex.Valid() && pInfoMsg->m_aKillerName[0] != 0)
{
pInfoMsg->m_KillerTextWidth = TextRender()->TextWidth(FontSize, pInfoMsg->m_aKillerName);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
unsigned Color = g_Config.m_ClKillMessageNormalColor;
if(pInfoMsg->m_KillerID == m_pClient->m_Snap.m_LocalClientID)
{
Color = g_Config.m_ClKillMessageHighlightColor;
}
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(Color)));
TextRender()->CreateTextContainer(pInfoMsg->m_KillerTextContainerIndex, &Cursor, pInfoMsg->m_aKillerName);
}
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
void CInfoMessages::CreateFinishTextContainersIfNotCreated(CInfoMsg *pInfoMsg)
{
const float FontSize = 36.0f;
if(!pInfoMsg->m_DiffTextContainerIndex.Valid() && pInfoMsg->m_aDiffText[0] != 0)
{
pInfoMsg->m_DiffTextWidth = TextRender()->TextWidth(FontSize, pInfoMsg->m_aDiffText);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
if(pInfoMsg->m_Diff > 0)
TextRender()->TextColor(1.0f, 0.5f, 0.5f, 1); // red
else if(pInfoMsg->m_Diff < 0)
TextRender()->TextColor(0.5f, 1.0f, 0.5f, 1); // green
else
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->CreateTextContainer(pInfoMsg->m_DiffTextContainerIndex, &Cursor, pInfoMsg->m_aDiffText);
}
if(!pInfoMsg->m_TimeTextContainerIndex.Valid() && pInfoMsg->m_aTimeText[0] != 0)
{
pInfoMsg->m_TimeTextWidth = TextRender()->TextWidth(FontSize, pInfoMsg->m_aTimeText);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->CreateTextContainer(pInfoMsg->m_TimeTextContainerIndex, &Cursor, pInfoMsg->m_aTimeText);
}
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
void CInfoMessages::OnMessage(int MsgType, void *pRawMsg)
{
if(m_pClient->m_SuppressEvents)
return;
if(MsgType == NETMSGTYPE_SV_KILLMSGTEAM && g_Config.m_ClShowKillMessages)
{
CNetMsg_Sv_KillMsgTeam *pMsg = (CNetMsg_Sv_KillMsgTeam *)pRawMsg;
CInfoMsg Kill{};
std::vector<std::pair<int, int>> vStrongWeakSorted;
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(m_pClient->m_Teams.Team(i) == pMsg->m_Team)
{
CCharacter *pChr = m_pClient->m_GameWorld.GetCharacterByID(i);
vStrongWeakSorted.emplace_back(i, pMsg->m_First == i ? MAX_CLIENTS : pChr ? pChr->GetStrongWeakID() : 0);
}
}
std::stable_sort(vStrongWeakSorted.begin(), vStrongWeakSorted.end(), [](auto &Left, auto &Right) { return Left.second > Right.second; });
Kill.m_TeamSize = vStrongWeakSorted.size();
if(Kill.m_TeamSize > MAX_KILLMSG_TEAM_MEMBERS)
Kill.m_TeamSize = MAX_KILLMSG_TEAM_MEMBERS;
Kill.m_VictimDDTeam = pMsg->m_Team;
for(int i = 0; i < Kill.m_TeamSize; i++)
{
if(m_pClient->m_aClients[vStrongWeakSorted[i].first].m_Active)
{
Kill.m_aVictimIds[i] = vStrongWeakSorted[i].first;
Kill.m_aVictimRenderInfo[i] = m_pClient->m_aClients[vStrongWeakSorted[i].first].m_RenderInfo;
}
else
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
}
for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
str_format(Kill.m_aVictimName, sizeof(Kill.m_aVictimName), Localize("Team %d"), pMsg->m_Team);
Kill.m_KillerID = -1;
Kill.m_aKillerName[0] = '\0';
Kill.m_KillerRenderInfo.Reset();
Kill.m_Weapon = -1;
Kill.m_ModeSpecial = 0;
Kill.m_VictimTextWidth = Kill.m_KillerTextWidth = 0.f;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
CreateNamesIfNotCreated(&Kill);
bool KillMsgValid = true;
for(int i = 0; i < Kill.m_TeamSize; i++)
{
KillMsgValid = KillMsgValid && Kill.m_aVictimIds[i] >= 0 && ((Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_OriginalRenderSkin.m_Body.IsValid()));
}
if(KillMsgValid)
{
AddInfoMsg(EType::TYPE_KILL, Kill);
}
else
{
DeleteTextContainers(&Kill);
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
if(MsgType == NETMSGTYPE_SV_KILLMSG && g_Config.m_ClShowKillMessages)
{
CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg;
CInfoMsg Kill{};
Kill.m_TeamSize = 1;
Kill.m_aVictimIds[0] = pMsg->m_Victim;
if(Kill.m_aVictimIds[0] >= 0 && Kill.m_aVictimIds[0] < MAX_CLIENTS)
{
Kill.m_VictimDDTeam = m_pClient->m_Teams.Team(Kill.m_aVictimIds[0]);
str_copy(Kill.m_aVictimName, m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_aName);
Kill.m_aVictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_RenderInfo;
}
else
{
Kill.m_VictimDDTeam = 0;
Kill.m_aVictimName[0] = '\0';
}
for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
Kill.m_KillerID = pMsg->m_Killer;
if(Kill.m_KillerID >= 0 && Kill.m_KillerID < MAX_CLIENTS)
{
str_copy(Kill.m_aKillerName, m_pClient->m_aClients[Kill.m_KillerID].m_aName);
Kill.m_KillerRenderInfo = m_pClient->m_aClients[Kill.m_KillerID].m_RenderInfo;
}
else
{
Kill.m_aKillerName[0] = '\0';
Kill.m_KillerRenderInfo.Reset();
}
Kill.m_Weapon = pMsg->m_Weapon;
Kill.m_ModeSpecial = pMsg->m_ModeSpecial;
Kill.m_FlagCarrierBlue = m_pClient->m_Snap.m_pGameDataObj ? m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue : -1;
Kill.m_VictimTextWidth = Kill.m_KillerTextWidth = 0.f;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
CreateNamesIfNotCreated(&Kill);
bool KillMsgValid = (Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_OriginalRenderSkin.m_Body.IsValid());
KillMsgValid &= Kill.m_KillerID == -1 || ((Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_OriginalRenderSkin.m_Body.IsValid()));
if(KillMsgValid)
{
AddInfoMsg(EType::TYPE_KILL, Kill);
}
else
{
DeleteTextContainers(&Kill);
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
if(MsgType == NETMSGTYPE_SV_RACEFINISH && g_Config.m_ClShowFinishMessages)
{
CNetMsg_Sv_RaceFinish *pMsg = (CNetMsg_Sv_RaceFinish *)pRawMsg;
char aBuf[256];
CInfoMsg Finish;
Finish.m_TeamSize = 1;
Finish.m_aVictimIds[0] = pMsg->m_ClientID;
Finish.m_VictimDDTeam = m_pClient->m_Teams.Team(Finish.m_aVictimIds[0]);
str_copy(Finish.m_aVictimName, m_pClient->m_aClients[Finish.m_aVictimIds[0]].m_aName);
Finish.m_aVictimRenderInfo[0] = m_pClient->m_aClients[pMsg->m_ClientID].m_RenderInfo;
Finish.m_aKillerName[0] = '\0';
Finish.m_Diff = pMsg->m_Diff;
Finish.m_RecordPersonal = (pMsg->m_RecordPersonal || pMsg->m_RecordServer);
// diff time text
if(Finish.m_Diff)
{
if(Finish.m_Diff < 0)
{
str_time_float(-Finish.m_Diff / 1000.0f, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf));
str_format(Finish.m_aDiffText, sizeof(Finish.m_aDiffText), "(-%s)", aBuf);
}
else
{
str_time_float(Finish.m_Diff / 1000.0f, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf));
str_format(Finish.m_aDiffText, sizeof(Finish.m_aDiffText), "(+%s)", aBuf);
}
}
else
{
Finish.m_aDiffText[0] = '\0';
}
// finish time text
str_time_float(pMsg->m_Time / 1000.0f, TIME_HOURS_CENTISECS, Finish.m_aTimeText, sizeof(Finish.m_aTimeText));
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
CreateNamesIfNotCreated(&Finish);
CreateFinishTextContainersIfNotCreated(&Finish);
AddInfoMsg(EType::TYPE_FINISH, Finish);
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
}
void CInfoMessages::RenderKillMsg(CInfoMsg *pInfoMsg, float x, float y)
{
// render victim name
x -= pInfoMsg->m_VictimTextWidth;
ColorRGBA TextColor;
if(g_Config.m_ClChatTeamColors && pInfoMsg->m_VictimDDTeam)
TextColor = m_pClient->GetDDTeamColor(pInfoMsg->m_VictimDDTeam, 0.75f);
else
TextColor = TextRender()->DefaultTextColor();
CreateNamesIfNotCreated(pInfoMsg);
if(pInfoMsg->m_VictimTextContainerIndex.Valid())
TextRender()->RenderTextContainer(pInfoMsg->m_VictimTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
// render victim tee
x -= 24.0f;
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS)
{
if(pInfoMsg->m_ModeSpecial & 1)
{
int QuadOffset = 0;
if(pInfoMsg->m_aVictimIds[0] == pInfoMsg->m_FlagCarrierBlue)
++QuadOffset;
if(QuadOffset == 0)
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagRed);
else
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagBlue);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, QuadOffset, x, y - 16);
}
}
for(int j = (pInfoMsg->m_TeamSize - 1); j >= 0; j--)
{
if(pInfoMsg->m_aVictimIds[j] < 0)
continue;
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &pInfoMsg->m_aVictimRenderInfo[j], OffsetToMid);
const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &pInfoMsg->m_aVictimRenderInfo[j], EMOTE_PAIN, vec2(-1, 0), TeeRenderPos);
x -= 44.0f;
}
// render weapon
x -= 32.0f;
if(pInfoMsg->m_Weapon >= 0)
{
Graphics()->TextureSet(GameClient()->m_GameSkin.m_aSpriteWeapons[pInfoMsg->m_Weapon]);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, 4 + pInfoMsg->m_Weapon, x, y + 28);
}
x -= 52.0f;
if(pInfoMsg->m_aVictimIds[0] != pInfoMsg->m_KillerID)
{
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS)
{
if(pInfoMsg->m_ModeSpecial & 2)
{
int QuadOffset = 2;
if(pInfoMsg->m_KillerID == pInfoMsg->m_FlagCarrierBlue)
++QuadOffset;
if(QuadOffset == 2)
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagRed);
else
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagBlue);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, QuadOffset, x - 56, y - 16);
}
}
// render killer tee
x -= 24.0f;
if(pInfoMsg->m_KillerID >= 0)
{
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &pInfoMsg->m_KillerRenderInfo, OffsetToMid);
const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &pInfoMsg->m_KillerRenderInfo, EMOTE_ANGRY, vec2(1, 0), TeeRenderPos);
}
x -= 32.0f;
// render killer name
x -= pInfoMsg->m_KillerTextWidth;
if(pInfoMsg->m_KillerTextContainerIndex.Valid())
TextRender()->RenderTextContainer(pInfoMsg->m_KillerTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
}
}
void CInfoMessages::RenderFinishMsg(CInfoMsg *pInfoMsg, float x, float y)
{
// render time diff
CreateFinishTextContainersIfNotCreated(pInfoMsg);
if(pInfoMsg->m_Diff)
{
x -= pInfoMsg->m_DiffTextWidth;
if(pInfoMsg->m_DiffTextContainerIndex.Valid())
TextRender()->RenderTextContainer(pInfoMsg->m_DiffTextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
}
// render time
x -= pInfoMsg->m_TimeTextWidth;
if(pInfoMsg->m_TimeTextContainerIndex.Valid())
TextRender()->RenderTextContainer(pInfoMsg->m_TimeTextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
// render flag
x -= 52.0f;
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_RACEFLAG].m_Id);
Graphics()->QuadsBegin();
IGraphics::CQuadItem QuadItem(x, y, 52, 52);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
// render victim name
ColorRGBA TextColor;
x -= pInfoMsg->m_VictimTextWidth;
if(g_Config.m_ClChatTeamColors && pInfoMsg->m_VictimDDTeam)
TextColor = m_pClient->GetDDTeamColor(pInfoMsg->m_VictimDDTeam, 0.75f);
else
TextColor = TextRender()->DefaultTextColor();
CreateNamesIfNotCreated(pInfoMsg);
if(pInfoMsg->m_VictimTextContainerIndex.Valid())
TextRender()->RenderTextContainer(pInfoMsg->m_VictimTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
// render victim tee
x -= 24.0f;
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &pInfoMsg->m_aVictimRenderInfo[0], OffsetToMid);
const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y);
const int Emote = pInfoMsg->m_RecordPersonal ? EMOTE_HAPPY : EMOTE_NORMAL;
RenderTools()->RenderTee(pIdleState, &pInfoMsg->m_aVictimRenderInfo[0], Emote, vec2(-1, 0), TeeRenderPos);
}
void CInfoMessages::OnRender()
{
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
float StartX = Width * 1.5f - 10.0f;
float y = 30.0f + 100.0f * ((g_Config.m_ClShowfps ? 1 : 0) + (g_Config.m_ClShowpred && Client()->State() != IClient::STATE_DEMOPLAYBACK));
for(int i = 1; i <= MAX_INFOMSGS; i++)
{
CInfoMsg *pInfoMsg = &m_aInfoMsgs[(m_InfoMsgCurrent + i) % MAX_INFOMSGS];
if(Client()->GameTick(g_Config.m_ClDummy) > pInfoMsg->m_Tick + Client()->GameTickSpeed() * 10)
continue;
if(pInfoMsg->m_Type == EType::TYPE_KILL && g_Config.m_ClShowKillMessages)
{
RenderKillMsg(pInfoMsg, StartX, y);
y += 46.0f;
}
else if(pInfoMsg->m_Type == EType::TYPE_FINISH && g_Config.m_ClShowFinishMessages)
{
RenderFinishMsg(pInfoMsg, StartX, y);
y += 46.0f;
}
}
}
void CInfoMessages::RefindSkins()
{
for(auto &InfoMsg : m_aInfoMsgs)
{
InfoMsg.m_KillerRenderInfo.Reset();
if(InfoMsg.m_KillerID >= 0)
{
const CGameClient::CClientData &Client = GameClient()->m_aClients[InfoMsg.m_KillerID];
if(Client.m_Active && Client.m_aSkinName[0] != '\0')
InfoMsg.m_KillerRenderInfo = Client.m_RenderInfo;
else
InfoMsg.m_KillerID = -1;
}
for(int i = 0; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
InfoMsg.m_aVictimRenderInfo[i].Reset();
if(InfoMsg.m_aVictimIds[i] >= 0)
{
const CGameClient::CClientData &Client = GameClient()->m_aClients[InfoMsg.m_aVictimIds[i]];
if(Client.m_Active && Client.m_aSkinName[0] != '\0')
InfoMsg.m_aVictimRenderInfo[i] = Client.m_RenderInfo;
else
InfoMsg.m_aVictimIds[i] = -1;
}
}
}
}

View file

@ -1,24 +1,31 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_KILLMESSAGES_H
#define GAME_CLIENT_COMPONENTS_KILLMESSAGES_H
#ifndef GAME_CLIENT_COMPONENTS_INFOMESSAGES_H
#define GAME_CLIENT_COMPONENTS_INFOMESSAGES_H
#include <game/client/component.h>
#include <game/client/render.h>
class CKillMessages : public CComponent
class CInfoMessages : public CComponent
{
int m_SpriteQuadContainerIndex;
enum
{
MAX_KILLMSGS = 5,
MAX_INFOMSGS = 5,
MAX_KILLMSG_TEAM_MEMBERS = 4,
};
public:
// kill messages
struct CKillMsg
enum EType
{
int m_Weapon;
TYPE_KILL,
TYPE_FINISH,
};
public:
// info messages
struct CInfoMsg
{
EType m_Type;
int m_Tick;
int m_aVictimIds[MAX_KILLMSG_TEAM_MEMBERS];
int m_VictimDDTeam;
@ -32,18 +39,36 @@ public:
float m_KillerTextWidth;
CTeeRenderInfo m_KillerRenderInfo;
// kill msg
int m_Weapon;
int m_ModeSpecial; // for CTF, if the guy is carrying a flag for example
int m_Tick;
int m_FlagCarrierBlue;
int m_TeamSize;
// finish msg
int m_Diff;
char m_aTimeText[32];
char m_aDiffText[32];
STextContainerIndex m_TimeTextContainerIndex;
STextContainerIndex m_DiffTextContainerIndex;
float m_TimeTextWidth;
float m_DiffTextWidth;
bool m_RecordPersonal;
};
private:
void CreateKillmessageNamesIfNotCreated(CKillMsg &Kill);
void AddInfoMsg(EType Type, CInfoMsg NewMsg);
void RenderKillMsg(CInfoMsg *pInfoMsg, float x, float y);
void RenderFinishMsg(CInfoMsg *pInfoMsg, float x, float y);
void CreateNamesIfNotCreated(CInfoMsg *pInfoMsg);
void CreateFinishTextContainersIfNotCreated(CInfoMsg *pInfoMsg);
void DeleteTextContainers(CInfoMsg *pInfoMsg);
public:
CKillMsg m_aKillmsgs[MAX_KILLMSGS];
int m_KillmsgCurrent;
CInfoMsg m_aInfoMsgs[MAX_INFOMSGS];
int m_InfoMsgCurrent;
virtual int Sizeof() const override { return sizeof(*this); }
virtual void OnWindowResize() override;

View file

@ -1,422 +0,0 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <engine/graphics.h>
#include <engine/shared/config.h>
#include <engine/shared/protocol.h>
#include <engine/textrender.h>
#include <game/generated/client_data.h>
#include <game/generated/protocol.h>
#include <game/localization.h>
#include "killmessages.h"
#include <game/client/animstate.h>
#include <game/client/gameclient.h>
#include <game/client/prediction/entities/character.h>
#include <game/client/prediction/gameworld.h>
void CKillMessages::OnWindowResize()
{
for(auto &Killmsg : m_aKillmsgs)
{
TextRender()->DeleteTextContainer(Killmsg.m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(Killmsg.m_KillerTextContainerIndex);
}
}
void CKillMessages::OnReset()
{
m_KillmsgCurrent = 0;
for(auto &Killmsg : m_aKillmsgs)
{
Killmsg.m_Tick = -100000;
TextRender()->DeleteTextContainer(Killmsg.m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(Killmsg.m_KillerTextContainerIndex);
}
}
void CKillMessages::OnInit()
{
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
m_SpriteQuadContainerIndex = Graphics()->CreateQuadContainer(false);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(1, 0, 0, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
Graphics()->QuadsSetSubset(1, 0, 0, 1);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 0.f, 0.f, 28.f, 56.f);
for(int i = 0; i < NUM_WEAPONS; ++i)
{
float ScaleX, ScaleY;
Graphics()->QuadsSetSubset(0, 0, 1, 1);
RenderTools()->GetSpriteScale(g_pData->m_Weapons.m_aId[i].m_pSpriteBody, ScaleX, ScaleY);
RenderTools()->QuadContainerAddSprite(m_SpriteQuadContainerIndex, 96.f * ScaleX, 96.f * ScaleY);
}
Graphics()->QuadContainerUpload(m_SpriteQuadContainerIndex);
}
void CKillMessages::CreateKillmessageNamesIfNotCreated(CKillMsg &Kill)
{
const float FontSize = 36.0f;
if(!Kill.m_VictimTextContainerIndex.Valid() && Kill.m_aVictimName[0] != 0)
{
Kill.m_VictimTextWidth = TextRender()->TextWidth(FontSize, Kill.m_aVictimName, -1, -1.0f);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
unsigned Color = g_Config.m_ClKillMessageNormalColor;
if(Kill.m_aVictimIds[0] == m_pClient->m_Snap.m_LocalClientID)
{
Color = g_Config.m_ClKillMessageHighlightColor;
}
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(Color)));
TextRender()->CreateTextContainer(Kill.m_VictimTextContainerIndex, &Cursor, Kill.m_aVictimName);
}
if(!Kill.m_KillerTextContainerIndex.Valid() && Kill.m_aKillerName[0] != 0)
{
Kill.m_KillerTextWidth = TextRender()->TextWidth(FontSize, Kill.m_aKillerName, -1, -1.0f);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
unsigned Color = g_Config.m_ClKillMessageNormalColor;
if(Kill.m_KillerID == m_pClient->m_Snap.m_LocalClientID)
{
Color = g_Config.m_ClKillMessageHighlightColor;
}
TextRender()->TextColor(color_cast<ColorRGBA>(ColorHSLA(Color)));
TextRender()->CreateTextContainer(Kill.m_KillerTextContainerIndex, &Cursor, Kill.m_aKillerName);
}
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
void CKillMessages::OnMessage(int MsgType, void *pRawMsg)
{
if(m_pClient->m_SuppressEvents)
return;
if(MsgType == NETMSGTYPE_SV_KILLMSGTEAM)
{
CNetMsg_Sv_KillMsgTeam *pMsg = (CNetMsg_Sv_KillMsgTeam *)pRawMsg;
CKillMsg Kill{};
std::vector<std::pair<int, int>> vStrongWeakSorted;
for(int i = 0; i < MAX_CLIENTS; i++)
{
if(m_pClient->m_Teams.Team(i) == pMsg->m_Team)
{
CCharacter *pChr = m_pClient->m_GameWorld.GetCharacterByID(i);
vStrongWeakSorted.emplace_back(i, pMsg->m_First == i ? MAX_CLIENTS : pChr ? pChr->GetStrongWeakID() : 0);
}
}
std::stable_sort(vStrongWeakSorted.begin(), vStrongWeakSorted.end(), [](auto &Left, auto &Right) { return Left.second > Right.second; });
Kill.m_TeamSize = vStrongWeakSorted.size();
if(Kill.m_TeamSize > MAX_KILLMSG_TEAM_MEMBERS)
Kill.m_TeamSize = MAX_KILLMSG_TEAM_MEMBERS;
Kill.m_VictimDDTeam = pMsg->m_Team;
for(int i = 0; i < Kill.m_TeamSize; i++)
{
if(m_pClient->m_aClients[vStrongWeakSorted[i].first].m_Active)
{
Kill.m_aVictimIds[i] = vStrongWeakSorted[i].first;
Kill.m_aVictimRenderInfo[i] = m_pClient->m_aClients[vStrongWeakSorted[i].first].m_RenderInfo;
}
else
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
}
for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
str_format(Kill.m_aVictimName, sizeof(Kill.m_aVictimName), Localize("Team %d"), pMsg->m_Team);
Kill.m_KillerID = -1;
Kill.m_aKillerName[0] = '\0';
Kill.m_KillerRenderInfo.Reset();
Kill.m_Weapon = -1;
Kill.m_ModeSpecial = 0;
Kill.m_Tick = Client()->GameTick(g_Config.m_ClDummy);
Kill.m_VictimTextWidth = Kill.m_KillerTextWidth = 0.f;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
CreateKillmessageNamesIfNotCreated(Kill);
bool KillMsgValid = true;
for(int i = 0; i < Kill.m_TeamSize; i++)
{
KillMsgValid = KillMsgValid && Kill.m_aVictimIds[i] >= 0 && ((Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[i].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[i].m_OriginalRenderSkin.m_Body.IsValid()));
}
if(KillMsgValid)
{
// add the message
m_KillmsgCurrent = (m_KillmsgCurrent + 1) % MAX_KILLMSGS;
TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_KillerTextContainerIndex);
m_aKillmsgs[m_KillmsgCurrent] = Kill;
}
else
{
TextRender()->DeleteTextContainer(Kill.m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(Kill.m_KillerTextContainerIndex);
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
if(MsgType == NETMSGTYPE_SV_KILLMSG)
{
CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg;
CKillMsg Kill{};
Kill.m_TeamSize = 1;
Kill.m_aVictimIds[0] = pMsg->m_Victim;
if(Kill.m_aVictimIds[0] >= 0 && Kill.m_aVictimIds[0] < MAX_CLIENTS)
{
Kill.m_VictimDDTeam = m_pClient->m_Teams.Team(Kill.m_aVictimIds[0]);
str_copy(Kill.m_aVictimName, m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_aName);
Kill.m_aVictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_aVictimIds[0]].m_RenderInfo;
}
else
{
Kill.m_VictimDDTeam = 0;
Kill.m_aVictimName[0] = '\0';
}
for(int i = Kill.m_TeamSize; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
Kill.m_aVictimIds[i] = -1;
Kill.m_aVictimRenderInfo[i].Reset();
}
Kill.m_KillerID = pMsg->m_Killer;
if(Kill.m_KillerID >= 0 && Kill.m_KillerID < MAX_CLIENTS)
{
str_copy(Kill.m_aKillerName, m_pClient->m_aClients[Kill.m_KillerID].m_aName);
Kill.m_KillerRenderInfo = m_pClient->m_aClients[Kill.m_KillerID].m_RenderInfo;
}
else
{
Kill.m_aKillerName[0] = '\0';
Kill.m_KillerRenderInfo.Reset();
}
Kill.m_Weapon = pMsg->m_Weapon;
Kill.m_ModeSpecial = pMsg->m_ModeSpecial;
Kill.m_Tick = Client()->GameTick(g_Config.m_ClDummy);
Kill.m_FlagCarrierBlue = m_pClient->m_Snap.m_pGameDataObj ? m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue : -1;
Kill.m_VictimTextWidth = Kill.m_KillerTextWidth = 0.f;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
CreateKillmessageNamesIfNotCreated(Kill);
bool KillMsgValid = (Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_aVictimRenderInfo[0].m_CustomColoredSkin && Kill.m_aVictimRenderInfo[0].m_OriginalRenderSkin.m_Body.IsValid());
KillMsgValid &= Kill.m_KillerID == -1 || ((Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_OriginalRenderSkin.m_Body.IsValid()));
if(KillMsgValid)
{
// add the message
m_KillmsgCurrent = (m_KillmsgCurrent + 1) % MAX_KILLMSGS;
TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_KillerTextContainerIndex);
m_aKillmsgs[m_KillmsgCurrent] = Kill;
}
else
{
TextRender()->DeleteTextContainer(Kill.m_VictimTextContainerIndex);
TextRender()->DeleteTextContainer(Kill.m_KillerTextContainerIndex);
}
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
}
void CKillMessages::OnRender()
{
if(!g_Config.m_ClShowKillMessages)
return;
float Height = 400 * 3.0f;
float Width = Height * Graphics()->ScreenAspect();
Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
float StartX = Width * 1.5f - 10.0f;
float y = 30.0f + 100.0f * ((g_Config.m_ClShowfps ? 1 : 0) + (g_Config.m_ClShowpred && Client()->State() != IClient::STATE_DEMOPLAYBACK));
for(int i = 1; i <= MAX_KILLMSGS; i++)
{
int r = (m_KillmsgCurrent + i) % MAX_KILLMSGS;
if(Client()->GameTick(g_Config.m_ClDummy) > m_aKillmsgs[r].m_Tick + Client()->GameTickSpeed() * 10)
continue;
float x = StartX;
// render victim name
x -= m_aKillmsgs[r].m_VictimTextWidth;
ColorRGBA TextColor;
if(g_Config.m_ClChatTeamColors && m_aKillmsgs[r].m_VictimDDTeam)
TextColor = m_pClient->GetDDTeamColor(m_aKillmsgs[r].m_VictimDDTeam, 0.75f);
else
TextColor = TextRender()->DefaultTextColor();
CreateKillmessageNamesIfNotCreated(m_aKillmsgs[r]);
if(m_aKillmsgs[r].m_VictimTextContainerIndex.Valid())
TextRender()->RenderTextContainer(m_aKillmsgs[r].m_VictimTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
// render victim tee
x -= 24.0f;
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS)
{
if(m_aKillmsgs[r].m_ModeSpecial & 1)
{
int QuadOffset = 0;
if(m_aKillmsgs[r].m_aVictimIds[0] == m_aKillmsgs[r].m_FlagCarrierBlue)
++QuadOffset;
if(QuadOffset == 0)
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagRed);
else
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagBlue);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, QuadOffset, x, y - 16);
}
}
for(int j = (m_aKillmsgs[r].m_TeamSize - 1); j >= 0; j--)
{
if(m_aKillmsgs[r].m_aVictimIds[j] < 0)
continue;
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &m_aKillmsgs[r].m_aVictimRenderInfo[j], OffsetToMid);
const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &m_aKillmsgs[r].m_aVictimRenderInfo[j], EMOTE_PAIN, vec2(-1, 0), TeeRenderPos);
x -= 44.0f;
}
// render weapon
x -= 32.0f;
if(m_aKillmsgs[r].m_Weapon >= 0)
{
Graphics()->TextureSet(GameClient()->m_GameSkin.m_aSpriteWeapons[m_aKillmsgs[r].m_Weapon]);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, 4 + m_aKillmsgs[r].m_Weapon, x, y + 28);
}
x -= 52.0f;
if(m_aKillmsgs[r].m_aVictimIds[0] != m_aKillmsgs[r].m_KillerID)
{
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_FLAGS)
{
if(m_aKillmsgs[r].m_ModeSpecial & 2)
{
int QuadOffset = 2;
if(m_aKillmsgs[r].m_KillerID == m_aKillmsgs[r].m_FlagCarrierBlue)
++QuadOffset;
if(QuadOffset == 2)
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagRed);
else
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteFlagBlue);
Graphics()->RenderQuadContainerAsSprite(m_SpriteQuadContainerIndex, QuadOffset, x - 56, y - 16);
}
}
// render killer tee
x -= 24.0f;
if(m_aKillmsgs[r].m_KillerID >= 0)
{
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &m_aKillmsgs[r].m_KillerRenderInfo, OffsetToMid);
const vec2 TeeRenderPos = vec2(x, y + 46.0f / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &m_aKillmsgs[r].m_KillerRenderInfo, EMOTE_ANGRY, vec2(1, 0), TeeRenderPos);
}
x -= 32.0f;
// render killer name
x -= m_aKillmsgs[r].m_KillerTextWidth;
if(m_aKillmsgs[r].m_KillerTextContainerIndex.Valid())
TextRender()->RenderTextContainer(m_aKillmsgs[r].m_KillerTextContainerIndex, TextColor, TextRender()->DefaultTextOutlineColor(), x, y + (46.f - 36.f) / 2.f);
}
y += 46.0f;
}
}
void CKillMessages::RefindSkins()
{
for(auto &KillMsg : m_aKillmsgs)
{
KillMsg.m_KillerRenderInfo.Reset();
if(KillMsg.m_KillerID >= 0)
{
const CGameClient::CClientData &Client = GameClient()->m_aClients[KillMsg.m_KillerID];
if(Client.m_Active && Client.m_aSkinName[0] != '\0')
KillMsg.m_KillerRenderInfo = Client.m_RenderInfo;
else
KillMsg.m_KillerID = -1;
}
for(int i = 0; i < MAX_KILLMSG_TEAM_MEMBERS; i++)
{
KillMsg.m_aVictimRenderInfo[i].Reset();
if(KillMsg.m_aVictimIds[i] >= 0)
{
const CGameClient::CClientData &Client = GameClient()->m_aClients[KillMsg.m_aVictimIds[i]];
if(Client.m_Active && Client.m_aSkinName[0] != '\0')
KillMsg.m_aVictimRenderInfo[i] = Client.m_RenderInfo;
else
KillMsg.m_aVictimIds[i] = -1;
}
}
}
}

View file

@ -78,7 +78,7 @@ void CMenus::HandleDemoSeeking(float PositionToSeek, float TimeToSeek)
if((PositionToSeek >= 0.0f && PositionToSeek <= 1.0f) || TimeToSeek != 0.0f)
{
m_pClient->m_Chat.Reset();
m_pClient->m_KillMessages.OnReset();
m_pClient->m_InfoMessages.OnReset();
m_pClient->m_Particles.OnReset();
m_pClient->m_Sounds.OnReset();
m_pClient->m_Scoreboard.OnReset();

View file

@ -2429,7 +2429,7 @@ enum
APPEARANCE_TAB_CHAT = 1,
APPEARANCE_TAB_NAME_PLATE = 2,
APPEARANCE_TAB_HOOK_COLLISION = 3,
APPEARANCE_TAB_KILL_MESSAGES = 4,
APPEARANCE_TAB_INFO_MESSAGES = 4,
APPEARANCE_TAB_LASER = 5,
NUMBER_OF_APPEARANCE_TABS = 6,
};
@ -2459,8 +2459,8 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
s_CurTab = APPEARANCE_TAB_NAME_PLATE;
if(DoButton_MenuTab(&s_aPageTabs[APPEARANCE_TAB_HOOK_COLLISION], Localize("Hook Collisions"), s_CurTab == APPEARANCE_TAB_HOOK_COLLISION, &Page4Tab, 0, NULL, NULL, NULL, NULL, 4))
s_CurTab = APPEARANCE_TAB_HOOK_COLLISION;
if(DoButton_MenuTab(&s_aPageTabs[APPEARANCE_TAB_KILL_MESSAGES], Localize("Kill Messages"), s_CurTab == APPEARANCE_TAB_KILL_MESSAGES, &Page5Tab, 0, NULL, NULL, NULL, NULL, 4))
s_CurTab = APPEARANCE_TAB_KILL_MESSAGES;
if(DoButton_MenuTab(&s_aPageTabs[APPEARANCE_TAB_INFO_MESSAGES], Localize("Info Messages"), s_CurTab == APPEARANCE_TAB_INFO_MESSAGES, &Page5Tab, 0, NULL, NULL, NULL, NULL, 4))
s_CurTab = APPEARANCE_TAB_INFO_MESSAGES;
if(DoButton_MenuTab(&s_aPageTabs[APPEARANCE_TAB_LASER], Localize("Laser"), s_CurTab == APPEARANCE_TAB_LASER, &Page6Tab, IGraphics::CORNER_R, NULL, NULL, NULL, NULL, 4))
s_CurTab = APPEARANCE_TAB_LASER;
@ -3005,20 +3005,31 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView)
DoLine_ColorPicker(&s_HookCollHookableCollResetID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Something hookable"), &g_Config.m_ClHookCollColorHookableColl, ColorRGBA(130.0f / 255.0f, 232.0f / 255.0f, 160.0f / 255.0f, 1.0f), false);
DoLine_ColorPicker(&s_HookCollTeeCollResetID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("A Tee"), &g_Config.m_ClHookCollColorTeeColl, ColorRGBA(1.0f, 1.0f, 0.0f, 1.0f), false);
}
else if(s_CurTab == APPEARANCE_TAB_KILL_MESSAGES)
else if(s_CurTab == APPEARANCE_TAB_INFO_MESSAGES)
{
MainView.VSplitMid(&LeftView, &RightView);
// ***** Kill Messages ***** //
// ***** Info Messages ***** //
LeftView.HSplitTop(HeadlineAndVMargin, &Label, &LeftView);
UI()->DoLabel(&Label, Localize("Kill Messages"), HeadlineFontSize, TEXTALIGN_ML);
UI()->DoLabel(&Label, Localize("Info Messages"), HeadlineFontSize, TEXTALIGN_ML);
// General kill messages settings
LeftView.HSplitTop(SectionTotalMargin + 2 * ColorPickerLineSize, &Section, &LeftView);
// General info messages settings
LeftView.HSplitTop(SectionTotalMargin + 2 * LineSize + 2 * ColorPickerLineSize, &Section, &LeftView);
Section.Margin(SectionMargin, &Section);
static CButtonContainer s_KillMessageNormalColorID, s_KillMessageHighlightColorID;
Section.HSplitTop(LineSize, &Button, &Section);
if(DoButton_CheckBox(&g_Config.m_ClShowKillMessages, Localize("Show kill messages"), g_Config.m_ClShowKillMessages, &Button))
{
g_Config.m_ClShowKillMessages ^= 1;
}
Section.HSplitTop(LineSize, &Button, &Section);
if(DoButton_CheckBox(&g_Config.m_ClShowFinishMessages, Localize("Show finish messages"), g_Config.m_ClShowFinishMessages, &Button))
{
g_Config.m_ClShowFinishMessages ^= 1;
}
static CButtonContainer s_KillMessageNormalColorID, s_KillMessageHighlightColorID;
DoLine_ColorPicker(&s_KillMessageNormalColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Normal Color"), &g_Config.m_ClKillMessageNormalColor, ColorRGBA(1.0f, 1.0f, 1.0f), false);
DoLine_ColorPicker(&s_KillMessageHighlightColorID, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &Section, Localize("Highlight Color"), &g_Config.m_ClKillMessageHighlightColor, ColorRGBA(1.0f, 1.0f, 1.0f), false);
}

View file

@ -51,8 +51,8 @@
#include "components/freezebars.h"
#include "components/ghost.h"
#include "components/hud.h"
#include "components/infomessages.h"
#include "components/items.h"
#include "components/killmessages.h"
#include "components/mapimages.h"
#include "components/maplayers.h"
#include "components/mapsounds.h"
@ -135,7 +135,7 @@ void CGameClient::OnConsoleInit()
&m_Hud,
&m_Spectator,
&m_Emoticon,
&m_KillMessages,
&m_InfoMessages,
&m_Chat,
&m_Broadcast,
&m_DebugHud,
@ -3389,7 +3389,7 @@ void CGameClient::RefindSkins()
}
m_Ghost.RefindSkins();
m_Chat.RefindSkins();
m_KillMessages.RefindSkins();
m_InfoMessages.RefindSkins();
}
static bool UnknownMapSettingCallback(const char *pCommand, void *pUser)

View file

@ -34,8 +34,8 @@
#include "components/freezebars.h"
#include "components/ghost.h"
#include "components/hud.h"
#include "components/infomessages.h"
#include "components/items.h"
#include "components/killmessages.h"
#include "components/mapimages.h"
#include "components/maplayers.h"
#include "components/mapsounds.h"
@ -111,7 +111,7 @@ class CGameClient : public IGameClient
{
public:
// all components
CKillMessages m_KillMessages;
CInfoMessages m_InfoMessages;
CCamera m_Camera;
CChat m_Chat;
CMotd m_Motd;

View file

@ -770,6 +770,18 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp)
Server()->SendPackMsg(&MsgLegacy, MSGFLAG_VITAL, ClientID);
}
}
CNetMsg_Sv_RaceFinish RaceFinishMsg;
RaceFinishMsg.m_ClientID = ClientID;
RaceFinishMsg.m_Time = Time * 1000;
RaceFinishMsg.m_Diff = 0;
if(pData->m_BestTime)
{
RaceFinishMsg.m_Diff = Diff * 1000 * (Time < pData->m_BestTime ? -1 : 1);
}
RaceFinishMsg.m_RecordPersonal = (Time < pData->m_BestTime || !pData->m_BestTime);
RaceFinishMsg.m_RecordServer = Time < GameServer()->m_pController->m_CurrentRecord;
Server()->SendPackMsg(&RaceFinishMsg, MSGFLAG_VITAL | MSGFLAG_NORECORD, -1);
}
else
{
@ -781,7 +793,7 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp)
{
Msg.m_Diff = Diff * 1000 * (Time < pData->m_BestTime ? -1 : 1);
}
Msg.m_RecordPersonal = Time < pData->m_BestTime;
Msg.m_RecordPersonal = (Time < pData->m_BestTime || !pData->m_BestTime);
Msg.m_RecordServer = Time < GameServer()->m_pController->m_CurrentRecord;
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL | MSGFLAG_NORECORD, -1);
}

View file

@ -757,11 +757,11 @@ TEST(Str, Time)
EXPECT_EQ(str_time(123456, TIME_DAYS, aBuf, 0), -1);
EXPECT_STREQ(aBuf, "foobar");
EXPECT_EQ(str_time(123456, TIME_MINS_CENTISECS + 1, aBuf, sizeof(aBuf)), -1);
EXPECT_EQ(str_time(123456, TIME_SECS_CENTISECS + 1, aBuf, sizeof(aBuf)), -1);
EXPECT_STREQ(aBuf, "");
EXPECT_EQ(str_time(-123456, TIME_MINS_CENTISECS, aBuf, sizeof(aBuf)), 8);
EXPECT_STREQ(aBuf, "00:00.00");
EXPECT_EQ(str_time(-123456, TIME_MINS_CENTISECS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "00.00");
EXPECT_EQ(str_time(INT64_MAX, TIME_DAYS, aBuf, sizeof(aBuf)), 23);
EXPECT_STREQ(aBuf, "1067519911673d 00:09:18");
@ -800,6 +800,13 @@ TEST(Str, Time)
EXPECT_STREQ(aBuf, "205:45.67");
EXPECT_EQ(str_time(12345678, TIME_MINS_CENTISECS, aBuf, sizeof(aBuf)), 10);
EXPECT_STREQ(aBuf, "2057:36.78");
EXPECT_EQ(str_time(123456, TIME_SECS_CENTISECS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "34.56");
EXPECT_EQ(str_time(1234567, TIME_SECS_CENTISECS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "45.67");
EXPECT_EQ(str_time(12345678, TIME_SECS_CENTISECS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "36.78");
}
TEST(Str, TimeFloat)
@ -808,8 +815,8 @@ TEST(Str, TimeFloat)
EXPECT_EQ(str_time_float(123456.78, TIME_DAYS, aBuf, sizeof(aBuf)), 11);
EXPECT_STREQ(aBuf, "1d 10:17:36");
EXPECT_EQ(str_time_float(12.16, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf)), 8);
EXPECT_STREQ(aBuf, "00:12.16");
EXPECT_EQ(str_time_float(12.16, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "12.16");
EXPECT_EQ(str_time_float(22.995, TIME_MINS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "00:22");