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