1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
20 uses e_msg
, g_net
, g_triggers
, Classes
, SysUtils
, md5
;
66 NET_MSG_RCON_AUTH
= 191;
67 NET_MSG_RCON_CMD
= 192;
68 NET_MSG_TIME_SYNC
= 194;
69 NET_MSG_VOTE_EVENT
= 195;
72 NET_MSG_MAP_REQUEST = 201;
73 NET_MSG_MAP_RESPONSE = 202;
74 NET_MSG_RES_REQUEST = 203;
75 NET_MSG_RES_RESPONSE = 204;
99 NET_EV_CHANGE_TEAM
= 3;
100 NET_EV_PLAYER_KICK
= 4;
101 NET_EV_PLAYER_BAN
= 5;
102 NET_EV_LMS_WARMUP
= 6;
103 NET_EV_LMS_SURVIVOR
= 7;
107 NET_EV_SCORE_MSG
= 11;
108 NET_EV_LMS_START
= 12;
110 NET_EV_TLMS_WIN
= 14;
111 NET_EV_LMS_LOSE
= 15;
112 NET_EV_LMS_DRAW
= 16;
113 NET_EV_KILLCOMBO
= 17;
114 NET_EV_PLAYER_TOUCH
= 18;
116 NET_EV_INTER_READY
= 20;
117 NET_EV_LMS_NOSPAWN
= 21;
124 NET_VE_INPROGRESS
= 6;
131 NET_CHEAT_SUICIDE
= 1;
132 NET_CHEAT_SPECTATE
= 2;
134 NET_CHEAT_DROPFLAG
= 4;
136 NET_MAX_DIFFTIME
= 5000 div 36;
140 procedure MH_MalformedPacket(C
: pTNetClient
);
141 procedure MH_ProcessFirstSpawn (C
: pTNetClient
);
143 procedure MH_RECV_Info(C
: pTNetClient
; var M
: TMsg
);
144 procedure MH_RECV_Chat(C
: pTNetClient
; var M
: TMsg
);
145 procedure MH_RECV_FullStateRequest(C
: pTNetClient
; var M
: TMsg
);
146 function MH_RECV_PlayerPos(C
: pTNetClient
; var M
: TMsg
): Word;
147 procedure MH_RECV_PlayerSettings(C
: pTNetClient
; var M
: TMsg
);
148 procedure MH_RECV_CheatRequest(C
: pTNetClient
; var M
: TMsg
);
149 procedure MH_RECV_RCONPassword(C
: pTNetClient
; var M
: TMsg
);
150 procedure MH_RECV_RCONCommand(C
: pTNetClient
; var M
: TMsg
);
151 //procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg);
152 //procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg);
153 procedure MH_RECV_Vote(C
: pTNetClient
; var M
: TMsg
);
156 procedure MH_SEND_Everything(CreatePlayers
: Boolean {= False}; ID
: Integer {= NET_EVERYONE});
157 procedure MH_SEND_Info(ID
: Byte);
158 procedure MH_SEND_Chat(Txt
: String; Mode
: Byte; ID
: Integer = NET_EVERYONE
);
159 procedure MH_SEND_Effect(X
, Y
: Integer; Ang
: SmallInt
; Kind
: Byte; ID
: Integer = NET_EVERYONE
);
160 procedure MH_SEND_Sound(X
, Y
: Integer; Name
: String; Pos
: Boolean = True; ID
: Integer = NET_EVERYONE
);
161 procedure MH_SEND_CreateProj(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
162 procedure MH_SEND_UpdateProj(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
163 procedure MH_SEND_DeleteProj(Proj
: LongInt; X
, Y
: LongInt; Loud
: Boolean = True; ID
: Integer = NET_EVERYONE
);
164 procedure MH_SEND_GameStats(ID
: Integer = NET_EVERYONE
);
165 procedure MH_SEND_CoopStats(ID
: Integer = NET_EVERYONE
);
166 procedure MH_SEND_GameEvent(EvType
: Byte; EvNum
: Integer = 0; EvStr
: String = 'N'; ID
: Integer = NET_EVERYONE
);
167 procedure MH_SEND_FlagEvent(EvType
: Byte; Flag
: Byte; PID
: Word; Quiet
: Boolean = False; ID
: Integer = NET_EVERYONE
);
168 procedure MH_SEND_FlagPos(Flag
: Byte; ID
: Integer = NET_EVERYONE
);
169 procedure MH_SEND_GameSettings(ID
: Integer = NET_EVERYONE
);
171 procedure MH_SEND_PlayerCreate(PID
: Word; ID
: Integer = NET_EVERYONE
);
172 procedure MH_SEND_PlayerPos(Reliable
: Boolean; PID
: Word; ID
: Integer = NET_EVERYONE
);
173 procedure MH_SEND_PlayerStats(PID
: Word; ID
: Integer = NET_EVERYONE
);
174 procedure MH_SEND_PlayerDelete(PID
: Word; ID
: Integer = NET_EVERYONE
);
175 procedure MH_SEND_PlayerDamage(PID
: Word; Kind
: Byte; Attacker
, Value
: Word; VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
176 procedure MH_SEND_PlayerFire(PID
: Word; Weapon
: Byte; X
, Y
, AX
, AY
: Integer; ShotID
: Integer; ID
: Integer = NET_EVERYONE
);
177 procedure MH_SEND_PlayerDeath(PID
: Word; KillType
, DeathType
: Byte; Attacker
: Word; ID
: Integer = NET_EVERYONE
);
178 procedure MH_SEND_PlayerSettings(PID
: Word; Mdl
: String = ''; ID
: Integer = NET_EVERYONE
);
180 procedure MH_SEND_ItemSpawn(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
181 procedure MH_SEND_ItemDestroy(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
182 procedure MH_SEND_ItemPos(IID
: Word; ID
: Integer = NET_EVERYONE
);
184 procedure MH_SEND_PanelTexture(PGUID
: Integer; AnimLoop
: Byte; ID
: Integer = NET_EVERYONE
);
185 procedure MH_SEND_PanelState(PGUID
: Integer; ID
: Integer = NET_EVERYONE
);
187 procedure MH_SEND_MonsterSpawn(UID
: Word; ID
: Integer = NET_EVERYONE
);
188 procedure MH_SEND_MonsterPos(UID
: Word; ID
: Integer = NET_EVERYONE
);
189 procedure MH_SEND_MonsterState(UID
: Word; ForcedAnim
: Byte = 255; ID
: Integer = NET_EVERYONE
);
190 procedure MH_SEND_MonsterShot(UID
: Word; X
, Y
, VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
191 procedure MH_SEND_MonsterDelete(UID
: Word; ID
: Integer = NET_EVERYONE
);
193 procedure MH_SEND_TriggerSound(ClientID
: DWORD
; play
: Boolean; pos
: LongWord
; count
: Integer; ID
: Integer = NET_EVERYONE
);
194 procedure MH_SEND_TriggerMusic(name
: String; play
: Boolean; pos
: LongWord
; pause
: Boolean; ID
: Integer = NET_EVERYONE
);
196 procedure MH_SEND_TimeSync(Time
: LongWord
; ID
: Integer = NET_EVERYONE
);
197 procedure MH_SEND_VoteEvent(EvType
: Byte; StrArg1
: String = 'a'; StrArg2
: String = 'b';
198 IntArg1
: SmallInt
= 0; IntArg2
: SmallInt
= 0; ID
: Integer = NET_EVERYONE
);
200 // CLIENT MESSAGES //
203 procedure MC_RECV_Chat(var M
: TMsg
);
204 procedure MC_RECV_Effect(var M
: TMsg
);
205 procedure MC_RECV_Sound(var M
: TMsg
);
206 procedure MC_RECV_GameStats(var M
: TMsg
);
207 procedure MC_RECV_CoopStats(var M
: TMsg
);
208 procedure MC_RECV_GameEvent(var M
: TMsg
);
209 procedure MC_RECV_FlagEvent(var M
: TMsg
);
210 procedure MC_RECV_FlagPos(var M
: TMsg
);
211 procedure MC_RECV_GameSettings(var M
: TMsg
);
213 function MC_RECV_PlayerCreate(var M
: TMsg
): Word;
214 function MC_RECV_PlayerPos(var M
: TMsg
): Word;
215 function MC_RECV_PlayerStats(var M
: TMsg
): Word;
216 function MC_RECV_PlayerDelete(var M
: TMsg
): Word;
217 function MC_RECV_PlayerDamage(var M
: TMsg
): Word;
218 function MC_RECV_PlayerDeath(var M
: TMsg
): Word;
219 function MC_RECV_PlayerFire(var M
: TMsg
): Word;
220 procedure MC_RECV_PlayerSettings(var M
: TMsg
);
222 procedure MC_RECV_ItemSpawn(var M
: TMsg
);
223 procedure MC_RECV_ItemDestroy(var M
: TMsg
);
224 procedure MC_RECV_ItemPos(var M
: TMsg
);
226 procedure MC_RECV_PanelTexture(var M
: TMsg
);
227 procedure MC_RECV_PanelState(var M
: TMsg
);
229 procedure MC_RECV_MonsterSpawn(var M
: TMsg
);
230 procedure MC_RECV_MonsterPos(var M
: TMsg
);
231 procedure MC_RECV_MonsterState(var M
: TMsg
);
232 procedure MC_RECV_MonsterShot(var M
: TMsg
);
233 procedure MC_RECV_MonsterDelete(var M
: TMsg
);
235 procedure MC_RECV_CreateProj(var M
: TMsg
);
236 procedure MC_RECV_UpdateProj(var M
: TMsg
);
237 procedure MC_RECV_DeleteProj(var M
: TMsg
);
239 procedure MC_RECV_TriggerSound(var M
: TMsg
);
240 procedure MC_RECV_TriggerMusic(var M
: TMsg
);
242 procedure MC_RECV_TimeSync(var M
: TMsg
);
243 procedure MC_RECV_VoteEvent(var M
: TMsg
);
245 procedure MC_SEND_Info(Password
: String);
246 procedure MC_SEND_Chat(Txt
: String; Mode
: Byte);
247 procedure MC_SEND_PlayerPos();
248 procedure MC_SEND_FullStateRequest();
249 procedure MC_SEND_PlayerSettings();
250 procedure MC_SEND_CheatRequest(Kind
: Byte);
251 procedure MC_SEND_RCONPassword(Password
: String);
252 procedure MC_SEND_RCONCommand(Cmd
: String);
253 procedure MC_SEND_Vote(Start
: Boolean = False; Command
: String = 'a');
255 //procedure MC_SEND_MapRequest();
256 //procedure MC_SEND_ResRequest(const resName: AnsiString);
260 TExternalResourceInfo
= record
275 ExternalResources
: array of TExternalResourceInfo
;
281 {$IFDEF ENABLE_SOUND}
284 Math
, ENet
, e_input
, e_graphics
, e_log
,
285 g_textures
, g_gfx
, g_console
, g_basic
, g_options
, g_main
,
286 g_game
, g_player
, g_map
, g_panel
, g_items
, g_weapons
, g_phys
, g_gui
,
287 g_language
, g_monsters
, g_netmaster
, utils
, wadreader
, MAPDEF
;
290 NET_KEY_LEFT
= 1 shl 0;
291 NET_KEY_RIGHT
= 1 shl 1;
292 NET_KEY_UP
= 1 shl 2;
293 NET_KEY_DOWN
= 1 shl 3;
294 NET_KEY_JUMP
= 1 shl 4;
295 NET_KEY_FIRE
= 1 shl 5;
296 NET_KEY_OPEN
= 1 shl 6;
297 NET_KEY_CHAT
= 1 shl 7;
298 NET_KEY_FORCEDIR
= 1 shl 8;
301 //kBytePrev: Word = 0;
302 //kDirPrev: TDirection = D_LEFT;
303 //HostGameTime: Word = 0;
311 procedure MH_MalformedPacket(C
: pTNetClient
);
313 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
314 _lc
[I_NET_DISC_BADMSG
]);
315 g_Net_Host_Ban(C
, True);
318 procedure MH_RECV_Chat(C
: pTNetClient
; var M
: TMsg
);
327 Pl
:= g_Player_Get(PID
);
331 Txt
:= M
.ReadString();
332 Mode
:= M
.ReadByte();
337 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
339 if (Mode
= NET_CHAT_SYSTEM
) then
340 Mode
:= NET_CHAT_PLAYER
; // prevent sending system messages from clients
341 if (Mode
= NET_CHAT_TEAM
) and (not gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
342 Mode
:= NET_CHAT_PLAYER
; // revert to player chat in non-team game
345 MH_SEND_Chat(Txt
, Mode
)
347 MH_SEND_Chat(Pl
.Name
+ ': ' + Txt
, Mode
, IfThen(Mode
= NET_CHAT_TEAM
, Pl
.Team
, NET_EVERYONE
));
350 procedure MH_RECV_Info(C
: pTNetClient
; var M
: TMsg
);
352 Ver
, PName
, Model
, Pw
: String;
355 TmpPrefArray
: Array [WP_FIRST
.. WP_LAST
+ 1] of Byte;
365 Ver
:= M
.ReadString();
366 Pw
:= M
.ReadString();
367 PName
:= M
.ReadString();
368 Model
:= M
.ReadString();
373 WeapSwitch
:= M
.ReadByte();
374 for I
:= WP_FIRST
to WP_LAST
+ 1 do
375 TmpPrefArray
[I
] := M
.ReadByte();
376 SwitchEmpty
:= M
.ReadByte();
377 SkipF
:= M
.ReadByte();
382 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
384 if Ver
<> GAME_VERSION
then
386 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
387 _lc
[I_NET_DISC_VERSION
]);
388 g_Net_Host_Kick(C
^.ID
, NET_DISC_VERSION
);
392 if g_Net_IsAddressBanned(C
^.Peer
^.address
.host
) then
394 if g_Net_IsAddressBanned(C
^.Peer
^.address
.host
, True) then
396 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
397 _lc
[I_NET_DISC_BAN
]);
398 g_Net_Host_Kick(C
^.ID
, NET_DISC_BAN
);
402 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
403 _lc
[I_NET_DISC_BAN
]);
404 g_Net_Host_Kick(C
^.ID
, NET_DISC_TEMPBAN
);
409 if NetPassword
<> '' then
410 if AnsiLowerCase(NetPassword
) <> AnsiLowerCase(Pw
) then
412 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
413 _lc
[I_NET_DISC_PASSWORD
]);
414 g_Net_Host_Kick(C
^.ID
, NET_DISC_PASSWORD
);
418 if (C
^.Player
<> 0) then
420 // already received info
421 g_Net_Penalize(C
, 'client info spam');
429 PID
:= g_Player_Create(Model
, Color
, T
, False);
430 with g_Player_Get(PID
) do
433 WeapSwitchMode
:= WeapSwitch
;
434 SetWeaponPrefs(TmpPrefArray
);
435 SwitchToEmpty
:= SwitchEmpty
;
436 SkipIronFist
:= SkipF
;
437 if (g_Force_Model_Get() <> 0) then
438 SetModel(g_Forced_Model_GetName());
443 C
^.WaitForFirstSpawn
:= false;
446 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [PName
]), True);
447 e_WriteLog('NET: Client ' + PName
+ ' [' + IntToStr(C
^.ID
) +
448 '] connected. Assigned player #' + IntToStr(PID
) + '.', TMsgType
.Notify
);
452 with g_Player_Get(PID
) do
456 // round in progress, don't spawn
457 e_LogWritefln('*** client #%u (cid #%u) authenticated...', [C
.ID
, C
.Player
]);
458 //e_LogWritefln('spawning player with pid #%u...', [PID]);
459 //Respawn(gGameSettings.GameType = GT_SINGLE);
460 //k8: no, do not spawn a player yet, wait for "request full state" packet
464 // `FWantsInGame` seems to mean "spawn the player on the next occasion".
465 // that is, if we'll set it to `true`, the player can be spawned after
466 // warmup time ran out, for example, regardless of the real player state.
467 // also, this seems to work only for the initial connection. further
468 // map changes could initiate resource downloading, but the player will
469 // be spawned immediately.
470 // the proper solution will require another player state, "ephemeral".
471 // the player should start any map in "ephemeral" state, and turned into
472 // real mobj only when they sent a special "i am ready" packet. this packet
473 // must be sent after receiving the full state, so the player will get a full
474 // map view before going into game.
475 FWantsInGame
:= false;
476 C
^.WaitForFirstSpawn
:= true;
479 //if not C^.WaitForFirstSpawn then
481 for I
:= Low(NetClients
) to High(NetClients
) do
483 if NetClients
[I
].ID
= C
^.ID
then Continue
;
484 MH_SEND_PlayerCreate(PID
, NetClients
[I
].ID
);
485 MH_SEND_PlayerPos(True, PID
, NetClients
[I
].ID
);
486 MH_SEND_PlayerStats(PID
, NetClients
[I
].ID
);
490 if gState
in [STATE_INTERCUSTOM
, STATE_FOLD
] then
491 MH_SEND_GameEvent(NET_EV_MAPEND
, 0, 'N', C
^.ID
);
495 //g_Net_Slist_Update;
501 procedure MH_ProcessFirstSpawn (C
: pTNetClient
);
505 if not C
.WaitForFirstSpawn
then Exit
;
506 plr
:= g_Player_Get(C
^.Player
);
507 if not assigned(plr
) then Exit
;
508 g_Net_Slist_ServerPlayerComes();
509 e_LogWritefln('*** client #%u (cid #%u) first spawn', [C
.ID
, C
.Player
]);
510 C
.WaitForFirstSpawn
:= false;
511 plr
.FNoRespawn
:= false;
512 plr
.FWantsInGame
:= true; // TODO: look into this later
514 if (gGameSettings
.MaxLives
> 0) and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
517 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN
, 0, 'N', C
.ID
);
522 if gLMSRespawn
> LMS_RESPAWN_NONE
then
523 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, gLMSRespawnTime
- gTime
, 'N', C
.ID
);
528 procedure MH_RECV_FullStateRequest(C
: pTNetClient
; var M
: TMsg
);
530 //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
532 if C
^.FullUpdateSent
then
534 // FullStateRequest spam?
535 g_Net_Penalize(C
, 'duplicate full state request');
541 MH_SEND_Everything((C
^.State
= NET_STATE_AUTH
), C
^.ID
)
545 C
^.RequestedFullUpdate
:= True;
551 function MH_RECV_PlayerPos(C
: pTNetClient
; var M
: TMsg
): Word;
564 if not gGameOn
then Exit
;
567 GT
:= M
.ReadLongWord();
572 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
575 Pl
:= g_Player_Get(PID
);
579 if (GT
> gTime
+ NET_MAX_DIFFTIME
) or (GT
< Pl
.NetTime
) then Exit
;
585 kByte
:= M
.ReadWord();
587 WeaponAct
:= M
.ReadByte();
588 WeaponSelect
:= M
.ReadWord();
593 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
595 //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
596 if Direction
<> TDirection(Dir
) then
597 JustTeleported
:= False;
599 SetDirection(TDirection(Dir
));
602 if kByte
= NET_KEY_CHAT
then
604 PressKey(KEY_CHAT
, 10000);
608 if LongBool(kByte
and NET_KEY_LEFT
) then PressKey(KEY_LEFT
, 10000);
609 if LongBool(kByte
and NET_KEY_RIGHT
) then PressKey(KEY_RIGHT
, 10000);
610 if LongBool(kByte
and NET_KEY_UP
) then PressKey(KEY_UP
, 10000);
611 if LongBool(kByte
and NET_KEY_DOWN
) then PressKey(KEY_DOWN
, 10000);
612 if LongBool(kByte
and NET_KEY_JUMP
) then PressKey(KEY_JUMP
, 10000);
613 if LongBool(kByte
and NET_KEY_FIRE
) then PressKey(KEY_FIRE
, 10000);
614 if LongBool(kByte
and NET_KEY_OPEN
) then PressKey(KEY_OPEN
, 10000);
618 if (WeaponAct
and Byte(1 shl i
)) <> 0 then
620 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
621 ProcessWeaponAction(i
);
627 if (WeaponSelect
and Word(1 shl i
)) <> 0 then
629 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
630 QueueWeaponSwitch(i
);
635 // MH_SEND_PlayerPos(False, PID, C^.ID);
638 procedure MH_RECV_CheatRequest(C
: pTNetClient
; var M
: TMsg
);
645 Pl
:= g_Player_Get(C
^.Player
);
646 if Pl
= nil then Exit
;
649 CheatKind
:= M
.ReadByte();
654 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
658 Pl
.Damage(SUICIDE_DAMAGE
, Pl
.UID
, 0, 0, HIT_SELF
);
661 if Pl
.FSpectator
then
663 if (gGameSettings
.MaxLives
= 0) or (gLMSRespawn
> LMS_RESPAWN_NONE
) then
666 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN
, Pl
.UID
);
673 if gState
<> STATE_INTERCUSTOM
then Exit
;
674 Pl
.FReady
:= not Pl
.FReady
;
677 MH_SEND_GameEvent(NET_EV_INTER_READY
, Pl
.UID
, 'Y');
678 Inc(gInterReadyCount
);
682 MH_SEND_GameEvent(NET_EV_INTER_READY
, Pl
.UID
, 'N');
683 Dec(gInterReadyCount
);
691 procedure MH_RECV_PlayerSettings(C
: pTNetClient
; var M
: TMsg
);
698 TmpPrefArray
: Array [WP_FIRST
.. WP_LAST
+ 1] of Byte;
707 TmpName
:= M
.ReadString();
708 TmpModel
:= M
.ReadString();
709 TmpColor
.R
:= M
.ReadByte();
710 TmpColor
.G
:= M
.ReadByte();
711 TmpColor
.B
:= M
.ReadByte();
712 TmpTeam
:= M
.ReadByte();
713 TmpWeapSwitch
:= M
.ReadByte();
714 for I
:= WP_FIRST
to WP_LAST
+ 1 do
715 TmpPrefArray
[I
] := M
.ReadByte();
716 TmpSwEmpty
:= M
.ReadByte();
717 TmpSkipF
:= M
.ReadByte();
722 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
724 Pl
:= g_Player_Get(C
^.Player
);
725 if Pl
= nil then Exit
;
727 if (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) and (Pl
.Team
<> TmpTeam
) then
730 Pl
.SetColor(TmpColor
);
732 if Pl
.Name
<> TmpName
then
734 g_Console_Add(Format(_lc
[I_PLAYER_NAME
], [Pl
.Name
, TmpName
]), True);
738 if (g_Force_Model_Get() <> 0) then
739 TmpModel
:= g_Forced_Model_GetName();
740 if TmpModel
<> Pl
.Model
.Name
then
741 Pl
.SetModel(TmpModel
);
743 if (TmpWeapSwitch
<> Pl
.WeapSwitchMode
) then
744 Pl
.WeapSwitchMode
:= TmpWeapSwitch
;
746 Pl
.SetWeaponPrefs(TmpPrefArray
);
747 if (TmpSwEmpty
<> Pl
.SwitchToEmpty
) then
748 Pl
.SwitchToEmpty
:= TmpSwEmpty
;
750 if (TmpSkipF
<> Pl
.SkipIronFist
) then
751 Pl
.SkipIronFist
:= TmpSkipF
;
753 MH_SEND_PlayerSettings(Pl
.UID
, TmpModel
);
758 procedure MH_RECV_RCONPassword(C
: pTNetClient
; var M
: TMsg
);
765 Pwd
:= M
.ReadString();
769 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
770 if not NetAllowRCON
then Exit
;
771 if Pwd
= NetRCONPassword
then
774 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_PWGOOD
, 'N', C
^.ID
);
777 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_PWBAD
, 'N', C
^.ID
);
780 procedure MH_RECV_RCONCommand(C
: pTNetClient
; var M
: TMsg
);
787 Cmd
:= M
.ReadString();
791 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
792 if not NetAllowRCON
then Exit
;
793 if not C
^.RCONAuth
then
795 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_NOAUTH
, 'N', C
^.ID
);
798 g_Console_Process(Cmd
);
803 procedure MH_RECV_Vote(C
: pTNetClient
; var M
: TMsg
);
806 Name
, Command
: String;
813 Start
:= M
.ReadByte() <> 0;
814 Command
:= M
.ReadString();
819 if Err
then begin MH_MalformedPacket(C
); Exit
; end;
821 Pl
:= g_Player_Get(C
^.Player
);
822 if Pl
= nil then Exit
;
827 if not g_Console_CommandBlacklisted(Command
) then
828 g_Game_StartVote(Command
, Name
);
830 else if gVoteInProgress
then
832 if (gPlayer1
<> nil) or (gPlayer2
<> nil)
833 then Need
:= Floor((NetClientCount
+1)/2.0) + 1
834 else Need
:= Floor(NetClientCount
/2.0) + 1;
839 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_REVOKED
], [Name
, gVoteCount
, Need
]), True);
840 MH_SEND_VoteEvent(NET_VE_REVOKE
, Name
, 'a', gVoteCount
, Need
);
846 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [Name
, gVoteCount
, Need
]), True);
847 MH_SEND_VoteEvent(NET_VE_VOTE
, Name
, 'a', gVoteCount
, Need
);
855 procedure MH_SEND_Everything(CreatePlayers
: Boolean; ID
: Integer);
857 function sendItemRespawn (it
: PItem
): Boolean;
859 result
:= false; // don't stop
860 MH_SEND_ItemSpawn(True, it
.myid
, ID
);
863 function sendMonSpawn (mon
: TMonster
): Boolean;
865 result
:= false; // don't stop
866 MH_SEND_MonsterSpawn(mon
.UID
, ID
);
869 function sendPanelState (pan
: TPanel
): Boolean;
871 result
:= false; // don't stop
872 MH_SEND_PanelState(pan
.guid
, ID
); // anyway, to sync mplats
873 if (pan
.CanChangeTexture
) then MH_SEND_PanelTexture(pan
.guid
, pan
.LastAnimLoop
, ID
);
879 if (Low(NetClients
) > ID
) or (High(NetClients
) < ID
) then
880 Exit
; // bogus client, this shouldn't happen
882 NetClients
[ID
].FullUpdateSent
:= True;
884 e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID
, NetClients
[ID
].Player
]);
886 MH_ProcessFirstSpawn(@NetClients
[ID
]);
888 for I
:= Low(gPlayers
) to High(gPlayers
) do
890 if gPlayers
[I
] <> nil then
892 if CreatePlayers
then MH_SEND_PlayerCreate(gPlayers
[I
].UID
, ID
);
893 MH_SEND_PlayerPos(True, gPlayers
[I
].UID
, ID
);
894 MH_SEND_PlayerStats(gPlayers
[I
].UID
, ID
);
896 if (gPlayers
[I
].Flag
<> FLAG_NONE
) and (gGameSettings
.GameMode
= GM_CTF
) then
897 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED
, gPlayers
[I
].Flag
, gPlayers
[I
].UID
, True, ID
);
901 g_Items_ForEachAlive(sendItemRespawn
, true); // backwards
902 g_Mons_ForEach(sendMonSpawn
);
903 g_Map_ForEachPanel(sendPanelState
);
905 for I
:= Low(gTriggers
) to High(gTriggers
) do
907 if gTriggers
[I
].TriggerType
= TRIGGER_SOUND
then
909 {$IFDEF ENABLE_SOUND}
910 if gTriggers
[I
].Sound
<> nil then
912 MH_SEND_TriggerSound(ClientID
, Sound
.IsPlaying(), Sound
.GetPosition(), SoundPlayCount
, ID
);
915 MH_SEND_TriggerSound(ClientID
, SoundPlay
, SoundPos
, SoundPlayCount
, ID
);
920 for I
:= Low(Projectiles
) to High(Projectiles
) do
922 if Projectiles
[i
].ShotType
in [WEAPON_ROCKETLAUNCHER
, WEAPON_PLASMA
, WEAPON_BFG
] then
923 MH_SEND_CreateProj(i
, ID
);
926 {$IFDEF ENABLE_SOUND}
927 MH_SEND_TriggerMusic(gMusic
.Name
, gMusic
.IsPlaying
, gMusic
.GetPosition
, gMusic
.SpecPause
or gMusic
.IsPaused
, ID
);
929 MH_SEND_TriggerMusic(gMusicName
, gMusicPlay
, gMusicPos
, gMusicPause
or not gMusicPlay
, ID
);
932 MH_SEND_GameStats(ID
);
933 MH_SEND_CoopStats(ID
);
935 if gGameSettings
.GameMode
= GM_CTF
then
937 if gFlags
[FLAG_RED
].State
<> FLAG_STATE_CAPTURED
then MH_SEND_FlagEvent(gFlags
[FLAG_RED
].State
, FLAG_RED
, 0, True, ID
);
938 if gFlags
[FLAG_BLUE
].State
<> FLAG_STATE_CAPTURED
then MH_SEND_FlagEvent(gFlags
[FLAG_BLUE
].State
, FLAG_BLUE
, 0, True, ID
);
941 if CreatePlayers
and (ID
>= 0) then NetClients
[ID
].State
:= NET_STATE_GAME
;
946 procedure MH_SEND_Info(ID
: Byte);
950 NetOut
.Write(Byte(NET_MSG_INFO
));
952 NetOut
.Write(NetClients
[ID
].Player
);
953 NetOut
.Write(ExtractFileName(gGameSettings
.WAD
));
954 NetOut
.Write(g_ExtractFileName(gMapInfo
.Map
));
955 NetOut
.Write(gWADHash
);
956 NetOut
.Write(gGameSettings
.GameMode
);
957 NetOut
.Write(gGameSettings
.ScoreLimit
);
958 NetOut
.Write(gGameSettings
.TimeLimit
);
959 NetOut
.Write(gGameSettings
.MaxLives
);
960 NetOut
.Write(LongWord(gGameSettings
.Options
));
963 g_Net_Host_Send(ID
, True);
966 procedure MH_SEND_Chat(Txt
: String; Mode
: Byte; ID
: Integer);
972 if (Mode
= NET_CHAT_TEAM
) and (not gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
973 Mode
:= NET_CHAT_PLAYER
;
976 if (Mode
= NET_CHAT_TEAM
) then
978 for i
:= Low(gPlayers
) to High(gPlayers
) do
979 if (gPlayers
[i
] <> nil) and (gPlayers
[i
].FClientID
>= 0) and
980 (gPlayers
[i
].Team
= ID
) then
982 NetOut
.Write(Byte(NET_MSG_CHAT
));
985 g_Net_Host_Send(gPlayers
[i
].FClientID
, True);
992 NetOut
.Write(Byte(NET_MSG_CHAT
));
995 g_Net_Host_Send(ID
, True);
998 if Mode
= NET_CHAT_SYSTEM
then
1001 if ID
= NET_EVERYONE
then
1003 if Mode
= NET_CHAT_PLAYER
then
1005 g_Console_Add(Txt
, True);
1006 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1007 {$IFDEF ENABLE_SOUND}
1008 g_Game_ChatSound(b_Text_Unformat(Txt
));
1012 if Mode
= NET_CHAT_TEAM
then
1013 if gPlayer1
<> nil then
1015 if (gPlayer1
.Team
= TEAM_RED
) and (Team
= TEAM_RED
) then
1017 g_Console_Add(#18'[Team] '#2 + Txt
, True);
1018 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1019 {$IFDEF ENABLE_SOUND}
1020 g_Game_ChatSound(b_Text_Unformat(Txt
));
1023 else if (gPlayer1
.Team
= TEAM_BLUE
) and (Team
= TEAM_BLUE
) then
1025 g_Console_Add(#20'[Team] '#2 + Txt
, True);
1026 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1027 {$IFDEF ENABLE_SOUND}
1028 g_Game_ChatSound(b_Text_Unformat(Txt
));
1035 Name
:= g_Net_ClientName_ByID(ID
);
1036 g_Console_Add('-> ' + Name
+ ': ' + Txt
, True);
1037 e_WriteLog('[Tell ' + Name
+ '] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1038 {$IFDEF ENABLE_SOUND}
1039 g_Game_ChatSound(b_Text_Unformat(Txt
), False);
1044 procedure MH_SEND_Effect(X
, Y
: Integer; Ang
: SmallInt
; Kind
: Byte; ID
: Integer);
1046 NetOut
.Write(Byte(NET_MSG_GFX
));
1052 g_Net_Host_Send(ID
, False);
1055 procedure MH_SEND_Sound(X
, Y
: Integer; Name
: String; Pos
: Boolean; ID
: Integer);
1057 NetOut
.Write(Byte(NET_MSG_SND
));
1061 NetOut
.Write(Byte(1));
1066 NetOut
.Write(Byte(0));
1068 g_Net_Host_Send(ID
, False);
1071 procedure MH_SEND_CreateProj(Proj
: LongInt; ID
: Integer);
1073 if (Low(Projectiles
) > Proj
) or (High(Projectiles
) < Proj
) then
1076 NetOut
.Write(Byte(NET_MSG_PJADD
));
1078 NetOut
.Write(Projectiles
[Proj
].ShotType
);
1079 NetOut
.Write(Projectiles
[Proj
].Target
);
1080 NetOut
.Write(Projectiles
[Proj
].SpawnerUID
);
1081 NetOut
.Write(Projectiles
[Proj
].Timeout
);
1082 NetOut
.Write(Projectiles
[Proj
].Obj
.X
);
1083 NetOut
.Write(Projectiles
[Proj
].Obj
.Y
);
1084 NetOut
.Write(Projectiles
[Proj
].Obj
.Vel
.X
);
1085 NetOut
.Write(Projectiles
[Proj
].Obj
.Vel
.Y
);
1087 g_Net_Host_Send(ID
, True);
1090 procedure MH_SEND_UpdateProj(Proj
: LongInt; ID
: Integer);
1092 if (Low(Projectiles
) > Proj
) or (High(Projectiles
) < Proj
) then
1095 NetOut
.Write(Byte(NET_MSG_PJPOS
));
1097 NetOut
.Write(Projectiles
[Proj
].Obj
.X
);
1098 NetOut
.Write(Projectiles
[Proj
].Obj
.Y
);
1099 NetOut
.Write(Projectiles
[Proj
].Obj
.Vel
.X
);
1100 NetOut
.Write(Projectiles
[Proj
].Obj
.Vel
.Y
);
1102 g_Net_Host_Send(ID
, False);
1105 procedure MH_SEND_DeleteProj(Proj
: LongInt; X
, Y
: LongInt; Loud
: Boolean; ID
: Integer);
1107 NetOut
.Write(Byte(NET_MSG_PJDEL
));
1109 NetOut
.Write(Byte(Loud
));
1113 g_Net_Host_Send(ID
, True);
1116 procedure MH_SEND_GameStats(ID
: Integer);
1118 NetOut
.Write(Byte(NET_MSG_SCORE
));
1119 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1121 NetOut
.Write(gTeamStat
[TEAM_RED
].Score
);
1122 NetOut
.Write(gTeamStat
[TEAM_BLUE
].Score
);
1125 if gGameSettings
.GameMode
= GM_COOP
then
1127 NetOut
.Write(gCoopMonstersKilled
);
1128 NetOut
.Write(gCoopSecretsFound
);
1131 g_Net_Host_Send(ID
, True);
1134 procedure MH_SEND_CoopStats(ID
: Integer);
1136 NetOut
.Write(Byte(NET_MSG_COOP
));
1137 NetOut
.Write(gTotalMonsters
);
1138 NetOut
.Write(gSecretsCount
);
1139 NetOut
.Write(gCoopTotalMonstersKilled
);
1140 NetOut
.Write(gCoopTotalSecretsFound
);
1141 NetOut
.Write(gCoopTotalMonsters
);
1142 NetOut
.Write(gCoopTotalSecrets
);
1145 procedure MH_SEND_GameEvent(EvType
: Byte; EvNum
: Integer; EvStr
: String; ID
: Integer);
1147 NetOut
.Write(Byte(NET_MSG_GEVENT
));
1148 NetOut
.Write(EvType
);
1149 NetOut
.Write(EvNum
);
1150 NetOut
.Write(EvStr
);
1151 NetOut
.Write(Byte(gLastMap
));
1152 NetOut
.Write(gTime
);
1153 if (EvType
= NET_EV_MAPSTART
) and isWadPath(EvStr
) then
1155 NetOut
.Write(Byte(1));
1156 NetOut
.Write(gWADHash
);
1158 NetOut
.Write(Byte(0));
1160 g_Net_Host_Send(ID
, True);
1163 procedure MH_SEND_FlagEvent(EvType
: Byte; Flag
: Byte; PID
: Word; Quiet
: Boolean; ID
: Integer);
1165 NetOut
.Write(Byte(NET_MSG_FLAG
));
1166 NetOut
.Write(EvType
);
1168 NetOut
.Write(Byte(Quiet
));
1170 NetOut
.Write(gFlags
[Flag
].State
);
1171 NetOut
.Write(gFlags
[Flag
].CaptureTime
);
1172 NetOut
.Write(gFlags
[Flag
].Obj
.X
);
1173 NetOut
.Write(gFlags
[Flag
].Obj
.Y
);
1174 NetOut
.Write(gFlags
[Flag
].Obj
.Vel
.X
);
1175 NetOut
.Write(gFlags
[Flag
].Obj
.Vel
.Y
);
1176 NetOut
.Write(Byte(gFlags
[Flag
].Direction
));
1178 g_Net_Host_Send(ID
, True);
1181 procedure MH_SEND_FlagPos(Flag
: Byte; ID
: Integer);
1183 NetOut
.Write(Byte(NET_MSG_FLAGPOS
));
1185 NetOut
.Write(gFlags
[Flag
].Obj
.X
);
1186 NetOut
.Write(gFlags
[Flag
].Obj
.Y
);
1187 NetOut
.Write(gFlags
[Flag
].Obj
.Vel
.X
);
1188 NetOut
.Write(gFlags
[Flag
].Obj
.Vel
.Y
);
1190 g_Net_Host_Send(ID
, False);
1193 procedure MH_SEND_GameSettings(ID
: Integer);
1195 NetOut
.Write(Byte(NET_MSG_GSET
));
1196 NetOut
.Write(gGameSettings
.GameMode
);
1197 NetOut
.Write(gGameSettings
.ScoreLimit
);
1198 NetOut
.Write(gGameSettings
.TimeLimit
);
1199 NetOut
.Write(gGameSettings
.MaxLives
);
1200 NetOut
.Write(LongWord(gGameSettings
.Options
));
1202 g_Net_Host_Send(ID
, True);
1207 procedure MH_SEND_PlayerCreate(PID
: Word; ID
: Integer);
1211 P
:= g_Player_Get(PID
);
1212 if P
= nil then Exit
;
1214 NetOut
.Write(Byte(NET_MSG_PLR
));
1216 NetOut
.Write(P
.Name
);
1218 NetOut
.Write(P
.FActualModelName
);
1219 NetOut
.Write(P
.FColor
.R
);
1220 NetOut
.Write(P
.FColor
.G
);
1221 NetOut
.Write(P
.FColor
.B
);
1222 NetOut
.Write(P
.Team
);
1224 g_Net_Host_Send(ID
, True);
1227 procedure MH_SEND_PlayerPos(Reliable
: Boolean; PID
: Word; ID
: Integer);
1232 Pl
:= g_Player_Get(PID
);
1233 if Pl
= nil then Exit
;
1234 if Pl
.FDummy
then Exit
;
1236 NetOut
.Write(Byte(NET_MSG_PLRPOS
));
1237 NetOut
.Write(gTime
);
1244 NetOut
.Write(FPing
);
1245 NetOut
.Write(FLoss
);
1246 if IsKeyPressed(KEY_CHAT
) then
1247 kByte
:= NET_KEY_CHAT
1250 if IsKeyPressed(KEY_LEFT
) then kByte
:= kByte
or NET_KEY_LEFT
;
1251 if IsKeyPressed(KEY_RIGHT
) then kByte
:= kByte
or NET_KEY_RIGHT
;
1252 if IsKeyPressed(KEY_UP
) then kByte
:= kByte
or NET_KEY_UP
;
1253 if IsKeyPressed(KEY_DOWN
) then kByte
:= kByte
or NET_KEY_DOWN
;
1254 if IsKeyPressed(KEY_JUMP
) then kByte
:= kByte
or NET_KEY_JUMP
;
1257 if JustTeleported
then kByte
:= kByte
or NET_KEY_FORCEDIR
;
1259 NetOut
.Write(kByte
);
1260 if Direction
= TDirection
.D_LEFT
then NetOut
.Write(Byte(0)) else NetOut
.Write(Byte(1));
1261 NetOut
.Write(GameX
);
1262 NetOut
.Write(GameY
);
1263 NetOut
.Write(GameVelX
);
1264 NetOut
.Write(GameVelY
);
1265 NetOut
.Write(GameAccelX
);
1266 NetOut
.Write(GameAccelY
);
1269 g_Net_Host_Send(ID
, Reliable
);
1272 procedure MH_SEND_PlayerStats(PID
: Word; ID
: Integer);
1277 P
:= g_Player_Get(PID
);
1278 if P
= nil then Exit
;
1280 NetOut
.Write(Byte(NET_MSG_PLRSTA
));
1285 NetOut
.Write(Byte(alive
));
1286 NetOut
.Write(Byte(GodMode
));
1287 NetOut
.Write(Health
);
1288 NetOut
.Write(Armor
);
1290 NetOut
.Write(JetFuel
);
1291 NetOut
.Write(Lives
);
1294 for I
:= WP_FIRST
to WP_LAST
do
1295 NetOut
.Write(Byte(FWeapon
[I
]));
1297 for I
:= A_BULLETS
to A_HIGH
do
1298 NetOut
.Write(FAmmo
[I
]);
1300 for I
:= A_BULLETS
to A_HIGH
do
1301 NetOut
.Write(FMaxAmmo
[I
]);
1303 for I
:= MR_SUIT
to MR_MAX
do
1304 NetOut
.Write(LongWord(FPowerups
[I
]));
1306 NetOut
.Write(Byte(R_ITEM_BACKPACK
in FInventory
));
1307 NetOut
.Write(Byte(R_KEY_RED
in FInventory
));
1308 NetOut
.Write(Byte(R_KEY_GREEN
in FInventory
));
1309 NetOut
.Write(Byte(R_KEY_BLUE
in FInventory
));
1310 NetOut
.Write(Byte(R_BERSERK
in FInventory
));
1312 NetOut
.Write(Frags
);
1313 NetOut
.Write(Death
);
1315 NetOut
.Write(CurrWeap
);
1317 NetOut
.Write(Byte(FSpectator
));
1318 NetOut
.Write(Byte(FGhost
));
1319 NetOut
.Write(Byte(FPhysics
));
1320 NetOut
.Write(Byte(FNoRespawn
));
1321 NetOut
.Write(Byte(FJetpack
));
1322 NetOut
.Write(FFireTime
);
1323 NetOut
.Write(Byte(FFlaming
));
1324 NetOut
.Write(FSpawnInvul
);
1327 g_Net_Host_Send(ID
, True);
1330 procedure MH_SEND_PlayerDamage(PID
: Word; Kind
: Byte; Attacker
, Value
: Word; VX
, VY
: Integer; ID
: Integer);
1332 NetOut
.Write(Byte(NET_MSG_PLRDMG
));
1335 NetOut
.Write(Attacker
);
1336 NetOut
.Write(Value
);
1340 g_Net_Host_Send(ID
, False);
1343 procedure MH_SEND_PlayerDeath(PID
: Word; KillType
, DeathType
: Byte; Attacker
: Word; ID
: Integer);
1345 NetOut
.Write(Byte(NET_MSG_PLRDIE
));
1347 NetOut
.Write(KillType
);
1348 NetOut
.Write(DeathType
);
1349 NetOut
.Write(Attacker
);
1351 g_Net_Host_Send(ID
, True);
1354 procedure MH_SEND_PlayerFire(PID
: Word; Weapon
: Byte; X
, Y
, AX
, AY
: Integer; ShotID
: Integer;
1357 NetOut
.Write(Byte(NET_MSG_PLRFIRE
));
1359 NetOut
.Write(Weapon
);
1364 NetOut
.Write(ShotID
);
1366 g_Net_Host_Send(ID
, True);
1369 procedure MH_SEND_PlayerDelete(PID
: Word; ID
: Integer);
1371 NetOut
.Write(Byte(NET_MSG_PLRDEL
));
1374 g_Net_Host_Send(ID
, True);
1377 procedure MH_SEND_PlayerSettings(PID
: Word; Mdl
: String; ID
: Integer);
1381 Pl
:= g_Player_Get(PID
);
1382 if Pl
= nil then Exit
;
1384 NetOut
.Write(Byte(NET_MSG_PLRSET
));
1386 NetOut
.Write(Pl
.Name
);
1388 then NetOut
.Write(Pl
.Model
.Name
)
1389 else NetOut
.Write(Mdl
);
1390 NetOut
.Write(Pl
.FColor
.R
);
1391 NetOut
.Write(Pl
.FColor
.G
);
1392 NetOut
.Write(Pl
.FColor
.B
);
1393 NetOut
.Write(Pl
.Team
);
1395 g_Net_Host_Send(ID
, True);
1400 procedure MH_SEND_ItemSpawn(Quiet
: Boolean; IID
: Word; ID
: Integer);
1405 it
:= g_Items_ByIdx(IID
);
1407 NetOut
.Write(Byte(NET_MSG_ISPAWN
));
1409 NetOut
.Write(Byte(Quiet
));
1411 if it
.dropped
then tt
:= tt
or $80;
1413 NetOut
.Write(Byte(it
.Fall
));
1414 NetOut
.Write(Byte(it
.Respawnable
));
1415 NetOut
.Write(it
.Obj
.X
);
1416 NetOut
.Write(it
.Obj
.Y
);
1417 NetOut
.Write(it
.Obj
.Vel
.X
);
1418 NetOut
.Write(it
.Obj
.Vel
.Y
);
1420 g_Net_Host_Send(ID
, True);
1423 procedure MH_SEND_ItemDestroy(Quiet
: Boolean; IID
: Word; ID
: Integer);
1425 NetOut
.Write(Byte(NET_MSG_IDEL
));
1427 NetOut
.Write(Byte(Quiet
));
1429 g_Net_Host_Send(ID
, True);
1432 procedure MH_SEND_ItemPos(IID
: Word; ID
: Integer);
1436 it
:= g_Items_ByIdx(IID
);
1438 NetOut
.Write(Byte(NET_MSG_IPOS
));
1440 NetOut
.Write(it
.Obj
.X
);
1441 NetOut
.Write(it
.Obj
.Y
);
1442 NetOut
.Write(it
.Obj
.Vel
.X
);
1443 NetOut
.Write(it
.Obj
.Vel
.Y
);
1445 g_Net_Host_Send(ID
, False);
1450 procedure MH_SEND_PanelTexture(PGUID
: Integer; AnimLoop
: Byte; ID
: Integer);
1454 TP
:= g_Map_PanelByGUID(PGUID
);
1455 if (TP
= nil) then Exit
;
1459 NetOut
.Write(Byte(NET_MSG_PTEX
));
1460 NetOut
.Write(LongWord(PGUID
));
1461 NetOut
.Write(FCurTexture
);
1462 NetOut
.Write(FCurFrame
);
1463 NetOut
.Write(FCurFrameCount
);
1464 NetOut
.Write(AnimLoop
);
1467 g_Net_Host_Send(ID
, True);
1470 procedure MH_SEND_PanelState(PGUID
: Integer; ID
: Integer);
1475 TP
:= g_Map_PanelByGUID(PGUID
);
1476 if (TP
= nil) then Exit
;
1478 NetOut
.Write(Byte(NET_MSG_PSTATE
));
1479 NetOut
.Write(LongWord(PGUID
));
1480 NetOut
.Write(Byte(TP
.Enabled
));
1481 NetOut
.Write(TP
.LiftType
);
1484 NetOut
.Write(Word(TP
.Width
));
1485 NetOut
.Write(Word(TP
.Height
));
1487 NetOut
.Write(LongInt(TP
.movingSpeedX
));
1488 NetOut
.Write(LongInt(TP
.movingSpeedY
));
1489 NetOut
.Write(LongInt(TP
.movingStartX
));
1490 NetOut
.Write(LongInt(TP
.movingStartY
));
1491 NetOut
.Write(LongInt(TP
.movingEndX
));
1492 NetOut
.Write(LongInt(TP
.movingEndY
));
1493 NetOut
.Write(LongInt(TP
.sizeSpeedX
));
1494 NetOut
.Write(LongInt(TP
.sizeSpeedY
));
1495 NetOut
.Write(LongInt(TP
.sizeEndX
));
1496 NetOut
.Write(LongInt(TP
.sizeEndY
));
1497 if TP
.movingActive
then mpflags
:= mpflags
or 1;
1498 if TP
.moveOnce
then mpflags
:= mpflags
or 2;
1499 NetOut
.Write(Byte(mpflags
));
1501 g_Net_Host_Send(ID
, True);
1506 procedure MH_SEND_TriggerSound(ClientID
: DWORD
; play
: Boolean; pos
: LongWord
; count
: Integer; ID
: Integer);
1508 if gTriggers
= nil then Exit
;
1510 NetOut
.Write(Byte(NET_MSG_TSOUND
));
1511 NetOut
.Write(ClientID
);
1512 NetOut
.Write(Byte(play
));
1513 NetOut
.Write(LongWord(pos
));
1514 NetOut
.Write(count
);
1516 g_Net_Host_Send(ID
, True);
1519 procedure MH_SEND_TriggerMusic(name
: String; play
: Boolean; pos
: LongWord
; pause
: Boolean; ID
: Integer);
1521 NetOut
.Write(Byte(NET_MSG_TMUSIC
));
1523 NetOut
.Write(Byte(play
));
1524 NetOut
.Write(LongWord(pos
));
1525 NetOut
.Write(Byte(pause
));
1527 g_Net_Host_Send(ID
, True);
1532 procedure MH_SEND_MonsterSpawn(UID
: Word; ID
: Integer);
1536 M
:= g_Monsters_ByUID(UID
);
1542 NetOut
.Write(Byte(NET_MSG_MSPAWN
));
1544 NetOut
.Write(MonsterType
);
1545 NetOut
.Write(MonsterState
);
1546 NetOut
.Write(MonsterAnim
);
1547 NetOut
.Write(MonsterTargetUID
);
1548 NetOut
.Write(MonsterTargetTime
);
1549 NetOut
.Write(MonsterBehaviour
);
1550 NetOut
.Write(MonsterSleep
);
1551 NetOut
.Write(MonsterHealth
);
1552 NetOut
.Write(MonsterAmmo
);
1553 NetOut
.Write(GameX
);
1554 NetOut
.Write(GameY
);
1555 NetOut
.Write(GameVelX
);
1556 NetOut
.Write(GameVelY
);
1557 NetOut
.Write(Byte(GameDirection
));
1560 g_Net_Host_Send(ID
, True);
1563 procedure MH_SEND_MonsterPos(UID
: Word; ID
: Integer);
1567 M
:= g_Monsters_ByUID(UID
);
1568 if M
= nil then Exit
;
1570 NetOut
.Write(Byte(NET_MSG_MPOS
));
1575 NetOut
.Write(GameX
);
1576 NetOut
.Write(GameY
);
1577 NetOut
.Write(GameVelX
);
1578 NetOut
.Write(GameVelY
);
1579 NetOut
.Write(Byte(GameDirection
));
1582 g_Net_Host_Send(ID
, False);
1585 procedure MH_SEND_MonsterState(UID
: Word; ForcedAnim
: Byte; ID
: Integer);
1589 M
:= g_Monsters_ByUID(UID
);
1590 if M
= nil then Exit
;
1592 NetOut
.Write(Byte(NET_MSG_MSTATE
));
1597 NetOut
.Write(MonsterState
);
1598 NetOut
.Write(ForcedAnim
);
1599 NetOut
.Write(MonsterTargetUID
);
1600 NetOut
.Write(MonsterTargetTime
);
1601 NetOut
.Write(MonsterSleep
);
1602 NetOut
.Write(MonsterHealth
);
1603 NetOut
.Write(MonsterAmmo
);
1604 NetOut
.Write(MonsterPain
);
1605 NetOut
.Write(Byte(AnimIsReverse
));
1606 NetOut
.Write(FFireTime
);
1609 g_Net_Host_Send(ID
, True);
1612 procedure MH_SEND_MonsterShot(UID
: Word; X
, Y
, VX
, VY
: Integer; ID
: Integer);
1614 NetOut
.Write(Byte(NET_MSG_MSHOT
));
1621 g_Net_Host_Send(ID
, True);
1624 procedure MH_SEND_MonsterDelete(UID
: Word; ID
: Integer);
1628 M
:= g_Monsters_ByUID(UID
);
1629 if M
= nil then Exit
;
1631 NetOut
.Write(Byte(NET_MSG_MDEL
));
1634 g_Net_Host_Send(ID
, True);
1639 procedure MH_SEND_TimeSync(Time
: LongWord
; ID
: Integer);
1641 NetOut
.Write(Byte(NET_MSG_TIME_SYNC
));
1644 g_Net_Host_Send(ID
, False);
1647 procedure MH_SEND_VoteEvent(EvType
: Byte; StrArg1
: String; StrArg2
: String; IntArg1
: SmallInt
;
1648 IntArg2
: SmallInt
; ID
: Integer);
1650 NetOut
.Write(Byte(NET_MSG_VOTE_EVENT
));
1651 NetOut
.Write(EvType
);
1652 NetOut
.Write(IntArg1
);
1653 NetOut
.Write(IntArg2
);
1654 NetOut
.Write(StrArg1
);
1655 NetOut
.Write(StrArg2
);
1657 g_Net_Host_Send(ID
, True);
1660 // CLIENT MESSAGES //
1664 procedure MC_RECV_Chat(var M
: TMsg
);
1669 Txt
:= M
.ReadString();
1670 Mode
:= M
.ReadByte();
1672 if Mode
<> NET_CHAT_SYSTEM
then
1674 if NetDeafLevel
= 0 then
1676 if Mode
= NET_CHAT_PLAYER
then
1678 g_Console_Add(Txt
, True);
1679 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1680 {$IFDEF ENABLE_SOUND}
1681 g_Game_ChatSound(b_Text_Unformat(Txt
));
1684 if (Mode
= NET_CHAT_TEAM
) and (gPlayer1
<> nil) then
1686 if gPlayer1
.Team
= TEAM_RED
then
1687 g_Console_Add(b_Text_Format('\r[Team] ') + Txt
, True);
1688 if gPlayer1
.Team
= TEAM_BLUE
then
1689 g_Console_Add(b_Text_Format('\b[Team] ') + Txt
, True);
1690 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), TMsgType
.Notify
);
1691 {$IFDEF ENABLE_SOUND}
1692 g_Game_ChatSound(b_Text_Unformat(Txt
));
1696 end else if (NetDeafLevel
< 2) then
1697 g_Console_Add(Txt
, True);
1700 procedure MC_RECV_Effect(var M
: TMsg
);
1708 if not gGameOn
then Exit
;
1709 Kind
:= M
.ReadByte();
1710 X
:= M
.ReadLongInt();
1711 Y
:= M
.ReadLongInt();
1712 Ang
:= M
.ReadSmallInt();
1716 g_GFX_Spark(X
, Y
, 2 + Random(2), Ang
, 0, 0);
1720 if g_Frames_Get(ID
, 'FRAMES_TELEPORT') then
1722 Anim
:= TAnimation
.Create(ID
, False, 3);
1723 g_GFX_OnceAnim(X
, Y
, Anim
);
1726 {$IFDEF ENABLE_SOUND}
1728 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X
, Y
);
1734 if g_Frames_Get(ID
, 'FRAMES_EXPLODE_ROCKET') then
1736 Anim
:= TAnimation
.Create(ID
, False, 6);
1737 Anim
.Blending
:= False;
1738 g_GFX_OnceAnim(X
-64, Y
-64, Anim
);
1741 {$IFDEF ENABLE_SOUND}
1743 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X
, Y
);
1749 if g_Frames_Get(ID
, 'FRAMES_EXPLODE_BFG') then
1751 Anim
:= TAnimation
.Create(ID
, False, 6);
1752 Anim
.Blending
:= False;
1753 g_GFX_OnceAnim(X
-64, Y
-64, Anim
);
1756 {$IFDEF ENABLE_SOUND}
1758 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X
, Y
);
1764 if g_Frames_Get(ID
, 'FRAMES_BFGHIT') then
1766 Anim
:= TAnimation
.Create(ID
, False, 4);
1767 g_GFX_OnceAnim(X
-32, Y
-32, Anim
);
1774 if g_Frames_Get(ID
, 'FRAMES_FIRE') then
1776 Anim
:= TAnimation
.Create(ID
, False, 4);
1777 g_GFX_OnceAnim(X
, Y
, Anim
);
1780 {$IFDEF ENABLE_SOUND}
1782 g_Sound_PlayExAt('SOUND_FIRE', X
, Y
);
1788 if g_Frames_Get(ID
, 'FRAMES_ITEM_RESPAWN') then
1790 Anim
:= TAnimation
.Create(ID
, False, 4);
1791 g_GFX_OnceAnim(X
, Y
, Anim
);
1794 {$IFDEF ENABLE_SOUND}
1796 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X
, Y
);
1801 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_BULLET
);
1804 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1808 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1809 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1814 procedure MC_RECV_Sound(var M
: TMsg
);
1820 Name
:= M
.ReadString();
1821 Pos
:= M
.ReadByte() <> 0;
1824 X
:= M
.ReadLongInt();
1825 Y
:= M
.ReadLongInt();
1826 {$IFDEF ENABLE_SOUND}
1827 g_Sound_PlayExAt(Name
, X
, Y
);
1832 {$IFDEF ENABLE_SOUND}
1833 g_Sound_PlayEx(Name
);
1838 procedure MC_RECV_CreateProj(var M
: TMsg
);
1840 I
, X
, Y
, XV
, YV
: Integer;
1842 Target
, Spawner
: Word;
1845 I
:= M
.ReadLongInt();
1846 ShType
:= M
.ReadByte();
1847 Target
:= M
.ReadWord();
1848 Spawner
:= M
.ReadWord();
1849 Timeout
:= M
.ReadLongWord();
1850 X
:= M
.ReadLongInt();
1851 Y
:= M
.ReadLongInt();
1852 XV
:= M
.ReadLongInt();
1853 YV
:= M
.ReadLongInt();
1855 I
:= g_Weapon_CreateProj(I
, ShType
, Spawner
, Target
, X
, Y
, XV
, YV
);
1856 if (Low(Projectiles
) <= I
) and (High(Projectiles
) >= I
) then
1858 Projectiles
[I
].Timeout
:= Timeout
;
1859 //Projectiles[I].Target := Target; // TODO: find a use for Target later
1863 procedure MC_RECV_UpdateProj(var M
: TMsg
);
1865 I
, TX
, TY
, TXV
, TYV
: Integer;
1867 I
:= M
.ReadLongInt();
1868 TX
:= M
.ReadLongInt();
1869 TY
:= M
.ReadLongInt();
1870 TXV
:= M
.ReadLongInt();
1871 TYV
:= M
.ReadLongInt();
1873 if (Low(Projectiles
) <= I
) and (High(Projectiles
) >= I
) then
1874 with (Projectiles
[i
]) do
1883 procedure MC_RECV_DeleteProj(var M
: TMsg
);
1888 if not gGameOn
then Exit
;
1889 I
:= M
.ReadLongInt();
1890 L
:= (M
.ReadByte() <> 0);
1891 X
:= M
.ReadLongInt();
1892 Y
:= M
.ReadLongInt();
1894 g_Weapon_DestroyProj(I
, X
, Y
, L
);
1897 procedure MC_RECV_GameStats(var M
: TMsg
);
1899 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1901 gTeamStat
[TEAM_RED
].Score
:= M
.ReadSmallInt();
1902 gTeamStat
[TEAM_BLUE
].Score
:= M
.ReadSmallInt();
1905 if gGameSettings
.GameMode
= GM_COOP
then
1907 gCoopMonstersKilled
:= M
.ReadWord();
1908 gCoopSecretsFound
:= M
.ReadWord();
1912 procedure MC_RECV_CoopStats(var M
: TMsg
);
1914 gTotalMonsters
:= M
.ReadLongInt();
1915 gSecretsCount
:= M
.ReadLongInt();
1916 gCoopTotalMonstersKilled
:= M
.ReadWord();
1917 gCoopTotalSecretsFound
:= M
.ReadWord();
1918 gCoopTotalMonsters
:= M
.ReadWord();
1919 gCoopTotalSecrets
:= M
.ReadWord();
1922 procedure MC_RECV_GameEvent(var M
: TMsg
);
1931 i1
, i2
: TStrings_Locale
;
1934 goodCmd
: Boolean = true;
1936 FillChar(EvHash
, Sizeof(EvHash
), 0);
1937 EvType
:= M
.ReadByte();
1938 EvNum
:= M
.ReadLongInt();
1939 EvStr
:= M
.ReadString();
1940 gLastMap
:= M
.ReadByte() <> 0;
1941 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then gStatsOff
:= True;
1942 gStatsPressed
:= True;
1943 EvTime
:= M
.ReadLongWord();
1944 BHash
:= M
.ReadByte() <> 0;
1946 EvHash
:= M
.ReadMD5();
1950 if (g_Res_received_map_start
<> 0) then
1952 if (g_Res_received_map_start
< 0) then Exit
;
1955 NET_EV_MAPSTART
: goodCmd
:= true;
1956 NET_EV_MAPEND
: goodCmd
:= true;
1957 NET_EV_PLAYER_KICK
: goodCmd
:= true;
1958 NET_EV_PLAYER_BAN
: goodCmd
:= true;
1959 NET_EV_LMS_WARMUP
: goodCmd
:= true;
1961 if not goodCmd
then Exit
;
1967 if (g_Res_received_map_start
<> 0) then
1969 g_Res_received_map_start
:= -1;
1974 g_Game_ClearLoading();
1975 {$IFDEF ENABLE_SOUND}
1976 g_Game_StopAllSounds(True);
1979 gSwitchGameMode
:= Byte(EvNum
);
1980 gGameSettings
.GameMode
:= gSwitchGameMode
;
1983 if not g_Game_StartMap(false{asMegawad}, EvStr
, True) then
1985 if not isWadPath(EvStr
) then
1986 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [gGameSettings
.WAD
+ ':\' + EvStr
]))
1988 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [EvStr
]));
1992 MC_SEND_FullStateRequest
;
1998 gLMSRespawn
:= LMS_RESPAWN_NONE
;
1999 gLMSRespawnTime
:= 0;
2000 if (g_Res_received_map_start
<> 0) then
2002 g_Res_received_map_start
:= -1;
2006 gMissionFailed
:= EvNum
<> 0;
2007 gExit
:= EXIT_ENDLEVELCUSTOM
;
2015 g_Console_Add(_lc
[I_NET_RCON_NOAUTH
], True);
2017 g_Console_Add(_lc
[I_NET_RCON_PWD_VALID
], True);
2019 g_Console_Add(_lc
[I_NET_RCON_PWD_INVALID
], True);
2025 if NetDeafLevel
< 2 then
2027 if EvNum
= TEAM_RED
then
2028 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_RED
], [EvStr
]), True);
2029 if EvNum
= TEAM_BLUE
then
2030 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_BLUE
], [EvStr
]), True);
2036 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [EvStr
]), True);
2037 if (g_Res_received_map_start
<> 0) then g_Res_received_map_start
:= -1;
2042 g_Console_Add(Format(_lc
[I_PLAYER_BAN
], [EvStr
]), True);
2043 if (g_Res_received_map_start
<> 0) then g_Res_received_map_start
:= -1;
2050 gLMSRespawn
:= LMS_RESPAWN_WARMUP
;
2051 gLMSRespawnTime
:= gTime
+ EvNum
;
2052 g_Console_Add(Format(_lc
[I_MSG_WARMUP_START
], [EvNum
div 1000]), True);
2054 else if gPlayer1
= nil then
2056 g_Console_Add(_lc
[I_PLAYER_SPECT4
], True);
2060 NET_EV_LMS_SURVIVOR
:
2061 g_Console_Add('*** ' + _lc
[I_MESSAGE_LMS_SURVIVOR
] + ' ***', True);
2064 if NetDeafLevel
< 2 then g_Game_Message(AnsiUpperCase(EvStr
), Word(EvNum
));
2068 pl
:= g_Player_Get(EvNum
and $FFFF);
2073 cnt
:= (EvNum
shr 16) and $FF;
2074 if Pos('w', EvStr
) = 0 then
2077 if Pos('t', EvStr
) = 0 then
2080 if Pos('-', EvStr
) = 0 then
2082 if Pos('e', EvStr
) = 0 then
2083 i1
:= I_PLAYER_SCORE_ADD_OWN
2085 i1
:= I_PLAYER_SCORE_ADD_ENEMY
;
2088 if Pos('e', EvStr
) = 0 then
2089 i1
:= I_PLAYER_SCORE_SUB_OWN
2091 i1
:= I_PLAYER_SCORE_SUB_ENEMY
;
2094 if Pos('r', EvStr
) > 0 then
2095 i2
:= I_PLAYER_SCORE_TO_RED
2097 i2
:= I_PLAYER_SCORE_TO_BLUE
;
2098 g_Console_Add(Format(_lc
[i1
], [pln
, cnt
, _lc
[i2
]]), True);
2102 if Pos('-', EvStr
) = 0 then
2103 i1
:= I_PLAYER_SCORE_ADD_TEAM
2105 i1
:= I_PLAYER_SCORE_SUB_TEAM
;
2107 if Pos('r', EvStr
) > 0 then
2108 i2
:= I_PLAYER_SCORE_RED
2110 i2
:= I_PLAYER_SCORE_BLUE
;
2111 g_Console_Add(Format(_lc
[i1
], [_lc
[i2
], cnt
]), True);
2116 if Pos('e', EvStr
) = 0 then
2117 i1
:= I_PLAYER_SCORE_WIN_OWN
2119 i1
:= I_PLAYER_SCORE_WIN_ENEMY
;
2121 if Pos('r', EvStr
) > 0 then
2122 i2
:= I_PLAYER_SCORE_TO_RED
2124 i2
:= I_PLAYER_SCORE_TO_BLUE
;
2125 g_Console_Add(Format(_lc
[i1
], [pln
, _lc
[i2
]]), True);
2131 if EvNum
= TEAM_RED
then
2132 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_ADD
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 108);
2133 if EvNum
= TEAM_BLUE
then
2134 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_ADD
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 108);
2135 if EvNum
= -TEAM_RED
then
2136 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_SUB
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 108);
2137 if EvNum
= -TEAM_BLUE
then
2138 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_SUB
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 108);
2143 g_Player_RemoveAllCorpses
;
2144 gLMSRespawn
:= LMS_RESPAWN_NONE
;
2145 g_Game_Message(_lc
[I_MESSAGE_LMS_START
], 144);
2149 g_Game_Message(Format(_lc
[I_MESSAGE_LMS_WIN
], [AnsiUpperCase(EvStr
)]), 144);
2153 if EvNum
= TEAM_RED
then
2154 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 144);
2155 if EvNum
= TEAM_BLUE
then
2156 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 144);
2160 g_Game_Message(_lc
[I_MESSAGE_LMS_LOSE
], 144);
2163 g_Game_Message(_lc
[I_GAME_WIN_DRAW
], 144);
2166 g_Console_Add(_lc
[I_PLAYER_SPECT4
], True);
2170 {$IFDEF ENABLE_SOUND}
2171 g_Game_Announce_KillCombo(EvNum
);
2175 NET_EV_PLAYER_TOUCH
:
2177 pl
:= g_Player_Get(EvNum
);
2184 pl
:= g_Player_Get(EvNum
);
2187 g_Console_Add(Format(_lc
[I_PLAYER_SECRET
], [pl
.Name
]), True);
2188 {$IFDEF ENABLE_SOUND}
2189 g_Sound_PlayEx('SOUND_GAME_SECRET');
2196 pl
:= g_Player_Get(EvNum
);
2197 if pl
<> nil then pl
.FReady
:= (EvStr
= 'Y');
2202 procedure MC_RECV_FlagPos(var M
: TMsg
);
2207 if Fl
= FLAG_NONE
then Exit
;
2208 gFlags
[Fl
].Obj
.X
:= M
.ReadLongInt();
2209 gFlags
[Fl
].Obj
.Y
:= M
.ReadLongInt();
2210 gFlags
[Fl
].Obj
.Vel
.X
:= M
.ReadLongInt();
2211 gFlags
[Fl
].Obj
.Vel
.Y
:= M
.ReadLongInt();
2214 procedure MC_RECV_FlagEvent(var M
: TMsg
);
2223 EvType
:= M
.ReadByte();
2226 if Fl
= FLAG_NONE
then Exit
;
2228 Quiet
:= (M
.ReadByte() <> 0);
2229 PID
:= M
.ReadWord();
2231 gFlags
[Fl
].State
:= M
.ReadByte();
2232 gFlags
[Fl
].CaptureTime
:= M
.ReadLongWord();
2233 gFlags
[Fl
].Obj
.X
:= M
.ReadLongInt();
2234 gFlags
[Fl
].Obj
.Y
:= M
.ReadLongInt();
2235 gFlags
[Fl
].Obj
.Vel
.X
:= M
.ReadLongInt();
2236 gFlags
[Fl
].Obj
.Vel
.Y
:= M
.ReadLongInt();
2237 gFlags
[Fl
].Direction
:= TDirection(M
.ReadByte());
2239 Pl
:= g_Player_Get(PID
);
2241 (EvType
<> FLAG_STATE_NORMAL
) and
2242 (EvType
<> FLAG_STATE_DROPPED
) and
2243 (EvType
<> FLAG_STATE_RETURNED
) then
2249 if Quiet
or (Pl
= nil) then Exit
;
2251 if Fl
= FLAG_RED
then
2252 s
:= _lc
[I_PLAYER_FLAG_RED
]
2254 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
2256 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_RETURN
], [AnsiUpperCase(s
)]), 144);
2258 if ((Pl
= gPlayer1
) or (Pl
= gPlayer2
)
2259 or ((gPlayer1
<> nil) and (gPlayer1
.Team
= Pl
.Team
))
2260 or ((gPlayer2
<> nil) and (gPlayer2
.Team
= Pl
.Team
))) then
2265 {$IFDEF ENABLE_SOUND}
2266 if not sound_ret_flag
[a
].IsPlaying() then
2267 sound_ret_flag
[a
].Play();
2271 FLAG_STATE_CAPTURED
:
2273 if (Pl
<> nil) then Pl
.SetFlag(Fl
);
2277 if Fl
= FLAG_RED
then
2278 s
:= _lc
[I_PLAYER_FLAG_RED
]
2280 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
2282 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_GET
], [Pl
.Name
, s
]), True);
2283 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_GET
], [AnsiUpperCase(s
)]), 144);
2285 {$IFDEF ENABLE_SOUND}
2286 if ((Pl
= gPlayer1
) or (Pl
= gPlayer2
)
2287 or ((gPlayer1
<> nil) and (gPlayer1
.Team
= Pl
.Team
))
2288 or ((gPlayer2
<> nil) and (gPlayer2
.Team
= Pl
.Team
))) then
2293 if not sound_get_flag
[a
].IsPlaying() then
2294 sound_get_flag
[a
].Play();
2300 if (Pl
<> nil) then Pl
.SetFlag(FLAG_NONE
);
2302 if Quiet
or (Pl
= nil) then Exit
;
2304 if Fl
= FLAG_RED
then
2305 s
:= _lc
[I_PLAYER_FLAG_RED
]
2307 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
2309 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_DROP
], [Pl
.Name
, s
]), True);
2310 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_DROP
], [AnsiUpperCase(s
)]), 144);
2312 {$IFDEF ENABLE_SOUND}
2313 if ((Pl
= gPlayer1
) or (Pl
= gPlayer2
)
2314 or ((gPlayer1
<> nil) and (gPlayer1
.Team
= Pl
.Team
))
2315 or ((gPlayer2
<> nil) and (gPlayer2
.Team
= Pl
.Team
))) then
2320 if not sound_lost_flag
[a
].IsPlaying() then
2321 sound_lost_flag
[a
].Play();
2327 g_Map_ResetFlag(FLAG_RED
);
2328 g_Map_ResetFlag(FLAG_BLUE
);
2329 if Quiet
or (Pl
= nil) then Exit
;
2330 Pl
.SetFlag(FLAG_NONE
);
2332 if Fl
= FLAG_RED
then
2333 s
:= _lc
[I_PLAYER_FLAG_RED
]
2335 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
2337 ts
:= Format('%.4d', [gFlags
[Fl
].CaptureTime
]);
2338 Insert('.', ts
, Length(ts
) + 1 - 3);
2339 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_CAPTURE
], [Pl
.Name
, s
, ts
]), True);
2340 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_CAPTURE
], [AnsiUpperCase(s
)]), 144);
2342 {$IFDEF ENABLE_SOUND}
2343 if ((Pl
= gPlayer1
) or (Pl
= gPlayer2
)
2344 or ((gPlayer1
<> nil) and (gPlayer1
.Team
= Pl
.Team
))
2345 or ((gPlayer2
<> nil) and (gPlayer2
.Team
= Pl
.Team
))) then
2350 if not sound_cap_flag
[a
].IsPlaying() then
2351 sound_cap_flag
[a
].Play();
2355 FLAG_STATE_RETURNED
:
2357 g_Map_ResetFlag(Fl
);
2360 if Fl
= FLAG_RED
then
2361 s
:= _lc
[I_PLAYER_FLAG_RED
]
2363 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
2365 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_RETURN
], [AnsiUpperCase(s
)]), 144);
2367 {$IFDEF ENABLE_SOUND}
2368 if ((Pl
= gPlayer1
) or (Pl
= gPlayer2
)
2369 or ((gPlayer1
<> nil) and (gPlayer1
.Team
= Pl
.Team
))
2370 or ((gPlayer2
<> nil) and (gPlayer2
.Team
= Pl
.Team
))) then
2375 if not sound_ret_flag
[a
].IsPlaying() then
2376 sound_ret_flag
[a
].Play();
2382 procedure MC_RECV_GameSettings(var M
: TMsg
);
2384 gGameSettings
.GameMode
:= M
.ReadByte();
2385 gGameSettings
.ScoreLimit
:= M
.ReadWord();
2386 gGameSettings
.TimeLimit
:= M
.ReadWord();
2387 gGameSettings
.MaxLives
:= M
.ReadByte();
2388 gGameSettings
.Options
:= TGameOptions(M
.ReadLongWord());
2393 function MC_RECV_PlayerCreate(var M
: TMsg
): Word;
2396 PName
, Model
: String;
2401 PID
:= M
.ReadWord();
2402 Pl
:= g_Player_Get(PID
);
2404 PName
:= M
.ReadString();
2405 Model
:= M
.ReadString();
2406 Color
.R
:= M
.ReadByte();
2407 Color
.G
:= M
.ReadByte();
2408 Color
.B
:= M
.ReadByte();
2412 if (PID
<> NetPlrUID1
) and (PID
<> NetPlrUID2
) then
2414 if (Pl
<> nil) then Exit
;
2415 if (g_Force_Model_Get() <> 0) then
2416 Model
:= g_Forced_Model_GetName();
2417 DID
:= g_Player_Create(Model
, Color
, T
, False);
2418 with g_Player_Get(DID
) do
2427 if (PID
= NetPlrUID1
) and (gPlayer1
<> nil) then begin
2428 gPlayer1
.UID
:= PID
;
2429 gPlayer1
.Model
.SetColor(Color
.R
, Color
.G
, Color
.B
);
2430 gPlayer1
.ChangeTeam(T
);
2432 if (PID
= NetPlrUID2
) and (gPlayer2
<> nil) then begin
2433 gPlayer2
.UID
:= PID
;
2434 gPlayer2
.Model
.SetColor(Color
.R
, Color
.G
, Color
.B
);
2435 gPlayer2
.ChangeTeam(T
);
2439 if NetDeafLevel
< 3 then
2440 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [PName
]), True);
2441 e_WriteLog('NET: Player ' + PName
+ ' [' + IntToStr(PID
) + '] added.', TMsgType
.Notify
);
2445 function MC_RECV_PlayerPos(var M
: TMsg
): Word;
2452 TmpX
, TmpY
: Integer;
2456 GT
:= M
.ReadLongWord();
2457 if GT
< gTime
- NET_MAX_DIFFTIME
then
2464 PID
:= M
.ReadWord();
2465 Pl
:= g_Player_Get(PID
);
2467 if Pl
= nil then Exit
;
2473 FPing
:= M
.ReadWord();
2474 FLoss
:= M
.ReadByte();
2475 kByte
:= M
.ReadWord();
2476 Dir
:= M
.ReadByte();
2478 TmpX
:= M
.ReadLongInt();
2479 TmpY
:= M
.ReadLongInt();
2483 if LongBool(kByte
and NET_KEY_CHAT
) then
2484 PressKey(KEY_CHAT
, 10000)
2487 if LongBool(kByte
and NET_KEY_LEFT
) then PressKey(KEY_LEFT
, 10000);
2488 if LongBool(kByte
and NET_KEY_RIGHT
) then PressKey(KEY_RIGHT
, 10000);
2489 if LongBool(kByte
and NET_KEY_UP
) then PressKey(KEY_UP
, 10000);
2490 if LongBool(kByte
and NET_KEY_DOWN
) then PressKey(KEY_DOWN
, 10000);
2491 if LongBool(kByte
and NET_KEY_JUMP
) then PressKey(KEY_JUMP
, 10000);
2494 JustTeleported
:= LongBool(kByte
and NET_KEY_FORCEDIR
);
2496 if ((Pl
<> gPlayer1
) and (Pl
<> gPlayer2
)) or JustTeleported
then
2497 SetDirection(TDirection(Dir
));
2499 GameVelX
:= M
.ReadLongInt();
2500 GameVelY
:= M
.ReadLongInt();
2501 GameAccelX
:= M
.ReadLongInt();
2502 GameAccelY
:= M
.ReadLongInt();
2503 SetLerp(TmpX
, TmpY
);
2504 if NetForcePlayerUpdate
then Update();
2508 function MC_RECV_PlayerStats(var M
: TMsg
): Word;
2512 I
, OldFire
: Integer;
2513 OldJet
, Flam
: Boolean;
2516 PID
:= M
.ReadWord();
2517 Pl
:= g_Player_Get(PID
);
2524 alive
:= (M
.ReadByte() <> 0);
2525 GodMode
:= (M
.ReadByte() <> 0);
2526 Health
:= M
.ReadLongInt();
2527 Armor
:= M
.ReadLongInt();
2528 Air
:= M
.ReadLongInt();
2529 JetFuel
:= M
.ReadLongInt();
2530 Lives
:= M
.ReadByte();
2531 NewTeam
:= M
.ReadByte();
2533 for I
:= WP_FIRST
to WP_LAST
do
2534 FWeapon
[I
] := (M
.ReadByte() <> 0);
2536 for I
:= A_BULLETS
to A_HIGH
do
2537 FAmmo
[I
] := M
.ReadWord();
2539 for I
:= A_BULLETS
to A_HIGH
do
2540 FMaxAmmo
[I
] := M
.ReadWord();
2542 for I
:= MR_SUIT
to MR_MAX
do
2543 FPowerups
[I
] := M
.ReadLongWord();
2546 if (M
.ReadByte() <> 0) then FInventory
+= [R_ITEM_BACKPACK
];
2547 if (M
.ReadByte() <> 0) then FInventory
+= [R_KEY_RED
];
2548 if (M
.ReadByte() <> 0) then FInventory
+= [R_KEY_GREEN
];
2549 if (M
.ReadByte() <> 0) then FInventory
+= [R_KEY_BLUE
];
2550 if (M
.ReadByte() <> 0) then FInventory
+= [R_BERSERK
];
2552 Frags
:= M
.ReadLongInt();
2553 Death
:= M
.ReadLongInt();
2555 SetWeapon(M
.ReadByte());
2557 FSpectator
:= M
.ReadByte() <> 0;
2560 if UID
= NetPlrUID1
then
2562 gSpectLatchPID1
:= UID
;
2565 if UID
= NetPlrUID2
then
2567 gSpectLatchPID2
:= UID
;
2573 if (gPlayer1
= nil) and (gSpectLatchPID1
> 0) and (UID
= gSpectLatchPID1
) then
2576 gSpectLatchPID1
:= 0;
2578 if (gPlayer2
= nil) and (gSpectLatchPID2
> 0) and (UID
= gSpectLatchPID2
) then
2581 gSpectLatchPID2
:= 0;
2584 FGhost
:= M
.ReadByte() <> 0;
2585 FPhysics
:= M
.ReadByte() <> 0;
2586 FNoRespawn
:= M
.ReadByte() <> 0;
2588 FJetpack
:= M
.ReadByte() <> 0;
2589 OldFire
:= FFireTime
;
2590 FFireTime
:= M
.ReadLongInt();
2591 {$IFDEF ENABLE_SOUND}
2592 if (OldFire
<= 0) and (FFireTime
> 0) then
2593 g_Sound_PlayExAt('SOUND_IGNITE', Obj
.X
, Obj
.Y
);
2595 Flam
:= M
.ReadByte() <> 0;
2596 FSpawnInvul
:= M
.ReadLongInt();
2597 if OldJet
and not FJetpack
then
2599 else if not OldJet
and FJetpack
then
2601 if FFlaming
and not Flam
then
2603 if Team
<> NewTeam
then
2604 Pl
.ChangeTeam(NewTeam
);
2610 function MC_RECV_PlayerDamage(var M
: TMsg
): Word;
2615 Attacker
, Value
: Word;
2619 if not gGameOn
then Exit
;
2620 PID
:= M
.ReadWord();
2621 Pl
:= g_Player_Get(PID
);
2622 if Pl
= nil then Exit
;
2624 Kind
:= M
.ReadByte();
2625 Attacker
:= M
.ReadWord();
2626 Value
:= M
.ReadWord();
2631 Damage(Value
, Attacker
, VX
, VY
, Kind
);
2636 function MC_RECV_PlayerDeath(var M
: TMsg
): Word;
2640 KillType
, DeathType
: Byte;
2644 if not gGameOn
then Exit
;
2645 PID
:= M
.ReadWord();
2646 Pl
:= g_Player_Get(PID
);
2647 if Pl
= nil then Exit
;
2649 KillType
:= M
.ReadByte();
2650 DeathType
:= M
.ReadByte();
2651 Attacker
:= M
.ReadWord();
2655 Kill(KillType
, Attacker
, DeathType
);
2660 function MC_RECV_PlayerDelete(var M
: TMsg
): Word;
2665 PID
:= M
.ReadWord();
2666 Pl
:= g_Player_Get(PID
);
2668 if Pl
= nil then Exit
;
2670 if NetDeafLevel
< 3 then
2671 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
2672 e_WriteLog('NET: Player ' + Pl
.Name
+ ' [' + IntToStr(PID
) + '] removed.', TMsgType
.Notify
);
2674 g_Player_Remove(PID
);
2679 function MC_RECV_PlayerFire(var M
: TMsg
): Word;
2684 X
, Y
, AX
, AY
: Integer;
2688 if not gGameOn
then Exit
;
2689 PID
:= M
.ReadWord();
2690 Pl
:= g_Player_Get(PID
);
2691 if Pl
= nil then Exit
;
2693 Weap
:= M
.ReadByte();
2694 X
:= M
.ReadLongInt();
2695 Y
:= M
.ReadLongInt();
2696 AX
:= M
.ReadLongInt();
2697 AY
:= M
.ReadLongInt();
2698 SHID
:= M
.ReadLongInt();
2701 if alive
then NetFire(Weap
, X
, Y
, AX
, AY
, SHID
);
2704 procedure MC_RECV_PlayerSettings(var M
: TMsg
);
2714 PID
:= M
.ReadWord();
2715 Pl
:= g_Player_Get(PID
);
2716 if Pl
= nil then Exit
;
2718 TmpName
:= M
.ReadString();
2719 TmpModel
:= M
.ReadString();
2720 TmpColor
.R
:= M
.ReadByte();
2721 TmpColor
.G
:= M
.ReadByte();
2722 TmpColor
.B
:= M
.ReadByte();
2723 TmpTeam
:= M
.ReadByte();
2725 if (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) and (Pl
.Team
<> TmpTeam
) then
2727 Pl
.ChangeTeam(TmpTeam
);
2728 if gPlayer1
= Pl
then
2729 gPlayer1Settings
.Team
:= TmpTeam
;
2730 if gPlayer2
= Pl
then
2731 gPlayer2Settings
.Team
:= TmpTeam
;
2733 Pl
.SetColor(TmpColor
);
2735 if Pl
.Name
<> TmpName
then
2737 if NetDeafLevel
< 3 then
2738 g_Console_Add(Format(_lc
[I_PLAYER_NAME
], [Pl
.Name
, TmpName
]), True);
2742 if (g_Force_Model_Get() <> 0) then
2743 TmpModel
:= g_Forced_Model_GetName();
2744 if TmpModel
<> Pl
.Model
.Name
then
2745 Pl
.SetModel(TmpModel
);
2750 procedure MC_RECV_ItemSpawn(var M
: TMsg
);
2754 X
, Y
, VX
, VY
: Integer;
2756 Quiet
, Fall
{, Resp}: Boolean;
2760 if not gGameOn
then Exit
;
2762 Quiet
:= M
.ReadByte() <> 0;
2764 Fall
:= M
.ReadByte() <> 0;
2765 {Resp :=} M
.ReadByte();
2766 X
:= M
.ReadLongInt();
2767 Y
:= M
.ReadLongInt();
2768 VX
:= M
.ReadLongInt();
2769 VY
:= M
.ReadLongInt();
2771 g_Items_Create(X
, Y
, T
and $7F, Fall
, False, False, ID
);
2772 if ((T
and $80) <> 0) then g_Items_SetDrop(ID
);
2774 it
:= g_Items_ByIdx(ID
);
2780 {$IFDEF ENABLE_SOUND}
2781 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X
, Y
);
2783 if g_Frames_Get(AID
, 'FRAMES_ITEM_RESPAWN') then
2785 Anim
:= TAnimation
.Create(AID
, False, 4);
2786 g_GFX_OnceAnim(X
+(it
.Obj
.Rect
.Width
div 2)-16, Y
+(it
.Obj
.Rect
.Height
div 2)-16, Anim
);
2792 procedure MC_RECV_ItemDestroy(var M
: TMsg
);
2801 Quiet
:= M
.ReadByte() <> 0;
2803 if not g_Items_ValidId(ID
) then
2806 {$IFDEF ENABLE_SOUND}
2808 g_Items_EmitPickupSound(ID
);
2814 procedure MC_RECV_ItemPos(var M
: TMsg
);
2817 X
, Y
, VX
, VY
: Integer;
2820 if not gGameOn
then Exit
;
2823 X
:= M
.ReadLongInt();
2824 Y
:= M
.ReadLongInt();
2825 VX
:= M
.ReadLongInt();
2826 VY
:= M
.ReadLongInt();
2828 if g_Items_ValidId(ID
) then
2830 it
:= g_Items_ByIdx(ID
);
2835 it
.positionChanged();
2841 procedure MC_RECV_PanelTexture(var M
: TMsg
);
2848 if not gGameOn
then Exit
;
2850 PGUID
:= Integer(M
.ReadLongWord());
2851 Tex
:= M
.ReadLongInt();
2852 Fr
:= M
.ReadLongInt();
2853 Cnt
:= M
.ReadByte();
2854 Loop
:= M
.ReadByte();
2856 TP
:= g_Map_PanelByGUID(PGUID
);
2860 TP
.SetTexture(Tex
, Loop
);
2861 TP
.SetFrame(Fr
, Cnt
);
2865 procedure MC_RECV_PanelState(var M
: TMsg
);
2870 X
, Y
, W
, H
: Integer;
2872 speedX
, speedY
, startX
, startY
, endX
, endY
: Integer;
2873 sizeSpX
, sizeSpY
, sizeEX
, sizeEY
: Integer;
2876 if not gGameOn
then Exit
;
2878 PGUID
:= Integer(M
.ReadLongWord());
2879 E
:= (M
.ReadByte() <> 0);
2880 Lift
:= M
.ReadByte();
2881 X
:= M
.ReadLongInt();
2882 Y
:= M
.ReadLongInt();
2886 speedX
:= M
.ReadLongInt();
2887 speedY
:= M
.ReadLongInt();
2888 startX
:= M
.ReadLongInt();
2889 startY
:= M
.ReadLongInt();
2890 endX
:= M
.ReadLongInt();
2891 endY
:= M
.ReadLongInt();
2892 sizeSpX
:= M
.ReadLongInt();
2893 sizeSpY
:= M
.ReadLongInt();
2894 sizeEX
:= M
.ReadLongInt();
2895 sizeEY
:= M
.ReadLongInt();
2896 mpflags
:= M
.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
2898 TP
:= g_Map_PanelByGUID(PGUID
);
2899 if (TP
= nil) then Exit
;
2901 // update lifts state
2902 if TP
.isGLift
then g_Map_SetLiftGUID(PGUID
, Lift
);
2904 // update enabled/disabled state for all panels
2905 if E
then g_Map_EnableWallGUID(PGUID
) else g_Map_DisableWallGUID(PGUID
);
2907 // update panel position, as it can be moved (mplat)
2912 // update mplat state
2913 TP
.movingSpeedX
:= speedX
;
2914 TP
.movingSpeedY
:= speedY
;
2915 TP
.movingStartX
:= startX
;
2916 TP
.movingStartY
:= startY
;
2917 TP
.movingEndX
:= endX
;
2918 TP
.movingEndY
:= endY
;
2919 TP
.sizeSpeedX
:= sizeSpX
;
2920 TP
.sizeSpeedY
:= sizeSpY
;
2921 TP
.sizeEndX
:= sizeEX
;
2922 TP
.sizeEndY
:= sizeEY
;
2923 TP
.movingActive
:= ((mpflags
and 1) <> 0);
2924 TP
.moveOnce
:= ((mpflags
and 2) <> 0);
2925 // notify panel of it's position/size change, so it can fix other internal structures
2926 TP
.positionChanged();
2931 procedure MC_RECV_TriggerSound(var M
: TMsg
);
2934 SPos
, SID
: LongWord
;
2938 if not gGameOn
then Exit
;
2939 if gTriggers
= nil then Exit
;
2941 SID
:= M
.ReadLongWord();
2942 SPlaying
:= M
.ReadByte() <> 0;
2943 SPos
:= M
.ReadLongWord();
2944 SCount
:= M
.ReadLongInt();
2946 for I
:= Low(gTriggers
) to High(gTriggers
) do
2947 if gTriggers
[I
].TriggerType
= TRIGGER_SOUND
then
2948 if gTriggers
[I
].ClientID
= SID
then
2949 with gTriggers
[I
] do
2951 {$IFDEF ENABLE_SOUND}
2952 if Sound
<> nil then
2957 Sound
.PlayVolumeAt(X
+(Width
div 2), Y
+(Height
div 2), tgcVolume
/255.0)
2959 Sound
.PlayPanVolume((tgcPan
-127.0)/128.0, tgcVolume
/255.0);
2960 Sound
.SetPosition(SPos
);
2962 else if Sound
.IsPlaying
then
2971 else if SoundPlay
then
2978 SoundPlayCount
:= SCount
;
2982 procedure MC_RECV_TriggerMusic(var M
: TMsg
);
2989 if not gGameOn
then Exit
;
2991 MName
:= M
.ReadString();
2992 MPlaying
:= M
.ReadByte() <> 0;
2993 MPos
:= M
.ReadLongWord();
2994 MPaused
:= M
.ReadByte() <> 0;
2995 MPos
:= MPos
+1; //k8: stfu, fpc!
2997 {$IFDEF ENABLE_SOUND}
3000 gMusic
.SetByName(MName
);
3002 gMusic
.SetPosition(MPos
);
3003 gMusic
.SpecPause
:= MPaused
;
3005 else if gMusic
.IsPlaying
then
3012 gMusicName
:= MName
;
3015 gMusicPause
:= MPaused
;
3017 else if gMusicPlay
then
3019 gMusicPlay
:= False;
3027 procedure MC_RECV_MonsterSpawn(var M
: TMsg
);
3030 MType
, MState
, MDir
, MAnim
, MBehav
: Byte;
3031 X
, Y
, VX
, VY
, MTargTime
, MHealth
, MAmmo
, MSleep
: Integer;
3036 Mon
:= g_Monsters_ByUID(ID
);
3040 MType
:= M
.ReadByte();
3041 MState
:= M
.ReadByte();
3042 MAnim
:= M
.ReadByte();
3043 MTarg
:= M
.ReadWord();
3044 MTargTime
:= M
.ReadLongInt();
3045 MBehav
:= M
.ReadByte();
3046 MSleep
:= M
.ReadLongInt();
3047 MHealth
:= M
.ReadLongInt();
3048 MAmmo
:= M
.ReadLongInt();
3050 X
:= M
.ReadLongInt();
3051 Y
:= M
.ReadLongInt();
3052 VX
:= M
.ReadLongInt();
3053 VY
:= M
.ReadLongInt();
3054 MDir
:= M
.ReadByte();
3056 g_Monsters_Create(MType
, X
, Y
, TDirection(MDir
), False, ID
);
3057 Mon
:= g_Monsters_ByUID(ID
);
3064 MonsterAnim
:= MAnim
;
3065 MonsterTargetUID
:= MTarg
;
3066 MonsterTargetTime
:= MTargTime
;
3067 MonsterBehaviour
:= MBehav
;
3068 MonsterSleep
:= MSleep
;
3069 MonsterAmmo
:= MAmmo
;
3074 setPosition(X
, Y
); // this will call positionChanged();
3080 procedure MC_RECV_MonsterPos(var M
: TMsg
);
3087 Mon
:= g_Monsters_ByUID(ID
);
3093 X
:= M
.ReadLongInt();
3094 Y
:= M
.ReadLongInt();
3095 Mon
.setPosition(X
, Y
); // this will call `positionChanged()`
3096 GameVelX
:= M
.ReadLongInt();
3097 GameVelY
:= M
.ReadLongInt();
3098 GameDirection
:= TDirection(M
.ReadByte());
3102 procedure MC_RECV_MonsterState(var M
: TMsg
);
3104 ID
, OldFire
: Integer;
3105 MState
, MFAnm
: Byte;
3107 AnimRevert
: Boolean;
3110 Mon
:= g_Monsters_ByUID(ID
);
3111 if Mon
= nil then Exit
;
3113 MState
:= M
.ReadByte();
3114 MFAnm
:= M
.ReadByte();
3118 MonsterTargetUID
:= M
.ReadWord();
3119 MonsterTargetTime
:= M
.ReadLongInt();
3120 MonsterSleep
:= M
.ReadLongInt();
3121 MonsterHealth
:= M
.ReadLongInt();
3122 MonsterAmmo
:= M
.ReadLongInt();
3123 MonsterPain
:= M
.ReadLongInt();
3124 AnimRevert
:= M
.ReadByte() <> 0;
3125 OldFire
:= FFireTime
;
3126 FFireTime
:= M
.ReadLongInt();
3127 {$IFDEF ENABLE_SOUND}
3128 if (OldFire
<= 0) and (FFireTime
> 0) then
3129 g_Sound_PlayExAt('SOUND_IGNITE', Obj
.X
, Obj
.Y
);
3131 RevertAnim(AnimRevert
);
3133 if MonsterState
<> MState
then
3135 if (MState
= MONSTATE_GO
) and (MonsterState
= MONSTATE_SLEEP
) then WakeUpSound();
3136 if (MState
= MONSTATE_DIE
) then DieSound();
3137 if (MState
= MONSTATE_PAIN
) then MakeBloodSimple(Min(200, MonsterPain
));
3138 if (MState
= MONSTATE_ATTACK
) then kick(nil);
3139 if (MState
= MONSTATE_DEAD
) then SetDeadAnim();
3141 SetState(MState
, MFAnm
);
3146 procedure MC_RECV_MonsterShot(var M
: TMsg
);
3150 X
, Y
, VX
, VY
: Integer;
3154 Mon
:= g_Monsters_ByUID(ID
);
3155 if Mon
= nil then Exit
;
3157 X
:= M
.ReadLongInt();
3158 Y
:= M
.ReadLongInt();
3159 VX
:= M
.ReadLongInt();
3160 VY
:= M
.ReadLongInt();
3162 Mon
.ClientAttack(X
, Y
, VX
, VY
);
3165 procedure MC_RECV_MonsterDelete(var M
: TMsg
);
3171 Mon
:= g_Monsters_ByUID(ID
);
3172 if Mon
= nil then Exit
;
3174 Mon
.MonsterRemoved
:= True;
3177 procedure MC_RECV_TimeSync(var M
: TMsg
);
3181 Time
:= M
.ReadLongWord();
3183 if gState
= STATE_INTERCUSTOM
then
3184 gServInterTime
:= Min(Time
, 255);
3187 procedure MC_RECV_VoteEvent(var M
: TMsg
);
3191 Int1
, Int2
: SmallInt
;
3193 EvID
:= M
.ReadByte();
3194 Int1
:= M
.ReadSmallInt();
3195 Int2
:= M
.ReadSmallInt();
3196 Str1
:= M
.ReadString();
3197 Str2
:= M
.ReadString();
3199 if NetDeafLevel
< 2 then
3202 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_STARTED
], [Str1
, Str2
, Int1
]), True);
3204 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [Str1
]), True);
3206 g_Console_Add(_lc
[I_MESSAGE_VOTE_FAILED
], True);
3208 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [Str1
, Int1
, Int2
]), True);
3210 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_INPROGRESS
], [Str1
]), True);
3216 procedure MC_SEND_Info(Password
: String);
3221 NetOut
.Write(Byte(NET_MSG_INFO
));
3222 NetOut
.Write(GAME_VERSION
);
3223 NetOut
.Write(Password
);
3224 NetOut
.Write(gPlayer1Settings
.Name
);
3225 NetOut
.Write(gPlayer1Settings
.Model
);
3226 NetOut
.Write(gPlayer1Settings
.Color
.R
);
3227 NetOut
.Write(gPlayer1Settings
.Color
.G
);
3228 NetOut
.Write(gPlayer1Settings
.Color
.B
);
3229 NetOut
.Write(gPlayer1Settings
.Team
);
3230 NetOut
.Write(gPlayer1Settings
.WeaponSwitch
);
3231 for i
:= WP_FIRST
to WP_LAST
+ 1 do
3232 NetOut
.Write(gPlayer1Settings
.WeaponPreferences
[i
]);
3233 NetOut
.Write(gPlayer1Settings
.SwitchToEmpty
);
3234 NetOut
.Write(gPlayer1Settings
.SkipIronFist
);
3236 g_Net_Client_Send(True);
3239 procedure MC_SEND_Chat(Txt
: String; Mode
: Byte);
3241 NetOut
.Write(Byte(NET_MSG_CHAT
));
3245 g_Net_Client_Send(True);
3248 procedure MC_SEND_PlayerPos();
3253 WeaponAct
: Byte = 0;
3254 WeaponSelect
: Word = 0;
3257 if not gGameOn
then Exit
;
3258 if gPlayers
= nil then Exit
;
3259 if gPlayer1
= nil then Exit
;
3262 Predict
:= NetPredictSelf
; // and (not NetGotKeys);
3264 if (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
3266 strafeDir
:= P1MoveButton
shr 4;
3267 P1MoveButton
:= P1MoveButton
and $0F;
3269 if gPlayerAction
[0, ACTION_MOVELEFT
] and (not gPlayerAction
[0, ACTION_MOVERIGHT
]) then
3271 else if (not gPlayerAction
[0, ACTION_MOVELEFT
]) and gPlayerAction
[0, ACTION_MOVERIGHT
] then
3273 else if (not gPlayerAction
[0, ACTION_MOVELEFT
]) and (not gPlayerAction
[0, ACTION_MOVERIGHT
]) then
3277 if gPlayerAction
[0, ACTION_STRAFE
] then
3279 // new strafe mechanics
3280 if (strafeDir
= 0) then
3281 strafeDir
:= P1MoveButton
; // start strafing
3282 // now set direction according to strafe (reversed)
3283 if (strafeDir
= 2) then
3284 gPlayer1
.SetDirection(TDirection
.D_LEFT
)
3285 else if (strafeDir
= 1) then
3286 gPlayer1
.SetDirection(TDirection
.D_RIGHT
)
3290 strafeDir
:= 0; // not strafing anymore
3291 if (P1MoveButton
= 2) and gPlayerAction
[0, ACTION_MOVELEFT
] then
3292 gPlayer1
.SetDirection(TDirection
.D_LEFT
)
3293 else if (P1MoveButton
= 1) and gPlayerAction
[0, ACTION_MOVERIGHT
] then
3294 gPlayer1
.SetDirection(TDirection
.D_RIGHT
)
3295 else if P1MoveButton
<> 0 then
3296 gPlayer1
.SetDirection(TDirection(P1MoveButton
-1));
3299 gPlayer1
.ReleaseKeys
;
3300 if P1MoveButton
= 1 then
3302 kByte
:= kByte
or NET_KEY_LEFT
;
3303 if Predict
then gPlayer1
.PressKey(KEY_LEFT
, 10000);
3305 if P1MoveButton
= 2 then
3307 kByte
:= kByte
or NET_KEY_RIGHT
;
3308 if Predict
then gPlayer1
.PressKey(KEY_RIGHT
, 10000);
3310 if gPlayerAction
[0, ACTION_LOOKUP
] then
3312 kByte
:= kByte
or NET_KEY_UP
;
3313 gPlayer1
.PressKey(KEY_UP
, 10000);
3315 if gPlayerAction
[0, ACTION_LOOKDOWN
] then
3317 kByte
:= kByte
or NET_KEY_DOWN
;
3318 gPlayer1
.PressKey(KEY_DOWN
, 10000);
3320 if gPlayerAction
[0, ACTION_JUMP
] then
3322 kByte
:= kByte
or NET_KEY_JUMP
;
3323 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
3325 if gPlayerAction
[0, ACTION_ATTACK
] then kByte
:= kByte
or NET_KEY_FIRE
;
3326 if gPlayerAction
[0, ACTION_ACTIVATE
] then kByte
:= kByte
or NET_KEY_OPEN
;
3328 for i
:= WP_FACT
to WP_LACT
do
3330 if gWeaponAction
[0, i
] then
3332 WeaponAct
:= WeaponAct
or Byte(1 shl i
);
3333 gWeaponAction
[0, i
] := False
3337 for i
:= WP_FIRST
to WP_LAST
do
3339 if gSelectWeapon
[0, i
] then
3341 WeaponSelect
:= WeaponSelect
or Word(1 shl i
);
3342 gSelectWeapon
[0, i
] := False
3346 // fix movebutton state
3347 P1MoveButton
:= P1MoveButton
or (strafeDir
shl 4);
3350 kByte
:= NET_KEY_CHAT
;
3352 NetOut
.Write(Byte(NET_MSG_PLRPOS
));
3353 NetOut
.Write(gTime
);
3354 NetOut
.Write(kByte
);
3355 NetOut
.Write(Byte(gPlayer1
.Direction
));
3356 NetOut
.Write(WeaponAct
);
3357 NetOut
.Write(WeaponSelect
);
3358 //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
3359 g_Net_Client_Send(True);
3361 //kBytePrev := kByte;
3362 //kDirPrev := gPlayer1.Direction;
3365 procedure MC_SEND_Vote(Start
: Boolean; Command
: String);
3367 NetOut
.Write(Byte(NET_MSG_VOTE_EVENT
));
3368 NetOut
.Write(Byte(Start
));
3369 NetOut
.Write(Command
);
3370 g_Net_Client_Send(True);
3373 procedure MC_SEND_PlayerSettings();
3376 NetOut
.Write(Byte(NET_MSG_PLRSET
));
3377 NetOut
.Write(gPlayer1Settings
.Name
);
3378 NetOut
.Write(gPlayer1Settings
.Model
);
3379 NetOut
.Write(gPlayer1Settings
.Color
.R
);
3380 NetOut
.Write(gPlayer1Settings
.Color
.G
);
3381 NetOut
.Write(gPlayer1Settings
.Color
.B
);
3382 NetOut
.Write(gPlayer1Settings
.Team
);
3383 NetOut
.Write(gPlayer1Settings
.WeaponSwitch
);
3384 for i
:= WP_FIRST
to WP_LAST
+ 1 do
3385 NetOut
.Write(gPlayer1Settings
.WeaponPreferences
[i
]);
3386 NetOut
.Write(gPlayer1Settings
.SwitchToEmpty
);
3387 NetOut
.Write(gPlayer1Settings
.SkipIronFist
);
3389 g_Net_Client_Send(True);
3392 procedure MC_SEND_FullStateRequest();
3394 NetOut
.Write(Byte(NET_MSG_REQFST
));
3396 g_Net_Client_Send(True);
3399 procedure MC_SEND_CheatRequest(Kind
: Byte);
3401 NetOut
.Write(Byte(NET_MSG_CHEAT
));
3404 g_Net_Client_Send(True);
3406 procedure MC_SEND_RCONPassword(Password
: String);
3408 NetOut
.Write(Byte(NET_MSG_RCON_AUTH
));
3409 NetOut
.Write(Password
);
3411 g_Net_Client_Send(True);
3413 procedure MC_SEND_RCONCommand(Cmd
: String);
3415 NetOut
.Write(Byte(NET_MSG_RCON_CMD
));
3418 g_Net_Client_Send(True);