DEADSOFTWARE

ddef35df216b9f645023eef3ab3d511aee45fe38
[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;
35 NET_MSG_PLR = 111;
36 NET_MSG_PLRPOS = 112;
37 NET_MSG_PLRSTA = 113;
38 NET_MSG_PLRDEL = 114;
39 NET_MSG_PLRDMG = 115;
40 NET_MSG_PLRDIE = 116;
41 NET_MSG_PLRFIRE= 117;
42 NET_MSG_PLRSET = 119;
43 NET_MSG_CHEAT = 120;
45 NET_MSG_ISPAWN = 121;
46 NET_MSG_IDEL = 122;
48 NET_MSG_MSPAWN = 131;
49 NET_MSG_MPOS = 132;
50 NET_MSG_MSTATE = 133;
51 NET_MSG_MSHOT = 134;
52 NET_MSG_MDEL = 135;
54 NET_MSG_PSTATE = 141;
55 NET_MSG_PTEX = 142;
57 NET_MSG_TSOUND = 151;
58 NET_MSG_TMUSIC = 152;
60 NET_MSG_SHDEL = 161;
61 NET_MSG_SHADD = 162;
62 NET_MSG_SHPOS = 163;
64 NET_MSG_RCON_AUTH = 191;
65 NET_MSG_RCON_CMD = 192;
66 NET_MSG_TIME_SYNC = 194;
67 NET_MSG_VOTE_EVENT = 195;
69 {
70 NET_MSG_MAP_REQUEST = 201;
71 NET_MSG_MAP_RESPONSE = 202;
72 NET_MSG_RES_REQUEST = 203;
73 NET_MSG_RES_RESPONSE = 204;
74 }
76 NET_CHAT_SYSTEM = 0;
77 NET_CHAT_PLAYER = 1;
78 NET_CHAT_TEAM = 2;
80 NET_RCON_NOAUTH = 0;
81 NET_RCON_PWGOOD = 1;
82 NET_RCON_PWBAD = 2;
84 NET_GFX_SPARK = 1;
85 NET_GFX_TELE = 2;
86 NET_GFX_RESPAWN = 3;
87 NET_GFX_FIRE = 4;
88 NET_GFX_EXPLODE = 5;
89 NET_GFX_BFGEXPL = 6;
90 NET_GFX_BFGHIT = 7;
91 NET_GFX_SHELL1 = 8;
92 NET_GFX_SHELL2 = 9;
93 NET_GFX_SHELL3 = 10;
95 NET_EV_MAPSTART = 1;
96 NET_EV_MAPEND = 2;
97 NET_EV_CHANGE_TEAM = 3;
98 NET_EV_PLAYER_KICK = 4;
99 NET_EV_PLAYER_BAN = 5;
100 NET_EV_LMS_WARMUP = 6;
101 NET_EV_LMS_SURVIVOR = 7;
102 NET_EV_RCON = 8;
103 NET_EV_BIGTEXT = 9;
104 NET_EV_SCORE = 10;
105 NET_EV_SCORE_MSG = 11;
106 NET_EV_LMS_START = 12;
107 NET_EV_LMS_WIN = 13;
108 NET_EV_TLMS_WIN = 14;
109 NET_EV_LMS_LOSE = 15;
110 NET_EV_LMS_DRAW = 16;
111 NET_EV_KILLCOMBO = 17;
112 NET_EV_PLAYER_TOUCH = 18;
113 NET_EV_SECRET = 19;
114 NET_EV_INTER_READY = 20;
116 NET_VE_STARTED = 1;
117 NET_VE_PASSED = 2;
118 NET_VE_FAILED = 3;
119 NET_VE_VOTE = 4;
120 NET_VE_REVOKE = 5;
121 NET_VE_INPROGRESS = 6;
123 NET_FLAG_GET = 1;
124 NET_FLAG_DROP = 2;
125 NET_FLAG_CAP = 3;
126 NET_FLAG_RETURN = 4;
128 NET_CHEAT_SUICIDE = 1;
129 NET_CHEAT_SPECTATE = 2;
130 NET_CHEAT_READY = 3;
132 NET_MAX_DIFFTIME = 5000 div 36;
134 // HOST MESSAGES
136 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
137 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
138 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
139 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
140 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
141 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
142 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
143 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
144 //procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg);
145 //procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg);
146 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
148 // GAME
149 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
150 procedure MH_SEND_Info(ID: Byte);
151 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
152 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
153 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
154 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
155 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
156 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
157 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
158 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
159 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
160 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
162 // PLAYER
163 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
165 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
166 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
167 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
168 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
169 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
170 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
171 // ITEM
172 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
173 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
174 // PANEL
175 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
176 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
177 // MONSTER
178 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
179 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
180 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
181 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
182 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
183 // TRIGGER
184 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
185 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
186 // MISC
187 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
188 procedure MH_SEND_VoteEvent(EvType: Byte;
189 StrArg1: string = 'a'; StrArg2: string = 'b';
190 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
191 ID: Integer = NET_EVERYONE);
193 // CLIENT MESSAGES //
195 // GAME
196 procedure MC_RECV_Chat(var M: TMsg);
197 procedure MC_RECV_Effect(var M: TMsg);
198 procedure MC_RECV_Sound(var M: TMsg);
199 procedure MC_RECV_GameStats(var M: TMsg);
200 procedure MC_RECV_CoopStats(var M: TMsg);
201 procedure MC_RECV_GameEvent(var M: TMsg);
202 procedure MC_RECV_FlagEvent(var M: TMsg);
203 procedure MC_RECV_GameSettings(var M: TMsg);
204 // PLAYER
205 function MC_RECV_PlayerCreate(var M: TMsg): Word;
206 function MC_RECV_PlayerPos(var M: TMsg): Word;
207 function MC_RECV_PlayerStats(var M: TMsg): Word;
208 function MC_RECV_PlayerDelete(var M: TMsg): Word;
209 function MC_RECV_PlayerDamage(var M: TMsg): Word;
210 function MC_RECV_PlayerDeath(var M: TMsg): Word;
211 function MC_RECV_PlayerFire(var M: TMsg): Word;
212 procedure MC_RECV_PlayerSettings(var M: TMsg);
213 // ITEM
214 procedure MC_RECV_ItemSpawn(var M: TMsg);
215 procedure MC_RECV_ItemDestroy(var M: TMsg);
216 // PANEL
217 procedure MC_RECV_PanelTexture(var M: TMsg);
218 procedure MC_RECV_PanelState(var M: TMsg);
219 // MONSTER
220 procedure MC_RECV_MonsterSpawn(var M: TMsg);
221 procedure MC_RECV_MonsterPos(var M: TMsg);
222 procedure MC_RECV_MonsterState(var M: TMsg);
223 procedure MC_RECV_MonsterShot(var M: TMsg);
224 procedure MC_RECV_MonsterDelete(var M: TMsg);
225 // SHOT
226 procedure MC_RECV_CreateShot(var M: TMsg);
227 procedure MC_RECV_UpdateShot(var M: TMsg);
228 procedure MC_RECV_DeleteShot(var M: TMsg);
229 // TRIGGER
230 procedure MC_RECV_TriggerSound(var M: TMsg);
231 procedure MC_RECV_TriggerMusic(var M: TMsg);
232 // MISC
233 procedure MC_RECV_TimeSync(var M: TMsg);
234 procedure MC_RECV_VoteEvent(var M: TMsg);
235 // SERVICE
236 procedure MC_SEND_Info(Password: string);
237 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
238 procedure MC_SEND_PlayerPos();
239 procedure MC_SEND_FullStateRequest();
240 procedure MC_SEND_PlayerSettings();
241 procedure MC_SEND_CheatRequest(Kind: Byte);
242 procedure MC_SEND_RCONPassword(Password: string);
243 procedure MC_SEND_RCONCommand(Cmd: string);
244 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
245 // DOWNLOAD
246 //procedure MC_SEND_MapRequest();
247 //procedure MC_SEND_ResRequest(const resName: AnsiString);
250 type
251 TExternalResourceInfo = record
252 Name: string[255];
253 md5: TMD5Digest;
254 end;
256 TResDataMsg = record
257 MsgId: Byte;
258 FileSize: Integer;
259 FileData: AByte;
260 end;
262 TMapDataMsg = record
263 MsgId: Byte;
264 FileSize: Integer;
265 FileData: AByte;
266 ExternalResources: array of TExternalResourceInfo;
267 end;
269 function IsValidFileName(const S: String): Boolean;
270 function IsValidFilePath(const S: String): Boolean;
273 implementation
275 uses
276 Math, ENet, e_input, e_graphics, e_log,
277 g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main,
278 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
279 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
281 const
282 NET_KEY_LEFT = 1;
283 NET_KEY_RIGHT = 2;
284 NET_KEY_UP = 4;
285 NET_KEY_DOWN = 8;
286 NET_KEY_JUMP = 16;
287 NET_KEY_FIRE = 32;
288 NET_KEY_OPEN = 64;
289 NET_KEY_NW = 256;
290 NET_KEY_PW = 512;
291 NET_KEY_CHAT = 2048;
292 NET_KEY_FORCEDIR = 4096;
294 //var
295 //kBytePrev: Word = 0;
296 //kDirPrev: TDirection = D_LEFT;
297 //HostGameTime: Word = 0;
300 function IsValidFileName(const S: String): Boolean;
301 const
302 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
303 var
304 I: Integer;
305 begin
306 Result := S <> '';
307 for I := 1 to Length(S) do
308 Result := Result and (not(S[I] in Forbidden));
309 end;
311 function IsValidFilePath(const S: String): Boolean;
312 var
313 I: Integer;
314 begin
315 Result := False;
316 if not IsValidFileName(S) then exit;
317 if FileExists(S) then exit;
318 I := LastDelimiter('\/', S);
319 if (I > 0) then
320 if (not DirectoryExists(Copy(S, 1, I-1))) then
321 exit;
322 Result := True;
323 end;
326 // HOST MESSAGES //
329 // GAME
331 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
332 var
333 Txt: string;
334 Mode: Byte;
335 PID: Word;
336 Pl: TPlayer;
337 begin
338 PID := C^.Player;
339 Pl := g_Player_Get(PID);
341 Txt := M.ReadString();
342 Mode := M.ReadByte();
343 if (Mode = NET_CHAT_SYSTEM) then
344 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
345 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
346 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
348 if Pl = nil then
349 MH_SEND_Chat(Txt, Mode)
350 else
351 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
352 end;
354 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
355 var
356 Ver, PName, Model, Pw: string;
357 R, G, B, T: Byte;
358 PID: Word;
359 Color: TRGB;
360 I: Integer;
361 begin
362 Ver := M.ReadString();
363 Pw := M.ReadString();
364 PName := M.ReadString();
365 Model := M.ReadString();
366 R := M.ReadByte();
367 G := M.ReadByte();
368 B := M.ReadByte();
369 T := M.ReadByte();
371 if Ver <> GAME_VERSION then
372 begin
373 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
374 _lc[I_NET_DISC_VERSION]);
375 enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
376 Exit;
377 end;
379 if g_Net_IsHostBanned(C^.Peer^.address.host) then
380 begin
381 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
382 begin
383 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
384 _lc[I_NET_DISC_BAN]);
385 enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
386 end
387 else
388 begin
389 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
390 _lc[I_NET_DISC_BAN]);
391 enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
392 end;
393 Exit;
394 end;
396 if NetPassword <> '' then
397 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
398 begin
399 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
400 _lc[I_NET_DISC_PASSWORD]);
401 enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
402 Exit;
403 end;
405 Color.R := R;
406 Color.B := B;
407 Color.G := G;
409 PID := g_Player_Create(Model, Color, T, False);
410 with g_Player_Get(PID) do
411 begin
412 Name := PName;
413 Reset(True);
414 end;
416 C^.Player := PID;
418 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
419 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
420 '] connected. Assigned player #' + IntToStr(PID) + '.', TMsgType.Notify);
422 MH_SEND_Info(C^.ID);
424 with g_Player_Get(PID) do
425 begin
426 Name := PName;
427 FClientID := C^.ID;
428 // round in progress, don't spawn
429 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
430 begin
431 Lives := 0;
432 FNoRespawn := True;
433 Spectate;
434 FWantsInGame := True; // TODO: look into this later
435 end
436 else
437 Respawn(gGameSettings.GameType = GT_SINGLE);
438 end;
440 for I := Low(NetClients) to High(NetClients) do
441 begin
442 if NetClients[I].ID = C^.ID then Continue;
443 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
444 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
445 MH_SEND_PlayerStats(PID, NetClients[I].ID);
446 end;
448 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
449 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
451 if NetUseMaster then g_Net_Slist_Update;
452 end;
454 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
455 begin
456 if gGameOn then
457 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
458 else
459 C^.RequestedFullUpdate := True;
460 end;
462 // PLAYER
464 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
465 var
466 Dir, i: Byte;
467 WeaponSelect: Word;
468 PID: Word;
469 kByte: Word;
470 Pl: TPlayer;
471 GT: LongWord;
472 begin
473 Result := 0;
474 if not gGameOn then Exit;
476 GT := M.ReadLongWord();
477 PID := C^.Player;
478 Pl := g_Player_Get(PID);
479 if Pl = nil then
480 Exit;
482 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
484 with Pl do
485 begin
486 NetTime := GT;
487 kByte := M.ReadWord();
488 Dir := M.ReadByte();
489 WeaponSelect := M.ReadWord();
490 //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
491 if Direction <> TDirection(Dir) then
492 JustTeleported := False;
494 SetDirection(TDirection(Dir));
495 ReleaseKeys;
497 if kByte = NET_KEY_CHAT then
498 begin
499 PressKey(KEY_CHAT, 10000);
500 Exit;
501 end;
503 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
504 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
505 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
506 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
507 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
508 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
509 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
510 if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
511 if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
513 for i := 0 to 15 do
514 begin
515 if (WeaponSelect and Word(1 shl i)) <> 0 then
516 begin
517 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
518 QueueWeaponSwitch(i);
519 end;
520 end;
521 end;
523 // MH_SEND_PlayerPos(False, PID, C^.ID);
524 end;
526 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
527 var
528 CheatKind: Byte;
529 Pl: TPlayer;
530 begin
531 Pl := g_Player_Get(C^.Player);
532 if Pl = nil then Exit;
534 CheatKind := M.ReadByte();
536 case CheatKind of
537 NET_CHEAT_SUICIDE:
538 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
539 NET_CHEAT_SPECTATE:
540 begin
541 if Pl.FSpectator then
542 Pl.Respawn(False)
543 else
544 Pl.Spectate;
545 end;
546 NET_CHEAT_READY:
547 begin
548 if gState <> STATE_INTERCUSTOM then Exit;
549 Pl.FReady := not Pl.FReady;
550 if Pl.FReady then
551 begin
552 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'Y');
553 Inc(gInterReadyCount);
554 end
555 else
556 begin
557 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'N');
558 Dec(gInterReadyCount);
559 end;
560 end;
561 end;
562 end;
564 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
565 var
566 TmpName: string;
567 TmpModel: string;
568 TmpColor: TRGB;
569 TmpTeam: Byte;
570 Pl: TPlayer;
571 begin
572 TmpName := M.ReadString();
573 TmpModel := M.ReadString();
574 TmpColor.R := M.ReadByte();
575 TmpColor.G := M.ReadByte();
576 TmpColor.B := M.ReadByte();
577 TmpTeam := M.ReadByte();
579 Pl := g_Player_Get(C^.Player);
580 if Pl = nil then Exit;
582 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
583 Pl.SwitchTeam
584 else
585 Pl.SetColor(TmpColor);
587 if Pl.Name <> TmpName then
588 begin
589 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
590 Pl.Name := TmpName;
591 end;
593 if TmpModel <> Pl.Model.Name then
594 Pl.SetModel(TmpModel);
596 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
597 end;
599 // RCON
601 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
602 var
603 Pwd: string;
604 begin
605 Pwd := M.ReadString();
606 if not NetAllowRCON then Exit;
607 if Pwd = NetRCONPassword then
608 begin
609 C^.RCONAuth := True;
610 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
611 end
612 else
613 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
614 end;
616 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
617 var
618 Cmd: string;
619 begin
620 Cmd := M.ReadString();
621 if not NetAllowRCON then Exit;
622 if not C^.RCONAuth then
623 begin
624 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
625 Exit;
626 end;
627 g_Console_Process(Cmd);
628 end;
630 // MISC
632 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
633 var
634 Start: Boolean;
635 Name, Command: string;
636 Need: Integer;
637 Pl: TPlayer;
638 begin
639 Start := M.ReadByte() <> 0;
640 Command := M.ReadString();
642 Pl := g_Player_Get(C^.Player);
643 if Pl = nil then Exit;
644 Name := Pl.Name;
646 if Start then
647 begin
648 if not g_Console_CommandBlacklisted(Command) then
649 g_Game_StartVote(Command, Name);
650 end
651 else if gVoteInProgress then
652 begin
653 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
654 Need := Floor((NetClientCount+1)/2.0) + 1
655 else
656 Need := Floor(NetClientCount/2.0) + 1;
657 if C^.Voted then
658 begin
659 Dec(gVoteCount);
660 C^.Voted := False;
661 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
662 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
663 end
664 else
665 begin
666 Inc(gVoteCount);
667 C^.Voted := True;
668 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
669 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
670 g_Game_CheckVote;
671 end;
672 end;
673 end;
675 // GAME (SEND)
677 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
679 function sendItemRespawn (it: PItem): Boolean;
680 begin
681 result := false; // don't stop
682 MH_SEND_ItemSpawn(True, it.myid, ID);
683 end;
685 function sendMonSpawn (mon: TMonster): Boolean;
686 begin
687 result := false; // don't stop
688 MH_SEND_MonsterSpawn(mon.UID, ID);
689 end;
691 function sendPanelState (pan: TPanel): Boolean;
692 begin
693 result := false; // don't stop
694 MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
695 if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
696 end;
698 var
699 I: Integer;
700 begin
701 if gPlayers <> nil then
702 begin
703 for I := Low(gPlayers) to High(gPlayers) do
704 begin
705 if gPlayers[I] <> nil then
706 begin
707 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
708 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
709 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
711 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
712 begin
713 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
714 end;
715 end;
716 end;
717 end;
719 g_Items_ForEachAlive(sendItemRespawn, true); // backwards
720 g_Mons_ForEach(sendMonSpawn);
721 g_Map_ForEachPanel(sendPanelState);
723 if gTriggers <> nil then
724 begin
725 for I := Low(gTriggers) to High(gTriggers) do
726 begin
727 if gTriggers[I].TriggerType = TRIGGER_SOUND then
728 begin
729 MH_SEND_TriggerSound(gTriggers[I], ID);
730 end;
731 end;
732 end;
734 if Shots <> nil then
735 begin
736 for I := Low(Shots) to High(Shots) do
737 begin
738 if Shots[i].ShotType in [6, 7, 8] then
739 begin
740 MH_SEND_CreateShot(i, ID);
741 end;
742 end;
743 end;
745 MH_SEND_TriggerMusic(ID);
747 MH_SEND_GameStats(ID);
748 MH_SEND_CoopStats(ID);
750 if gGameSettings.GameMode = GM_CTF then
751 begin
752 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
753 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
754 end;
756 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
758 if gLMSRespawn > LMS_RESPAWN_NONE then
759 begin
760 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID);
761 end;
763 g_Net_Flush();
764 end;
766 procedure MH_SEND_Info(ID: Byte);
767 var
768 Map: string;
769 begin
770 Map := g_ExtractFileName(gMapInfo.Map);
772 NetOut.Clear();
774 NetOut.Write(Byte(NET_MSG_INFO));
775 NetOut.Write(ID);
776 NetOut.Write(NetClients[ID].Player);
777 NetOut.Write(gGameSettings.WAD);
778 NetOut.Write(Map);
779 NetOut.Write(gWADHash);
780 NetOut.Write(gGameSettings.GameMode);
781 NetOut.Write(gGameSettings.GoalLimit);
782 NetOut.Write(gGameSettings.TimeLimit);
783 NetOut.Write(gGameSettings.MaxLives);
784 NetOut.Write(gGameSettings.Options);
785 NetOut.Write(gTime);
787 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
788 end;
790 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
791 var
792 Name: string;
793 i: Integer;
794 Team: Byte;
795 begin
796 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
797 Mode := NET_CHAT_PLAYER;
799 Team := 0;
800 if (Mode = NET_CHAT_TEAM) then
801 begin
802 for i := Low(gPlayers) to High(gPlayers) do
803 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
804 (gPlayers[i].Team = ID) then
805 begin
806 NetOut.Write(Byte(NET_MSG_CHAT));
807 NetOut.Write(Txt);
808 NetOut.Write(Mode);
809 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
810 end;
811 Team := ID;
812 ID := NET_EVERYONE;
813 end
814 else
815 begin
816 NetOut.Write(Byte(NET_MSG_CHAT));
817 NetOut.Write(Txt);
818 NetOut.Write(Mode);
819 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
820 end;
822 if Mode = NET_CHAT_SYSTEM then
823 Exit;
825 if ID = NET_EVERYONE then
826 begin
827 if Mode = NET_CHAT_PLAYER then
828 begin
829 g_Console_Add(Txt, True);
830 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
831 g_Game_ChatSound(b_Text_Unformat(Txt));
832 end
833 else
834 if Mode = NET_CHAT_TEAM then
835 if gPlayer1 <> nil then
836 begin
837 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
838 begin
839 g_Console_Add(#18'[Team] '#2 + Txt, True);
840 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
841 g_Game_ChatSound(b_Text_Unformat(Txt));
842 end
843 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
844 begin
845 g_Console_Add(#20'[Team] '#2 + Txt, True);
846 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
847 g_Game_ChatSound(b_Text_Unformat(Txt));
848 end;
849 end;
850 end
851 else
852 begin
853 Name := g_Net_ClientName_ByID(ID);
854 g_Console_Add('-> ' + Name + ': ' + Txt, True);
855 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), TMsgType.Notify);
856 g_Game_ChatSound(b_Text_Unformat(Txt), False);
857 end;
858 end;
860 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
861 begin
862 NetOut.Write(Byte(NET_MSG_GFX));
863 NetOut.Write(Kind);
864 NetOut.Write(X);
865 NetOut.Write(Y);
866 NetOut.Write(Ang);
868 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
869 end;
871 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
872 begin
873 NetOut.Write(Byte(NET_MSG_SND));
874 NetOut.Write(Name);
875 if Pos then
876 begin
877 NetOut.Write(Byte(1));
878 NetOut.Write(X);
879 NetOut.Write(Y);
880 end
881 else
882 NetOut.Write(Byte(0));
884 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
885 end;
887 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
888 begin
889 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
891 NetOut.Write(Byte(NET_MSG_SHADD));
892 NetOut.Write(Proj);
893 NetOut.Write(Shots[Proj].ShotType);
894 NetOut.Write(Shots[Proj].Target);
895 NetOut.Write(Shots[Proj].SpawnerUID);
896 NetOut.Write(Shots[Proj].Timeout);
897 NetOut.Write(Shots[Proj].Obj.X);
898 NetOut.Write(Shots[Proj].Obj.Y);
899 NetOut.Write(Shots[Proj].Obj.Vel.X);
900 NetOut.Write(Shots[Proj].Obj.Vel.Y);
902 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
903 end;
905 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
906 begin
907 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
909 NetOut.Write(Byte(NET_MSG_SHPOS));
910 NetOut.Write(Proj);
911 NetOut.Write(Shots[Proj].Obj.X);
912 NetOut.Write(Shots[Proj].Obj.Y);
913 NetOut.Write(Shots[Proj].Obj.Vel.X);
914 NetOut.Write(Shots[Proj].Obj.Vel.Y);
916 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
917 end;
919 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
920 begin
921 NetOut.Write(Byte(NET_MSG_SHDEL));
922 NetOut.Write(Proj);
923 NetOut.Write(Byte(Loud));
924 NetOut.Write(X);
925 NetOut.Write(Y);
927 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
928 end;
930 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
931 begin
932 NetOut.Write(Byte(NET_MSG_SCORE));
933 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
934 begin
935 NetOut.Write(gTeamStat[TEAM_RED].Goals);
936 NetOut.Write(gTeamStat[TEAM_BLUE].Goals);
937 end
938 else
939 if gGameSettings.GameMode = GM_COOP then
940 begin
941 NetOut.Write(gCoopMonstersKilled);
942 NetOut.Write(gCoopSecretsFound);
943 end;
945 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
946 end;
948 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
949 begin
950 NetOut.Write(Byte(NET_MSG_COOP));
951 NetOut.Write(gTotalMonsters);
952 NetOut.Write(gSecretsCount);
953 NetOut.Write(gCoopTotalMonstersKilled);
954 NetOut.Write(gCoopTotalSecretsFound);
955 NetOut.Write(gCoopTotalMonsters);
956 NetOut.Write(gCoopTotalSecrets);
957 end;
959 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
960 begin
961 NetOut.Write(Byte(NET_MSG_GEVENT));
962 NetOut.Write(EvType);
963 NetOut.Write(EvNum);
964 NetOut.Write(EvStr);
965 NetOut.Write(Byte(gLastMap));
966 NetOut.Write(gTime);
967 if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then
968 begin
969 NetOut.Write(Byte(1));
970 NetOut.Write(gWADHash);
971 end else
972 NetOut.Write(Byte(0));
974 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
975 end;
977 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
978 begin
979 NetOut.Write(Byte(NET_MSG_FLAG));
980 NetOut.Write(EvType);
981 NetOut.Write(Flag);
982 NetOut.Write(Byte(Quiet));
983 NetOut.Write(PID);
984 NetOut.Write(gFlags[Flag].State);
985 NetOut.Write(gFlags[Flag].CaptureTime);
986 NetOut.Write(gFlags[Flag].Obj.X);
987 NetOut.Write(gFlags[Flag].Obj.Y);
988 NetOut.Write(gFlags[Flag].Obj.Vel.X);
989 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
991 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
992 end;
994 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
995 begin
996 NetOut.Write(Byte(NET_MSG_GSET));
997 NetOut.Write(gGameSettings.GameMode);
998 NetOut.Write(gGameSettings.GoalLimit);
999 NetOut.Write(gGameSettings.TimeLimit);
1000 NetOut.Write(gGameSettings.MaxLives);
1001 NetOut.Write(gGameSettings.Options);
1003 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1004 end;
1006 // PLAYER (SEND)
1008 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
1009 var
1010 P: TPlayer;
1011 begin
1012 P := g_Player_Get(PID);
1013 if P = nil then Exit;
1015 NetOut.Write(Byte(NET_MSG_PLR));
1016 NetOut.Write(PID);
1017 NetOut.Write(P.Name);
1019 NetOut.Write(P.FActualModelName);
1020 NetOut.Write(P.FColor.R);
1021 NetOut.Write(P.FColor.G);
1022 NetOut.Write(P.FColor.B);
1023 NetOut.Write(P.Team);
1025 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
1026 end;
1028 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
1029 var
1030 kByte: Word;
1031 Pl: TPlayer;
1032 begin
1033 Pl := g_Player_Get(PID);
1034 if Pl = nil then Exit;
1035 if Pl.FDummy then Exit;
1037 NetOut.Write(Byte(NET_MSG_PLRPOS));
1038 NetOut.Write(gTime);
1039 NetOut.Write(PID);
1041 kByte := 0;
1043 with Pl do
1044 begin
1045 NetOut.Write(FPing);
1046 NetOut.Write(FLoss);
1047 if IsKeyPressed(KEY_CHAT) then
1048 kByte := NET_KEY_CHAT
1049 else
1050 begin
1051 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
1052 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
1053 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
1054 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1055 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1056 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1057 end;
1059 NetOut.Write(kByte);
1060 if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
1061 NetOut.Write(GameX);
1062 NetOut.Write(GameY);
1063 NetOut.Write(GameVelX);
1064 NetOut.Write(GameVelY);
1065 NetOut.Write(GameAccelX);
1066 NetOut.Write(GameAccelY);
1067 end;
1069 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1070 end;
1072 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1073 var
1074 P: TPlayer;
1075 I: Integer;
1076 begin
1077 P := g_Player_Get(PID);
1078 if P = nil then Exit;
1080 NetOut.Write(Byte(NET_MSG_PLRSTA));
1081 NetOut.Write(PID);
1083 with P do
1084 begin
1085 NetOut.Write(Byte(alive));
1086 NetOut.Write(Byte(GodMode));
1087 NetOut.Write(Health);
1088 NetOut.Write(Armor);
1089 NetOut.Write(Air);
1090 NetOut.Write(JetFuel);
1091 NetOut.Write(Lives);
1092 NetOut.Write(Team);
1094 for I := WP_FIRST to WP_LAST do
1095 NetOut.Write(Byte(FWeapon[I]));
1097 for I := A_BULLETS to A_HIGH do
1098 NetOut.Write(FAmmo[I]);
1100 for I := A_BULLETS to A_HIGH do
1101 NetOut.Write(FMaxAmmo[I]);
1103 for I := MR_SUIT to MR_MAX do
1104 NetOut.Write(LongWord(FMegaRulez[I]));
1106 NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez));
1107 NetOut.Write(Byte(R_KEY_RED in FRulez));
1108 NetOut.Write(Byte(R_KEY_GREEN in FRulez));
1109 NetOut.Write(Byte(R_KEY_BLUE in FRulez));
1110 NetOut.Write(Byte(R_BERSERK in FRulez));
1112 NetOut.Write(Frags);
1113 NetOut.Write(Death);
1115 NetOut.Write(CurrWeap);
1117 NetOut.Write(Byte(FSpectator));
1118 NetOut.Write(Byte(FGhost));
1119 NetOut.Write(Byte(FPhysics));
1120 NetOut.Write(Byte(FNoRespawn));
1121 NetOut.Write(Byte(FJetpack));
1122 NetOut.Write(FFireTime);
1123 NetOut.Write(Byte(FFlaming));
1124 end;
1126 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1127 end;
1129 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1130 begin
1131 NetOut.Write(Byte(NET_MSG_PLRDMG));
1132 NetOut.Write(PID);
1133 NetOut.Write(Kind);
1134 NetOut.Write(Attacker);
1135 NetOut.Write(Value);
1136 NetOut.Write(VX);
1137 NetOut.Write(VY);
1139 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1140 end;
1142 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1143 begin
1144 NetOut.Write(Byte(NET_MSG_PLRDIE));
1145 NetOut.Write(PID);
1146 NetOut.Write(KillType);
1147 NetOut.Write(DeathType);
1148 NetOut.Write(Attacker);
1150 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1151 end;
1153 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1154 begin
1155 NetOut.Write(Byte(NET_MSG_PLRFIRE));
1156 NetOut.Write(PID);
1157 NetOut.Write(Weapon);
1158 NetOut.Write(X);
1159 NetOut.Write(Y);
1160 NetOut.Write(AX);
1161 NetOut.Write(AY);
1162 NetOut.Write(ShotID);
1164 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1165 end;
1167 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1168 begin
1169 NetOut.Write(Byte(NET_MSG_PLRDEL));
1170 NetOut.Write(PID);
1172 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1173 end;
1175 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1176 var
1177 Pl: TPlayer;
1178 begin
1179 Pl := g_Player_Get(PID);
1180 if Pl = nil then Exit;
1182 NetOut.Write(Byte(NET_MSG_PLRSET));
1183 NetOut.Write(PID);
1184 NetOut.Write(Pl.Name);
1185 if Mdl = '' then
1186 NetOut.Write(Pl.Model.Name)
1187 else
1188 NetOut.Write(Mdl);
1189 NetOut.Write(Pl.FColor.R);
1190 NetOut.Write(Pl.FColor.G);
1191 NetOut.Write(Pl.FColor.B);
1192 NetOut.Write(Pl.Team);
1194 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1195 end;
1197 // ITEM (SEND)
1199 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1200 var
1201 it: PItem;
1202 tt: Byte;
1203 begin
1204 it := g_Items_ByIdx(IID);
1206 NetOut.Write(Byte(NET_MSG_ISPAWN));
1207 NetOut.Write(IID);
1208 NetOut.Write(Byte(Quiet));
1209 tt := it.ItemType;
1210 if it.dropped then tt := tt or $80;
1211 NetOut.Write(tt);
1212 NetOut.Write(Byte(it.Fall));
1213 NetOut.Write(Byte(it.Respawnable));
1214 NetOut.Write(it.Obj.X);
1215 NetOut.Write(it.Obj.Y);
1216 NetOut.Write(it.Obj.Vel.X);
1217 NetOut.Write(it.Obj.Vel.Y);
1219 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1220 end;
1222 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1223 begin
1224 NetOut.Write(Byte(NET_MSG_IDEL));
1225 NetOut.Write(IID);
1226 NetOut.Write(Byte(Quiet));
1228 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1229 end;
1231 // PANEL
1233 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1234 var
1235 TP: TPanel;
1236 begin
1237 TP := g_Map_PanelByGUID(PGUID);
1238 if (TP = nil) then exit;
1240 with TP do
1241 begin
1242 NetOut.Write(Byte(NET_MSG_PTEX));
1243 NetOut.Write(LongWord(PGUID));
1244 NetOut.Write(FCurTexture);
1245 NetOut.Write(FCurFrame);
1246 NetOut.Write(FCurFrameCount);
1247 NetOut.Write(AnimLoop);
1248 end;
1250 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1251 end;
1253 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
1254 var
1255 TP: TPanel;
1256 mpflags: Byte = 0;
1257 begin
1258 TP := g_Map_PanelByGUID(PGUID);
1259 if (TP = nil) then exit;
1261 NetOut.Write(Byte(NET_MSG_PSTATE));
1262 NetOut.Write(LongWord(PGUID));
1263 NetOut.Write(Byte(TP.Enabled));
1264 NetOut.Write(TP.LiftType);
1265 NetOut.Write(TP.X);
1266 NetOut.Write(TP.Y);
1267 NetOut.Write(Word(TP.Width));
1268 NetOut.Write(Word(TP.Height));
1269 // mplats
1270 NetOut.Write(LongInt(TP.movingSpeedX));
1271 NetOut.Write(LongInt(TP.movingSpeedY));
1272 NetOut.Write(LongInt(TP.movingStartX));
1273 NetOut.Write(LongInt(TP.movingStartY));
1274 NetOut.Write(LongInt(TP.movingEndX));
1275 NetOut.Write(LongInt(TP.movingEndY));
1276 NetOut.Write(LongInt(TP.sizeSpeedX));
1277 NetOut.Write(LongInt(TP.sizeSpeedY));
1278 NetOut.Write(LongInt(TP.sizeEndX));
1279 NetOut.Write(LongInt(TP.sizeEndY));
1280 if TP.movingActive then mpflags := mpflags or 1;
1281 if TP.moveOnce then mpflags := mpflags or 2;
1282 NetOut.Write(Byte(mpflags));
1284 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1285 end;
1287 // TRIGGER
1289 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1290 begin
1291 if gTriggers = nil then Exit;
1292 if T.Sound = nil then Exit;
1294 NetOut.Write(Byte(NET_MSG_TSOUND));
1295 NetOut.Write(T.ClientID);
1296 NetOut.Write(Byte(T.Sound.IsPlaying));
1297 NetOut.Write(LongWord(T.Sound.GetPosition));
1298 NetOut.Write(T.SoundPlayCount);
1300 g_Net_Host_Send(ID, True);
1301 end;
1303 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1304 begin
1305 NetOut.Write(Byte(NET_MSG_TMUSIC));
1306 NetOut.Write(gMusic.Name);
1307 NetOut.Write(Byte(gMusic.IsPlaying));
1308 NetOut.Write(LongWord(gMusic.GetPosition));
1309 NetOut.Write(Byte(gMusic.SpecPause or gMusic.IsPaused));
1311 g_Net_Host_Send(ID, True);
1312 end;
1314 // MONSTER
1316 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1317 var
1318 M: TMonster;
1319 begin
1320 M := g_Monsters_ByUID(UID);
1321 if M = nil then
1322 Exit;
1324 with M do
1325 begin
1326 NetOut.Write(Byte(NET_MSG_MSPAWN));
1327 NetOut.Write(UID);
1328 NetOut.Write(MonsterType);
1329 NetOut.Write(MonsterState);
1330 NetOut.Write(MonsterAnim);
1331 NetOut.Write(MonsterTargetUID);
1332 NetOut.Write(MonsterTargetTime);
1333 NetOut.Write(MonsterBehaviour);
1334 NetOut.Write(MonsterSleep);
1335 NetOut.Write(MonsterHealth);
1336 NetOut.Write(MonsterAmmo);
1337 NetOut.Write(GameX);
1338 NetOut.Write(GameY);
1339 NetOut.Write(GameVelX);
1340 NetOut.Write(GameVelY);
1341 NetOut.Write(Byte(GameDirection));
1342 end;
1344 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1345 end;
1347 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1348 var
1349 M: TMonster;
1350 begin
1351 M := g_Monsters_ByUID(UID);
1352 if M = nil then Exit;
1354 NetOut.Write(Byte(NET_MSG_MPOS));
1355 NetOut.Write(UID);
1357 with M do
1358 begin
1359 NetOut.Write(GameX);
1360 NetOut.Write(GameY);
1361 NetOut.Write(GameVelX);
1362 NetOut.Write(GameVelY);
1363 NetOut.Write(Byte(GameDirection));
1364 end;
1366 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1367 end;
1369 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1370 var
1371 M: TMonster;
1372 begin
1373 M := g_Monsters_ByUID(UID);
1374 if M = nil then Exit;
1376 NetOut.Write(Byte(NET_MSG_MSTATE));
1377 NetOut.Write(UID);
1379 with M do
1380 begin
1381 NetOut.Write(MonsterState);
1382 NetOut.Write(ForcedAnim);
1383 NetOut.Write(MonsterTargetUID);
1384 NetOut.Write(MonsterTargetTime);
1385 NetOut.Write(MonsterSleep);
1386 NetOut.Write(MonsterHealth);
1387 NetOut.Write(MonsterAmmo);
1388 NetOut.Write(MonsterPain);
1389 NetOut.Write(Byte(AnimIsReverse));
1390 NetOut.Write(FFireTime);
1391 end;
1393 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1394 end;
1396 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1397 begin
1398 NetOut.Write(Byte(NET_MSG_MSHOT));
1399 NetOut.Write(UID);
1400 NetOut.Write(X);
1401 NetOut.Write(Y);
1402 NetOut.Write(VX);
1403 NetOut.Write(VY);
1405 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1406 end;
1408 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1409 var
1410 M: TMonster;
1411 begin
1412 M := g_Monsters_ByUID(UID);
1413 if M = nil then Exit;
1415 NetOut.Write(Byte(NET_MSG_MDEL));
1416 NetOut.Write(UID);
1418 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1419 end;
1421 // MISC
1423 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1424 begin
1425 NetOut.Write(Byte(NET_MSG_TIME_SYNC));
1426 NetOut.Write(Time);
1428 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1429 end;
1431 procedure MH_SEND_VoteEvent(EvType: Byte;
1432 StrArg1: string = 'a'; StrArg2: string = 'b';
1433 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1434 ID: Integer = NET_EVERYONE);
1435 begin
1436 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
1437 NetOut.Write(EvType);
1438 NetOut.Write(IntArg1);
1439 NetOut.Write(IntArg2);
1440 NetOut.Write(StrArg1);
1441 NetOut.Write(StrArg2);
1443 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1444 end;
1446 // CLIENT MESSAGES //
1448 // GAME
1450 procedure MC_RECV_Chat(var M: TMsg);
1451 var
1452 Txt: string;
1453 Mode: Byte;
1454 begin
1455 Txt := M.ReadString();
1456 Mode := M.ReadByte();
1458 if Mode <> NET_CHAT_SYSTEM then
1459 begin
1460 if Mode = NET_CHAT_PLAYER then
1461 begin
1462 g_Console_Add(Txt, True);
1463 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1464 g_Game_ChatSound(b_Text_Unformat(Txt));
1465 end else
1466 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1467 begin
1468 if gPlayer1.Team = TEAM_RED then
1469 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1470 if gPlayer1.Team = TEAM_BLUE then
1471 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1472 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1473 g_Game_ChatSound(b_Text_Unformat(Txt));
1474 end;
1475 end else
1476 g_Console_Add(Txt, True);
1477 end;
1479 procedure MC_RECV_Effect(var M: TMsg);
1480 var
1481 Kind: Byte;
1482 X, Y: Integer;
1483 Ang: SmallInt;
1484 Anim: TAnimation;
1485 ID: LongWord;
1486 begin
1487 if not gGameOn then Exit;
1488 Kind := M.ReadByte();
1489 X := M.ReadLongInt();
1490 Y := M.ReadLongInt();
1491 Ang := M.ReadSmallInt();
1493 case Kind of
1494 NET_GFX_SPARK:
1495 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1497 NET_GFX_TELE:
1498 begin
1499 if g_Frames_Get(ID, 'FRAMES_TELEPORT') then
1500 begin
1501 Anim := TAnimation.Create(ID, False, 3);
1502 g_GFX_OnceAnim(X, Y, Anim);
1503 Anim.Free();
1504 end;
1505 if Ang = 1 then
1506 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1507 end;
1509 NET_GFX_EXPLODE:
1510 begin
1511 if g_Frames_Get(ID, 'FRAMES_EXPLODE_ROCKET') then
1512 begin
1513 Anim := TAnimation.Create(ID, False, 6);
1514 Anim.Blending := False;
1515 g_GFX_OnceAnim(X-64, Y-64, Anim);
1516 Anim.Free();
1517 end;
1518 if Ang = 1 then
1519 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1520 end;
1522 NET_GFX_BFGEXPL:
1523 begin
1524 if g_Frames_Get(ID, 'FRAMES_EXPLODE_BFG') then
1525 begin
1526 Anim := TAnimation.Create(ID, False, 6);
1527 Anim.Blending := False;
1528 g_GFX_OnceAnim(X-64, Y-64, Anim);
1529 Anim.Free();
1530 end;
1531 if Ang = 1 then
1532 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1533 end;
1535 NET_GFX_BFGHIT:
1536 begin
1537 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1538 begin
1539 Anim := TAnimation.Create(ID, False, 4);
1540 g_GFX_OnceAnim(X-32, Y-32, Anim);
1541 Anim.Free();
1542 end;
1543 end;
1545 NET_GFX_FIRE:
1546 begin
1547 if g_Frames_Get(ID, 'FRAMES_FIRE') then
1548 begin
1549 Anim := TAnimation.Create(ID, False, 4);
1550 g_GFX_OnceAnim(X, Y, Anim);
1551 Anim.Free();
1552 end;
1553 if Ang = 1 then
1554 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1555 end;
1557 NET_GFX_RESPAWN:
1558 begin
1559 if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
1560 begin
1561 Anim := TAnimation.Create(ID, False, 4);
1562 g_GFX_OnceAnim(X, Y, Anim);
1563 Anim.Free();
1564 end;
1565 if Ang = 1 then
1566 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1567 end;
1569 NET_GFX_SHELL1:
1570 g_Player_CreateShell(X, Y, 0, -2, SHELL_BULLET);
1572 NET_GFX_SHELL2:
1573 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1575 NET_GFX_SHELL3:
1576 begin
1577 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1578 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1579 end;
1580 end;
1581 end;
1583 procedure MC_RECV_Sound(var M: TMsg);
1584 var
1585 Name: string;
1586 X, Y: Integer;
1587 Pos: Boolean;
1588 begin
1589 Name := M.ReadString();
1590 Pos := M.ReadByte() <> 0;
1591 if Pos then
1592 begin
1593 X := M.ReadLongInt();
1594 Y := M.ReadLongInt();
1595 g_Sound_PlayExAt(Name, X, Y);
1596 end
1597 else
1598 g_Sound_PlayEx(Name);
1599 end;
1601 procedure MC_RECV_CreateShot(var M: TMsg);
1602 var
1603 I, X, Y, XV, YV: Integer;
1604 Timeout: LongWord;
1605 Target, Spawner: Word;
1606 ShType: Byte;
1607 begin
1608 I := M.ReadLongInt();
1609 ShType := M.ReadByte();
1610 Target := M.ReadWord();
1611 Spawner := M.ReadWord();
1612 Timeout := M.ReadLongWord();
1613 X := M.ReadLongInt();
1614 Y := M.ReadLongInt();
1615 XV := M.ReadLongInt();
1616 YV := M.ReadLongInt();
1618 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1619 if (Shots <> nil) and (I <= High(Shots)) then
1620 begin
1621 Shots[I].Timeout := Timeout;
1622 //Shots[I].Target := Target; // TODO: find a use for Target later
1623 end;
1624 end;
1626 procedure MC_RECV_UpdateShot(var M: TMsg);
1627 var
1628 I, TX, TY, TXV, TYV: Integer;
1629 begin
1630 I := M.ReadLongInt();
1631 TX := M.ReadLongInt();
1632 TY := M.ReadLongInt();
1633 TXV := M.ReadLongInt();
1634 TYV := M.ReadLongInt();
1636 if (Shots <> nil) and (I <= High(Shots)) then
1637 with (Shots[i]) do
1638 begin
1639 Obj.X := TX;
1640 Obj.Y := TY;
1641 Obj.Vel.X := TXV;
1642 Obj.Vel.Y := TYV;
1643 end;
1644 end;
1646 procedure MC_RECV_DeleteShot(var M: TMsg);
1647 var
1648 I, X, Y: Integer;
1649 L: Boolean;
1650 begin
1651 if not gGameOn then Exit;
1652 I := M.ReadLongInt();
1653 L := (M.ReadByte() <> 0);
1654 X := M.ReadLongInt();
1655 Y := M.ReadLongInt();
1657 g_Weapon_DestroyShot(I, X, Y, L);
1658 end;
1660 procedure MC_RECV_GameStats(var M: TMsg);
1661 begin
1662 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1663 begin
1664 gTeamStat[TEAM_RED].Goals := M.ReadSmallInt();
1665 gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt();
1666 end
1667 else
1668 if gGameSettings.GameMode = GM_COOP then
1669 begin
1670 gCoopMonstersKilled := M.ReadWord();
1671 gCoopSecretsFound := M.ReadWord();
1672 end;
1673 end;
1675 procedure MC_RECV_CoopStats(var M: TMsg);
1676 begin
1677 gTotalMonsters := M.ReadLongInt();
1678 gSecretsCount := M.ReadLongInt();
1679 gCoopTotalMonstersKilled := M.ReadWord();
1680 gCoopTotalSecretsFound := M.ReadWord();
1681 gCoopTotalMonsters := M.ReadWord();
1682 gCoopTotalSecrets := M.ReadWord();
1683 end;
1685 procedure MC_RECV_GameEvent(var M: TMsg);
1686 var
1687 EvType: Byte;
1688 EvNum: Integer;
1689 EvStr: string;
1690 EvTime: LongWord;
1691 BHash: Boolean;
1692 EvHash: TMD5Digest;
1693 pl: TPlayer;
1694 i1, i2: TStrings_Locale;
1695 pln: String;
1696 cnt: Byte;
1697 goodCmd: Boolean = true;
1698 begin
1699 FillChar(EvHash, Sizeof(EvHash), 0);
1700 EvType := M.ReadByte();
1701 EvNum := M.ReadLongInt();
1702 EvStr := M.ReadString();
1703 gLastMap := M.ReadByte() <> 0;
1704 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1705 gStatsPressed := True;
1706 EvTime := M.ReadLongWord();
1707 BHash := M.ReadByte() <> 0;
1708 if BHash then
1709 EvHash := M.ReadMD5();
1711 gTime := EvTime;
1713 if (g_Res_received_map_start <> 0) then
1714 begin
1715 if (g_Res_received_map_start < 0) then exit;
1716 goodCmd := false;
1717 case EvType of
1718 NET_EV_MAPSTART: goodCmd := true;
1719 NET_EV_MAPEND: goodCmd := true;
1720 NET_EV_PLAYER_KICK: goodCmd := true;
1721 NET_EV_PLAYER_BAN: goodCmd := true;
1722 end;
1723 if not goodCmd then exit;
1724 end;
1726 case EvType of
1727 NET_EV_MAPSTART:
1728 begin
1729 if (g_Res_received_map_start <> 0) then
1730 begin
1731 g_Res_received_map_start := -1;
1732 end
1733 else
1734 begin
1735 gGameOn := False;
1736 g_Game_ClearLoading();
1737 g_Game_StopAllSounds(True);
1739 gSwitchGameMode := Byte(EvNum);
1740 gGameSettings.GameMode := gSwitchGameMode;
1742 gWADHash := EvHash;
1743 if not g_Game_StartMap(EvStr, True) then
1744 begin
1745 if not isWadPath(EvStr) then
1746 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1747 else
1748 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1749 Exit;
1750 end;
1752 MC_SEND_FullStateRequest;
1753 end;
1754 end;
1756 NET_EV_MAPEND:
1757 begin
1758 if (g_Res_received_map_start <> 0) then
1759 begin
1760 g_Res_received_map_start := -1;
1761 end
1762 else
1763 begin
1764 gMissionFailed := EvNum <> 0;
1765 gExit := EXIT_ENDLEVELCUSTOM;
1766 end;
1767 end;
1769 NET_EV_RCON:
1770 begin
1771 case EvNum of
1772 NET_RCON_NOAUTH:
1773 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
1774 NET_RCON_PWGOOD:
1775 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
1776 NET_RCON_PWBAD:
1777 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
1778 end;
1779 end;
1781 NET_EV_CHANGE_TEAM:
1782 begin
1783 if EvNum = TEAM_RED then
1784 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
1785 if EvNum = TEAM_BLUE then
1786 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
1787 end;
1789 NET_EV_PLAYER_KICK:
1790 begin
1791 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
1792 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1793 end;
1795 NET_EV_PLAYER_BAN:
1796 begin
1797 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
1798 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1799 end;
1801 NET_EV_LMS_WARMUP:
1802 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum]), True);
1804 NET_EV_LMS_SURVIVOR:
1805 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
1807 NET_EV_BIGTEXT:
1808 g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
1810 NET_EV_SCORE:
1811 begin
1812 pl := g_Player_Get(EvNum and $FFFF);
1813 if pl = nil then
1814 pln := '?'
1815 else
1816 pln := pl.Name;
1817 cnt := (EvNum shr 16) and $FF;
1818 if Pos('w', EvStr) = 0 then
1819 begin
1820 // Default score
1821 if Pos('t', EvStr) = 0 then
1822 begin
1823 // Player +/- score
1824 if Pos('-', EvStr) = 0 then
1825 begin
1826 if Pos('e', EvStr) = 0 then
1827 i1 := I_PLAYER_SCORE_ADD_OWN
1828 else
1829 i1 := I_PLAYER_SCORE_ADD_ENEMY;
1830 end else
1831 begin
1832 if Pos('e', EvStr) = 0 then
1833 i1 := I_PLAYER_SCORE_SUB_OWN
1834 else
1835 i1 := I_PLAYER_SCORE_SUB_ENEMY;
1836 end;
1837 // Which team
1838 if Pos('r', EvStr) > 0 then
1839 i2 := I_PLAYER_SCORE_TO_RED
1840 else
1841 i2 := I_PLAYER_SCORE_TO_BLUE;
1842 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
1843 end else
1844 begin
1845 // Team +/- score
1846 if Pos('-', EvStr) = 0 then
1847 i1 := I_PLAYER_SCORE_ADD_TEAM
1848 else
1849 i1 := I_PLAYER_SCORE_SUB_TEAM;
1850 // Which team
1851 if Pos('r', EvStr) > 0 then
1852 i2 := I_PLAYER_SCORE_RED
1853 else
1854 i2 := I_PLAYER_SCORE_BLUE;
1855 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
1856 end;
1857 end else
1858 begin
1859 // Game Win
1860 if Pos('e', EvStr) = 0 then
1861 i1 := I_PLAYER_SCORE_WIN_OWN
1862 else
1863 i1 := I_PLAYER_SCORE_WIN_ENEMY;
1864 // Which team
1865 if Pos('r', EvStr) > 0 then
1866 i2 := I_PLAYER_SCORE_TO_RED
1867 else
1868 i2 := I_PLAYER_SCORE_TO_BLUE;
1869 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
1870 end;
1871 end;
1873 NET_EV_SCORE_MSG:
1874 begin
1875 if EvNum = TEAM_RED then
1876 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1877 if EvNum = TEAM_BLUE then
1878 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1879 if EvNum = -TEAM_RED then
1880 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1881 if EvNum = -TEAM_BLUE then
1882 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1883 end;
1885 NET_EV_LMS_START:
1886 begin
1887 g_Player_RemoveAllCorpses;
1888 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
1889 end;
1891 NET_EV_LMS_WIN:
1892 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
1894 NET_EV_TLMS_WIN:
1895 begin
1896 if EvNum = TEAM_RED then
1897 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
1898 if EvNum = TEAM_BLUE then
1899 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
1900 end;
1902 NET_EV_LMS_LOSE:
1903 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
1905 NET_EV_LMS_DRAW:
1906 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
1908 NET_EV_KILLCOMBO:
1909 g_Game_Announce_KillCombo(EvNum);
1911 NET_EV_PLAYER_TOUCH:
1912 begin
1913 pl := g_Player_Get(EvNum);
1914 if pl <> nil then
1915 pl.Touch();
1916 end;
1918 NET_EV_SECRET:
1919 begin
1920 pl := g_Player_Get(EvNum);
1921 if pl <> nil then
1922 begin
1923 g_Console_Add(Format(_lc[I_PLAYER_SECRET], [pl.Name]), True);
1924 g_Sound_PlayEx('SOUND_GAME_SECRET');
1925 end;
1926 end;
1928 NET_EV_INTER_READY:
1929 begin
1930 pl := g_Player_Get(EvNum);
1931 if pl <> nil then pl.FReady := (EvStr = 'Y');
1932 end;
1933 end;
1934 end;
1936 procedure MC_RECV_FlagEvent(var M: TMsg);
1937 var
1938 PID: Word;
1939 Pl: TPlayer;
1940 EvType: Byte;
1941 Fl, a: Byte;
1942 Quiet: Boolean;
1943 s, ts: string;
1944 begin
1945 EvType := M.ReadByte();
1946 Fl := M.ReadByte();
1948 if Fl = FLAG_NONE then Exit;
1950 Quiet := (M.ReadByte() <> 0);
1951 PID := M.ReadWord();
1953 gFlags[Fl].State := M.ReadByte();
1954 gFlags[Fl].CaptureTime := M.ReadLongWord();
1955 gFlags[Fl].Obj.X := M.ReadLongInt();
1956 gFlags[Fl].Obj.Y := M.ReadLongInt();
1957 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
1958 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
1960 Pl := g_Player_Get(PID);
1961 if (Pl = nil) and
1962 (EvType <> FLAG_STATE_NORMAL) and
1963 (EvType <> FLAG_STATE_DROPPED) and
1964 (EvType <> FLAG_STATE_RETURNED) then
1965 Exit;
1967 case EvType of
1968 FLAG_STATE_NORMAL:
1969 begin
1970 if Quiet or (Pl = nil) then Exit;
1972 if Fl = FLAG_RED then
1973 s := _lc[I_PLAYER_FLAG_RED]
1974 else
1975 s := _lc[I_PLAYER_FLAG_BLUE];
1977 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1979 if ((Pl = gPlayer1) or (Pl = gPlayer2)
1980 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
1981 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
1982 a := 0
1983 else
1984 a := 1;
1986 if not sound_ret_flag[a].IsPlaying() then
1987 sound_ret_flag[a].Play();
1988 end;
1990 FLAG_STATE_CAPTURED:
1991 begin
1992 if (Pl <> nil) then Pl.SetFlag(Fl);
1994 if Quiet then Exit;
1996 if Fl = FLAG_RED then
1997 s := _lc[I_PLAYER_FLAG_RED]
1998 else
1999 s := _lc[I_PLAYER_FLAG_BLUE];
2001 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
2002 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
2004 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2005 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2006 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2007 a := 0
2008 else
2009 a := 1;
2011 if not sound_get_flag[a].IsPlaying() then
2012 sound_get_flag[a].Play();
2013 end;
2015 FLAG_STATE_DROPPED:
2016 begin
2017 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
2019 if Quiet or (Pl = nil) then Exit;
2021 if Fl = FLAG_RED then
2022 s := _lc[I_PLAYER_FLAG_RED]
2023 else
2024 s := _lc[I_PLAYER_FLAG_BLUE];
2026 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
2027 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
2029 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2030 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2031 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2032 a := 0
2033 else
2034 a := 1;
2036 if not sound_lost_flag[a].IsPlaying() then
2037 sound_lost_flag[a].Play();
2038 end;
2040 FLAG_STATE_SCORED:
2041 begin
2042 g_Map_ResetFlag(FLAG_RED);
2043 g_Map_ResetFlag(FLAG_BLUE);
2044 if Quiet or (Pl = nil) then Exit;
2045 Pl.SetFlag(FLAG_NONE);
2047 if Fl = FLAG_RED then
2048 s := _lc[I_PLAYER_FLAG_RED]
2049 else
2050 s := _lc[I_PLAYER_FLAG_BLUE];
2052 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
2053 Insert('.', ts, Length(ts) + 1 - 3);
2054 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
2055 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
2057 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2058 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2059 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2060 a := 0
2061 else
2062 a := 1;
2064 if not sound_cap_flag[a].IsPlaying() then
2065 sound_cap_flag[a].Play();
2066 end;
2068 FLAG_STATE_RETURNED:
2069 begin
2070 g_Map_ResetFlag(Fl);
2071 if Quiet then Exit;
2073 if Fl = FLAG_RED then
2074 s := _lc[I_PLAYER_FLAG_RED]
2075 else
2076 s := _lc[I_PLAYER_FLAG_BLUE];
2078 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2080 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2081 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2082 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2083 a := 0
2084 else
2085 a := 1;
2087 if not sound_ret_flag[a].IsPlaying() then
2088 sound_ret_flag[a].Play();
2089 end;
2090 end;
2091 end;
2093 procedure MC_RECV_GameSettings(var M: TMsg);
2094 begin
2095 gGameSettings.GameMode := M.ReadByte();
2096 gGameSettings.GoalLimit := M.ReadWord();
2097 gGameSettings.TimeLimit := M.ReadWord();
2098 gGameSettings.MaxLives := M.ReadByte();
2099 gGameSettings.Options := M.ReadLongWord();
2100 end;
2102 // PLAYER
2104 function MC_RECV_PlayerCreate(var M: TMsg): Word;
2105 var
2106 PID, DID: Word;
2107 PName, Model: string;
2108 Color: TRGB;
2109 T: Byte;
2110 Pl: TPlayer;
2111 begin
2112 PID := M.ReadWord();
2113 Pl := g_Player_Get(PID);
2115 PName := M.ReadString();
2116 Model := M.ReadString();
2117 Color.R := M.ReadByte();
2118 Color.G := M.ReadByte();
2119 Color.B := M.ReadByte();
2120 T := M.ReadByte();
2122 Result := 0;
2123 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
2124 begin
2125 if (Pl <> nil) then Exit;
2126 DID := g_Player_Create(Model, Color, T, False);
2127 with g_Player_Get(DID) do
2128 begin
2129 UID := PID;
2130 Name := PName;
2131 Reset(True);
2132 end;
2133 end
2134 else
2135 begin
2136 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
2137 gPlayer1.UID := PID;
2138 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
2139 gPlayer1.ChangeTeam(T);
2140 end;
2141 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
2142 gPlayer2.UID := PID;
2143 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
2144 gPlayer2.ChangeTeam(T);
2145 end;
2146 end;
2148 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
2149 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify);
2150 Result := PID;
2151 end;
2153 function MC_RECV_PlayerPos(var M: TMsg): Word;
2154 var
2155 GT: LongWord;
2156 PID: Word;
2157 kByte: Word;
2158 Pl: TPlayer;
2159 Dir: Byte;
2160 TmpX, TmpY: Integer;
2161 begin
2162 Result := 0;
2164 GT := M.ReadLongWord();
2165 if GT < gTime - NET_MAX_DIFFTIME then
2166 begin
2167 gTime := GT;
2168 Exit;
2169 end;
2170 gTime := GT;
2172 PID := M.ReadWord();
2173 Pl := g_Player_Get(PID);
2175 if Pl = nil then Exit;
2177 Result := PID;
2179 with Pl do
2180 begin
2181 FPing := M.ReadWord();
2182 FLoss := M.ReadByte();
2183 kByte := M.ReadWord();
2184 Dir := M.ReadByte();
2186 TmpX := M.ReadLongInt();
2187 TmpY := M.ReadLongInt();
2189 ReleaseKeys;
2191 if (kByte = NET_KEY_CHAT) then
2192 PressKey(KEY_CHAT, 10000)
2193 else
2194 begin
2195 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2196 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2197 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2198 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2199 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2200 end;
2202 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or LongBool(kByte and NET_KEY_FORCEDIR) then
2203 SetDirection(TDirection(Dir));
2205 GameVelX := M.ReadLongInt();
2206 GameVelY := M.ReadLongInt();
2207 GameAccelX := M.ReadLongInt();
2208 GameAccelY := M.ReadLongInt();
2209 SetLerp(TmpX, TmpY);
2210 if NetForcePlayerUpdate then Update();
2211 end;
2212 end;
2214 function MC_RECV_PlayerStats(var M: TMsg): Word;
2215 var
2216 PID: Word;
2217 Pl: TPlayer;
2218 I, OldFire: Integer;
2219 OldJet, Flam: Boolean;
2220 NewTeam: Byte;
2221 begin
2222 PID := M.ReadWord();
2223 Pl := g_Player_Get(PID);
2224 Result := 0;
2225 if Pl = nil then
2226 Exit;
2228 with Pl do
2229 begin
2230 alive := (M.ReadByte() <> 0);
2231 GodMode := (M.ReadByte() <> 0);
2232 Health := M.ReadLongInt();
2233 Armor := M.ReadLongInt();
2234 Air := M.ReadLongInt();
2235 JetFuel := M.ReadLongInt();
2236 Lives := M.ReadByte();
2237 NewTeam := M.ReadByte();
2239 for I := WP_FIRST to WP_LAST do
2240 FWeapon[I] := (M.ReadByte() <> 0);
2242 for I := A_BULLETS to A_HIGH do
2243 FAmmo[I] := M.ReadWord();
2245 for I := A_BULLETS to A_HIGH do
2246 FMaxAmmo[I] := M.ReadWord();
2248 for I := MR_SUIT to MR_MAX do
2249 FMegaRulez[I] := M.ReadLongWord();
2251 FRulez := [];
2252 if (M.ReadByte() <> 0) then
2253 FRulez := FRulez + [R_ITEM_BACKPACK];
2254 if (M.ReadByte() <> 0) then
2255 FRulez := FRulez + [R_KEY_RED];
2256 if (M.ReadByte() <> 0) then
2257 FRulez := FRulez + [R_KEY_GREEN];
2258 if (M.ReadByte() <> 0) then
2259 FRulez := FRulez + [R_KEY_BLUE];
2260 if (M.ReadByte() <> 0) then
2261 FRulez := FRulez + [R_BERSERK];
2263 Frags := M.ReadLongInt();
2264 Death := M.ReadLongInt();
2266 SetWeapon(M.ReadByte());
2268 FSpectator := M.ReadByte() <> 0;
2269 if FSpectator then
2270 begin
2271 if Pl = gPlayer1 then
2272 begin
2273 gLMSPID1 := UID;
2274 gPlayer1 := nil;
2275 end;
2276 if Pl = gPlayer2 then
2277 begin
2278 gLMSPID2 := UID;
2279 gPlayer2 := nil;
2280 end;
2281 end
2282 else
2283 begin
2284 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
2285 gPlayer1 := g_Player_Get(gLMSPID1);
2286 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
2287 gPlayer2 := g_Player_Get(gLMSPID2);
2288 end;
2289 FGhost := M.ReadByte() <> 0;
2290 FPhysics := M.ReadByte() <> 0;
2291 FNoRespawn := M.ReadByte() <> 0;
2292 OldJet := FJetpack;
2293 FJetpack := M.ReadByte() <> 0;
2294 OldFire := FFireTime;
2295 FFireTime := M.ReadLongInt();
2296 if (OldFire <= 0) and (FFireTime > 0) then
2297 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2298 Flam := M.ReadByte() <> 0;
2299 if OldJet and not FJetpack then
2300 JetpackOff
2301 else if not OldJet and FJetpack then
2302 JetpackOn;
2303 if FFlaming and not Flam then
2304 FlamerOff;
2305 if Team <> NewTeam then
2306 Pl.ChangeTeam(NewTeam);
2307 end;
2309 Result := PID;
2310 end;
2312 function MC_RECV_PlayerDamage(var M: TMsg): Word;
2313 var
2314 PID: Word;
2315 Pl: TPlayer;
2316 Kind: Byte;
2317 Attacker, Value: Word;
2318 VX, VY: Integer;
2319 begin
2320 Result := 0;
2321 if not gGameOn then Exit;
2322 PID := M.ReadWord();
2323 Pl := g_Player_Get(PID);
2324 if Pl = nil then Exit;
2326 Kind := M.ReadByte();
2327 Attacker := M.ReadWord();
2328 Value := M.ReadWord();
2329 VX := M.ReadWord();
2330 VY := M.ReadWord();
2332 with Pl do
2333 Damage(Value, Attacker, VX, VY, Kind);
2335 Result := PID;
2336 end;
2338 function MC_RECV_PlayerDeath(var M: TMsg): Word;
2339 var
2340 PID: Word;
2341 Pl: TPlayer;
2342 KillType, DeathType: Byte;
2343 Attacker: Word;
2344 begin
2345 Result := 0;
2346 if not gGameOn then Exit;
2347 PID := M.ReadWord();
2348 Pl := g_Player_Get(PID);
2349 if Pl = nil then Exit;
2351 KillType := M.ReadByte();
2352 DeathType := M.ReadByte();
2353 Attacker := M.ReadWord();
2355 with Pl do
2356 begin
2357 Kill(KillType, Attacker, DeathType);
2358 SoftReset;
2359 end;
2360 end;
2362 function MC_RECV_PlayerDelete(var M: TMsg): Word;
2363 var
2364 PID: Word;
2365 Pl: TPlayer;
2366 begin
2367 PID := M.ReadWord();
2368 Pl := g_Player_Get(PID);
2369 Result := 0;
2370 if Pl = nil then Exit;
2372 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2373 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify);
2375 g_Player_Remove(PID);
2377 Result := PID;
2378 end;
2380 function MC_RECV_PlayerFire(var M: TMsg): Word;
2381 var
2382 PID: Word;
2383 Weap: Byte;
2384 Pl: TPlayer;
2385 X, Y, AX, AY: Integer;
2386 SHID: Integer;
2387 begin
2388 Result := 0;
2389 if not gGameOn then Exit;
2390 PID := M.ReadWord();
2391 Pl := g_Player_Get(PID);
2392 if Pl = nil then Exit;
2394 Weap := M.ReadByte();
2395 X := M.ReadLongInt();
2396 Y := M.ReadLongInt();
2397 AX := M.ReadLongInt();
2398 AY := M.ReadLongInt();
2399 SHID := M.ReadLongInt();
2401 with Pl do
2402 if alive then NetFire(Weap, X, Y, AX, AY, SHID);
2403 end;
2405 procedure MC_RECV_PlayerSettings(var M: TMsg);
2406 var
2407 TmpName: string;
2408 TmpModel: string;
2409 TmpColor: TRGB;
2410 TmpTeam: Byte;
2411 Pl: TPlayer;
2412 PID: Word;
2413 begin
2414 PID := M.ReadWord();
2415 Pl := g_Player_Get(PID);
2416 if Pl = nil then Exit;
2418 TmpName := M.ReadString();
2419 TmpModel := M.ReadString();
2420 TmpColor.R := M.ReadByte();
2421 TmpColor.G := M.ReadByte();
2422 TmpColor.B := M.ReadByte();
2423 TmpTeam := M.ReadByte();
2425 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2426 begin
2427 Pl.ChangeTeam(TmpTeam);
2428 if gPlayer1 = Pl then
2429 gPlayer1Settings.Team := TmpTeam;
2430 if gPlayer2 = Pl then
2431 gPlayer2Settings.Team := TmpTeam;
2432 end else
2433 Pl.SetColor(TmpColor);
2435 if Pl.Name <> TmpName then
2436 begin
2437 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2438 Pl.Name := TmpName;
2439 end;
2441 if TmpModel <> Pl.Model.Name then
2442 Pl.SetModel(TmpModel);
2443 end;
2445 // ITEM
2447 procedure MC_RECV_ItemSpawn(var M: TMsg);
2448 var
2449 ID: Word;
2450 AID: DWord;
2451 X, Y, VX, VY: Integer;
2452 T: Byte;
2453 Quiet, Fall{, Resp}: Boolean;
2454 Anim: TAnimation;
2455 it: PItem;
2456 begin
2457 if not gGameOn then Exit;
2458 ID := M.ReadWord();
2459 Quiet := M.ReadByte() <> 0;
2460 T := M.ReadByte();
2461 Fall := M.ReadByte() <> 0;
2462 {Resp :=} M.ReadByte();
2463 X := M.ReadLongInt();
2464 Y := M.ReadLongInt();
2465 VX := M.ReadLongInt();
2466 VY := M.ReadLongInt();
2468 g_Items_Create(X, Y, T and $7F, Fall, False, False, ID);
2469 if ((T and $80) <> 0) then g_Items_SetDrop(ID);
2471 it := g_Items_ByIdx(ID);
2472 it.Obj.Vel.X := VX;
2473 it.Obj.Vel.Y := VY;
2475 if not Quiet then
2476 begin
2477 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2478 if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
2479 begin
2480 Anim := TAnimation.Create(AID, False, 4);
2481 g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim);
2482 Anim.Free();
2483 end;
2484 end;
2485 end;
2487 procedure MC_RECV_ItemDestroy(var M: TMsg);
2488 var
2489 ID: Word;
2490 Quiet: Boolean;
2491 begin
2492 if not gGameOn then Exit;
2493 ID := M.ReadWord();
2494 Quiet := M.ReadByte() <> 0;
2496 if not g_Items_ValidId(ID) then exit;
2498 if not Quiet then g_Items_EmitPickupSound(ID);
2500 g_Items_Remove(ID);
2501 end;
2503 // PANEL
2505 procedure MC_RECV_PanelTexture(var M: TMsg);
2506 var
2507 TP: TPanel;
2508 PGUID: Integer;
2509 Tex, Fr: Integer;
2510 Loop, Cnt: Byte;
2511 begin
2512 if not gGameOn then Exit;
2514 PGUID := Integer(M.ReadLongWord());
2515 Tex := M.ReadLongInt();
2516 Fr := M.ReadLongInt();
2517 Cnt := M.ReadByte();
2518 Loop := M.ReadByte();
2520 TP := g_Map_PanelByGUID(PGUID);
2521 if (TP <> nil) then
2522 begin
2523 // switch texture
2524 TP.SetTexture(Tex, Loop);
2525 TP.SetFrame(Fr, Cnt);
2526 end;
2527 end;
2529 procedure MC_RECV_PanelState(var M: TMsg);
2530 var
2531 PGUID: Integer;
2532 E: Boolean;
2533 Lift: Byte;
2534 X, Y, W, H: Integer;
2535 TP: TPanel;
2536 speedX, speedY, startX, startY, endX, endY: Integer;
2537 sizeSpX, sizeSpY, sizeEX, sizeEY: Integer;
2538 mpflags: Byte;
2539 begin
2540 if not gGameOn then Exit;
2542 PGUID := Integer(M.ReadLongWord());
2543 E := (M.ReadByte() <> 0);
2544 Lift := M.ReadByte();
2545 X := M.ReadLongInt();
2546 Y := M.ReadLongInt();
2547 W := M.ReadWord();
2548 H := M.ReadWord();
2549 // mplats
2550 speedX := M.ReadLongInt();
2551 speedY := M.ReadLongInt();
2552 startX := M.ReadLongInt();
2553 startY := M.ReadLongInt();
2554 endX := M.ReadLongInt();
2555 endY := M.ReadLongInt();
2556 sizeSpX := M.ReadLongInt();
2557 sizeSpY := M.ReadLongInt();
2558 sizeEX := M.ReadLongInt();
2559 sizeEY := M.ReadLongInt();
2560 mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
2562 TP := g_Map_PanelByGUID(PGUID);
2563 if (TP = nil) then exit;
2565 // update lifts state
2566 if TP.isGLift then g_Map_SetLiftGUID(PGUID, Lift);
2568 // update enabled/disabled state for all panels
2569 if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
2571 // update panel position, as it can be moved (mplat)
2572 TP.X := X;
2573 TP.Y := Y;
2574 TP.Width := W;
2575 TP.Height := H;
2576 // update mplat state
2577 TP.movingSpeedX := speedX;
2578 TP.movingSpeedY := speedY;
2579 TP.movingStartX := startX;
2580 TP.movingStartY := startY;
2581 TP.movingEndX := endX;
2582 TP.movingEndY := endY;
2583 TP.sizeSpeedX := sizeSpX;
2584 TP.sizeSpeedY := sizeSpY;
2585 TP.sizeEndX := sizeEX;
2586 TP.sizeEndY := sizeEY;
2587 TP.movingActive := ((mpflags and 1) <> 0);
2588 TP.moveOnce := ((mpflags and 2) <> 0);
2589 // notify panel of it's position/size change, so it can fix other internal structures
2590 TP.positionChanged();
2591 end;
2593 // TRIGGERS
2595 procedure MC_RECV_TriggerSound(var M: TMsg);
2596 var
2597 SPlaying: Boolean;
2598 SPos, SID: LongWord;
2599 SCount: LongInt;
2600 I: Integer;
2601 begin
2602 if not gGameOn then Exit;
2603 if gTriggers = nil then Exit;
2605 SID := M.ReadLongWord();
2606 SPlaying := M.ReadByte() <> 0;
2607 SPos := M.ReadLongWord();
2608 SCount := M.ReadLongInt();
2610 for I := Low(gTriggers) to High(gTriggers) do
2611 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2612 if gTriggers[I].ClientID = SID then
2613 with gTriggers[I] do
2614 begin
2615 if Sound <> nil then
2616 begin
2617 if SPlaying then
2618 begin
2619 if tgcLocal then
2620 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0)
2621 else
2622 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2623 Sound.SetPosition(SPos);
2624 end
2625 else
2626 if Sound.IsPlaying then Sound.Stop;
2627 end;
2629 SoundPlayCount := SCount;
2630 end;
2631 end;
2633 procedure MC_RECV_TriggerMusic(var M: TMsg);
2634 var
2635 MName: string;
2636 MPlaying: Boolean;
2637 MPos: LongWord;
2638 MPaused: Boolean;
2639 begin
2640 if not gGameOn then Exit;
2642 MName := M.ReadString();
2643 MPlaying := M.ReadByte() <> 0;
2644 MPos := M.ReadLongWord();
2645 MPaused := M.ReadByte() <> 0;
2646 MPos := MPos+1; //k8: stfu, fpc!
2648 if MPlaying then
2649 begin
2650 gMusic.SetByName(MName);
2651 gMusic.Play(True);
2652 // gMusic.SetPosition(MPos);
2653 gMusic.SpecPause := MPaused;
2654 end
2655 else
2656 if gMusic.IsPlaying then gMusic.Stop;
2657 end;
2659 // MONSTERS
2661 procedure MC_RECV_MonsterSpawn(var M: TMsg);
2662 var
2663 ID: Word;
2664 MType, MState, MDir, MAnim, MBehav: Byte;
2665 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2666 MTarg: Word;
2667 Mon: TMonster;
2668 begin
2669 ID := M.ReadWord();
2670 Mon := g_Monsters_ByUID(ID);
2671 if Mon <> nil then
2672 Exit;
2674 MType := M.ReadByte();
2675 MState := M.ReadByte();
2676 MAnim := M.ReadByte();
2677 MTarg := M.ReadWord();
2678 MTargTime := M.ReadLongInt();
2679 MBehav := M.ReadByte();
2680 MSleep := M.ReadLongInt();
2681 MHealth := M.ReadLongInt();
2682 MAmmo := M.ReadLongInt();
2684 X := M.ReadLongInt();
2685 Y := M.ReadLongInt();
2686 VX := M.ReadLongInt();
2687 VY := M.ReadLongInt();
2688 MDir := M.ReadByte();
2690 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
2691 Mon := g_Monsters_ByUID(ID);
2692 if Mon = nil then
2693 Exit;
2695 with Mon do
2696 begin
2698 MonsterAnim := MAnim;
2699 MonsterTargetUID := MTarg;
2700 MonsterTargetTime := MTargTime;
2701 MonsterBehaviour := MBehav;
2702 MonsterSleep := MSleep;
2703 MonsterAmmo := MAmmo;
2704 SetHealth(MHealth);
2706 SetState(MState);
2708 setPosition(X, Y); // this will call positionChanged();
2709 GameVelX := VX;
2710 GameVelY := VY;
2711 end;
2712 end;
2714 procedure MC_RECV_MonsterPos(var M: TMsg);
2715 var
2716 Mon: TMonster;
2717 ID: Word;
2718 X, Y: Integer;
2719 begin
2720 ID := M.ReadWord();
2721 Mon := g_Monsters_ByUID(ID);
2722 if Mon = nil then
2723 Exit;
2725 with Mon do
2726 begin
2727 X := M.ReadLongInt();
2728 Y := M.ReadLongInt();
2729 Mon.setPosition(X, Y); // this will call `positionChanged()`
2730 GameVelX := M.ReadLongInt();
2731 GameVelY := M.ReadLongInt();
2732 GameDirection := TDirection(M.ReadByte());
2733 end;
2734 end;
2736 procedure MC_RECV_MonsterState(var M: TMsg);
2737 var
2738 ID, OldFire: Integer;
2739 MState, MFAnm: Byte;
2740 Mon: TMonster;
2741 AnimRevert: Boolean;
2742 begin
2743 ID := M.ReadWord();
2744 Mon := g_Monsters_ByUID(ID);
2745 if Mon = nil then Exit;
2747 MState := M.ReadByte();
2748 MFAnm := M.ReadByte();
2750 with Mon do
2751 begin
2752 MonsterTargetUID := M.ReadWord();
2753 MonsterTargetTime := M.ReadLongInt();
2754 MonsterSleep := M.ReadLongInt();
2755 MonsterHealth := M.ReadLongInt();
2756 MonsterAmmo := M.ReadLongInt();
2757 MonsterPain := M.ReadLongInt();
2758 AnimRevert := M.ReadByte() <> 0;
2759 OldFire := FFireTime;
2760 FFireTime := M.ReadLongInt();
2761 if (OldFire <= 0) and (FFireTime > 0) then
2762 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2763 RevertAnim(AnimRevert);
2765 if MonsterState <> MState then
2766 begin
2767 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then WakeUpSound();
2768 if (MState = MONSTATE_DIE) then DieSound();
2769 if (MState = MONSTATE_PAIN) then MakeBloodSimple(Min(200, MonsterPain));
2770 if (MState = MONSTATE_ATTACK) then kick(nil);
2771 if (MState = MONSTATE_DEAD) then SetDeadAnim();
2773 SetState(MState, MFAnm);
2774 end;
2775 end;
2776 end;
2778 procedure MC_RECV_MonsterShot(var M: TMsg);
2779 var
2780 ID: Integer;
2781 Mon: TMonster;
2782 X, Y, VX, VY: Integer;
2783 begin
2784 ID := M.ReadWord();
2786 Mon := g_Monsters_ByUID(ID);
2787 if Mon = nil then Exit;
2789 X := M.ReadLongInt();
2790 Y := M.ReadLongInt();
2791 VX := M.ReadLongInt();
2792 VY := M.ReadLongInt();
2794 Mon.ClientAttack(X, Y, VX, VY);
2795 end;
2797 procedure MC_RECV_MonsterDelete(var M: TMsg);
2798 var
2799 ID: Integer;
2800 Mon: TMonster;
2801 begin
2802 ID := M.ReadWord();
2803 Mon := g_Monsters_ByUID(ID);
2804 if Mon = nil then Exit;
2805 Mon.SetState(5);
2806 Mon.MonsterRemoved := True;
2807 end;
2809 procedure MC_RECV_TimeSync(var M: TMsg);
2810 var
2811 Time: LongWord;
2812 begin
2813 Time := M.ReadLongWord();
2815 if gState = STATE_INTERCUSTOM then
2816 gServInterTime := Min(Time, 255);
2817 end;
2819 procedure MC_RECV_VoteEvent(var M: TMsg);
2820 var
2821 EvID: Byte;
2822 Str1, Str2: string;
2823 Int1, Int2: SmallInt;
2824 begin
2825 EvID := M.ReadByte();
2826 Int1 := M.ReadSmallInt();
2827 Int2 := M.ReadSmallInt();
2828 Str1 := M.ReadString();
2829 Str2 := M.ReadString();
2831 case EvID of
2832 NET_VE_STARTED:
2833 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
2834 NET_VE_PASSED:
2835 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
2836 NET_VE_FAILED:
2837 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
2838 NET_VE_VOTE:
2839 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
2840 NET_VE_INPROGRESS:
2841 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
2842 end;
2843 end;
2845 // CLIENT SEND
2847 procedure MC_SEND_Info(Password: string);
2848 begin
2849 NetOut.Clear();
2851 NetOut.Write(Byte(NET_MSG_INFO));
2852 NetOut.Write(GAME_VERSION);
2853 NetOut.Write(Password);
2854 NetOut.Write(gPlayer1Settings.Name);
2855 NetOut.Write(gPlayer1Settings.Model);
2856 NetOut.Write(gPlayer1Settings.Color.R);
2857 NetOut.Write(gPlayer1Settings.Color.G);
2858 NetOut.Write(gPlayer1Settings.Color.B);
2859 NetOut.Write(gPlayer1Settings.Team);
2861 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2862 end;
2864 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
2865 begin
2866 NetOut.Write(Byte(NET_MSG_CHAT));
2867 NetOut.Write(Txt);
2868 NetOut.Write(Mode);
2870 g_Net_Client_Send(True, NET_CHAN_CHAT);
2871 end;
2873 procedure MC_SEND_PlayerPos();
2874 var
2875 kByte: Word;
2876 Predict: Boolean;
2877 strafeDir: Byte;
2878 WeaponSelect: Word = 0;
2879 i: Integer;
2880 begin
2881 if not gGameOn then Exit;
2882 if gPlayers = nil then Exit;
2883 if gPlayer1 = nil then Exit;
2885 kByte := 0;
2886 Predict := NetPredictSelf; // and (not NetGotKeys);
2888 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2889 begin
2890 strafeDir := P1MoveButton shr 4;
2891 P1MoveButton := P1MoveButton and $0F;
2893 if gPlayerAction[0, ACTION_MOVELEFT] and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
2894 P1MoveButton := 1
2895 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and gPlayerAction[0, ACTION_MOVERIGHT] then
2896 P1MoveButton := 2
2897 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
2898 P1MoveButton := 0;
2900 // strafing
2901 if gPlayerAction[0, ACTION_STRAFE] then
2902 begin
2903 // new strafe mechanics
2904 if (strafeDir = 0) then
2905 strafeDir := P1MoveButton; // start strafing
2906 // now set direction according to strafe (reversed)
2907 if (strafeDir = 2) then
2908 gPlayer1.SetDirection(TDirection.D_LEFT)
2909 else if (strafeDir = 1) then
2910 gPlayer1.SetDirection(TDirection.D_RIGHT)
2911 end
2912 else
2913 begin
2914 strafeDir := 0; // not strafing anymore
2915 if (P1MoveButton = 2) and gPlayerAction[0, ACTION_MOVELEFT] then
2916 gPlayer1.SetDirection(TDirection.D_LEFT)
2917 else if (P1MoveButton = 1) and gPlayerAction[0, ACTION_MOVERIGHT] then
2918 gPlayer1.SetDirection(TDirection.D_RIGHT)
2919 else if P1MoveButton <> 0 then
2920 gPlayer1.SetDirection(TDirection(P1MoveButton-1));
2921 end;
2923 gPlayer1.ReleaseKeys;
2924 if P1MoveButton = 1 then
2925 begin
2926 kByte := kByte or NET_KEY_LEFT;
2927 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
2928 end;
2929 if P1MoveButton = 2 then
2930 begin
2931 kByte := kByte or NET_KEY_RIGHT;
2932 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
2933 end;
2934 if gPlayerAction[0, ACTION_LOOKUP] then
2935 begin
2936 kByte := kByte or NET_KEY_UP;
2937 gPlayer1.PressKey(KEY_UP, 10000);
2938 end;
2939 if gPlayerAction[0, ACTION_LOOKDOWN] then
2940 begin
2941 kByte := kByte or NET_KEY_DOWN;
2942 gPlayer1.PressKey(KEY_DOWN, 10000);
2943 end;
2944 if gPlayerAction[0, ACTION_JUMP] then
2945 begin
2946 kByte := kByte or NET_KEY_JUMP;
2947 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
2948 end;
2949 if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE;
2950 if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN;
2951 if gPlayerAction[0, ACTION_WEAPNEXT] then kByte := kByte or NET_KEY_NW;
2952 if gPlayerAction[0, ACTION_WEAPPREV] then kByte := kByte or NET_KEY_PW;
2954 gPlayerAction[0, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation
2955 gPlayerAction[0, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation
2957 for i := WP_FIRST to WP_LAST do
2958 begin
2959 if gSelectWeapon[0, i] then
2960 begin
2961 WeaponSelect := WeaponSelect or Word(1 shl i);
2962 gSelectWeapon[0, i] := False
2963 end
2964 end;
2966 // fix movebutton state
2967 P1MoveButton := P1MoveButton or (strafeDir shl 4);
2968 end
2969 else
2970 kByte := NET_KEY_CHAT;
2972 NetOut.Write(Byte(NET_MSG_PLRPOS));
2973 NetOut.Write(gTime);
2974 NetOut.Write(kByte);
2975 NetOut.Write(Byte(gPlayer1.Direction));
2976 NetOut.Write(WeaponSelect);
2977 //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
2978 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
2980 //kBytePrev := kByte;
2981 //kDirPrev := gPlayer1.Direction;
2982 end;
2984 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
2985 begin
2986 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
2987 NetOut.Write(Byte(Start));
2988 NetOut.Write(Command);
2989 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2990 end;
2992 procedure MC_SEND_PlayerSettings();
2993 begin
2994 NetOut.Write(Byte(NET_MSG_PLRSET));
2995 NetOut.Write(gPlayer1Settings.Name);
2996 NetOut.Write(gPlayer1Settings.Model);
2997 NetOut.Write(gPlayer1Settings.Color.R);
2998 NetOut.Write(gPlayer1Settings.Color.G);
2999 NetOut.Write(gPlayer1Settings.Color.B);
3000 NetOut.Write(gPlayer1Settings.Team);
3002 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3003 end;
3005 procedure MC_SEND_FullStateRequest();
3006 begin
3007 NetOut.Write(Byte(NET_MSG_REQFST));
3009 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3010 end;
3012 procedure MC_SEND_CheatRequest(Kind: Byte);
3013 begin
3014 NetOut.Write(Byte(NET_MSG_CHEAT));
3015 NetOut.Write(Kind);
3017 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3018 end;
3019 procedure MC_SEND_RCONPassword(Password: string);
3020 begin
3021 NetOut.Write(Byte(NET_MSG_RCON_AUTH));
3022 NetOut.Write(Password);
3024 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3025 end;
3026 procedure MC_SEND_RCONCommand(Cmd: string);
3027 begin
3028 NetOut.Write(Byte(NET_MSG_RCON_CMD));
3029 NetOut.Write(Cmd);
3031 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3032 end;
3035 end.