1 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2 /* If you are missing that file, acquire a complete release at teeworlds.com. */
3 #include <engine/editor.h>
4 #include <engine/engine.h>
5 #include <engine/friends.h>
6 #include <engine/graphics.h>
7 #include <engine/textrender.h>
8 #include <engine/demo.h>
9 #include <engine/map.h>
10 #include <engine/storage.h>
11 #include <engine/sound.h>
12 #include <engine/serverbrowser.h>
13 #include <engine/shared/demo.h>
14 #include <engine/shared/config.h>
16 #include <game/generated/protocol.h>
17 #include <game/generated/client_data.h>
19 #include <game/localization.h>
20 #include <game/version.h>
23 #include "gameclient.h"
25 #include "components/binds.h"
26 #include "components/broadcast.h"
27 #include "components/camera.h"
28 #include "components/chat.h"
29 #include "components/console.h"
30 #include "components/controls.h"
31 #include "components/countryflags.h"
32 #include "components/damageind.h"
33 #include "components/debughud.h"
34 #include "components/effects.h"
35 #include "components/emoticon.h"
36 #include "components/flow.h"
37 #include "components/hud.h"
38 #include "components/items.h"
39 #include "components/killmessages.h"
40 #include "components/mapimages.h"
41 #include "components/maplayers.h"
42 #include "components/menus.h"
43 #include "components/motd.h"
44 #include "components/particles.h"
45 #include "components/players.h"
46 #include "components/nameplates.h"
47 #include "components/scoreboard.h"
48 #include "components/skins.h"
49 #include "components/sounds.h"
50 #include "components/spectator.h"
51 #include "components/voting.h"
53 CGameClient g_GameClient
;
55 // instanciate all systems
56 static CKillMessages gs_KillMessages
;
57 static CCamera gs_Camera
;
60 static CBroadcast gs_Broadcast
;
61 static CGameConsole gs_GameConsole
;
62 static CBinds gs_Binds
;
63 static CParticles gs_Particles
;
64 static CMenus gs_Menus
;
65 static CSkins gs_Skins
;
66 static CCountryFlags gs_CountryFlags
;
69 static CDebugHud gs_DebugHud
;
70 static CControls gs_Controls
;
71 static CEffects gs_Effects
;
72 static CScoreboard gs_Scoreboard
;
73 static CSounds gs_Sounds
;
74 static CEmoticon gs_Emoticon
;
75 static CDamageInd gsDamageInd
;
76 static CVoting gs_Voting
;
77 static CSpectator gs_Spectator
;
79 static CPlayers gs_Players
;
80 static CNamePlates gs_NamePlates
;
81 static CItems gs_Items
;
82 static CMapImages gs_MapImages
;
84 static CMapLayers
gs_MapLayersBackGround(CMapLayers::TYPE_BACKGROUND
);
85 static CMapLayers
gs_MapLayersForeGround(CMapLayers::TYPE_FOREGROUND
);
87 CGameClient::CStack::CStack() { m_Num
= 0; }
88 void CGameClient::CStack::Add(class CComponent
*pComponent
) { m_paComponents
[m_Num
++] = pComponent
; }
90 const char *CGameClient::Version() { return GAME_VERSION
; }
91 const char *CGameClient::NetVersion() { return GAME_NETVERSION
; }
92 const char *CGameClient::GetItemName(int Type
) { return m_NetObjHandler
.GetObjName(Type
); }
94 void CGameClient::OnConsoleInit()
96 m_pEngine
= Kernel()->RequestInterface
<IEngine
>();
97 m_pClient
= Kernel()->RequestInterface
<IClient
>();
98 m_pGraphics
= Kernel()->RequestInterface
<IGraphics
>();
99 m_pTextRender
= Kernel()->RequestInterface
<ITextRender
>();
100 m_pSound
= Kernel()->RequestInterface
<ISound
>();
101 m_pInput
= Kernel()->RequestInterface
<IInput
>();
102 m_pConsole
= Kernel()->RequestInterface
<IConsole
>();
103 m_pStorage
= Kernel()->RequestInterface
<IStorage
>();
104 m_pDemoPlayer
= Kernel()->RequestInterface
<IDemoPlayer
>();
105 m_pDemoRecorder
= Kernel()->RequestInterface
<IDemoRecorder
>();
106 m_pServerBrowser
= Kernel()->RequestInterface
<IServerBrowser
>();
107 m_pEditor
= Kernel()->RequestInterface
<IEditor
>();
108 m_pFriends
= Kernel()->RequestInterface
<IFriends
>();
111 m_pBinds
= &::gs_Binds
;
112 m_pGameConsole
= &::gs_GameConsole
;
113 m_pParticles
= &::gs_Particles
;
114 m_pMenus
= &::gs_Menus
;
115 m_pSkins
= &::gs_Skins
;
116 m_pCountryFlags
= &::gs_CountryFlags
;
117 m_pChat
= &::gs_Chat
;
118 m_pFlow
= &::gs_Flow
;
119 m_pCamera
= &::gs_Camera
;
120 m_pControls
= &::gs_Controls
;
121 m_pEffects
= &::gs_Effects
;
122 m_pSounds
= &::gs_Sounds
;
123 m_pMotd
= &::gs_Motd
;
124 m_pDamageind
= &::gsDamageInd
;
125 m_pMapimages
= &::gs_MapImages
;
126 m_pVoting
= &::gs_Voting
;
127 m_pScoreboard
= &::gs_Scoreboard
;
128 m_pItems
= &::gs_Items
;
130 // make a list of all the systems, make sure to add them in the corrent render order
132 m_All
.Add(m_pCountryFlags
);
133 m_All
.Add(m_pMapimages
);
134 m_All
.Add(m_pEffects
); // doesn't render anything, just updates effects
135 m_All
.Add(m_pParticles
);
137 m_All
.Add(m_pControls
);
138 m_All
.Add(m_pCamera
);
139 m_All
.Add(m_pSounds
);
140 m_All
.Add(m_pVoting
);
141 m_All
.Add(m_pParticles
); // doesn't render anything, just updates all the particles
143 m_All
.Add(&gs_MapLayersBackGround
); // first to render
144 m_All
.Add(&m_pParticles
->m_RenderTrail
);
146 m_All
.Add(&gs_Players
);
147 m_All
.Add(&gs_MapLayersForeGround
);
148 m_All
.Add(&m_pParticles
->m_RenderExplosions
);
149 m_All
.Add(&gs_NamePlates
);
150 m_All
.Add(&m_pParticles
->m_RenderGeneral
);
151 m_All
.Add(m_pDamageind
);
153 m_All
.Add(&gs_Spectator
);
154 m_All
.Add(&gs_Emoticon
);
155 m_All
.Add(&gs_KillMessages
);
157 m_All
.Add(&gs_Broadcast
);
158 m_All
.Add(&gs_DebugHud
);
159 m_All
.Add(&gs_Scoreboard
);
162 m_All
.Add(m_pGameConsole
);
164 // build the input stack
165 m_Input
.Add(&m_pMenus
->m_Binder
); // this will take over all input when we want to bind a key
166 m_Input
.Add(&m_pBinds
->m_SpecialBinds
);
167 m_Input
.Add(m_pGameConsole
);
168 m_Input
.Add(m_pChat
); // chat has higher prio due to tha you can quit it by pressing esc
169 m_Input
.Add(m_pMotd
); // for pressing esc to remove it
170 m_Input
.Add(m_pMenus
);
171 m_Input
.Add(&gs_Spectator
);
172 m_Input
.Add(&gs_Emoticon
);
173 m_Input
.Add(m_pControls
);
174 m_Input
.Add(m_pBinds
);
176 // add the some console commands
177 Console()->Register("team", "i", CFGFLAG_CLIENT
, ConTeam
, this, "Switch team");
178 Console()->Register("kill", "", CFGFLAG_CLIENT
, ConKill
, this, "Kill yourself");
180 // register server dummy commands for tab completion
181 Console()->Register("tune", "si", CFGFLAG_SERVER
, 0, 0, "Tune variable to value");
182 Console()->Register("tune_reset", "", CFGFLAG_SERVER
, 0, 0, "Reset tuning");
183 Console()->Register("tune_dump", "", CFGFLAG_SERVER
, 0, 0, "Dump tuning");
184 Console()->Register("change_map", "?r", CFGFLAG_SERVER
, 0, 0, "Change map");
185 Console()->Register("restart", "?i", CFGFLAG_SERVER
, 0, 0, "Restart in x seconds");
186 Console()->Register("broadcast", "r", CFGFLAG_SERVER
, 0, 0, "Broadcast message");
187 Console()->Register("say", "r", CFGFLAG_SERVER
, 0, 0, "Say in chat");
188 Console()->Register("set_team", "ii?i", CFGFLAG_SERVER
, 0, 0, "Set team of player to team");
189 Console()->Register("set_team_all", "i", CFGFLAG_SERVER
, 0, 0, "Set team of all players to team");
190 Console()->Register("add_vote", "sr", CFGFLAG_SERVER
, 0, 0, "Add a voting option");
191 Console()->Register("remove_vote", "s", CFGFLAG_SERVER
, 0, 0, "remove a voting option");
192 Console()->Register("force_vote", "ss?r", CFGFLAG_SERVER
, 0, 0, "Force a voting option");
193 Console()->Register("clear_votes", "", CFGFLAG_SERVER
, 0, 0, "Clears the voting options");
194 Console()->Register("vote", "r", CFGFLAG_SERVER
, 0, 0, "Force a vote to yes/no");
197 // propagate pointers
198 m_UI
.SetGraphics(Graphics(), TextRender());
199 m_RenderTools
.m_pGraphics
= Graphics();
200 m_RenderTools
.m_pUI
= UI();
201 for(int i
= 0; i
< m_All
.m_Num
; i
++)
202 m_All
.m_paComponents
[i
]->m_pClient
= this;
204 // let all the other components register their console commands
205 for(int i
= 0; i
< m_All
.m_Num
; i
++)
206 m_All
.m_paComponents
[i
]->OnConsoleInit();
210 Console()->Chain("player_name", ConchainSpecialInfoupdate
, this);
211 Console()->Chain("player_clan", ConchainSpecialInfoupdate
, this);
212 Console()->Chain("player_country", ConchainSpecialInfoupdate
, this);
213 Console()->Chain("player_use_custom_color", ConchainSpecialInfoupdate
, this);
214 Console()->Chain("player_color_body", ConchainSpecialInfoupdate
, this);
215 Console()->Chain("player_color_feet", ConchainSpecialInfoupdate
, this);
216 Console()->Chain("player_skin", ConchainSpecialInfoupdate
, this);
219 m_SuppressEvents
= false;
222 void CGameClient::OnInit()
224 int64 Start
= time_get();
227 g_Localization
.Load(g_Config
.m_ClLanguagefile
, Storage(), Console());
229 // TODO: this should be different
231 for(int i
= 0; i
< NUM_NETOBJTYPES
; i
++)
232 Client()->SnapSetStaticsize(i
, m_NetObjHandler
.GetObjSize(i
));
235 static CFont
*pDefaultFont
= 0;
237 IOHANDLE File
= Storage()->OpenFile("fonts/DejaVuSans.ttf", IOFLAG_READ
, IStorage::TYPE_ALL
, aFilename
, sizeof(aFilename
));
241 pDefaultFont
= TextRender()->LoadFont(aFilename
);
242 TextRender()->SetDefaultFont(pDefaultFont
);
245 Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD
, "gameclient", "failed to load font. filename='fonts/DejaVuSans.ttf'");
247 // init all components
248 for(int i
= m_All
.m_Num
-1; i
>= 0; --i
)
249 m_All
.m_paComponents
[i
]->OnInit();
251 // setup load amount// load textures
252 for(int i
= 0; i
< g_pData
->m_NumImages
; i
++)
254 g_pData
->m_aImages
[i
].m_Id
= Graphics()->LoadTexture(g_pData
->m_aImages
[i
].m_pFilename
, IStorage::TYPE_ALL
, CImageInfo::FORMAT_AUTO
, 0);
255 g_GameClient
.m_pMenus
->RenderLoading();
258 for(int i
= 0; i
< m_All
.m_Num
; i
++)
259 m_All
.m_paComponents
[i
]->OnReset();
261 int64 End
= time_get();
263 str_format(aBuf
, sizeof(aBuf
), "initialisation finished after %.2fms", ((End
-Start
)*1000)/(float)time_freq());
264 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "gameclient", aBuf
);
266 m_ServerMode
= SERVERMODE_PURE
;
269 void CGameClient::DispatchInput()
271 // handle mouse movement
272 float x
= 0.0f
, y
= 0.0f
;
273 Input()->MouseRelative(&x
, &y
);
274 if(x
!= 0.0f
|| y
!= 0.0f
)
276 for(int h
= 0; h
< m_Input
.m_Num
; h
++)
278 if(m_Input
.m_paComponents
[h
]->OnMouseMove(x
, y
))
283 // handle key presses
284 for(int i
= 0; i
< Input()->NumEvents(); i
++)
286 IInput::CEvent e
= Input()->GetEvent(i
);
288 for(int h
= 0; h
< m_Input
.m_Num
; h
++)
290 if(m_Input
.m_paComponents
[h
]->OnInput(e
))
292 //dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags);
298 // clear all events for this frame
299 Input()->ClearEvents();
303 int CGameClient::OnSnapInput(int *pData
)
305 return m_pControls
->SnapInput(pData
);
308 void CGameClient::OnConnected()
310 m_Layers
.Init(Kernel());
311 m_Collision
.Init(Layers());
313 RenderTools()->RenderTilemapGenerateSkip(Layers());
315 for(int i
= 0; i
< m_All
.m_Num
; i
++)
317 m_All
.m_paComponents
[i
]->OnMapLoad();
318 m_All
.m_paComponents
[i
]->OnReset();
321 CServerInfo CurrentServerInfo
;
322 Client()->GetServerInfo(&CurrentServerInfo
);
324 m_ServerMode
= SERVERMODE_PURE
;
327 // send the inital info
331 void CGameClient::OnReset()
333 // clear out the invalid pointers
334 m_LastNewPredictedTick
= -1;
335 mem_zero(&g_GameClient
.m_Snap
, sizeof(g_GameClient
.m_Snap
));
337 for(int i
= 0; i
< MAX_CLIENTS
; i
++)
338 m_aClients
[i
].Reset();
340 for(int i
= 0; i
< m_All
.m_Num
; i
++)
341 m_All
.m_paComponents
[i
]->OnReset();
343 m_DemoSpecID
= SPEC_FREEVIEW
;
344 m_FlagDropTick
[TEAM_RED
] = 0;
345 m_FlagDropTick
[TEAM_BLUE
] = 0;
346 m_Tuning
= CTuningParams();
350 void CGameClient::UpdatePositions()
352 // local character position
353 if(g_Config
.m_ClPredict
&& Client()->State() != IClient::STATE_DEMOPLAYBACK
)
355 if(!m_Snap
.m_pLocalCharacter
|| (m_Snap
.m_pGameInfoObj
&& m_Snap
.m_pGameInfoObj
->m_GameStateFlags
&GAMESTATEFLAG_GAMEOVER
))
357 // don't use predicted
360 m_LocalCharacterPos
= mix(m_PredictedPrevChar
.m_Pos
, m_PredictedChar
.m_Pos
, Client()->PredIntraGameTick());
362 else if(m_Snap
.m_pLocalCharacter
&& m_Snap
.m_pLocalPrevCharacter
)
364 m_LocalCharacterPos
= mix(
365 vec2(m_Snap
.m_pLocalPrevCharacter
->m_X
, m_Snap
.m_pLocalPrevCharacter
->m_Y
),
366 vec2(m_Snap
.m_pLocalCharacter
->m_X
, m_Snap
.m_pLocalCharacter
->m_Y
), Client()->IntraGameTick());
369 // spectator position
370 if(m_Snap
.m_SpecInfo
.m_Active
)
372 if(Client()->State() == IClient::STATE_DEMOPLAYBACK
&& DemoPlayer()->GetDemoType() == IDemoPlayer::DEMOTYPE_SERVER
&&
373 m_Snap
.m_SpecInfo
.m_SpectatorID
!= SPEC_FREEVIEW
)
375 m_Snap
.m_SpecInfo
.m_Position
= mix(
376 vec2(m_Snap
.m_aCharacters
[m_Snap
.m_SpecInfo
.m_SpectatorID
].m_Prev
.m_X
, m_Snap
.m_aCharacters
[m_Snap
.m_SpecInfo
.m_SpectatorID
].m_Prev
.m_Y
),
377 vec2(m_Snap
.m_aCharacters
[m_Snap
.m_SpecInfo
.m_SpectatorID
].m_Cur
.m_X
, m_Snap
.m_aCharacters
[m_Snap
.m_SpecInfo
.m_SpectatorID
].m_Cur
.m_Y
),
378 Client()->IntraGameTick());
379 m_Snap
.m_SpecInfo
.m_UsePosition
= true;
381 else if(m_Snap
.m_pSpectatorInfo
&& (Client()->State() == IClient::STATE_DEMOPLAYBACK
|| m_Snap
.m_SpecInfo
.m_SpectatorID
!= SPEC_FREEVIEW
))
383 if(m_Snap
.m_pPrevSpectatorInfo
)
384 m_Snap
.m_SpecInfo
.m_Position
= mix(vec2(m_Snap
.m_pPrevSpectatorInfo
->m_X
, m_Snap
.m_pPrevSpectatorInfo
->m_Y
),
385 vec2(m_Snap
.m_pSpectatorInfo
->m_X
, m_Snap
.m_pSpectatorInfo
->m_Y
), Client()->IntraGameTick());
387 m_Snap
.m_SpecInfo
.m_Position
= vec2(m_Snap
.m_pSpectatorInfo
->m_X
, m_Snap
.m_pSpectatorInfo
->m_Y
);
388 m_Snap
.m_SpecInfo
.m_UsePosition
= true;
394 static void Evolve(CNetObj_Character
*pCharacter
, int Tick
)
396 CWorldCore TempWorld
;
397 CCharacterCore TempCore
;
398 mem_zero(&TempCore
, sizeof(TempCore
));
399 TempCore
.Init(&TempWorld
, g_GameClient
.Collision());
400 TempCore
.Read(pCharacter
);
402 while(pCharacter
->m_Tick
< Tick
)
404 pCharacter
->m_Tick
++;
405 TempCore
.Tick(false);
410 TempCore
.Write(pCharacter
);
414 void CGameClient::OnRender()
416 /*Graphics()->Clear(1,0,0);
418 menus->render_background();
421 Graphics()->Clear(1,0,0);
422 Graphics()->MapScreen(0,0,100,100);
424 Graphics()->QuadsBegin();
425 Graphics()->SetColor(1,1,1,1);
426 Graphics()->QuadsDraw(50, 50, 30, 30);
427 Graphics()->QuadsEnd();
431 // update the local character and spectate position
434 // dispatch all input to systems
437 // render all systems
438 for(int i
= 0; i
< m_All
.m_Num
; i
++)
439 m_All
.m_paComponents
[i
]->OnRender();
441 // clear new tick flags
443 m_NewPredictedTick
= false;
445 // check if client info has to be resent
446 if(m_LastSendInfo
&& Client()->State() == IClient::STATE_ONLINE
&& !m_pMenus
->IsActive() && m_LastSendInfo
+time_freq()*5 < time_get())
448 // resend if client info differs
449 if(str_comp(g_Config
.m_PlayerName
, m_aClients
[m_Snap
.m_LocalClientID
].m_aName
) ||
450 str_comp(g_Config
.m_PlayerClan
, m_aClients
[m_Snap
.m_LocalClientID
].m_aClan
) ||
451 g_Config
.m_PlayerCountry
!= m_aClients
[m_Snap
.m_LocalClientID
].m_Country
||
452 str_comp(g_Config
.m_PlayerSkin
, m_aClients
[m_Snap
.m_LocalClientID
].m_aSkinName
) ||
453 (m_Snap
.m_pGameInfoObj
&& !(m_Snap
.m_pGameInfoObj
->m_GameFlags
&GAMEFLAG_TEAMS
) && // no teamgame?
454 (g_Config
.m_PlayerUseCustomColor
!= m_aClients
[m_Snap
.m_LocalClientID
].m_UseCustomColor
||
455 g_Config
.m_PlayerColorBody
!= m_aClients
[m_Snap
.m_LocalClientID
].m_ColorBody
||
456 g_Config
.m_PlayerColorFeet
!= m_aClients
[m_Snap
.m_LocalClientID
].m_ColorFeet
)))
464 void CGameClient::OnRelease()
466 // release all systems
467 for(int i
= 0; i
< m_All
.m_Num
; i
++)
468 m_All
.m_paComponents
[i
]->OnRelease();
471 void CGameClient::OnMessage(int MsgId
, CUnpacker
*pUnpacker
)
474 if(MsgId
== NETMSGTYPE_SV_EXTRAPROJECTILE
)
476 int Num
= pUnpacker
->GetInt();
478 for(int k
= 0; k
< Num
; k
++)
480 CNetObj_Projectile Proj
;
481 for(unsigned i
= 0; i
< sizeof(CNetObj_Projectile
)/sizeof(int); i
++)
482 ((int *)&Proj
)[i
] = pUnpacker
->GetInt();
484 if(pUnpacker
->Error())
487 g_GameClient
.m_pItems
->AddExtraProjectile(&Proj
);
492 else if(MsgId
== NETMSGTYPE_SV_TUNEPARAMS
)
494 // unpack the new tuning
495 CTuningParams NewTuning
;
496 int *pParams
= (int *)&NewTuning
;
497 for(unsigned i
= 0; i
< sizeof(CTuningParams
)/sizeof(int); i
++)
498 pParams
[i
] = pUnpacker
->GetInt();
500 // check for unpacking errors
501 if(pUnpacker
->Error())
504 m_ServerMode
= SERVERMODE_PURE
;
507 m_Tuning
= NewTuning
;
511 void *pRawMsg
= m_NetObjHandler
.SecureUnpackMsg(MsgId
, pUnpacker
);
515 str_format(aBuf
, sizeof(aBuf
), "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler
.GetMsgName(MsgId
), MsgId
, m_NetObjHandler
.FailedMsgOn());
516 Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO
, "client", aBuf
);
520 // TODO: this should be done smarter
521 for(int i
= 0; i
< m_All
.m_Num
; i
++)
522 m_All
.m_paComponents
[i
]->OnMessage(MsgId
, pRawMsg
);
524 if(MsgId
== NETMSGTYPE_SV_READYTOENTER
)
526 Client()->EnterGame();
528 else if (MsgId
== NETMSGTYPE_SV_EMOTICON
)
530 CNetMsg_Sv_Emoticon
*pMsg
= (CNetMsg_Sv_Emoticon
*)pRawMsg
;
533 m_aClients
[pMsg
->m_ClientID
].m_Emoticon
= pMsg
->m_Emoticon
;
534 m_aClients
[pMsg
->m_ClientID
].m_EmoticonStart
= Client()->GameTick();
536 else if(MsgId
== NETMSGTYPE_SV_SOUNDGLOBAL
)
541 // don't enqueue pseudo-global sounds from demos (created by PlayAndRecord)
542 CNetMsg_Sv_SoundGlobal
*pMsg
= (CNetMsg_Sv_SoundGlobal
*)pRawMsg
;
543 if(pMsg
->m_SoundID
== SOUND_CTF_DROP
|| pMsg
->m_SoundID
== SOUND_CTF_RETURN
||
544 pMsg
->m_SoundID
== SOUND_CTF_CAPTURE
|| pMsg
->m_SoundID
== SOUND_CTF_GRAB_EN
||
545 pMsg
->m_SoundID
== SOUND_CTF_GRAB_PL
)
546 g_GameClient
.m_pSounds
->Enqueue(CSounds::CHN_GLOBAL
, pMsg
->m_SoundID
);
548 g_GameClient
.m_pSounds
->Play(CSounds::CHN_GLOBAL
, pMsg
->m_SoundID
, 1.0f
, vec2(0,0));
552 void CGameClient::OnStateChange(int NewState
, int OldState
)
554 // reset everything when not already connected (to keep gathered stuff)
555 if(NewState
< IClient::STATE_ONLINE
)
558 // then change the state
559 for(int i
= 0; i
< m_All
.m_Num
; i
++)
560 m_All
.m_paComponents
[i
]->OnStateChange(NewState
, OldState
);
563 void CGameClient::OnShutdown() {}
564 void CGameClient::OnEnterGame() {}
566 void CGameClient::OnGameOver()
568 if(Client()->State() != IClient::STATE_DEMOPLAYBACK
)
569 Client()->AutoScreenshot_Start();
572 void CGameClient::OnStartGame()
574 if(Client()->State() != IClient::STATE_DEMOPLAYBACK
)
575 Client()->DemoRecorder_HandleAutoStart();
578 void CGameClient::OnRconLine(const char *pLine
)
580 m_pGameConsole
->PrintLine(CGameConsole::CONSOLETYPE_REMOTE
, pLine
);
583 void CGameClient::ProcessEvents()
588 int SnapType
= IClient::SNAP_CURRENT
;
589 int Num
= Client()->SnapNumItems(SnapType
);
590 for(int Index
= 0; Index
< Num
; Index
++)
592 IClient::CSnapItem Item
;
593 const void *pData
= Client()->SnapGetItem(SnapType
, Index
, &Item
);
595 if(Item
.m_Type
== NETEVENTTYPE_DAMAGEIND
)
597 CNetEvent_DamageInd
*ev
= (CNetEvent_DamageInd
*)pData
;
598 g_GameClient
.m_pEffects
->DamageIndicator(vec2(ev
->m_X
, ev
->m_Y
), GetDirection(ev
->m_Angle
));
600 else if(Item
.m_Type
== NETEVENTTYPE_EXPLOSION
)
602 CNetEvent_Explosion
*ev
= (CNetEvent_Explosion
*)pData
;
603 g_GameClient
.m_pEffects
->Explosion(vec2(ev
->m_X
, ev
->m_Y
));
605 else if(Item
.m_Type
== NETEVENTTYPE_HAMMERHIT
)
607 CNetEvent_HammerHit
*ev
= (CNetEvent_HammerHit
*)pData
;
608 g_GameClient
.m_pEffects
->HammerHit(vec2(ev
->m_X
, ev
->m_Y
));
610 else if(Item
.m_Type
== NETEVENTTYPE_SPAWN
)
612 CNetEvent_Spawn
*ev
= (CNetEvent_Spawn
*)pData
;
613 g_GameClient
.m_pEffects
->PlayerSpawn(vec2(ev
->m_X
, ev
->m_Y
));
615 else if(Item
.m_Type
== NETEVENTTYPE_DEATH
)
617 CNetEvent_Death
*ev
= (CNetEvent_Death
*)pData
;
618 g_GameClient
.m_pEffects
->PlayerDeath(vec2(ev
->m_X
, ev
->m_Y
), ev
->m_ClientID
);
620 else if(Item
.m_Type
== NETEVENTTYPE_SOUNDWORLD
)
622 CNetEvent_SoundWorld
*ev
= (CNetEvent_SoundWorld
*)pData
;
623 g_GameClient
.m_pSounds
->Play(CSounds::CHN_WORLD
, ev
->m_SoundID
, 1.0f
, vec2(ev
->m_X
, ev
->m_Y
));
628 void CGameClient::OnNewSnapshot()
632 // clear out the invalid pointers
633 mem_zero(&g_GameClient
.m_Snap
, sizeof(g_GameClient
.m_Snap
));
634 m_Snap
.m_LocalClientID
= -1;
638 int Num
= Client()->SnapNumItems(IClient::SNAP_CURRENT
);
639 for(int Index
= 0; Index
< Num
; Index
++)
641 IClient::CSnapItem Item
;
642 void *pData
= Client()->SnapGetItem(IClient::SNAP_CURRENT
, Index
, &Item
);
643 if(m_NetObjHandler
.ValidateObj(Item
.m_Type
, pData
, Item
.m_DataSize
) != 0)
648 str_format(aBuf
, sizeof(aBuf
), "invalidated index=%d type=%d (%s) size=%d id=%d", Index
, Item
.m_Type
, m_NetObjHandler
.GetObjName(Item
.m_Type
), Item
.m_DataSize
, Item
.m_ID
);
649 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "game", aBuf
);
651 Client()->SnapInvalidateItem(IClient::SNAP_CURRENT
, Index
);
658 if(g_Config
.m_DbgStress
)
660 if((Client()->GameTick()%100) == 0)
663 int MsgLen
= rand()%(sizeof(aMessage
)-1);
664 for(int i
= 0; i
< MsgLen
; i
++)
665 aMessage
[i
] = 'a'+(rand()%('z'-'a'));
666 aMessage
[MsgLen
] = 0;
669 Msg
.m_Team
= rand()&1;
670 Msg
.m_pMessage
= aMessage
;
671 Client()->SendPackMsg(&Msg
, MSGFLAG_VITAL
);
675 // go trough all the items in the snapshot and gather the info we want
677 m_Snap
.m_aTeamSize
[TEAM_RED
] = m_Snap
.m_aTeamSize
[TEAM_BLUE
] = 0;
679 int Num
= Client()->SnapNumItems(IClient::SNAP_CURRENT
);
680 for(int i
= 0; i
< Num
; i
++)
682 IClient::CSnapItem Item
;
683 const void *pData
= Client()->SnapGetItem(IClient::SNAP_CURRENT
, i
, &Item
);
685 if(Item
.m_Type
== NETOBJTYPE_CLIENTINFO
)
687 const CNetObj_ClientInfo
*pInfo
= (const CNetObj_ClientInfo
*)pData
;
688 int ClientID
= Item
.m_ID
;
689 IntsToStr(&pInfo
->m_Name0
, 4, m_aClients
[ClientID
].m_aName
);
690 IntsToStr(&pInfo
->m_Clan0
, 3, m_aClients
[ClientID
].m_aClan
);
691 m_aClients
[ClientID
].m_Country
= pInfo
->m_Country
;
692 IntsToStr(&pInfo
->m_Skin0
, 6, m_aClients
[ClientID
].m_aSkinName
);
694 m_aClients
[ClientID
].m_UseCustomColor
= pInfo
->m_UseCustomColor
;
695 m_aClients
[ClientID
].m_ColorBody
= pInfo
->m_ColorBody
;
696 m_aClients
[ClientID
].m_ColorFeet
= pInfo
->m_ColorFeet
;
699 if(m_aClients
[ClientID
].m_aSkinName
[0] == 'x' || m_aClients
[ClientID
].m_aSkinName
[1] == '_')
700 str_copy(m_aClients
[ClientID
].m_aSkinName
, "default", 64);
702 m_aClients
[ClientID
].m_SkinInfo
.m_ColorBody
= m_pSkins
->GetColorV4(m_aClients
[ClientID
].m_ColorBody
);
703 m_aClients
[ClientID
].m_SkinInfo
.m_ColorFeet
= m_pSkins
->GetColorV4(m_aClients
[ClientID
].m_ColorFeet
);
704 m_aClients
[ClientID
].m_SkinInfo
.m_Size
= 64;
707 m_aClients
[ClientID
].m_SkinID
= g_GameClient
.m_pSkins
->Find(m_aClients
[ClientID
].m_aSkinName
);
708 if(m_aClients
[ClientID
].m_SkinID
< 0)
710 m_aClients
[ClientID
].m_SkinID
= g_GameClient
.m_pSkins
->Find("default");
711 if(m_aClients
[ClientID
].m_SkinID
< 0)
712 m_aClients
[ClientID
].m_SkinID
= 0;
715 if(m_aClients
[ClientID
].m_UseCustomColor
)
716 m_aClients
[ClientID
].m_SkinInfo
.m_Texture
= g_GameClient
.m_pSkins
->Get(m_aClients
[ClientID
].m_SkinID
)->m_ColorTexture
;
719 m_aClients
[ClientID
].m_SkinInfo
.m_Texture
= g_GameClient
.m_pSkins
->Get(m_aClients
[ClientID
].m_SkinID
)->m_OrgTexture
;
720 m_aClients
[ClientID
].m_SkinInfo
.m_ColorBody
= vec4(1,1,1,1);
721 m_aClients
[ClientID
].m_SkinInfo
.m_ColorFeet
= vec4(1,1,1,1);
724 m_aClients
[ClientID
].UpdateRenderInfo();
727 else if(Item
.m_Type
== NETOBJTYPE_PLAYERINFO
)
729 const CNetObj_PlayerInfo
*pInfo
= (const CNetObj_PlayerInfo
*)pData
;
731 m_aClients
[pInfo
->m_ClientID
].m_Team
= pInfo
->m_Team
;
732 m_aClients
[pInfo
->m_ClientID
].m_Active
= true;
733 m_Snap
.m_paPlayerInfos
[pInfo
->m_ClientID
] = pInfo
;
734 m_Snap
.m_NumPlayers
++;
738 m_Snap
.m_LocalClientID
= Item
.m_ID
;
739 m_Snap
.m_pLocalInfo
= pInfo
;
741 if(pInfo
->m_Team
== TEAM_SPECTATORS
)
743 m_Snap
.m_SpecInfo
.m_Active
= true;
744 m_Snap
.m_SpecInfo
.m_SpectatorID
= SPEC_FREEVIEW
;
748 // calculate team-balance
749 if(pInfo
->m_Team
!= TEAM_SPECTATORS
)
750 m_Snap
.m_aTeamSize
[pInfo
->m_Team
]++;
753 else if(Item
.m_Type
== NETOBJTYPE_CHARACTER
)
755 const void *pOld
= Client()->SnapFindItem(IClient::SNAP_PREV
, NETOBJTYPE_CHARACTER
, Item
.m_ID
);
756 m_Snap
.m_aCharacters
[Item
.m_ID
].m_Cur
= *((const CNetObj_Character
*)pData
);
759 m_Snap
.m_aCharacters
[Item
.m_ID
].m_Active
= true;
760 m_Snap
.m_aCharacters
[Item
.m_ID
].m_Prev
= *((const CNetObj_Character
*)pOld
);
762 if(m_Snap
.m_aCharacters
[Item
.m_ID
].m_Prev
.m_Tick
)
763 Evolve(&m_Snap
.m_aCharacters
[Item
.m_ID
].m_Prev
, Client()->PrevGameTick());
764 if(m_Snap
.m_aCharacters
[Item
.m_ID
].m_Cur
.m_Tick
)
765 Evolve(&m_Snap
.m_aCharacters
[Item
.m_ID
].m_Cur
, Client()->GameTick());
768 else if(Item
.m_Type
== NETOBJTYPE_SPECTATORINFO
)
770 m_Snap
.m_pSpectatorInfo
= (const CNetObj_SpectatorInfo
*)pData
;
771 m_Snap
.m_pPrevSpectatorInfo
= (const CNetObj_SpectatorInfo
*)Client()->SnapFindItem(IClient::SNAP_PREV
, NETOBJTYPE_SPECTATORINFO
, Item
.m_ID
);
773 m_Snap
.m_SpecInfo
.m_SpectatorID
= m_Snap
.m_pSpectatorInfo
->m_SpectatorID
;
775 else if(Item
.m_Type
== NETOBJTYPE_GAMEINFO
)
777 static bool s_GameOver
= 0;
778 m_Snap
.m_pGameInfoObj
= (const CNetObj_GameInfo
*)pData
;
779 if(!s_GameOver
&& m_Snap
.m_pGameInfoObj
->m_GameStateFlags
&GAMESTATEFLAG_GAMEOVER
)
781 else if(s_GameOver
&& !(m_Snap
.m_pGameInfoObj
->m_GameStateFlags
&GAMESTATEFLAG_GAMEOVER
))
783 s_GameOver
= m_Snap
.m_pGameInfoObj
->m_GameStateFlags
&GAMESTATEFLAG_GAMEOVER
;
785 else if(Item
.m_Type
== NETOBJTYPE_GAMEDATA
)
787 m_Snap
.m_pGameDataObj
= (const CNetObj_GameData
*)pData
;
788 m_Snap
.m_GameDataSnapID
= Item
.m_ID
;
789 if(m_Snap
.m_pGameDataObj
->m_FlagCarrierRed
== FLAG_TAKEN
)
791 if(m_FlagDropTick
[TEAM_RED
] == 0)
792 m_FlagDropTick
[TEAM_RED
] = Client()->GameTick();
794 else if(m_FlagDropTick
[TEAM_RED
] != 0)
795 m_FlagDropTick
[TEAM_RED
] = 0;
796 if(m_Snap
.m_pGameDataObj
->m_FlagCarrierBlue
== FLAG_TAKEN
)
798 if(m_FlagDropTick
[TEAM_BLUE
] == 0)
799 m_FlagDropTick
[TEAM_BLUE
] = Client()->GameTick();
801 else if(m_FlagDropTick
[TEAM_BLUE
] != 0)
802 m_FlagDropTick
[TEAM_BLUE
] = 0;
804 else if(Item
.m_Type
== NETOBJTYPE_FLAG
)
805 m_Snap
.m_paFlags
[Item
.m_ID
%2] = (const CNetObj_Flag
*)pData
;
809 // setup local pointers
810 if(m_Snap
.m_LocalClientID
>= 0)
812 CSnapState::CCharacterInfo
*c
= &m_Snap
.m_aCharacters
[m_Snap
.m_LocalClientID
];
815 m_Snap
.m_pLocalCharacter
= &c
->m_Cur
;
816 m_Snap
.m_pLocalPrevCharacter
= &c
->m_Prev
;
817 m_LocalCharacterPos
= vec2(m_Snap
.m_pLocalCharacter
->m_X
, m_Snap
.m_pLocalCharacter
->m_Y
);
819 else if(Client()->SnapFindItem(IClient::SNAP_PREV
, NETOBJTYPE_CHARACTER
, m_Snap
.m_LocalClientID
))
822 m_pControls
->OnPlayerDeath();
827 m_Snap
.m_SpecInfo
.m_Active
= true;
828 if(Client()->State() == IClient::STATE_DEMOPLAYBACK
&& DemoPlayer()->GetDemoType() == IDemoPlayer::DEMOTYPE_SERVER
&&
829 m_DemoSpecID
!= SPEC_FREEVIEW
&& m_Snap
.m_aCharacters
[m_DemoSpecID
].m_Active
)
830 m_Snap
.m_SpecInfo
.m_SpectatorID
= m_DemoSpecID
;
832 m_Snap
.m_SpecInfo
.m_SpectatorID
= SPEC_FREEVIEW
;
835 // clear out unneeded client data
836 for(int i
= 0; i
< MAX_CLIENTS
; ++i
)
838 if(!m_Snap
.m_paPlayerInfos
[i
] && m_aClients
[i
].m_Active
)
839 m_aClients
[i
].Reset();
842 // update friend state
843 for(int i
= 0; i
< MAX_CLIENTS
; ++i
)
845 if(i
== m_Snap
.m_LocalClientID
|| !m_Snap
.m_paPlayerInfos
[i
] || !Friends()->IsFriend(m_aClients
[i
].m_aName
, m_aClients
[i
].m_aClan
, true))
846 m_aClients
[i
].m_Friend
= false;
848 m_aClients
[i
].m_Friend
= true;
851 // sort player infos by score
852 mem_copy(m_Snap
.m_paInfoByScore
, m_Snap
.m_paPlayerInfos
, sizeof(m_Snap
.m_paInfoByScore
));
853 for(int k
= 0; k
< MAX_CLIENTS
-1; k
++) // ffs, bubblesort
855 for(int i
= 0; i
< MAX_CLIENTS
-k
-1; i
++)
857 if(m_Snap
.m_paInfoByScore
[i
+1] && (!m_Snap
.m_paInfoByScore
[i
] || m_Snap
.m_paInfoByScore
[i
]->m_Score
< m_Snap
.m_paInfoByScore
[i
+1]->m_Score
))
859 const CNetObj_PlayerInfo
*pTmp
= m_Snap
.m_paInfoByScore
[i
];
860 m_Snap
.m_paInfoByScore
[i
] = m_Snap
.m_paInfoByScore
[i
+1];
861 m_Snap
.m_paInfoByScore
[i
+1] = pTmp
;
866 CTuningParams StandardTuning
;
867 CServerInfo CurrentServerInfo
;
868 Client()->GetServerInfo(&CurrentServerInfo
);
869 if(CurrentServerInfo
.m_aGameType
[0] != '0')
871 if(str_comp(CurrentServerInfo
.m_aGameType
, "DM") != 0 && str_comp(CurrentServerInfo
.m_aGameType
, "TDM") != 0 && str_comp(CurrentServerInfo
.m_aGameType
, "CTF") != 0)
872 m_ServerMode
= SERVERMODE_MOD
;
873 else if(mem_comp(&StandardTuning
, &m_Tuning
, sizeof(CTuningParams
)) == 0)
874 m_ServerMode
= SERVERMODE_PURE
;
876 m_ServerMode
= SERVERMODE_PUREMOD
;
881 void CGameClient::OnPredict()
883 // store the previous values so we can detect prediction errors
884 CCharacterCore BeforePrevChar
= m_PredictedPrevChar
;
885 CCharacterCore BeforeChar
= m_PredictedChar
;
887 // we can't predict without our own id or own character
888 if(m_Snap
.m_LocalClientID
== -1 || !m_Snap
.m_aCharacters
[m_Snap
.m_LocalClientID
].m_Active
)
891 // don't predict anything if we are paused
892 if(m_Snap
.m_pGameInfoObj
&& m_Snap
.m_pGameInfoObj
->m_GameStateFlags
&GAMESTATEFLAG_PAUSED
)
894 if(m_Snap
.m_pLocalCharacter
)
895 m_PredictedChar
.Read(m_Snap
.m_pLocalCharacter
);
896 if(m_Snap
.m_pLocalPrevCharacter
)
897 m_PredictedPrevChar
.Read(m_Snap
.m_pLocalPrevCharacter
);
901 // repredict character
903 World
.m_Tuning
= m_Tuning
;
905 // search for players
906 for(int i
= 0; i
< MAX_CLIENTS
; i
++)
908 if(!m_Snap
.m_aCharacters
[i
].m_Active
)
911 g_GameClient
.m_aClients
[i
].m_Predicted
.Init(&World
, Collision());
912 World
.m_apCharacters
[i
] = &g_GameClient
.m_aClients
[i
].m_Predicted
;
913 g_GameClient
.m_aClients
[i
].m_Predicted
.Read(&m_Snap
.m_aCharacters
[i
].m_Cur
);
917 for(int Tick
= Client()->GameTick()+1; Tick
<= Client()->PredGameTick(); Tick
++)
920 if(Tick
== Client()->PredGameTick() && World
.m_apCharacters
[m_Snap
.m_LocalClientID
])
921 m_PredictedPrevChar
= *World
.m_apCharacters
[m_Snap
.m_LocalClientID
];
923 // first calculate where everyone should move
924 for(int c
= 0; c
< MAX_CLIENTS
; c
++)
926 if(!World
.m_apCharacters
[c
])
929 mem_zero(&World
.m_apCharacters
[c
]->m_Input
, sizeof(World
.m_apCharacters
[c
]->m_Input
));
930 if(m_Snap
.m_LocalClientID
== c
)
932 // apply player input
933 int *pInput
= Client()->GetInput(Tick
);
935 World
.m_apCharacters
[c
]->m_Input
= *((CNetObj_PlayerInput
*)pInput
);
936 World
.m_apCharacters
[c
]->Tick(true);
939 World
.m_apCharacters
[c
]->Tick(false);
943 // move all players and quantize their data
944 for(int c
= 0; c
< MAX_CLIENTS
; c
++)
946 if(!World
.m_apCharacters
[c
])
949 World
.m_apCharacters
[c
]->Move();
950 World
.m_apCharacters
[c
]->Quantize();
953 // check if we want to trigger effects
954 if(Tick
> m_LastNewPredictedTick
)
956 m_LastNewPredictedTick
= Tick
;
957 m_NewPredictedTick
= true;
959 if(m_Snap
.m_LocalClientID
!= -1 && World
.m_apCharacters
[m_Snap
.m_LocalClientID
])
961 vec2 Pos
= World
.m_apCharacters
[m_Snap
.m_LocalClientID
]->m_Pos
;
962 int Events
= World
.m_apCharacters
[m_Snap
.m_LocalClientID
]->m_TriggeredEvents
;
963 if(Events
&COREEVENT_GROUND_JUMP
) g_GameClient
.m_pSounds
->PlayAndRecord(CSounds::CHN_WORLD
, SOUND_PLAYER_JUMP
, 1.0f
, Pos
);
965 /*if(events&COREEVENT_AIR_JUMP)
967 GameClient.effects->air_jump(pos);
968 GameClient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos);
971 //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
972 //if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos);
973 if(Events
&COREEVENT_HOOK_ATTACH_GROUND
) g_GameClient
.m_pSounds
->PlayAndRecord(CSounds::CHN_WORLD
, SOUND_HOOK_ATTACH_GROUND
, 1.0f
, Pos
);
974 if(Events
&COREEVENT_HOOK_HIT_NOHOOK
) g_GameClient
.m_pSounds
->PlayAndRecord(CSounds::CHN_WORLD
, SOUND_HOOK_NOATTACH
, 1.0f
, Pos
);
975 //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
979 if(Tick
== Client()->PredGameTick() && World
.m_apCharacters
[m_Snap
.m_LocalClientID
])
980 m_PredictedChar
= *World
.m_apCharacters
[m_Snap
.m_LocalClientID
];
983 if(g_Config
.m_Debug
&& g_Config
.m_ClPredict
&& m_PredictedTick
== Client()->PredGameTick())
985 CNetObj_CharacterCore Before
= {0}, Now
= {0}, BeforePrev
= {0}, NowPrev
= {0};
986 BeforeChar
.Write(&Before
);
987 BeforePrevChar
.Write(&BeforePrev
);
988 m_PredictedChar
.Write(&Now
);
989 m_PredictedPrevChar
.Write(&NowPrev
);
991 if(mem_comp(&Before
, &Now
, sizeof(CNetObj_CharacterCore
)) != 0)
993 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "client", "prediction error");
994 for(unsigned i
= 0; i
< sizeof(CNetObj_CharacterCore
)/sizeof(int); i
++)
995 if(((int *)&Before
)[i
] != ((int *)&Now
)[i
])
998 str_format(aBuf
, sizeof(aBuf
), " %d %d %d (%d %d)", i
, ((int *)&Before
)[i
], ((int *)&Now
)[i
], ((int *)&BeforePrev
)[i
], ((int *)&NowPrev
)[i
]);
999 Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "client", aBuf
);
1004 m_PredictedTick
= Client()->PredGameTick();
1007 void CGameClient::OnActivateEditor()
1012 void CGameClient::CClientData::UpdateRenderInfo()
1014 m_RenderInfo
= m_SkinInfo
;
1016 // force team colors
1017 if(g_GameClient
.m_Snap
.m_pGameInfoObj
&& g_GameClient
.m_Snap
.m_pGameInfoObj
->m_GameFlags
&GAMEFLAG_TEAMS
)
1019 m_RenderInfo
.m_Texture
= g_GameClient
.m_pSkins
->Get(m_SkinID
)->m_ColorTexture
;
1020 const int TeamColors
[2] = {65387, 10223467};
1021 if(m_Team
>= TEAM_RED
&& m_Team
<= TEAM_BLUE
)
1023 m_RenderInfo
.m_ColorBody
= g_GameClient
.m_pSkins
->GetColorV4(TeamColors
[m_Team
]);
1024 m_RenderInfo
.m_ColorFeet
= g_GameClient
.m_pSkins
->GetColorV4(TeamColors
[m_Team
]);
1028 m_RenderInfo
.m_ColorBody
= g_GameClient
.m_pSkins
->GetColorV4(12895054);
1029 m_RenderInfo
.m_ColorFeet
= g_GameClient
.m_pSkins
->GetColorV4(12895054);
1034 void CGameClient::CClientData::Reset()
1043 m_EmoticonStart
= -1;
1045 m_ChatIgnore
= false;
1046 m_SkinInfo
.m_Texture
= g_GameClient
.m_pSkins
->Get(0)->m_ColorTexture
;
1047 m_SkinInfo
.m_ColorBody
= vec4(1,1,1,1);
1048 m_SkinInfo
.m_ColorFeet
= vec4(1,1,1,1);
1052 void CGameClient::SendSwitchTeam(int Team
)
1054 CNetMsg_Cl_SetTeam Msg
;
1056 Client()->SendPackMsg(&Msg
, MSGFLAG_VITAL
);
1059 void CGameClient::SendInfo(bool Start
)
1063 CNetMsg_Cl_StartInfo Msg
;
1064 Msg
.m_pName
= g_Config
.m_PlayerName
;
1065 Msg
.m_pClan
= g_Config
.m_PlayerClan
;
1066 Msg
.m_Country
= g_Config
.m_PlayerCountry
;
1067 Msg
.m_pSkin
= g_Config
.m_PlayerSkin
;
1068 Msg
.m_UseCustomColor
= g_Config
.m_PlayerUseCustomColor
;
1069 Msg
.m_ColorBody
= g_Config
.m_PlayerColorBody
;
1070 Msg
.m_ColorFeet
= g_Config
.m_PlayerColorFeet
;
1071 Client()->SendPackMsg(&Msg
, MSGFLAG_VITAL
);
1075 CNetMsg_Cl_ChangeInfo Msg
;
1076 Msg
.m_pName
= g_Config
.m_PlayerName
;
1077 Msg
.m_pClan
= g_Config
.m_PlayerClan
;
1078 Msg
.m_Country
= g_Config
.m_PlayerCountry
;
1079 Msg
.m_pSkin
= g_Config
.m_PlayerSkin
;
1080 Msg
.m_UseCustomColor
= g_Config
.m_PlayerUseCustomColor
;
1081 Msg
.m_ColorBody
= g_Config
.m_PlayerColorBody
;
1082 Msg
.m_ColorFeet
= g_Config
.m_PlayerColorFeet
;
1083 Client()->SendPackMsg(&Msg
, MSGFLAG_VITAL
);
1085 // activate timer to resend the info if it gets filtered
1086 if(!m_LastSendInfo
|| m_LastSendInfo
+time_freq()*5 < time_get())
1087 m_LastSendInfo
= time_get();
1091 void CGameClient::SendKill(int ClientID
)
1093 CNetMsg_Cl_Kill Msg
;
1094 Client()->SendPackMsg(&Msg
, MSGFLAG_VITAL
);
1097 void CGameClient::ConTeam(IConsole::IResult
*pResult
, void *pUserData
)
1099 ((CGameClient
*)pUserData
)->SendSwitchTeam(pResult
->GetInteger(0));
1102 void CGameClient::ConKill(IConsole::IResult
*pResult
, void *pUserData
)
1104 ((CGameClient
*)pUserData
)->SendKill(-1);
1107 void CGameClient::ConchainSpecialInfoupdate(IConsole::IResult
*pResult
, void *pUserData
, IConsole::FCommandCallback pfnCallback
, void *pCallbackUserData
)
1109 pfnCallback(pResult
, pCallbackUserData
);
1110 if(pResult
->NumArguments())
1111 ((CGameClient
*)pUserData
)->SendInfo(false);
1114 IGameClient
*CreateGameClient()
1116 return &g_GameClient
;