DEADSOFTWARE

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