DEADSOFTWARE

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