DEADSOFTWARE

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