DEADSOFTWARE

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