DEADSOFTWARE

game: disable gfx for server
[d2df-sdl.git] / src / game / g_triggers.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_triggers;
18 interface
20 uses
21 SysUtils, Variants, Classes,
22 MAPDEF, g_basic, g_sound,
23 xdynrec, hashtable, exoma;
25 type
26 TActivator = record
27 UID: Word;
28 TimeOut: Word;
29 end;
31 PTrigger = ^TTrigger;
32 TTrigger = record
33 public
34 ID: DWORD;
35 ClientID: DWORD;
36 TriggerType: Byte;
37 X, Y: Integer;
38 Width, Height: Word;
39 Enabled: Boolean;
40 ActivateType: Byte;
41 Keys: Byte;
42 TexturePanelGUID: Integer;
43 //TexturePanelType: Word;
45 TimeOut: Word;
46 ActivateUID: Word;
47 Activators: array of TActivator;
48 PlayerCollide: Boolean;
49 DoorTime: Integer;
50 PressTime: Integer;
51 PressCount: Integer;
52 SoundPlayCount: Integer;
53 Sound: TPlayableSound;
54 AutoSpawn: Boolean;
55 SpawnCooldown: Integer;
56 SpawnedCount: Integer;
57 //ShotPanelType: Word;
58 ShotPanelTime: Integer;
59 ShotSightTime: Integer;
60 ShotSightTimeout: Integer;
61 ShotSightTarget: Word;
62 ShotSightTargetN: Word;
63 ShotAmmoCount: Word;
64 ShotReloadTime: Integer;
66 mapId: AnsiString; // trigger id, from map
67 mapIndex: Integer; // index in fields['trigger'], used in save/load
68 trigPanelGUID: Integer;
70 trigDataRec: TDynRecord; // triggerdata; owned by trigger (cloned)
71 exoInit, exoThink, exoCheck, exoAction: TExprBase;
73 userVars: THashStrVariant;
75 {$INCLUDE ../shared/mapdef_tgc_def.inc}
77 public
78 function trigCenter (): TDFPoint; inline;
79 end;
81 function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
82 procedure g_Triggers_Update();
83 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
84 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
85 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
86 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
87 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
88 procedure g_Triggers_OpenAll();
89 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
90 procedure g_Triggers_Free();
91 procedure g_Triggers_SaveState (st: TStream);
92 procedure g_Triggers_LoadState (st: TStream);
95 var
96 gTriggerClientID: Integer = 0;
97 gTriggers: array of TTrigger;
98 gSecretsCount: Integer = 0;
99 gMonstersSpawned: array of LongInt = nil;
102 implementation
104 uses
105 {$IFDEF ENABLE_GFX}
106 g_gfx,
107 {$ENDIF}
108 Math,
109 g_player, g_map, g_panel, g_game,
110 g_console, g_monsters, g_items, g_phys, g_weapons,
111 wadreader, e_log, g_language, e_res,
112 g_options, g_net, g_netmsg, utils, xparser, xstreams
115 const
116 TRIGGER_SIGNATURE = $58475254; // 'TRGX'
117 TRAP_DAMAGE = 1000;
119 {$INCLUDE ../shared/mapdef_tgc_impl.inc}
122 // ////////////////////////////////////////////////////////////////////////// //
123 type
124 TTrigScope = class(TExprScope)
125 private
126 plrprops: TPropHash;
127 monsprops: TPropHash;
128 platprops: TPropHash;
130 public
131 me: PTrigger;
133 public
134 constructor Create ();
135 destructor Destroy (); override;
137 function getObj (const aname: AnsiString): TObject; override;
138 function getField (obj: TObject; const afldname: AnsiString): Variant; override;
139 procedure setField (obj: TObject; const afldname: AnsiString; var aval: Variant); override;
140 end;
143 // ////////////////////////////////////////////////////////////////////////// //
144 type
145 TMyConstList = class(TExprConstList)
146 public
147 function valid (const cname: AnsiString): Boolean; override;
148 function get (const cname: AnsiString; out v: Variant): Boolean; override;
149 end;
152 // ////////////////////////////////////////////////////////////////////////// //
153 function TMyConstList.valid (const cname: AnsiString): Boolean;
154 begin
155 //writeln('CHECK: ''', cname, '''');
156 result :=
157 (cname = 'player') or
158 (cname = 'self') or
159 false;
160 end;
162 function TMyConstList.get (const cname: AnsiString; out v: Variant): Boolean;
163 var
164 eidx: Integer;
165 ebs: TDynEBS;
166 begin
167 //if (cname = 'answer') then begin v := LongInt(42); result := true; exit; end;
168 result := false;
169 if (gCurrentMap = nil) then exit;
170 for eidx := 0 to gCurrentMap.mapdef.ebsTypeCount-1 do
171 begin
172 ebs := gCurrentMap.mapdef.ebsTypeAt[eidx];
173 if ebs.has[cname] then
174 begin
175 //writeln('FOUND: ''', cname, '''');
176 v := ebs[cname];
177 result := true;
178 exit;
179 end;
180 end;
181 end;
184 // ////////////////////////////////////////////////////////////////////////// //
185 constructor TTrigScope.Create ();
186 begin
187 plrprops := TPropHash.Create(TPlayer, 'e');
188 monsprops := TPropHash.Create(TMonster, 'e');
189 platprops := TPropHash.Create(TPanel, 'e');
190 me := nil;
191 end;
194 destructor TTrigScope.Destroy ();
195 begin
196 platprops.Free();
197 monsprops.Free();
198 plrprops.Free();
199 inherited;
200 end;
203 function TTrigScope.getObj (const aname: AnsiString): TObject;
204 begin
205 if (aname = 'player') then result := gPlayers[0] //FIXME
206 else if (aname = 'self') or (aname = 'this') then result := TObject(Pointer(PtrUInt(1)))
207 else result := inherited getObj(aname);
208 end;
211 function TTrigScope.getField (obj: TObject; const afldname: AnsiString): Variant;
212 begin
213 if (obj = gPlayers[0]) then
214 begin
215 if plrprops.get(obj, afldname, result) then exit;
216 end
217 else if (obj = TObject(Pointer(PtrUInt(1)))) then
218 begin
219 if (me <> nil) and (me.userVars <> nil) then
220 begin
221 if me.userVars.get(afldname, result) then exit;
222 end;
223 end;
224 result := inherited getField(obj, afldname);
225 end;
228 procedure TTrigScope.setField (obj: TObject; const afldname: AnsiString; var aval: Variant);
229 begin
230 if (obj = gPlayers[0]) then
231 begin
232 if plrprops.put(obj, afldname, aval) then exit;
233 end
234 else if (obj = TObject(Pointer(PtrUInt(1)))) then
235 begin
236 if (me <> nil) then
237 begin
238 if (Length(afldname) > 4) and (afldname[1] = 'u') and (afldname[2] = 's') and
239 (afldname[3] = 'e') and (afldname[4] = 'r') then
240 begin
241 if (me.userVars = nil) then me.userVars := THashStrVariant.Create();
242 me.userVars.put(afldname, aval);
243 exit;
244 end;
245 end;
246 end;
247 inherited setField(obj, afldname, aval);
248 end;
251 // ////////////////////////////////////////////////////////////////////////// //
252 var
253 tgscope: TTrigScope = nil;
254 tgclist: TMyConstList = nil;
257 // ////////////////////////////////////////////////////////////////////////// //
258 function TTrigger.trigCenter (): TDFPoint; inline;
259 begin
260 result := TDFPoint.Create(x+width div 2, y+height div 2);
261 end;
264 function FindTrigger (): DWORD;
265 var
266 i, olen: Integer;
267 begin
268 olen := Length(gTriggers);
270 for i := 0 to olen-1 do
271 begin
272 if gTriggers[i].TriggerType = TRIGGER_NONE then begin result := i; exit; end;
273 end;
275 SetLength(gTriggers, olen+8);
276 result := olen;
278 for i := result to High(gTriggers) do
279 begin
280 gTriggers[i].TriggerType := TRIGGER_NONE;
281 gTriggers[i].trigDataRec := nil;
282 gTriggers[i].exoInit := nil;
283 gTriggers[i].exoThink := nil;
284 gTriggers[i].exoCheck := nil;
285 gTriggers[i].exoAction := nil;
286 gTriggers[i].userVars := nil;
287 end;
288 end;
291 function tr_CloseDoor (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
292 var
293 a, b, c: Integer;
294 pan: TPanel;
295 PanelID: Integer;
296 begin
297 result := false;
298 pan := g_Map_PanelByGUID(PanelGUID);
299 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
300 PanelID := pan.arrIdx;
302 if not d2d then
303 begin
304 with gWalls[PanelID] do
305 begin
306 if g_CollidePlayer(X, Y, Width, Height) or g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
307 if not Enabled then
308 begin
309 if not NoSound then
310 begin
311 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
312 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
313 end;
314 g_Map_EnableWallGUID(PanelGUID);
315 result := true;
316 end;
317 end;
318 end
319 else
320 begin
321 if (gDoorMap = nil) then exit;
323 c := -1;
324 for a := 0 to High(gDoorMap) do
325 begin
326 for b := 0 to High(gDoorMap[a]) do
327 begin
328 if gDoorMap[a, b] = DWORD(PanelID) then
329 begin
330 c := a;
331 break;
332 end;
333 end;
334 if (c <> -1) then break;
335 end;
336 if (c = -1) then exit;
338 for b := 0 to High(gDoorMap[c]) do
339 begin
340 with gWalls[gDoorMap[c, b]] do
341 begin
342 if g_CollidePlayer(X, Y, Width, Height) or g_Mons_IsAnyAliveAt(X, Y, Width, Height) then exit;
343 end;
344 end;
346 if not NoSound then
347 begin
348 for b := 0 to High(gDoorMap[c]) do
349 begin
350 if not gWalls[gDoorMap[c, b]].Enabled then
351 begin
352 with gWalls[PanelID] do
353 begin
354 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
355 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
356 end;
357 break;
358 end;
359 end;
360 end;
362 for b := 0 to High(gDoorMap[c]) do
363 begin
364 if not gWalls[gDoorMap[c, b]].Enabled then
365 begin
366 g_Map_EnableWall_XXX(gDoorMap[c, b]);
367 result := true;
368 end;
369 end;
370 end;
371 end;
374 procedure tr_CloseTrap (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean);
375 var
376 a, b, c: Integer;
377 wx, wy, wh, ww: Integer;
378 pan: TPanel;
379 PanelID: Integer;
381 function monsDamage (mon: TMonster): Boolean;
382 begin
383 result := false; // don't stop
384 if g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
385 end;
387 begin
388 pan := g_Map_PanelByGUID(PanelGUID);
390 if (pan = nil) then
391 begin
392 e_LogWritefln('tr_CloseTrap: pguid=%s; NO PANEL!', [PanelGUID], MSG_WARNING);
393 end
394 else
395 begin
396 e_LogWritefln('tr_CloseTrap: pguid=%s; isGWall=%s; arrIdx=%s', [PanelGUID, pan.isGWall, pan.arrIdx]);
397 end;
399 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
400 PanelID := pan.arrIdx;
402 if not d2d then
403 begin
404 with gWalls[PanelID] do
405 begin
406 if (not NoSound) and (not Enabled) then
407 begin
408 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
409 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
410 end;
411 end;
413 wx := gWalls[PanelID].X;
414 wy := gWalls[PanelID].Y;
415 ww := gWalls[PanelID].Width;
416 wh := gWalls[PanelID].Height;
418 with gWalls[PanelID] do
419 begin
420 if gPlayers <> nil then
421 begin
422 for a := 0 to High(gPlayers) do
423 begin
424 if (gPlayers[a] <> nil) and gPlayers[a].alive and gPlayers[a].Collide(X, Y, Width, Height) then
425 begin
426 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
427 end;
428 end;
429 end;
431 //g_Mons_ForEach(monsDamage);
432 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
434 if not Enabled then g_Map_EnableWallGUID(PanelGUID);
435 end;
436 end
437 else
438 begin
439 if (gDoorMap = nil) then exit;
441 c := -1;
442 for a := 0 to High(gDoorMap) do
443 begin
444 for b := 0 to High(gDoorMap[a]) do
445 begin
446 if gDoorMap[a, b] = DWORD(PanelID) then
447 begin
448 c := a;
449 break;
450 end;
451 end;
452 if (c <> -1) then break;
453 end;
454 if (c = -1) then exit;
456 if not NoSound then
457 begin
458 for b := 0 to High(gDoorMap[c]) do
459 begin
460 if not gWalls[gDoorMap[c, b]].Enabled then
461 begin
462 with gWalls[PanelID] do
463 begin
464 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
465 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
466 end;
467 Break;
468 end;
469 end;
470 end;
472 for b := 0 to High(gDoorMap[c]) do
473 begin
474 wx := gWalls[gDoorMap[c, b]].X;
475 wy := gWalls[gDoorMap[c, b]].Y;
476 ww := gWalls[gDoorMap[c, b]].Width;
477 wh := gWalls[gDoorMap[c, b]].Height;
479 with gWalls[gDoorMap[c, b]] do
480 begin
481 if gPlayers <> nil then
482 begin
483 for a := 0 to High(gPlayers) do
484 begin
485 if (gPlayers[a] <> nil) and gPlayers[a].alive and gPlayers[a].Collide(X, Y, Width, Height) then
486 begin
487 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
488 end;
489 end;
490 end;
492 //g_Mons_ForEach(monsDamage);
493 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
494 (*
495 if gMonsters <> nil then
496 for a := 0 to High(gMonsters) do
497 if (gMonsters[a] <> nil) and gMonsters[a].alive and
498 g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
499 gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
500 *)
502 if not Enabled then g_Map_EnableWall_XXX(gDoorMap[c, b]);
503 end;
504 end;
505 end;
506 end;
509 function tr_OpenDoor (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
510 var
511 a, b, c: Integer;
512 pan: TPanel;
513 PanelID: Integer;
514 begin
515 result := false;
516 pan := g_Map_PanelByGUID(PanelGUID);
517 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
518 PanelID := pan.arrIdx;
520 if not d2d then
521 begin
522 with gWalls[PanelID] do
523 begin
524 if Enabled then
525 begin
526 if not NoSound then
527 begin
528 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
529 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
530 end;
531 g_Map_DisableWallGUID(PanelGUID);
532 result := true;
533 end;
534 end
535 end
536 else
537 begin
538 if (gDoorMap = nil) then exit;
540 c := -1;
541 for a := 0 to High(gDoorMap) do
542 begin
543 for b := 0 to High(gDoorMap[a]) do
544 begin
545 if gDoorMap[a, b] = DWORD(PanelID) then
546 begin
547 c := a;
548 break;
549 end;
550 end;
551 if (c <> -1) then break;
552 end;
553 if (c = -1) then exit;
555 if not NoSound then
556 begin
557 for b := 0 to High(gDoorMap[c]) do
558 begin
559 if gWalls[gDoorMap[c, b]].Enabled then
560 begin
561 with gWalls[PanelID] do
562 begin
563 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
564 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
565 end;
566 break;
567 end;
568 end;
569 end;
571 for b := 0 to High(gDoorMap[c]) do
572 begin
573 if gWalls[gDoorMap[c, b]].Enabled then
574 begin
575 g_Map_DisableWall_XXX(gDoorMap[c, b]);
576 result := true;
577 end;
578 end;
579 end;
580 end;
583 function tr_SetLift (PanelGUID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
584 var
585 a, b, c: Integer;
586 t: Integer = 0;
587 pan: TPanel;
588 PanelID: Integer;
589 begin
590 result := false;
591 pan := g_Map_PanelByGUID(PanelGUID);
592 if (pan = nil) or not pan.isGLift then exit; //!FIXME!TRIGANY!
593 PanelID := pan.arrIdx;
595 if (gLifts[PanelID].PanelType = PANEL_LIFTUP) or (gLifts[PanelID].PanelType = PANEL_LIFTDOWN) then
596 begin
597 case d of
598 0: t := LIFTTYPE_UP;
599 1: t := LIFTTYPE_DOWN;
600 else t := IfThen(gLifts[PanelID].LiftType = LIFTTYPE_DOWN, LIFTTYPE_UP, LIFTTYPE_DOWN);
601 end
602 end
603 else if (gLifts[PanelID].PanelType = PANEL_LIFTLEFT) or (gLifts[PanelID].PanelType = PANEL_LIFTRIGHT) then
604 begin
605 case d of
606 0: t := LIFTTYPE_LEFT;
607 1: t := LIFTTYPE_RIGHT;
608 else t := IfThen(gLifts[PanelID].LiftType = LIFTTYPE_LEFT, LIFTTYPE_RIGHT, LIFTTYPE_LEFT);
609 end;
610 end;
612 if not d2d then
613 begin
614 with gLifts[PanelID] do
615 begin
616 if (LiftType <> t) then
617 begin
618 g_Map_SetLiftGUID(PanelGUID, t); //???
619 //if not NoSound then g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
620 result := true;
621 end;
622 end;
623 end
624 else // Êàê â D2d
625 begin
626 if (gLiftMap = nil) then exit;
628 c := -1;
629 for a := 0 to High(gLiftMap) do
630 begin
631 for b := 0 to High(gLiftMap[a]) do
632 begin
633 if (gLiftMap[a, b] = DWORD(PanelID)) then
634 begin
635 c := a;
636 break;
637 end;
638 end;
639 if (c <> -1) then break;
640 end;
641 if (c = -1) then exit;
643 {if not NoSound then
644 for b := 0 to High(gLiftMap[c]) do
645 if gLifts[gLiftMap[c, b]].LiftType <> t then
646 begin
647 with gLifts[PanelID] do
648 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
649 Break;
650 end;}
652 for b := 0 to High(gLiftMap[c]) do
653 begin
654 with gLifts[gLiftMap[c, b]] do
655 begin
656 if (LiftType <> t) then
657 begin
658 g_Map_SetLift_XXX(gLiftMap[c, b], t);
659 result := true;
660 end;
661 end;
662 end;
663 end;
664 end;
667 function tr_SpawnShot (ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
668 var
669 snd: string;
670 Projectile: Boolean;
671 begin
672 result := -1;
673 snd := 'SOUND_WEAPON_FIREROCKET';
674 Projectile := true;
676 case ShotType of
677 TRIGGER_SHOT_PISTOL:
678 begin
679 g_Weapon_pistol(wx, wy, dx, dy, 0, True);
680 snd := 'SOUND_WEAPON_FIREPISTOL';
681 Projectile := False;
682 if ShotSound then
683 begin
684 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
685 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
686 end;
687 end;
689 TRIGGER_SHOT_BULLET:
690 begin
691 g_Weapon_mgun(wx, wy, dx, dy, 0, True);
692 if gSoundEffectsDF then snd := 'SOUND_WEAPON_FIRECGUN'
693 else snd := 'SOUND_WEAPON_FIREPISTOL';
694 Projectile := False;
695 if ShotSound then
696 begin
697 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
698 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
699 end;
700 end;
702 TRIGGER_SHOT_SHOTGUN:
703 begin
704 g_Weapon_Shotgun(wx, wy, dx, dy, 0, True);
705 snd := 'SOUND_WEAPON_FIRESHOTGUN';
706 Projectile := False;
707 if ShotSound then
708 begin
709 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
710 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL2);
711 end;
712 end;
714 TRIGGER_SHOT_SSG:
715 begin
716 g_Weapon_DShotgun(wx, wy, dx, dy, 0, True);
717 snd := 'SOUND_WEAPON_FIRESHOTGUN2';
718 Projectile := False;
719 if ShotSound then
720 begin
721 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
722 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
723 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL3);
724 end;
725 end;
727 TRIGGER_SHOT_IMP:
728 begin
729 g_Weapon_ball1(wx, wy, dx, dy, 0, -1, True);
730 snd := 'SOUND_WEAPON_FIREBALL';
731 end;
733 TRIGGER_SHOT_PLASMA:
734 begin
735 g_Weapon_Plasma(wx, wy, dx, dy, 0, -1, True);
736 snd := 'SOUND_WEAPON_FIREPLASMA';
737 end;
739 TRIGGER_SHOT_SPIDER:
740 begin
741 g_Weapon_aplasma(wx, wy, dx, dy, 0, -1, True);
742 snd := 'SOUND_WEAPON_FIREPLASMA';
743 end;
745 TRIGGER_SHOT_CACO:
746 begin
747 g_Weapon_ball2(wx, wy, dx, dy, 0, -1, True);
748 snd := 'SOUND_WEAPON_FIREBALL';
749 end;
751 TRIGGER_SHOT_BARON:
752 begin
753 g_Weapon_ball7(wx, wy, dx, dy, 0, -1, True);
754 snd := 'SOUND_WEAPON_FIREBALL';
755 end;
757 TRIGGER_SHOT_MANCUB:
758 begin
759 g_Weapon_manfire(wx, wy, dx, dy, 0, -1, True);
760 snd := 'SOUND_WEAPON_FIREBALL';
761 end;
763 TRIGGER_SHOT_REV:
764 begin
765 g_Weapon_revf(wx, wy, dx, dy, 0, ShotTarget, -1, True);
766 snd := 'SOUND_WEAPON_FIREREV';
767 end;
769 TRIGGER_SHOT_ROCKET:
770 begin
771 g_Weapon_Rocket(wx, wy, dx, dy, 0, -1, True);
772 snd := 'SOUND_WEAPON_FIREROCKET';
773 end;
775 TRIGGER_SHOT_BFG:
776 begin
777 g_Weapon_BFGShot(wx, wy, dx, dy, 0, -1, True);
778 snd := 'SOUND_WEAPON_FIREBFG';
779 end;
781 TRIGGER_SHOT_EXPL:
782 begin
783 {$IFDEF ENABLE_GFX}
784 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, wx - 64, wy - 64);
785 {$ENDIF}
786 Projectile := False;
787 g_Weapon_Explode(wx, wy, 60, 0);
788 snd := 'SOUND_WEAPON_EXPLODEROCKET';
789 end;
791 TRIGGER_SHOT_BFGEXPL:
792 begin
793 {$IFDEF ENABLE_GFX}
794 g_GFX_QueueEffect(R_GFX_EXPLODE_BFG, wx - 64, wy - 64);
795 {$ENDIF}
796 Projectile := False;
797 g_Weapon_BFG9000(wx, wy, 0);
798 snd := 'SOUND_WEAPON_EXPLODEBFG';
799 end;
801 TRIGGER_SHOT_FLAME:
802 begin
803 g_Weapon_flame(wx, wy, dx, dy, 0, -1, True);
804 snd := 'SOUND_GAME_BURNING';
805 end;
807 else exit;
808 end;
810 if g_Game_IsNet and g_Game_IsServer then
811 begin
812 case ShotType of
813 TRIGGER_SHOT_EXPL: MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_EXPLODE);
814 TRIGGER_SHOT_BFGEXPL: MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_BFGEXPL);
815 else
816 begin
817 if Projectile then MH_SEND_CreateShot(LastShotID);
818 if ShotSound then MH_SEND_Sound(wx, wy, snd);
819 end;
820 end;
821 end;
823 if ShotSound then g_Sound_PlayExAt(snd, wx, wy);
825 if Projectile then Result := LastShotID;
826 end;
829 procedure MakeShot (var Trigger: TTrigger; wx, wy, dx, dy: Integer; TargetUID: Word);
830 begin
831 with Trigger do
832 begin
833 if (tgcAmmo = 0) or ((tgcAmmo > 0) and (ShotAmmoCount > 0)) then
834 begin
835 if (trigPanelGUID <> -1) and (ShotPanelTime = 0) then
836 begin
837 g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
838 ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
839 end;
841 if (tgcSight > 0) then ShotSightTimeout := 180; // ~= 5 ñåêóíä
843 if (ShotAmmoCount > 0) then Dec(ShotAmmoCount);
845 dx += Random(tgcAccuracy)-Random(tgcAccuracy);
846 dy += Random(tgcAccuracy)-Random(tgcAccuracy);
848 tr_SpawnShot(tgcShotType, wx, wy, dx, dy, tgcShotSound, TargetUID);
849 end
850 else
851 begin
852 if (tgcReload > 0) and (ShotReloadTime = 0) then
853 begin
854 ShotReloadTime := tgcReload; // òèêîâ íà ïåðåçàðÿäêó ïóøêè
855 end;
856 end;
857 end;
858 end;
861 procedure tr_MakeEffect (X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
862 begin
863 {$IFDEF ENABLE_GFX}
864 if T = TRIGGER_EFFECT_PARTICLE then
865 begin
866 case ST of
867 TRIGGER_EFFECT_SLIQUID:
868 begin
869 if (CR = 255) and (CG = 0) and (CB = 0) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 1, 0, 0, 0)
870 else if (CR = 0) and (CG = 255) and (CB = 0) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 2, 0, 0, 0)
871 else if (CR = 0) and (CG = 0) and (CB = 255) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 3, 0, 0, 0)
872 else g_GFX_SimpleWater(X, Y, 1, VX, VY, 0, 0, 0, 0);
873 end;
874 TRIGGER_EFFECT_LLIQUID: g_GFX_SimpleWater(X, Y, 1, VX, VY, 4, CR, CG, CB);
875 TRIGGER_EFFECT_DLIQUID: g_GFX_SimpleWater(X, Y, 1, VX, VY, 5, CR, CG, CB);
876 TRIGGER_EFFECT_BLOOD: g_GFX_Blood(X, Y, 1, VX, VY, 0, 0, CR, CG, CB);
877 TRIGGER_EFFECT_SPARK: g_GFX_Spark(X, Y, 1, GetAngle2(VX, VY), 0, 0);
878 TRIGGER_EFFECT_BUBBLE: g_GFX_Bubbles(X, Y, 1, 0, 0);
879 end;
880 end;
881 {$ENDIF}
883 if T = TRIGGER_EFFECT_ANIMATION then
884 begin
885 case ST of
886 EFFECT_TELEPORT: begin
887 if not Silent then g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
888 {$IFDEF ENABLE_GFX}
889 g_GFX_QueueEffect(R_GFX_TELEPORT_FAST, X - 32, Y - 32);
890 {$ENDIF}
891 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X, Y, Byte(not Silent), NET_GFX_TELE);
892 end;
893 EFFECT_RESPAWN: begin
894 if not Silent then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
895 {$IFDEF ENABLE_GFX}
896 g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X - 16, Y - 16);
897 {$ENDIF}
898 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X-16, Y-16, Byte(not Silent), NET_GFX_RESPAWN);
899 end;
900 EFFECT_FIRE: begin
901 if not Silent then g_Sound_PlayExAt('SOUND_FIRE', X, Y);
902 {$IFDEF ENABLE_GFX}
903 g_GFX_QueueEffect(R_GFX_FIRE, X - 32, Y - 128);
904 {$ENDIF}
905 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X-32, Y-128, Byte(not Silent), NET_GFX_FIRE);
906 end;
907 end;
908 end;
909 end;
912 function tr_Teleport (ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
913 var
914 p: TPlayer;
915 m: TMonster;
916 begin
917 Result := False;
918 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
919 case g_GetUIDType(ActivateUID) of
920 UID_PLAYER:
921 begin
922 p := g_Player_Get(ActivateUID);
923 if p = nil then Exit;
924 if D2D then
925 begin
926 if p.TeleportTo(TX-(p.Obj.Rect.Width div 2), TY-p.Obj.Rect.Height, Silent, TDir) then result := true;
927 end
928 else
929 begin
930 if p.TeleportTo(TX, TY, Silent, TDir) then result := true;
931 end;
932 end;
933 UID_MONSTER:
934 begin
935 m := g_Monsters_ByUID(ActivateUID);
936 if m = nil then Exit;
937 if D2D then
938 begin
939 if m.TeleportTo(TX-(m.Obj.Rect.Width div 2), TY-m.Obj.Rect.Height, Silent, TDir) then result := true;
940 end
941 else
942 begin
943 if m.TeleportTo(TX, TY, Silent, TDir) then result := true;
944 end;
945 end;
946 end;
947 end;
950 function tr_Push (ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
951 var
952 p: TPlayer;
953 m: TMonster;
954 begin
955 result := true;
956 if (ActivateUID < 0) or (ActivateUID > $FFFF) then exit;
957 case g_GetUIDType(ActivateUID) of
958 UID_PLAYER:
959 begin
960 p := g_Player_Get(ActivateUID);
961 if p = nil then Exit;
963 if ResetVel then
964 begin
965 p.GameVelX := 0;
966 p.GameVelY := 0;
967 p.GameAccelX := 0;
968 p.GameAccelY := 0;
969 end;
971 p.Push(VX, VY);
972 end;
974 UID_MONSTER:
975 begin
976 m := g_Monsters_ByUID(ActivateUID);
977 if m = nil then Exit;
979 if ResetVel then
980 begin
981 m.GameVelX := 0;
982 m.GameVelY := 0;
983 m.GameAccelX := 0;
984 m.GameAccelY := 0;
985 end;
987 m.Push(VX, VY);
988 end;
989 end;
990 end;
993 function tr_Message (MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
994 var
995 msg: string;
996 p: TPlayer;
997 i: Integer;
998 begin
999 Result := True;
1000 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
1001 msg := b_Text_Format(MText);
1002 case MSendTo of
1003 TRIGGER_MESSAGE_DEST_ME: // activator
1004 begin
1005 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1006 begin
1007 if g_Game_IsWatchedPlayer(ActivateUID) then
1008 begin
1009 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1010 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1011 end
1012 else
1013 begin
1014 p := g_Player_Get(ActivateUID);
1015 if g_Game_IsNet and (p.FClientID >= 0) then
1016 begin
1017 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, p.FClientID)
1018 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, p.FClientID);
1019 end;
1020 end;
1021 end;
1022 end;
1024 TRIGGER_MESSAGE_DEST_MY_TEAM: // activator's team
1025 begin
1026 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1027 begin
1028 p := g_Player_Get(ActivateUID);
1029 if g_Game_IsWatchedTeam(p.Team) then
1030 begin
1031 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1032 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1033 end;
1035 if g_Game_IsNet then
1036 begin
1037 for i := Low(gPlayers) to High(gPlayers) do
1038 begin
1039 if (gPlayers[i].Team = p.Team) and (gPlayers[i].FClientID >= 0) then
1040 begin
1041 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
1042 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
1043 end;
1044 end;
1045 end;
1046 end;
1047 end;
1049 TRIGGER_MESSAGE_DEST_ENEMY_TEAM: // activator's enemy team
1050 begin
1051 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1052 begin
1053 p := g_Player_Get(ActivateUID);
1054 if g_Game_IsWatchedTeam(p.Team) then
1055 begin
1056 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1057 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1058 end;
1060 if g_Game_IsNet then
1061 begin
1062 for i := Low(gPlayers) to High(gPlayers) do
1063 begin
1064 if (gPlayers[i].Team <> p.Team) and (gPlayers[i].FClientID >= 0) then
1065 begin
1066 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
1067 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
1068 end;
1069 end;
1070 end;
1071 end;
1072 end;
1074 TRIGGER_MESSAGE_DEST_RED_TEAM: // red team
1075 begin
1076 if g_Game_IsWatchedTeam(TEAM_RED) then
1077 begin
1078 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1079 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1080 end;
1082 if g_Game_IsNet then
1083 begin
1084 for i := Low(gPlayers) to High(gPlayers) do
1085 begin
1086 if (gPlayers[i].Team = TEAM_RED) and (gPlayers[i].FClientID >= 0) then
1087 begin
1088 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
1089 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
1090 end;
1091 end;
1092 end;
1093 end;
1095 TRIGGER_MESSAGE_DEST_BLUE_TEAM: // blue team
1096 begin
1097 if g_Game_IsWatchedTeam(TEAM_BLUE) then
1098 begin
1099 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1100 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1101 end;
1103 if g_Game_IsNet then
1104 begin
1105 for i := Low(gPlayers) to High(gPlayers) do
1106 begin
1107 if (gPlayers[i].Team = TEAM_BLUE) and (gPlayers[i].FClientID >= 0) then
1108 begin
1109 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
1110 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
1111 end;
1112 end;
1113 end;
1114 end;
1116 TRIGGER_MESSAGE_DEST_EVERYONE: // everyone
1117 begin
1118 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
1119 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
1121 if g_Game_IsNet then
1122 begin
1123 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM)
1124 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg);
1125 end;
1126 end;
1127 end;
1128 end;
1131 function tr_ShotAimCheck (var Trigger: TTrigger; Obj: PObj): Boolean;
1132 begin
1133 result := false;
1134 with Trigger do
1135 begin
1136 if TriggerType <> TRIGGER_SHOT then Exit;
1137 result := (tgcAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
1138 or g_Obj_Collide(X, Y, Width, Height, Obj);
1139 if result and (tgcAim and TRIGGER_SHOT_AIM_TRACE > 0) then
1140 begin
1141 result := g_TraceVector(tgcTX, tgcTY,
1142 Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
1143 Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
1144 end;
1145 end;
1146 end;
1149 function ActivateTrigger (var Trigger: TTrigger; actType: Byte): Boolean;
1150 var
1151 animonce: Boolean;
1152 p: TPlayer;
1153 m: TMonster;
1154 pan: TPanel;
1155 idx, k, wx, wy, xd, yd: Integer;
1156 iid: LongWord;
1157 coolDown: Boolean;
1158 pAngle: Real;
1159 UIDType: Byte;
1160 TargetUID: Word;
1161 it: PItem;
1162 mon: TMonster;
1164 function monsShotTarget (mon: TMonster): Boolean;
1165 begin
1166 result := false; // don't stop
1167 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1168 begin
1169 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1170 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1171 TargetUID := mon.UID;
1172 result := true; // stop
1173 end;
1174 end;
1176 function monsShotTargetMonPlr (mon: TMonster): Boolean;
1177 begin
1178 result := false; // don't stop
1179 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1180 begin
1181 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1182 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1183 TargetUID := mon.UID;
1184 result := true; // stop
1185 end;
1186 end;
1188 function monShotTargetPlrMon (mon: TMonster): Boolean;
1189 begin
1190 result := false; // don't stop
1191 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1192 begin
1193 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1194 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1195 TargetUID := mon.UID;
1196 result := true; // stop
1197 end;
1198 end;
1200 var
1201 tvval: Variant;
1202 begin
1203 result := false;
1204 if g_Game_IsClient then exit;
1206 if not Trigger.Enabled then exit;
1207 if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then exit;
1208 if (gLMSRespawn > LMS_RESPAWN_NONE) then exit;
1210 if (Trigger.exoCheck <> nil) then
1211 begin
1212 //conwritefln('exocheck: [%s]', [Trigger.exoCheck.toString()]);
1213 try
1214 tgscope.me := @Trigger;
1215 tvval := Trigger.exoCheck.value(tgscope);
1216 tgscope.me := nil;
1217 if not Boolean(tvval) then exit;
1218 except on e: Exception do
1219 begin
1220 tgscope.me := nil;
1221 conwritefln('trigger exocheck error: %s [%s]', [e.message, Trigger.exoCheck.toString()]);
1222 exit;
1223 end;
1224 end;
1225 end;
1227 animonce := False;
1229 coolDown := (actType <> 0);
1231 if (Trigger.exoAction <> nil) then
1232 begin
1233 //conwritefln('exoactivate: [%s]', [Trigger.exoAction.toString()]);
1234 try
1235 tgscope.me := @Trigger;
1236 Trigger.exoAction.value(tgscope);
1237 tgscope.me := nil;
1238 except on e: Exception do
1239 begin
1240 tgscope.me := nil;
1241 conwritefln('trigger exoactivate error: %s [%s]', [e.message, Trigger.exoAction.toString()]);
1242 exit;
1243 end;
1244 end;
1245 end;
1247 with Trigger do
1248 begin
1249 case TriggerType of
1250 TRIGGER_EXIT:
1251 begin
1252 g_Sound_PlayEx('SOUND_GAME_SWITCH0');
1253 if g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH0');
1254 gExitByTrigger := True;
1255 g_Game_ExitLevel(tgcMap);
1256 TimeOut := 18;
1257 Result := True;
1259 Exit;
1260 end;
1262 TRIGGER_TELEPORT:
1263 begin
1264 Result := tr_Teleport(ActivateUID,
1265 tgcTarget.X, tgcTarget.Y,
1266 tgcDirection, tgcSilent,
1267 tgcD2d);
1268 TimeOut := 0;
1269 end;
1271 TRIGGER_OPENDOOR:
1272 begin
1273 Result := tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
1274 TimeOut := 0;
1275 end;
1277 TRIGGER_CLOSEDOOR:
1278 begin
1279 Result := tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d);
1280 TimeOut := 0;
1281 end;
1283 TRIGGER_DOOR, TRIGGER_DOOR5:
1284 begin
1285 pan := g_Map_PanelByGUID(trigPanelGUID);
1286 if (pan <> nil) and pan.isGWall then
1287 begin
1288 if gWalls[{trigPanelID}pan.arrIdx].Enabled then
1289 begin
1290 result := tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
1291 if (TriggerType = TRIGGER_DOOR5) then DoorTime := 180;
1292 end
1293 else
1294 begin
1295 result := tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d);
1296 end;
1298 if result then TimeOut := 18;
1299 end;
1300 end;
1302 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1303 begin
1304 tr_CloseTrap(trigPanelGUID, tgcSilent, tgcD2d);
1306 if TriggerType = TRIGGER_TRAP then
1307 begin
1308 DoorTime := 40;
1309 TimeOut := 76;
1310 end
1311 else
1312 begin
1313 DoorTime := -1;
1314 TimeOut := 0;
1315 end;
1317 Result := True;
1318 end;
1320 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
1321 begin
1322 PressCount += 1;
1323 if PressTime = -1 then PressTime := tgcWait;
1324 if coolDown then TimeOut := 18 else TimeOut := 0;
1325 Result := True;
1326 end;
1328 TRIGGER_SECRET:
1329 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1330 begin
1331 Enabled := False;
1332 Result := True;
1333 p := g_Player_Get(ActivateUID);
1334 p.GetSecret();
1335 Inc(gCoopSecretsFound);
1336 if g_Game_IsNet then
1337 begin
1338 MH_SEND_GameStats();
1339 MH_SEND_GameEvent(NET_EV_SECRET, p.UID, '');
1340 end;
1341 end;
1343 TRIGGER_LIFTUP:
1344 begin
1345 Result := tr_SetLift(trigPanelGUID, 0, tgcSilent, tgcD2d);
1346 TimeOut := 0;
1348 if (not tgcSilent) and Result then begin
1349 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1350 X + (Width div 2),
1351 Y + (Height div 2));
1352 if g_Game_IsServer and g_Game_IsNet then
1353 MH_SEND_Sound(X + (Width div 2),
1354 Y + (Height div 2),
1355 'SOUND_GAME_SWITCH0');
1356 end;
1357 end;
1359 TRIGGER_LIFTDOWN:
1360 begin
1361 Result := tr_SetLift(trigPanelGUID, 1, tgcSilent, tgcD2d);
1362 TimeOut := 0;
1364 if (not tgcSilent) and Result then begin
1365 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1366 X + (Width div 2),
1367 Y + (Height div 2));
1368 if g_Game_IsServer and g_Game_IsNet then
1369 MH_SEND_Sound(X + (Width div 2),
1370 Y + (Height div 2),
1371 'SOUND_GAME_SWITCH0');
1372 end;
1373 end;
1375 TRIGGER_LIFT:
1376 begin
1377 Result := tr_SetLift(trigPanelGUID, 3, tgcSilent, tgcD2d);
1379 if Result then
1380 begin
1381 TimeOut := 18;
1383 if (not tgcSilent) and Result then begin
1384 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1385 X + (Width div 2),
1386 Y + (Height div 2));
1387 if g_Game_IsServer and g_Game_IsNet then
1388 MH_SEND_Sound(X + (Width div 2),
1389 Y + (Height div 2),
1390 'SOUND_GAME_SWITCH0');
1391 end;
1392 end;
1393 end;
1395 TRIGGER_TEXTURE:
1396 begin
1397 if tgcActivateOnce then
1398 begin
1399 Enabled := False;
1400 TriggerType := TRIGGER_NONE;
1401 end
1402 else
1403 if coolDown then
1404 TimeOut := 6
1405 else
1406 TimeOut := 0;
1408 animonce := tgcAnimateOnce;
1409 Result := True;
1410 end;
1412 TRIGGER_SOUND:
1413 begin
1414 if Sound <> nil then
1415 begin
1416 if tgcSoundSwitch and Sound.IsPlaying() then
1417 begin // Íóæíî âûêëþ÷èòü, åñëè èãðàë
1418 Sound.Stop();
1419 SoundPlayCount := 0;
1420 Result := True;
1421 end
1422 else // (not Data.SoundSwitch) or (not Sound.IsPlaying())
1423 if (tgcPlayCount > 0) or (not Sound.IsPlaying()) then
1424 begin
1425 if tgcPlayCount > 0 then
1426 SoundPlayCount := tgcPlayCount
1427 else // 0 - èãðàåì áåñêîíå÷íî
1428 SoundPlayCount := 1;
1429 Result := True;
1430 end;
1431 if g_Game_IsNet then MH_SEND_TriggerSound(Trigger);
1432 end;
1433 end;
1435 TRIGGER_SPAWNMONSTER:
1436 if (tgcSpawnMonsType in [MONSTER_DEMON..MONSTER_MAN]) then
1437 begin
1438 Result := False;
1439 if (tgcDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1440 begin
1441 AutoSpawn := not AutoSpawn;
1442 SpawnCooldown := 0;
1443 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1444 Result := True;
1445 end;
1447 if ((tgcDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1448 or ((tgcDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1449 for k := 1 to tgcMonsCount do
1450 begin
1451 if (actType = ACTIVATE_CUSTOM) and (tgcDelay > 0) then
1452 SpawnCooldown := -1; // Çàäåðæêà âûñòàâèòñÿ ìîíñòðîì ïðè óíè÷òîæåíèè
1453 if (tgcMax > 0) and (SpawnedCount >= tgcMax) then
1454 Break;
1456 mon := g_Monsters_Create(tgcSpawnMonsType,
1457 tgcTX, tgcTY,
1458 TDirection(tgcDirection), True);
1460 Result := True;
1462 // Çäîðîâüå:
1463 if (tgcHealth > 0) then
1464 mon.SetHealth(tgcHealth);
1465 // Óñòàíàâëèâàåì ïîâåäåíèå:
1466 mon.MonsterBehaviour := tgcBehaviour;
1467 mon.FNoRespawn := True;
1468 if g_Game_IsNet then
1469 MH_SEND_MonsterSpawn(mon.UID);
1470 // Èäåì èñêàòü öåëü, åñëè íàäî:
1471 if tgcActive then
1472 mon.WakeUp();
1474 if tgcSpawnMonsType <> MONSTER_BARREL then Inc(gTotalMonsters);
1476 if g_Game_IsNet then
1477 begin
1478 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
1479 gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
1480 end;
1482 mon.SpawnTrigger := ID;
1483 if tgcMax > 0 then Inc(SpawnedCount);
1485 case tgcEffect of
1486 EFFECT_TELEPORT:
1487 begin
1488 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', tgcTX, tgcTY);
1489 {$IFDEF ENABLE_GFX}
1490 g_GFX_QueueEffect(
1491 R_GFX_TELEPORT_FAST,
1492 mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1493 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32
1494 );
1495 {$ENDIF}
1496 if g_Game_IsServer and g_Game_IsNet then
1497 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1498 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
1499 NET_GFX_TELE);
1500 end;
1501 EFFECT_RESPAWN:
1502 begin
1503 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', tgcTX, tgcTY);
1504 {$IFDEF ENABLE_GFX}
1505 g_GFX_QueueEffect(
1506 R_GFX_ITEM_RESPAWN,
1507 mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1508 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16
1509 );
1510 {$ENDIF}
1511 if g_Game_IsServer and g_Game_IsNet then
1512 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1513 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
1514 NET_GFX_RESPAWN);
1515 end;
1516 EFFECT_FIRE:
1517 begin
1518 g_Sound_PlayExAt('SOUND_FIRE', tgcTX, tgcTY);
1519 {$IFDEF ENABLE_GFX}
1520 g_GFX_QueueEffect(
1521 R_GFX_FIRE,
1522 mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1523 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128
1524 );
1525 {$ENDIF}
1526 if g_Game_IsServer and g_Game_IsNet then
1527 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1528 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, 1,
1529 NET_GFX_FIRE);
1530 end;
1531 end;
1532 end;
1533 if g_Game_IsNet then
1534 begin
1535 MH_SEND_GameStats();
1536 MH_SEND_CoopStats();
1537 end;
1539 if coolDown then
1540 TimeOut := 18
1541 else
1542 TimeOut := 0;
1543 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1544 if actType = ACTIVATE_CUSTOM then
1545 Result := False;
1546 end;
1548 TRIGGER_SPAWNITEM:
1549 if (tgcSpawnItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX]) then
1550 begin
1551 Result := False;
1552 if (tgcDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1553 begin
1554 AutoSpawn := not AutoSpawn;
1555 SpawnCooldown := 0;
1556 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1557 Result := True;
1558 end;
1560 if ((tgcDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1561 or ((tgcDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1562 if (not tgcDmonly) or
1563 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1564 for k := 1 to tgcItemCount do
1565 begin
1566 if (actType = ACTIVATE_CUSTOM) and (tgcDelay > 0) then
1567 SpawnCooldown := -1; // Çàäåðæêà âûñòàâèòñÿ èòåìîì ïðè óíè÷òîæåíèè
1568 if (tgcMax > 0) and (SpawnedCount >= tgcMax) then
1569 Break;
1571 iid := g_Items_Create(tgcTX, tgcTY,
1572 tgcSpawnItemType, tgcGravity, False, True);
1574 Result := True;
1576 it := g_Items_ByIdx(iid);
1577 it.SpawnTrigger := ID;
1578 if tgcMax > 0 then Inc(SpawnedCount);
1580 case tgcEffect of
1581 EFFECT_TELEPORT:
1582 begin
1583 it := g_Items_ByIdx(iid);
1584 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', tgcTX, tgcTY);
1585 {$IFDEF ENABLE_GFX}
1586 g_GFX_QueueEffect(
1587 R_GFX_TELEPORT_FAST,
1588 it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1589 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32
1590 );
1591 {$ENDIF}
1592 if g_Game_IsServer and g_Game_IsNet then
1593 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1594 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
1595 NET_GFX_TELE);
1596 end;
1597 EFFECT_RESPAWN:
1598 begin
1599 it := g_Items_ByIdx(iid);
1600 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', tgcTX, tgcTY);
1601 {$IFDEF ENABLE_GFX}
1602 g_GFX_QueueEffect(
1603 R_GFX_ITEM_RESPAWN,
1604 it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1605 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16
1606 );
1607 {$ENDIF}
1608 if g_Game_IsServer and g_Game_IsNet then
1609 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1610 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
1611 NET_GFX_RESPAWN);
1612 end;
1613 EFFECT_FIRE:
1614 begin
1615 it := g_Items_ByIdx(iid);
1616 g_Sound_PlayExAt('SOUND_FIRE', tgcTX, tgcTY);
1617 {$IFDEF ENABLE_GFX}
1618 g_GFX_QueueEffect(
1619 R_GFX_FIRE,
1620 it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1621 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128
1622 );
1623 {$ENDIF}
1624 if g_Game_IsServer and g_Game_IsNet then
1625 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1626 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
1627 NET_GFX_FIRE);
1628 end;
1629 end;
1631 if g_Game_IsNet then
1632 MH_SEND_ItemSpawn(True, iid);
1633 end;
1635 if coolDown then
1636 TimeOut := 18
1637 else
1638 TimeOut := 0;
1639 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1640 if actType = ACTIVATE_CUSTOM then
1641 Result := False;
1642 end;
1644 TRIGGER_MUSIC:
1645 begin
1646 // Ìåíÿåì ìóçûêó, åñëè åñòü íà ÷òî:
1647 if (Trigger.tgcMusicName <> '') then
1648 begin
1649 gMusic.SetByName(Trigger.tgcMusicName);
1650 gMusic.SpecPause := True;
1651 gMusic.Play();
1652 end;
1654 case Trigger.tgcMusicAction of
1655 TRIGGER_MUSIC_ACTION_STOP: // Âûêëþ÷èòü
1656 gMusic.SpecPause := True; // Ïàóçà
1657 TRIGGER_MUSIC_ACTION_PLAY: // Âêëþ÷èòü
1658 if gMusic.SpecPause then // Áûëà íà ïàóçå => èãðàòü
1659 gMusic.SpecPause := False
1660 else // Èãðàëà => ñíà÷àëà
1661 gMusic.SetPosition(0);
1662 end;
1664 if coolDown then
1665 TimeOut := 36
1666 else
1667 TimeOut := 0;
1668 Result := True;
1669 if g_Game_IsNet then MH_SEND_TriggerMusic;
1670 end;
1672 TRIGGER_PUSH:
1673 begin
1674 pAngle := -DegToRad(tgcAngle);
1675 Result := tr_Push(ActivateUID,
1676 Floor(Cos(pAngle)*tgcForce),
1677 Floor(Sin(pAngle)*tgcForce),
1678 tgcResetVelocity);
1679 TimeOut := 0;
1680 end;
1682 TRIGGER_SCORE:
1683 begin
1684 Result := False;
1685 // Ïðèáàâèòü èëè îòíÿòü î÷êî
1686 if (tgcScoreAction in [TRIGGER_SCORE_ACTION_ADD, TRIGGER_SCORE_ACTION_SUB]) and (tgcScoreCount > 0) then
1687 begin
1688 // Ñâîåé èëè ÷óæîé êîìàíäå
1689 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1690 begin
1691 p := g_Player_Get(ActivateUID);
1692 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED))
1693 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1694 begin
1695 Inc(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Scores
1697 if tgcScoreCon then
1698 begin
1699 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1700 begin
1701 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1702 if g_Game_IsServer and g_Game_IsNet then
1703 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+r');
1704 end else
1705 begin
1706 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1707 if g_Game_IsServer and g_Game_IsNet then
1708 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+re');
1709 end;
1710 end;
1712 if tgcScoreMsg then
1713 begin
1714 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1715 if g_Game_IsServer and g_Game_IsNet then
1716 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1717 end;
1718 end;
1719 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED))
1720 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1721 begin
1722 Dec(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Fouls
1724 if tgcScoreCon then
1725 begin
1726 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1727 begin
1728 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1729 if g_Game_IsServer and g_Game_IsNet then
1730 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-r');
1731 end else
1732 begin
1733 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1734 if g_Game_IsServer and g_Game_IsNet then
1735 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-re');
1736 end;
1737 end;
1739 if tgcScoreMsg then
1740 begin
1741 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1742 if g_Game_IsServer and g_Game_IsNet then
1743 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1744 end;
1745 end;
1746 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE))
1747 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1748 begin
1749 Inc(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Scores
1751 if tgcScoreCon then
1752 begin
1753 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1754 begin
1755 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1756 if g_Game_IsServer and g_Game_IsNet then
1757 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+b');
1758 end else
1759 begin
1760 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1761 if g_Game_IsServer and g_Game_IsNet then
1762 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+be');
1763 end;
1764 end;
1766 if tgcScoreMsg then
1767 begin
1768 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1769 if g_Game_IsServer and g_Game_IsNet then
1770 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1771 end;
1772 end;
1773 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE))
1774 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1775 begin
1776 Dec(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Fouls
1778 if tgcScoreCon then
1779 begin
1780 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1781 begin
1782 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1783 if g_Game_IsServer and g_Game_IsNet then
1784 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-b');
1785 end else
1786 begin
1787 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1788 if g_Game_IsServer and g_Game_IsNet then
1789 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-be');
1790 end;
1791 end;
1793 if tgcScoreMsg then
1794 begin
1795 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1796 if g_Game_IsServer and g_Game_IsNet then
1797 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1798 end;
1799 end;
1800 Result := (p.Team = TEAM_RED) or (p.Team = TEAM_BLUE);
1801 end;
1802 // Êàêîé-òî êîíêðåòíîé êîìàíäå
1803 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_RED, TRIGGER_SCORE_TEAM_FORCE_BLUE] then
1804 begin
1805 if (tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then
1806 begin
1807 Inc(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Scores
1809 if tgcScoreCon then
1810 begin
1811 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_RED], tgcScoreCount]), True);
1812 if g_Game_IsServer and g_Game_IsNet then
1813 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '+tr');
1814 end;
1816 if tgcScoreMsg then
1817 begin
1818 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1819 if g_Game_IsServer and g_Game_IsNet then
1820 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1821 end;
1822 end;
1823 if (tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then
1824 begin
1825 Dec(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Fouls
1827 if tgcScoreCon then
1828 begin
1829 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_RED], tgcScoreCount]), True);
1830 if g_Game_IsServer and g_Game_IsNet then
1831 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '-tr');
1832 end;
1834 if tgcScoreMsg then
1835 begin
1836 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1837 if g_Game_IsServer and g_Game_IsNet then
1838 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1839 end;
1840 end;
1841 if (tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then
1842 begin
1843 Inc(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Scores
1845 if tgcScoreCon then
1846 begin
1847 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_BLUE], tgcScoreCount]), True);
1848 if g_Game_IsServer and g_Game_IsNet then
1849 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '+tb');
1850 end;
1852 if tgcScoreMsg then
1853 begin
1854 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1855 if g_Game_IsServer and g_Game_IsNet then
1856 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1857 end;
1858 end;
1859 if (tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then
1860 begin
1861 Dec(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Fouls
1863 if tgcScoreCon then
1864 begin
1865 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_BLUE], tgcScoreCount]), True);
1866 if g_Game_IsServer and g_Game_IsNet then
1867 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '-tb');
1868 end;
1870 if tgcScoreMsg then
1871 begin
1872 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1873 if g_Game_IsServer and g_Game_IsNet then
1874 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1875 end;
1876 end;
1877 Result := True;
1878 end;
1879 end;
1880 // Âûèãðûø
1881 if (tgcScoreAction = TRIGGER_SCORE_ACTION_WIN) and (gGameSettings.GoalLimit > 0) then
1882 begin
1883 // Ñâîåé èëè ÷óæîé êîìàíäû
1884 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1885 begin
1886 p := g_Player_Get(ActivateUID);
1887 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED)) // Red Wins
1888 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1889 begin
1890 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1891 begin
1892 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1894 if tgcScoreCon then
1895 begin
1896 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1897 begin
1898 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1899 if g_Game_IsServer and g_Game_IsNet then
1900 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1901 end else
1902 begin
1903 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1904 if g_Game_IsServer and g_Game_IsNet then
1905 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1906 end;
1907 end;
1909 Result := True;
1910 end;
1911 end;
1912 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE)) // Blue Wins
1913 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1914 begin
1915 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1916 begin
1917 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1919 if tgcScoreCon then
1920 begin
1921 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1922 begin
1923 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1924 if g_Game_IsServer and g_Game_IsNet then
1925 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1926 end else
1927 begin
1928 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1929 if g_Game_IsServer and g_Game_IsNet then
1930 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1931 end;
1932 end;
1934 Result := True;
1935 end;
1936 end;
1937 end;
1938 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1939 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_RED, TRIGGER_SCORE_TEAM_FORCE_BLUE] then
1940 begin
1941 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then // Red Wins
1942 begin
1943 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1944 begin
1945 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1946 Result := True;
1947 end;
1948 end;
1949 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then // Blue Wins
1950 begin
1951 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1952 begin
1953 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1954 Result := True;
1955 end;
1956 end;
1957 end;
1958 end;
1959 // Ïðîèãðûø
1960 if (tgcScoreAction = TRIGGER_SCORE_ACTION_LOOSE) and (gGameSettings.GoalLimit > 0) then
1961 begin
1962 // Ñâîåé èëè ÷óæîé êîìàíäû
1963 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1964 begin
1965 p := g_Player_Get(ActivateUID);
1966 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE)) // Red Wins
1967 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1968 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1969 begin
1970 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1972 if tgcScoreCon then
1973 if tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED then
1974 begin
1975 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1976 if g_Game_IsServer and g_Game_IsNet then
1977 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1978 end else
1979 begin
1980 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1981 if g_Game_IsServer and g_Game_IsNet then
1982 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1983 end;
1985 Result := True;
1986 end;
1987 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED)) // Blue Wins
1988 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1989 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1990 begin
1991 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1993 if tgcScoreCon then
1994 if tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED then
1995 begin
1996 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1997 if g_Game_IsServer and g_Game_IsNet then
1998 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1999 end else
2000 begin
2001 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
2002 if g_Game_IsServer and g_Game_IsNet then
2003 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
2004 end;
2006 Result := True;
2007 end;
2008 end;
2009 // Êàêîé-òî êîíêðåòíîé êîìàíäû
2010 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_BLUE, TRIGGER_SCORE_TEAM_FORCE_RED] then
2011 begin
2012 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then // Red Wins
2013 begin
2014 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
2015 begin
2016 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
2017 Result := True;
2018 end;
2019 end;
2020 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then // Blue Wins
2021 begin
2022 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
2023 begin
2024 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
2025 Result := True;
2026 end;
2027 end;
2028 end;
2029 end;
2030 if Result then begin
2031 if coolDown then
2032 TimeOut := 18
2033 else
2034 TimeOut := 0;
2035 if g_Game_IsServer and g_Game_IsNet then
2036 MH_SEND_GameStats;
2037 end;
2038 end;
2040 TRIGGER_MESSAGE:
2041 begin
2042 Result := tr_Message(tgcKind, tgcText,
2043 tgcMsgDest, tgcMsgTime,
2044 ActivateUID);
2045 TimeOut := 18;
2046 end;
2048 TRIGGER_DAMAGE, TRIGGER_HEALTH:
2049 begin
2050 Result := False;
2051 UIDType := g_GetUIDType(ActivateUID);
2052 if (UIDType = UID_PLAYER) or (UIDType = UID_MONSTER) then
2053 begin
2054 Result := True;
2055 k := -1;
2056 if coolDown then
2057 begin
2058 // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
2059 for idx := 0 to High(Activators) do
2060 if Activators[idx].UID = ActivateUID then
2061 begin
2062 k := idx;
2063 Break;
2064 end;
2065 if k = -1 then
2066 begin // Âèäèì åãî âïåðâûå
2067 // Çàïîìèíàåì åãî
2068 SetLength(Activators, Length(Activators) + 1);
2069 k := High(Activators);
2070 Activators[k].UID := ActivateUID;
2071 end else
2072 begin // Óæå âèäåëè åãî
2073 // Åñëè èíòåðâàë îòêëþ÷¸í, íî îí âñ¸ åù¸ â çîíå ïîðàæåíèÿ, äà¸ì åìó âðåìÿ
2074 if (tgcInterval = 0) and (Activators[k].TimeOut > 0) then
2075 Activators[k].TimeOut := 65535;
2076 // Òàéìàóò ïðîø¸ë - ðàáîòàåì
2077 Result := Activators[k].TimeOut = 0;
2078 end;
2079 end;
2081 if Result then
2082 begin
2083 case UIDType of
2084 UID_PLAYER:
2085 begin
2086 p := g_Player_Get(ActivateUID);
2087 if p = nil then
2088 Exit;
2090 // Íàíîñèì óðîí èãðîêó
2091 if (TriggerType = TRIGGER_DAMAGE) and (tgcAmount > 0) then
2092 begin
2093 // Êèñëîòíûé óðîí íå íàíîñèòñÿ êîãäà åñòü êîñòþì
2094 // "Âîäÿíîé" óðîí íå íàíîñèòñÿ êîãäà åñòü êèñëîðîä
2095 if not (((tgcKind = HIT_ACID) and (p.FMegaRulez[MR_SUIT] > gTime)) or
2096 ((tgcKind = HIT_WATER) and (p.Air > 0))) then
2097 p.Damage(tgcAmount, 0, 0, 0, tgcKind);
2098 if (tgcKind = HIT_FLAME) then p.CatchFire(0);
2099 end;
2101 // Ëå÷èì èãðîêà
2102 if (TriggerType = TRIGGER_HEALTH) and (tgcAmount > 0) then
2103 if p.Heal(tgcAmount, not tgcHealMax) and (not tgcSilent) then
2104 begin
2105 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', p.Obj.X, p.Obj.Y);
2106 if g_Game_IsServer and g_Game_IsNet then
2107 MH_SEND_Sound(p.Obj.X, p.Obj.Y, 'SOUND_ITEM_GETITEM');
2108 end;
2109 end;
2111 UID_MONSTER:
2112 begin
2113 m := g_Monsters_ByUID(ActivateUID);
2114 if m = nil then
2115 Exit;
2117 // Íàíîñèì óðîí ìîíñòðó
2118 if (TriggerType = TRIGGER_DAMAGE) and (tgcAmount > 0) then
2119 begin
2120 m.Damage(tgcAmount, 0, 0, 0, tgcKind);
2121 if (tgcKind = HIT_FLAME) then m.CatchFire(0);
2122 end;
2124 // Ëå÷èì ìîíñòðà
2125 if (TriggerType = TRIGGER_HEALTH) and (tgcAmount > 0) then
2126 if m.Heal(tgcAmount) and (not tgcSilent) then
2127 begin
2128 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', m.Obj.X, m.Obj.Y);
2129 if g_Game_IsServer and g_Game_IsNet then
2130 MH_SEND_Sound(m.Obj.X, m.Obj.Y, 'SOUND_ITEM_GETITEM');
2131 end;
2132 end;
2133 end;
2134 // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
2135 idx := tgcInterval;
2136 if coolDown then
2137 if idx > 0 then
2138 Activators[k].TimeOut := idx
2139 else
2140 Activators[k].TimeOut := 65535;
2141 end;
2142 end;
2143 TimeOut := 0;
2144 end;
2146 TRIGGER_SHOT:
2147 begin
2148 if ShotSightTime > 0 then
2149 Exit;
2151 // put this at the beginning so it doesn't trigger itself
2152 TimeOut := tgcWait + 1;
2154 wx := tgcTX;
2155 wy := tgcTY;
2156 pAngle := -DegToRad(tgcAngle);
2157 xd := wx + Round(Cos(pAngle) * 32.0);
2158 yd := wy + Round(Sin(pAngle) * 32.0);
2159 TargetUID := 0;
2161 case tgcShotTarget of
2162 TRIGGER_SHOT_TARGET_MON: // monsters
2163 //TODO: accelerate this!
2164 g_Mons_ForEachAlive(monsShotTarget);
2166 TRIGGER_SHOT_TARGET_PLR: // players
2167 if gPlayers <> nil then
2168 for idx := Low(gPlayers) to High(gPlayers) do
2169 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2170 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2171 begin
2172 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2173 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2174 TargetUID := gPlayers[idx].UID;
2175 break;
2176 end;
2178 TRIGGER_SHOT_TARGET_RED: // red team
2179 if gPlayers <> nil then
2180 for idx := Low(gPlayers) to High(gPlayers) do
2181 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2182 (gPlayers[idx].Team = TEAM_RED) and
2183 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2184 begin
2185 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2186 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2187 TargetUID := gPlayers[idx].UID;
2188 break;
2189 end;
2191 TRIGGER_SHOT_TARGET_BLUE: // blue team
2192 if gPlayers <> nil then
2193 for idx := Low(gPlayers) to High(gPlayers) do
2194 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2195 (gPlayers[idx].Team = TEAM_BLUE) and
2196 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2197 begin
2198 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2199 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2200 TargetUID := gPlayers[idx].UID;
2201 break;
2202 end;
2204 TRIGGER_SHOT_TARGET_MONPLR: // monsters then players
2205 begin
2206 //TODO: accelerate this!
2207 g_Mons_ForEachAlive(monsShotTargetMonPlr);
2209 if (TargetUID = 0) and (gPlayers <> nil) then
2210 for idx := Low(gPlayers) to High(gPlayers) do
2211 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2212 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2213 begin
2214 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2215 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2216 TargetUID := gPlayers[idx].UID;
2217 break;
2218 end;
2219 end;
2221 TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
2222 begin
2223 if gPlayers <> nil then
2224 for idx := Low(gPlayers) to High(gPlayers) do
2225 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2226 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2227 begin
2228 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2229 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2230 TargetUID := gPlayers[idx].UID;
2231 break;
2232 end;
2233 if TargetUID = 0 then
2234 begin
2235 //TODO: accelerate this!
2236 g_Mons_ForEachAlive(monShotTargetPlrMon);
2237 end;
2238 end;
2240 else begin
2241 if (tgcShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
2242 (tgcShotType <> TRIGGER_SHOT_REV) then
2243 TargetUID := ActivateUID;
2244 end;
2245 end;
2247 if (tgcShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
2248 ((tgcShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
2249 begin
2250 Result := True;
2251 if (tgcSight = 0) or
2252 (tgcShotTarget = TRIGGER_SHOT_TARGET_NONE) or
2253 (TargetUID = ShotSightTarget) then
2254 MakeShot(Trigger, wx, wy, xd, yd, TargetUID)
2255 else
2256 begin
2257 ShotSightTime := tgcSight;
2258 ShotSightTargetN := TargetUID;
2259 if tgcShotType = TRIGGER_SHOT_BFG then
2260 begin
2261 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', wx, wy);
2262 if g_Game_IsNet and g_Game_IsServer then
2263 MH_SEND_Sound(wx, wy, 'SOUND_WEAPON_STARTFIREBFG');
2264 end;
2265 end;
2266 end;
2267 end;
2269 TRIGGER_EFFECT:
2270 begin
2271 idx := tgcFXCount;
2273 while idx > 0 do
2274 begin
2275 case tgcFXPos of
2276 TRIGGER_EFFECT_POS_CENTER:
2277 begin
2278 wx := X + Width div 2;
2279 wy := Y + Height div 2;
2280 end;
2281 TRIGGER_EFFECT_POS_AREA:
2282 begin
2283 wx := X + Random(Width);
2284 wy := Y + Random(Height);
2285 end;
2286 else begin
2287 wx := X + Width div 2;
2288 wy := Y + Height div 2;
2289 end;
2290 end;
2291 xd := tgcVelX;
2292 yd := tgcVelY;
2293 if tgcSpreadL > 0 then xd -= Random(tgcSpreadL+1);
2294 if tgcSpreadR > 0 then xd += Random(tgcSpreadR+1);
2295 if tgcSpreadU > 0 then yd -= Random(tgcSpreadU+1);
2296 if tgcSpreadD > 0 then yd += Random(tgcSpreadD+1);
2297 tr_MakeEffect(wx, wy, xd, yd,
2298 tgcFXType, tgcFXSubType,
2299 tgcFXRed, tgcFXGreen, tgcFXBlue, True, False);
2300 Dec(idx);
2301 end;
2302 TimeOut := tgcWait;
2303 result := true;
2304 end;
2305 end;
2306 end;
2308 if Result {and (Trigger.TexturePanel <> -1)} then
2309 begin
2310 g_Map_SwitchTextureGUID({Trigger.TexturePanelType,} Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
2311 end;
2312 end;
2315 function g_Triggers_CreateWithMapIndex (aTrigger: TTrigger; arridx, mapidx: Integer): DWORD;
2316 var
2317 triggers: TDynField;
2318 begin
2319 triggers := gCurrentMap['trigger'];
2320 if (triggers = nil) then raise Exception.Create('LOAD: map has no triggers');
2321 if (mapidx < 0) or (mapidx >= triggers.count) then raise Exception.Create('LOAD: invalid map trigger index');
2322 aTrigger.mapIndex := mapidx;
2323 result := g_Triggers_Create(aTrigger, triggers.itemAt[mapidx], arridx);
2324 end;
2327 function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
2328 var
2329 find_id: DWORD;
2330 fn: AnsiString;
2331 f, olen: Integer;
2332 ptg: PTrigger;
2333 begin
2334 if (tgscope = nil) then tgscope := TTrigScope.Create();
2335 if (tgclist = nil) then tgclist := TMyConstList.Create();
2337 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà
2338 if (aTrigger.TriggerType = TRIGGER_EXIT) and
2339 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2340 begin
2341 aTrigger.TriggerType := TRIGGER_NONE;
2342 end;
2344 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð
2345 if (aTrigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2346 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2347 (gGameSettings.GameType <> GT_SINGLE) then
2348 begin
2349 aTrigger.TriggerType := TRIGGER_NONE;
2350 end;
2352 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå
2353 if (aTrigger.TriggerType = TRIGGER_SECRET) then gSecretsCount += 1;
2355 if (forceInternalIndex < 0) then
2356 begin
2357 find_id := FindTrigger();
2358 end
2359 else
2360 begin
2361 olen := Length(gTriggers);
2362 if (forceInternalIndex >= olen) then
2363 begin
2364 SetLength(gTriggers, forceInternalIndex+1);
2365 for f := olen to High(gTriggers) do
2366 begin
2367 gTriggers[f].TriggerType := TRIGGER_NONE;
2368 gTriggers[f].trigDataRec := nil;
2369 gTriggers[f].exoInit := nil;
2370 gTriggers[f].exoThink := nil;
2371 gTriggers[f].exoCheck := nil;
2372 gTriggers[f].exoAction := nil;
2373 gTriggers[f].userVars := nil;
2374 end;
2375 end;
2376 f := forceInternalIndex;
2377 gTriggers[f].trigDataRec.Free();
2378 gTriggers[f].exoInit.Free();
2379 gTriggers[f].exoThink.Free();
2380 gTriggers[f].exoCheck.Free();
2381 gTriggers[f].exoAction.Free();
2382 gTriggers[f].userVars.Free();
2383 gTriggers[f].trigDataRec := nil;
2384 gTriggers[f].exoInit := nil;
2385 gTriggers[f].exoThink := nil;
2386 gTriggers[f].exoCheck := nil;
2387 gTriggers[f].exoAction := nil;
2388 gTriggers[f].userVars := nil;
2389 find_id := DWORD(forceInternalIndex);
2390 end;
2391 gTriggers[find_id] := aTrigger;
2392 ptg := @gTriggers[find_id];
2394 ptg.mapId := trec.id;
2395 // clone trigger data
2396 if (trec.trigRec = nil) then
2397 begin
2398 ptg.trigDataRec := nil;
2399 //HACK!
2400 if (ptg.TriggerType <> TRIGGER_SECRET) then
2401 begin
2402 e_LogWritefln('trigger of type %s has no triggerdata; wtf?!', [ptg.TriggerType], TMsgType.Warning);
2403 end;
2404 end
2405 else
2406 begin
2407 ptg.trigDataRec := trec.trigRec.clone(nil);
2408 end;
2410 with ptg^ do
2411 begin
2412 ID := find_id;
2413 // if this type of trigger exists both on the client and on the server
2414 // use an uniform numeration
2415 ClientID := 0;
2416 if (ptg.TriggerType = TRIGGER_SOUND) then
2417 begin
2418 Inc(gTriggerClientID);
2419 ClientID := gTriggerClientID;
2420 end;
2421 TimeOut := 0;
2422 ActivateUID := 0;
2423 PlayerCollide := False;
2424 DoorTime := -1;
2425 PressTime := -1;
2426 PressCount := 0;
2427 SoundPlayCount := 0;
2428 Sound := nil;
2429 AutoSpawn := False;
2430 SpawnCooldown := 0;
2431 SpawnedCount := 0;
2432 end;
2434 // update cached trigger variables
2435 trigUpdateCacheData(ptg^, ptg.trigDataRec);
2437 ptg.userVars := nil;
2439 try
2440 ptg.exoThink := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_think']));
2441 except
2442 on e: TExomaParseException do
2443 begin
2444 conwritefln('*** ERROR parsing exoma_think (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_think'])]);
2445 ptg.exoThink := nil;
2446 end;
2447 else
2448 raise;
2449 end;
2450 try
2451 ptg.exoCheck := TExprBase.parse(tgclist, VarToStr(trec.user['exoma_check']));
2452 except
2453 on e: TExomaParseException do
2454 begin
2455 conwritefln('*** ERROR parsing exoma_check (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_check'])]);
2456 ptg.exoCheck := nil;
2457 end;
2458 else
2459 raise;
2460 end;
2461 try
2462 ptg.exoAction := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_action']));
2463 except
2464 on e: TExomaParseException do
2465 begin
2466 conwritefln('*** ERROR parsing exoma_action (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_action'])]);
2467 ptg.exoAction := nil;
2468 end;
2469 else
2470 raise;
2471 end;
2472 try
2473 ptg.exoInit := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_init']));
2474 except
2475 on e: TExomaParseException do
2476 begin
2477 conwritefln('*** ERROR parsing exoma_init (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_init'])]);
2478 ptg.exoInit := nil;
2479 end;
2480 else
2481 raise;
2482 end;
2484 if (forceInternalIndex < 0) and (ptg.exoInit <> nil) then
2485 begin
2486 //conwritefln('executing trigger init: [%s]', [gTriggers[find_id].exoInit.toString()]);
2487 try
2488 tgscope.me := ptg;
2489 ptg.exoInit.value(tgscope);
2490 tgscope.me := nil;
2491 except
2492 tgscope.me := nil;
2493 conwritefln('*** trigger exoactivate error: %s', [ptg.exoInit.toString()]);
2494 exit;
2495 end;
2496 end;
2498 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê"
2499 if (ptg.TriggerType = TRIGGER_SOUND) and (ptg.tgcSoundName <> '') then
2500 begin
2501 // Åùå íåò òàêîãî çâóêà
2502 if not g_Sound_Exists(ptg.tgcSoundName) then
2503 begin
2504 fn := e_GetResourcePath(WadDirs, ptg.tgcSoundName, g_ExtractWadName(gMapInfo.Map));
2505 //e_LogWritefln('loading trigger sound ''%s''', [fn]);
2506 if not g_Sound_CreateWADEx(ptg.tgcSoundName, fn) then
2507 begin
2508 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcSoundName]));
2509 end;
2510 end;
2512 // Ñîçäàåì îáúåêò çâóêà
2513 with ptg^ do
2514 begin
2515 Sound := TPlayableSound.Create();
2516 if not Sound.SetByName(ptg.tgcSoundName) then
2517 begin
2518 Sound.Free();
2519 Sound := nil;
2520 end;
2521 end;
2522 end;
2524 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà"
2525 if (ptg.TriggerType = TRIGGER_MUSIC) and (ptg.tgcMusicName <> '') then
2526 begin
2527 // Åùå íåò òàêîé ìóçûêè
2528 if not g_Sound_Exists(ptg.tgcMusicName) then
2529 begin
2530 fn := e_GetResourcePath(WadDirs, ptg.tgcMusicName, g_ExtractWadName(gMapInfo.Map));
2531 if not g_Sound_CreateWADEx(ptg.tgcMusicName, fn, True) then
2532 begin
2533 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcMusicName]));
2534 end;
2535 end;
2536 end;
2538 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü"
2539 if (ptg.TriggerType = TRIGGER_SHOT) then
2540 begin
2541 with ptg^ do
2542 begin
2543 ShotPanelTime := 0;
2544 ShotSightTime := 0;
2545 ShotSightTimeout := 0;
2546 ShotSightTarget := 0;
2547 ShotSightTargetN := 0;
2548 ShotAmmoCount := ptg.tgcAmmo;
2549 ShotReloadTime := 0;
2550 end;
2551 end;
2553 Result := find_id;
2554 end;
2557 // sorry; grid doesn't support recursive queries, so we have to do this
2558 type
2559 TSimpleMonsterList = specialize TSimpleList<TMonster>;
2561 var
2562 tgMonsList: TSimpleMonsterList = nil;
2564 procedure g_Triggers_Update();
2565 var
2566 a, b, i: Integer;
2567 Affected: array of Integer;
2569 function monsNear (mon: TMonster): Boolean;
2570 begin
2571 result := false; // don't stop
2573 gTriggers[a].ActivateUID := mon.UID;
2574 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2576 tgMonsList.append(mon);
2577 end;
2579 var
2580 mon: TMonster;
2581 pan: TPanel;
2582 begin
2583 if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
2585 if gTriggers = nil then Exit;
2586 if gLMSRespawn > LMS_RESPAWN_NONE then Exit; // don't update triggers at all
2588 SetLength(Affected, 0);
2590 for a := 0 to High(gTriggers) do
2591 with gTriggers[a] do
2592 // Åñòü òðèããåð:
2593 if TriggerType <> TRIGGER_NONE then
2594 begin
2595 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè)
2596 if DoorTime > 0 then DoorTime := DoorTime - 1;
2597 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ
2598 if PressTime > 0 then PressTime := PressTime - 1;
2599 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2600 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2601 begin
2602 for b := 0 to High(Activators) do
2603 begin
2604 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2605 if Activators[b].TimeOut > 0 then
2606 begin
2607 Dec(Activators[b].TimeOut);
2608 end
2609 else
2610 begin
2611 continue;
2612 end;
2613 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2614 if (tgcInterval = 0) and (Activators[b].TimeOut < 65530) then Activators[b].TimeOut := 0;
2615 end;
2616 end;
2618 // Îáðàáàòûâàåì ñïàâíåðû
2619 if Enabled and AutoSpawn then
2620 begin
2621 if SpawnCooldown = 0 then
2622 begin
2623 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà
2624 if (TriggerType = TRIGGER_SPAWNMONSTER) and (tgcDelay > 0) then
2625 begin
2626 ActivateUID := 0;
2627 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2628 end;
2629 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò
2630 if (TriggerType = TRIGGER_SPAWNITEM) and (tgcDelay > 0) then
2631 begin
2632 ActivateUID := 0;
2633 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2634 end;
2635 end
2636 else
2637 begin
2638 // Óìåíüøàåì âðåìÿ îæèäàíèÿ
2639 Dec(SpawnCooldown);
2640 end;
2641 end;
2643 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü"
2644 if TriggerType = TRIGGER_SHOT then
2645 begin
2646 if ShotPanelTime > 0 then
2647 begin
2648 Dec(ShotPanelTime);
2649 if ShotPanelTime = 0 then g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
2650 end;
2651 if ShotSightTime > 0 then
2652 begin
2653 Dec(ShotSightTime);
2654 if ShotSightTime = 0 then ShotSightTarget := ShotSightTargetN;
2655 end;
2656 if ShotSightTimeout > 0 then
2657 begin
2658 Dec(ShotSightTimeout);
2659 if ShotSightTimeout = 0 then ShotSightTarget := 0;
2660 end;
2661 if ShotReloadTime > 0 then
2662 begin
2663 Dec(ShotReloadTime);
2664 if ShotReloadTime = 0 then ShotAmmoCount := tgcAmmo;
2665 end;
2666 end;
2668 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì
2669 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2670 begin
2671 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2672 begin
2673 if tgcPlayCount > 0 then Dec(SoundPlayCount); (* looped sound if zero *)
2674 if tgcLocal then
2675 Sound.PlayVolumeAtRect(X, Y, Width, Height, tgcVolume / 255.0)
2676 else
2677 Sound.PlayPanVolume((tgcPan - 127.0) / 128.0, tgcVolume / 255.0);
2678 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then
2679 MH_SEND_TriggerSound(gTriggers[a])
2680 end
2681 end;
2683 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü
2684 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2685 begin
2686 tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
2687 DoorTime := -1;
2688 end;
2690 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü
2691 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2692 begin
2693 pan := g_Map_PanelByGUID(trigPanelGUID);
2694 if (pan <> nil) and pan.isGWall then
2695 begin
2696 // Óæå çàêðûòà
2697 if {gWalls[trigPanelID].Enabled} pan.Enabled then
2698 begin
2699 DoorTime := -1;
2700 end
2701 else
2702 begin
2703 // Ïîêà îòêðûòà - çàêðûâàåì
2704 if tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d) then DoorTime := -1;
2705 end;
2706 end;
2707 end;
2709 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2710 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2711 (PressTime = 0) and (PressCount >= tgcPressCount) then
2712 begin
2713 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2714 PressTime := -1;
2715 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2716 if tgcPressCount > 0 then PressCount -= tgcPressCount else PressCount := 0;
2718 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2719 for b := 0 to High(gTriggers) do
2720 begin
2721 if g_Collide(tgcTX, tgcTY, tgcTWidth, tgcTHeight, gTriggers[b].X, gTriggers[b].Y,
2722 gTriggers[b].Width, gTriggers[b].Height) and
2723 ((b <> a) or (tgcWait > 0)) then
2724 begin // Can be self-activated, if there is Data.Wait
2725 if (not tgcExtRandom) or gTriggers[b].Enabled then
2726 begin
2727 SetLength(Affected, Length(Affected) + 1);
2728 Affected[High(Affected)] := b;
2729 end;
2730 end;
2731 end;
2733 //HACK!
2734 // if we have panelid, assume that it will switch the moving platform
2735 pan := g_Map_PanelByGUID(trigPanelGUID);
2736 if (pan <> nil) then
2737 begin
2738 case TriggerType of
2739 TRIGGER_PRESS: pan.movingActive := true; // what to do here?
2740 TRIGGER_ON: pan.movingActive := true;
2741 TRIGGER_OFF: pan.movingActive := false;
2742 TRIGGER_ONOFF: pan.movingActive := not pan.movingActive;
2743 end;
2744 if not tgcSilent and (Length(tgcSound) > 0) then
2745 begin
2746 g_Sound_PlayExAt(tgcSound, X, Y);
2747 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, tgcSound);
2748 end;
2749 end;
2751 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2752 if (TriggerType = TRIGGER_PRESS) and tgcExtRandom then
2753 begin
2754 if (Length(Affected) > 0) then
2755 begin
2756 b := Affected[Random(Length(Affected))];
2757 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2758 ActivateTrigger(gTriggers[b], 0);
2759 end;
2760 end
2761 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2762 begin
2763 for i := 0 to High(Affected) do
2764 begin
2765 b := Affected[i];
2766 case TriggerType of
2767 TRIGGER_PRESS:
2768 begin
2769 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2770 ActivateTrigger(gTriggers[b], 0);
2771 end;
2772 TRIGGER_ON:
2773 begin
2774 gTriggers[b].Enabled := True;
2775 end;
2776 TRIGGER_OFF:
2777 begin
2778 gTriggers[b].Enabled := False;
2779 gTriggers[b].TimeOut := 0;
2780 if gTriggers[b].AutoSpawn then
2781 begin
2782 gTriggers[b].AutoSpawn := False;
2783 gTriggers[b].SpawnCooldown := 0;
2784 end;
2785 end;
2786 TRIGGER_ONOFF:
2787 begin
2788 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2789 if not gTriggers[b].Enabled then
2790 begin
2791 gTriggers[b].TimeOut := 0;
2792 if gTriggers[b].AutoSpawn then
2793 begin
2794 gTriggers[b].AutoSpawn := False;
2795 gTriggers[b].SpawnCooldown := 0;
2796 end;
2797 end;
2798 end;
2799 end;
2800 end;
2801 end;
2802 SetLength(Affected, 0);
2803 end;
2805 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2806 if TimeOut > 0 then
2807 begin
2808 TimeOut := TimeOut - 1;
2809 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2810 end;
2812 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2813 if not Enabled then
2814 Continue;
2816 // "Èãðîê áëèçêî":
2817 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2818 (TimeOut = 0) then
2819 if gPlayers <> nil then
2820 for b := 0 to High(gPlayers) do
2821 if gPlayers[b] <> nil then
2822 with gPlayers[b] do
2823 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2824 if alive and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2825 Collide(X, Y, Width, Height) then
2826 begin
2827 gTriggers[a].ActivateUID := UID;
2829 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2830 PlayerCollide then
2831 { Don't activate sound/music again if player is here }
2832 else
2833 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2834 end;
2836 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2838 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2839 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2840 (TimeOut = 0) and (Keys = 0) then
2841 begin
2842 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2843 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2844 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2845 gTriggers[a].ActivateUID := 0;
2846 ActivateTrigger(gTriggers[a], 0);
2847 end else
2848 begin
2849 // "Ìîíñòð áëèçêî"
2850 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2851 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2852 begin
2853 //g_Mons_ForEach(monsNear);
2854 //Alive?!
2855 tgMonsList.reset();
2856 g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
2857 for mon in tgMonsList do
2858 begin
2859 gTriggers[a].ActivateUID := mon.UID;
2860 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2861 end;
2862 tgMonsList.reset(); // just in case
2863 end;
2865 // "Ìîíñòðîâ íåò"
2866 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2867 (TimeOut = 0) and (Keys = 0) then
2868 if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
2869 begin
2870 gTriggers[a].ActivateUID := 0;
2871 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2872 end;
2873 end;
2875 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2876 end;
2877 end;
2879 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2880 begin
2881 if (ID >= Length(gTriggers)) then exit;
2882 gTriggers[ID].ActivateUID := ActivateUID;
2883 ActivateTrigger(gTriggers[ID], ActivateType);
2884 end;
2886 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2887 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2888 var
2889 a: Integer;
2890 k: Byte;
2891 p: TPlayer;
2892 begin
2893 Result := nil;
2895 if gTriggers = nil then Exit;
2897 case g_GetUIDType(UID) of
2898 UID_GAME: k := 255;
2899 UID_PLAYER:
2900 begin
2901 p := g_Player_Get(UID);
2902 if p <> nil then
2903 k := p.GetKeys
2904 else
2905 k := 0;
2906 end;
2907 else k := 0;
2908 end;
2910 for a := 0 to High(gTriggers) do
2911 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2912 (gTriggers[a].TimeOut = 0) and
2913 (not InDWArray(a, IgnoreList)) and
2914 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2915 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2916 if g_Collide(X, Y, Width, Height,
2917 gTriggers[a].X, gTriggers[a].Y,
2918 gTriggers[a].Width, gTriggers[a].Height) then
2919 begin
2920 gTriggers[a].ActivateUID := UID;
2921 if ActivateTrigger(gTriggers[a], ActivateType) then
2922 begin
2923 SetLength(Result, Length(Result)+1);
2924 Result[High(Result)] := a;
2925 end;
2926 end;
2927 end;
2929 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2930 var
2931 a: Integer;
2932 k: Byte;
2933 p: TPlayer;
2934 begin
2935 if gTriggers = nil then Exit;
2937 case g_GetUIDType(UID) of
2938 UID_GAME: k := 255;
2939 UID_PLAYER:
2940 begin
2941 p := g_Player_Get(UID);
2942 if p <> nil then
2943 k := p.GetKeys
2944 else
2945 k := 0;
2946 end;
2947 else k := 0;
2948 end;
2950 for a := 0 to High(gTriggers) do
2951 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2952 (gTriggers[a].TimeOut = 0) and
2953 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2954 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2955 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2956 gTriggers[a].Width, gTriggers[a].Height) then
2957 begin
2958 gTriggers[a].ActivateUID := UID;
2959 ActivateTrigger(gTriggers[a], ActivateType);
2960 end;
2961 end;
2963 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2964 var
2965 a: Integer;
2966 k: Byte;
2967 rsq: Word;
2968 p: TPlayer;
2969 begin
2970 if gTriggers = nil then
2971 Exit;
2973 case g_GetUIDType(UID) of
2974 UID_GAME: k := 255;
2975 UID_PLAYER:
2976 begin
2977 p := g_Player_Get(UID);
2978 if p <> nil then
2979 k := p.GetKeys
2980 else
2981 k := 0;
2982 end;
2983 else k := 0;
2984 end;
2986 rsq := Radius * Radius;
2988 for a := 0 to High(gTriggers) do
2989 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
2990 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2991 (gTriggers[a].TimeOut = 0) and
2992 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2993 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2994 with gTriggers[a] do
2995 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
2996 X, Y, Width, Height) then
2997 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
2998 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
2999 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
3000 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
3001 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
3002 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
3003 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
3004 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
3005 begin
3006 ActivateUID := UID;
3007 ActivateTrigger(gTriggers[a], ActivateType);
3008 end;
3009 end;
3011 procedure g_Triggers_OpenAll();
3012 var
3013 a: Integer;
3014 b: Boolean;
3015 begin
3016 if gTriggers = nil then Exit;
3018 b := False;
3019 for a := 0 to High(gTriggers) do
3020 begin
3021 with gTriggers[a] do
3022 begin
3023 if (TriggerType = TRIGGER_OPENDOOR) or
3024 (TriggerType = TRIGGER_DOOR5) or
3025 (TriggerType = TRIGGER_DOOR) then
3026 begin
3027 tr_OpenDoor(trigPanelGUID, True, tgcD2d);
3028 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
3029 b := True;
3030 end;
3031 end;
3032 end;
3034 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
3035 end;
3037 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
3038 begin
3039 if (gTriggers <> nil) then
3040 begin
3041 if gTriggers[ID].tgcMax > 0 then
3042 begin
3043 if gTriggers[ID].SpawnedCount > 0 then
3044 Dec(gTriggers[ID].SpawnedCount);
3045 end;
3046 if gTriggers[ID].tgcDelay > 0 then
3047 begin
3048 if gTriggers[ID].SpawnCooldown < 0 then
3049 gTriggers[ID].SpawnCooldown := gTriggers[ID].tgcDelay;
3050 end;
3051 end;
3052 end;
3054 procedure g_Triggers_Free ();
3055 var
3056 a: Integer;
3057 begin
3058 for a := 0 to High(gTriggers) do
3059 begin
3060 if (gTriggers[a].TriggerType = TRIGGER_SOUND) then
3061 begin
3062 if g_Sound_Exists(gTriggers[a].tgcSoundName) then
3063 begin
3064 g_Sound_Delete(gTriggers[a].tgcSoundName);
3065 end;
3066 gTriggers[a].Sound.Free();
3067 end;
3068 if (gTriggers[a].Activators <> nil) then
3069 begin
3070 SetLength(gTriggers[a].Activators, 0);
3071 end;
3072 gTriggers[a].trigDataRec.Free();
3074 gTriggers[a].exoThink.Free();
3075 gTriggers[a].exoCheck.Free();
3076 gTriggers[a].exoAction.Free();
3077 gTriggers[a].userVars.Free();
3078 end;
3080 gTriggers := nil;
3081 gSecretsCount := 0;
3082 SetLength(gMonstersSpawned, 0);
3083 end;
3086 procedure g_Triggers_SaveState (st: TStream);
3087 var
3088 count, actCount, i, j: Integer;
3089 sg: Single;
3090 b: Boolean;
3091 kv: THashStrVariant.PEntry;
3092 t: LongInt;
3093 begin
3094 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
3095 count := Length(gTriggers);
3097 // Êîëè÷åñòâî òðèããåðîâ
3098 utils.writeInt(st, LongInt(count));
3099 if (count = 0) then exit;
3101 for i := 0 to High(gTriggers) do
3102 begin
3103 // Ñèãíàòóðà òðèããåðà
3104 utils.writeSign(st, 'TRGX');
3105 utils.writeInt(st, Byte(0));
3106 // Òèï òðèããåðà
3107 utils.writeInt(st, Byte(gTriggers[i].TriggerType));
3108 if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
3109 // Ñïåöèàëüíûå äàííûå òðèããåðà: ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
3110 utils.writeInt(st, LongInt(gTriggers[i].mapIndex));
3111 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
3112 utils.writeInt(st, LongInt(gTriggers[i].X));
3113 utils.writeInt(st, LongInt(gTriggers[i].Y));
3114 // Ðàçìåðû
3115 utils.writeInt(st, Word(gTriggers[i].Width));
3116 utils.writeInt(st, Word(gTriggers[i].Height));
3117 // Âêëþ÷åí ëè òðèããåð
3118 utils.writeBool(st, gTriggers[i].Enabled);
3119 // Òèï àêòèâàöèè òðèããåðà
3120 utils.writeInt(st, Byte(gTriggers[i].ActivateType));
3121 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
3122 utils.writeInt(st, Byte(gTriggers[i].Keys));
3123 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
3124 utils.writeInt(st, LongInt(gTriggers[i].TexturePanelGUID));
3125 // Òèï ýòîé ïàíåëè
3126 //Mem.WriteWord(gTriggers[i].TexturePanelType);
3127 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
3128 utils.writeInt(st, LongInt(gTriggers[i].trigPanelGUID));
3129 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
3130 utils.writeInt(st, Word(gTriggers[i].TimeOut));
3131 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
3132 utils.writeInt(st, Word(gTriggers[i].ActivateUID));
3133 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
3134 actCount := Length(gTriggers[i].Activators);
3135 utils.writeInt(st, LongInt(actCount));
3136 for j := 0 to actCount-1 do
3137 begin
3138 // UID îáúåêòà
3139 utils.writeInt(st, Word(gTriggers[i].Activators[j].UID));
3140 // Âðåìÿ îæèäàíèÿ
3141 utils.writeInt(st, Word(gTriggers[i].Activators[j].TimeOut));
3142 end;
3143 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
3144 utils.writeBool(st, gTriggers[i].PlayerCollide);
3145 // Âðåìÿ äî çàêðûòèÿ äâåðè
3146 utils.writeInt(st, LongInt(gTriggers[i].DoorTime));
3147 // Çàäåðæêà àêòèâàöèè
3148 utils.writeInt(st, LongInt(gTriggers[i].PressTime));
3149 // Ñ÷åò÷èê íàæàòèé
3150 utils.writeInt(st, LongInt(gTriggers[i].PressCount));
3151 // Ñïàâíåð àêòèâåí
3152 utils.writeBool(st, gTriggers[i].AutoSpawn);
3153 // Çàäåðæêà ñïàâíåðà
3154 utils.writeInt(st, LongInt(gTriggers[i].SpawnCooldown));
3155 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
3156 utils.writeInt(st, LongInt(gTriggers[i].SpawnedCount));
3157 // Ñêîëüêî ðàç ïðîèãðàí çâóê
3158 utils.writeInt(st, LongInt(gTriggers[i].SoundPlayCount));
3159 // Ïðîèãðûâàåòñÿ ëè çâóê?
3160 if (gTriggers[i].Sound <> nil) then b := gTriggers[i].Sound.IsPlaying() else b := false;
3161 utils.writeBool(st, b);
3162 if b then
3163 begin
3164 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
3165 utils.writeInt(st, LongWord(gTriggers[i].Sound.GetPosition()));
3166 // Ãðîìêîñòü çâóêà
3167 sg := gTriggers[i].Sound.GetVolume();
3168 sg := sg/(gSoundLevel/255.0);
3169 //Mem.WriteSingle(sg);
3170 st.WriteBuffer(sg, sizeof(sg)); // sorry
3171 // Ñòåðåî ñìåùåíèå çâóêà
3172 sg := gTriggers[i].Sound.GetPan();
3173 //Mem.WriteSingle(sg);
3174 st.WriteBuffer(sg, sizeof(sg)); // sorry
3175 end;
3176 // uservars
3177 if (gTriggers[i].userVars = nil) then
3178 begin
3179 utils.writeInt(st, LongInt(0));
3180 end
3181 else
3182 begin
3183 utils.writeInt(st, LongInt(gTriggers[i].userVars.count)); //FIXME: check for overflow
3184 for kv in gTriggers[i].userVars.byKeyValue do
3185 begin
3186 //writeln('<', kv.key, '>:<', VarToStr(kv.value), '>');
3187 utils.writeStr(st, kv.key);
3188 t := LongInt(varType(kv.value));
3189 utils.writeInt(st, LongInt(t));
3190 case t of
3191 varString: utils.writeStr(st, AnsiString(kv.value));
3192 varBoolean: utils.writeBool(st, Boolean(kv.value));
3193 varShortInt: utils.writeInt(st, LongInt(kv.value));
3194 varSmallint: utils.writeInt(st, LongInt(kv.value));
3195 varInteger: utils.writeInt(st, LongInt(kv.value));
3196 //varInt64: Mem.WriteInt(Integer(kv.value));
3197 varByte: utils.writeInt(st, LongInt(kv.value));
3198 varWord: utils.writeInt(st, LongInt(kv.value));
3199 varLongWord: utils.writeInt(st, LongInt(kv.value));
3200 //varQWord:
3201 else raise Exception.CreateFmt('cannot save uservar ''%s''', [kv.key]);
3202 end;
3203 end;
3204 end;
3205 end;
3206 end;
3209 procedure g_Triggers_LoadState (st: TStream);
3210 var
3211 count, actCount, i, j, a: Integer;
3212 dw: DWORD;
3213 vol, pan: Single;
3214 b: Boolean;
3215 Trig: TTrigger;
3216 mapIndex: Integer;
3217 uvcount: Integer;
3218 vt: LongInt;
3219 vv: Variant;
3220 uvname: AnsiString = '';
3221 ustr: AnsiString = '';
3222 uint: LongInt;
3223 ubool: Boolean;
3224 begin
3225 assert(st <> nil);
3227 g_Triggers_Free();
3229 // Êîëè÷åñòâî òðèããåðîâ
3230 count := utils.readLongInt(st);
3231 if (count = 0) then exit;
3232 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid trigger count');
3234 for a := 0 to count-1 do
3235 begin
3236 // Ñèãíàòóðà òðèããåðà
3237 if not utils.checkSign(st, 'TRGX') then raise XStreamError.Create('invalid trigger signature');
3238 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid trigger version');
3239 // Òèï òðèããåðà
3240 Trig.TriggerType := utils.readByte(st);
3241 if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
3242 // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
3243 mapIndex := utils.readLongInt(st);
3244 i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
3245 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
3246 gTriggers[i].X := utils.readLongInt(st);
3247 gTriggers[i].Y := utils.readLongInt(st);
3248 // Ðàçìåðû
3249 gTriggers[i].Width := utils.readWord(st);
3250 gTriggers[i].Height := utils.readWord(st);
3251 // Âêëþ÷åí ëè òðèããåð
3252 gTriggers[i].Enabled := utils.readBool(st);
3253 // Òèï àêòèâàöèè òðèããåðà
3254 gTriggers[i].ActivateType := utils.readByte(st);
3255 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
3256 gTriggers[i].Keys := utils.readByte(st);
3257 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
3258 gTriggers[i].TexturePanelGUID := utils.readLongInt(st);
3259 // Òèï ýòîé ïàíåëè
3260 //Mem.ReadWord(gTriggers[i].TexturePanelType);
3261 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
3262 gTriggers[i].trigPanelGUID := utils.readLongInt(st);
3263 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
3264 gTriggers[i].TimeOut := utils.readWord(st);
3265 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
3266 gTriggers[i].ActivateUID := utils.readWord(st);
3267 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
3268 actCount := utils.readLongInt(st);
3269 if (actCount < 0) or (actCount > 1024*1024) then raise XStreamError.Create('invalid activated object count');
3270 if (actCount > 0) then
3271 begin
3272 SetLength(gTriggers[i].Activators, actCount);
3273 for j := 0 to actCount-1 do
3274 begin
3275 // UID îáúåêòà
3276 gTriggers[i].Activators[j].UID := utils.readWord(st);
3277 // Âðåìÿ îæèäàíèÿ
3278 gTriggers[i].Activators[j].TimeOut := utils.readWord(st);
3279 end;
3280 end;
3281 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
3282 gTriggers[i].PlayerCollide := utils.readBool(st);
3283 // Âðåìÿ äî çàêðûòèÿ äâåðè
3284 gTriggers[i].DoorTime := utils.readLongInt(st);
3285 // Çàäåðæêà àêòèâàöèè
3286 gTriggers[i].PressTime := utils.readLongInt(st);
3287 // Ñ÷åò÷èê íàæàòèé
3288 gTriggers[i].PressCount := utils.readLongInt(st);
3289 // Ñïàâíåð àêòèâåí
3290 gTriggers[i].AutoSpawn := utils.readBool(st);
3291 // Çàäåðæêà ñïàâíåðà
3292 gTriggers[i].SpawnCooldown := utils.readLongInt(st);
3293 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
3294 gTriggers[i].SpawnedCount := utils.readLongInt(st);
3295 // Ñêîëüêî ðàç ïðîèãðàí çâóê
3296 gTriggers[i].SoundPlayCount := utils.readLongInt(st);
3297 // Ïðîèãðûâàåòñÿ ëè çâóê?
3298 b := utils.readBool(st);
3299 if b then
3300 begin
3301 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
3302 dw := utils.readLongWord(st);
3303 // Ãðîìêîñòü çâóêà
3304 //Mem.ReadSingle(vol);
3305 st.ReadBuffer(vol, sizeof(vol)); // sorry
3306 // Ñòåðåî ñìåùåíèå çâóêà
3307 //Mem.ReadSingle(pan);
3308 st.ReadBuffer(pan, sizeof(pan)); // sorry
3309 // Çàïóñêàåì çâóê, åñëè åñòü
3310 if (gTriggers[i].Sound <> nil) then
3311 begin
3312 gTriggers[i].Sound.PlayPanVolume(pan, vol);
3313 gTriggers[i].Sound.Pause(True);
3314 gTriggers[i].Sound.SetPosition(dw);
3315 end
3316 end;
3317 // uservars
3318 gTriggers[i].userVars.Free();
3319 gTriggers[i].userVars := nil;
3320 uvcount := utils.readLongInt(st);
3321 if (uvcount < 0) or (uvcount > 1024*1024) then raise XStreamError.Create('invalid number of user vars in trigger');
3322 if (uvcount > 0) then
3323 begin
3324 gTriggers[i].userVars := THashStrVariant.Create();
3325 vv := Unassigned;
3326 while (uvcount > 0) do
3327 begin
3328 Dec(uvcount);
3329 uvname := utils.readStr(st);
3330 vt := utils.readLongInt(st);
3331 case vt of
3332 varString: begin ustr := utils.readStr(st); vv := ustr; end;
3333 varBoolean: begin ubool := utils.readBool(st); vv := ubool; end;
3334 varShortInt: begin uint := utils.readLongInt(st); vv := ShortInt(uint); end;
3335 varSmallint: begin uint := utils.readLongInt(st); vv := SmallInt(uint); end;
3336 varInteger: begin uint := utils.readLongInt(st); vv := LongInt(uint); end;
3337 varByte: begin uint := utils.readLongInt(st); vv := Byte(uint); end;
3338 varWord: begin uint := utils.readLongInt(st); vv := Word(uint); end;
3339 varLongWord: begin uint := utils.readLongInt(st); vv := LongWord(uint); end;
3340 else raise Exception.CreateFmt('cannot load uservar ''%s''', [uvname]);
3341 end;
3342 gTriggers[i].userVars.put(uvname, vv);
3343 end;
3344 end;
3345 end;
3346 end;
3349 end.