DEADSOFTWARE

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