DEADSOFTWARE

net: sync moving items and flags
[d2df-sdl.git] / src / game / g_netmsg.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
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.
6 *
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.
11 *
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/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_netmsg;
18 interface
20 uses e_msg, g_net, g_triggers, Classes, SysUtils, md5;
22 const
23 NET_MSG_INFO = 100;
25 NET_MSG_CHAT = 101;
26 NET_MSG_SND = 102;
27 NET_MSG_GFX = 103;
28 NET_MSG_GEVENT = 104;
29 NET_MSG_SCORE = 105;
30 NET_MSG_COOP = 106;
31 NET_MSG_FLAG = 107;
32 NET_MSG_REQFST = 108;
33 NET_MSG_GSET = 109;
34 NET_MSG_FLAGPOS= 110;
36 NET_MSG_PLR = 111;
37 NET_MSG_PLRPOS = 112;
38 NET_MSG_PLRSTA = 113;
39 NET_MSG_PLRDEL = 114;
40 NET_MSG_PLRDMG = 115;
41 NET_MSG_PLRDIE = 116;
42 NET_MSG_PLRFIRE= 117;
43 NET_MSG_PLRSET = 119;
44 NET_MSG_CHEAT = 120;
46 NET_MSG_ISPAWN = 121;
47 NET_MSG_IDEL = 122;
48 NET_MSG_IPOS = 123;
50 NET_MSG_MSPAWN = 131;
51 NET_MSG_MPOS = 132;
52 NET_MSG_MSTATE = 133;
53 NET_MSG_MSHOT = 134;
54 NET_MSG_MDEL = 135;
56 NET_MSG_PSTATE = 141;
57 NET_MSG_PTEX = 142;
59 NET_MSG_TSOUND = 151;
60 NET_MSG_TMUSIC = 152;
62 NET_MSG_SHDEL = 161;
63 NET_MSG_SHADD = 162;
64 NET_MSG_SHPOS = 163;
66 NET_MSG_RCON_AUTH = 191;
67 NET_MSG_RCON_CMD = 192;
68 NET_MSG_TIME_SYNC = 194;
69 NET_MSG_VOTE_EVENT = 195;
71 {
72 NET_MSG_MAP_REQUEST = 201;
73 NET_MSG_MAP_RESPONSE = 202;
74 NET_MSG_RES_REQUEST = 203;
75 NET_MSG_RES_RESPONSE = 204;
76 }
78 NET_CHAT_SYSTEM = 0;
79 NET_CHAT_PLAYER = 1;
80 NET_CHAT_TEAM = 2;
82 NET_RCON_NOAUTH = 0;
83 NET_RCON_PWGOOD = 1;
84 NET_RCON_PWBAD = 2;
86 NET_GFX_SPARK = 1;
87 NET_GFX_TELE = 2;
88 NET_GFX_RESPAWN = 3;
89 NET_GFX_FIRE = 4;
90 NET_GFX_EXPLODE = 5;
91 NET_GFX_BFGEXPL = 6;
92 NET_GFX_BFGHIT = 7;
93 NET_GFX_SHELL1 = 8;
94 NET_GFX_SHELL2 = 9;
95 NET_GFX_SHELL3 = 10;
97 NET_EV_MAPSTART = 1;
98 NET_EV_MAPEND = 2;
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;
104 NET_EV_RCON = 8;
105 NET_EV_BIGTEXT = 9;
106 NET_EV_SCORE = 10;
107 NET_EV_SCORE_MSG = 11;
108 NET_EV_LMS_START = 12;
109 NET_EV_LMS_WIN = 13;
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;
115 NET_EV_SECRET = 19;
116 NET_EV_INTER_READY = 20;
117 NET_EV_LMS_NOSPAWN = 21;
119 NET_VE_STARTED = 1;
120 NET_VE_PASSED = 2;
121 NET_VE_FAILED = 3;
122 NET_VE_VOTE = 4;
123 NET_VE_REVOKE = 5;
124 NET_VE_INPROGRESS = 6;
126 NET_FLAG_GET = 1;
127 NET_FLAG_DROP = 2;
128 NET_FLAG_CAP = 3;
129 NET_FLAG_RETURN = 4;
131 NET_CHEAT_SUICIDE = 1;
132 NET_CHEAT_SPECTATE = 2;
133 NET_CHEAT_READY = 3;
134 NET_CHEAT_DROPFLAG = 4;
136 NET_MAX_DIFFTIME = 5000 div 36;
138 // HOST MESSAGES
140 procedure MH_ProcessFirstSpawn (C: pTNetClient);
142 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
143 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
144 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
145 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
146 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
147 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
148 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
149 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
150 //procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg);
151 //procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg);
152 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
154 // GAME
155 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
156 procedure MH_SEND_Info(ID: Byte);
157 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
158 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
159 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
160 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
162 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
163 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
165 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
166 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
167 procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
168 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
169 // PLAYER
170 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
171 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
172 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
173 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
174 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
175 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
176 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
177 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
178 // ITEM
179 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
180 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
181 procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
182 // PANEL
183 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
184 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
185 // MONSTER
186 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
187 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
188 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
189 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
190 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
191 // TRIGGER
192 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
193 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
194 // MISC
195 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
196 procedure MH_SEND_VoteEvent(EvType: Byte;
197 StrArg1: string = 'a'; StrArg2: string = 'b';
198 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
199 ID: Integer = NET_EVERYONE);
201 // CLIENT MESSAGES //
203 // GAME
204 procedure MC_RECV_Chat(var M: TMsg);
205 procedure MC_RECV_Effect(var M: TMsg);
206 procedure MC_RECV_Sound(var M: TMsg);
207 procedure MC_RECV_GameStats(var M: TMsg);
208 procedure MC_RECV_CoopStats(var M: TMsg);
209 procedure MC_RECV_GameEvent(var M: TMsg);
210 procedure MC_RECV_FlagEvent(var M: TMsg);
211 procedure MC_RECV_FlagPos(var M: TMsg);
212 procedure MC_RECV_GameSettings(var M: TMsg);
213 // PLAYER
214 function MC_RECV_PlayerCreate(var M: TMsg): Word;
215 function MC_RECV_PlayerPos(var M: TMsg): Word;
216 function MC_RECV_PlayerStats(var M: TMsg): Word;
217 function MC_RECV_PlayerDelete(var M: TMsg): Word;
218 function MC_RECV_PlayerDamage(var M: TMsg): Word;
219 function MC_RECV_PlayerDeath(var M: TMsg): Word;
220 function MC_RECV_PlayerFire(var M: TMsg): Word;
221 procedure MC_RECV_PlayerSettings(var M: TMsg);
222 // ITEM
223 procedure MC_RECV_ItemSpawn(var M: TMsg);
224 procedure MC_RECV_ItemDestroy(var M: TMsg);
225 procedure MC_RECV_ItemPos(var M: TMsg);
226 // PANEL
227 procedure MC_RECV_PanelTexture(var M: TMsg);
228 procedure MC_RECV_PanelState(var M: TMsg);
229 // MONSTER
230 procedure MC_RECV_MonsterSpawn(var M: TMsg);
231 procedure MC_RECV_MonsterPos(var M: TMsg);
232 procedure MC_RECV_MonsterState(var M: TMsg);
233 procedure MC_RECV_MonsterShot(var M: TMsg);
234 procedure MC_RECV_MonsterDelete(var M: TMsg);
235 // SHOT
236 procedure MC_RECV_CreateShot(var M: TMsg);
237 procedure MC_RECV_UpdateShot(var M: TMsg);
238 procedure MC_RECV_DeleteShot(var M: TMsg);
239 // TRIGGER
240 procedure MC_RECV_TriggerSound(var M: TMsg);
241 procedure MC_RECV_TriggerMusic(var M: TMsg);
242 // MISC
243 procedure MC_RECV_TimeSync(var M: TMsg);
244 procedure MC_RECV_VoteEvent(var M: TMsg);
245 // SERVICE
246 procedure MC_SEND_Info(Password: string);
247 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
248 procedure MC_SEND_PlayerPos();
249 procedure MC_SEND_FullStateRequest();
250 procedure MC_SEND_PlayerSettings();
251 procedure MC_SEND_CheatRequest(Kind: Byte);
252 procedure MC_SEND_RCONPassword(Password: string);
253 procedure MC_SEND_RCONCommand(Cmd: string);
254 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
255 // DOWNLOAD
256 //procedure MC_SEND_MapRequest();
257 //procedure MC_SEND_ResRequest(const resName: AnsiString);
260 type
261 TExternalResourceInfo = record
262 Name: string[255];
263 md5: TMD5Digest;
264 end;
266 TResDataMsg = record
267 MsgId: Byte;
268 FileSize: Integer;
269 FileData: AByte;
270 end;
272 TMapDataMsg = record
273 MsgId: Byte;
274 FileSize: Integer;
275 FileData: AByte;
276 ExternalResources: array of TExternalResourceInfo;
277 end;
279 function IsValidFileName(const S: String): Boolean;
280 function IsValidFilePath(const S: String): Boolean;
283 implementation
285 uses
286 Math, ENet, e_input, e_graphics, e_log,
287 g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main,
288 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
289 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
291 const
292 NET_KEY_LEFT = 1 shl 0;
293 NET_KEY_RIGHT = 1 shl 1;
294 NET_KEY_UP = 1 shl 2;
295 NET_KEY_DOWN = 1 shl 3;
296 NET_KEY_JUMP = 1 shl 4;
297 NET_KEY_FIRE = 1 shl 5;
298 NET_KEY_OPEN = 1 shl 6;
299 NET_KEY_CHAT = 1 shl 7;
300 NET_KEY_FORCEDIR = 1 shl 8;
302 //var
303 //kBytePrev: Word = 0;
304 //kDirPrev: TDirection = D_LEFT;
305 //HostGameTime: Word = 0;
308 function IsValidFileName(const S: String): Boolean;
309 const
310 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
311 var
312 I: Integer;
313 begin
314 Result := S <> '';
315 for I := 1 to Length(S) do
316 Result := Result and (not(S[I] in Forbidden));
317 end;
319 function IsValidFilePath(const S: String): Boolean;
320 var
321 I: Integer;
322 begin
323 Result := False;
324 if not IsValidFileName(S) then exit;
325 if FileExists(S) then exit;
326 I := LastDelimiter('\/', S);
327 if (I > 0) then
328 if (not DirectoryExists(Copy(S, 1, I-1))) then
329 exit;
330 Result := True;
331 end;
334 // HOST MESSAGES //
337 // GAME
339 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
340 var
341 Txt: string;
342 Mode: Byte;
343 PID: Word;
344 Pl: TPlayer;
345 begin
346 PID := C^.Player;
347 Pl := g_Player_Get(PID);
349 Txt := M.ReadString();
350 Mode := M.ReadByte();
351 if (Mode = NET_CHAT_SYSTEM) then
352 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
353 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
354 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
356 if Pl = nil then
357 MH_SEND_Chat(Txt, Mode)
358 else
359 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
360 end;
362 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
363 var
364 Ver, PName, Model, Pw: string;
365 R, G, B, T: Byte;
366 PID: Word;
367 Color: TRGB;
368 I: Integer;
369 begin
370 Ver := M.ReadString();
371 Pw := M.ReadString();
372 PName := M.ReadString();
373 Model := M.ReadString();
374 R := M.ReadByte();
375 G := M.ReadByte();
376 B := M.ReadByte();
377 T := M.ReadByte();
379 if Ver <> GAME_VERSION then
380 begin
381 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
382 _lc[I_NET_DISC_VERSION]);
383 enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
384 Exit;
385 end;
387 if g_Net_IsHostBanned(C^.Peer^.address.host) then
388 begin
389 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
390 begin
391 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
392 _lc[I_NET_DISC_BAN]);
393 enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
394 end
395 else
396 begin
397 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
398 _lc[I_NET_DISC_BAN]);
399 enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
400 end;
401 Exit;
402 end;
404 if NetPassword <> '' then
405 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
406 begin
407 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
408 _lc[I_NET_DISC_PASSWORD]);
409 enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
410 Exit;
411 end;
413 Color.R := R;
414 Color.B := B;
415 Color.G := G;
417 PID := g_Player_Create(Model, Color, T, False);
418 with g_Player_Get(PID) do
419 begin
420 Name := PName;
421 Reset(True);
422 end;
424 C^.Player := PID;
425 C^.WaitForFirstSpawn := false;
427 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
428 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
429 '] connected. Assigned player #' + IntToStr(PID) + '.', TMsgType.Notify);
431 MH_SEND_Info(C^.ID);
433 with g_Player_Get(PID) do
434 begin
435 Name := PName;
436 FClientID := C^.ID;
437 // round in progress, don't spawn
438 e_LogWritefln('*** client #%u (cid #%u) authenticated...', [C.ID, C.Player]);
439 //e_LogWritefln('spawning player with pid #%u...', [PID]);
440 //Respawn(gGameSettings.GameType = GT_SINGLE);
441 //k8: no, do not spawn a player yet, wait for "request full state" packet
442 Lives := 0;
443 Spectate;
444 FNoRespawn := True;
445 // `FWantsInGame` seems to mean "spawn the player on the next occasion".
446 // that is, if we'll set it to `true`, the player can be spawned after
447 // warmup time ran out, for example, regardless of the real player state.
448 // also, this seems to work only for the initial connection. further
449 // map changes could initiate resource downloading, but the player will
450 // be spawned immediately.
451 // the proper solution will require another player state, "ephemeral".
452 // the player should start any map in "ephemeral" state, and turned into
453 // real mobj only when they sent a special "i am ready" packet. this packet
454 // must be sent after receiving the full state, so the player will get a full
455 // map view before going into game.
456 FWantsInGame := false;
457 C^.WaitForFirstSpawn := true;
458 end;
460 //if not C^.WaitForFirstSpawn then
461 begin
462 for I := Low(NetClients) to High(NetClients) do
463 begin
464 if NetClients[I].ID = C^.ID then Continue;
465 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
466 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
467 MH_SEND_PlayerStats(PID, NetClients[I].ID);
468 end;
469 end;
471 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
472 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
474 if NetUseMaster then
475 begin
476 //g_Net_Slist_Update;
477 g_Net_Slist_Pulse();
478 end;
479 end;
482 procedure MH_ProcessFirstSpawn (C: pTNetClient);
483 var
484 plr: TPlayer;
485 begin
486 if not C.WaitForFirstSpawn then exit;
487 plr := g_Player_Get(C^.Player);
488 if not assigned(plr) then exit;
489 g_Net_Slist_ServerPlayerComes();
490 e_LogWritefln('*** client #%u (cid #%u) first spawn', [C.ID, C.Player]);
491 C.WaitForFirstSpawn := false;
492 plr.FNoRespawn := false;
493 plr.FWantsInGame := true; // TODO: look into this later
495 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
496 begin
497 plr.Spectate;
498 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, 0, 'N', C.ID);
499 end
500 else
501 begin
502 plr.Respawn(False);
503 if gLMSRespawn > LMS_RESPAWN_NONE then
504 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime, 'N', C.ID);
505 end;
506 end;
509 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
510 begin
511 //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
512 if gGameOn then
513 begin
514 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
515 end
516 else
517 begin
518 C^.RequestedFullUpdate := True;
519 end;
520 end;
522 // PLAYER
524 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
525 var
526 Dir, i: Byte;
527 WeaponAct: Byte;
528 WeaponSelect: Word;
529 PID: Word;
530 kByte: Word;
531 Pl: TPlayer;
532 GT: LongWord;
533 begin
534 Result := 0;
535 if not gGameOn then Exit;
537 GT := M.ReadLongWord();
538 PID := C^.Player;
539 Pl := g_Player_Get(PID);
540 if Pl = nil then
541 Exit;
543 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
545 with Pl do
546 begin
547 NetTime := GT;
548 kByte := M.ReadWord();
549 Dir := M.ReadByte();
550 WeaponAct := M.ReadByte();
551 WeaponSelect := M.ReadWord();
552 //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
553 if Direction <> TDirection(Dir) then
554 JustTeleported := False;
556 SetDirection(TDirection(Dir));
557 ReleaseKeys;
559 if kByte = NET_KEY_CHAT then
560 begin
561 PressKey(KEY_CHAT, 10000);
562 Exit;
563 end;
565 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
566 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
567 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
568 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
569 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
570 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
571 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
573 for i := 0 to 7 do
574 begin
575 if (WeaponAct and Byte(1 shl i)) <> 0 then
576 begin
577 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
578 ProcessWeaponAction(i);
579 end;
580 end;
582 for i := 0 to 15 do
583 begin
584 if (WeaponSelect and Word(1 shl i)) <> 0 then
585 begin
586 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
587 QueueWeaponSwitch(i);
588 end;
589 end;
590 end;
592 // MH_SEND_PlayerPos(False, PID, C^.ID);
593 end;
595 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
596 var
597 CheatKind: Byte;
598 Pl: TPlayer;
599 begin
600 Pl := g_Player_Get(C^.Player);
601 if Pl = nil then Exit;
603 CheatKind := M.ReadByte();
605 case CheatKind of
606 NET_CHEAT_SUICIDE:
607 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
608 NET_CHEAT_SPECTATE:
609 begin
610 if Pl.FSpectator then
611 begin
612 if (gGameSettings.MaxLives = 0) or (gLMSRespawn > LMS_RESPAWN_NONE) then
613 Pl.Respawn(False)
614 else
615 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, Pl.UID);
616 end
617 else
618 Pl.Spectate;
619 end;
620 NET_CHEAT_READY:
621 begin
622 if gState <> STATE_INTERCUSTOM then Exit;
623 Pl.FReady := not Pl.FReady;
624 if Pl.FReady then
625 begin
626 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'Y');
627 Inc(gInterReadyCount);
628 end
629 else
630 begin
631 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'N');
632 Dec(gInterReadyCount);
633 end;
634 end;
635 NET_CHEAT_DROPFLAG:
636 Pl.TryDropFlag();
637 end;
638 end;
640 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
641 var
642 TmpName: string;
643 TmpModel: string;
644 TmpColor: TRGB;
645 TmpTeam: Byte;
646 Pl: TPlayer;
647 begin
648 TmpName := M.ReadString();
649 TmpModel := M.ReadString();
650 TmpColor.R := M.ReadByte();
651 TmpColor.G := M.ReadByte();
652 TmpColor.B := M.ReadByte();
653 TmpTeam := M.ReadByte();
655 Pl := g_Player_Get(C^.Player);
656 if Pl = nil then Exit;
658 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
659 Pl.SwitchTeam
660 else
661 Pl.SetColor(TmpColor);
663 if Pl.Name <> TmpName then
664 begin
665 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
666 Pl.Name := TmpName;
667 end;
669 if TmpModel <> Pl.Model.Name then
670 Pl.SetModel(TmpModel);
672 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
673 end;
675 // RCON
677 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
678 var
679 Pwd: string;
680 begin
681 Pwd := M.ReadString();
682 if not NetAllowRCON then Exit;
683 if Pwd = NetRCONPassword then
684 begin
685 C^.RCONAuth := True;
686 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
687 end
688 else
689 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
690 end;
692 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
693 var
694 Cmd: string;
695 begin
696 Cmd := M.ReadString();
697 if not NetAllowRCON then Exit;
698 if not C^.RCONAuth then
699 begin
700 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
701 Exit;
702 end;
703 g_Console_Process(Cmd);
704 end;
706 // MISC
708 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
709 var
710 Start: Boolean;
711 Name, Command: string;
712 Need: Integer;
713 Pl: TPlayer;
714 begin
715 Start := M.ReadByte() <> 0;
716 Command := M.ReadString();
718 Pl := g_Player_Get(C^.Player);
719 if Pl = nil then Exit;
720 Name := Pl.Name;
722 if Start then
723 begin
724 if not g_Console_CommandBlacklisted(Command) then
725 g_Game_StartVote(Command, Name);
726 end
727 else if gVoteInProgress then
728 begin
729 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
730 Need := Floor((NetClientCount+1)/2.0) + 1
731 else
732 Need := Floor(NetClientCount/2.0) + 1;
733 if C^.Voted then
734 begin
735 Dec(gVoteCount);
736 C^.Voted := False;
737 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
738 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
739 end
740 else
741 begin
742 Inc(gVoteCount);
743 C^.Voted := True;
744 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
745 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
746 g_Game_CheckVote;
747 end;
748 end;
749 end;
751 // GAME (SEND)
753 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
755 function sendItemRespawn (it: PItem): Boolean;
756 begin
757 result := false; // don't stop
758 MH_SEND_ItemSpawn(True, it.myid, ID);
759 end;
761 function sendMonSpawn (mon: TMonster): Boolean;
762 begin
763 result := false; // don't stop
764 MH_SEND_MonsterSpawn(mon.UID, ID);
765 end;
767 function sendPanelState (pan: TPanel): Boolean;
768 begin
769 result := false; // don't stop
770 MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
771 if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
772 end;
774 var
775 I: Integer;
776 begin
777 if (ID >= 0) and (ID < length(NetClients)) then
778 begin
779 e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
780 MH_ProcessFirstSpawn(@NetClients[ID]);
781 end;
783 if gPlayers <> nil then
784 begin
785 for I := Low(gPlayers) to High(gPlayers) do
786 begin
787 if gPlayers[I] <> nil then
788 begin
789 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
790 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
791 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
793 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
794 begin
795 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
796 end;
797 end;
798 end;
799 end;
801 g_Items_ForEachAlive(sendItemRespawn, true); // backwards
802 g_Mons_ForEach(sendMonSpawn);
803 g_Map_ForEachPanel(sendPanelState);
805 if gTriggers <> nil then
806 begin
807 for I := Low(gTriggers) to High(gTriggers) do
808 begin
809 if gTriggers[I].TriggerType = TRIGGER_SOUND then
810 begin
811 MH_SEND_TriggerSound(gTriggers[I], ID);
812 end;
813 end;
814 end;
816 if Shots <> nil then
817 begin
818 for I := Low(Shots) to High(Shots) do
819 begin
820 if Shots[i].ShotType in [6, 7, 8] then
821 begin
822 MH_SEND_CreateShot(i, ID);
823 end;
824 end;
825 end;
827 MH_SEND_TriggerMusic(ID);
829 MH_SEND_GameStats(ID);
830 MH_SEND_CoopStats(ID);
832 if gGameSettings.GameMode = GM_CTF then
833 begin
834 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
835 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
836 end;
838 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
840 g_Net_Flush();
841 end;
843 procedure MH_SEND_Info(ID: Byte);
844 begin
845 NetOut.Clear();
847 NetOut.Write(Byte(NET_MSG_INFO));
848 NetOut.Write(ID);
849 NetOut.Write(NetClients[ID].Player);
850 NetOut.Write(ExtractFileName(gGameSettings.WAD));
851 NetOut.Write(g_ExtractFileName(gMapInfo.Map));
852 NetOut.Write(gWADHash);
853 NetOut.Write(gGameSettings.GameMode);
854 NetOut.Write(gGameSettings.GoalLimit);
855 NetOut.Write(gGameSettings.TimeLimit);
856 NetOut.Write(gGameSettings.MaxLives);
857 NetOut.Write(gGameSettings.Options);
858 NetOut.Write(gTime);
860 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
861 end;
863 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
864 var
865 Name: string;
866 i: Integer;
867 Team: Byte;
868 begin
869 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
870 Mode := NET_CHAT_PLAYER;
872 Team := 0;
873 if (Mode = NET_CHAT_TEAM) then
874 begin
875 for i := Low(gPlayers) to High(gPlayers) do
876 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
877 (gPlayers[i].Team = ID) then
878 begin
879 NetOut.Write(Byte(NET_MSG_CHAT));
880 NetOut.Write(Txt);
881 NetOut.Write(Mode);
882 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
883 end;
884 Team := ID;
885 ID := NET_EVERYONE;
886 end
887 else
888 begin
889 NetOut.Write(Byte(NET_MSG_CHAT));
890 NetOut.Write(Txt);
891 NetOut.Write(Mode);
892 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
893 end;
895 if Mode = NET_CHAT_SYSTEM then
896 Exit;
898 if ID = NET_EVERYONE then
899 begin
900 if Mode = NET_CHAT_PLAYER then
901 begin
902 g_Console_Add(Txt, True);
903 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
904 g_Game_ChatSound(b_Text_Unformat(Txt));
905 end
906 else
907 if Mode = NET_CHAT_TEAM then
908 if gPlayer1 <> nil then
909 begin
910 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
911 begin
912 g_Console_Add(#18'[Team] '#2 + Txt, True);
913 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
914 g_Game_ChatSound(b_Text_Unformat(Txt));
915 end
916 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
917 begin
918 g_Console_Add(#20'[Team] '#2 + Txt, True);
919 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
920 g_Game_ChatSound(b_Text_Unformat(Txt));
921 end;
922 end;
923 end
924 else
925 begin
926 Name := g_Net_ClientName_ByID(ID);
927 g_Console_Add('-> ' + Name + ': ' + Txt, True);
928 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), TMsgType.Notify);
929 g_Game_ChatSound(b_Text_Unformat(Txt), False);
930 end;
931 end;
933 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
934 begin
935 NetOut.Write(Byte(NET_MSG_GFX));
936 NetOut.Write(Kind);
937 NetOut.Write(X);
938 NetOut.Write(Y);
939 NetOut.Write(Ang);
941 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
942 end;
944 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
945 begin
946 NetOut.Write(Byte(NET_MSG_SND));
947 NetOut.Write(Name);
948 if Pos then
949 begin
950 NetOut.Write(Byte(1));
951 NetOut.Write(X);
952 NetOut.Write(Y);
953 end
954 else
955 NetOut.Write(Byte(0));
957 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
958 end;
960 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
961 begin
962 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
964 NetOut.Write(Byte(NET_MSG_SHADD));
965 NetOut.Write(Proj);
966 NetOut.Write(Shots[Proj].ShotType);
967 NetOut.Write(Shots[Proj].Target);
968 NetOut.Write(Shots[Proj].SpawnerUID);
969 NetOut.Write(Shots[Proj].Timeout);
970 NetOut.Write(Shots[Proj].Obj.X);
971 NetOut.Write(Shots[Proj].Obj.Y);
972 NetOut.Write(Shots[Proj].Obj.Vel.X);
973 NetOut.Write(Shots[Proj].Obj.Vel.Y);
975 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
976 end;
978 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
979 begin
980 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
982 NetOut.Write(Byte(NET_MSG_SHPOS));
983 NetOut.Write(Proj);
984 NetOut.Write(Shots[Proj].Obj.X);
985 NetOut.Write(Shots[Proj].Obj.Y);
986 NetOut.Write(Shots[Proj].Obj.Vel.X);
987 NetOut.Write(Shots[Proj].Obj.Vel.Y);
989 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
990 end;
992 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
993 begin
994 NetOut.Write(Byte(NET_MSG_SHDEL));
995 NetOut.Write(Proj);
996 NetOut.Write(Byte(Loud));
997 NetOut.Write(X);
998 NetOut.Write(Y);
1000 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1001 end;
1003 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
1004 begin
1005 NetOut.Write(Byte(NET_MSG_SCORE));
1006 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1007 begin
1008 NetOut.Write(gTeamStat[TEAM_RED].Goals);
1009 NetOut.Write(gTeamStat[TEAM_BLUE].Goals);
1010 end
1011 else
1012 if gGameSettings.GameMode = GM_COOP then
1013 begin
1014 NetOut.Write(gCoopMonstersKilled);
1015 NetOut.Write(gCoopSecretsFound);
1016 end;
1018 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1019 end;
1021 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
1022 begin
1023 NetOut.Write(Byte(NET_MSG_COOP));
1024 NetOut.Write(gTotalMonsters);
1025 NetOut.Write(gSecretsCount);
1026 NetOut.Write(gCoopTotalMonstersKilled);
1027 NetOut.Write(gCoopTotalSecretsFound);
1028 NetOut.Write(gCoopTotalMonsters);
1029 NetOut.Write(gCoopTotalSecrets);
1030 end;
1032 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
1033 begin
1034 NetOut.Write(Byte(NET_MSG_GEVENT));
1035 NetOut.Write(EvType);
1036 NetOut.Write(EvNum);
1037 NetOut.Write(EvStr);
1038 NetOut.Write(Byte(gLastMap));
1039 NetOut.Write(gTime);
1040 if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then
1041 begin
1042 NetOut.Write(Byte(1));
1043 NetOut.Write(gWADHash);
1044 end else
1045 NetOut.Write(Byte(0));
1047 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
1048 end;
1050 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
1051 begin
1052 NetOut.Write(Byte(NET_MSG_FLAG));
1053 NetOut.Write(EvType);
1054 NetOut.Write(Flag);
1055 NetOut.Write(Byte(Quiet));
1056 NetOut.Write(PID);
1057 NetOut.Write(gFlags[Flag].State);
1058 NetOut.Write(gFlags[Flag].CaptureTime);
1059 NetOut.Write(gFlags[Flag].Obj.X);
1060 NetOut.Write(gFlags[Flag].Obj.Y);
1061 NetOut.Write(gFlags[Flag].Obj.Vel.X);
1062 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
1063 NetOut.Write(Byte(gFlags[Flag].Direction));
1065 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1066 end;
1068 procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
1069 begin
1070 NetOut.Write(Byte(NET_MSG_FLAGPOS));
1071 NetOut.Write(Flag);
1072 NetOut.Write(gFlags[Flag].Obj.X);
1073 NetOut.Write(gFlags[Flag].Obj.Y);
1074 NetOut.Write(gFlags[Flag].Obj.Vel.X);
1075 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
1077 g_Net_Host_Send(ID, False, NET_CHAN_IMPORTANT);
1078 end;
1080 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
1081 begin
1082 NetOut.Write(Byte(NET_MSG_GSET));
1083 NetOut.Write(gGameSettings.GameMode);
1084 NetOut.Write(gGameSettings.GoalLimit);
1085 NetOut.Write(gGameSettings.TimeLimit);
1086 NetOut.Write(gGameSettings.MaxLives);
1087 NetOut.Write(gGameSettings.Options);
1089 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1090 end;
1092 // PLAYER (SEND)
1094 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
1095 var
1096 P: TPlayer;
1097 begin
1098 P := g_Player_Get(PID);
1099 if P = nil then Exit;
1101 NetOut.Write(Byte(NET_MSG_PLR));
1102 NetOut.Write(PID);
1103 NetOut.Write(P.Name);
1105 NetOut.Write(P.FActualModelName);
1106 NetOut.Write(P.FColor.R);
1107 NetOut.Write(P.FColor.G);
1108 NetOut.Write(P.FColor.B);
1109 NetOut.Write(P.Team);
1111 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
1112 end;
1114 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
1115 var
1116 kByte: Word;
1117 Pl: TPlayer;
1118 begin
1119 Pl := g_Player_Get(PID);
1120 if Pl = nil then Exit;
1121 if Pl.FDummy then Exit;
1123 NetOut.Write(Byte(NET_MSG_PLRPOS));
1124 NetOut.Write(gTime);
1125 NetOut.Write(PID);
1127 kByte := 0;
1129 with Pl do
1130 begin
1131 NetOut.Write(FPing);
1132 NetOut.Write(FLoss);
1133 if IsKeyPressed(KEY_CHAT) then
1134 kByte := NET_KEY_CHAT
1135 else
1136 begin
1137 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
1138 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
1139 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
1140 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1141 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1142 end;
1144 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1146 NetOut.Write(kByte);
1147 if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
1148 NetOut.Write(GameX);
1149 NetOut.Write(GameY);
1150 NetOut.Write(GameVelX);
1151 NetOut.Write(GameVelY);
1152 NetOut.Write(GameAccelX);
1153 NetOut.Write(GameAccelY);
1154 end;
1156 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1157 end;
1159 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1160 var
1161 P: TPlayer;
1162 I: Integer;
1163 begin
1164 P := g_Player_Get(PID);
1165 if P = nil then Exit;
1167 NetOut.Write(Byte(NET_MSG_PLRSTA));
1168 NetOut.Write(PID);
1170 with P do
1171 begin
1172 NetOut.Write(Byte(alive));
1173 NetOut.Write(Byte(GodMode));
1174 NetOut.Write(Health);
1175 NetOut.Write(Armor);
1176 NetOut.Write(Air);
1177 NetOut.Write(JetFuel);
1178 NetOut.Write(Lives);
1179 NetOut.Write(Team);
1181 for I := WP_FIRST to WP_LAST do
1182 NetOut.Write(Byte(FWeapon[I]));
1184 for I := A_BULLETS to A_HIGH do
1185 NetOut.Write(FAmmo[I]);
1187 for I := A_BULLETS to A_HIGH do
1188 NetOut.Write(FMaxAmmo[I]);
1190 for I := MR_SUIT to MR_MAX do
1191 NetOut.Write(LongWord(FMegaRulez[I]));
1193 NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez));
1194 NetOut.Write(Byte(R_KEY_RED in FRulez));
1195 NetOut.Write(Byte(R_KEY_GREEN in FRulez));
1196 NetOut.Write(Byte(R_KEY_BLUE in FRulez));
1197 NetOut.Write(Byte(R_BERSERK in FRulez));
1199 NetOut.Write(Frags);
1200 NetOut.Write(Death);
1202 NetOut.Write(CurrWeap);
1204 NetOut.Write(Byte(FSpectator));
1205 NetOut.Write(Byte(FGhost));
1206 NetOut.Write(Byte(FPhysics));
1207 NetOut.Write(Byte(FNoRespawn));
1208 NetOut.Write(Byte(FJetpack));
1209 NetOut.Write(FFireTime);
1210 NetOut.Write(Byte(FFlaming));
1211 NetOut.Write(FSpawnInvul);
1212 end;
1214 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1215 end;
1217 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1218 begin
1219 NetOut.Write(Byte(NET_MSG_PLRDMG));
1220 NetOut.Write(PID);
1221 NetOut.Write(Kind);
1222 NetOut.Write(Attacker);
1223 NetOut.Write(Value);
1224 NetOut.Write(VX);
1225 NetOut.Write(VY);
1227 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1228 end;
1230 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1231 begin
1232 NetOut.Write(Byte(NET_MSG_PLRDIE));
1233 NetOut.Write(PID);
1234 NetOut.Write(KillType);
1235 NetOut.Write(DeathType);
1236 NetOut.Write(Attacker);
1238 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1239 end;
1241 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1242 begin
1243 NetOut.Write(Byte(NET_MSG_PLRFIRE));
1244 NetOut.Write(PID);
1245 NetOut.Write(Weapon);
1246 NetOut.Write(X);
1247 NetOut.Write(Y);
1248 NetOut.Write(AX);
1249 NetOut.Write(AY);
1250 NetOut.Write(ShotID);
1252 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1253 end;
1255 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1256 begin
1257 NetOut.Write(Byte(NET_MSG_PLRDEL));
1258 NetOut.Write(PID);
1260 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1261 end;
1263 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1264 var
1265 Pl: TPlayer;
1266 begin
1267 Pl := g_Player_Get(PID);
1268 if Pl = nil then Exit;
1270 NetOut.Write(Byte(NET_MSG_PLRSET));
1271 NetOut.Write(PID);
1272 NetOut.Write(Pl.Name);
1273 if Mdl = '' then
1274 NetOut.Write(Pl.Model.Name)
1275 else
1276 NetOut.Write(Mdl);
1277 NetOut.Write(Pl.FColor.R);
1278 NetOut.Write(Pl.FColor.G);
1279 NetOut.Write(Pl.FColor.B);
1280 NetOut.Write(Pl.Team);
1282 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1283 end;
1285 // ITEM (SEND)
1287 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1288 var
1289 it: PItem;
1290 tt: Byte;
1291 begin
1292 it := g_Items_ByIdx(IID);
1294 NetOut.Write(Byte(NET_MSG_ISPAWN));
1295 NetOut.Write(IID);
1296 NetOut.Write(Byte(Quiet));
1297 tt := it.ItemType;
1298 if it.dropped then tt := tt or $80;
1299 NetOut.Write(tt);
1300 NetOut.Write(Byte(it.Fall));
1301 NetOut.Write(Byte(it.Respawnable));
1302 NetOut.Write(it.Obj.X);
1303 NetOut.Write(it.Obj.Y);
1304 NetOut.Write(it.Obj.Vel.X);
1305 NetOut.Write(it.Obj.Vel.Y);
1307 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1308 end;
1310 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1311 begin
1312 NetOut.Write(Byte(NET_MSG_IDEL));
1313 NetOut.Write(IID);
1314 NetOut.Write(Byte(Quiet));
1316 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1317 end;
1319 procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
1320 var
1321 it: PItem;
1322 begin
1323 it := g_Items_ByIdx(IID);
1325 NetOut.Write(Byte(NET_MSG_IPOS));
1326 NetOut.Write(IID);
1327 NetOut.Write(it.Obj.X);
1328 NetOut.Write(it.Obj.Y);
1329 NetOut.Write(it.Obj.Vel.X);
1330 NetOut.Write(it.Obj.Vel.Y);
1332 g_Net_Host_Send(ID, False, NET_CHAN_LARGEDATA);
1333 end;
1335 // PANEL
1337 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1338 var
1339 TP: TPanel;
1340 begin
1341 TP := g_Map_PanelByGUID(PGUID);
1342 if (TP = nil) then exit;
1344 with TP do
1345 begin
1346 NetOut.Write(Byte(NET_MSG_PTEX));
1347 NetOut.Write(LongWord(PGUID));
1348 NetOut.Write(FCurTexture);
1349 NetOut.Write(FCurFrame);
1350 NetOut.Write(FCurFrameCount);
1351 NetOut.Write(AnimLoop);
1352 end;
1354 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1355 end;
1357 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
1358 var
1359 TP: TPanel;
1360 mpflags: Byte = 0;
1361 begin
1362 TP := g_Map_PanelByGUID(PGUID);
1363 if (TP = nil) then exit;
1365 NetOut.Write(Byte(NET_MSG_PSTATE));
1366 NetOut.Write(LongWord(PGUID));
1367 NetOut.Write(Byte(TP.Enabled));
1368 NetOut.Write(TP.LiftType);
1369 NetOut.Write(TP.X);
1370 NetOut.Write(TP.Y);
1371 NetOut.Write(Word(TP.Width));
1372 NetOut.Write(Word(TP.Height));
1373 // mplats
1374 NetOut.Write(LongInt(TP.movingSpeedX));
1375 NetOut.Write(LongInt(TP.movingSpeedY));
1376 NetOut.Write(LongInt(TP.movingStartX));
1377 NetOut.Write(LongInt(TP.movingStartY));
1378 NetOut.Write(LongInt(TP.movingEndX));
1379 NetOut.Write(LongInt(TP.movingEndY));
1380 NetOut.Write(LongInt(TP.sizeSpeedX));
1381 NetOut.Write(LongInt(TP.sizeSpeedY));
1382 NetOut.Write(LongInt(TP.sizeEndX));
1383 NetOut.Write(LongInt(TP.sizeEndY));
1384 if TP.movingActive then mpflags := mpflags or 1;
1385 if TP.moveOnce then mpflags := mpflags or 2;
1386 NetOut.Write(Byte(mpflags));
1388 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1389 end;
1391 // TRIGGER
1393 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1394 begin
1395 if gTriggers = nil then Exit;
1396 if T.Sound = nil then Exit;
1398 NetOut.Write(Byte(NET_MSG_TSOUND));
1399 NetOut.Write(T.ClientID);
1400 NetOut.Write(Byte(T.Sound.IsPlaying));
1401 NetOut.Write(LongWord(T.Sound.GetPosition));
1402 NetOut.Write(T.SoundPlayCount);
1404 g_Net_Host_Send(ID, True);
1405 end;
1407 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1408 begin
1409 NetOut.Write(Byte(NET_MSG_TMUSIC));
1410 NetOut.Write(gMusic.Name);
1411 NetOut.Write(Byte(gMusic.IsPlaying));
1412 NetOut.Write(LongWord(gMusic.GetPosition));
1413 NetOut.Write(Byte(gMusic.SpecPause or gMusic.IsPaused));
1415 g_Net_Host_Send(ID, True);
1416 end;
1418 // MONSTER
1420 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1421 var
1422 M: TMonster;
1423 begin
1424 M := g_Monsters_ByUID(UID);
1425 if M = nil then
1426 Exit;
1428 with M do
1429 begin
1430 NetOut.Write(Byte(NET_MSG_MSPAWN));
1431 NetOut.Write(UID);
1432 NetOut.Write(MonsterType);
1433 NetOut.Write(MonsterState);
1434 NetOut.Write(MonsterAnim);
1435 NetOut.Write(MonsterTargetUID);
1436 NetOut.Write(MonsterTargetTime);
1437 NetOut.Write(MonsterBehaviour);
1438 NetOut.Write(MonsterSleep);
1439 NetOut.Write(MonsterHealth);
1440 NetOut.Write(MonsterAmmo);
1441 NetOut.Write(GameX);
1442 NetOut.Write(GameY);
1443 NetOut.Write(GameVelX);
1444 NetOut.Write(GameVelY);
1445 NetOut.Write(Byte(GameDirection));
1446 end;
1448 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1449 end;
1451 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1452 var
1453 M: TMonster;
1454 begin
1455 M := g_Monsters_ByUID(UID);
1456 if M = nil then Exit;
1458 NetOut.Write(Byte(NET_MSG_MPOS));
1459 NetOut.Write(UID);
1461 with M do
1462 begin
1463 NetOut.Write(GameX);
1464 NetOut.Write(GameY);
1465 NetOut.Write(GameVelX);
1466 NetOut.Write(GameVelY);
1467 NetOut.Write(Byte(GameDirection));
1468 end;
1470 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1471 end;
1473 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1474 var
1475 M: TMonster;
1476 begin
1477 M := g_Monsters_ByUID(UID);
1478 if M = nil then Exit;
1480 NetOut.Write(Byte(NET_MSG_MSTATE));
1481 NetOut.Write(UID);
1483 with M do
1484 begin
1485 NetOut.Write(MonsterState);
1486 NetOut.Write(ForcedAnim);
1487 NetOut.Write(MonsterTargetUID);
1488 NetOut.Write(MonsterTargetTime);
1489 NetOut.Write(MonsterSleep);
1490 NetOut.Write(MonsterHealth);
1491 NetOut.Write(MonsterAmmo);
1492 NetOut.Write(MonsterPain);
1493 NetOut.Write(Byte(AnimIsReverse));
1494 NetOut.Write(FFireTime);
1495 end;
1497 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1498 end;
1500 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1501 begin
1502 NetOut.Write(Byte(NET_MSG_MSHOT));
1503 NetOut.Write(UID);
1504 NetOut.Write(X);
1505 NetOut.Write(Y);
1506 NetOut.Write(VX);
1507 NetOut.Write(VY);
1509 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1510 end;
1512 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1513 var
1514 M: TMonster;
1515 begin
1516 M := g_Monsters_ByUID(UID);
1517 if M = nil then Exit;
1519 NetOut.Write(Byte(NET_MSG_MDEL));
1520 NetOut.Write(UID);
1522 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1523 end;
1525 // MISC
1527 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1528 begin
1529 NetOut.Write(Byte(NET_MSG_TIME_SYNC));
1530 NetOut.Write(Time);
1532 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1533 end;
1535 procedure MH_SEND_VoteEvent(EvType: Byte;
1536 StrArg1: string = 'a'; StrArg2: string = 'b';
1537 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1538 ID: Integer = NET_EVERYONE);
1539 begin
1540 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
1541 NetOut.Write(EvType);
1542 NetOut.Write(IntArg1);
1543 NetOut.Write(IntArg2);
1544 NetOut.Write(StrArg1);
1545 NetOut.Write(StrArg2);
1547 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1548 end;
1550 // CLIENT MESSAGES //
1552 // GAME
1554 procedure MC_RECV_Chat(var M: TMsg);
1555 var
1556 Txt: string;
1557 Mode: Byte;
1558 begin
1559 Txt := M.ReadString();
1560 Mode := M.ReadByte();
1562 if Mode <> NET_CHAT_SYSTEM then
1563 begin
1564 if NetDeafLevel = 0 then
1565 begin
1566 if Mode = NET_CHAT_PLAYER then
1567 begin
1568 g_Console_Add(Txt, True);
1569 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1570 g_Game_ChatSound(b_Text_Unformat(Txt));
1571 end else
1572 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1573 begin
1574 if gPlayer1.Team = TEAM_RED then
1575 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1576 if gPlayer1.Team = TEAM_BLUE then
1577 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1578 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1579 g_Game_ChatSound(b_Text_Unformat(Txt));
1580 end;
1581 end;
1582 end else if (NetDeafLevel < 2) then
1583 g_Console_Add(Txt, True);
1584 end;
1586 procedure MC_RECV_Effect(var M: TMsg);
1587 var
1588 Kind: Byte;
1589 X, Y: Integer;
1590 Ang: SmallInt;
1591 Anim: TAnimation;
1592 ID: LongWord;
1593 begin
1594 if not gGameOn then Exit;
1595 Kind := M.ReadByte();
1596 X := M.ReadLongInt();
1597 Y := M.ReadLongInt();
1598 Ang := M.ReadSmallInt();
1600 case Kind of
1601 NET_GFX_SPARK:
1602 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1604 NET_GFX_TELE:
1605 begin
1606 if g_Frames_Get(ID, 'FRAMES_TELEPORT') then
1607 begin
1608 Anim := TAnimation.Create(ID, False, 3);
1609 g_GFX_OnceAnim(X, Y, Anim);
1610 Anim.Free();
1611 end;
1612 if Ang = 1 then
1613 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1614 end;
1616 NET_GFX_EXPLODE:
1617 begin
1618 if g_Frames_Get(ID, 'FRAMES_EXPLODE_ROCKET') then
1619 begin
1620 Anim := TAnimation.Create(ID, False, 6);
1621 Anim.Blending := False;
1622 g_GFX_OnceAnim(X-64, Y-64, Anim);
1623 Anim.Free();
1624 end;
1625 if Ang = 1 then
1626 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1627 end;
1629 NET_GFX_BFGEXPL:
1630 begin
1631 if g_Frames_Get(ID, 'FRAMES_EXPLODE_BFG') then
1632 begin
1633 Anim := TAnimation.Create(ID, False, 6);
1634 Anim.Blending := False;
1635 g_GFX_OnceAnim(X-64, Y-64, Anim);
1636 Anim.Free();
1637 end;
1638 if Ang = 1 then
1639 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1640 end;
1642 NET_GFX_BFGHIT:
1643 begin
1644 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1645 begin
1646 Anim := TAnimation.Create(ID, False, 4);
1647 g_GFX_OnceAnim(X-32, Y-32, Anim);
1648 Anim.Free();
1649 end;
1650 end;
1652 NET_GFX_FIRE:
1653 begin
1654 if g_Frames_Get(ID, 'FRAMES_FIRE') then
1655 begin
1656 Anim := TAnimation.Create(ID, False, 4);
1657 g_GFX_OnceAnim(X, Y, Anim);
1658 Anim.Free();
1659 end;
1660 if Ang = 1 then
1661 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1662 end;
1664 NET_GFX_RESPAWN:
1665 begin
1666 if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
1667 begin
1668 Anim := TAnimation.Create(ID, False, 4);
1669 g_GFX_OnceAnim(X, Y, Anim);
1670 Anim.Free();
1671 end;
1672 if Ang = 1 then
1673 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1674 end;
1676 NET_GFX_SHELL1:
1677 g_Player_CreateShell(X, Y, 0, -2, SHELL_BULLET);
1679 NET_GFX_SHELL2:
1680 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1682 NET_GFX_SHELL3:
1683 begin
1684 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1685 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1686 end;
1687 end;
1688 end;
1690 procedure MC_RECV_Sound(var M: TMsg);
1691 var
1692 Name: string;
1693 X, Y: Integer;
1694 Pos: Boolean;
1695 begin
1696 Name := M.ReadString();
1697 Pos := M.ReadByte() <> 0;
1698 if Pos then
1699 begin
1700 X := M.ReadLongInt();
1701 Y := M.ReadLongInt();
1702 g_Sound_PlayExAt(Name, X, Y);
1703 end
1704 else
1705 g_Sound_PlayEx(Name);
1706 end;
1708 procedure MC_RECV_CreateShot(var M: TMsg);
1709 var
1710 I, X, Y, XV, YV: Integer;
1711 Timeout: LongWord;
1712 Target, Spawner: Word;
1713 ShType: Byte;
1714 begin
1715 I := M.ReadLongInt();
1716 ShType := M.ReadByte();
1717 Target := M.ReadWord();
1718 Spawner := M.ReadWord();
1719 Timeout := M.ReadLongWord();
1720 X := M.ReadLongInt();
1721 Y := M.ReadLongInt();
1722 XV := M.ReadLongInt();
1723 YV := M.ReadLongInt();
1725 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1726 if (Shots <> nil) and (I <= High(Shots)) then
1727 begin
1728 Shots[I].Timeout := Timeout;
1729 //Shots[I].Target := Target; // TODO: find a use for Target later
1730 end;
1731 end;
1733 procedure MC_RECV_UpdateShot(var M: TMsg);
1734 var
1735 I, TX, TY, TXV, TYV: Integer;
1736 begin
1737 I := M.ReadLongInt();
1738 TX := M.ReadLongInt();
1739 TY := M.ReadLongInt();
1740 TXV := M.ReadLongInt();
1741 TYV := M.ReadLongInt();
1743 if (Shots <> nil) and (I <= High(Shots)) then
1744 with (Shots[i]) do
1745 begin
1746 Obj.X := TX;
1747 Obj.Y := TY;
1748 Obj.Vel.X := TXV;
1749 Obj.Vel.Y := TYV;
1750 end;
1751 end;
1753 procedure MC_RECV_DeleteShot(var M: TMsg);
1754 var
1755 I, X, Y: Integer;
1756 L: Boolean;
1757 begin
1758 if not gGameOn then Exit;
1759 I := M.ReadLongInt();
1760 L := (M.ReadByte() <> 0);
1761 X := M.ReadLongInt();
1762 Y := M.ReadLongInt();
1764 g_Weapon_DestroyShot(I, X, Y, L);
1765 end;
1767 procedure MC_RECV_GameStats(var M: TMsg);
1768 begin
1769 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1770 begin
1771 gTeamStat[TEAM_RED].Goals := M.ReadSmallInt();
1772 gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt();
1773 end
1774 else
1775 if gGameSettings.GameMode = GM_COOP then
1776 begin
1777 gCoopMonstersKilled := M.ReadWord();
1778 gCoopSecretsFound := M.ReadWord();
1779 end;
1780 end;
1782 procedure MC_RECV_CoopStats(var M: TMsg);
1783 begin
1784 gTotalMonsters := M.ReadLongInt();
1785 gSecretsCount := M.ReadLongInt();
1786 gCoopTotalMonstersKilled := M.ReadWord();
1787 gCoopTotalSecretsFound := M.ReadWord();
1788 gCoopTotalMonsters := M.ReadWord();
1789 gCoopTotalSecrets := M.ReadWord();
1790 end;
1792 procedure MC_RECV_GameEvent(var M: TMsg);
1793 var
1794 EvType: Byte;
1795 EvNum: Integer;
1796 EvStr: string;
1797 EvTime: LongWord;
1798 BHash: Boolean;
1799 EvHash: TMD5Digest;
1800 pl: TPlayer;
1801 i1, i2: TStrings_Locale;
1802 pln: String;
1803 cnt: Byte;
1804 goodCmd: Boolean = true;
1805 begin
1806 FillChar(EvHash, Sizeof(EvHash), 0);
1807 EvType := M.ReadByte();
1808 EvNum := M.ReadLongInt();
1809 EvStr := M.ReadString();
1810 gLastMap := M.ReadByte() <> 0;
1811 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1812 gStatsPressed := True;
1813 EvTime := M.ReadLongWord();
1814 BHash := M.ReadByte() <> 0;
1815 if BHash then
1816 EvHash := M.ReadMD5();
1818 gTime := EvTime;
1820 if (g_Res_received_map_start <> 0) then
1821 begin
1822 if (g_Res_received_map_start < 0) then exit;
1823 goodCmd := false;
1824 case EvType of
1825 NET_EV_MAPSTART: goodCmd := true;
1826 NET_EV_MAPEND: goodCmd := true;
1827 NET_EV_PLAYER_KICK: goodCmd := true;
1828 NET_EV_PLAYER_BAN: goodCmd := true;
1829 NET_EV_LMS_WARMUP: goodCmd := true;
1830 end;
1831 if not goodCmd then exit;
1832 end;
1834 case EvType of
1835 NET_EV_MAPSTART:
1836 begin
1837 if (g_Res_received_map_start <> 0) then
1838 begin
1839 g_Res_received_map_start := -1;
1840 end
1841 else
1842 begin
1843 gGameOn := False;
1844 g_Game_ClearLoading();
1845 g_Game_StopAllSounds(True);
1847 gSwitchGameMode := Byte(EvNum);
1848 gGameSettings.GameMode := gSwitchGameMode;
1850 gWADHash := EvHash;
1851 if not g_Game_StartMap(false{asMegawad}, EvStr, True) then
1852 begin
1853 if not isWadPath(EvStr) then
1854 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1855 else
1856 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1857 Exit;
1858 end;
1860 MC_SEND_FullStateRequest;
1861 end;
1862 end;
1864 NET_EV_MAPEND:
1865 begin
1866 gLMSRespawn := LMS_RESPAWN_NONE;
1867 gLMSRespawnTime := 0;
1868 if (g_Res_received_map_start <> 0) then
1869 begin
1870 g_Res_received_map_start := -1;
1871 end
1872 else
1873 begin
1874 gMissionFailed := EvNum <> 0;
1875 gExit := EXIT_ENDLEVELCUSTOM;
1876 end;
1877 end;
1879 NET_EV_RCON:
1880 begin
1881 case EvNum of
1882 NET_RCON_NOAUTH:
1883 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
1884 NET_RCON_PWGOOD:
1885 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
1886 NET_RCON_PWBAD:
1887 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
1888 end;
1889 end;
1891 NET_EV_CHANGE_TEAM:
1892 begin
1893 if NetDeafLevel < 2 then
1894 begin
1895 if EvNum = TEAM_RED then
1896 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
1897 if EvNum = TEAM_BLUE then
1898 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
1899 end;
1900 end;
1902 NET_EV_PLAYER_KICK:
1903 begin
1904 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
1905 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1906 end;
1908 NET_EV_PLAYER_BAN:
1909 begin
1910 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
1911 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1912 end;
1914 NET_EV_LMS_WARMUP:
1915 begin
1916 if EvNum > 0 then
1917 begin
1918 gLMSRespawn := LMS_RESPAWN_WARMUP;
1919 gLMSRespawnTime := gTime + EvNum;
1920 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum div 1000]), True);
1921 end
1922 else if gPlayer1 = nil then
1923 begin
1924 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
1925 end;
1926 end;
1928 NET_EV_LMS_SURVIVOR:
1929 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
1931 NET_EV_BIGTEXT:
1932 if NetDeafLevel < 2 then g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
1934 NET_EV_SCORE:
1935 begin
1936 pl := g_Player_Get(EvNum and $FFFF);
1937 if pl = nil then
1938 pln := '?'
1939 else
1940 pln := pl.Name;
1941 cnt := (EvNum shr 16) and $FF;
1942 if Pos('w', EvStr) = 0 then
1943 begin
1944 // Default score
1945 if Pos('t', EvStr) = 0 then
1946 begin
1947 // Player +/- score
1948 if Pos('-', EvStr) = 0 then
1949 begin
1950 if Pos('e', EvStr) = 0 then
1951 i1 := I_PLAYER_SCORE_ADD_OWN
1952 else
1953 i1 := I_PLAYER_SCORE_ADD_ENEMY;
1954 end else
1955 begin
1956 if Pos('e', EvStr) = 0 then
1957 i1 := I_PLAYER_SCORE_SUB_OWN
1958 else
1959 i1 := I_PLAYER_SCORE_SUB_ENEMY;
1960 end;
1961 // Which team
1962 if Pos('r', EvStr) > 0 then
1963 i2 := I_PLAYER_SCORE_TO_RED
1964 else
1965 i2 := I_PLAYER_SCORE_TO_BLUE;
1966 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
1967 end else
1968 begin
1969 // Team +/- score
1970 if Pos('-', EvStr) = 0 then
1971 i1 := I_PLAYER_SCORE_ADD_TEAM
1972 else
1973 i1 := I_PLAYER_SCORE_SUB_TEAM;
1974 // Which team
1975 if Pos('r', EvStr) > 0 then
1976 i2 := I_PLAYER_SCORE_RED
1977 else
1978 i2 := I_PLAYER_SCORE_BLUE;
1979 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
1980 end;
1981 end else
1982 begin
1983 // Game Win
1984 if Pos('e', EvStr) = 0 then
1985 i1 := I_PLAYER_SCORE_WIN_OWN
1986 else
1987 i1 := I_PLAYER_SCORE_WIN_ENEMY;
1988 // Which team
1989 if Pos('r', EvStr) > 0 then
1990 i2 := I_PLAYER_SCORE_TO_RED
1991 else
1992 i2 := I_PLAYER_SCORE_TO_BLUE;
1993 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
1994 end;
1995 end;
1997 NET_EV_SCORE_MSG:
1998 begin
1999 if EvNum = TEAM_RED then
2000 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
2001 if EvNum = TEAM_BLUE then
2002 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
2003 if EvNum = -TEAM_RED then
2004 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
2005 if EvNum = -TEAM_BLUE then
2006 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
2007 end;
2009 NET_EV_LMS_START:
2010 begin
2011 g_Player_RemoveAllCorpses;
2012 gLMSRespawn := LMS_RESPAWN_NONE;
2013 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
2014 end;
2016 NET_EV_LMS_WIN:
2017 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
2019 NET_EV_TLMS_WIN:
2020 begin
2021 if EvNum = TEAM_RED then
2022 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
2023 if EvNum = TEAM_BLUE then
2024 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
2025 end;
2027 NET_EV_LMS_LOSE:
2028 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
2030 NET_EV_LMS_DRAW:
2031 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
2033 NET_EV_LMS_NOSPAWN:
2034 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
2036 NET_EV_KILLCOMBO:
2037 g_Game_Announce_KillCombo(EvNum);
2039 NET_EV_PLAYER_TOUCH:
2040 begin
2041 pl := g_Player_Get(EvNum);
2042 if pl <> nil then
2043 pl.Touch();
2044 end;
2046 NET_EV_SECRET:
2047 begin
2048 pl := g_Player_Get(EvNum);
2049 if pl <> nil then
2050 begin
2051 g_Console_Add(Format(_lc[I_PLAYER_SECRET], [pl.Name]), True);
2052 g_Sound_PlayEx('SOUND_GAME_SECRET');
2053 end;
2054 end;
2056 NET_EV_INTER_READY:
2057 begin
2058 pl := g_Player_Get(EvNum);
2059 if pl <> nil then pl.FReady := (EvStr = 'Y');
2060 end;
2061 end;
2062 end;
2064 procedure MC_RECV_FlagPos(var M: TMsg);
2065 var
2066 Fl: Byte;
2067 begin
2068 Fl := M.ReadByte();
2069 if Fl = FLAG_NONE then Exit;
2070 gFlags[Fl].Obj.X := M.ReadLongInt();
2071 gFlags[Fl].Obj.Y := M.ReadLongInt();
2072 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
2073 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
2074 end;
2076 procedure MC_RECV_FlagEvent(var M: TMsg);
2077 var
2078 PID: Word;
2079 Pl: TPlayer;
2080 EvType: Byte;
2081 Fl, a: Byte;
2082 Quiet: Boolean;
2083 s, ts: string;
2084 begin
2085 EvType := M.ReadByte();
2086 Fl := M.ReadByte();
2088 if Fl = FLAG_NONE then Exit;
2090 Quiet := (M.ReadByte() <> 0);
2091 PID := M.ReadWord();
2093 gFlags[Fl].State := M.ReadByte();
2094 gFlags[Fl].CaptureTime := M.ReadLongWord();
2095 gFlags[Fl].Obj.X := M.ReadLongInt();
2096 gFlags[Fl].Obj.Y := M.ReadLongInt();
2097 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
2098 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
2099 gFlags[Fl].Direction := TDirection(M.ReadByte());
2101 Pl := g_Player_Get(PID);
2102 if (Pl = nil) and
2103 (EvType <> FLAG_STATE_NORMAL) and
2104 (EvType <> FLAG_STATE_DROPPED) and
2105 (EvType <> FLAG_STATE_RETURNED) then
2106 Exit;
2108 case EvType of
2109 FLAG_STATE_NORMAL:
2110 begin
2111 if Quiet or (Pl = nil) then Exit;
2113 if Fl = FLAG_RED then
2114 s := _lc[I_PLAYER_FLAG_RED]
2115 else
2116 s := _lc[I_PLAYER_FLAG_BLUE];
2118 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2120 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2121 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2122 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2123 a := 0
2124 else
2125 a := 1;
2127 if not sound_ret_flag[a].IsPlaying() then
2128 sound_ret_flag[a].Play();
2129 end;
2131 FLAG_STATE_CAPTURED:
2132 begin
2133 if (Pl <> nil) then Pl.SetFlag(Fl);
2135 if Quiet then Exit;
2137 if Fl = FLAG_RED then
2138 s := _lc[I_PLAYER_FLAG_RED]
2139 else
2140 s := _lc[I_PLAYER_FLAG_BLUE];
2142 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
2143 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
2145 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2146 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2147 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2148 a := 0
2149 else
2150 a := 1;
2152 if not sound_get_flag[a].IsPlaying() then
2153 sound_get_flag[a].Play();
2154 end;
2156 FLAG_STATE_DROPPED:
2157 begin
2158 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
2160 if Quiet or (Pl = nil) then Exit;
2162 if Fl = FLAG_RED then
2163 s := _lc[I_PLAYER_FLAG_RED]
2164 else
2165 s := _lc[I_PLAYER_FLAG_BLUE];
2167 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
2168 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
2170 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2171 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2172 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2173 a := 0
2174 else
2175 a := 1;
2177 if not sound_lost_flag[a].IsPlaying() then
2178 sound_lost_flag[a].Play();
2179 end;
2181 FLAG_STATE_SCORED:
2182 begin
2183 g_Map_ResetFlag(FLAG_RED);
2184 g_Map_ResetFlag(FLAG_BLUE);
2185 if Quiet or (Pl = nil) then Exit;
2186 Pl.SetFlag(FLAG_NONE);
2188 if Fl = FLAG_RED then
2189 s := _lc[I_PLAYER_FLAG_RED]
2190 else
2191 s := _lc[I_PLAYER_FLAG_BLUE];
2193 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
2194 Insert('.', ts, Length(ts) + 1 - 3);
2195 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
2196 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
2198 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2199 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2200 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2201 a := 0
2202 else
2203 a := 1;
2205 if not sound_cap_flag[a].IsPlaying() then
2206 sound_cap_flag[a].Play();
2207 end;
2209 FLAG_STATE_RETURNED:
2210 begin
2211 g_Map_ResetFlag(Fl);
2212 if Quiet then Exit;
2214 if Fl = FLAG_RED then
2215 s := _lc[I_PLAYER_FLAG_RED]
2216 else
2217 s := _lc[I_PLAYER_FLAG_BLUE];
2219 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2221 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2222 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2223 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2224 a := 0
2225 else
2226 a := 1;
2228 if not sound_ret_flag[a].IsPlaying() then
2229 sound_ret_flag[a].Play();
2230 end;
2231 end;
2232 end;
2234 procedure MC_RECV_GameSettings(var M: TMsg);
2235 begin
2236 gGameSettings.GameMode := M.ReadByte();
2237 gGameSettings.GoalLimit := M.ReadWord();
2238 gGameSettings.TimeLimit := M.ReadWord();
2239 gGameSettings.MaxLives := M.ReadByte();
2240 gGameSettings.Options := M.ReadLongWord();
2241 end;
2243 // PLAYER
2245 function MC_RECV_PlayerCreate(var M: TMsg): Word;
2246 var
2247 PID, DID: Word;
2248 PName, Model: string;
2249 Color: TRGB;
2250 T: Byte;
2251 Pl: TPlayer;
2252 begin
2253 PID := M.ReadWord();
2254 Pl := g_Player_Get(PID);
2256 PName := M.ReadString();
2257 Model := M.ReadString();
2258 Color.R := M.ReadByte();
2259 Color.G := M.ReadByte();
2260 Color.B := M.ReadByte();
2261 T := M.ReadByte();
2263 Result := 0;
2264 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
2265 begin
2266 if (Pl <> nil) then Exit;
2267 DID := g_Player_Create(Model, Color, T, False);
2268 with g_Player_Get(DID) do
2269 begin
2270 UID := PID;
2271 Name := PName;
2272 Reset(True);
2273 end;
2274 end
2275 else
2276 begin
2277 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
2278 gPlayer1.UID := PID;
2279 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
2280 gPlayer1.ChangeTeam(T);
2281 end;
2282 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
2283 gPlayer2.UID := PID;
2284 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
2285 gPlayer2.ChangeTeam(T);
2286 end;
2287 end;
2289 if NetDeafLevel < 3 then
2290 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
2291 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify);
2292 Result := PID;
2293 end;
2295 function MC_RECV_PlayerPos(var M: TMsg): Word;
2296 var
2297 GT: LongWord;
2298 PID: Word;
2299 kByte: Word;
2300 Pl: TPlayer;
2301 Dir: Byte;
2302 TmpX, TmpY: Integer;
2303 begin
2304 Result := 0;
2306 GT := M.ReadLongWord();
2307 if GT < gTime - NET_MAX_DIFFTIME then
2308 begin
2309 gTime := GT;
2310 Exit;
2311 end;
2312 gTime := GT;
2314 PID := M.ReadWord();
2315 Pl := g_Player_Get(PID);
2317 if Pl = nil then Exit;
2319 Result := PID;
2321 with Pl do
2322 begin
2323 FPing := M.ReadWord();
2324 FLoss := M.ReadByte();
2325 kByte := M.ReadWord();
2326 Dir := M.ReadByte();
2328 TmpX := M.ReadLongInt();
2329 TmpY := M.ReadLongInt();
2331 ReleaseKeys;
2333 if LongBool(kByte and NET_KEY_CHAT) then
2334 PressKey(KEY_CHAT, 10000)
2335 else
2336 begin
2337 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2338 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2339 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2340 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2341 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2342 end;
2344 JustTeleported := LongBool(kByte and NET_KEY_FORCEDIR);
2346 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or JustTeleported then
2347 SetDirection(TDirection(Dir));
2349 GameVelX := M.ReadLongInt();
2350 GameVelY := M.ReadLongInt();
2351 GameAccelX := M.ReadLongInt();
2352 GameAccelY := M.ReadLongInt();
2353 SetLerp(TmpX, TmpY);
2354 if NetForcePlayerUpdate then Update();
2355 end;
2356 end;
2358 function MC_RECV_PlayerStats(var M: TMsg): Word;
2359 var
2360 PID: Word;
2361 Pl: TPlayer;
2362 I, OldFire: Integer;
2363 OldJet, Flam: Boolean;
2364 NewTeam: Byte;
2365 begin
2366 PID := M.ReadWord();
2367 Pl := g_Player_Get(PID);
2368 Result := 0;
2369 if Pl = nil then
2370 Exit;
2372 with Pl do
2373 begin
2374 alive := (M.ReadByte() <> 0);
2375 GodMode := (M.ReadByte() <> 0);
2376 Health := M.ReadLongInt();
2377 Armor := M.ReadLongInt();
2378 Air := M.ReadLongInt();
2379 JetFuel := M.ReadLongInt();
2380 Lives := M.ReadByte();
2381 NewTeam := M.ReadByte();
2383 for I := WP_FIRST to WP_LAST do
2384 FWeapon[I] := (M.ReadByte() <> 0);
2386 for I := A_BULLETS to A_HIGH do
2387 FAmmo[I] := M.ReadWord();
2389 for I := A_BULLETS to A_HIGH do
2390 FMaxAmmo[I] := M.ReadWord();
2392 for I := MR_SUIT to MR_MAX do
2393 FMegaRulez[I] := M.ReadLongWord();
2395 FRulez := [];
2396 if (M.ReadByte() <> 0) then
2397 FRulez := FRulez + [R_ITEM_BACKPACK];
2398 if (M.ReadByte() <> 0) then
2399 FRulez := FRulez + [R_KEY_RED];
2400 if (M.ReadByte() <> 0) then
2401 FRulez := FRulez + [R_KEY_GREEN];
2402 if (M.ReadByte() <> 0) then
2403 FRulez := FRulez + [R_KEY_BLUE];
2404 if (M.ReadByte() <> 0) then
2405 FRulez := FRulez + [R_BERSERK];
2407 Frags := M.ReadLongInt();
2408 Death := M.ReadLongInt();
2410 SetWeapon(M.ReadByte());
2412 FSpectator := M.ReadByte() <> 0;
2413 if FSpectator then
2414 begin
2415 if UID = NetPlrUID1 then
2416 begin
2417 gSpectLatchPID1 := UID;
2418 gPlayer1 := nil;
2419 end;
2420 if UID = NetPlrUID2 then
2421 begin
2422 gSpectLatchPID2 := UID;
2423 gPlayer2 := nil;
2424 end;
2425 end
2426 else
2427 begin
2428 if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) and (UID = gSpectLatchPID1) then
2429 begin
2430 gPlayer1 := Pl;
2431 gSpectLatchPID1 := 0;
2432 end;
2433 if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) and (UID = gSpectLatchPID2) then
2434 begin
2435 gPlayer2 := Pl;
2436 gSpectLatchPID2 := 0;
2437 end;
2438 end;
2439 FGhost := M.ReadByte() <> 0;
2440 FPhysics := M.ReadByte() <> 0;
2441 FNoRespawn := M.ReadByte() <> 0;
2442 OldJet := FJetpack;
2443 FJetpack := M.ReadByte() <> 0;
2444 OldFire := FFireTime;
2445 FFireTime := M.ReadLongInt();
2446 if (OldFire <= 0) and (FFireTime > 0) then
2447 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2448 Flam := M.ReadByte() <> 0;
2449 FSpawnInvul := M.ReadLongInt();
2450 if OldJet and not FJetpack then
2451 JetpackOff
2452 else if not OldJet and FJetpack then
2453 JetpackOn;
2454 if FFlaming and not Flam then
2455 FlamerOff;
2456 if Team <> NewTeam then
2457 Pl.ChangeTeam(NewTeam);
2458 end;
2460 Result := PID;
2461 end;
2463 function MC_RECV_PlayerDamage(var M: TMsg): Word;
2464 var
2465 PID: Word;
2466 Pl: TPlayer;
2467 Kind: Byte;
2468 Attacker, Value: Word;
2469 VX, VY: Integer;
2470 begin
2471 Result := 0;
2472 if not gGameOn then Exit;
2473 PID := M.ReadWord();
2474 Pl := g_Player_Get(PID);
2475 if Pl = nil then Exit;
2477 Kind := M.ReadByte();
2478 Attacker := M.ReadWord();
2479 Value := M.ReadWord();
2480 VX := M.ReadWord();
2481 VY := M.ReadWord();
2483 with Pl do
2484 Damage(Value, Attacker, VX, VY, Kind);
2486 Result := PID;
2487 end;
2489 function MC_RECV_PlayerDeath(var M: TMsg): Word;
2490 var
2491 PID: Word;
2492 Pl: TPlayer;
2493 KillType, DeathType: Byte;
2494 Attacker: Word;
2495 begin
2496 Result := 0;
2497 if not gGameOn then Exit;
2498 PID := M.ReadWord();
2499 Pl := g_Player_Get(PID);
2500 if Pl = nil then Exit;
2502 KillType := M.ReadByte();
2503 DeathType := M.ReadByte();
2504 Attacker := M.ReadWord();
2506 with Pl do
2507 begin
2508 Kill(KillType, Attacker, DeathType);
2509 SoftReset;
2510 end;
2511 end;
2513 function MC_RECV_PlayerDelete(var M: TMsg): Word;
2514 var
2515 PID: Word;
2516 Pl: TPlayer;
2517 begin
2518 PID := M.ReadWord();
2519 Pl := g_Player_Get(PID);
2520 Result := 0;
2521 if Pl = nil then Exit;
2523 if NetDeafLevel < 3 then
2524 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2525 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify);
2527 g_Player_Remove(PID);
2529 Result := PID;
2530 end;
2532 function MC_RECV_PlayerFire(var M: TMsg): Word;
2533 var
2534 PID: Word;
2535 Weap: Byte;
2536 Pl: TPlayer;
2537 X, Y, AX, AY: Integer;
2538 SHID: Integer;
2539 begin
2540 Result := 0;
2541 if not gGameOn then Exit;
2542 PID := M.ReadWord();
2543 Pl := g_Player_Get(PID);
2544 if Pl = nil then Exit;
2546 Weap := M.ReadByte();
2547 X := M.ReadLongInt();
2548 Y := M.ReadLongInt();
2549 AX := M.ReadLongInt();
2550 AY := M.ReadLongInt();
2551 SHID := M.ReadLongInt();
2553 with Pl do
2554 if alive then NetFire(Weap, X, Y, AX, AY, SHID);
2555 end;
2557 procedure MC_RECV_PlayerSettings(var M: TMsg);
2558 var
2559 TmpName: string;
2560 TmpModel: string;
2561 TmpColor: TRGB;
2562 TmpTeam: Byte;
2563 Pl: TPlayer;
2564 PID: Word;
2565 begin
2566 PID := M.ReadWord();
2567 Pl := g_Player_Get(PID);
2568 if Pl = nil then Exit;
2570 TmpName := M.ReadString();
2571 TmpModel := M.ReadString();
2572 TmpColor.R := M.ReadByte();
2573 TmpColor.G := M.ReadByte();
2574 TmpColor.B := M.ReadByte();
2575 TmpTeam := M.ReadByte();
2577 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2578 begin
2579 Pl.ChangeTeam(TmpTeam);
2580 if gPlayer1 = Pl then
2581 gPlayer1Settings.Team := TmpTeam;
2582 if gPlayer2 = Pl then
2583 gPlayer2Settings.Team := TmpTeam;
2584 end else
2585 Pl.SetColor(TmpColor);
2587 if Pl.Name <> TmpName then
2588 begin
2589 if NetDeafLevel < 3 then
2590 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2591 Pl.Name := TmpName;
2592 end;
2594 if TmpModel <> Pl.Model.Name then
2595 Pl.SetModel(TmpModel);
2596 end;
2598 // ITEM
2600 procedure MC_RECV_ItemSpawn(var M: TMsg);
2601 var
2602 ID: Word;
2603 AID: DWord;
2604 X, Y, VX, VY: Integer;
2605 T: Byte;
2606 Quiet, Fall{, Resp}: Boolean;
2607 Anim: TAnimation;
2608 it: PItem;
2609 begin
2610 if not gGameOn then Exit;
2611 ID := M.ReadWord();
2612 Quiet := M.ReadByte() <> 0;
2613 T := M.ReadByte();
2614 Fall := M.ReadByte() <> 0;
2615 {Resp :=} M.ReadByte();
2616 X := M.ReadLongInt();
2617 Y := M.ReadLongInt();
2618 VX := M.ReadLongInt();
2619 VY := M.ReadLongInt();
2621 g_Items_Create(X, Y, T and $7F, Fall, False, False, ID);
2622 if ((T and $80) <> 0) then g_Items_SetDrop(ID);
2624 it := g_Items_ByIdx(ID);
2625 it.Obj.Vel.X := VX;
2626 it.Obj.Vel.Y := VY;
2628 if not Quiet then
2629 begin
2630 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2631 if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
2632 begin
2633 Anim := TAnimation.Create(AID, False, 4);
2634 g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim);
2635 Anim.Free();
2636 end;
2637 end;
2638 end;
2640 procedure MC_RECV_ItemDestroy(var M: TMsg);
2641 var
2642 ID: Word;
2643 Quiet: Boolean;
2644 begin
2645 if not gGameOn then Exit;
2646 ID := M.ReadWord();
2647 Quiet := M.ReadByte() <> 0;
2649 if not g_Items_ValidId(ID) then exit;
2651 if not Quiet then g_Items_EmitPickupSound(ID);
2653 g_Items_Remove(ID);
2654 end;
2656 procedure MC_RECV_ItemPos(var M: TMsg);
2657 var
2658 ID: Word;
2659 X, Y, VX, VY: Integer;
2660 it: PItem;
2661 begin
2662 if not gGameOn then Exit;
2664 ID := M.ReadWord();
2665 X := M.ReadLongInt();
2666 Y := M.ReadLongInt();
2667 VX := M.ReadLongInt();
2668 VY := M.ReadLongInt();
2670 if g_Items_ValidId(ID) then
2671 begin
2672 it := g_Items_ByIdx(ID);
2673 it.Obj.X := X;
2674 it.Obj.Y := Y;
2675 it.Obj.Vel.X := VX;
2676 it.Obj.Vel.Y := VY;
2677 it.positionChanged();
2678 end;
2679 end;
2681 // PANEL
2683 procedure MC_RECV_PanelTexture(var M: TMsg);
2684 var
2685 TP: TPanel;
2686 PGUID: Integer;
2687 Tex, Fr: Integer;
2688 Loop, Cnt: Byte;
2689 begin
2690 if not gGameOn then Exit;
2692 PGUID := Integer(M.ReadLongWord());
2693 Tex := M.ReadLongInt();
2694 Fr := M.ReadLongInt();
2695 Cnt := M.ReadByte();
2696 Loop := M.ReadByte();
2698 TP := g_Map_PanelByGUID(PGUID);
2699 if (TP <> nil) then
2700 begin
2701 // switch texture
2702 TP.SetTexture(Tex, Loop);
2703 TP.SetFrame(Fr, Cnt);
2704 end;
2705 end;
2707 procedure MC_RECV_PanelState(var M: TMsg);
2708 var
2709 PGUID: Integer;
2710 E: Boolean;
2711 Lift: Byte;
2712 X, Y, W, H: Integer;
2713 TP: TPanel;
2714 speedX, speedY, startX, startY, endX, endY: Integer;
2715 sizeSpX, sizeSpY, sizeEX, sizeEY: Integer;
2716 mpflags: Byte;
2717 begin
2718 if not gGameOn then Exit;
2720 PGUID := Integer(M.ReadLongWord());
2721 E := (M.ReadByte() <> 0);
2722 Lift := M.ReadByte();
2723 X := M.ReadLongInt();
2724 Y := M.ReadLongInt();
2725 W := M.ReadWord();
2726 H := M.ReadWord();
2727 // mplats
2728 speedX := M.ReadLongInt();
2729 speedY := M.ReadLongInt();
2730 startX := M.ReadLongInt();
2731 startY := M.ReadLongInt();
2732 endX := M.ReadLongInt();
2733 endY := M.ReadLongInt();
2734 sizeSpX := M.ReadLongInt();
2735 sizeSpY := M.ReadLongInt();
2736 sizeEX := M.ReadLongInt();
2737 sizeEY := M.ReadLongInt();
2738 mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
2740 TP := g_Map_PanelByGUID(PGUID);
2741 if (TP = nil) then exit;
2743 // update lifts state
2744 if TP.isGLift then g_Map_SetLiftGUID(PGUID, Lift);
2746 // update enabled/disabled state for all panels
2747 if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
2749 // update panel position, as it can be moved (mplat)
2750 TP.X := X;
2751 TP.Y := Y;
2752 TP.Width := W;
2753 TP.Height := H;
2754 // update mplat state
2755 TP.movingSpeedX := speedX;
2756 TP.movingSpeedY := speedY;
2757 TP.movingStartX := startX;
2758 TP.movingStartY := startY;
2759 TP.movingEndX := endX;
2760 TP.movingEndY := endY;
2761 TP.sizeSpeedX := sizeSpX;
2762 TP.sizeSpeedY := sizeSpY;
2763 TP.sizeEndX := sizeEX;
2764 TP.sizeEndY := sizeEY;
2765 TP.movingActive := ((mpflags and 1) <> 0);
2766 TP.moveOnce := ((mpflags and 2) <> 0);
2767 // notify panel of it's position/size change, so it can fix other internal structures
2768 TP.positionChanged();
2769 end;
2771 // TRIGGERS
2773 procedure MC_RECV_TriggerSound(var M: TMsg);
2774 var
2775 SPlaying: Boolean;
2776 SPos, SID: LongWord;
2777 SCount: LongInt;
2778 I: Integer;
2779 begin
2780 if not gGameOn then Exit;
2781 if gTriggers = nil then Exit;
2783 SID := M.ReadLongWord();
2784 SPlaying := M.ReadByte() <> 0;
2785 SPos := M.ReadLongWord();
2786 SCount := M.ReadLongInt();
2788 for I := Low(gTriggers) to High(gTriggers) do
2789 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2790 if gTriggers[I].ClientID = SID then
2791 with gTriggers[I] do
2792 begin
2793 if Sound <> nil then
2794 begin
2795 if SPlaying then
2796 begin
2797 if tgcLocal then
2798 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0)
2799 else
2800 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2801 Sound.SetPosition(SPos);
2802 end
2803 else
2804 if Sound.IsPlaying then Sound.Stop;
2805 end;
2807 SoundPlayCount := SCount;
2808 end;
2809 end;
2811 procedure MC_RECV_TriggerMusic(var M: TMsg);
2812 var
2813 MName: string;
2814 MPlaying: Boolean;
2815 MPos: LongWord;
2816 MPaused: Boolean;
2817 begin
2818 if not gGameOn then Exit;
2820 MName := M.ReadString();
2821 MPlaying := M.ReadByte() <> 0;
2822 MPos := M.ReadLongWord();
2823 MPaused := M.ReadByte() <> 0;
2824 MPos := MPos+1; //k8: stfu, fpc!
2826 if MPlaying then
2827 begin
2828 gMusic.SetByName(MName);
2829 gMusic.Play(True);
2830 // gMusic.SetPosition(MPos);
2831 gMusic.SpecPause := MPaused;
2832 end
2833 else
2834 if gMusic.IsPlaying then gMusic.Stop;
2835 end;
2837 // MONSTERS
2839 procedure MC_RECV_MonsterSpawn(var M: TMsg);
2840 var
2841 ID: Word;
2842 MType, MState, MDir, MAnim, MBehav: Byte;
2843 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2844 MTarg: Word;
2845 Mon: TMonster;
2846 begin
2847 ID := M.ReadWord();
2848 Mon := g_Monsters_ByUID(ID);
2849 if Mon <> nil then
2850 Exit;
2852 MType := M.ReadByte();
2853 MState := M.ReadByte();
2854 MAnim := M.ReadByte();
2855 MTarg := M.ReadWord();
2856 MTargTime := M.ReadLongInt();
2857 MBehav := M.ReadByte();
2858 MSleep := M.ReadLongInt();
2859 MHealth := M.ReadLongInt();
2860 MAmmo := M.ReadLongInt();
2862 X := M.ReadLongInt();
2863 Y := M.ReadLongInt();
2864 VX := M.ReadLongInt();
2865 VY := M.ReadLongInt();
2866 MDir := M.ReadByte();
2868 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
2869 Mon := g_Monsters_ByUID(ID);
2870 if Mon = nil then
2871 Exit;
2873 with Mon do
2874 begin
2876 MonsterAnim := MAnim;
2877 MonsterTargetUID := MTarg;
2878 MonsterTargetTime := MTargTime;
2879 MonsterBehaviour := MBehav;
2880 MonsterSleep := MSleep;
2881 MonsterAmmo := MAmmo;
2882 SetHealth(MHealth);
2884 SetState(MState);
2886 setPosition(X, Y); // this will call positionChanged();
2887 GameVelX := VX;
2888 GameVelY := VY;
2889 end;
2890 end;
2892 procedure MC_RECV_MonsterPos(var M: TMsg);
2893 var
2894 Mon: TMonster;
2895 ID: Word;
2896 X, Y: Integer;
2897 begin
2898 ID := M.ReadWord();
2899 Mon := g_Monsters_ByUID(ID);
2900 if Mon = nil then
2901 Exit;
2903 with Mon do
2904 begin
2905 X := M.ReadLongInt();
2906 Y := M.ReadLongInt();
2907 Mon.setPosition(X, Y); // this will call `positionChanged()`
2908 GameVelX := M.ReadLongInt();
2909 GameVelY := M.ReadLongInt();
2910 GameDirection := TDirection(M.ReadByte());
2911 end;
2912 end;
2914 procedure MC_RECV_MonsterState(var M: TMsg);
2915 var
2916 ID, OldFire: Integer;
2917 MState, MFAnm: Byte;
2918 Mon: TMonster;
2919 AnimRevert: Boolean;
2920 begin
2921 ID := M.ReadWord();
2922 Mon := g_Monsters_ByUID(ID);
2923 if Mon = nil then Exit;
2925 MState := M.ReadByte();
2926 MFAnm := M.ReadByte();
2928 with Mon do
2929 begin
2930 MonsterTargetUID := M.ReadWord();
2931 MonsterTargetTime := M.ReadLongInt();
2932 MonsterSleep := M.ReadLongInt();
2933 MonsterHealth := M.ReadLongInt();
2934 MonsterAmmo := M.ReadLongInt();
2935 MonsterPain := M.ReadLongInt();
2936 AnimRevert := M.ReadByte() <> 0;
2937 OldFire := FFireTime;
2938 FFireTime := M.ReadLongInt();
2939 if (OldFire <= 0) and (FFireTime > 0) then
2940 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2941 RevertAnim(AnimRevert);
2943 if MonsterState <> MState then
2944 begin
2945 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then WakeUpSound();
2946 if (MState = MONSTATE_DIE) then DieSound();
2947 if (MState = MONSTATE_PAIN) then MakeBloodSimple(Min(200, MonsterPain));
2948 if (MState = MONSTATE_ATTACK) then kick(nil);
2949 if (MState = MONSTATE_DEAD) then SetDeadAnim();
2951 SetState(MState, MFAnm);
2952 end;
2953 end;
2954 end;
2956 procedure MC_RECV_MonsterShot(var M: TMsg);
2957 var
2958 ID: Integer;
2959 Mon: TMonster;
2960 X, Y, VX, VY: Integer;
2961 begin
2962 ID := M.ReadWord();
2964 Mon := g_Monsters_ByUID(ID);
2965 if Mon = nil then Exit;
2967 X := M.ReadLongInt();
2968 Y := M.ReadLongInt();
2969 VX := M.ReadLongInt();
2970 VY := M.ReadLongInt();
2972 Mon.ClientAttack(X, Y, VX, VY);
2973 end;
2975 procedure MC_RECV_MonsterDelete(var M: TMsg);
2976 var
2977 ID: Integer;
2978 Mon: TMonster;
2979 begin
2980 ID := M.ReadWord();
2981 Mon := g_Monsters_ByUID(ID);
2982 if Mon = nil then Exit;
2983 Mon.SetState(5);
2984 Mon.MonsterRemoved := True;
2985 end;
2987 procedure MC_RECV_TimeSync(var M: TMsg);
2988 var
2989 Time: LongWord;
2990 begin
2991 Time := M.ReadLongWord();
2993 if gState = STATE_INTERCUSTOM then
2994 gServInterTime := Min(Time, 255);
2995 end;
2997 procedure MC_RECV_VoteEvent(var M: TMsg);
2998 var
2999 EvID: Byte;
3000 Str1, Str2: string;
3001 Int1, Int2: SmallInt;
3002 begin
3003 EvID := M.ReadByte();
3004 Int1 := M.ReadSmallInt();
3005 Int2 := M.ReadSmallInt();
3006 Str1 := M.ReadString();
3007 Str2 := M.ReadString();
3009 if NetDeafLevel < 2 then
3010 case EvID of
3011 NET_VE_STARTED:
3012 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
3013 NET_VE_PASSED:
3014 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
3015 NET_VE_FAILED:
3016 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
3017 NET_VE_VOTE:
3018 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
3019 NET_VE_INPROGRESS:
3020 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
3021 end;
3022 end;
3024 // CLIENT SEND
3026 procedure MC_SEND_Info(Password: string);
3027 begin
3028 NetOut.Clear();
3030 NetOut.Write(Byte(NET_MSG_INFO));
3031 NetOut.Write(GAME_VERSION);
3032 NetOut.Write(Password);
3033 NetOut.Write(gPlayer1Settings.Name);
3034 NetOut.Write(gPlayer1Settings.Model);
3035 NetOut.Write(gPlayer1Settings.Color.R);
3036 NetOut.Write(gPlayer1Settings.Color.G);
3037 NetOut.Write(gPlayer1Settings.Color.B);
3038 NetOut.Write(gPlayer1Settings.Team);
3040 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3041 end;
3043 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
3044 begin
3045 NetOut.Write(Byte(NET_MSG_CHAT));
3046 NetOut.Write(Txt);
3047 NetOut.Write(Mode);
3049 g_Net_Client_Send(True, NET_CHAN_CHAT);
3050 end;
3052 procedure MC_SEND_PlayerPos();
3053 var
3054 kByte: Word;
3055 Predict: Boolean;
3056 strafeDir: Byte;
3057 WeaponAct: Byte = 0;
3058 WeaponSelect: Word = 0;
3059 i: Integer;
3060 begin
3061 if not gGameOn then Exit;
3062 if gPlayers = nil then Exit;
3063 if gPlayer1 = nil then Exit;
3065 kByte := 0;
3066 Predict := NetPredictSelf; // and (not NetGotKeys);
3068 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
3069 begin
3070 strafeDir := P1MoveButton shr 4;
3071 P1MoveButton := P1MoveButton and $0F;
3073 if gPlayerAction[0, ACTION_MOVELEFT] and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
3074 P1MoveButton := 1
3075 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and gPlayerAction[0, ACTION_MOVERIGHT] then
3076 P1MoveButton := 2
3077 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
3078 P1MoveButton := 0;
3080 // strafing
3081 if gPlayerAction[0, ACTION_STRAFE] then
3082 begin
3083 // new strafe mechanics
3084 if (strafeDir = 0) then
3085 strafeDir := P1MoveButton; // start strafing
3086 // now set direction according to strafe (reversed)
3087 if (strafeDir = 2) then
3088 gPlayer1.SetDirection(TDirection.D_LEFT)
3089 else if (strafeDir = 1) then
3090 gPlayer1.SetDirection(TDirection.D_RIGHT)
3091 end
3092 else
3093 begin
3094 strafeDir := 0; // not strafing anymore
3095 if (P1MoveButton = 2) and gPlayerAction[0, ACTION_MOVELEFT] then
3096 gPlayer1.SetDirection(TDirection.D_LEFT)
3097 else if (P1MoveButton = 1) and gPlayerAction[0, ACTION_MOVERIGHT] then
3098 gPlayer1.SetDirection(TDirection.D_RIGHT)
3099 else if P1MoveButton <> 0 then
3100 gPlayer1.SetDirection(TDirection(P1MoveButton-1));
3101 end;
3103 gPlayer1.ReleaseKeys;
3104 if P1MoveButton = 1 then
3105 begin
3106 kByte := kByte or NET_KEY_LEFT;
3107 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
3108 end;
3109 if P1MoveButton = 2 then
3110 begin
3111 kByte := kByte or NET_KEY_RIGHT;
3112 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
3113 end;
3114 if gPlayerAction[0, ACTION_LOOKUP] then
3115 begin
3116 kByte := kByte or NET_KEY_UP;
3117 gPlayer1.PressKey(KEY_UP, 10000);
3118 end;
3119 if gPlayerAction[0, ACTION_LOOKDOWN] then
3120 begin
3121 kByte := kByte or NET_KEY_DOWN;
3122 gPlayer1.PressKey(KEY_DOWN, 10000);
3123 end;
3124 if gPlayerAction[0, ACTION_JUMP] then
3125 begin
3126 kByte := kByte or NET_KEY_JUMP;
3127 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
3128 end;
3129 if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE;
3130 if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN;
3132 for i := WP_FACT to WP_LACT do
3133 begin
3134 if gWeaponAction[0, i] then
3135 begin
3136 WeaponAct := WeaponAct or Byte(1 shl i);
3137 gWeaponAction[0, i] := False
3138 end
3139 end;
3141 for i := WP_FIRST to WP_LAST do
3142 begin
3143 if gSelectWeapon[0, i] then
3144 begin
3145 WeaponSelect := WeaponSelect or Word(1 shl i);
3146 gSelectWeapon[0, i] := False
3147 end
3148 end;
3150 // fix movebutton state
3151 P1MoveButton := P1MoveButton or (strafeDir shl 4);
3152 end
3153 else
3154 kByte := NET_KEY_CHAT;
3156 NetOut.Write(Byte(NET_MSG_PLRPOS));
3157 NetOut.Write(gTime);
3158 NetOut.Write(kByte);
3159 NetOut.Write(Byte(gPlayer1.Direction));
3160 NetOut.Write(WeaponAct);
3161 NetOut.Write(WeaponSelect);
3162 //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
3163 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
3165 //kBytePrev := kByte;
3166 //kDirPrev := gPlayer1.Direction;
3167 end;
3169 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
3170 begin
3171 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
3172 NetOut.Write(Byte(Start));
3173 NetOut.Write(Command);
3174 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3175 end;
3177 procedure MC_SEND_PlayerSettings();
3178 begin
3179 NetOut.Write(Byte(NET_MSG_PLRSET));
3180 NetOut.Write(gPlayer1Settings.Name);
3181 NetOut.Write(gPlayer1Settings.Model);
3182 NetOut.Write(gPlayer1Settings.Color.R);
3183 NetOut.Write(gPlayer1Settings.Color.G);
3184 NetOut.Write(gPlayer1Settings.Color.B);
3185 NetOut.Write(gPlayer1Settings.Team);
3187 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3188 end;
3190 procedure MC_SEND_FullStateRequest();
3191 begin
3192 NetOut.Write(Byte(NET_MSG_REQFST));
3194 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3195 end;
3197 procedure MC_SEND_CheatRequest(Kind: Byte);
3198 begin
3199 NetOut.Write(Byte(NET_MSG_CHEAT));
3200 NetOut.Write(Kind);
3202 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3203 end;
3204 procedure MC_SEND_RCONPassword(Password: string);
3205 begin
3206 NetOut.Write(Byte(NET_MSG_RCON_AUTH));
3207 NetOut.Write(Password);
3209 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3210 end;
3211 procedure MC_SEND_RCONCommand(Cmd: string);
3212 begin
3213 NetOut.Write(Byte(NET_MSG_RCON_CMD));
3214 NetOut.Write(Cmd);
3216 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3217 end;
3220 end.