DEADSOFTWARE

texture switching on "effect" trigger should work now
[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 result := true;
2308 end;
2309 end;
2310 end;
2312 if Result {and (Trigger.TexturePanel <> -1)} then
2313 begin
2314 g_Map_SwitchTextureGUID({Trigger.TexturePanelType,} Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
2315 end;
2316 end;
2319 function g_Triggers_CreateWithMapIndex (aTrigger: TTrigger; arridx, mapidx: Integer): DWORD;
2320 var
2321 triggers: TDynField;
2322 begin
2323 triggers := gCurrentMap['trigger'];
2324 if (triggers = nil) then raise Exception.Create('LOAD: map has no triggers');
2325 if (mapidx < 0) or (mapidx >= triggers.count) then raise Exception.Create('LOAD: invalid map trigger index');
2326 aTrigger.mapIndex := mapidx;
2327 result := g_Triggers_Create(aTrigger, triggers.itemAt[mapidx], arridx);
2328 end;
2331 function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
2332 var
2333 find_id: DWORD;
2334 fn, mapw: AnsiString;
2335 f, olen: Integer;
2336 ptg: PTrigger;
2337 begin
2338 if (tgscope = nil) then tgscope := TTrigScope.Create();
2339 if (tgclist = nil) then tgclist := TMyConstList.Create();
2341 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà
2342 if (aTrigger.TriggerType = TRIGGER_EXIT) and
2343 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2344 begin
2345 aTrigger.TriggerType := TRIGGER_NONE;
2346 end;
2348 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð
2349 if (aTrigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2350 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2351 (gGameSettings.GameType <> GT_SINGLE) then
2352 begin
2353 aTrigger.TriggerType := TRIGGER_NONE;
2354 end;
2356 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå
2357 if (aTrigger.TriggerType = TRIGGER_SECRET) then gSecretsCount += 1;
2359 if (forceInternalIndex < 0) then
2360 begin
2361 find_id := FindTrigger();
2362 end
2363 else
2364 begin
2365 olen := Length(gTriggers);
2366 if (forceInternalIndex >= olen) then
2367 begin
2368 SetLength(gTriggers, forceInternalIndex+1);
2369 for f := olen to High(gTriggers) do
2370 begin
2371 gTriggers[f].TriggerType := TRIGGER_NONE;
2372 gTriggers[f].trigDataRec := nil;
2373 gTriggers[f].exoInit := nil;
2374 gTriggers[f].exoThink := nil;
2375 gTriggers[f].exoCheck := nil;
2376 gTriggers[f].exoAction := nil;
2377 gTriggers[f].userVars := nil;
2378 end;
2379 end;
2380 f := forceInternalIndex;
2381 gTriggers[f].trigDataRec.Free();
2382 gTriggers[f].exoInit.Free();
2383 gTriggers[f].exoThink.Free();
2384 gTriggers[f].exoCheck.Free();
2385 gTriggers[f].exoAction.Free();
2386 gTriggers[f].userVars.Free();
2387 gTriggers[f].trigDataRec := nil;
2388 gTriggers[f].exoInit := nil;
2389 gTriggers[f].exoThink := nil;
2390 gTriggers[f].exoCheck := nil;
2391 gTriggers[f].exoAction := nil;
2392 gTriggers[f].userVars := nil;
2393 find_id := DWORD(forceInternalIndex);
2394 end;
2395 gTriggers[find_id] := aTrigger;
2396 ptg := @gTriggers[find_id];
2398 ptg.mapId := trec.id;
2399 // clone trigger data
2400 if (trec.trigRec = nil) then
2401 begin
2402 ptg.trigDataRec := nil;
2403 //HACK!
2404 if (ptg.TriggerType <> TRIGGER_SECRET) then
2405 begin
2406 e_LogWritefln('trigger of type %s has no triggerdata; wtf?!', [ptg.TriggerType], TMsgType.Warning);
2407 end;
2408 end
2409 else
2410 begin
2411 ptg.trigDataRec := trec.trigRec.clone(nil);
2412 end;
2414 with ptg^ do
2415 begin
2416 ID := find_id;
2417 // if this type of trigger exists both on the client and on the server
2418 // use an uniform numeration
2419 ClientID := 0;
2420 if (ptg.TriggerType = TRIGGER_SOUND) then
2421 begin
2422 Inc(gTriggerClientID);
2423 ClientID := gTriggerClientID;
2424 end;
2425 TimeOut := 0;
2426 ActivateUID := 0;
2427 PlayerCollide := False;
2428 DoorTime := -1;
2429 PressTime := -1;
2430 PressCount := 0;
2431 SoundPlayCount := 0;
2432 Sound := nil;
2433 AutoSpawn := False;
2434 SpawnCooldown := 0;
2435 SpawnedCount := 0;
2436 end;
2438 // update cached trigger variables
2439 trigUpdateCacheData(ptg^, ptg.trigDataRec);
2441 ptg.userVars := nil; //THashStrVariant.Create(hashStrHash, hashStrEqu);
2443 try
2444 ptg.exoThink := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_think']));
2445 except
2446 on e: TExomaParseException do
2447 begin
2448 conwritefln('*** ERROR parsing exoma_think (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_think'])]);
2449 ptg.exoThink := nil;
2450 end;
2451 else
2452 raise;
2453 end;
2454 try
2455 ptg.exoCheck := TExprBase.parse(tgclist, VarToStr(trec.user['exoma_check']));
2456 except
2457 on e: TExomaParseException do
2458 begin
2459 conwritefln('*** ERROR parsing exoma_check (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_check'])]);
2460 ptg.exoCheck := nil;
2461 end;
2462 else
2463 raise;
2464 end;
2465 try
2466 ptg.exoAction := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_action']));
2467 except
2468 on e: TExomaParseException do
2469 begin
2470 conwritefln('*** ERROR parsing exoma_action (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_action'])]);
2471 ptg.exoAction := nil;
2472 end;
2473 else
2474 raise;
2475 end;
2476 try
2477 ptg.exoInit := TExprBase.parseStatList(tgclist, VarToStr(trec.user['exoma_init']));
2478 except
2479 on e: TExomaParseException do
2480 begin
2481 conwritefln('*** ERROR parsing exoma_init (%s,%s): %s [%s]', [e.tokLine, e.tokCol, e.message, VarToStr(trec.user['exoma_init'])]);
2482 ptg.exoInit := nil;
2483 end;
2484 else
2485 raise;
2486 end;
2488 if (forceInternalIndex < 0) and (ptg.exoInit <> nil) then
2489 begin
2490 //conwritefln('executing trigger init: [%s]', [gTriggers[find_id].exoInit.toString()]);
2491 try
2492 tgscope.me := ptg;
2493 ptg.exoInit.value(tgscope);
2494 tgscope.me := nil;
2495 except
2496 tgscope.me := nil;
2497 conwritefln('*** trigger exoactivate error: %s', [ptg.exoInit.toString()]);
2498 exit;
2499 end;
2500 end;
2502 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê"
2503 if (ptg.TriggerType = TRIGGER_SOUND) and (ptg.tgcSoundName <> '') then
2504 begin
2505 // Åùå íåò òàêîãî çâóêà
2506 if not g_Sound_Exists(ptg.tgcSoundName) then
2507 begin
2508 fn := g_ExtractWadName(ptg.tgcSoundName);
2509 if (fn = '') then
2510 begin // Çâóê â ôàéëå ñ êàðòîé
2511 mapw := g_ExtractWadName(gMapInfo.Map);
2512 fn := mapw+':'+g_ExtractFilePathName(ptg.tgcSoundName);
2513 end
2514 else // Çâóê â îòäåëüíîì ôàéëå
2515 begin
2516 fn := GameDir + '/wads/' + ptg.tgcSoundName;
2517 end;
2519 //e_LogWritefln('loading trigger sound ''%s''', [fn]);
2520 if not g_Sound_CreateWADEx(ptg.tgcSoundName, fn) then
2521 begin
2522 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcSoundName]));
2523 end;
2524 end;
2526 // Ñîçäàåì îáúåêò çâóêà
2527 with ptg^ do
2528 begin
2529 Sound := TPlayableSound.Create();
2530 if not Sound.SetByName(ptg.tgcSoundName) then
2531 begin
2532 Sound.Free();
2533 Sound := nil;
2534 end;
2535 end;
2536 end;
2538 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà"
2539 if (ptg.TriggerType = TRIGGER_MUSIC) and (ptg.tgcMusicName <> '') then
2540 begin
2541 // Åùå íåò òàêîé ìóçûêè
2542 if not g_Sound_Exists(ptg.tgcMusicName) then
2543 begin
2544 fn := g_ExtractWadName(ptg.tgcMusicName);
2546 if fn = '' then
2547 begin // Ìóçûêà â ôàéëå ñ êàðòîé
2548 mapw := g_ExtractWadName(gMapInfo.Map);
2549 fn := mapw+':'+g_ExtractFilePathName(ptg.tgcMusicName);
2550 end
2551 else // Ìóçûêà â ôàéëå ñ êàðòîé
2552 begin
2553 fn := GameDir+'/wads/'+ptg.tgcMusicName;
2554 end;
2556 if not g_Sound_CreateWADEx(ptg.tgcMusicName, fn, True) then
2557 begin
2558 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcMusicName]));
2559 end;
2560 end;
2561 end;
2563 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü"
2564 if (ptg.TriggerType = TRIGGER_SHOT) then
2565 begin
2566 with ptg^ do
2567 begin
2568 ShotPanelTime := 0;
2569 ShotSightTime := 0;
2570 ShotSightTimeout := 0;
2571 ShotSightTarget := 0;
2572 ShotSightTargetN := 0;
2573 ShotAmmoCount := ptg.tgcAmmo;
2574 ShotReloadTime := 0;
2575 end;
2576 end;
2578 Result := find_id;
2579 end;
2582 // sorry; grid doesn't support recursive queries, so we have to do this
2583 type
2584 TSimpleMonsterList = specialize TSimpleList<TMonster>;
2586 var
2587 tgMonsList: TSimpleMonsterList = nil;
2589 procedure g_Triggers_Update();
2590 var
2591 a, b, i: Integer;
2592 Affected: array of Integer;
2594 function monsNear (mon: TMonster): Boolean;
2595 begin
2596 result := false; // don't stop
2598 gTriggers[a].ActivateUID := mon.UID;
2599 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2601 tgMonsList.append(mon);
2602 end;
2604 var
2605 mon: TMonster;
2606 pan: TPanel;
2607 begin
2608 if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
2610 if gTriggers = nil then
2611 Exit;
2612 SetLength(Affected, 0);
2614 for a := 0 to High(gTriggers) do
2615 with gTriggers[a] do
2616 // Åñòü òðèããåð:
2617 if TriggerType <> TRIGGER_NONE then
2618 begin
2619 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè)
2620 if DoorTime > 0 then DoorTime := DoorTime - 1;
2621 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ
2622 if PressTime > 0 then PressTime := PressTime - 1;
2623 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2624 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2625 begin
2626 for b := 0 to High(Activators) do
2627 begin
2628 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2629 if Activators[b].TimeOut > 0 then
2630 begin
2631 Dec(Activators[b].TimeOut);
2632 end
2633 else
2634 begin
2635 continue;
2636 end;
2637 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2638 if (tgcInterval = 0) and (Activators[b].TimeOut < 65530) then Activators[b].TimeOut := 0;
2639 end;
2640 end;
2642 // Îáðàáàòûâàåì ñïàâíåðû
2643 if Enabled and AutoSpawn then
2644 begin
2645 if SpawnCooldown = 0 then
2646 begin
2647 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà
2648 if (TriggerType = TRIGGER_SPAWNMONSTER) and (tgcDelay > 0) then
2649 begin
2650 ActivateUID := 0;
2651 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2652 end;
2653 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò
2654 if (TriggerType = TRIGGER_SPAWNITEM) and (tgcDelay > 0) then
2655 begin
2656 ActivateUID := 0;
2657 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2658 end;
2659 end
2660 else
2661 begin
2662 // Óìåíüøàåì âðåìÿ îæèäàíèÿ
2663 Dec(SpawnCooldown);
2664 end;
2665 end;
2667 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü"
2668 if TriggerType = TRIGGER_SHOT then
2669 begin
2670 if ShotPanelTime > 0 then
2671 begin
2672 Dec(ShotPanelTime);
2673 if ShotPanelTime = 0 then g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
2674 end;
2675 if ShotSightTime > 0 then
2676 begin
2677 Dec(ShotSightTime);
2678 if ShotSightTime = 0 then ShotSightTarget := ShotSightTargetN;
2679 end;
2680 if ShotSightTimeout > 0 then
2681 begin
2682 Dec(ShotSightTimeout);
2683 if ShotSightTimeout = 0 then ShotSightTarget := 0;
2684 end;
2685 if ShotReloadTime > 0 then
2686 begin
2687 Dec(ShotReloadTime);
2688 if ShotReloadTime = 0 then ShotAmmoCount := tgcAmmo;
2689 end;
2690 end;
2692 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì
2693 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2694 begin
2695 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2696 begin
2697 if tgcPlayCount > 0 then SoundPlayCount -= 1; // Åñëè 0 - èãðàåì çâóê áåñêîíå÷íî
2698 if tgcLocal then
2699 begin
2700 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
2701 end
2702 else
2703 begin
2704 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2705 end;
2706 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then MH_SEND_TriggerSound(gTriggers[a]);
2707 end;
2708 end;
2710 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü
2711 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2712 begin
2713 tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
2714 DoorTime := -1;
2715 end;
2717 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü
2718 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2719 begin
2720 pan := g_Map_PanelByGUID(trigPanelGUID);
2721 if (pan <> nil) and pan.isGWall then
2722 begin
2723 // Óæå çàêðûòà
2724 if {gWalls[trigPanelID].Enabled} pan.Enabled then
2725 begin
2726 DoorTime := -1;
2727 end
2728 else
2729 begin
2730 // Ïîêà îòêðûòà - çàêðûâàåì
2731 if tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d) then DoorTime := -1;
2732 end;
2733 end;
2734 end;
2736 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2737 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2738 (PressTime = 0) and (PressCount >= tgcPressCount) then
2739 begin
2740 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2741 PressTime := -1;
2742 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2743 if tgcPressCount > 0 then PressCount -= tgcPressCount else PressCount := 0;
2745 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2746 for b := 0 to High(gTriggers) do
2747 begin
2748 if g_Collide(tgcTX, tgcTY, tgcTWidth, tgcTHeight, gTriggers[b].X, gTriggers[b].Y,
2749 gTriggers[b].Width, gTriggers[b].Height) and
2750 ((b <> a) or (tgcWait > 0)) then
2751 begin // Can be self-activated, if there is Data.Wait
2752 if (not tgcExtRandom) or gTriggers[b].Enabled then
2753 begin
2754 SetLength(Affected, Length(Affected) + 1);
2755 Affected[High(Affected)] := b;
2756 end;
2757 end;
2758 end;
2760 //HACK!
2761 // if we have panelid, assume that it will switch the moving platform
2762 pan := g_Map_PanelByGUID(trigPanelGUID);
2763 if (pan <> nil) then
2764 begin
2765 case TriggerType of
2766 TRIGGER_PRESS: pan.movingActive := true; // what to do here?
2767 TRIGGER_ON: pan.movingActive := true;
2768 TRIGGER_OFF: pan.movingActive := false;
2769 TRIGGER_ONOFF: pan.movingActive := not pan.movingActive;
2770 end;
2771 if not tgcSilent and (Length(tgcSound) > 0) then
2772 begin
2773 g_Sound_PlayExAt(tgcSound, X, Y);
2774 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, tgcSound);
2775 end;
2776 end;
2778 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2779 if (TriggerType = TRIGGER_PRESS) and tgcExtRandom then
2780 begin
2781 if (Length(Affected) > 0) then
2782 begin
2783 b := Affected[Random(Length(Affected))];
2784 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2785 ActivateTrigger(gTriggers[b], 0);
2786 end;
2787 end
2788 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2789 begin
2790 for i := 0 to High(Affected) do
2791 begin
2792 b := Affected[i];
2793 case TriggerType of
2794 TRIGGER_PRESS:
2795 begin
2796 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2797 ActivateTrigger(gTriggers[b], 0);
2798 end;
2799 TRIGGER_ON:
2800 begin
2801 gTriggers[b].Enabled := True;
2802 end;
2803 TRIGGER_OFF:
2804 begin
2805 gTriggers[b].Enabled := False;
2806 gTriggers[b].TimeOut := 0;
2807 if gTriggers[b].AutoSpawn then
2808 begin
2809 gTriggers[b].AutoSpawn := False;
2810 gTriggers[b].SpawnCooldown := 0;
2811 end;
2812 end;
2813 TRIGGER_ONOFF:
2814 begin
2815 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2816 if not gTriggers[b].Enabled then
2817 begin
2818 gTriggers[b].TimeOut := 0;
2819 if gTriggers[b].AutoSpawn then
2820 begin
2821 gTriggers[b].AutoSpawn := False;
2822 gTriggers[b].SpawnCooldown := 0;
2823 end;
2824 end;
2825 end;
2826 end;
2827 end;
2828 end;
2829 SetLength(Affected, 0);
2830 end;
2832 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2833 if TimeOut > 0 then
2834 begin
2835 TimeOut := TimeOut - 1;
2836 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2837 end;
2839 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2840 if not Enabled then
2841 Continue;
2843 // "Èãðîê áëèçêî":
2844 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2845 (TimeOut = 0) then
2846 if gPlayers <> nil then
2847 for b := 0 to High(gPlayers) do
2848 if gPlayers[b] <> nil then
2849 with gPlayers[b] do
2850 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2851 if alive and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2852 Collide(X, Y, Width, Height) then
2853 begin
2854 gTriggers[a].ActivateUID := UID;
2856 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2857 PlayerCollide then
2858 { Don't activate sound/music again if player is here }
2859 else
2860 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2861 end;
2863 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2865 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2866 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2867 (TimeOut = 0) and (Keys = 0) then
2868 begin
2869 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2870 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2871 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2872 gTriggers[a].ActivateUID := 0;
2873 ActivateTrigger(gTriggers[a], 0);
2874 end else
2875 begin
2876 // "Ìîíñòð áëèçêî"
2877 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2878 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2879 begin
2880 //g_Mons_ForEach(monsNear);
2881 //Alive?!
2882 tgMonsList.reset();
2883 g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
2884 for mon in tgMonsList do
2885 begin
2886 gTriggers[a].ActivateUID := mon.UID;
2887 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2888 end;
2889 tgMonsList.reset(); // just in case
2890 end;
2892 // "Ìîíñòðîâ íåò"
2893 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2894 (TimeOut = 0) and (Keys = 0) then
2895 if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
2896 begin
2897 gTriggers[a].ActivateUID := 0;
2898 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2899 end;
2900 end;
2902 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2903 end;
2904 end;
2906 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2907 begin
2908 if (ID >= Length(gTriggers)) then exit;
2909 gTriggers[ID].ActivateUID := ActivateUID;
2910 ActivateTrigger(gTriggers[ID], ActivateType);
2911 end;
2913 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2914 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2915 var
2916 a: Integer;
2917 k: Byte;
2918 p: TPlayer;
2919 begin
2920 Result := nil;
2922 if gTriggers = nil then Exit;
2924 case g_GetUIDType(UID) of
2925 UID_GAME: k := 255;
2926 UID_PLAYER:
2927 begin
2928 p := g_Player_Get(UID);
2929 if p <> nil then
2930 k := p.GetKeys
2931 else
2932 k := 0;
2933 end;
2934 else k := 0;
2935 end;
2937 for a := 0 to High(gTriggers) do
2938 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2939 (gTriggers[a].TimeOut = 0) and
2940 (not InDWArray(a, IgnoreList)) and
2941 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2942 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2943 if g_Collide(X, Y, Width, Height,
2944 gTriggers[a].X, gTriggers[a].Y,
2945 gTriggers[a].Width, gTriggers[a].Height) then
2946 begin
2947 gTriggers[a].ActivateUID := UID;
2948 if ActivateTrigger(gTriggers[a], ActivateType) then
2949 begin
2950 SetLength(Result, Length(Result)+1);
2951 Result[High(Result)] := a;
2952 end;
2953 end;
2954 end;
2956 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2957 var
2958 a: Integer;
2959 k: Byte;
2960 p: TPlayer;
2961 begin
2962 if gTriggers = nil then Exit;
2964 case g_GetUIDType(UID) of
2965 UID_GAME: k := 255;
2966 UID_PLAYER:
2967 begin
2968 p := g_Player_Get(UID);
2969 if p <> nil then
2970 k := p.GetKeys
2971 else
2972 k := 0;
2973 end;
2974 else k := 0;
2975 end;
2977 for a := 0 to High(gTriggers) do
2978 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2979 (gTriggers[a].TimeOut = 0) and
2980 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2981 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2982 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2983 gTriggers[a].Width, gTriggers[a].Height) then
2984 begin
2985 gTriggers[a].ActivateUID := UID;
2986 ActivateTrigger(gTriggers[a], ActivateType);
2987 end;
2988 end;
2990 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2991 var
2992 a: Integer;
2993 k: Byte;
2994 rsq: Word;
2995 p: TPlayer;
2996 begin
2997 if gTriggers = nil then
2998 Exit;
3000 case g_GetUIDType(UID) of
3001 UID_GAME: k := 255;
3002 UID_PLAYER:
3003 begin
3004 p := g_Player_Get(UID);
3005 if p <> nil then
3006 k := p.GetKeys
3007 else
3008 k := 0;
3009 end;
3010 else k := 0;
3011 end;
3013 rsq := Radius * Radius;
3015 for a := 0 to High(gTriggers) do
3016 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
3017 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
3018 (gTriggers[a].TimeOut = 0) and
3019 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
3020 ByteBool(gTriggers[a].ActivateType and ActivateType) then
3021 with gTriggers[a] do
3022 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
3023 X, Y, Width, Height) then
3024 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
3025 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
3026 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
3027 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
3028 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
3029 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
3030 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
3031 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
3032 begin
3033 ActivateUID := UID;
3034 ActivateTrigger(gTriggers[a], ActivateType);
3035 end;
3036 end;
3038 procedure g_Triggers_OpenAll();
3039 var
3040 a: Integer;
3041 b: Boolean;
3042 begin
3043 if gTriggers = nil then Exit;
3045 b := False;
3046 for a := 0 to High(gTriggers) do
3047 begin
3048 with gTriggers[a] do
3049 begin
3050 if (TriggerType = TRIGGER_OPENDOOR) or
3051 (TriggerType = TRIGGER_DOOR5) or
3052 (TriggerType = TRIGGER_DOOR) then
3053 begin
3054 tr_OpenDoor(trigPanelGUID, True, tgcD2d);
3055 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
3056 b := True;
3057 end;
3058 end;
3059 end;
3061 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
3062 end;
3064 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
3065 begin
3066 if (gTriggers <> nil) then
3067 if gTriggers[ID].SpawnedCount > 0 then
3068 Dec(gTriggers[ID].SpawnedCount);
3069 end;
3072 procedure g_Triggers_Free ();
3073 var
3074 a: Integer;
3075 begin
3076 for a := 0 to High(gTriggers) do
3077 begin
3078 if (gTriggers[a].TriggerType = TRIGGER_SOUND) then
3079 begin
3080 if g_Sound_Exists(gTriggers[a].tgcSoundName) then
3081 begin
3082 g_Sound_Delete(gTriggers[a].tgcSoundName);
3083 end;
3084 gTriggers[a].Sound.Free();
3085 end;
3086 if (gTriggers[a].Activators <> nil) then
3087 begin
3088 SetLength(gTriggers[a].Activators, 0);
3089 end;
3090 gTriggers[a].trigDataRec.Free();
3092 gTriggers[a].exoThink.Free();
3093 gTriggers[a].exoCheck.Free();
3094 gTriggers[a].exoAction.Free();
3095 gTriggers[a].userVars.Free();
3096 end;
3098 gTriggers := nil;
3099 gSecretsCount := 0;
3100 SetLength(gMonstersSpawned, 0);
3101 end;
3104 procedure g_Triggers_SaveState (st: TStream);
3105 var
3106 count, actCount, i, j: Integer;
3107 sg: Single;
3108 b: Boolean;
3109 kv: THashStrVariant.PEntry;
3110 t: LongInt;
3111 begin
3112 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
3113 count := Length(gTriggers);
3115 // Êîëè÷åñòâî òðèããåðîâ
3116 utils.writeInt(st, LongInt(count));
3117 if (count = 0) then exit;
3119 for i := 0 to High(gTriggers) do
3120 begin
3121 // Ñèãíàòóðà òðèããåðà
3122 utils.writeSign(st, 'TRGX');
3123 utils.writeInt(st, Byte(0));
3124 // Òèï òðèããåðà
3125 utils.writeInt(st, Byte(gTriggers[i].TriggerType));
3126 if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
3127 // Ñïåöèàëüíûå äàííûå òðèããåðà: ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
3128 utils.writeInt(st, LongInt(gTriggers[i].mapIndex));
3129 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
3130 utils.writeInt(st, LongInt(gTriggers[i].X));
3131 utils.writeInt(st, LongInt(gTriggers[i].Y));
3132 // Ðàçìåðû
3133 utils.writeInt(st, Word(gTriggers[i].Width));
3134 utils.writeInt(st, Word(gTriggers[i].Height));
3135 // Âêëþ÷åí ëè òðèããåð
3136 utils.writeBool(st, gTriggers[i].Enabled);
3137 // Òèï àêòèâàöèè òðèããåðà
3138 utils.writeInt(st, Byte(gTriggers[i].ActivateType));
3139 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
3140 utils.writeInt(st, Byte(gTriggers[i].Keys));
3141 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
3142 utils.writeInt(st, LongInt(gTriggers[i].TexturePanelGUID));
3143 // Òèï ýòîé ïàíåëè
3144 //Mem.WriteWord(gTriggers[i].TexturePanelType);
3145 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
3146 utils.writeInt(st, LongInt(gTriggers[i].trigPanelGUID));
3147 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
3148 utils.writeInt(st, Word(gTriggers[i].TimeOut));
3149 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
3150 utils.writeInt(st, Word(gTriggers[i].ActivateUID));
3151 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
3152 actCount := Length(gTriggers[i].Activators);
3153 utils.writeInt(st, LongInt(actCount));
3154 for j := 0 to actCount-1 do
3155 begin
3156 // UID îáúåêòà
3157 utils.writeInt(st, Word(gTriggers[i].Activators[j].UID));
3158 // Âðåìÿ îæèäàíèÿ
3159 utils.writeInt(st, Word(gTriggers[i].Activators[j].TimeOut));
3160 end;
3161 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
3162 utils.writeBool(st, gTriggers[i].PlayerCollide);
3163 // Âðåìÿ äî çàêðûòèÿ äâåðè
3164 utils.writeInt(st, LongInt(gTriggers[i].DoorTime));
3165 // Çàäåðæêà àêòèâàöèè
3166 utils.writeInt(st, LongInt(gTriggers[i].PressTime));
3167 // Ñ÷åò÷èê íàæàòèé
3168 utils.writeInt(st, LongInt(gTriggers[i].PressCount));
3169 // Ñïàâíåð àêòèâåí
3170 utils.writeBool(st, gTriggers[i].AutoSpawn);
3171 // Çàäåðæêà ñïàâíåðà
3172 utils.writeInt(st, LongInt(gTriggers[i].SpawnCooldown));
3173 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
3174 utils.writeInt(st, LongInt(gTriggers[i].SpawnedCount));
3175 // Ñêîëüêî ðàç ïðîèãðàí çâóê
3176 utils.writeInt(st, LongInt(gTriggers[i].SoundPlayCount));
3177 // Ïðîèãðûâàåòñÿ ëè çâóê?
3178 if (gTriggers[i].Sound <> nil) then b := gTriggers[i].Sound.IsPlaying() else b := false;
3179 utils.writeBool(st, b);
3180 if b then
3181 begin
3182 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
3183 utils.writeInt(st, LongWord(gTriggers[i].Sound.GetPosition()));
3184 // Ãðîìêîñòü çâóêà
3185 sg := gTriggers[i].Sound.GetVolume();
3186 sg := sg/(gSoundLevel/255.0);
3187 //Mem.WriteSingle(sg);
3188 st.WriteBuffer(sg, sizeof(sg)); // sorry
3189 // Ñòåðåî ñìåùåíèå çâóêà
3190 sg := gTriggers[i].Sound.GetPan();
3191 //Mem.WriteSingle(sg);
3192 st.WriteBuffer(sg, sizeof(sg)); // sorry
3193 end;
3194 // uservars
3195 if (gTriggers[i].userVars = nil) then
3196 begin
3197 utils.writeInt(st, LongInt(0));
3198 end
3199 else
3200 begin
3201 utils.writeInt(st, LongInt(gTriggers[i].userVars.count)); //FIXME: check for overflow
3202 for kv in gTriggers[i].userVars.byKeyValue do
3203 begin
3204 //writeln('<', kv.key, '>:<', VarToStr(kv.value), '>');
3205 utils.writeStr(st, kv.key);
3206 t := LongInt(varType(kv.value));
3207 utils.writeInt(st, LongInt(t));
3208 case t of
3209 varString: utils.writeStr(st, AnsiString(kv.value));
3210 varBoolean: utils.writeBool(st, Boolean(kv.value));
3211 varShortInt: utils.writeInt(st, LongInt(kv.value));
3212 varSmallint: utils.writeInt(st, LongInt(kv.value));
3213 varInteger: utils.writeInt(st, LongInt(kv.value));
3214 //varInt64: Mem.WriteInt(Integer(kv.value));
3215 varByte: utils.writeInt(st, LongInt(kv.value));
3216 varWord: utils.writeInt(st, LongInt(kv.value));
3217 varLongWord: utils.writeInt(st, LongInt(kv.value));
3218 //varQWord:
3219 else raise Exception.CreateFmt('cannot save uservar ''%s''', [kv.key]);
3220 end;
3221 end;
3222 end;
3223 end;
3224 end;
3227 procedure g_Triggers_LoadState (st: TStream);
3228 var
3229 count, actCount, i, j, a: Integer;
3230 dw: DWORD;
3231 vol, pan: Single;
3232 b: Boolean;
3233 Trig: TTrigger;
3234 mapIndex: Integer;
3235 uvcount: Integer;
3236 vt: LongInt;
3237 vv: Variant;
3238 uvname: AnsiString = '';
3239 ustr: AnsiString = '';
3240 uint: LongInt;
3241 ubool: Boolean;
3242 begin
3243 assert(st <> nil);
3245 g_Triggers_Free();
3247 // Êîëè÷åñòâî òðèããåðîâ
3248 count := utils.readLongInt(st);
3249 if (count = 0) then exit;
3250 if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid trigger count');
3252 for a := 0 to count-1 do
3253 begin
3254 // Ñèãíàòóðà òðèããåðà
3255 if not utils.checkSign(st, 'TRGX') then raise XStreamError.Create('invalid trigger signature');
3256 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid trigger version');
3257 // Òèï òðèããåðà
3258 Trig.TriggerType := utils.readByte(st);
3259 if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
3260 // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
3261 mapIndex := utils.readLongInt(st);
3262 i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
3263 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
3264 gTriggers[i].X := utils.readLongInt(st);
3265 gTriggers[i].Y := utils.readLongInt(st);
3266 // Ðàçìåðû
3267 gTriggers[i].Width := utils.readWord(st);
3268 gTriggers[i].Height := utils.readWord(st);
3269 // Âêëþ÷åí ëè òðèããåð
3270 gTriggers[i].Enabled := utils.readBool(st);
3271 // Òèï àêòèâàöèè òðèããåðà
3272 gTriggers[i].ActivateType := utils.readByte(st);
3273 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
3274 gTriggers[i].Keys := utils.readByte(st);
3275 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
3276 gTriggers[i].TexturePanelGUID := utils.readLongInt(st);
3277 // Òèï ýòîé ïàíåëè
3278 //Mem.ReadWord(gTriggers[i].TexturePanelType);
3279 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
3280 gTriggers[i].trigPanelGUID := utils.readLongInt(st);
3281 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
3282 gTriggers[i].TimeOut := utils.readWord(st);
3283 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
3284 gTriggers[i].ActivateUID := utils.readWord(st);
3285 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
3286 actCount := utils.readLongInt(st);
3287 if (actCount < 0) or (actCount > 1024*1024) then raise XStreamError.Create('invalid activated object count');
3288 if (actCount > 0) then
3289 begin
3290 SetLength(gTriggers[i].Activators, actCount);
3291 for j := 0 to actCount-1 do
3292 begin
3293 // UID îáúåêòà
3294 gTriggers[i].Activators[j].UID := utils.readWord(st);
3295 // Âðåìÿ îæèäàíèÿ
3296 gTriggers[i].Activators[j].TimeOut := utils.readWord(st);
3297 end;
3298 end;
3299 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
3300 gTriggers[i].PlayerCollide := utils.readBool(st);
3301 // Âðåìÿ äî çàêðûòèÿ äâåðè
3302 gTriggers[i].DoorTime := utils.readLongInt(st);
3303 // Çàäåðæêà àêòèâàöèè
3304 gTriggers[i].PressTime := utils.readLongInt(st);
3305 // Ñ÷åò÷èê íàæàòèé
3306 gTriggers[i].PressCount := utils.readLongInt(st);
3307 // Ñïàâíåð àêòèâåí
3308 gTriggers[i].AutoSpawn := utils.readBool(st);
3309 // Çàäåðæêà ñïàâíåðà
3310 gTriggers[i].SpawnCooldown := utils.readLongInt(st);
3311 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
3312 gTriggers[i].SpawnedCount := utils.readLongInt(st);
3313 // Ñêîëüêî ðàç ïðîèãðàí çâóê
3314 gTriggers[i].SoundPlayCount := utils.readLongInt(st);
3315 // Ïðîèãðûâàåòñÿ ëè çâóê?
3316 b := utils.readBool(st);
3317 if b then
3318 begin
3319 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
3320 dw := utils.readLongWord(st);
3321 // Ãðîìêîñòü çâóêà
3322 //Mem.ReadSingle(vol);
3323 st.ReadBuffer(vol, sizeof(vol)); // sorry
3324 // Ñòåðåî ñìåùåíèå çâóêà
3325 //Mem.ReadSingle(pan);
3326 st.ReadBuffer(pan, sizeof(pan)); // sorry
3327 // Çàïóñêàåì çâóê, åñëè åñòü
3328 if (gTriggers[i].Sound <> nil) then
3329 begin
3330 gTriggers[i].Sound.PlayPanVolume(pan, vol);
3331 gTriggers[i].Sound.Pause(True);
3332 gTriggers[i].Sound.SetPosition(dw);
3333 end
3334 end;
3335 // uservars
3336 gTriggers[i].userVars.Free();
3337 gTriggers[i].userVars := nil;
3338 uvcount := utils.readLongInt(st);
3339 if (uvcount < 0) or (uvcount > 1024*1024) then raise XStreamError.Create('invalid number of user vars in trigger');
3340 if (uvcount > 0) then
3341 begin
3342 gTriggers[i].userVars := THashStrVariant.Create(hashStrHash, hashStrEqu);
3343 vv := Unassigned;
3344 while (uvcount > 0) do
3345 begin
3346 Dec(uvcount);
3347 uvname := utils.readStr(st);
3348 vt := utils.readLongInt(st);
3349 case vt of
3350 varString: begin ustr := utils.readStr(st); vv := ustr; end;
3351 varBoolean: begin ubool := utils.readBool(st); vv := ubool; end;
3352 varShortInt: begin uint := utils.readLongInt(st); vv := ShortInt(uint); end;
3353 varSmallint: begin uint := utils.readLongInt(st); vv := SmallInt(uint); end;
3354 varInteger: begin uint := utils.readLongInt(st); vv := LongInt(uint); end;
3355 varByte: begin uint := utils.readLongInt(st); vv := Byte(uint); end;
3356 varWord: begin uint := utils.readLongInt(st); vv := Word(uint); end;
3357 varLongWord: begin uint := utils.readLongInt(st); vv := LongWord(uint); end;
3358 else raise Exception.CreateFmt('cannot load uservar ''%s''', [uvname]);
3359 end;
3360 gTriggers[i].userVars.put(uvname, vv);
3361 end;
3362 end;
3363 end;
3364 end;
3367 end.