DEADSOFTWARE

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