DEADSOFTWARE

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