DEADSOFTWARE

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