5 uses g_net
, g_triggers
, Classes
, SysUtils
, md5
;
51 NET_MSG_RCON_AUTH
= 191;
52 NET_MSG_RCON_CMD
= 192;
53 NET_MSG_TIME_SYNC
= 194;
54 NET_MSG_VOTE_EVENT
= 195;
56 NET_MSG_MAP_REQUEST
= 201;
57 NET_MSG_MAP_RESPONSE
= 202;
58 NET_MSG_RES_REQUEST
= 203;
59 NET_MSG_RES_RESPONSE
= 204;
82 NET_EV_CHANGE_TEAM
= 3;
83 NET_EV_PLAYER_KICK
= 4;
84 NET_EV_PLAYER_BAN
= 5;
85 NET_EV_LMS_WARMUP
= 6;
86 NET_EV_LMS_SURVIVOR
= 7;
90 NET_EV_SCORE_MSG
= 11;
91 NET_EV_LMS_START
= 12;
96 NET_EV_KILLCOMBO
= 17;
97 NET_EV_PLAYER_TOUCH
= 18;
104 NET_VE_INPROGRESS
= 6;
111 NET_CHEAT_SUICIDE
= 1;
112 NET_CHEAT_SPECTATE
= 2;
114 NET_MAX_DIFFTIME
= 5000 div 36;
118 procedure MH_RECV_Info(C
: pTNetClient
; P
: Pointer);
119 procedure MH_RECV_Chat(C
: pTNetClient
; P
: Pointer);
120 procedure MH_RECV_FullStateRequest(C
: pTNetClient
; P
: Pointer);
121 function MH_RECV_PlayerPos(C
: pTNetClient
; P
: Pointer): Word;
122 procedure MH_RECV_PlayerSettings(C
: pTNetClient
; P
: Pointer);
123 procedure MH_RECV_CheatRequest(C
: pTNetClient
; P
: Pointer);
124 procedure MH_RECV_RCONPassword(C
: pTNetClient
; P
: Pointer);
125 procedure MH_RECV_RCONCommand(C
: pTNetClient
; P
: Pointer);
126 procedure MH_RECV_MapRequest(C
: pTNetClient
; P
: Pointer);
127 procedure MH_RECV_ResRequest(C
: pTNetClient
; P
: Pointer);
128 procedure MH_RECV_Vote(C
: pTNetClient
; P
: Pointer);
131 procedure MH_SEND_Everything(CreatePlayers
: Boolean = False; ID
: Integer = NET_EVERYONE
);
132 procedure MH_SEND_Info(ID
: Byte);
133 procedure MH_SEND_Chat(Txt
: string; Mode
: Byte; ID
: Integer = NET_EVERYONE
);
134 procedure MH_SEND_Effect(X
, Y
: Integer; Ang
: SmallInt; Kind
: Byte; ID
: Integer = NET_EVERYONE
);
135 procedure MH_SEND_Sound(X
, Y
: Integer; Name
: string; ID
: Integer = NET_EVERYONE
);
136 procedure MH_SEND_CreateShot(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
137 procedure MH_SEND_UpdateShot(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
138 procedure MH_SEND_DeleteShot(Proj
: LongInt; X
, Y
: LongInt; Loud
: Boolean = True; ID
: Integer = NET_EVERYONE
);
139 procedure MH_SEND_GameStats(ID
: Integer = NET_EVERYONE
);
140 procedure MH_SEND_CoopStats(ID
: Integer = NET_EVERYONE
);
141 procedure MH_SEND_GameEvent(EvType
: Byte; EvNum
: Integer = 0; EvStr
: string = 'N'; ID
: Integer = NET_EVERYONE
);
142 procedure MH_SEND_FlagEvent(EvType
: Byte; Flag
: Byte; PID
: Word; Quiet
: Boolean = False; ID
: Integer = NET_EVERYONE
);
143 procedure MH_SEND_GameSettings(ID
: Integer = NET_EVERYONE
);
145 procedure MH_SEND_PlayerCreate(PID
: Word; ID
: Integer = NET_EVERYONE
);
146 procedure MH_SEND_PlayerPos(Reliable
: Boolean; PID
: Word; ID
: Integer = NET_EVERYONE
);
147 procedure MH_SEND_PlayerStats(PID
: Word; ID
: Integer = NET_EVERYONE
);
148 procedure MH_SEND_PlayerDelete(PID
: Word; ID
: Integer = NET_EVERYONE
);
149 procedure MH_SEND_PlayerDamage(PID
: Word; Kind
: Byte; Attacker
, Value
: Word; VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
150 procedure MH_SEND_PlayerFire(PID
: Word; Weapon
: Byte; X
, Y
, AX
, AY
: Integer; ShotID
: Integer = -1; ID
: Integer = NET_EVERYONE
);
151 procedure MH_SEND_PlayerDeath(PID
: Word; KillType
, DeathType
: Byte; Attacker
: Word; ID
: Integer = NET_EVERYONE
);
152 procedure MH_SEND_PlayerSettings(PID
: Word; Mdl
: string = ''; ID
: Integer = NET_EVERYONE
);
154 procedure MH_SEND_ItemSpawn(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
155 procedure MH_SEND_ItemDestroy(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
157 procedure MH_SEND_PanelTexture(PType
: Word; PID
: LongWord; AnimLoop
: Byte; ID
: Integer = NET_EVERYONE
);
158 procedure MH_SEND_PanelState(PType
: Word; PID
: LongWord; ID
: Integer = NET_EVERYONE
);
160 procedure MH_SEND_MonsterSpawn(UID
: Word; ID
: Integer = NET_EVERYONE
);
161 procedure MH_SEND_MonsterPos(UID
: Word; ID
: Integer = NET_EVERYONE
);
162 procedure MH_SEND_MonsterState(UID
: Word; ForcedAnim
: Byte = 255; ID
: Integer = NET_EVERYONE
);
163 procedure MH_SEND_MonsterShot(UID
: Word; X
, Y
, VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
164 procedure MH_SEND_MonsterDelete(UID
: Word; ID
: Integer = NET_EVERYONE
);
166 procedure MH_SEND_TriggerSound(var T
: TTrigger
; ID
: Integer = NET_EVERYONE
);
167 procedure MH_SEND_TriggerMusic(ID
: Integer = NET_EVERYONE
);
169 procedure MH_SEND_TimeSync(Time
: LongWord; ID
: Integer = NET_EVERYONE
);
170 procedure MH_SEND_VoteEvent(EvType
: Byte;
171 StrArg1
: string = 'a'; StrArg2
: string = 'b';
172 IntArg1
: SmallInt = 0; IntArg2
: SmallInt = 0;
173 ID
: Integer = NET_EVERYONE
);
175 // CLIENT MESSAGES //
178 procedure MC_RECV_Chat(P
: Pointer);
179 procedure MC_RECV_Effect(P
: Pointer);
180 procedure MC_RECV_Sound(P
: Pointer);
181 procedure MC_RECV_GameStats(P
: Pointer);
182 procedure MC_RECV_CoopStats(P
: Pointer);
183 procedure MC_RECV_GameEvent(P
: Pointer);
184 procedure MC_RECV_FlagEvent(P
: Pointer);
185 procedure MC_RECV_GameSettings(P
: Pointer);
187 function MC_RECV_PlayerCreate(P
: Pointer): Word;
188 function MC_RECV_PlayerPos(P
: Pointer): Word;
189 function MC_RECV_PlayerStats(P
: Pointer): Word;
190 function MC_RECV_PlayerDelete(P
: Pointer): Word;
191 function MC_RECV_PlayerDamage(P
: Pointer): Word;
192 function MC_RECV_PlayerDeath(P
: Pointer): Word;
193 function MC_RECV_PlayerFire(P
: Pointer): Word;
194 procedure MC_RECV_PlayerSettings(P
: Pointer);
196 procedure MC_RECV_ItemSpawn(P
: Pointer);
197 procedure MC_RECV_ItemDestroy(P
: Pointer);
199 procedure MC_RECV_PanelTexture(P
: Pointer);
200 procedure MC_RECV_PanelState(P
: Pointer);
202 procedure MC_RECV_MonsterSpawn(P
: Pointer);
203 procedure MC_RECV_MonsterPos(P
: Pointer);
204 procedure MC_RECV_MonsterState(P
: Pointer);
205 procedure MC_RECV_MonsterShot(P
: Pointer);
206 procedure MC_RECV_MonsterDelete(P
: Pointer);
208 procedure MC_RECV_CreateShot(P
: Pointer);
209 procedure MC_RECV_UpdateShot(P
: Pointer);
210 procedure MC_RECV_DeleteShot(P
: Pointer);
212 procedure MC_RECV_TriggerSound(P
: Pointer);
213 procedure MC_RECV_TriggerMusic(P
: Pointer);
215 procedure MC_RECV_TimeSync(P
: Pointer);
216 procedure MC_RECV_VoteEvent(P
: Pointer);
218 procedure MC_SEND_Info(Password
: string);
219 procedure MC_SEND_Chat(Txt
: string; Mode
: Byte);
220 procedure MC_SEND_PlayerPos();
221 procedure MC_SEND_FullStateRequest();
222 procedure MC_SEND_PlayerSettings();
223 procedure MC_SEND_CheatRequest(Kind
: Byte);
224 procedure MC_SEND_RCONPassword(Password
: string);
225 procedure MC_SEND_RCONCommand(Cmd
: string);
226 procedure MC_SEND_Vote(Start
: Boolean = False; Command
: string = 'a');
228 procedure MC_SEND_MapRequest();
229 procedure MC_SEND_ResRequest(const resName
: AnsiString);
232 TExternalResourceInfo
= record
247 ExternalResources
: array of TExternalResourceInfo
;
250 function MapDataFromMsgStream(msgStream
: TMemoryStream
):TMapDataMsg
;
251 function ResDataFromMsgStream(msgStream
: TMemoryStream
):TResDataMsg
;
256 Math
, ENet
, e_input
, e_fixedbuffer
, e_graphics
, e_log
,
257 g_textures
, g_gfx
, g_sound
, g_console
, g_basic
, g_options
, g_main
,
258 g_game
, g_player
, g_map
, g_panel
, g_items
, g_weapons
, g_phys
, g_gui
,
259 g_language
, g_monsters
, g_netmaster
, utils
, wadreader
, MAPDEF
;
272 NET_KEY_FORCEDIR
= 4096;
275 //kBytePrev: Word = 0;
276 //kDirPrev: TDirection = D_LEFT;
277 //HostGameTime: Word = 0;
284 procedure MH_RECV_Chat(C
: pTNetClient
; P
: Pointer);
292 Pl
:= g_Player_Get(PID
);
294 Txt
:= e_Raw_Read_String(P
);
295 Mode
:= e_Raw_Read_Byte(P
);
296 if (Mode
= NET_CHAT_SYSTEM
) then
297 Mode
:= NET_CHAT_PLAYER
; // prevent sending system messages from clients
298 if (Mode
= NET_CHAT_TEAM
) and (not gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
299 Mode
:= NET_CHAT_PLAYER
; // revert to player chat in non-team game
302 MH_SEND_Chat(Txt
, Mode
)
304 MH_SEND_Chat(Pl
.Name
+ ': ' + Txt
, Mode
, IfThen(Mode
= NET_CHAT_TEAM
, Pl
.Team
, NET_EVERYONE
));
307 procedure MH_RECV_Info(C
: pTNetClient
; P
: Pointer);
309 Ver
, PName
, Model
, Pw
: string;
314 ProtoVer
, Zero
: Byte;
316 Zero
:= e_Raw_Read_Byte(P
);
317 ProtoVer
:= e_Raw_Read_Byte(P
);
318 Ver
:= e_Raw_Read_String(P
);
319 Pw
:= e_Raw_Read_String(P
);
320 PName
:= e_Raw_Read_String(P
);
321 Model
:= e_Raw_Read_String(P
);
322 R
:= e_Raw_Read_Byte(P
);
323 G
:= e_Raw_Read_Byte(P
);
324 B
:= e_Raw_Read_Byte(P
);
325 T
:= e_Raw_Read_Byte(P
);
327 if (Ver
<> GAME_VERSION
) or (ProtoVer
<> NET_PROTO_VERSION
) or (Zero
<> 0) then
329 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
330 _lc
[I_NET_DISC_VERSION
]);
331 enet_peer_disconnect(C
^.Peer
, NET_DISC_VERSION
);
335 if g_Net_IsHostBanned(C
^.Peer
^.address
.host
) then
337 if g_Net_IsHostBanned(C
^.Peer
^.address
.host
, True) then
339 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
340 _lc
[I_NET_DISC_BAN
]);
341 enet_peer_disconnect(C
^.Peer
, NET_DISC_BAN
);
345 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
346 _lc
[I_NET_DISC_BAN
]);
347 enet_peer_disconnect(C
^.Peer
, NET_DISC_TEMPBAN
);
352 if NetPassword
<> '' then
353 if AnsiLowerCase(NetPassword
) <> AnsiLowerCase(Pw
) then
355 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
356 _lc
[I_NET_DISC_PASSWORD
]);
357 enet_peer_disconnect(C
^.Peer
, NET_DISC_PASSWORD
);
365 PID
:= g_Player_Create(Model
, Color
, T
, False);
366 with g_Player_Get(PID
) do
374 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [PName
]), True);
375 e_WriteLog('NET: Client ' + PName
+ ' [' + IntToStr(C
^.ID
) +
376 '] connected. Assigned player #' + IntToStr(PID
) + '.', MSG_NOTIFY
);
380 with g_Player_Get(PID
) do
384 // round in progress, don't spawn
385 if (gGameSettings
.MaxLives
> 0) and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
390 FWantsInGame
:= True; // TODO: look into this later
393 Respawn(gGameSettings
.GameType
= GT_SINGLE
);
396 for I
:= Low(NetClients
) to High(NetClients
) do
398 if NetClients
[I
].ID
= C
^.ID
then Continue
;
399 MH_SEND_PlayerCreate(PID
, NetClients
[I
].ID
);
400 MH_SEND_PlayerPos(True, PID
, NetClients
[I
].ID
);
401 MH_SEND_PlayerStats(PID
, NetClients
[I
].ID
);
404 if gState
in [STATE_INTERCUSTOM
, STATE_FOLD
] then
405 MH_SEND_GameEvent(NET_EV_MAPEND
, 0, 'N', C
^.ID
);
407 if NetUseMaster
then g_Net_Slist_Update
;
410 procedure MH_RECV_FullStateRequest(C
: pTNetClient
; P
: Pointer);
413 MH_SEND_Everything((C
^.State
= NET_STATE_AUTH
), C
^.ID
)
415 C
^.RequestedFullUpdate
:= True;
420 function MH_RECV_PlayerPos(C
: pTNetClient
; P
: Pointer): Word;
429 if not gGameOn
then Exit
;
431 GT
:= e_Raw_Read_LongWord(P
);
433 Pl
:= g_Player_Get(PID
);
437 if (GT
> gTime
+ NET_MAX_DIFFTIME
) or (GT
< Pl
.NetTime
) then Exit
;
442 kByte
:= e_Raw_Read_Word(P
);
443 Dir
:= e_Raw_Read_Byte(P
);
444 if Direction
<> TDirection(Dir
) then
445 JustTeleported
:= False;
446 SetDirection(TDirection(Dir
));
449 if kByte
= NET_KEY_CHAT
then
451 PressKey(KEY_CHAT
, 10000);
455 if LongBool(kByte
and NET_KEY_LEFT
) then PressKey(KEY_LEFT
, 10000);
456 if LongBool(kByte
and NET_KEY_RIGHT
) then PressKey(KEY_RIGHT
, 10000);
457 if LongBool(kByte
and NET_KEY_UP
) then PressKey(KEY_UP
, 10000);
458 if LongBool(kByte
and NET_KEY_DOWN
) then PressKey(KEY_DOWN
, 10000);
459 if LongBool(kByte
and NET_KEY_JUMP
) then PressKey(KEY_JUMP
, 10000);
460 if LongBool(kByte
and NET_KEY_FIRE
) then PressKey(KEY_FIRE
, 10000);
461 if LongBool(kByte
and NET_KEY_OPEN
) then PressKey(KEY_OPEN
, 10000);
462 if LongBool(kByte
and NET_KEY_NW
) then PressKey(KEY_NEXTWEAPON
, 10000);
463 if LongBool(kByte
and NET_KEY_PW
) then PressKey(KEY_PREVWEAPON
, 10000);
466 // MH_SEND_PlayerPos(False, PID, C^.ID);
469 procedure MH_RECV_CheatRequest(C
: pTNetClient
; P
: Pointer);
474 Pl
:= g_Player_Get(C
^.Player
);
475 if Pl
= nil then Exit
;
477 CheatKind
:= e_Raw_Read_Byte(P
);
481 Pl
.Damage(SUICIDE_DAMAGE
, Pl
.UID
, 0, 0, HIT_SELF
);
484 if Pl
.FSpectator
then
492 procedure MH_RECV_PlayerSettings(C
: pTNetClient
; P
: Pointer);
500 TmpName
:= e_Raw_Read_String(P
);
501 TmpModel
:= e_Raw_Read_String(P
);
502 TmpColor
.R
:= e_Raw_Read_Byte(P
);
503 TmpColor
.G
:= e_Raw_Read_Byte(P
);
504 TmpColor
.B
:= e_Raw_Read_Byte(P
);
505 TmpTeam
:= e_Raw_Read_Byte(P
);
507 Pl
:= g_Player_Get(C
^.Player
);
508 if Pl
= nil then Exit
;
510 if (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) and (Pl
.Team
<> TmpTeam
) then
513 Pl
.SetColor(TmpColor
);
515 if Pl
.Name
<> TmpName
then
517 g_Console_Add(Format(_lc
[I_PLAYER_NAME
], [Pl
.Name
, TmpName
]), True);
521 if TmpModel
<> Pl
.Model
.Name
then
522 Pl
.SetModel(TmpModel
);
524 MH_SEND_PlayerSettings(Pl
.UID
, TmpModel
);
529 procedure MH_RECV_RCONPassword(C
: pTNetClient
; P
: Pointer);
533 Pwd
:= e_Raw_Read_String(P
);
534 if not NetAllowRCON
then Exit
;
535 if Pwd
= NetRCONPassword
then
538 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_PWGOOD
, 'N', C
^.ID
);
541 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_PWBAD
, 'N', C
^.ID
);
544 procedure MH_RECV_RCONCommand(C
: pTNetClient
; P
: Pointer);
548 Cmd
:= e_Raw_Read_String(P
);
549 if not NetAllowRCON
then Exit
;
550 if not C
^.RCONAuth
then
552 MH_SEND_GameEvent(NET_EV_RCON
, NET_RCON_NOAUTH
, 'N', C
^.ID
);
555 g_Console_Process(Cmd
);
560 procedure MH_RECV_Vote(C
: pTNetClient
; P
: Pointer);
563 Name
, Command
: string;
567 Start
:= e_Raw_Read_Byte(P
) <> 0;
568 Command
:= e_Raw_Read_String(P
);
570 Pl
:= g_Player_Get(C
^.Player
);
571 if Pl
= nil then Exit
;
576 if not g_Console_CommandBlacklisted(Command
) then
577 g_Game_StartVote(Command
, Name
);
579 else if gVoteInProgress
then
581 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
582 Need
:= Floor((NetClientCount
+1)/2.0) + 1
584 Need
:= Floor(NetClientCount
/2.0) + 1;
589 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_REVOKED
], [Name
, gVoteCount
, Need
]), True);
590 MH_SEND_VoteEvent(NET_VE_REVOKE
, Name
, 'a', gVoteCount
, Need
);
596 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [Name
, gVoteCount
, Need
]), True);
597 MH_SEND_VoteEvent(NET_VE_VOTE
, Name
, 'a', gVoteCount
, Need
);
605 procedure MH_SEND_Everything(CreatePlayers
: Boolean = False; ID
: Integer = NET_EVERYONE
);
609 if gPlayers
<> nil then
610 for I
:= Low(gPlayers
) to High(gPlayers
) do
611 if gPlayers
[I
] <> nil then
613 if CreatePlayers
then MH_SEND_PlayerCreate(gPlayers
[I
].UID
, ID
);
614 MH_SEND_PlayerPos(True, gPlayers
[I
].UID
, ID
);
615 MH_SEND_PlayerStats(gPlayers
[I
].UID
, ID
);
617 if (gPlayers
[I
].Flag
<> FLAG_NONE
) and (gGameSettings
.GameMode
= GM_CTF
) then
618 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED
, gPlayers
[I
].Flag
, gPlayers
[I
].UID
, True, ID
);
621 if gItems
<> nil then
623 for I
:= High(gItems
) downto Low(gItems
) do
624 if gItems
[I
].Live
then
625 MH_SEND_ItemSpawn(True, I
, ID
);
628 if gMonsters
<> nil then
629 for I
:= 0 to High(gMonsters
) do
630 if gMonsters
[I
] <> nil then
631 MH_SEND_MonsterSpawn(gMonsters
[I
].UID
, ID
);
633 if gWalls
<> nil then
634 for I
:= Low(gWalls
) to High(gWalls
) do
635 if gWalls
[I
] <> nil then
639 MH_SEND_PanelState(PanelType
, I
, ID
);
641 if GetTextureCount
> 1 then
642 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
645 if gLifts
<> nil then
646 for I
:= Low(gLifts
) to High(gLifts
) do
647 if gLifts
[I
] <> nil then
649 MH_SEND_PanelState(PanelType
, I
, ID
);
651 if gRenderForegrounds
<> nil then
652 for I
:= Low(gRenderForegrounds
) to High(gRenderForegrounds
) do
653 if gRenderForegrounds
[I
] <> nil then
654 with gRenderForegrounds
[I
] do
655 if (GetTextureCount
> 1) then
656 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
657 if gRenderBackgrounds
<> nil then
658 for I
:= Low(gRenderBackgrounds
) to High(gRenderBackgrounds
) do
659 if gRenderBackgrounds
[I
] <> nil then
660 with gRenderBackgrounds
[I
] do
661 if GetTextureCount
> 1 then
662 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
663 if gWater
<> nil then
664 for I
:= Low(gWater
) to High(gWater
) do
665 if gWater
[I
] <> nil then
667 if GetTextureCount
> 1 then
668 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
669 if gAcid1
<> nil then
670 for I
:= Low(gAcid1
) to High(gAcid1
) do
671 if gAcid1
[I
] <> nil then
673 if GetTextureCount
> 1 then
674 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
675 if gAcid2
<> nil then
676 for I
:= Low(gAcid2
) to High(gAcid2
) do
677 if gAcid2
[I
] <> nil then
679 if GetTextureCount
> 1 then
680 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
681 if gSteps
<> nil then
682 for I
:= Low(gSteps
) to High(gSteps
) do
683 if gSteps
[I
] <> nil then
685 if GetTextureCount
> 1 then
686 MH_SEND_PanelTexture(PanelType
, I
, LastAnimLoop
, ID
);
688 if gTriggers
<> nil then
689 for I
:= Low(gTriggers
) to High(gTriggers
) do
690 if gTriggers
[I
].TriggerType
= TRIGGER_SOUND
then
691 MH_SEND_TriggerSound(gTriggers
[I
], ID
);
694 for I
:= Low(Shots
) to High(Shots
) do
695 if Shots
[i
].ShotType
in [6, 7, 8] then
696 MH_SEND_CreateShot(i
, ID
);
698 MH_SEND_TriggerMusic(ID
);
700 MH_SEND_GameStats(ID
);
701 MH_SEND_CoopStats(ID
);
703 if gGameSettings
.GameMode
= GM_CTF
then
705 if gFlags
[FLAG_RED
].State
<> FLAG_STATE_CAPTURED
then
706 MH_SEND_FlagEvent(gFlags
[FLAG_RED
].State
, FLAG_RED
, 0, True, ID
);
707 if gFlags
[FLAG_BLUE
].State
<> FLAG_STATE_CAPTURED
then
708 MH_SEND_FlagEvent(gFlags
[FLAG_BLUE
].State
, FLAG_BLUE
, 0, True, ID
);
711 if CreatePlayers
and (ID
>= 0) then NetClients
[ID
].State
:= NET_STATE_GAME
;
713 if gLMSRespawn
> LMS_RESPAWN_NONE
then
714 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, (gLMSRespawnTime
- gTime
) div 1000, 'N', ID
);
717 procedure MH_SEND_Info(ID
: Byte);
721 g_ProcessResourceStr(gMapInfo
.Map
, nil, nil, @Map
);
723 e_Buffer_Clear(@NetOut
);
725 e_Buffer_Write(@NetOut
, Byte(NET_MSG_INFO
));
726 e_Buffer_Write(@NetOut
, ID
);
727 e_Buffer_Write(@NetOut
, NetClients
[ID
].Player
);
728 e_Buffer_Write(@NetOut
, gGameSettings
.WAD
);
729 e_Buffer_Write(@NetOut
, Map
);
730 e_Buffer_Write(@NetOut
, gWADHash
);
731 e_Buffer_Write(@NetOut
, gGameSettings
.GameMode
);
732 e_Buffer_Write(@NetOut
, gGameSettings
.GoalLimit
);
733 e_Buffer_Write(@NetOut
, gGameSettings
.TimeLimit
);
734 e_Buffer_Write(@NetOut
, gGameSettings
.MaxLives
);
735 e_Buffer_Write(@NetOut
, gGameSettings
.Options
);
736 e_Buffer_Write(@NetOut
, gTime
);
738 g_Net_Host_Send(ID
, True, NET_CHAN_SERVICE
);
741 procedure MH_SEND_Chat(Txt
: string; Mode
: Byte; ID
: Integer = NET_EVERYONE
);
747 if (Mode
= NET_CHAT_TEAM
) and (not gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
748 Mode
:= NET_CHAT_PLAYER
;
751 if (Mode
= NET_CHAT_TEAM
) then
753 for i
:= Low(gPlayers
) to High(gPlayers
) do
754 if (gPlayers
[i
] <> nil) and (gPlayers
[i
].FClientID
>= 0) and
755 (gPlayers
[i
].Team
= ID
) then
757 e_Buffer_Write(@NetOut
, Byte(NET_MSG_CHAT
));
758 e_Buffer_Write(@NetOut
, Txt
);
759 e_Buffer_Write(@NetOut
, Mode
);
760 g_Net_Host_Send(gPlayers
[i
].FClientID
, True, NET_CHAN_CHAT
);
767 e_Buffer_Write(@NetOut
, Byte(NET_MSG_CHAT
));
768 e_Buffer_Write(@NetOut
, Txt
);
769 e_Buffer_Write(@NetOut
, Mode
);
770 g_Net_Host_Send(ID
, True, NET_CHAN_CHAT
);
773 if Mode
= NET_CHAT_SYSTEM
then
776 if ID
= NET_EVERYONE
then
778 if Mode
= NET_CHAT_PLAYER
then
780 g_Console_Add(Txt
, True);
781 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
782 g_Sound_PlayEx('SOUND_GAME_RADIO');
785 if Mode
= NET_CHAT_TEAM
then
786 if gPlayer1
<> nil then
788 if (gPlayer1
.Team
= TEAM_RED
) and (Team
= TEAM_RED
) then
790 g_Console_Add(#18'[Team] '#2 + Txt
, True);
791 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
792 g_Sound_PlayEx('SOUND_GAME_RADIO');
794 else if (gPlayer1
.Team
= TEAM_BLUE
) and (Team
= TEAM_BLUE
) then
796 g_Console_Add(#20'[Team] '#2 + Txt
, True);
797 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
798 g_Sound_PlayEx('SOUND_GAME_RADIO');
804 Name
:= g_Net_ClientName_ByID(ID
);
805 g_Console_Add('-> ' + Name
+ ': ' + Txt
, True);
806 e_WriteLog('[Tell ' + Name
+ '] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
807 g_Sound_PlayEx('SOUND_GAME_RADIO');
811 procedure MH_SEND_Effect(X
, Y
: Integer; Ang
: SmallInt; Kind
: Byte; ID
: Integer = NET_EVERYONE
);
813 e_Buffer_Write(@NetOut
, Byte(NET_MSG_GFX
));
814 e_Buffer_Write(@NetOut
, Kind
);
815 e_Buffer_Write(@NetOut
, X
);
816 e_Buffer_Write(@NetOut
, Y
);
817 e_Buffer_Write(@NetOut
, Ang
);
819 g_Net_Host_Send(ID
, False, NET_CHAN_GAME
);
822 procedure MH_SEND_Sound(X
, Y
: Integer; Name
: string; ID
: Integer = NET_EVERYONE
);
824 e_Buffer_Write(@NetOut
, Byte(NET_MSG_SND
));
825 e_Buffer_Write(@NetOut
, Name
);
826 e_Buffer_Write(@NetOut
, X
);
827 e_Buffer_Write(@NetOut
, Y
);
829 g_Net_Host_Send(ID
, False, NET_CHAN_GAME
);
832 procedure MH_SEND_CreateShot(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
834 if (Shots
= nil) or (Proj
< 0) or (Proj
> High(Shots
)) then Exit
;
836 e_Buffer_Write(@NetOut
, Byte(NET_MSG_SHADD
));
837 e_Buffer_Write(@NetOut
, Proj
);
838 e_Buffer_Write(@NetOut
, Shots
[Proj
].ShotType
);
839 e_Buffer_Write(@NetOut
, Shots
[Proj
].Target
);
840 e_Buffer_Write(@NetOut
, Shots
[Proj
].SpawnerUID
);
841 e_Buffer_Write(@NetOut
, Shots
[Proj
].Timeout
);
842 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.X
);
843 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Y
);
844 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Vel
.X
);
845 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Vel
.Y
);
847 g_Net_Host_Send(ID
, True, NET_CHAN_SHOTS
);
850 procedure MH_SEND_UpdateShot(Proj
: LongInt; ID
: Integer = NET_EVERYONE
);
852 if (Shots
= nil) or (Proj
< 0) or (Proj
> High(Shots
)) then Exit
;
854 e_Buffer_Write(@NetOut
, Byte(NET_MSG_SHPOS
));
855 e_Buffer_Write(@NetOut
, Proj
);
856 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.X
);
857 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Y
);
858 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Vel
.X
);
859 e_Buffer_Write(@NetOut
, Shots
[Proj
].Obj
.Vel
.Y
);
861 g_Net_Host_Send(ID
, False, NET_CHAN_SHOTS
);
864 procedure MH_Send_DeleteShot(Proj
: LongInt; X
, Y
: LongInt; Loud
: Boolean = True; ID
: Integer = NET_EVERYONE
);
866 e_Buffer_Write(@NetOut
, Byte(NET_MSG_SHDEL
));
867 e_Buffer_Write(@NetOut
, Proj
);
868 e_Buffer_Write(@NetOut
, Byte(Loud
));
869 e_Buffer_Write(@NetOut
, X
);
870 e_Buffer_Write(@NetOut
, Y
);
872 g_Net_Host_Send(ID
, True, NET_CHAN_SHOTS
);
875 procedure MH_SEND_GameStats(ID
: Integer = NET_EVERYONE
);
877 e_Buffer_Write(@NetOut
, Byte(NET_MSG_SCORE
));
878 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
880 e_Buffer_Write(@NetOut
, gTeamStat
[TEAM_RED
].Goals
);
881 e_Buffer_Write(@NetOut
, gTeamStat
[TEAM_BLUE
].Goals
);
884 if gGameSettings
.GameMode
= GM_COOP
then
886 e_Buffer_Write(@NetOut
, gCoopMonstersKilled
);
887 e_Buffer_Write(@NetOut
, gCoopSecretsFound
);
890 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
893 procedure MH_SEND_CoopStats(ID
: Integer = NET_EVERYONE
);
895 e_Buffer_Write(@NetOut
, Byte(NET_MSG_COOP
));
896 e_Buffer_Write(@NetOut
, gTotalMonsters
);
897 e_Buffer_Write(@NetOut
, gSecretsCount
);
898 e_Buffer_Write(@NetOut
, gCoopTotalMonstersKilled
);
899 e_Buffer_Write(@NetOut
, gCoopTotalSecretsFound
);
900 e_Buffer_Write(@NetOut
, gCoopTotalMonsters
);
901 e_Buffer_Write(@NetOut
, gCoopTotalSecrets
);
904 procedure MH_SEND_GameEvent(EvType
: Byte; EvNum
: Integer = 0; EvStr
: string = 'N'; ID
: Integer = NET_EVERYONE
);
906 e_Buffer_Write(@NetOut
, Byte(NET_MSG_GEVENT
));
907 e_Buffer_Write(@NetOut
, EvType
);
908 e_Buffer_Write(@NetOut
, EvNum
);
909 e_Buffer_Write(@NetOut
, EvStr
);
910 e_Buffer_Write(@NetOut
, Byte(gLastMap
));
911 e_Buffer_Write(@NetOut
, gTime
);
912 if (EvType
= NET_EV_MAPSTART
) and (Pos(':\', EvStr
) > 0) then
914 e_Buffer_Write(@NetOut
, Byte(1));
915 e_Buffer_Write(@NetOut
, gWADHash
);
917 e_Buffer_Write(@NetOut
, Byte(0));
919 g_Net_Host_Send(ID
, True, NET_CHAN_SERVICE
);
922 procedure MH_SEND_FlagEvent(EvType
: Byte; Flag
: Byte; PID
: Word; Quiet
: Boolean = False; ID
: Integer = NET_EVERYONE
);
924 e_Buffer_Write(@NetOut
, Byte(NET_MSG_FLAG
));
925 e_Buffer_Write(@NetOut
, EvType
);
926 e_Buffer_Write(@NetOut
, Flag
);
927 e_Buffer_Write(@NetOut
, Byte(Quiet
));
928 e_Buffer_Write(@NetOut
, PID
);
929 e_Buffer_Write(@NetOut
, gFlags
[Flag
].State
);
930 e_Buffer_Write(@NetOut
, gFlags
[Flag
].CaptureTime
);
931 e_Buffer_Write(@NetOut
, gFlags
[Flag
].Obj
.X
);
932 e_Buffer_Write(@NetOut
, gFlags
[Flag
].Obj
.Y
);
933 e_Buffer_Write(@NetOut
, gFlags
[Flag
].Obj
.Vel
.X
);
934 e_Buffer_Write(@NetOut
, gFlags
[Flag
].Obj
.Vel
.Y
);
936 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
939 procedure MH_SEND_GameSettings(ID
: Integer = NET_EVERYONE
);
941 e_Buffer_Write(@NetOut
, Byte(NET_MSG_GSET
));
942 e_Buffer_Write(@NetOut
, gGameSettings
.GameMode
);
943 e_Buffer_Write(@NetOut
, gGameSettings
.GoalLimit
);
944 e_Buffer_Write(@NetOut
, gGameSettings
.TimeLimit
);
945 e_Buffer_Write(@NetOut
, gGameSettings
.MaxLives
);
946 e_Buffer_Write(@NetOut
, gGameSettings
.Options
);
948 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
953 procedure MH_SEND_PlayerCreate(PID
: Word; ID
: Integer = NET_EVERYONE
);
957 P
:= g_Player_Get(PID
);
958 if P
= nil then Exit
;
960 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLR
));
961 e_Buffer_Write(@NetOut
, PID
);
962 e_Buffer_Write(@NetOut
, P
.Name
);
964 e_Buffer_Write(@NetOut
, P
.FActualModelName
);
965 e_Buffer_Write(@NetOut
, P
.FColor
.R
);
966 e_Buffer_Write(@NetOut
, P
.FColor
.G
);
967 e_Buffer_Write(@NetOut
, P
.FColor
.B
);
968 e_Buffer_Write(@NetOut
, P
.Team
);
970 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
)
973 procedure MH_SEND_PlayerPos(Reliable
: Boolean; PID
: Word; ID
: Integer = NET_EVERYONE
);
978 Pl
:= g_Player_Get(PID
);
979 if Pl
= nil then Exit
;
980 if Pl
.FDummy
then Exit
;
982 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRPOS
));
983 e_Buffer_Write(@NetOut
, gTime
);
984 e_Buffer_Write(@NetOut
, PID
);
990 e_Buffer_Write(@NetOut
, FPing
);
991 e_Buffer_Write(@NetOut
, FLoss
);
992 if IsKeyPressed(KEY_CHAT
) then
993 kByte
:= NET_KEY_CHAT
996 if IsKeyPressed(KEY_LEFT
) then kByte
:= kByte
or NET_KEY_LEFT
;
997 if IsKeyPressed(KEY_RIGHT
) then kByte
:= kByte
or NET_KEY_RIGHT
;
998 if IsKeyPressed(KEY_UP
) then kByte
:= kByte
or NET_KEY_UP
;
999 if IsKeyPressed(KEY_DOWN
) then kByte
:= kByte
or NET_KEY_DOWN
;
1000 if IsKeyPressed(KEY_JUMP
) then kByte
:= kByte
or NET_KEY_JUMP
;
1001 if JustTeleported
then kByte
:= kByte
or NET_KEY_FORCEDIR
;
1004 e_Buffer_Write(@NetOut
, kByte
);
1005 if Direction
= D_LEFT
then e_Buffer_Write(@NetOut
, Byte(0)) else e_Buffer_Write(@NetOut
, Byte(1));
1006 e_Buffer_Write(@NetOut
, GameX
);
1007 e_Buffer_Write(@NetOut
, GameY
);
1008 e_Buffer_Write(@NetOut
, GameVelX
);
1009 e_Buffer_Write(@NetOut
, GameVelY
);
1010 e_Buffer_Write(@NetOut
, GameAccelX
);
1011 e_Buffer_Write(@NetOut
, GameAccelY
);
1014 g_Net_Host_Send(ID
, Reliable
, NET_CHAN_PLAYERPOS
);
1017 procedure MH_SEND_PlayerStats(PID
: Word; ID
: Integer = NET_EVERYONE
);
1022 P
:= g_Player_Get(PID
);
1023 if P
= nil then Exit
;
1025 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRSTA
));
1026 e_Buffer_Write(@NetOut
, PID
);
1030 e_Buffer_Write(@NetOut
, Byte(Live
));
1031 e_Buffer_Write(@NetOut
, Byte(GodMode
));
1032 e_Buffer_Write(@NetOut
, Health
);
1033 e_Buffer_Write(@NetOut
, Armor
);
1034 e_Buffer_Write(@NetOut
, Air
);
1035 e_Buffer_Write(@NetOut
, JetFuel
);
1036 e_Buffer_Write(@NetOut
, Lives
);
1037 e_Buffer_Write(@NetOut
, Team
);
1039 for I
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
1040 e_Buffer_Write(@NetOut
, Byte(FWeapon
[I
]));
1042 for I
:= A_BULLETS
to A_CELLS
do
1043 e_Buffer_Write(@NetOut
, FAmmo
[I
]);
1045 for I
:= A_BULLETS
to A_CELLS
do
1046 e_Buffer_Write(@NetOut
, FMaxAmmo
[I
]);
1048 for I
:= MR_SUIT
to MR_MAX
do
1049 e_Buffer_Write(@NetOut
, LongWord(FMegaRulez
[I
]));
1051 e_Buffer_Write(@NetOut
, Byte(R_ITEM_BACKPACK
in FRulez
));
1052 e_Buffer_Write(@NetOut
, Byte(R_KEY_RED
in FRulez
));
1053 e_Buffer_Write(@NetOut
, Byte(R_KEY_GREEN
in FRulez
));
1054 e_Buffer_Write(@NetOut
, Byte(R_KEY_BLUE
in FRulez
));
1055 e_Buffer_Write(@NetOut
, Byte(R_BERSERK
in FRulez
));
1057 e_Buffer_Write(@NetOut
, Frags
);
1058 e_Buffer_Write(@NetOut
, Death
);
1060 e_Buffer_Write(@NetOut
, CurrWeap
);
1062 e_Buffer_Write(@NetOut
, Byte(FSpectator
));
1063 e_Buffer_Write(@NetOut
, Byte(FGhost
));
1064 e_Buffer_Write(@NetOut
, Byte(FPhysics
));
1065 e_Buffer_Write(@NetOut
, Byte(FNoRespawn
));
1066 e_Buffer_Write(@NetOut
, Byte(FJetpack
));
1069 g_Net_Host_Send(ID
, True, NET_CHAN_PLAYER
);
1072 procedure MH_SEND_PlayerDamage(PID
: Word; Kind
: Byte; Attacker
, Value
: Word; VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
1074 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRDMG
));
1075 e_Buffer_Write(@NetOut
, PID
);
1076 e_Buffer_Write(@NetOut
, Kind
);
1077 e_Buffer_Write(@NetOut
, Attacker
);
1078 e_Buffer_Write(@NetOut
, Value
);
1079 e_Buffer_Write(@NetOut
, VX
);
1080 e_Buffer_Write(@NetOut
, VY
);
1082 g_Net_Host_Send(ID
, False, NET_CHAN_PLAYER
);
1085 procedure MH_SEND_PlayerDeath(PID
: Word; KillType
, DeathType
: Byte; Attacker
: Word; ID
: Integer = NET_EVERYONE
);
1087 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRDIE
));
1088 e_Buffer_Write(@NetOut
, PID
);
1089 e_Buffer_Write(@NetOut
, KillType
);
1090 e_Buffer_Write(@NetOut
, DeathType
);
1091 e_Buffer_Write(@NetOut
, Attacker
);
1093 g_Net_Host_Send(ID
, True, NET_CHAN_PLAYER
);
1096 procedure MH_SEND_PlayerFire(PID
: Word; Weapon
: Byte; X
, Y
, AX
, AY
: Integer; ShotID
: Integer = -1; ID
: Integer = NET_EVERYONE
);
1098 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRFIRE
));
1099 e_Buffer_Write(@NetOut
, PID
);
1100 e_Buffer_Write(@NetOut
, Weapon
);
1101 e_Buffer_Write(@NetOut
, X
);
1102 e_Buffer_Write(@NetOut
, Y
);
1103 e_Buffer_Write(@NetOut
, AX
);
1104 e_Buffer_Write(@NetOut
, AY
);
1105 e_Buffer_Write(@NetOut
, ShotID
);
1107 g_Net_Host_Send(ID
, True, NET_CHAN_SHOTS
);
1110 procedure MH_SEND_PlayerDelete(PID
: Word; ID
: Integer = NET_EVERYONE
);
1112 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRDEL
));
1113 e_Buffer_Write(@NetOut
, PID
);
1115 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
1118 procedure MH_SEND_PlayerSettings(PID
: Word; Mdl
: string = ''; ID
: Integer = NET_EVERYONE
);
1122 Pl
:= g_Player_Get(PID
);
1123 if Pl
= nil then Exit
;
1125 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRSET
));
1126 e_Buffer_Write(@NetOut
, PID
);
1127 e_Buffer_Write(@NetOut
, Pl
.Name
);
1129 e_Buffer_Write(@NetOut
, Pl
.Model
.Name
)
1131 e_Buffer_Write(@NetOut
, Mdl
);
1132 e_Buffer_Write(@NetOut
, Pl
.FColor
.R
);
1133 e_Buffer_Write(@NetOut
, Pl
.FColor
.G
);
1134 e_Buffer_Write(@NetOut
, Pl
.FColor
.B
);
1135 e_Buffer_Write(@NetOut
, Pl
.Team
);
1137 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
1142 procedure MH_SEND_ItemSpawn(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
1144 e_Buffer_Write(@NetOut
, Byte(NET_MSG_ISPAWN
));
1145 e_Buffer_Write(@NetOut
, IID
);
1146 e_Buffer_Write(@NetOut
, Byte(Quiet
));
1147 e_Buffer_Write(@NetOut
, gItems
[IID
].ItemType
);
1148 e_Buffer_Write(@NetOut
, Byte(gItems
[IID
].Fall
));
1149 e_Buffer_Write(@NetOut
, Byte(gItems
[IID
].Respawnable
));
1150 e_Buffer_Write(@NetOut
, gItems
[IID
].Obj
.X
);
1151 e_Buffer_Write(@NetOut
, gItems
[IID
].Obj
.Y
);
1152 e_Buffer_Write(@NetOut
, gItems
[IID
].Obj
.Vel
.X
);
1153 e_Buffer_Write(@NetOut
, gItems
[IID
].Obj
.Vel
.Y
);
1155 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1158 procedure MH_SEND_ItemDestroy(Quiet
: Boolean; IID
: Word; ID
: Integer = NET_EVERYONE
);
1160 e_Buffer_Write(@NetOut
, Byte(NET_MSG_IDEL
));
1161 e_Buffer_Write(@NetOut
, IID
);
1162 e_Buffer_Write(@NetOut
, Byte(Quiet
));
1164 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1169 procedure MH_SEND_PanelTexture(PType
: Word; PID
: LongWord; AnimLoop
: Byte; ID
: Integer = NET_EVERYONE
);
1174 PANEL_WALL
, PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
1177 TP
:= gRenderForegrounds
[PID
];
1179 TP
:= gRenderBackgrounds
[PID
];
1194 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PTEX
));
1195 e_Buffer_Write(@NetOut
, PType
);
1196 e_Buffer_Write(@NetOut
, PID
);
1197 e_Buffer_Write(@NetOut
, FCurTexture
);
1198 e_Buffer_Write(@NetOut
, FCurFrame
);
1199 e_Buffer_Write(@NetOut
, FCurFrameCount
);
1200 e_Buffer_Write(@NetOut
, AnimLoop
);
1203 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1206 procedure MH_SEND_PanelState(PType
: Word; PID
: LongWord; ID
: Integer = NET_EVERYONE
);
1211 PANEL_WALL
, PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
1213 PANEL_LIFTUP
, PANEL_LIFTDOWN
, PANEL_LIFTLEFT
, PANEL_LIFTRIGHT
:
1219 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PSTATE
));
1220 e_Buffer_Write(@NetOut
, PType
);
1221 e_Buffer_Write(@NetOut
, PID
);
1222 e_Buffer_Write(@NetOut
, Byte(TP
.Enabled
));
1223 e_Buffer_Write(@NetOut
, TP
.LiftType
);
1225 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1230 procedure MH_SEND_TriggerSound(var T
: TTrigger
; ID
: Integer = NET_EVERYONE
);
1232 if gTriggers
= nil then Exit
;
1233 if T
.Sound
= nil then Exit
;
1235 e_Buffer_Write(@NetOut
, Byte(NET_MSG_TSOUND
));
1236 e_Buffer_Write(@NetOut
, T
.ClientID
);
1237 e_Buffer_Write(@NetOut
, Byte(T
.Sound
.IsPlaying
));
1238 e_Buffer_Write(@NetOut
, LongWord(T
.Sound
.GetPosition
));
1239 e_Buffer_Write(@NetOut
, T
.SoundPlayCount
);
1241 g_Net_Host_Send(ID
, True);
1244 procedure MH_SEND_TriggerMusic(ID
: Integer = NET_EVERYONE
);
1246 e_Buffer_Write(@NetOut
, Byte(NET_MSG_TMUSIC
));
1247 e_Buffer_Write(@NetOut
, gMusic
.Name
);
1248 e_Buffer_Write(@NetOut
, Byte(gMusic
.IsPlaying
));
1249 e_Buffer_Write(@NetOut
, LongWord(gMusic
.GetPosition
));
1250 e_Buffer_Write(@NetOut
, Byte(gMusic
.SpecPause
or gMusic
.IsPaused
));
1252 g_Net_Host_Send(ID
, True);
1257 procedure MH_SEND_MonsterSpawn(UID
: Word; ID
: Integer = NET_EVERYONE
);
1261 M
:= g_Monsters_Get(UID
);
1267 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MSPAWN
));
1268 e_Buffer_Write(@NetOut
, UID
);
1269 e_Buffer_Write(@NetOut
, MonsterType
);
1270 e_Buffer_Write(@NetOut
, MonsterState
);
1271 e_Buffer_Write(@NetOut
, MonsterAnim
);
1272 e_Buffer_Write(@NetOut
, MonsterTargetUID
);
1273 e_Buffer_Write(@NetOut
, MonsterTargetTime
);
1274 e_Buffer_Write(@NetOut
, MonsterBehaviour
);
1275 e_Buffer_Write(@NetOut
, MonsterSleep
);
1276 e_Buffer_Write(@NetOut
, MonsterHealth
);
1277 e_Buffer_Write(@NetOut
, MonsterAmmo
);
1278 e_Buffer_Write(@NetOut
, GameX
);
1279 e_Buffer_Write(@NetOut
, GameY
);
1280 e_Buffer_Write(@NetOut
, GameVelX
);
1281 e_Buffer_Write(@NetOut
, GameVelY
);
1282 e_Buffer_Write(@NetOut
, Byte(GameDirection
));
1285 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1288 procedure MH_SEND_MonsterPos(UID
: Word; ID
: Integer = NET_EVERYONE
);
1292 M
:= g_Monsters_Get(UID
);
1293 if M
= nil then Exit
;
1295 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MPOS
));
1296 e_Buffer_Write(@NetOut
, UID
);
1300 e_Buffer_Write(@NetOut
, GameX
);
1301 e_Buffer_Write(@NetOut
, GameY
);
1302 e_Buffer_Write(@NetOut
, GameVelX
);
1303 e_Buffer_Write(@NetOut
, GameVelY
);
1304 e_Buffer_Write(@NetOut
, Byte(GameDirection
));
1307 g_Net_Host_Send(ID
, False, NET_CHAN_MONSTERPOS
);
1310 procedure MH_SEND_MonsterState(UID
: Word; ForcedAnim
: Byte = 255; ID
: Integer = NET_EVERYONE
);
1314 M
:= g_Monsters_Get(UID
);
1315 if M
= nil then Exit
;
1317 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MSTATE
));
1318 e_Buffer_Write(@NetOut
, UID
);
1322 e_Buffer_Write(@NetOut
, MonsterState
);
1323 e_Buffer_Write(@NetOut
, ForcedAnim
);
1324 e_Buffer_Write(@NetOut
, MonsterTargetUID
);
1325 e_Buffer_Write(@NetOut
, MonsterTargetTime
);
1326 e_Buffer_Write(@NetOut
, MonsterSleep
);
1327 e_Buffer_Write(@NetOut
, MonsterHealth
);
1328 e_Buffer_Write(@NetOut
, MonsterAmmo
);
1329 e_Buffer_Write(@NetOut
, MonsterPain
);
1330 e_Buffer_Write(@NetOut
, Byte(AnimIsReverse
));
1333 g_Net_Host_Send(ID
, True, NET_CHAN_MONSTER
);
1336 procedure MH_SEND_MonsterShot(UID
: Word; X
, Y
, VX
, VY
: Integer; ID
: Integer = NET_EVERYONE
);
1338 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MSHOT
));
1339 e_Buffer_Write(@NetOut
, UID
);
1340 e_Buffer_Write(@NetOut
, X
);
1341 e_Buffer_Write(@NetOut
, Y
);
1342 e_Buffer_Write(@NetOut
, VX
);
1343 e_Buffer_Write(@NetOut
, VY
);
1345 g_Net_Host_Send(ID
, True, NET_CHAN_MONSTER
);
1348 procedure MH_SEND_MonsterDelete(UID
: Word; ID
: Integer = NET_EVERYONE
);
1352 M
:= g_Monsters_Get(UID
);
1353 if M
= nil then Exit
;
1355 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MDEL
));
1356 e_Buffer_Write(@NetOut
, UID
);
1358 g_Net_Host_Send(ID
, True, NET_CHAN_LARGEDATA
);
1363 procedure MH_SEND_TimeSync(Time
: LongWord; ID
: Integer = NET_EVERYONE
);
1365 e_Buffer_Write(@NetOut
, Byte(NET_MSG_TIME_SYNC
));
1366 e_Buffer_Write(@NetOut
, Time
);
1368 g_Net_Host_Send(ID
, False, NET_CHAN_SERVICE
);
1371 procedure MH_SEND_VoteEvent(EvType
: Byte;
1372 StrArg1
: string = 'a'; StrArg2
: string = 'b';
1373 IntArg1
: SmallInt = 0; IntArg2
: SmallInt = 0;
1374 ID
: Integer = NET_EVERYONE
);
1376 e_Buffer_Write(@NetOut
, Byte(NET_MSG_VOTE_EVENT
));
1377 e_Buffer_Write(@NetOut
, EvType
);
1378 e_Buffer_Write(@NetOut
, IntArg1
);
1379 e_Buffer_Write(@NetOut
, IntArg2
);
1380 e_Buffer_Write(@NetOut
, StrArg1
);
1381 e_Buffer_Write(@NetOut
, StrArg2
);
1383 g_Net_Host_Send(ID
, True, NET_CHAN_IMPORTANT
);
1386 // CLIENT MESSAGES //
1390 procedure MC_RECV_Chat(P
: Pointer);
1395 Txt
:= e_Raw_Read_String(P
);
1396 Mode
:= e_Raw_Read_Byte(P
);
1398 if Mode
<> NET_CHAT_SYSTEM
then
1400 if Mode
= NET_CHAT_PLAYER
then
1402 g_Console_Add(Txt
, True);
1403 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
1404 g_Sound_PlayEx('SOUND_GAME_RADIO');
1406 if (Mode
= NET_CHAT_TEAM
) and (gPlayer1
<> nil) then
1408 if gPlayer1
.Team
= TEAM_RED
then
1409 g_Console_Add(b_Text_Format('\r[Team] ') + Txt
, True);
1410 if gPlayer1
.Team
= TEAM_BLUE
then
1411 g_Console_Add(b_Text_Format('\b[Team] ') + Txt
, True);
1412 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt
), MSG_NOTIFY
);
1413 g_Sound_PlayEx('SOUND_GAME_RADIO');
1416 g_Console_Add(Txt
, True);
1419 procedure MC_RECV_Effect(P
: Pointer);
1427 if not gGameOn
then Exit
;
1428 Kind
:= e_Raw_Read_Byte(P
);
1429 X
:= e_Raw_Read_LongInt(P
);
1430 Y
:= e_Raw_Read_LongInt(P
);
1431 Ang
:= e_Raw_Read_SmallInt(P
);
1435 g_GFX_Spark(X
, Y
, 2 + Random(2), Ang
, 0, 0);
1439 if g_Frames_Get(ID
, 'FRAMES_TELEPORT') then
1441 Anim
:= TAnimation
.Create(ID
, False, 3);
1442 g_GFX_OnceAnim(X
, Y
, Anim
);
1446 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X
, Y
);
1451 if g_Frames_Get(ID
, 'FRAMES_EXPLODE_ROCKET') then
1453 Anim
:= TAnimation
.Create(ID
, False, 6);
1454 Anim
.Blending
:= False;
1455 g_GFX_OnceAnim(X
-64, Y
-64, Anim
);
1459 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X
, Y
);
1464 if g_Frames_Get(ID
, 'FRAMES_EXPLODE_BFG') then
1466 Anim
:= TAnimation
.Create(ID
, False, 6);
1467 Anim
.Blending
:= False;
1468 g_GFX_OnceAnim(X
-64, Y
-64, Anim
);
1472 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X
, Y
);
1477 if g_Frames_Get(ID
, 'FRAMES_BFGHIT') then
1479 Anim
:= TAnimation
.Create(ID
, False, 4);
1480 g_GFX_OnceAnim(X
-32, Y
-32, Anim
);
1487 if g_Frames_Get(ID
, 'FRAMES_FIRE') then
1489 Anim
:= TAnimation
.Create(ID
, False, 4);
1490 g_GFX_OnceAnim(X
, Y
, Anim
);
1494 g_Sound_PlayExAt('SOUND_FIRE', X
, Y
);
1499 if g_Frames_Get(ID
, 'FRAMES_ITEM_RESPAWN') then
1501 Anim
:= TAnimation
.Create(ID
, False, 4);
1502 g_GFX_OnceAnim(X
, Y
, Anim
);
1506 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X
, Y
);
1510 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_BULLET
);
1513 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1517 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1518 g_Player_CreateShell(X
, Y
, 0, -2, SHELL_SHELL
);
1523 procedure MC_RECV_Sound(P
: Pointer);
1528 Name
:= e_Raw_Read_String(P
);
1529 X
:= e_Raw_Read_LongInt(P
);
1530 Y
:= e_Raw_Read_LongInt(P
);
1531 g_Sound_PlayExAt(Name
, X
, Y
);
1534 procedure MC_RECV_CreateShot(P
: Pointer);
1536 I
, X
, Y
, XV
, YV
: Integer;
1538 Target
, Spawner
: Word;
1541 I
:= e_Raw_Read_LongInt(P
);
1542 ShType
:= e_Raw_Read_Byte(P
);
1543 Target
:= e_Raw_Read_Word(P
);
1544 Spawner
:= e_Raw_Read_Word(P
);
1545 Timeout
:= e_Raw_Read_LongWord(P
);
1546 X
:= e_Raw_Read_LongInt(P
);
1547 Y
:= e_Raw_Read_LongInt(P
);
1548 XV
:= e_Raw_Read_LongInt(P
);
1549 YV
:= e_Raw_Read_LongInt(P
);
1551 I
:= g_Weapon_CreateShot(I
, ShType
, Spawner
, Target
, X
, Y
, XV
, YV
);
1552 if (Shots
<> nil) and (I
<= High(Shots
)) then
1554 Shots
[I
].Timeout
:= Timeout
;
1555 //Shots[I].Target := Target; // TODO: find a use for Target later
1559 procedure MC_RECV_UpdateShot(P
: Pointer);
1561 I
, TX
, TY
, TXV
, TYV
: Integer;
1563 I
:= e_Raw_Read_LongInt(P
);
1564 TX
:= e_Raw_Read_LongInt(P
);
1565 TY
:= e_Raw_Read_LongInt(P
);
1566 TXV
:= e_Raw_Read_LongInt(P
);
1567 TYV
:= e_Raw_Read_LongInt(P
);
1569 if (Shots
<> nil) and (I
<= High(Shots
)) then
1579 procedure MC_RECV_DeleteShot(P
: Pointer);
1584 if not gGameOn
then Exit
;
1585 I
:= e_Raw_Read_LongInt(P
);
1586 L
:= (e_Raw_Read_Byte(P
) <> 0);
1587 X
:= e_Raw_Read_LongInt(P
);
1588 Y
:= e_Raw_Read_LongInt(P
);
1590 g_Weapon_DestroyShot(I
, X
, Y
, L
);
1593 procedure MC_RECV_GameStats(P
: Pointer);
1595 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
1597 gTeamStat
[TEAM_RED
].Goals
:= e_Raw_Read_SmallInt(P
);
1598 gTeamStat
[TEAM_BLUE
].Goals
:= e_Raw_Read_SmallInt(P
);
1601 if gGameSettings
.GameMode
= GM_COOP
then
1603 gCoopMonstersKilled
:= e_Raw_Read_Word(P
);
1604 gCoopSecretsFound
:= e_Raw_Read_Word(P
);
1608 procedure MC_RECV_CoopStats(P
: Pointer);
1610 gTotalMonsters
:= e_Raw_Read_LongInt(P
);
1611 gSecretsCount
:= e_Raw_Read_LongInt(P
);
1612 gCoopTotalMonstersKilled
:= e_Raw_Read_Word(P
);
1613 gCoopTotalSecretsFound
:= e_Raw_Read_Word(P
);
1614 gCoopTotalMonsters
:= e_Raw_Read_Word(P
);
1615 gCoopTotalSecrets
:= e_Raw_Read_Word(P
);
1618 procedure MC_RECV_GameEvent(P
: Pointer);
1627 i1
, i2
: TStrings_Locale
;
1631 FillChar(EvHash
, Sizeof(EvHash
), 0);
1632 EvType
:= e_Raw_Read_Byte(P
);
1633 EvNum
:= e_Raw_Read_LongInt(P
);
1634 EvStr
:= e_Raw_Read_String(P
);
1635 gLastMap
:= e_Raw_Read_Byte(P
) <> 0;
1636 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then gStatsOff
:= True;
1637 gStatsPressed
:= True;
1638 EvTime
:= e_Raw_Read_LongWord(P
);
1639 BHash
:= e_Raw_Read_Byte(P
) <> 0;
1641 EvHash
:= e_Raw_Read_MD5(P
);
1649 g_Game_ClearLoading();
1650 g_Game_StopAllSounds(True);
1652 gSwitchGameMode
:= Byte(EvNum
);
1653 gGameSettings
.GameMode
:= gSwitchGameMode
;
1656 if not g_Game_StartMap(EvStr
, True) then
1658 if Pos(':\', EvStr
) = 0 then
1659 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [gGameSettings
.WAD
+ ':\' + EvStr
]))
1661 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [EvStr
]));
1665 MC_SEND_FullStateRequest
;
1670 gMissionFailed
:= EvNum
<> 0;
1671 gExit
:= EXIT_ENDLEVELCUSTOM
;
1678 g_Console_Add(_lc
[I_NET_RCON_NOAUTH
], True);
1680 g_Console_Add(_lc
[I_NET_RCON_PWD_VALID
], True);
1682 g_Console_Add(_lc
[I_NET_RCON_PWD_INVALID
], True);
1688 if EvNum
= TEAM_RED
then
1689 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_RED
], [EvStr
]), True);
1690 if EvNum
= TEAM_BLUE
then
1691 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_BLUE
], [EvStr
]), True);
1695 g_Console_Add(Format(_lc
[I_PLAYER_KICK
], [EvStr
]), True);
1698 g_Console_Add(Format(_lc
[I_PLAYER_BAN
], [EvStr
]), True);
1701 g_Console_Add(Format(_lc
[I_MSG_WARMUP_START
], [EvNum
]), True);
1703 NET_EV_LMS_SURVIVOR
:
1704 g_Console_Add('*** ' + _lc
[I_MESSAGE_LMS_SURVIVOR
] + ' ***', True);
1707 g_Game_Message(AnsiUpperCase(EvStr
), Word(EvNum
));
1711 pl
:= g_Player_Get(EvNum
and $FFFF);
1716 cnt
:= (EvNum
shr 16) and $FF;
1717 if Pos('w', EvStr
) = 0 then
1720 if Pos('t', EvStr
) = 0 then
1723 if Pos('-', EvStr
) = 0 then
1725 if Pos('e', EvStr
) = 0 then
1726 i1
:= I_PLAYER_SCORE_ADD_OWN
1728 i1
:= I_PLAYER_SCORE_ADD_ENEMY
;
1731 if Pos('e', EvStr
) = 0 then
1732 i1
:= I_PLAYER_SCORE_SUB_OWN
1734 i1
:= I_PLAYER_SCORE_SUB_ENEMY
;
1737 if Pos('r', EvStr
) > 0 then
1738 i2
:= I_PLAYER_SCORE_TO_RED
1740 i2
:= I_PLAYER_SCORE_TO_BLUE
;
1741 g_Console_Add(Format(_lc
[i1
], [pln
, cnt
, _lc
[i2
]]), True);
1745 if Pos('-', EvStr
) = 0 then
1746 i1
:= I_PLAYER_SCORE_ADD_TEAM
1748 i1
:= I_PLAYER_SCORE_SUB_TEAM
;
1750 if Pos('r', EvStr
) > 0 then
1751 i2
:= I_PLAYER_SCORE_RED
1753 i2
:= I_PLAYER_SCORE_BLUE
;
1754 g_Console_Add(Format(_lc
[i1
], [_lc
[i2
], cnt
]), True);
1759 if Pos('e', EvStr
) = 0 then
1760 i1
:= I_PLAYER_SCORE_WIN_OWN
1762 i1
:= I_PLAYER_SCORE_WIN_ENEMY
;
1764 if Pos('r', EvStr
) > 0 then
1765 i2
:= I_PLAYER_SCORE_TO_RED
1767 i2
:= I_PLAYER_SCORE_TO_BLUE
;
1768 g_Console_Add(Format(_lc
[i1
], [pln
, _lc
[i2
]]), True);
1774 if EvNum
= TEAM_RED
then
1775 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_ADD
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 108);
1776 if EvNum
= TEAM_BLUE
then
1777 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_ADD
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 108);
1778 if EvNum
= -TEAM_RED
then
1779 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_SUB
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 108);
1780 if EvNum
= -TEAM_BLUE
then
1781 g_Game_Message(Format(_lc
[I_MESSAGE_SCORE_SUB
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 108);
1786 g_Player_RemoveAllCorpses
;
1787 g_Game_Message(_lc
[I_MESSAGE_LMS_START
], 144);
1791 g_Game_Message(Format(_lc
[I_MESSAGE_LMS_WIN
], [AnsiUpperCase(EvStr
)]), 144);
1795 if EvNum
= TEAM_RED
then
1796 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 144);
1797 if EvNum
= TEAM_BLUE
then
1798 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 144);
1802 g_Game_Message(_lc
[I_MESSAGE_LMS_LOSE
], 144);
1805 g_Game_Message(_lc
[I_GAME_WIN_DRAW
], 144);
1808 g_Game_Announce_KillCombo(EvNum
);
1810 NET_EV_PLAYER_TOUCH
:
1812 pl
:= g_Player_Get(EvNum
);
1820 procedure MC_RECV_FlagEvent(P
: Pointer);
1829 EvType
:= e_Raw_Read_Byte(P
);
1830 Fl
:= e_Raw_Read_Byte(P
);
1832 if Fl
= FLAG_NONE
then Exit
;
1834 Quiet
:= (e_Raw_Read_Byte(P
) <> 0);
1835 PID
:= e_Raw_Read_Word(P
);
1837 gFlags
[Fl
].State
:= e_Raw_Read_Byte(P
);
1838 gFlags
[Fl
].CaptureTime
:= e_Raw_Read_LongWord(P
);
1839 gFlags
[Fl
].Obj
.X
:= e_Raw_Read_LongInt(P
);
1840 gFlags
[Fl
].Obj
.Y
:= e_Raw_Read_LongInt(P
);
1841 gFlags
[Fl
].Obj
.Vel
.X
:= e_Raw_Read_LongInt(P
);
1842 gFlags
[Fl
].Obj
.Vel
.Y
:= e_Raw_Read_LongInt(P
);
1844 Pl
:= g_Player_Get(PID
);
1846 (EvType
<> FLAG_STATE_NORMAL
) and
1847 (EvType
<> FLAG_STATE_DROPPED
) and
1848 (EvType
<> FLAG_STATE_RETURNED
) then
1854 if Quiet
or (Pl
= nil) then Exit
;
1856 if Fl
= FLAG_RED
then
1857 s
:= _lc
[I_PLAYER_FLAG_RED
]
1859 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
1861 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_RETURN
], [AnsiUpperCase(s
)]), 144);
1864 FLAG_STATE_CAPTURED
:
1866 if (Pl
<> nil) then Pl
.SetFlag(Fl
);
1870 if Fl
= FLAG_RED
then
1871 s
:= _lc
[I_PLAYER_FLAG_RED
]
1873 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
1875 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_GET
], [Pl
.Name
, s
]), True);
1876 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_GET
], [AnsiUpperCase(s
)]), 144);
1881 if (Pl
<> nil) then Pl
.SetFlag(FLAG_NONE
);
1883 if Quiet
or (Pl
= nil) then Exit
;
1885 if Fl
= FLAG_RED
then
1886 s
:= _lc
[I_PLAYER_FLAG_RED
]
1888 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
1890 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_DROP
], [Pl
.Name
, s
]), True);
1891 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_DROP
], [AnsiUpperCase(s
)]), 144);
1896 g_Map_ResetFlag(FLAG_RED
);
1897 g_Map_ResetFlag(FLAG_BLUE
);
1898 if Quiet
or (Pl
= nil) then Exit
;
1899 Pl
.SetFlag(FLAG_NONE
);
1901 if Fl
= FLAG_RED
then
1902 s
:= _lc
[I_PLAYER_FLAG_RED
]
1904 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
1906 ts
:= Format('%.4d', [gFlags
[Fl
].CaptureTime
]);
1907 Insert('.', ts
, Length(ts
) + 1 - 3);
1908 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_CAPTURE
], [Pl
.Name
, s
, ts
]), True);
1909 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_CAPTURE
], [AnsiUpperCase(s
)]), 144);
1912 FLAG_STATE_RETURNED
:
1914 g_Map_ResetFlag(Fl
);
1917 if Fl
= FLAG_RED
then
1918 s
:= _lc
[I_PLAYER_FLAG_RED
]
1920 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
1922 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_RETURN
], [AnsiUpperCase(s
)]), 144);
1927 procedure MC_RECV_GameSettings(P
: Pointer);
1929 gGameSettings
.GameMode
:= e_Raw_Read_Byte(P
);
1930 gGameSettings
.GoalLimit
:= e_Raw_Read_Word(P
);
1931 gGameSettings
.TimeLimit
:= e_Raw_Read_Word(P
);
1932 gGameSettings
.MaxLives
:= e_Raw_Read_Byte(P
);
1933 gGameSettings
.Options
:= e_Raw_Read_LongWord(P
);
1938 function MC_RECV_PlayerCreate(P
: Pointer): Word;
1941 PName
, Model
: string;
1946 PID
:= e_Raw_Read_Word(P
);
1947 Pl
:= g_Player_Get(PID
);
1949 PName
:= e_Raw_Read_String(P
);
1950 Model
:= e_Raw_Read_String(P
);
1951 Color
.R
:= e_Raw_Read_Byte(P
);
1952 Color
.G
:= e_Raw_Read_Byte(P
);
1953 Color
.B
:= e_Raw_Read_Byte(P
);
1954 T
:= e_Raw_Read_Byte(P
);
1957 if (PID
<> NetPlrUID1
) and (PID
<> NetPlrUID2
) then
1959 if (Pl
<> nil) then Exit
;
1960 DID
:= g_Player_Create(Model
, Color
, T
, False);
1961 with g_Player_Get(DID
) do
1970 if (PID
= NetPlrUID1
) and (gPlayer1
<> nil) then begin
1971 gPlayer1
.UID
:= PID
;
1972 gPlayer1
.Model
.SetColor(Color
.R
, Color
.G
, Color
.B
);
1973 gPlayer1
.ChangeTeam(T
);
1975 if (PID
= NetPlrUID2
) and (gPlayer2
<> nil) then begin
1976 gPlayer2
.UID
:= PID
;
1977 gPlayer2
.Model
.SetColor(Color
.R
, Color
.G
, Color
.B
);
1978 gPlayer2
.ChangeTeam(T
);
1982 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [PName
]), True);
1983 e_WriteLog('NET: Player ' + PName
+ ' [' + IntToStr(PID
) + '] added.', MSG_NOTIFY
);
1987 function MC_RECV_PlayerPos(P
: Pointer): Word;
1994 TmpX
, TmpY
: Integer;
1998 GT
:= e_Raw_Read_LongWord(P
);
1999 if GT
< gTime
- NET_MAX_DIFFTIME
then
2006 PID
:= e_Raw_Read_Word(P
);
2007 Pl
:= g_Player_Get(PID
);
2009 if Pl
= nil then Exit
;
2015 FPing
:= e_Raw_Read_Word(P
);
2016 FLoss
:= e_Raw_Read_Byte(P
);
2017 kByte
:= e_Raw_Read_Word(P
);
2018 Dir
:= e_Raw_Read_Byte(P
);
2020 TmpX
:= e_Raw_Read_LongInt(P
);
2021 TmpY
:= e_Raw_Read_LongInt(P
);
2025 if (kByte
= NET_KEY_CHAT
) then
2026 PressKey(KEY_CHAT
, 10000)
2029 if LongBool(kByte
and NET_KEY_LEFT
) then PressKey(KEY_LEFT
, 10000);
2030 if LongBool(kByte
and NET_KEY_RIGHT
) then PressKey(KEY_RIGHT
, 10000);
2031 if LongBool(kByte
and NET_KEY_UP
) then PressKey(KEY_UP
, 10000);
2032 if LongBool(kByte
and NET_KEY_DOWN
) then PressKey(KEY_DOWN
, 10000);
2033 if LongBool(kByte
and NET_KEY_JUMP
) then PressKey(KEY_JUMP
, 10000);
2036 if ((Pl
<> gPlayer1
) and (Pl
<> gPlayer2
)) or LongBool(kByte
and NET_KEY_FORCEDIR
) then
2037 SetDirection(TDirection(Dir
));
2039 GameVelX
:= e_Raw_Read_LongInt(P
);
2040 GameVelY
:= e_Raw_Read_LongInt(P
);
2041 GameAccelX
:= e_Raw_Read_LongInt(P
);
2042 GameAccelY
:= e_Raw_Read_LongInt(P
);
2043 SetLerp(TmpX
, TmpY
);
2044 if NetForcePlayerUpdate
then Update();
2048 function MC_RECV_PlayerStats(P
: Pointer): Word;
2056 PID
:= e_Raw_Read_Word(P
);
2057 Pl
:= g_Player_Get(PID
);
2064 Live
:= (e_Raw_Read_Byte(P
) <> 0);
2065 GodMode
:= (e_Raw_Read_Byte(P
) <> 0);
2066 Health
:= e_Raw_Read_LongInt(P
);
2067 Armor
:= e_Raw_Read_LongInt(P
);
2068 Air
:= e_Raw_Read_LongInt(P
);
2069 JetFuel
:= e_Raw_Read_LongInt(P
);
2070 Lives
:= e_Raw_Read_Byte(P
);
2071 NewTeam
:= e_Raw_Read_Byte(P
);
2073 for I
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
2074 FWeapon
[I
] := (e_Raw_Read_Byte(P
) <> 0);
2076 for I
:= A_BULLETS
to A_CELLS
do
2077 FAmmo
[I
] := e_Raw_Read_Word(P
);
2079 for I
:= A_BULLETS
to A_CELLS
do
2080 FMaxAmmo
[I
] := e_Raw_Read_Word(P
);
2082 for I
:= MR_SUIT
to MR_MAX
do
2083 FMegaRulez
[I
] := e_Raw_Read_LongWord(P
);
2086 if (e_Raw_Read_Byte(P
) <> 0) then
2087 FRulez
:= FRulez
+ [R_ITEM_BACKPACK
];
2088 if (e_Raw_Read_Byte(P
) <> 0) then
2089 FRulez
:= FRulez
+ [R_KEY_RED
];
2090 if (e_Raw_Read_Byte(P
) <> 0) then
2091 FRulez
:= FRulez
+ [R_KEY_GREEN
];
2092 if (e_Raw_Read_Byte(P
) <> 0) then
2093 FRulez
:= FRulez
+ [R_KEY_BLUE
];
2094 if (e_Raw_Read_Byte(P
) <> 0) then
2095 FRulez
:= FRulez
+ [R_BERSERK
];
2097 Frags
:= e_Raw_Read_LongInt(P
);
2098 Death
:= e_Raw_Read_LongInt(P
);
2100 SetWeapon(e_Raw_Read_Byte(P
));
2102 FSpectator
:= e_Raw_Read_Byte(P
) <> 0;
2105 if Pl
= gPlayer1
then
2110 if Pl
= gPlayer2
then
2118 if (gPlayer1
= nil) and (gLMSPID1
> 0) then
2119 gPlayer1
:= g_Player_Get(gLMSPID1
);
2120 if (gPlayer2
= nil) and (gLMSPID2
> 0) then
2121 gPlayer2
:= g_Player_Get(gLMSPID2
);
2123 FGhost
:= e_Raw_Read_Byte(P
) <> 0;
2124 FPhysics
:= e_Raw_Read_Byte(P
) <> 0;
2125 FNoRespawn
:= e_Raw_Read_Byte(P
) <> 0;
2127 FJetpack
:= e_Raw_Read_Byte(P
) <> 0;
2128 if OldJet
and not FJetpack
then
2130 else if not OldJet
and FJetpack
then
2132 if Team
<> NewTeam
then
2133 Pl
.ChangeTeam(NewTeam
);
2139 function MC_RECV_PlayerDamage(P
: Pointer): Word;
2144 Attacker
, Value
: Word;
2148 if not gGameOn
then Exit
;
2149 PID
:= e_Raw_Read_Word(P
);
2150 Pl
:= g_Player_Get(PID
);
2151 if Pl
= nil then Exit
;
2153 Kind
:= e_Raw_Read_Byte(P
);
2154 Attacker
:= e_Raw_Read_Word(P
);
2155 Value
:= e_Raw_Read_Word(P
);
2156 VX
:= e_Raw_Read_Word(P
);
2157 VY
:= e_Raw_Read_Word(P
);
2160 Damage(Value
, Attacker
, VX
, VY
, Kind
);
2165 function MC_RECV_PlayerDeath(P
: Pointer): Word;
2169 KillType
, DeathType
: Byte;
2173 if not gGameOn
then Exit
;
2174 PID
:= e_Raw_Read_Word(P
);
2175 Pl
:= g_Player_Get(PID
);
2176 if Pl
= nil then Exit
;
2178 KillType
:= e_Raw_Read_Byte(P
);
2179 DeathType
:= e_Raw_Read_Byte(P
);
2180 Attacker
:= e_Raw_Read_Word(P
);
2184 Kill(KillType
, Attacker
, DeathType
);
2189 function MC_RECV_PlayerDelete(P
: Pointer): Word;
2194 PID
:= e_Raw_Read_Word(P
);
2195 Pl
:= g_Player_Get(PID
);
2197 if Pl
= nil then Exit
;
2199 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [Pl
.Name
]), True);
2200 e_WriteLog('NET: Player ' + Pl
.Name
+ ' [' + IntToStr(PID
) + '] removed.', MSG_NOTIFY
);
2202 g_Player_Remove(PID
);
2207 function MC_RECV_PlayerFire(P
: Pointer): Word;
2212 X
, Y
, AX
, AY
: Integer;
2216 if not gGameOn
then Exit
;
2217 PID
:= e_Raw_Read_Word(P
);
2218 Pl
:= g_Player_Get(PID
);
2219 if Pl
= nil then Exit
;
2221 Weap
:= e_Raw_Read_Byte(P
);
2222 X
:= e_Raw_Read_LongInt(P
);
2223 Y
:= e_Raw_Read_LongInt(P
);
2224 AX
:= e_Raw_Read_LongInt(P
);
2225 AY
:= e_Raw_Read_LongInt(P
);
2226 SHID
:= e_Raw_Read_LongInt(P
);
2229 if Live
then NetFire(Weap
, X
, Y
, AX
, AY
, SHID
);
2232 procedure MC_RECV_PlayerSettings(P
: Pointer);
2241 PID
:= e_Raw_Read_Word(P
);
2242 Pl
:= g_Player_Get(PID
);
2243 if Pl
= nil then Exit
;
2245 TmpName
:= e_Raw_Read_String(P
);
2246 TmpModel
:= e_Raw_Read_String(P
);
2247 TmpColor
.R
:= e_Raw_Read_Byte(P
);
2248 TmpColor
.G
:= e_Raw_Read_Byte(P
);
2249 TmpColor
.B
:= e_Raw_Read_Byte(P
);
2250 TmpTeam
:= e_Raw_Read_Byte(P
);
2252 if (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) and (Pl
.Team
<> TmpTeam
) then
2254 Pl
.ChangeTeam(TmpTeam
);
2255 if gPlayer1
= Pl
then
2256 gPlayer1Settings
.Team
:= TmpTeam
;
2257 if gPlayer2
= Pl
then
2258 gPlayer2Settings
.Team
:= TmpTeam
;
2260 Pl
.SetColor(TmpColor
);
2262 if Pl
.Name
<> TmpName
then
2264 g_Console_Add(Format(_lc
[I_PLAYER_NAME
], [Pl
.Name
, TmpName
]), True);
2268 if TmpModel
<> Pl
.Model
.Name
then
2269 Pl
.SetModel(TmpModel
);
2274 procedure MC_RECV_ItemSpawn(P
: Pointer);
2278 X
, Y
, VX
, VY
: Integer;
2280 Quiet
, Fall
{, Resp}: Boolean;
2283 if not gGameOn
then Exit
;
2284 ID
:= e_Raw_Read_Word(P
);
2285 Quiet
:= e_Raw_Read_Byte(P
) <> 0;
2286 T
:= e_Raw_Read_Byte(P
);
2287 Fall
:= e_Raw_Read_Byte(P
) <> 0;
2288 {Resp :=} e_Raw_Read_Byte(P
);
2289 X
:= e_Raw_Read_LongInt(P
);
2290 Y
:= e_Raw_Read_LongInt(P
);
2291 VX
:= e_Raw_Read_LongInt(P
);
2292 VY
:= e_Raw_Read_LongInt(P
);
2294 g_Items_Create(X
, Y
, T
, Fall
, False, False, ID
);
2295 gItems
[ID
].Obj
.Vel
.X
:= VX
;
2296 gItems
[ID
].Obj
.Vel
.Y
:= VY
;
2300 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X
, Y
);
2301 if g_Frames_Get(AID
, 'FRAMES_ITEM_RESPAWN') then
2303 Anim
:= TAnimation
.Create(AID
, False, 4);
2304 g_GFX_OnceAnim(X
+(gItems
[ID
].Obj
.Rect
.Width
div 2)-16, Y
+(gItems
[ID
].Obj
.Rect
.Height
div 2)-16, Anim
);
2310 procedure MC_RECV_ItemDestroy(P
: Pointer);
2315 if not gGameOn
then Exit
;
2316 ID
:= e_Raw_Read_Word(P
);
2317 Quiet
:= e_Raw_Read_Byte(P
) <> 0;
2318 if gItems
= nil then Exit
;
2319 if (ID
> High(gItems
)) then Exit
;
2322 if gSoundEffectsDF
then
2324 if gItems
[ID
].ItemType
in [ITEM_SPHERE_BLUE
, ITEM_SPHERE_WHITE
, ITEM_INVUL
,
2325 ITEM_INVIS
, ITEM_MEDKIT_BLACK
, ITEM_JETPACK
] then
2326 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2327 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
)
2329 if gItems
[ID
].ItemType
in [ITEM_WEAPON_SAW
, ITEM_WEAPON_PISTOL
, ITEM_WEAPON_SHOTGUN1
, ITEM_WEAPON_SHOTGUN2
,
2330 ITEM_WEAPON_CHAINGUN
, ITEM_WEAPON_ROCKETLAUNCHER
, ITEM_WEAPON_PLASMA
,
2331 ITEM_WEAPON_BFG
, ITEM_WEAPON_SUPERPULEMET
, ITEM_AMMO_BACKPACK
] then
2332 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2333 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
)
2335 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2336 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
);
2340 if gItems
[ID
].ItemType
in [ITEM_SPHERE_BLUE
, ITEM_SPHERE_WHITE
, ITEM_SUIT
,
2341 ITEM_MEDKIT_BLACK
, ITEM_INVUL
, ITEM_INVIS
, ITEM_JETPACK
] then
2342 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2343 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
)
2345 if gItems
[ID
].ItemType
in [ITEM_WEAPON_SAW
, ITEM_WEAPON_PISTOL
, ITEM_WEAPON_SHOTGUN1
, ITEM_WEAPON_SHOTGUN2
,
2346 ITEM_WEAPON_CHAINGUN
, ITEM_WEAPON_ROCKETLAUNCHER
, ITEM_WEAPON_PLASMA
,
2347 ITEM_WEAPON_BFG
, ITEM_WEAPON_SUPERPULEMET
] then
2348 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2349 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
)
2351 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2352 gItems
[ID
].Obj
.X
, gItems
[ID
].Obj
.Y
);
2360 procedure MC_RECV_PanelTexture(P
: Pointer);
2368 if not gGameOn
then Exit
;
2369 PType
:= e_Raw_Read_Word(P
);
2370 ID
:= e_Raw_Read_LongWord(P
);
2371 Tex
:= e_Raw_Read_LongInt(P
);
2372 Fr
:= e_Raw_Read_LongInt(P
);
2373 Cnt
:= e_Raw_Read_Byte(P
);
2374 Loop
:= e_Raw_Read_Byte(P
);
2379 PANEL_WALL
, PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
2380 if gWalls
<> nil then
2383 if gRenderForegrounds
<> nil then
2384 TP
:= gRenderForegrounds
[ID
];
2386 if gRenderBackgrounds
<> nil then
2387 TP
:= gRenderBackgrounds
[ID
];
2389 if gWater
<> nil then
2392 if gAcid1
<> nil then
2395 if gAcid2
<> nil then
2398 if gSteps
<> nil then
2406 begin // switch texture
2407 TP
.SetTexture(Tex
, Loop
);
2408 TP
.SetFrame(Fr
, Cnt
);
2409 end else // looped or non-looped animation
2410 TP
.NextTexture(Loop
);
2413 procedure MC_RECV_PanelState(P
: Pointer);
2420 if not gGameOn
then Exit
;
2421 PType
:= e_Raw_Read_Word(P
);
2422 ID
:= e_Raw_Read_LongWord(P
);
2423 E
:= (e_Raw_Read_Byte(P
) <> 0);
2424 Lift
:= e_Raw_Read_Byte(P
);
2427 PANEL_WALL
, PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
2429 g_Map_EnableWall(ID
)
2431 g_Map_DisableWall(ID
);
2433 PANEL_LIFTUP
, PANEL_LIFTDOWN
, PANEL_LIFTLEFT
, PANEL_LIFTRIGHT
:
2434 g_Map_SetLift(ID
, Lift
);
2440 procedure MC_RECV_TriggerSound(P
: Pointer);
2443 SPos
, SID
: LongWord;
2447 if not gGameOn
then Exit
;
2448 if gTriggers
= nil then Exit
;
2450 SID
:= e_Raw_Read_LongWord(P
);
2451 SPlaying
:= e_Raw_Read_Byte(P
) <> 0;
2452 SPos
:= e_Raw_Read_LongWord(P
);
2453 SCount
:= e_Raw_Read_LongInt(P
);
2455 for I
:= Low(gTriggers
) to High(gTriggers
) do
2456 if gTriggers
[I
].TriggerType
= TRIGGER_SOUND
then
2457 if gTriggers
[I
].ClientID
= SID
then
2458 with gTriggers
[I
] do
2463 Sound
.PlayVolumeAt(X
+(Width
div 2), Y
+(Height
div 2), Data
.Volume
/255.0)
2465 Sound
.PlayPanVolume((Data
.Pan
-127.0)/128.0, Data
.Volume
/255.0);
2466 Sound
.SetPosition(SPos
);
2469 if Sound
.IsPlaying
then Sound
.Stop
;
2471 SoundPlayCount
:= SCount
;
2475 procedure MC_RECV_TriggerMusic(P
: Pointer);
2482 if not gGameOn
then Exit
;
2484 MName
:= e_Raw_Read_String(P
);
2485 MPlaying
:= e_Raw_Read_Byte(P
) <> 0;
2486 MPos
:= e_Raw_Read_LongWord(P
);
2487 MPaused
:= e_Raw_Read_Byte(P
) <> 0;
2491 gMusic
.SetByName(MName
);
2493 gMusic
.SetPosition(MPos
);
2494 gMusic
.SpecPause
:= MPaused
;
2497 if gMusic
.IsPlaying
then gMusic
.Stop
;
2502 procedure MC_RECV_MonsterSpawn(P
: Pointer);
2505 MType
, MState
, MDir
, MAnim
, MBehav
: Byte;
2506 X
, Y
, VX
, VY
, MTargTime
, MHealth
, MAmmo
, MSleep
: Integer;
2510 ID
:= e_Raw_Read_Word(P
);
2511 M
:= g_Monsters_Get(ID
);
2515 MType
:= e_Raw_Read_Byte(P
);
2516 MState
:= e_Raw_Read_Byte(P
);
2517 MAnim
:= e_Raw_Read_Byte(P
);
2518 MTarg
:= e_Raw_Read_Word(P
);
2519 MTargTime
:= e_Raw_Read_LongInt(P
);
2520 MBehav
:= e_Raw_Read_Byte(P
);
2521 MSleep
:= e_Raw_Read_LongInt(P
);
2522 MHealth
:= e_Raw_Read_LongInt(P
);
2523 MAmmo
:= e_Raw_Read_LongInt(P
);
2525 X
:= e_Raw_Read_LongInt(P
);
2526 Y
:= e_Raw_Read_LongInt(P
);
2527 VX
:= e_Raw_Read_LongInt(P
);
2528 VY
:= e_Raw_Read_LongInt(P
);
2529 MDir
:= e_Raw_Read_Byte(P
);
2531 g_Monsters_Create(MType
, X
, Y
, TDirection(MDir
), False, ID
);
2532 M
:= g_Monsters_Get(ID
);
2543 MonsterAnim
:= MAnim
;
2544 MonsterTargetUID
:= MTarg
;
2545 MonsterTargetTime
:= MTargTime
;
2546 MonsterBehaviour
:= MBehav
;
2547 MonsterSleep
:= MSleep
;
2548 MonsterAmmo
:= MAmmo
;
2555 procedure MC_RECV_MonsterPos(P
: Pointer);
2560 ID
:= e_Raw_Read_Word(P
);
2561 M
:= g_Monsters_Get(ID
);
2567 GameX
:= e_Raw_Read_LongInt(P
);
2568 GameY
:= e_Raw_Read_LongInt(P
);
2569 GameVelX
:= e_Raw_Read_LongInt(P
);
2570 GameVelY
:= e_Raw_Read_LongInt(P
);
2571 GameDirection
:= TDirection(e_Raw_Read_Byte(P
));
2575 procedure MC_RECV_MonsterState(P
: Pointer);
2578 MState
, MFAnm
: Byte;
2580 AnimRevert
: Boolean;
2582 ID
:= e_Raw_Read_Word(P
);
2583 M
:= g_Monsters_Get(ID
);
2584 if M
= nil then Exit
;
2586 MState
:= e_Raw_Read_Byte(P
);
2587 MFAnm
:= e_Raw_Read_Byte(P
);
2591 MonsterTargetUID
:= e_Raw_Read_Word(P
);
2592 MonsterTargetTime
:= e_Raw_Read_LongInt(P
);
2593 MonsterSleep
:= e_Raw_Read_LongInt(P
);
2594 MonsterHealth
:= e_Raw_Read_LongInt(P
);
2595 MonsterAmmo
:= e_Raw_Read_LongInt(P
);
2596 MonsterPain
:= e_Raw_Read_LongInt(P
);
2597 AnimRevert
:= e_Raw_Read_Byte(P
) <> 0;
2598 RevertAnim(AnimRevert
);
2600 if MonsterState
<> MState
then
2602 if (MState
= MONSTATE_GO
) and (MonsterState
= MONSTATE_SLEEP
) then
2604 if (MState
= MONSTATE_DIE
) then
2606 if (MState
= MONSTATE_PAIN
) then
2607 MakeBloodSimple(Min(200, MonsterPain
));
2608 if (MState
= MONSTATE_ATTACK
) then
2610 if (MState
= MONSTATE_DEAD
) then
2613 SetState(MState
, MFAnm
);
2618 procedure MC_RECV_MonsterShot(P
: Pointer);
2622 X
, Y
, VX
, VY
: Integer;
2624 ID
:= e_Raw_Read_Word(P
);
2626 M
:= g_Monsters_Get(ID
);
2627 if M
= nil then Exit
;
2629 X
:= e_Raw_Read_LongInt(P
);
2630 Y
:= e_Raw_Read_LongInt(P
);
2631 VX
:= e_Raw_Read_LongInt(P
);
2632 VY
:= e_Raw_Read_LongInt(P
);
2634 M
.ClientAttack(X
, Y
, VX
, VY
);
2637 procedure MC_RECV_MonsterDelete(P
: Pointer);
2642 ID
:= e_Raw_Read_Word(P
);
2643 M
:= g_Monsters_Get(ID
);
2644 if M
= nil then Exit
;
2646 gMonsters
[ID
].SetState(5);
2647 gMonsters
[ID
].MonsterRemoved
:= True;
2650 procedure MC_RECV_TimeSync(P
: Pointer);
2654 Time
:= e_Raw_Read_LongWord(P
);
2656 if gState
= STATE_INTERCUSTOM
then
2657 gServInterTime
:= Min(Time
, 255);
2660 procedure MC_RECV_VoteEvent(P
: Pointer);
2664 Int1
, Int2
: SmallInt;
2666 EvID
:= e_Raw_Read_Byte(P
);
2667 Int1
:= e_Raw_Read_SmallInt(P
);
2668 Int2
:= e_Raw_Read_SmallInt(P
);
2669 Str1
:= e_Raw_Read_String(P
);
2670 Str2
:= e_Raw_Read_String(P
);
2674 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_STARTED
], [Str1
, Str2
, Int1
]), True);
2676 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_PASSED
], [Str1
]), True);
2678 g_Console_Add(_lc
[I_MESSAGE_VOTE_FAILED
], True);
2680 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_VOTE
], [Str1
, Int1
, Int2
]), True);
2682 g_Console_Add(Format(_lc
[I_MESSAGE_VOTE_INPROGRESS
], [Str1
]), True);
2688 procedure MC_SEND_Info(Password
: string);
2690 e_Buffer_Clear(@NetOut
);
2692 e_Buffer_Write(@NetOut
, Byte(NET_MSG_INFO
));
2693 e_Buffer_Write(@NetOut
, Byte(0)); // to kill old clients
2694 e_Buffer_Write(@NetOut
, Byte(NET_PROTO_VERSION
));
2695 e_Buffer_Write(@NetOut
, GAME_VERSION
);
2696 e_Buffer_Write(@NetOut
, Password
);
2697 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Name
);
2698 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Model
);
2699 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.R
);
2700 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.G
);
2701 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.B
);
2702 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Team
);
2704 g_Net_Client_Send(True, NET_CHAN_SERVICE
);
2707 procedure MC_SEND_Chat(Txt
: string; Mode
: Byte);
2709 e_Buffer_Write(@NetOut
, Byte(NET_MSG_CHAT
));
2710 e_Buffer_Write(@NetOut
, Txt
);
2711 e_Buffer_Write(@NetOut
, Mode
);
2713 g_Net_Client_Send(True, NET_CHAN_CHAT
);
2716 procedure MC_SEND_PlayerPos();
2721 if not gGameOn
then Exit
;
2722 if gPlayers
= nil then Exit
;
2723 if gPlayer1
= nil then Exit
;
2726 Predict
:= NetPredictSelf
; // and (not NetGotKeys);
2728 if (not gConsoleShow
) and (not gChatShow
) and (g_ActiveWindow
= nil) then
2729 with gGameControls
.P1Control
do
2731 if e_KeyPressed(KeyLeft
) and (not e_KeyPressed(KeyRight
)) then
2734 if (not e_KeyPressed(KeyLeft
)) and e_KeyPressed(KeyRight
) then
2737 if (not e_KeyPressed(KeyLeft
)) and (not e_KeyPressed(KeyRight
)) then
2740 if (P1MoveButton
= 2) and e_KeyPressed(KeyLeft
) then
2741 gPlayer1
.SetDirection(D_LEFT
)
2743 if (P1MoveButton
= 1) and e_KeyPressed(KeyRight
) then
2744 gPlayer1
.SetDirection(D_RIGHT
)
2746 if P1MoveButton
<> 0 then
2747 gPlayer1
.SetDirection(TDirection(P1MoveButton
-1));
2749 gPlayer1
.ReleaseKeys
;
2750 if P1MoveButton
= 1 then
2752 kByte
:= kByte
or NET_KEY_LEFT
;
2753 if Predict
then gPlayer1
.PressKey(KEY_LEFT
, 10000);
2755 if P1MoveButton
= 2 then
2757 kByte
:= kByte
or NET_KEY_RIGHT
;
2758 if Predict
then gPlayer1
.PressKey(KEY_RIGHT
, 10000);
2760 if e_KeyPressed(KeyUp
) then
2762 kByte
:= kByte
or NET_KEY_UP
;
2763 gPlayer1
.PressKey(KEY_UP
, 10000);
2765 if e_KeyPressed(KeyDown
) then
2767 kByte
:= kByte
or NET_KEY_DOWN
;
2768 gPlayer1
.PressKey(KEY_DOWN
, 10000);
2770 if e_KeyPressed(KeyJump
) then
2772 kByte
:= kByte
or NET_KEY_JUMP
;
2773 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
2775 if e_KeyPressed(KeyFire
) then kByte
:= kByte
or NET_KEY_FIRE
;
2776 if e_KeyPressed(KeyOpen
) then kByte
:= kByte
or NET_KEY_OPEN
;
2777 if e_KeyPressed(KeyNextWeapon
) then kByte
:= kByte
or NET_KEY_NW
;
2778 if e_KeyPressed(KeyPrevWeapon
) then kByte
:= kByte
or NET_KEY_PW
;
2781 kByte
:= NET_KEY_CHAT
;
2783 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRPOS
));
2784 e_Buffer_Write(@NetOut
, gTime
);
2785 e_Buffer_Write(@NetOut
, kByte
);
2786 e_Buffer_Write(@NetOut
, Byte(gPlayer1
.Direction
));
2787 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS
);
2789 //kBytePrev := kByte;
2790 //kDirPrev := gPlayer1.Direction;
2793 procedure MC_SEND_Vote(Start
: Boolean = False; Command
: string = 'a');
2795 e_Buffer_Write(@NetOut
, Byte(NET_MSG_VOTE_EVENT
));
2796 e_Buffer_Write(@NetOut
, Byte(Start
));
2797 e_Buffer_Write(@NetOut
, Command
);
2798 g_Net_Client_Send(True, NET_CHAN_IMPORTANT
);
2801 procedure MC_SEND_PlayerSettings();
2803 e_Buffer_Write(@NetOut
, Byte(NET_MSG_PLRSET
));
2804 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Name
);
2805 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Model
);
2806 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.R
);
2807 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.G
);
2808 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Color
.B
);
2809 e_Buffer_Write(@NetOut
, gPlayer1Settings
.Team
);
2811 g_Net_Client_Send(True, NET_CHAN_IMPORTANT
);
2814 procedure MC_SEND_FullStateRequest();
2816 e_Buffer_Write(@NetOut
, Byte(NET_MSG_REQFST
));
2818 g_Net_Client_Send(True, NET_CHAN_SERVICE
);
2821 procedure MC_SEND_CheatRequest(Kind
: Byte);
2823 e_Buffer_Write(@NetOut
, Byte(NET_MSG_CHEAT
));
2824 e_Buffer_Write(@NetOut
, Kind
);
2826 g_Net_Client_Send(True, NET_CHAN_IMPORTANT
);
2828 procedure MC_SEND_RCONPassword(Password
: string);
2830 e_Buffer_Write(@NetOut
, Byte(NET_MSG_RCON_AUTH
));
2831 e_Buffer_Write(@NetOut
, Password
);
2833 g_Net_Client_Send(True, NET_CHAN_SERVICE
);
2835 procedure MC_SEND_RCONCommand(Cmd
: string);
2837 e_Buffer_Write(@NetOut
, Byte(NET_MSG_RCON_CMD
));
2838 e_Buffer_Write(@NetOut
, Cmd
);
2840 g_Net_Client_Send(True, NET_CHAN_SERVICE
);
2843 // i have no idea why all this stuff is in here
2845 function ReadFile(const FileName
: TFileName
): AByte
;
2847 FileStream
: TStream
;
2850 e_WriteLog(Format('NETWORK: looking for file "%s"', [FileName
]), MSG_NOTIFY
);
2851 fname
:= findDiskWad(FileName
);
2852 if length(fname
) = 0 then
2854 e_WriteLog(Format('NETWORK: file "%s" not found!', [FileName
]), MSG_FATALERROR
);
2855 SetLength(Result
, 0);
2858 e_WriteLog(Format('NETWORK: found file "%s"', [fname
]), MSG_NOTIFY
);
2860 FileStream
:= openDiskFileRO(fname
);
2862 if FileStream
.Size
> 0 then
2864 SetLength(Result
, FileStream
.Size
);
2865 FileStream
.Read(Result
[0], FileStream
.Size
);
2872 function CreateMapDataMsg(const FileName
: TFileName
; ResList
: TStringList
): TMapDataMsg
;
2876 Result
.MsgId
:= NET_MSG_MAP_RESPONSE
;
2877 Result
.FileData
:= ReadFile(FileName
);
2878 Result
.FileSize
:= Length(Result
.FileData
);
2880 SetLength(Result
.ExternalResources
, ResList
.Count
);
2881 for i
:=0 to ResList
.Count
-1 do
2883 Result
.ExternalResources
[i
].Name
:= ResList
.Strings
[i
];
2884 Result
.ExternalResources
[i
].md5
:= MD5File(GameDir
+'/wads/'+ResList
.Strings
[i
]);
2888 procedure ResDataMsgToBytes(var bytes
: AByte
; const ResData
: TResDataMsg
);
2890 ResultStream
: TMemoryStream
;
2892 ResultStream
:= TMemoryStream
.Create
;
2894 ResultStream
.WriteBuffer(ResData
.MsgId
, SizeOf(ResData
.MsgId
)); //msgId
2895 ResultStream
.WriteBuffer(ResData
.FileSize
, SizeOf(ResData
.FileSize
)); //file size
2896 ResultStream
.WriteBuffer(ResData
.FileData
[0], ResData
.FileSize
); //file data
2898 SetLength(bytes
, ResultStream
.Size
);
2899 ResultStream
.Seek(0, soFromBeginning
);
2900 ResultStream
.ReadBuffer(bytes
[0], ResultStream
.Size
);
2905 function ResDataFromMsgStream(msgStream
: TMemoryStream
):TResDataMsg
;
2907 msgStream
.ReadBuffer(Result
.MsgId
, SizeOf(Result
.MsgId
));
2908 msgStream
.ReadBuffer(Result
.FileSize
, SizeOf(Result
.FileSize
));
2909 SetLength(Result
.FileData
, Result
.FileSize
);
2910 msgStream
.ReadBuffer(Result
.FileData
[0], Result
.FileSize
);
2913 procedure MapDataMsgToBytes(var bytes
: AByte
; const MapDataMsg
: TMapDataMsg
);
2915 ResultStream
: TMemoryStream
;
2918 resCount
:= Length(MapDataMsg
.ExternalResources
);
2920 ResultStream
:= TMemoryStream
.Create
;
2922 ResultStream
.WriteBuffer(MapDataMsg
.MsgId
, SizeOf(MapDataMsg
.MsgId
)); //msgId
2923 ResultStream
.WriteBuffer(MapDataMsg
.FileSize
, SizeOf(MapDataMsg
.FileSize
)); //file size
2924 ResultStream
.WriteBuffer(MapDataMsg
.FileData
[0], MapDataMsg
.FileSize
); //file data
2926 ResultStream
.WriteBuffer(resCount
, SizeOf(resCount
)); //res count
2927 ResultStream
.WriteBuffer(MapDataMsg
.ExternalResources
[0], resCount
*SizeOf(TExternalResourceInfo
)); //res data
2929 SetLength(bytes
, ResultStream
.Size
);
2930 ResultStream
.Seek(0, soFromBeginning
);
2931 ResultStream
.ReadBuffer(bytes
[0], ResultStream
.Size
);
2936 function MapDataFromMsgStream(msgStream
: TMemoryStream
):TMapDataMsg
;
2940 msgStream
.ReadBuffer(Result
.MsgId
, SizeOf(Result
.MsgId
));
2941 msgStream
.ReadBuffer(Result
.FileSize
, SizeOf(Result
.FileSize
)); //file size
2943 SetLength(Result
.FileData
, Result
.FileSize
);
2944 msgStream
.ReadBuffer(Result
.FileData
[0], Result
.FileSize
); //file data
2946 msgStream
.ReadBuffer(resCount
, SizeOf(resCount
)); //res count
2947 SetLength(Result
.ExternalResources
, resCount
);
2949 msgStream
.ReadBuffer(Result
.ExternalResources
[0], resCount
* SizeOf(TExternalResourceInfo
)); //res data
2952 function IsValidFileName(const S
: String): Boolean;
2954 Forbidden
: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
2959 for I
:= 1 to Length(S
) do
2960 Result
:= Result
and (not(S
[I
] in Forbidden
));
2963 function IsValidFilePath(const S
: String): Boolean;
2968 if not IsValidFileName(S
) then exit
;
2969 if FileExists(S
) then exit
;
2970 I
:= LastDelimiter('\/', S
);
2972 if (not DirectoryExists(Copy(S
, 1, I
-1))) then
2977 procedure MC_SEND_MapRequest();
2979 e_Buffer_Write(@NetOut
, Byte(NET_MSG_MAP_REQUEST
));
2980 g_Net_Client_Send(True, NET_CHAN_IMPORTANT
);
2983 procedure MC_SEND_ResRequest(const resName
: AnsiString);
2985 e_Buffer_Write(@NetOut
, Byte(NET_MSG_RES_REQUEST
));
2986 e_Buffer_Write(@NetOut
, resName
);
2987 g_Net_Client_Send(True, NET_CHAN_IMPORTANT
);
2990 procedure MH_RECV_MapRequest(C
: pTNetClient
; P
: Pointer);
2994 mapDataMsg
: TMapDataMsg
;
2996 e_WriteLog('NET: Received map request from ' +
2997 DecodeIPV4(C
.Peer
.address
.host
), MSG_NOTIFY
);
2999 mapDataMsg
:= CreateMapDataMsg(MapsDir
+ gGameSettings
.WAD
, gExternalResources
);
3000 peer
:= NetClients
[C
.ID
].Peer
;
3002 MapDataMsgToBytes(payload
, mapDataMsg
);
3003 g_Net_SendData(payload
, peer
, True, NET_CHAN_DOWNLOAD
);
3006 mapDataMsg
.FileData
:= nil;
3007 mapDataMsg
.ExternalResources
:= nil;
3010 procedure MH_RECV_ResRequest(C
: pTNetClient
; P
: Pointer);
3015 resDataMsg
: TResDataMsg
;
3017 FileName
:= ExtractFileName(e_Raw_Read_String(P
));
3018 e_WriteLog('NET: Received res request: ' + FileName
+
3019 ' from ' + DecodeIPV4(C
.Peer
.address
.host
), MSG_NOTIFY
);
3021 if not IsValidFilePath(FileName
) then
3023 e_WriteLog('Invalid filename: ' + FileName
, MSG_WARNING
);
3027 peer
:= NetClients
[C
.ID
].Peer
;
3029 if gExternalResources
.IndexOf(FileName
) > -1 then
3031 resDataMsg
.MsgId
:= NET_MSG_RES_RESPONSE
;
3032 resDataMsg
.FileData
:= ReadFile(GameDir
+'/wads/'+FileName
);
3033 resDataMsg
.FileSize
:= Length(resDataMsg
.FileData
);
3035 ResDataMsgToBytes(payload
, resDataMsg
);
3036 g_Net_SendData(payload
, peer
, True, NET_CHAN_DOWNLOAD
);