mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-13 11:38:19 +00:00
fa2ac1bb64
* master: (87 commits) Remove base/tl/string.h Replace remaining usage of base/tl/string with std::string Remove unused includes of base/tl/string.h Store localized strings in a CHeap instead of using tl/string.h Mark methods as const Add CHeap::StoreString method Rules are chat responses too Add margins to demo slice popup, decrease error font size, UI scaling Remove redundant parameters which are overridden later Use Margin instead of both VMargin and HMargin Move variable declaration Only output messages intended for chat to the user of a chat command Remove unused chat response variables Don't print the first "Waiting for score threads to complete" fix usage of undefined behavior for default eyes remove duplicate HOOK_RETRACTED assignment do not send swap request notification to complete team 0 make swap messages more personal Move ninja shield to other position (fixes #5047) do not release the hooks if you swap ...
904 lines
33 KiB
C++
904 lines
33 KiB
C++
/* (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 <base/tl/sorted_array.h>
|
|
|
|
#include <engine/demo.h>
|
|
#include <engine/engine.h>
|
|
#include <engine/graphics.h>
|
|
#include <engine/serverbrowser.h>
|
|
#include <engine/shared/config.h>
|
|
#include <game/generated/client_data.h>
|
|
#include <game/generated/protocol.h>
|
|
|
|
#include <game/client/animstate.h>
|
|
#include <game/client/gameclient.h>
|
|
#include <game/client/render.h>
|
|
#include <game/client/ui.h>
|
|
|
|
#include <game/client/components/controls.h>
|
|
#include <game/client/components/effects.h>
|
|
#include <game/client/components/flow.h>
|
|
#include <game/client/components/skins.h>
|
|
#include <game/client/components/sounds.h>
|
|
|
|
#include <engine/textrender.h>
|
|
|
|
#include "players.h"
|
|
|
|
#include <base/color.h>
|
|
|
|
void CPlayers::RenderHand(CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset, float Alpha)
|
|
{
|
|
vec2 HandPos = CenterPos + Dir;
|
|
float Angle = angle(Dir);
|
|
if(Dir.x < 0)
|
|
Angle -= AngleOffset;
|
|
else
|
|
Angle += AngleOffset;
|
|
|
|
vec2 DirX = Dir;
|
|
vec2 DirY(-Dir.y, Dir.x);
|
|
|
|
if(Dir.x < 0)
|
|
DirY = -DirY;
|
|
|
|
HandPos += DirX * PostRotOffset.x;
|
|
HandPos += DirY * PostRotOffset.y;
|
|
|
|
const CSkin::SSkinTextures *pSkinTextures = pInfo->m_CustomColoredSkin ? &pInfo->m_ColorableRenderSkin : &pInfo->m_OriginalRenderSkin;
|
|
|
|
Graphics()->SetColor(pInfo->m_ColorBody.r, pInfo->m_ColorBody.g, pInfo->m_ColorBody.b, Alpha);
|
|
|
|
// two passes
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
int QuadOffset = NUM_WEAPONS * 2 + i;
|
|
Graphics()->QuadsSetRotation(Angle);
|
|
Graphics()->TextureSet(i == 0 ? pSkinTextures->m_HandsOutline : pSkinTextures->m_Hands);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, HandPos.x, HandPos.y);
|
|
}
|
|
}
|
|
|
|
inline float NormalizeAngular(float f)
|
|
{
|
|
return fmod(f + pi * 2, pi * 2);
|
|
}
|
|
|
|
inline float AngularMixDirection(float Src, float Dst) { return sinf(Dst - Src) > 0 ? 1 : -1; }
|
|
inline float AngularDistance(float Src, float Dst) { return asinf(sinf(Dst - Src)); }
|
|
|
|
inline float AngularApproach(float Src, float Dst, float Amount)
|
|
{
|
|
float d = AngularMixDirection(Src, Dst);
|
|
float n = Src + Amount * d;
|
|
if(AngularMixDirection(n, Dst) != d)
|
|
return Dst;
|
|
return n;
|
|
}
|
|
|
|
float CPlayers::GetPlayerTargetAngle(
|
|
const CNetObj_Character *pPrevChar,
|
|
const CNetObj_Character *pPlayerChar,
|
|
int ClientID,
|
|
float Intra)
|
|
{
|
|
float AngleIntraTick = Intra;
|
|
// using unpredicted angle when rendering other players in-game
|
|
if(ClientID >= 0)
|
|
AngleIntraTick = Client()->IntraGameTick(g_Config.m_ClDummy);
|
|
if(ClientID >= 0 && m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo)
|
|
{
|
|
CNetObj_DDNetCharacterDisplayInfo *CharacterDisplayInfo = &m_pClient->m_Snap.m_aCharacters[ClientID].m_ExtendedDisplayInfo;
|
|
if(m_pClient->m_Snap.m_aCharacters[ClientID].m_PrevExtendedDisplayInfo)
|
|
{
|
|
const CNetObj_DDNetCharacterDisplayInfo *PrevCharacterDisplayInfo = m_pClient->m_Snap.m_aCharacters[ClientID].m_PrevExtendedDisplayInfo;
|
|
|
|
float MixX = mix((float)PrevCharacterDisplayInfo->m_TargetX, (float)CharacterDisplayInfo->m_TargetX, AngleIntraTick);
|
|
float MixY = mix((float)PrevCharacterDisplayInfo->m_TargetY, (float)CharacterDisplayInfo->m_TargetY, AngleIntraTick);
|
|
|
|
return angle(vec2(MixX, MixY));
|
|
}
|
|
else
|
|
{
|
|
return angle(vec2(CharacterDisplayInfo->m_TargetX, CharacterDisplayInfo->m_TargetY));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the player moves their weapon through top, then change
|
|
// the end angle by 2*Pi, so that the mix function will use the
|
|
// short path and not the long one.
|
|
if(pPlayerChar->m_Angle > (256.0f * pi) && pPrevChar->m_Angle < 0)
|
|
{
|
|
return mix((float)pPrevChar->m_Angle, (float)(pPlayerChar->m_Angle - 256.0f * 2 * pi), AngleIntraTick) / 256.0f;
|
|
}
|
|
else if(pPlayerChar->m_Angle < 0 && pPrevChar->m_Angle > (256.0f * pi))
|
|
{
|
|
return mix((float)pPrevChar->m_Angle, (float)(pPlayerChar->m_Angle + 256.0f * 2 * pi), AngleIntraTick) / 256.0f;
|
|
}
|
|
else
|
|
{
|
|
return mix((float)pPrevChar->m_Angle, (float)pPlayerChar->m_Angle, AngleIntraTick) / 256.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPlayers::RenderHookCollLine(
|
|
const CNetObj_Character *pPrevChar,
|
|
const CNetObj_Character *pPlayerChar,
|
|
int ClientID,
|
|
float Intra)
|
|
{
|
|
CNetObj_Character Prev;
|
|
CNetObj_Character Player;
|
|
Prev = *pPrevChar;
|
|
Player = *pPlayerChar;
|
|
|
|
bool Local = m_pClient->m_Snap.m_LocalClientID == ClientID;
|
|
bool OtherTeam = m_pClient->IsOtherTeam(ClientID);
|
|
float Alpha = (OtherTeam || ClientID < 0) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
|
|
Alpha *= (float)g_Config.m_ClHookCollAlpha / 100;
|
|
|
|
float IntraTick = Intra;
|
|
if(ClientID >= 0)
|
|
IntraTick = m_pClient->m_aClients[ClientID].m_IsPredicted ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy);
|
|
|
|
float Angle;
|
|
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
|
{
|
|
// just use the direct input if it's the local player we are rendering
|
|
Angle = angle(m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy]);
|
|
}
|
|
else
|
|
{
|
|
Angle = GetPlayerTargetAngle(&Prev, &Player, ClientID, IntraTick);
|
|
}
|
|
|
|
vec2 Direction = direction(Angle);
|
|
vec2 Position;
|
|
if(in_range(ClientID, MAX_CLIENTS - 1))
|
|
Position = m_pClient->m_aClients[ClientID].m_RenderPos;
|
|
else
|
|
Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick);
|
|
// draw hook collision line
|
|
{
|
|
bool AlwaysRenderHookColl = GameClient()->m_GameInfo.m_AllowHookColl && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) == 2;
|
|
bool RenderHookCollPlayer = ClientID >= 0 && Player.m_PlayerFlags & PLAYERFLAG_AIM && (Local ? g_Config.m_ClShowHookCollOwn : g_Config.m_ClShowHookCollOther) > 0;
|
|
bool RenderHookCollVideo = true;
|
|
#if defined(CONF_VIDEORECORDER)
|
|
RenderHookCollVideo = !IVideo::Current() || g_Config.m_ClVideoShowHookCollOther || Local;
|
|
#endif
|
|
if((AlwaysRenderHookColl || RenderHookCollPlayer) && RenderHookCollVideo)
|
|
{
|
|
vec2 ExDirection = Direction;
|
|
|
|
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
|
{
|
|
ExDirection = normalize(vec2((int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].x, (int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].y));
|
|
|
|
// fix direction if mouse is exactly in the center
|
|
if(!(int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].x && !(int)m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy].y)
|
|
ExDirection = vec2(1, 0);
|
|
}
|
|
Graphics()->TextureClear();
|
|
vec2 InitPos = Position;
|
|
vec2 FinishPos = InitPos + ExDirection * (m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength - 42.0f);
|
|
|
|
if(g_Config.m_ClHookCollSize > 0)
|
|
Graphics()->QuadsBegin();
|
|
else
|
|
Graphics()->LinesBegin();
|
|
|
|
ColorRGBA HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorNoColl));
|
|
|
|
float PhysSize = 28.0f;
|
|
|
|
vec2 OldPos = InitPos + ExDirection * PhysSize * 1.5f;
|
|
vec2 NewPos = OldPos;
|
|
|
|
bool DoBreak = false;
|
|
int Hit = 0;
|
|
|
|
do
|
|
{
|
|
OldPos = NewPos;
|
|
NewPos = OldPos + ExDirection * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookFireSpeed;
|
|
|
|
if(distance(InitPos, NewPos) > m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength)
|
|
{
|
|
NewPos = InitPos + normalize(NewPos - InitPos) * m_pClient->m_Tuning[g_Config.m_ClDummy].m_HookLength;
|
|
DoBreak = true;
|
|
}
|
|
|
|
int TeleNr = 0;
|
|
Hit = Collision()->IntersectLineTeleHook(OldPos, NewPos, &FinishPos, 0x0, &TeleNr);
|
|
|
|
if(!DoBreak && Hit)
|
|
{
|
|
if(Hit != TILE_NOHOOK)
|
|
{
|
|
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorHookableColl));
|
|
}
|
|
}
|
|
|
|
if(m_pClient->IntersectCharacter(OldPos, FinishPos, FinishPos, ClientID) != -1)
|
|
{
|
|
HookCollColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClHookCollColorTeeColl));
|
|
break;
|
|
}
|
|
|
|
if(Hit)
|
|
break;
|
|
|
|
NewPos.x = round_to_int(NewPos.x);
|
|
NewPos.y = round_to_int(NewPos.y);
|
|
|
|
if(OldPos == NewPos)
|
|
break;
|
|
|
|
ExDirection.x = round_to_int(ExDirection.x * 256.0f) / 256.0f;
|
|
ExDirection.y = round_to_int(ExDirection.y * 256.0f) / 256.0f;
|
|
} while(!DoBreak);
|
|
|
|
if(AlwaysRenderHookColl && RenderHookCollPlayer)
|
|
{
|
|
// invert the hook coll colors when using cl_show_hook_coll_always and +showhookcoll is pressed
|
|
HookCollColor = color_invert(HookCollColor);
|
|
}
|
|
Graphics()->SetColor(HookCollColor.WithAlpha(Alpha));
|
|
if(g_Config.m_ClHookCollSize > 0)
|
|
{
|
|
float LineWidth = 0.5f + (float)(g_Config.m_ClHookCollSize - 1) * 0.25f;
|
|
vec2 PerpToAngle = normalize(vec2(ExDirection.y, -ExDirection.x)) * GameClient()->m_Camera.m_Zoom;
|
|
vec2 Pos0 = FinishPos + PerpToAngle * -LineWidth;
|
|
vec2 Pos1 = FinishPos + PerpToAngle * LineWidth;
|
|
vec2 Pos2 = InitPos + PerpToAngle * -LineWidth;
|
|
vec2 Pos3 = InitPos + PerpToAngle * LineWidth;
|
|
IGraphics::CFreeformItem FreeformItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y, Pos2.x, Pos2.y, Pos3.x, Pos3.y);
|
|
Graphics()->QuadsDrawFreeform(&FreeformItem, 1);
|
|
Graphics()->QuadsEnd();
|
|
}
|
|
else
|
|
{
|
|
IGraphics::CLineItem LineItem(InitPos.x, InitPos.y, FinishPos.x, FinishPos.y);
|
|
Graphics()->LinesDraw(&LineItem, 1);
|
|
Graphics()->LinesEnd();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void CPlayers::RenderHook(
|
|
const CNetObj_Character *pPrevChar,
|
|
const CNetObj_Character *pPlayerChar,
|
|
const CTeeRenderInfo *pRenderInfo,
|
|
int ClientID,
|
|
float Intra)
|
|
{
|
|
CNetObj_Character Prev;
|
|
CNetObj_Character Player;
|
|
Prev = *pPrevChar;
|
|
Player = *pPlayerChar;
|
|
|
|
CTeeRenderInfo RenderInfo = *pRenderInfo;
|
|
|
|
// don't render hooks to not active character cores
|
|
if(pPlayerChar->m_HookedPlayer != -1 && !m_pClient->m_Snap.m_aCharacters[pPlayerChar->m_HookedPlayer].m_Active)
|
|
return;
|
|
|
|
float IntraTick = Intra;
|
|
if(ClientID >= 0)
|
|
IntraTick = (m_pClient->m_aClients[ClientID].m_IsPredicted) ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy);
|
|
|
|
bool OtherTeam = m_pClient->IsOtherTeam(ClientID);
|
|
float Alpha = (OtherTeam || ClientID < 0) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
|
|
|
|
RenderInfo.m_Size = 64.0f;
|
|
|
|
vec2 Position;
|
|
if(in_range(ClientID, MAX_CLIENTS - 1))
|
|
Position = m_pClient->m_aClients[ClientID].m_RenderPos;
|
|
else
|
|
Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick);
|
|
|
|
// draw hook
|
|
if(Prev.m_HookState > 0 && Player.m_HookState > 0)
|
|
{
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
if(ClientID < 0)
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
vec2 Pos = Position;
|
|
vec2 HookPos;
|
|
|
|
if(in_range(pPlayerChar->m_HookedPlayer, MAX_CLIENTS - 1))
|
|
HookPos = m_pClient->m_aClients[pPlayerChar->m_HookedPlayer].m_RenderPos;
|
|
else
|
|
HookPos = mix(vec2(Prev.m_HookX, Prev.m_HookY), vec2(Player.m_HookX, Player.m_HookY), IntraTick);
|
|
|
|
float d = distance(Pos, HookPos);
|
|
vec2 Dir = normalize(Pos - HookPos);
|
|
|
|
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteHookHead);
|
|
Graphics()->QuadsSetRotation(angle(Dir) + pi);
|
|
// render head
|
|
int QuadOffset = NUM_WEAPONS * 2 + 2;
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, Alpha);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, HookPos.x, HookPos.y);
|
|
|
|
// render chain
|
|
++QuadOffset;
|
|
static IGraphics::SRenderSpriteInfo s_HookChainRenderInfo[1024];
|
|
int HookChainCount = 0;
|
|
for(float f = 24; f < d && HookChainCount < 1024; f += 24, ++HookChainCount)
|
|
{
|
|
vec2 p = HookPos + Dir * f;
|
|
s_HookChainRenderInfo[HookChainCount].m_Pos[0] = p.x;
|
|
s_HookChainRenderInfo[HookChainCount].m_Pos[1] = p.y;
|
|
|
|
s_HookChainRenderInfo[HookChainCount].m_Scale = 1;
|
|
s_HookChainRenderInfo[HookChainCount].m_Rotation = angle(Dir) + pi;
|
|
}
|
|
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteHookChain);
|
|
Graphics()->RenderQuadContainerAsSpriteMultiple(m_WeaponEmoteQuadContainerIndex, QuadOffset, HookChainCount, s_HookChainRenderInfo);
|
|
|
|
Graphics()->QuadsSetRotation(0);
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
RenderHand(&RenderInfo, Position, normalize(HookPos - Pos), -pi / 2, vec2(20, 0), Alpha);
|
|
}
|
|
}
|
|
|
|
void CPlayers::RenderPlayer(
|
|
const CNetObj_Character *pPrevChar,
|
|
const CNetObj_Character *pPlayerChar,
|
|
const CTeeRenderInfo *pRenderInfo,
|
|
int ClientID,
|
|
float Intra)
|
|
{
|
|
CNetObj_Character Prev;
|
|
CNetObj_Character Player;
|
|
Prev = *pPrevChar;
|
|
Player = *pPlayerChar;
|
|
|
|
CTeeRenderInfo RenderInfo = *pRenderInfo;
|
|
|
|
bool Local = m_pClient->m_Snap.m_LocalClientID == ClientID;
|
|
bool OtherTeam = m_pClient->IsOtherTeam(ClientID);
|
|
float Alpha = (OtherTeam || ClientID < 0) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
|
|
|
|
// set size
|
|
RenderInfo.m_Size = 64.0f;
|
|
|
|
float IntraTick = Intra;
|
|
if(ClientID >= 0)
|
|
IntraTick = m_pClient->m_aClients[ClientID].m_IsPredicted ? Client()->PredIntraGameTick(g_Config.m_ClDummy) : Client()->IntraGameTick(g_Config.m_ClDummy);
|
|
|
|
static float s_LastGameTickTime = Client()->GameTickTime(g_Config.m_ClDummy);
|
|
static float s_LastPredIntraTick = Client()->PredIntraGameTick(g_Config.m_ClDummy);
|
|
if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED))
|
|
{
|
|
s_LastGameTickTime = Client()->GameTickTime(g_Config.m_ClDummy);
|
|
s_LastPredIntraTick = Client()->PredIntraGameTick(g_Config.m_ClDummy);
|
|
}
|
|
|
|
bool PredictLocalWeapons = false;
|
|
float AttackTime = (Client()->PrevGameTick(g_Config.m_ClDummy) - Player.m_AttackTick) / (float)SERVER_TICK_SPEED + Client()->GameTickTime(g_Config.m_ClDummy);
|
|
float LastAttackTime = (Client()->PrevGameTick(g_Config.m_ClDummy) - Player.m_AttackTick) / (float)SERVER_TICK_SPEED + s_LastGameTickTime;
|
|
if(ClientID >= 0 && m_pClient->m_aClients[ClientID].m_IsPredictedLocal && m_pClient->AntiPingGunfire())
|
|
{
|
|
PredictLocalWeapons = true;
|
|
AttackTime = (Client()->PredIntraGameTick(g_Config.m_ClDummy) + (Client()->PredGameTick(g_Config.m_ClDummy) - 1 - Player.m_AttackTick)) / (float)SERVER_TICK_SPEED;
|
|
LastAttackTime = (s_LastPredIntraTick + (Client()->PredGameTick(g_Config.m_ClDummy) - 1 - Player.m_AttackTick)) / (float)SERVER_TICK_SPEED;
|
|
}
|
|
float AttackTicksPassed = AttackTime * (float)SERVER_TICK_SPEED;
|
|
|
|
float Angle;
|
|
if(Local && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
|
{
|
|
// just use the direct input if it's the local player we are rendering
|
|
Angle = angle(m_pClient->m_Controls.m_MousePos[g_Config.m_ClDummy]);
|
|
}
|
|
else
|
|
{
|
|
Angle = GetPlayerTargetAngle(&Prev, &Player, ClientID, IntraTick);
|
|
}
|
|
|
|
vec2 Direction = direction(Angle);
|
|
vec2 Position;
|
|
if(in_range(ClientID, MAX_CLIENTS - 1))
|
|
Position = m_pClient->m_aClients[ClientID].m_RenderPos;
|
|
else
|
|
Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick);
|
|
vec2 Vel = mix(vec2(Prev.m_VelX / 256.0f, Prev.m_VelY / 256.0f), vec2(Player.m_VelX / 256.0f, Player.m_VelY / 256.0f), IntraTick);
|
|
|
|
m_pClient->m_Flow.Add(Position, Vel * 100.0f, 10.0f);
|
|
|
|
RenderInfo.m_GotAirJump = Player.m_Jumped & 2 ? 0 : 1;
|
|
|
|
bool Stationary = Player.m_VelX <= 1 && Player.m_VelX >= -1;
|
|
bool InAir = !Collision()->CheckPoint(Player.m_X, Player.m_Y + 16);
|
|
bool WantOtherDir = (Player.m_Direction == -1 && Vel.x > 0) || (Player.m_Direction == 1 && Vel.x < 0);
|
|
|
|
// evaluate animation
|
|
float WalkTime = fmod(Position.x, 100.0f) / 100.0f;
|
|
if(WalkTime < 0)
|
|
{
|
|
// Don't do a moon walk outside the left border
|
|
WalkTime += 1;
|
|
}
|
|
CAnimState State;
|
|
State.Set(&g_pData->m_aAnimations[ANIM_BASE], 0);
|
|
|
|
if(InAir)
|
|
State.Add(&g_pData->m_aAnimations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here
|
|
else if(Stationary)
|
|
State.Add(&g_pData->m_aAnimations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here
|
|
else if(!WantOtherDir)
|
|
State.Add(&g_pData->m_aAnimations[ANIM_WALK], WalkTime, 1.0f);
|
|
|
|
if(Player.m_Weapon == WEAPON_HAMMER)
|
|
State.Add(&g_pData->m_aAnimations[ANIM_HAMMER_SWING], clamp(LastAttackTime * 5.0f, 0.0f, 1.0f), 1.0f);
|
|
if(Player.m_Weapon == WEAPON_NINJA)
|
|
State.Add(&g_pData->m_aAnimations[ANIM_NINJA_SWING], clamp(LastAttackTime * 2.0f, 0.0f, 1.0f), 1.0f);
|
|
|
|
// do skidding
|
|
if(!InAir && WantOtherDir && length(Vel * 50) > 500.0f)
|
|
{
|
|
static int64_t SkidSoundTime = 0;
|
|
if(time() - SkidSoundTime > time_freq() / 10)
|
|
{
|
|
if(g_Config.m_SndGame)
|
|
m_pClient->m_Sounds.PlayAt(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position);
|
|
SkidSoundTime = time();
|
|
}
|
|
|
|
m_pClient->m_Effects.SkidTrail(
|
|
Position + vec2(-Player.m_Direction * 6, 12),
|
|
vec2(-Player.m_Direction * 100 * length(Vel), -50));
|
|
}
|
|
|
|
// draw gun
|
|
{
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Graphics()->QuadsSetRotation(State.GetAttach()->m_Angle * pi * 2 + Angle);
|
|
|
|
if(ClientID < 0)
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
// normal weapons
|
|
int iw = clamp(Player.m_Weapon, 0, NUM_WEAPONS - 1);
|
|
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeapons[iw]);
|
|
int QuadOffset = iw * 2 + (Direction.x < 0 ? 1 : 0);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, Alpha);
|
|
|
|
vec2 Dir = Direction;
|
|
float Recoil = 0.0f;
|
|
vec2 p;
|
|
if(Player.m_Weapon == WEAPON_HAMMER)
|
|
{
|
|
// Static position for hammer
|
|
p = Position + vec2(State.GetAttach()->m_X, State.GetAttach()->m_Y);
|
|
p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety;
|
|
// if attack is under way, bash stuffs
|
|
if(Direction.x < 0)
|
|
{
|
|
Graphics()->QuadsSetRotation(-pi / 2 - State.GetAttach()->m_Angle * pi * 2);
|
|
p.x -= g_pData->m_Weapons.m_aId[iw].m_Offsetx;
|
|
}
|
|
else
|
|
{
|
|
Graphics()->QuadsSetRotation(-pi / 2 + State.GetAttach()->m_Angle * pi * 2);
|
|
}
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, p.x, p.y);
|
|
}
|
|
else if(Player.m_Weapon == WEAPON_NINJA)
|
|
{
|
|
p = Position;
|
|
p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety;
|
|
|
|
if(Direction.x < 0)
|
|
{
|
|
Graphics()->QuadsSetRotation(-pi / 2 - State.GetAttach()->m_Angle * pi * 2);
|
|
p.x -= g_pData->m_Weapons.m_aId[iw].m_Offsetx;
|
|
m_pClient->m_Effects.PowerupShine(p + vec2(32, 0), vec2(32, 12));
|
|
}
|
|
else
|
|
{
|
|
Graphics()->QuadsSetRotation(-pi / 2 + State.GetAttach()->m_Angle * pi * 2);
|
|
m_pClient->m_Effects.PowerupShine(p - vec2(32, 0), vec2(32, 12));
|
|
}
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, p.x, p.y);
|
|
|
|
// HADOKEN
|
|
if(AttackTime <= 1 / 6.f && g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles)
|
|
{
|
|
int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles;
|
|
static int s_LastIteX = IteX;
|
|
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
|
|
{
|
|
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
|
|
if(pInfo->m_Paused)
|
|
IteX = s_LastIteX;
|
|
else
|
|
s_LastIteX = IteX;
|
|
}
|
|
else
|
|
{
|
|
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED)
|
|
IteX = s_LastIteX;
|
|
else
|
|
s_LastIteX = IteX;
|
|
}
|
|
if(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX])
|
|
{
|
|
if(PredictLocalWeapons)
|
|
Dir = vec2(pPlayerChar->m_X, pPlayerChar->m_Y) - vec2(pPrevChar->m_X, pPrevChar->m_Y);
|
|
else
|
|
Dir = vec2(m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_X, m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_Y) - vec2(m_pClient->m_Snap.m_aCharacters[ClientID].m_Prev.m_X, m_pClient->m_Snap.m_aCharacters[ClientID].m_Prev.m_Y);
|
|
float HadOkenAngle = 0;
|
|
if(absolute(Dir.x) > 0.0001f || absolute(Dir.y) > 0.0001f)
|
|
{
|
|
Dir = normalize(Dir);
|
|
HadOkenAngle = angle(Dir);
|
|
}
|
|
else
|
|
{
|
|
Dir = vec2(1, 0);
|
|
}
|
|
Graphics()->QuadsSetRotation(HadOkenAngle);
|
|
QuadOffset = IteX * 2;
|
|
vec2 DirY(-Dir.y, Dir.x);
|
|
p = Position;
|
|
float OffsetX = g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsetx;
|
|
p -= Dir * OffsetX;
|
|
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponsMuzzles[iw][IteX]);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponSpriteMuzzleQuadContainerIndex[iw], QuadOffset, p.x, p.y);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: should be an animation
|
|
Recoil = 0;
|
|
float a = AttackTicksPassed / 5.0f;
|
|
if(a < 1)
|
|
Recoil = sinf(a * pi);
|
|
p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir * Recoil * 10.0f;
|
|
p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety;
|
|
if(Player.m_Weapon == WEAPON_GUN && g_Config.m_ClOldGunPosition)
|
|
p.y -= 8;
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, p.x, p.y);
|
|
}
|
|
|
|
if(RenderInfo.m_ShineDecoration)
|
|
{
|
|
if(Direction.x < 0)
|
|
{
|
|
m_pClient->m_Effects.PowerupShine(p + vec2(32, 0), vec2(32, 12));
|
|
}
|
|
else
|
|
{
|
|
m_pClient->m_Effects.PowerupShine(p - vec2(32, 0), vec2(32, 12));
|
|
}
|
|
}
|
|
|
|
if(Player.m_Weapon == WEAPON_GUN || Player.m_Weapon == WEAPON_SHOTGUN)
|
|
{
|
|
// check if we're firing stuff
|
|
if(g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles) //prev.attackticks)
|
|
{
|
|
float AlphaMuzzle = 0.0f;
|
|
if(AttackTicksPassed < g_pData->m_Weapons.m_aId[iw].m_Muzzleduration + 3)
|
|
{
|
|
float t = AttackTicksPassed / g_pData->m_Weapons.m_aId[iw].m_Muzzleduration;
|
|
AlphaMuzzle = mix(2.0f, 0.0f, minimum(1.0f, maximum(0.0f, t)));
|
|
}
|
|
|
|
int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles;
|
|
static int s_LastIteX = IteX;
|
|
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
|
|
{
|
|
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
|
|
if(pInfo->m_Paused)
|
|
IteX = s_LastIteX;
|
|
else
|
|
s_LastIteX = IteX;
|
|
}
|
|
else
|
|
{
|
|
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED)
|
|
IteX = s_LastIteX;
|
|
else
|
|
s_LastIteX = IteX;
|
|
}
|
|
if(AlphaMuzzle > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX])
|
|
{
|
|
float OffsetY = -g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsety;
|
|
QuadOffset = IteX * 2 + (Direction.x < 0 ? 1 : 0);
|
|
if(Direction.x < 0)
|
|
OffsetY = -OffsetY;
|
|
|
|
vec2 DirY(-Dir.y, Dir.x);
|
|
vec2 MuzzlePos = p + Dir * g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsetx + DirY * OffsetY;
|
|
Graphics()->TextureSet(GameClient()->m_GameSkin.m_SpriteWeaponsMuzzles[iw][IteX]);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponSpriteMuzzleQuadContainerIndex[iw], QuadOffset, MuzzlePos.x, MuzzlePos.y);
|
|
}
|
|
}
|
|
}
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Graphics()->QuadsSetRotation(0);
|
|
|
|
switch(Player.m_Weapon)
|
|
{
|
|
case WEAPON_GUN: RenderHand(&RenderInfo, p, Direction, -3 * pi / 4, vec2(-15, 4), Alpha); break;
|
|
case WEAPON_SHOTGUN: RenderHand(&RenderInfo, p, Direction, -pi / 2, vec2(-5, 4), Alpha); break;
|
|
case WEAPON_GRENADE: RenderHand(&RenderInfo, p, Direction, -pi / 2, vec2(-4, 7), Alpha); break;
|
|
}
|
|
}
|
|
|
|
// render the "shadow" tee
|
|
if(Local && ((g_Config.m_Debug && g_Config.m_ClUnpredictedShadow >= 0) || g_Config.m_ClUnpredictedShadow == 1))
|
|
{
|
|
vec2 GhostPosition = Position;
|
|
if(ClientID >= 0)
|
|
GhostPosition = mix(
|
|
vec2(m_pClient->m_Snap.m_aCharacters[ClientID].m_Prev.m_X, m_pClient->m_Snap.m_aCharacters[ClientID].m_Prev.m_Y),
|
|
vec2(m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_X, m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_Y),
|
|
Client()->IntraGameTick(g_Config.m_ClDummy));
|
|
|
|
CTeeRenderInfo Ghost = RenderInfo;
|
|
RenderTools()->RenderTee(&State, &Ghost, Player.m_Emote, Direction, GhostPosition, 0.5f); // render ghost
|
|
}
|
|
|
|
RenderTools()->RenderTee(&State, &RenderInfo, Player.m_Emote, Direction, Position, Alpha);
|
|
|
|
int QuadOffsetToEmoticon = NUM_WEAPONS * 2 + 2 + 2;
|
|
if((Player.m_PlayerFlags & PLAYERFLAG_CHATTING) && !m_pClient->m_aClients[ClientID].m_Afk)
|
|
{
|
|
int CurEmoticon = (SPRITE_DOTDOT - SPRITE_OOP);
|
|
Graphics()->TextureSet(GameClient()->m_EmoticonsSkin.m_SpriteEmoticons[CurEmoticon]);
|
|
int QuadOffset = QuadOffsetToEmoticon + CurEmoticon;
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, Alpha);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, Position.x + 24.f, Position.y - 40.f);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Graphics()->QuadsSetRotation(0);
|
|
}
|
|
|
|
if(ClientID < 0)
|
|
return;
|
|
|
|
if(g_Config.m_ClAfkEmote && m_pClient->m_aClients[ClientID].m_Afk && !(Client()->DummyConnected() && ClientID == m_pClient->m_LocalIDs[!g_Config.m_ClDummy]))
|
|
{
|
|
int CurEmoticon = (SPRITE_ZZZ - SPRITE_OOP);
|
|
Graphics()->TextureSet(GameClient()->m_EmoticonsSkin.m_SpriteEmoticons[CurEmoticon]);
|
|
int QuadOffset = QuadOffsetToEmoticon + CurEmoticon;
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, Alpha);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, Position.x + 24.f, Position.y - 40.f);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Graphics()->QuadsSetRotation(0);
|
|
}
|
|
|
|
if(g_Config.m_ClShowEmotes && !m_pClient->m_aClients[ClientID].m_EmoticonIgnore && m_pClient->m_aClients[ClientID].m_EmoticonStartTick != -1)
|
|
{
|
|
float SinceStart = (Client()->GameTick(g_Config.m_ClDummy) - m_pClient->m_aClients[ClientID].m_EmoticonStartTick) + (Client()->IntraGameTickSincePrev(g_Config.m_ClDummy) - m_pClient->m_aClients[ClientID].m_EmoticonStartFraction);
|
|
float FromEnd = (2 * Client()->GameTickSpeed()) - SinceStart;
|
|
|
|
if(0 <= SinceStart && FromEnd > 0)
|
|
{
|
|
float a = 1;
|
|
|
|
if(FromEnd < Client()->GameTickSpeed() / 5)
|
|
a = FromEnd / (Client()->GameTickSpeed() / 5.0f);
|
|
|
|
float h = 1;
|
|
if(SinceStart < Client()->GameTickSpeed() / 10)
|
|
h = SinceStart / (Client()->GameTickSpeed() / 10.0f);
|
|
|
|
float Wiggle = 0;
|
|
if(SinceStart < Client()->GameTickSpeed() / 5)
|
|
Wiggle = SinceStart / (Client()->GameTickSpeed() / 5.0f);
|
|
|
|
float WiggleAngle = sinf(5 * Wiggle);
|
|
|
|
Graphics()->QuadsSetRotation(pi / 6 * WiggleAngle);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, a * Alpha);
|
|
// client_datas::emoticon is an offset from the first emoticon
|
|
int QuadOffset = QuadOffsetToEmoticon + m_pClient->m_aClients[ClientID].m_Emoticon;
|
|
Graphics()->TextureSet(GameClient()->m_EmoticonsSkin.m_SpriteEmoticons[m_pClient->m_aClients[ClientID].m_Emoticon]);
|
|
Graphics()->RenderQuadContainerAsSprite(m_WeaponEmoteQuadContainerIndex, QuadOffset, Position.x, Position.y - 23.f - 32.f * h, 1.f, (64.f * h) / 64.f);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
Graphics()->QuadsSetRotation(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline bool CPlayers::IsPlayerInfoAvailable(int ClientID) const
|
|
{
|
|
const void *pPrevInfo = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_PLAYERINFO, ClientID);
|
|
const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, ClientID);
|
|
return pPrevInfo && pInfo;
|
|
}
|
|
|
|
void CPlayers::OnRender()
|
|
{
|
|
// update RenderInfo for ninja
|
|
bool IsTeamplay = false;
|
|
if(m_pClient->m_Snap.m_pGameInfoObj)
|
|
IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) != 0;
|
|
for(int i = 0; i < MAX_CLIENTS; ++i)
|
|
{
|
|
m_aRenderInfo[i] = m_pClient->m_aClients[i].m_RenderInfo;
|
|
m_aRenderInfo[i].m_ShineDecoration = m_pClient->m_aClients[i].m_LiveFrozen;
|
|
if(m_pClient->m_Snap.m_aCharacters[i].m_Cur.m_Weapon == WEAPON_NINJA && g_Config.m_ClShowNinja)
|
|
{
|
|
// change the skin for the player to the ninja
|
|
int Skin = m_pClient->m_Skins.Find("x_ninja");
|
|
if(Skin != -1)
|
|
{
|
|
const CSkin *pSkin = m_pClient->m_Skins.Get(Skin);
|
|
m_aRenderInfo[i].m_OriginalRenderSkin = pSkin->m_OriginalSkin;
|
|
m_aRenderInfo[i].m_ColorableRenderSkin = pSkin->m_ColorableSkin;
|
|
m_aRenderInfo[i].m_BloodColor = pSkin->m_BloodColor;
|
|
m_aRenderInfo[i].m_SkinMetrics = pSkin->m_Metrics;
|
|
m_aRenderInfo[i].m_CustomColoredSkin = IsTeamplay;
|
|
if(!IsTeamplay)
|
|
{
|
|
m_aRenderInfo[i].m_ColorBody = ColorRGBA(1, 1, 1);
|
|
m_aRenderInfo[i].m_ColorFeet = ColorRGBA(1, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int Skin = m_pClient->m_Skins.Find("x_spec");
|
|
const CSkin *pSkin = m_pClient->m_Skins.Get(Skin);
|
|
m_RenderInfoSpec.m_OriginalRenderSkin = pSkin->m_OriginalSkin;
|
|
m_RenderInfoSpec.m_ColorableRenderSkin = pSkin->m_ColorableSkin;
|
|
m_RenderInfoSpec.m_BloodColor = pSkin->m_BloodColor;
|
|
m_RenderInfoSpec.m_SkinMetrics = pSkin->m_Metrics;
|
|
m_RenderInfoSpec.m_CustomColoredSkin = false;
|
|
m_RenderInfoSpec.m_Size = 64.0f;
|
|
int LocalClientID = m_pClient->m_Snap.m_LocalClientID;
|
|
|
|
// get screen edges to avoid rendering offscreen
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
|
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
// expand the edges to prevent popping in/out onscreen
|
|
//
|
|
// it is assumed that the tee, all its weapons, and emotes fit into a 200x200 box centered on the tee
|
|
// this may need to be changed or calculated differently in the future
|
|
float BorderBuffer = 100;
|
|
ScreenX0 -= BorderBuffer;
|
|
ScreenX1 += BorderBuffer;
|
|
ScreenY0 -= BorderBuffer;
|
|
ScreenY1 += BorderBuffer;
|
|
|
|
// render everyone else's hook, then our own
|
|
for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++)
|
|
{
|
|
if(ClientID == LocalClientID || !m_pClient->m_Snap.m_aCharacters[ClientID].m_Active || !IsPlayerInfoAvailable(ClientID))
|
|
{
|
|
continue;
|
|
}
|
|
RenderHook(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID);
|
|
}
|
|
if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID))
|
|
{
|
|
const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID];
|
|
RenderHook(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID);
|
|
}
|
|
|
|
// render spectating players
|
|
for(auto &m_aClient : m_pClient->m_aClients)
|
|
{
|
|
if(!m_aClient.m_SpecCharPresent)
|
|
{
|
|
continue;
|
|
}
|
|
RenderTools()->RenderTee(CAnimState::GetIdle(), &m_RenderInfoSpec, EMOTE_BLINK, vec2(1, 0), m_aClient.m_SpecChar);
|
|
}
|
|
|
|
// render everyone else's tee, then our own
|
|
for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++)
|
|
{
|
|
if(ClientID == LocalClientID || !m_pClient->m_Snap.m_aCharacters[ClientID].m_Active || !IsPlayerInfoAvailable(ClientID))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RenderHookCollLine(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, ClientID);
|
|
|
|
//don't render offscreen
|
|
vec2 *pRenderPos = &m_pClient->m_aClients[ClientID].m_RenderPos;
|
|
if(pRenderPos->x < ScreenX0 || pRenderPos->x > ScreenX1 || pRenderPos->y < ScreenY0 || pRenderPos->y > ScreenY1)
|
|
{
|
|
continue;
|
|
}
|
|
RenderPlayer(&m_pClient->m_aClients[ClientID].m_RenderPrev, &m_pClient->m_aClients[ClientID].m_RenderCur, &m_aRenderInfo[ClientID], ClientID);
|
|
}
|
|
if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID))
|
|
{
|
|
const CGameClient::CClientData *pLocalClientData = &m_pClient->m_aClients[LocalClientID];
|
|
RenderHookCollLine(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, LocalClientID);
|
|
RenderPlayer(&pLocalClientData->m_RenderPrev, &pLocalClientData->m_RenderCur, &m_aRenderInfo[LocalClientID], LocalClientID);
|
|
}
|
|
}
|
|
|
|
void CPlayers::OnInit()
|
|
{
|
|
m_WeaponEmoteQuadContainerIndex = Graphics()->CreateQuadContainer(false);
|
|
|
|
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
|
|
|
for(int i = 0; i < NUM_WEAPONS; ++i)
|
|
{
|
|
float ScaleX, ScaleY;
|
|
RenderTools()->GetSpriteScale(g_pData->m_Weapons.m_aId[i].m_pSpriteBody, ScaleX, ScaleY);
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleX, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleY);
|
|
Graphics()->QuadsSetSubset(0, 1, 1, 0);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleX, g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleY);
|
|
}
|
|
float ScaleX, ScaleY;
|
|
|
|
// at the end the hand
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, 20.f);
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, 20.f);
|
|
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, -12.f, -8.f, 24.f, 16.f);
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, -12.f, -8.f, 24.f, 16.f);
|
|
|
|
for(int i = 0; i < NUM_EMOTICONS; ++i)
|
|
{
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponEmoteQuadContainerIndex, 64.f);
|
|
}
|
|
Graphics()->QuadContainerUpload(m_WeaponEmoteQuadContainerIndex);
|
|
|
|
for(int i = 0; i < NUM_WEAPONS; ++i)
|
|
{
|
|
m_WeaponSpriteMuzzleQuadContainerIndex[i] = Graphics()->CreateQuadContainer(false);
|
|
for(int n = 0; n < g_pData->m_Weapons.m_aId[i].m_NumSpriteMuzzles; ++n)
|
|
{
|
|
if(g_pData->m_Weapons.m_aId[i].m_aSpriteMuzzles[n])
|
|
{
|
|
if(i == WEAPON_GUN || i == WEAPON_SHOTGUN)
|
|
{
|
|
// TODO: hardcoded for now to get the same particle size as before
|
|
RenderTools()->GetSpriteScaleImpl(96, 64, ScaleX, ScaleY);
|
|
}
|
|
else
|
|
RenderTools()->GetSpriteScale(g_pData->m_Weapons.m_aId[i].m_aSpriteMuzzles[n], ScaleX, ScaleY);
|
|
}
|
|
|
|
float SWidth = (g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleX) * (4.0f / 3.0f);
|
|
float SHeight = g_pData->m_Weapons.m_aId[i].m_VisualSize * ScaleY;
|
|
|
|
Graphics()->QuadsSetSubset(0, 0, 1, 1);
|
|
if(WEAPON_NINJA == i)
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponSpriteMuzzleQuadContainerIndex[i], 160.f * ScaleX, 160.f * ScaleY);
|
|
else
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponSpriteMuzzleQuadContainerIndex[i], SWidth, SHeight);
|
|
|
|
Graphics()->QuadsSetSubset(0, 1, 1, 0);
|
|
if(WEAPON_NINJA == i)
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponSpriteMuzzleQuadContainerIndex[i], 160.f * ScaleX, 160.f * ScaleY);
|
|
else
|
|
RenderTools()->QuadContainerAddSprite(m_WeaponSpriteMuzzleQuadContainerIndex[i], SWidth, SHeight);
|
|
}
|
|
Graphics()->QuadContainerUpload(m_WeaponSpriteMuzzleQuadContainerIndex[i]);
|
|
}
|
|
|
|
Graphics()->QuadsSetSubset(0.f, 0.f, 1.f, 1.f);
|
|
Graphics()->QuadsSetRotation(0.f);
|
|
}
|