DEADSOFTWARE

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