DEADSOFTWARE

simplified trigger loading code
[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 MAPDEF, e_graphics, g_basic, g_sound,
23 BinEditor, xdynrec;
25 type
26 TActivator = record
27 UID: Word;
28 TimeOut: Word;
29 end;
30 PTrigger = ^TTrigger;
31 TTrigger = record
32 public
33 ID: DWORD;
34 ClientID: DWORD;
35 TriggerType: Byte;
36 X, Y: Integer;
37 Width, Height: Word;
38 Enabled: Boolean;
39 ActivateType: Byte;
40 Keys: Byte;
41 TexturePanelGUID: Integer;
42 TexturePanelType: Word;
44 TimeOut: Word;
45 ActivateUID: Word;
46 Activators: array of TActivator;
47 PlayerCollide: Boolean;
48 DoorTime: Integer;
49 PressTime: Integer;
50 PressCount: Integer;
51 SoundPlayCount: Integer;
52 Sound: TPlayableSound;
53 AutoSpawn: Boolean;
54 SpawnCooldown: Integer;
55 SpawnedCount: Integer;
56 ShotPanelType: Word;
57 ShotPanelTime: Integer;
58 ShotSightTime: Integer;
59 ShotSightTimeout: Integer;
60 ShotSightTarget: Word;
61 ShotSightTargetN: Word;
62 ShotAmmoCount: Word;
63 ShotReloadTime: Integer;
65 mapId: AnsiString; // trigger id, from map
66 mapIndex: Integer; // index in fields['trigger'], used in save/load
67 trigPanelGUID: Integer;
69 trigDataRec: TDynRecord; // triggerdata; owned by trigger (cloned)
71 {$INCLUDE ../shared/mapdef_tgc_def.inc}
73 public
74 function trigCenter (): TDFPoint; inline;
75 end;
77 function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
78 procedure g_Triggers_Update();
79 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
80 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
81 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
82 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
83 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
84 procedure g_Triggers_OpenAll();
85 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
86 procedure g_Triggers_Free();
87 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
88 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
91 var
92 gTriggerClientID: Integer = 0;
93 gTriggers: array of TTrigger;
94 gSecretsCount: Integer = 0;
95 gMonstersSpawned: array of LongInt = nil;
98 implementation
100 uses
101 Math,
102 g_player, g_map, g_panel, g_gfx, g_game, g_textures,
103 g_console, g_monsters, g_items, g_phys, g_weapons,
104 wadreader, g_main, SysUtils, e_log, g_language,
105 g_options, g_net, g_netmsg, utils, xparser;
107 const
108 TRIGGER_SIGNATURE = $58475254; // 'TRGX'
109 TRAP_DAMAGE = 1000;
111 {$INCLUDE ../shared/mapdef_tgc_impl.inc}
113 function TTrigger.trigCenter (): TDFPoint; inline;
114 begin
115 result := TDFPoint.Create(x+width div 2, y+height div 2);
116 end;
119 function FindTrigger (): DWORD;
120 var
121 i: Integer;
122 begin
123 for i := 0 to High(gTriggers) do
124 begin
125 if gTriggers[i].TriggerType = TRIGGER_NONE then begin result := i; exit; end;
126 end;
128 if (gTriggers = nil) then
129 begin
130 SetLength(gTriggers, 8);
131 result := 0;
132 end
133 else
134 begin
135 result := Length(gTriggers);
136 SetLength(gTriggers, result+8);
137 for i := result to High(gTriggers) do gTriggers[i].TriggerType := TRIGGER_NONE;
138 end;
139 end;
142 function tr_CloseDoor (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
143 var
144 a, b, c: Integer;
145 pan: TPanel;
146 PanelID: Integer;
147 begin
148 result := false;
149 pan := g_Map_PanelByGUID(PanelGUID);
150 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
151 PanelID := pan.arrIdx;
153 if not d2d then
154 begin
155 with gWalls[PanelID] do
156 begin
157 if g_CollidePlayer(X, Y, Width, Height) or g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
158 if not Enabled then
159 begin
160 if not NoSound then
161 begin
162 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
163 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
164 end;
165 g_Map_EnableWallGUID(PanelGUID);
166 result := true;
167 end;
168 end;
169 end
170 else
171 begin
172 if (gDoorMap = nil) then exit;
174 c := -1;
175 for a := 0 to High(gDoorMap) do
176 begin
177 for b := 0 to High(gDoorMap[a]) do
178 begin
179 if gDoorMap[a, b] = DWORD(PanelID) then
180 begin
181 c := a;
182 break;
183 end;
184 end;
185 if (c <> -1) then break;
186 end;
187 if (c = -1) then exit;
189 for b := 0 to High(gDoorMap[c]) do
190 begin
191 with gWalls[gDoorMap[c, b]] do
192 begin
193 if g_CollidePlayer(X, Y, Width, Height) or g_Mons_IsAnyAliveAt(X, Y, Width, Height) then exit;
194 end;
195 end;
197 if not NoSound then
198 begin
199 for b := 0 to High(gDoorMap[c]) do
200 begin
201 if not gWalls[gDoorMap[c, b]].Enabled then
202 begin
203 with gWalls[PanelID] do
204 begin
205 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
206 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
207 end;
208 break;
209 end;
210 end;
211 end;
213 for b := 0 to High(gDoorMap[c]) do
214 begin
215 if not gWalls[gDoorMap[c, b]].Enabled then
216 begin
217 g_Map_EnableWall_XXX(gDoorMap[c, b]);
218 result := true;
219 end;
220 end;
221 end;
222 end;
225 procedure tr_CloseTrap (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean);
226 var
227 a, b, c: Integer;
228 wx, wy, wh, ww: Integer;
229 pan: TPanel;
230 PanelID: Integer;
232 function monsDamage (mon: TMonster): Boolean;
233 begin
234 result := false; // don't stop
235 if g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
236 end;
238 begin
239 pan := g_Map_PanelByGUID(PanelGUID);
241 if (pan = nil) then
242 begin
243 e_LogWritefln('tr_CloseTrap: pguid=%s; NO PANEL!', [PanelGUID], MSG_WARNING);
244 end
245 else
246 begin
247 e_LogWritefln('tr_CloseTrap: pguid=%s; isGWall=%s; arrIdx=%s', [PanelGUID, pan.isGWall, pan.arrIdx]);
248 end;
250 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
251 PanelID := pan.arrIdx;
253 if not d2d then
254 begin
255 with gWalls[PanelID] do
256 begin
257 if (not NoSound) and (not Enabled) then
258 begin
259 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
260 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
261 end;
262 end;
264 wx := gWalls[PanelID].X;
265 wy := gWalls[PanelID].Y;
266 ww := gWalls[PanelID].Width;
267 wh := gWalls[PanelID].Height;
269 with gWalls[PanelID] do
270 begin
271 if gPlayers <> nil then
272 begin
273 for a := 0 to High(gPlayers) do
274 begin
275 if (gPlayers[a] <> nil) and gPlayers[a].alive and gPlayers[a].Collide(X, Y, Width, Height) then
276 begin
277 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
278 end;
279 end;
280 end;
282 //g_Mons_ForEach(monsDamage);
283 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
285 if not Enabled then g_Map_EnableWallGUID(PanelGUID);
286 end;
287 end
288 else
289 begin
290 if (gDoorMap = nil) then exit;
292 c := -1;
293 for a := 0 to High(gDoorMap) do
294 begin
295 for b := 0 to High(gDoorMap[a]) do
296 begin
297 if gDoorMap[a, b] = DWORD(PanelID) then
298 begin
299 c := a;
300 break;
301 end;
302 end;
303 if (c <> -1) then break;
304 end;
305 if (c = -1) then exit;
307 if not NoSound then
308 begin
309 for b := 0 to High(gDoorMap[c]) do
310 begin
311 if not gWalls[gDoorMap[c, b]].Enabled then
312 begin
313 with gWalls[PanelID] do
314 begin
315 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
316 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
317 end;
318 Break;
319 end;
320 end;
321 end;
323 for b := 0 to High(gDoorMap[c]) do
324 begin
325 wx := gWalls[gDoorMap[c, b]].X;
326 wy := gWalls[gDoorMap[c, b]].Y;
327 ww := gWalls[gDoorMap[c, b]].Width;
328 wh := gWalls[gDoorMap[c, b]].Height;
330 with gWalls[gDoorMap[c, b]] do
331 begin
332 if gPlayers <> nil then
333 begin
334 for a := 0 to High(gPlayers) do
335 begin
336 if (gPlayers[a] <> nil) and gPlayers[a].alive and gPlayers[a].Collide(X, Y, Width, Height) then
337 begin
338 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
339 end;
340 end;
341 end;
343 //g_Mons_ForEach(monsDamage);
344 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
345 (*
346 if gMonsters <> nil then
347 for a := 0 to High(gMonsters) do
348 if (gMonsters[a] <> nil) and gMonsters[a].alive and
349 g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
350 gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
351 *)
353 if not Enabled then g_Map_EnableWall_XXX(gDoorMap[c, b]);
354 end;
355 end;
356 end;
357 end;
360 function tr_OpenDoor (PanelGUID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
361 var
362 a, b, c: Integer;
363 pan: TPanel;
364 PanelID: Integer;
365 begin
366 result := false;
367 pan := g_Map_PanelByGUID(PanelGUID);
368 if (pan = nil) or not pan.isGWall then exit; //!FIXME!TRIGANY!
369 PanelID := pan.arrIdx;
371 if not d2d then
372 begin
373 with gWalls[PanelID] do
374 begin
375 if Enabled then
376 begin
377 if not NoSound then
378 begin
379 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
380 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
381 end;
382 g_Map_DisableWallGUID(PanelGUID);
383 result := true;
384 end;
385 end
386 end
387 else
388 begin
389 if (gDoorMap = nil) then exit;
391 c := -1;
392 for a := 0 to High(gDoorMap) do
393 begin
394 for b := 0 to High(gDoorMap[a]) do
395 begin
396 if gDoorMap[a, b] = DWORD(PanelID) then
397 begin
398 c := a;
399 break;
400 end;
401 end;
402 if (c <> -1) then break;
403 end;
404 if (c = -1) then exit;
406 if not NoSound then
407 begin
408 for b := 0 to High(gDoorMap[c]) do
409 begin
410 if gWalls[gDoorMap[c, b]].Enabled then
411 begin
412 with gWalls[PanelID] do
413 begin
414 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
415 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
416 end;
417 break;
418 end;
419 end;
420 end;
422 for b := 0 to High(gDoorMap[c]) do
423 begin
424 if gWalls[gDoorMap[c, b]].Enabled then
425 begin
426 g_Map_DisableWall_XXX(gDoorMap[c, b]);
427 result := true;
428 end;
429 end;
430 end;
431 end;
434 function tr_SetLift (PanelGUID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
435 var
436 a, b, c: Integer;
437 t: Integer = 0;
438 pan: TPanel;
439 PanelID: Integer;
440 begin
441 result := false;
442 pan := g_Map_PanelByGUID(PanelGUID);
443 if (pan = nil) or not pan.isGLift then exit; //!FIXME!TRIGANY!
444 PanelID := pan.arrIdx;
446 if (gLifts[PanelID].PanelType = PANEL_LIFTUP) or (gLifts[PanelID].PanelType = PANEL_LIFTDOWN) then
447 begin
448 case d of
449 0: t := 0;
450 1: t := 1;
451 else t := IfThen(gLifts[PanelID].LiftType = 1, 0, 1);
452 end
453 end
454 else if (gLifts[PanelID].PanelType = PANEL_LIFTLEFT) or (gLifts[PanelID].PanelType = PANEL_LIFTRIGHT) then
455 begin
456 case d of
457 0: t := 2;
458 1: t := 3;
459 else t := IfThen(gLifts[PanelID].LiftType = 2, 3, 2);
460 end;
461 end;
463 if not d2d then
464 begin
465 with gLifts[PanelID] do
466 begin
467 if (LiftType <> t) then
468 begin
469 g_Map_SetLiftGUID(PanelGUID, t); //???
470 //if not NoSound then g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
471 result := true;
472 end;
473 end;
474 end
475 else // Êàê â D2d
476 begin
477 if (gLiftMap = nil) then exit;
479 c := -1;
480 for a := 0 to High(gLiftMap) do
481 begin
482 for b := 0 to High(gLiftMap[a]) do
483 begin
484 if (gLiftMap[a, b] = DWORD(PanelID)) then
485 begin
486 c := a;
487 break;
488 end;
489 end;
490 if (c <> -1) then break;
491 end;
492 if (c = -1) then exit;
494 {if not NoSound then
495 for b := 0 to High(gLiftMap[c]) do
496 if gLifts[gLiftMap[c, b]].LiftType <> t then
497 begin
498 with gLifts[PanelID] do
499 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
500 Break;
501 end;}
503 for b := 0 to High(gLiftMap[c]) do
504 begin
505 with gLifts[gLiftMap[c, b]] do
506 begin
507 if (LiftType <> t) then
508 begin
509 g_Map_SetLift_XXX(gLiftMap[c, b], t);
510 result := true;
511 end;
512 end;
513 end;
514 end;
515 end;
518 function tr_SpawnShot (ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
519 var
520 snd: string;
521 Projectile: Boolean;
522 TextureID: DWORD;
523 Anim: TAnimation;
524 begin
525 result := -1;
526 TextureID := DWORD(-1);
527 snd := 'SOUND_WEAPON_FIREROCKET';
528 Projectile := true;
530 case ShotType of
531 TRIGGER_SHOT_PISTOL:
532 begin
533 g_Weapon_pistol(wx, wy, dx, dy, 0, True);
534 snd := 'SOUND_WEAPON_FIREPISTOL';
535 Projectile := False;
536 if ShotSound then
537 begin
538 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
539 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
540 end;
541 end;
543 TRIGGER_SHOT_BULLET:
544 begin
545 g_Weapon_mgun(wx, wy, dx, dy, 0, True);
546 if gSoundEffectsDF then snd := 'SOUND_WEAPON_FIRECGUN'
547 else snd := 'SOUND_WEAPON_FIREPISTOL';
548 Projectile := False;
549 if ShotSound then
550 begin
551 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
552 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
553 end;
554 end;
556 TRIGGER_SHOT_SHOTGUN:
557 begin
558 g_Weapon_Shotgun(wx, wy, dx, dy, 0, True);
559 snd := 'SOUND_WEAPON_FIRESHOTGUN';
560 Projectile := False;
561 if ShotSound then
562 begin
563 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
564 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL2);
565 end;
566 end;
568 TRIGGER_SHOT_SSG:
569 begin
570 g_Weapon_DShotgun(wx, wy, dx, dy, 0, True);
571 snd := 'SOUND_WEAPON_FIRESHOTGUN2';
572 Projectile := False;
573 if ShotSound then
574 begin
575 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
576 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
577 if g_Game_IsNet then MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL3);
578 end;
579 end;
581 TRIGGER_SHOT_IMP:
582 begin
583 g_Weapon_ball1(wx, wy, dx, dy, 0, -1, True);
584 snd := 'SOUND_WEAPON_FIREBALL';
585 end;
587 TRIGGER_SHOT_PLASMA:
588 begin
589 g_Weapon_Plasma(wx, wy, dx, dy, 0, -1, True);
590 snd := 'SOUND_WEAPON_FIREPLASMA';
591 end;
593 TRIGGER_SHOT_SPIDER:
594 begin
595 g_Weapon_aplasma(wx, wy, dx, dy, 0, -1, True);
596 snd := 'SOUND_WEAPON_FIREPLASMA';
597 end;
599 TRIGGER_SHOT_CACO:
600 begin
601 g_Weapon_ball2(wx, wy, dx, dy, 0, -1, True);
602 snd := 'SOUND_WEAPON_FIREBALL';
603 end;
605 TRIGGER_SHOT_BARON:
606 begin
607 g_Weapon_ball7(wx, wy, dx, dy, 0, -1, True);
608 snd := 'SOUND_WEAPON_FIREBALL';
609 end;
611 TRIGGER_SHOT_MANCUB:
612 begin
613 g_Weapon_manfire(wx, wy, dx, dy, 0, -1, True);
614 snd := 'SOUND_WEAPON_FIREBALL';
615 end;
617 TRIGGER_SHOT_REV:
618 begin
619 g_Weapon_revf(wx, wy, dx, dy, 0, ShotTarget, -1, True);
620 snd := 'SOUND_WEAPON_FIREREV';
621 end;
623 TRIGGER_SHOT_ROCKET:
624 begin
625 g_Weapon_Rocket(wx, wy, dx, dy, 0, -1, True);
626 snd := 'SOUND_WEAPON_FIREROCKET';
627 end;
629 TRIGGER_SHOT_BFG:
630 begin
631 g_Weapon_BFGShot(wx, wy, dx, dy, 0, -1, True);
632 snd := 'SOUND_WEAPON_FIREBFG';
633 end;
635 TRIGGER_SHOT_EXPL:
636 begin
637 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
638 begin
639 Anim := TAnimation.Create(TextureID, False, 6);
640 Anim.Blending := False;
641 g_GFX_OnceAnim(wx-64, wy-64, Anim);
642 Anim.Free();
643 end;
644 Projectile := False;
645 g_Weapon_Explode(wx, wy, 60, 0);
646 snd := 'SOUND_WEAPON_EXPLODEROCKET';
647 end;
649 TRIGGER_SHOT_BFGEXPL:
650 begin
651 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
652 begin
653 Anim := TAnimation.Create(TextureID, False, 6);
654 Anim.Blending := False;
655 g_GFX_OnceAnim(wx-64, wy-64, Anim);
656 Anim.Free();
657 end;
658 Projectile := False;
659 g_Weapon_BFG9000(wx, wy, 0);
660 snd := 'SOUND_WEAPON_EXPLODEBFG';
661 end;
663 else exit;
664 end;
666 if g_Game_IsNet and g_Game_IsServer then
667 begin
668 case ShotType of
669 TRIGGER_SHOT_EXPL: MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_EXPLODE);
670 TRIGGER_SHOT_BFGEXPL: MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_BFGEXPL);
671 else
672 begin
673 if Projectile then MH_SEND_CreateShot(LastShotID);
674 if ShotSound then MH_SEND_Sound(wx, wy, snd);
675 end;
676 end;
677 end;
679 if ShotSound then g_Sound_PlayExAt(snd, wx, wy);
681 if Projectile then Result := LastShotID;
682 end;
685 procedure MakeShot (var Trigger: TTrigger; wx, wy, dx, dy: Integer; TargetUID: Word);
686 begin
687 with Trigger do
688 begin
689 if (tgcAmmo = 0) or ((tgcAmmo > 0) and (ShotAmmoCount > 0)) then
690 begin
691 if (trigPanelGUID <> -1) and (ShotPanelTime = 0) then
692 begin
693 g_Map_SwitchTextureGUID(ShotPanelType, trigPanelGUID);
694 ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
695 end;
697 if (tgcSight > 0) then ShotSightTimeout := 180; // ~= 5 ñåêóíä
699 if (ShotAmmoCount > 0) then Dec(ShotAmmoCount);
701 dx += Random(tgcAccuracy)-Random(tgcAccuracy);
702 dy += Random(tgcAccuracy)-Random(tgcAccuracy);
704 tr_SpawnShot(tgcShotType, wx, wy, dx, dy, not tgcQuiet, TargetUID);
705 end
706 else
707 begin
708 if (tgcReload > 0) and (ShotReloadTime = 0) then
709 begin
710 ShotReloadTime := tgcReload; // òèêîâ íà ïåðåçàðÿäêó ïóøêè
711 end;
712 end;
713 end;
714 end;
717 procedure tr_MakeEffect (X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
718 var
719 FramesID: DWORD;
720 Anim: TAnimation;
721 begin
722 if T = TRIGGER_EFFECT_PARTICLE then
723 begin
724 case ST of
725 TRIGGER_EFFECT_SLIQUID:
726 begin
727 if (CR = 255) and (CG = 0) and (CB = 0) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 1, 0, 0, 0)
728 else if (CR = 0) and (CG = 255) and (CB = 0) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 2, 0, 0, 0)
729 else if (CR = 0) and (CG = 0) and (CB = 255) then g_GFX_SimpleWater(X, Y, 1, VX, VY, 3, 0, 0, 0)
730 else g_GFX_SimpleWater(X, Y, 1, VX, VY, 0, 0, 0, 0);
731 end;
732 TRIGGER_EFFECT_LLIQUID: g_GFX_SimpleWater(X, Y, 1, VX, VY, 4, CR, CG, CB);
733 TRIGGER_EFFECT_DLIQUID: g_GFX_SimpleWater(X, Y, 1, VX, VY, 5, CR, CG, CB);
734 TRIGGER_EFFECT_BLOOD: g_GFX_Blood(X, Y, 1, VX, VY, 0, 0, CR, CG, CB);
735 TRIGGER_EFFECT_SPARK: g_GFX_Spark(X, Y, 1, GetAngle2(VX, VY), 0, 0);
736 TRIGGER_EFFECT_BUBBLE: g_GFX_Bubbles(X, Y, 1, 0, 0);
737 end;
738 end;
740 if T = TRIGGER_EFFECT_ANIMATION then
741 begin
742 case ST of
743 EFFECT_TELEPORT: begin
744 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
745 begin
746 Anim := TAnimation.Create(FramesID, False, 3);
747 if not Silent then g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
748 g_GFX_OnceAnim(X-32, Y-32, Anim);
749 Anim.Free();
750 end;
751 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X, Y, Byte(not Silent), NET_GFX_TELE);
752 end;
753 EFFECT_RESPAWN: begin
754 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
755 begin
756 Anim := TAnimation.Create(FramesID, False, 4);
757 if not Silent then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
758 g_GFX_OnceAnim(X-16, Y-16, Anim);
759 Anim.Free();
760 end;
761 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X-16, Y-16, Byte(not Silent), NET_GFX_RESPAWN);
762 end;
763 EFFECT_FIRE: begin
764 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
765 begin
766 Anim := TAnimation.Create(FramesID, False, 4);
767 if not Silent then g_Sound_PlayExAt('SOUND_FIRE', X, Y);
768 g_GFX_OnceAnim(X-32, Y-128, Anim);
769 Anim.Free();
770 end;
771 if Send and g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(X-32, Y-128, Byte(not Silent), NET_GFX_FIRE);
772 end;
773 end;
774 end;
775 end;
778 function tr_Teleport (ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
779 var
780 p: TPlayer;
781 m: TMonster;
782 begin
783 Result := False;
784 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
785 case g_GetUIDType(ActivateUID) of
786 UID_PLAYER:
787 begin
788 p := g_Player_Get(ActivateUID);
789 if p = nil then Exit;
790 if D2D then
791 begin
792 if p.TeleportTo(TX-(p.Obj.Rect.Width div 2), TY-p.Obj.Rect.Height, Silent, TDir) then result := true;
793 end
794 else
795 begin
796 if p.TeleportTo(TX, TY, Silent, TDir) then result := true;
797 end;
798 end;
799 UID_MONSTER:
800 begin
801 m := g_Monsters_ByUID(ActivateUID);
802 if m = nil then Exit;
803 if D2D then
804 begin
805 if m.TeleportTo(TX-(m.Obj.Rect.Width div 2), TY-m.Obj.Rect.Height, Silent, TDir) then result := true;
806 end
807 else
808 begin
809 if m.TeleportTo(TX, TY, Silent, TDir) then result := true;
810 end;
811 end;
812 end;
813 end;
816 function tr_Push (ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
817 var
818 p: TPlayer;
819 m: TMonster;
820 begin
821 result := true;
822 if (ActivateUID < 0) or (ActivateUID > $FFFF) then exit;
823 case g_GetUIDType(ActivateUID) of
824 UID_PLAYER:
825 begin
826 p := g_Player_Get(ActivateUID);
827 if p = nil then Exit;
829 if ResetVel then
830 begin
831 p.GameVelX := 0;
832 p.GameVelY := 0;
833 p.GameAccelX := 0;
834 p.GameAccelY := 0;
835 end;
837 p.Push(VX, VY);
838 end;
840 UID_MONSTER:
841 begin
842 m := g_Monsters_ByUID(ActivateUID);
843 if m = nil then Exit;
845 if ResetVel then
846 begin
847 m.GameVelX := 0;
848 m.GameVelY := 0;
849 m.GameAccelX := 0;
850 m.GameAccelY := 0;
851 end;
853 m.Push(VX, VY);
854 end;
855 end;
856 end;
859 function tr_Message (MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
860 var
861 msg: string;
862 p: TPlayer;
863 i: Integer;
864 begin
865 Result := True;
866 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
867 msg := b_Text_Format(MText);
868 case MSendTo of
869 TRIGGER_MESSAGE_DEST_ME: // activator
870 begin
871 if g_GetUIDType(ActivateUID) = UID_PLAYER then
872 begin
873 if g_Game_IsWatchedPlayer(ActivateUID) then
874 begin
875 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
876 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
877 end
878 else
879 begin
880 p := g_Player_Get(ActivateUID);
881 if g_Game_IsNet and (p.FClientID >= 0) then
882 begin
883 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, p.FClientID)
884 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, p.FClientID);
885 end;
886 end;
887 end;
888 end;
890 TRIGGER_MESSAGE_DEST_MY_TEAM: // activator's team
891 begin
892 if g_GetUIDType(ActivateUID) = UID_PLAYER then
893 begin
894 p := g_Player_Get(ActivateUID);
895 if g_Game_IsWatchedTeam(p.Team) then
896 begin
897 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
898 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
899 end;
901 if g_Game_IsNet then
902 begin
903 for i := Low(gPlayers) to High(gPlayers) do
904 begin
905 if (gPlayers[i].Team = p.Team) and (gPlayers[i].FClientID >= 0) then
906 begin
907 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
908 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
909 end;
910 end;
911 end;
912 end;
913 end;
915 TRIGGER_MESSAGE_DEST_ENEMY_TEAM: // activator's enemy team
916 begin
917 if g_GetUIDType(ActivateUID) = UID_PLAYER then
918 begin
919 p := g_Player_Get(ActivateUID);
920 if g_Game_IsWatchedTeam(p.Team) then
921 begin
922 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
923 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
924 end;
926 if g_Game_IsNet then
927 begin
928 for i := Low(gPlayers) to High(gPlayers) do
929 begin
930 if (gPlayers[i].Team <> p.Team) and (gPlayers[i].FClientID >= 0) then
931 begin
932 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
933 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
934 end;
935 end;
936 end;
937 end;
938 end;
940 TRIGGER_MESSAGE_DEST_RED_TEAM: // red team
941 begin
942 if g_Game_IsWatchedTeam(TEAM_RED) then
943 begin
944 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
945 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
946 end;
948 if g_Game_IsNet then
949 begin
950 for i := Low(gPlayers) to High(gPlayers) do
951 begin
952 if (gPlayers[i].Team = TEAM_RED) and (gPlayers[i].FClientID >= 0) then
953 begin
954 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
955 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
956 end;
957 end;
958 end;
959 end;
961 TRIGGER_MESSAGE_DEST_BLUE_TEAM: // blue team
962 begin
963 if g_Game_IsWatchedTeam(TEAM_BLUE) then
964 begin
965 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
966 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
967 end;
969 if g_Game_IsNet then
970 begin
971 for i := Low(gPlayers) to High(gPlayers) do
972 begin
973 if (gPlayers[i].Team = TEAM_BLUE) and (gPlayers[i].FClientID >= 0) then
974 begin
975 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
976 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
977 end;
978 end;
979 end;
980 end;
982 TRIGGER_MESSAGE_DEST_EVERYONE: // everyone
983 begin
984 if MKind = TRIGGER_MESSAGE_KIND_CHAT then g_Console_Add(msg, True)
985 else if MKind = TRIGGER_MESSAGE_KIND_GAME then g_Game_Message(msg, MTime);
987 if g_Game_IsNet then
988 begin
989 if MKind = TRIGGER_MESSAGE_KIND_CHAT then MH_SEND_Chat(msg, NET_CHAT_SYSTEM)
990 else if MKind = TRIGGER_MESSAGE_KIND_GAME then MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg);
991 end;
992 end;
993 end;
994 end;
997 function tr_ShotAimCheck (var Trigger: TTrigger; Obj: PObj): Boolean;
998 begin
999 result := false;
1000 with Trigger do
1001 begin
1002 if TriggerType <> TRIGGER_SHOT then Exit;
1003 result := (tgcAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
1004 or g_Obj_Collide(X, Y, Width, Height, Obj);
1005 if result and (tgcAim and TRIGGER_SHOT_AIM_TRACE > 0) then
1006 begin
1007 result := g_TraceVector(tgcTX, tgcTY,
1008 Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
1009 Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
1010 end;
1011 end;
1012 end;
1015 function ActivateTrigger (var Trigger: TTrigger; actType: Byte): Boolean;
1016 var
1017 animonce: Boolean;
1018 p: TPlayer;
1019 m: TMonster;
1020 pan: TPanel;
1021 idx, k, wx, wy, xd, yd: Integer;
1022 iid: LongWord;
1023 coolDown: Boolean;
1024 pAngle: Real;
1025 FramesID: DWORD;
1026 Anim: TAnimation;
1027 UIDType: Byte;
1028 TargetUID: Word;
1029 it: PItem;
1030 mon: TMonster;
1032 function monsShotTarget (mon: TMonster): Boolean;
1033 begin
1034 result := false; // don't stop
1035 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1036 begin
1037 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1038 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1039 TargetUID := mon.UID;
1040 result := true; // stop
1041 end;
1042 end;
1044 function monsShotTargetMonPlr (mon: TMonster): Boolean;
1045 begin
1046 result := false; // don't stop
1047 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1048 begin
1049 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1050 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1051 TargetUID := mon.UID;
1052 result := true; // stop
1053 end;
1054 end;
1056 function monShotTargetPlrMon (mon: TMonster): Boolean;
1057 begin
1058 result := false; // don't stop
1059 if mon.alive and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1060 begin
1061 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1062 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1063 TargetUID := mon.UID;
1064 result := true; // stop
1065 end;
1066 end;
1068 begin
1069 result := false;
1070 if g_Game_IsClient then exit;
1072 if not Trigger.Enabled then exit;
1073 if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then exit;
1074 if gLMSRespawn = LMS_RESPAWN_WARMUP then exit;
1076 animonce := False;
1078 coolDown := (actType <> 0);
1080 with Trigger do
1081 begin
1082 case TriggerType of
1083 TRIGGER_EXIT:
1084 begin
1085 g_Sound_PlayEx('SOUND_GAME_SWITCH0');
1086 if g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH0');
1087 gExitByTrigger := True;
1088 g_Game_ExitLevel(tgcMap);
1089 TimeOut := 18;
1090 Result := True;
1092 Exit;
1093 end;
1095 TRIGGER_TELEPORT:
1096 begin
1097 Result := tr_Teleport(ActivateUID,
1098 tgcTarget.X, tgcTarget.Y,
1099 tgcDirection, tgcSilent,
1100 tgcD2d);
1101 TimeOut := 0;
1102 end;
1104 TRIGGER_OPENDOOR:
1105 begin
1106 Result := tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
1107 TimeOut := 0;
1108 end;
1110 TRIGGER_CLOSEDOOR:
1111 begin
1112 Result := tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d);
1113 TimeOut := 0;
1114 end;
1116 TRIGGER_DOOR, TRIGGER_DOOR5:
1117 begin
1118 pan := g_Map_PanelByGUID(trigPanelGUID);
1119 if (pan <> nil) and pan.isGWall then
1120 begin
1121 if gWalls[{trigPanelID}pan.arrIdx].Enabled then
1122 begin
1123 result := tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
1124 if (TriggerType = TRIGGER_DOOR5) then DoorTime := 180;
1125 end
1126 else
1127 begin
1128 result := tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d);
1129 end;
1131 if result then TimeOut := 18;
1132 end;
1133 end;
1135 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1136 begin
1137 tr_CloseTrap(trigPanelGUID, tgcSilent, tgcD2d);
1139 if TriggerType = TRIGGER_TRAP then
1140 begin
1141 DoorTime := 40;
1142 TimeOut := 76;
1143 end
1144 else
1145 begin
1146 DoorTime := -1;
1147 TimeOut := 0;
1148 end;
1150 Result := True;
1151 end;
1153 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
1154 begin
1155 PressCount += 1;
1156 if PressTime = -1 then PressTime := tgcWait;
1157 if coolDown then TimeOut := 18 else TimeOut := 0;
1158 Result := True;
1159 end;
1161 TRIGGER_SECRET:
1162 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1163 begin
1164 Enabled := False;
1165 Result := True;
1166 if gLMSRespawn = LMS_RESPAWN_NONE then
1167 begin
1168 g_Player_Get(ActivateUID).GetSecret();
1169 Inc(gCoopSecretsFound);
1170 if g_Game_IsNet then MH_SEND_GameStats();
1171 end;
1172 end;
1174 TRIGGER_LIFTUP:
1175 begin
1176 Result := tr_SetLift(trigPanelGUID, 0, tgcSilent, tgcD2d);
1177 TimeOut := 0;
1179 if (not tgcSilent) and Result then begin
1180 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1181 X + (Width div 2),
1182 Y + (Height div 2));
1183 if g_Game_IsServer and g_Game_IsNet then
1184 MH_SEND_Sound(X + (Width div 2),
1185 Y + (Height div 2),
1186 'SOUND_GAME_SWITCH0');
1187 end;
1188 end;
1190 TRIGGER_LIFTDOWN:
1191 begin
1192 Result := tr_SetLift(trigPanelGUID, 1, tgcSilent, tgcD2d);
1193 TimeOut := 0;
1195 if (not tgcSilent) and Result then begin
1196 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1197 X + (Width div 2),
1198 Y + (Height div 2));
1199 if g_Game_IsServer and g_Game_IsNet then
1200 MH_SEND_Sound(X + (Width div 2),
1201 Y + (Height div 2),
1202 'SOUND_GAME_SWITCH0');
1203 end;
1204 end;
1206 TRIGGER_LIFT:
1207 begin
1208 Result := tr_SetLift(trigPanelGUID, 3, tgcSilent, tgcD2d);
1210 if Result then
1211 begin
1212 TimeOut := 18;
1214 if (not tgcSilent) and Result then begin
1215 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1216 X + (Width div 2),
1217 Y + (Height div 2));
1218 if g_Game_IsServer and g_Game_IsNet then
1219 MH_SEND_Sound(X + (Width div 2),
1220 Y + (Height div 2),
1221 'SOUND_GAME_SWITCH0');
1222 end;
1223 end;
1224 end;
1226 TRIGGER_TEXTURE:
1227 begin
1228 if tgcActivateOnce then
1229 begin
1230 Enabled := False;
1231 TriggerType := TRIGGER_NONE;
1232 end
1233 else
1234 if coolDown then
1235 TimeOut := 6
1236 else
1237 TimeOut := 0;
1239 animonce := tgcAnimateOnce;
1240 Result := True;
1241 end;
1243 TRIGGER_SOUND:
1244 begin
1245 if Sound <> nil then
1246 begin
1247 if tgcSoundSwitch and Sound.IsPlaying() then
1248 begin // Íóæíî âûêëþ÷èòü, åñëè èãðàë
1249 Sound.Stop();
1250 SoundPlayCount := 0;
1251 Result := True;
1252 end
1253 else // (not Data.SoundSwitch) or (not Sound.IsPlaying())
1254 if (tgcPlayCount > 0) or (not Sound.IsPlaying()) then
1255 begin
1256 if tgcPlayCount > 0 then
1257 SoundPlayCount := tgcPlayCount
1258 else // 0 - èãðàåì áåñêîíå÷íî
1259 SoundPlayCount := 1;
1260 Result := True;
1261 end;
1262 if g_Game_IsNet then MH_SEND_TriggerSound(Trigger);
1263 end;
1264 end;
1266 TRIGGER_SPAWNMONSTER:
1267 if (tgcSpawnMonsType in [MONSTER_DEMON..MONSTER_MAN]) then
1268 begin
1269 Result := False;
1270 if (tgcDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1271 begin
1272 AutoSpawn := not AutoSpawn;
1273 SpawnCooldown := 0;
1274 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1275 Result := True;
1276 end;
1278 if ((tgcDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1279 or ((tgcDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1280 for k := 1 to tgcMonsCount do
1281 begin
1282 if (actType = ACTIVATE_CUSTOM) and (tgcDelay > 0) then
1283 SpawnCooldown := tgcDelay;
1284 if (tgcMax > 0) and (SpawnedCount >= tgcMax) then
1285 Break;
1287 mon := g_Monsters_Create(tgcSpawnMonsType,
1288 tgcTX, tgcTY,
1289 TDirection(tgcDirection), True);
1291 Result := True;
1293 // Çäîðîâüå:
1294 if (tgcHealth > 0) then
1295 mon.SetHealth(tgcHealth);
1296 // Óñòàíàâëèâàåì ïîâåäåíèå:
1297 mon.MonsterBehaviour := tgcBehaviour;
1298 mon.FNoRespawn := True;
1299 if g_Game_IsNet then
1300 MH_SEND_MonsterSpawn(mon.UID);
1301 // Èäåì èñêàòü öåëü, åñëè íàäî:
1302 if tgcActive then
1303 mon.WakeUp();
1305 if tgcSpawnMonsType <> MONSTER_BARREL then Inc(gTotalMonsters);
1307 if g_Game_IsNet then
1308 begin
1309 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
1310 gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
1311 end;
1313 if tgcMax > 0 then
1314 begin
1315 mon.SpawnTrigger := ID;
1316 Inc(SpawnedCount);
1317 end;
1319 case tgcEffect of
1320 EFFECT_TELEPORT: begin
1321 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1322 begin
1323 Anim := TAnimation.Create(FramesID, False, 3);
1324 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', tgcTX, tgcTY);
1325 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1326 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, Anim);
1327 Anim.Free();
1328 end;
1329 if g_Game_IsServer and g_Game_IsNet then
1330 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1331 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
1332 NET_GFX_TELE);
1333 end;
1334 EFFECT_RESPAWN: begin
1335 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1336 begin
1337 Anim := TAnimation.Create(FramesID, False, 4);
1338 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', tgcTX, tgcTY);
1339 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1340 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, Anim);
1341 Anim.Free();
1342 end;
1343 if g_Game_IsServer and g_Game_IsNet then
1344 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1345 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
1346 NET_GFX_RESPAWN);
1347 end;
1348 EFFECT_FIRE: begin
1349 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1350 begin
1351 Anim := TAnimation.Create(FramesID, False, 4);
1352 g_Sound_PlayExAt('SOUND_FIRE', tgcTX, tgcTY);
1353 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1354 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, Anim);
1355 Anim.Free();
1356 end;
1357 if g_Game_IsServer and g_Game_IsNet then
1358 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1359 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, 1,
1360 NET_GFX_FIRE);
1361 end;
1362 end;
1363 end;
1364 if g_Game_IsNet then
1365 begin
1366 MH_SEND_GameStats();
1367 MH_SEND_CoopStats();
1368 end;
1370 if coolDown then
1371 TimeOut := 18
1372 else
1373 TimeOut := 0;
1374 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1375 if actType = ACTIVATE_CUSTOM then
1376 Result := False;
1377 end;
1379 TRIGGER_SPAWNITEM:
1380 if (tgcSpawnItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX]) then
1381 begin
1382 Result := False;
1383 if (tgcDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1384 begin
1385 AutoSpawn := not AutoSpawn;
1386 SpawnCooldown := 0;
1387 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1388 Result := True;
1389 end;
1391 if ((tgcDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1392 or ((tgcDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1393 if (not tgcDmonly) or
1394 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1395 for k := 1 to tgcItemCount do
1396 begin
1397 if (actType = ACTIVATE_CUSTOM) and (tgcDelay > 0) then
1398 SpawnCooldown := tgcDelay;
1399 if (tgcMax > 0) and (SpawnedCount >= tgcMax) then
1400 Break;
1402 iid := g_Items_Create(tgcTX, tgcTY,
1403 tgcSpawnItemType, tgcGravity, False, True);
1405 Result := True;
1407 if tgcMax > 0 then
1408 begin
1409 it := g_Items_ByIdx(iid);
1410 it.SpawnTrigger := ID;
1411 Inc(SpawnedCount);
1412 end;
1414 case tgcEffect of
1415 EFFECT_TELEPORT: begin
1416 it := g_Items_ByIdx(iid);
1417 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1418 begin
1419 Anim := TAnimation.Create(FramesID, False, 3);
1420 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', tgcTX, tgcTY);
1421 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1422 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
1423 Anim.Free();
1424 end;
1425 if g_Game_IsServer and g_Game_IsNet then
1426 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1427 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
1428 NET_GFX_TELE);
1429 end;
1430 EFFECT_RESPAWN: begin
1431 it := g_Items_ByIdx(iid);
1432 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1433 begin
1434 Anim := TAnimation.Create(FramesID, False, 4);
1435 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', tgcTX, tgcTY);
1436 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1437 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
1438 Anim.Free();
1439 end;
1440 if g_Game_IsServer and g_Game_IsNet then
1441 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1442 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
1443 NET_GFX_RESPAWN);
1444 end;
1445 EFFECT_FIRE: begin
1446 it := g_Items_ByIdx(iid);
1447 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1448 begin
1449 Anim := TAnimation.Create(FramesID, False, 4);
1450 g_Sound_PlayExAt('SOUND_FIRE', tgcTX, tgcTY);
1451 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1452 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
1453 Anim.Free();
1454 end;
1455 if g_Game_IsServer and g_Game_IsNet then
1456 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1457 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
1458 NET_GFX_FIRE);
1459 end;
1460 end;
1462 if g_Game_IsNet then
1463 MH_SEND_ItemSpawn(True, iid);
1464 end;
1466 if coolDown then
1467 TimeOut := 18
1468 else
1469 TimeOut := 0;
1470 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1471 if actType = ACTIVATE_CUSTOM then
1472 Result := False;
1473 end;
1475 TRIGGER_MUSIC:
1476 begin
1477 // Ìåíÿåì ìóçûêó, åñëè åñòü íà ÷òî:
1478 if (Trigger.tgcMusicName <> '') then
1479 begin
1480 gMusic.SetByName(Trigger.tgcMusicName);
1481 gMusic.SpecPause := True;
1482 gMusic.Play();
1483 end;
1485 case Trigger.tgcMusicAction of
1486 TRIGGER_MUSIC_ACTION_STOP: // Âûêëþ÷èòü
1487 gMusic.SpecPause := True; // Ïàóçà
1488 TRIGGER_MUSIC_ACTION_PLAY: // Âêëþ÷èòü
1489 if gMusic.SpecPause then // Áûëà íà ïàóçå => èãðàòü
1490 gMusic.SpecPause := False
1491 else // Èãðàëà => ñíà÷àëà
1492 gMusic.SetPosition(0);
1493 end;
1495 if coolDown then
1496 TimeOut := 36
1497 else
1498 TimeOut := 0;
1499 Result := True;
1500 if g_Game_IsNet then MH_SEND_TriggerMusic;
1501 end;
1503 TRIGGER_PUSH:
1504 begin
1505 pAngle := -DegToRad(tgcAngle);
1506 Result := tr_Push(ActivateUID,
1507 Floor(Cos(pAngle)*tgcForce),
1508 Floor(Sin(pAngle)*tgcForce),
1509 tgcResetVelocity);
1510 TimeOut := 0;
1511 end;
1513 TRIGGER_SCORE:
1514 begin
1515 Result := False;
1516 // Ïðèáàâèòü èëè îòíÿòü î÷êî
1517 if (tgcScoreAction in [TRIGGER_SCORE_ACTION_ADD, TRIGGER_SCORE_ACTION_SUB]) and (tgcScoreCount > 0) then
1518 begin
1519 // Ñâîåé èëè ÷óæîé êîìàíäå
1520 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1521 begin
1522 p := g_Player_Get(ActivateUID);
1523 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED))
1524 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1525 begin
1526 Inc(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Scores
1528 if tgcScoreCon then
1529 begin
1530 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1531 begin
1532 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1533 if g_Game_IsServer and g_Game_IsNet then
1534 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+r');
1535 end else
1536 begin
1537 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1538 if g_Game_IsServer and g_Game_IsNet then
1539 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+re');
1540 end;
1541 end;
1543 if tgcScoreMsg then
1544 begin
1545 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1546 if g_Game_IsServer and g_Game_IsNet then
1547 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1548 end;
1549 end;
1550 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED))
1551 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1552 begin
1553 Dec(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Fouls
1555 if tgcScoreCon then
1556 begin
1557 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1558 begin
1559 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1560 if g_Game_IsServer and g_Game_IsNet then
1561 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-r');
1562 end else
1563 begin
1564 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1565 if g_Game_IsServer and g_Game_IsNet then
1566 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-re');
1567 end;
1568 end;
1570 if tgcScoreMsg then
1571 begin
1572 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1573 if g_Game_IsServer and g_Game_IsNet then
1574 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1575 end;
1576 end;
1577 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE))
1578 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1579 begin
1580 Inc(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Scores
1582 if tgcScoreCon then
1583 begin
1584 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1585 begin
1586 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1587 if g_Game_IsServer and g_Game_IsNet then
1588 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+b');
1589 end else
1590 begin
1591 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1592 if g_Game_IsServer and g_Game_IsNet then
1593 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '+be');
1594 end;
1595 end;
1597 if tgcScoreMsg then
1598 begin
1599 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1600 if g_Game_IsServer and g_Game_IsNet then
1601 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1602 end;
1603 end;
1604 if ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE))
1605 or ((tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1606 begin
1607 Dec(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Fouls
1609 if tgcScoreCon then
1610 begin
1611 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1612 begin
1613 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1614 if g_Game_IsServer and g_Game_IsNet then
1615 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-b');
1616 end else
1617 begin
1618 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, tgcScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1619 if g_Game_IsServer and g_Game_IsNet then
1620 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (tgcScoreCount shl 16), '-be');
1621 end;
1622 end;
1624 if tgcScoreMsg then
1625 begin
1626 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1627 if g_Game_IsServer and g_Game_IsNet then
1628 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1629 end;
1630 end;
1631 Result := (p.Team = TEAM_RED) or (p.Team = TEAM_BLUE);
1632 end;
1633 // Êàêîé-òî êîíêðåòíîé êîìàíäå
1634 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_RED, TRIGGER_SCORE_TEAM_FORCE_BLUE] then
1635 begin
1636 if (tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then
1637 begin
1638 Inc(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Scores
1640 if tgcScoreCon then
1641 begin
1642 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_RED], tgcScoreCount]), True);
1643 if g_Game_IsServer and g_Game_IsNet then
1644 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '+tr');
1645 end;
1647 if tgcScoreMsg then
1648 begin
1649 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1650 if g_Game_IsServer and g_Game_IsNet then
1651 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1652 end;
1653 end;
1654 if (tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then
1655 begin
1656 Dec(gTeamStat[TEAM_RED].Goals, tgcScoreCount); // Red Fouls
1658 if tgcScoreCon then
1659 begin
1660 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_RED], tgcScoreCount]), True);
1661 if g_Game_IsServer and g_Game_IsNet then
1662 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '-tr');
1663 end;
1665 if tgcScoreMsg then
1666 begin
1667 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1668 if g_Game_IsServer and g_Game_IsNet then
1669 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1670 end;
1671 end;
1672 if (tgcScoreAction = TRIGGER_SCORE_ACTION_ADD) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then
1673 begin
1674 Inc(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Scores
1676 if tgcScoreCon then
1677 begin
1678 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_BLUE], tgcScoreCount]), True);
1679 if g_Game_IsServer and g_Game_IsNet then
1680 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '+tb');
1681 end;
1683 if tgcScoreMsg then
1684 begin
1685 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1686 if g_Game_IsServer and g_Game_IsNet then
1687 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1688 end;
1689 end;
1690 if (tgcScoreAction = TRIGGER_SCORE_ACTION_SUB) and (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then
1691 begin
1692 Dec(gTeamStat[TEAM_BLUE].Goals, tgcScoreCount); // Blue Fouls
1694 if tgcScoreCon then
1695 begin
1696 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_BLUE], tgcScoreCount]), True);
1697 if g_Game_IsServer and g_Game_IsNet then
1698 MH_SEND_GameEvent(NET_EV_SCORE, tgcScoreCount shl 16, '-tb');
1699 end;
1701 if tgcScoreMsg then
1702 begin
1703 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1704 if g_Game_IsServer and g_Game_IsNet then
1705 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1706 end;
1707 end;
1708 Result := True;
1709 end;
1710 end;
1711 // Âûèãðûø
1712 if (tgcScoreAction = TRIGGER_SCORE_ACTION_WIN) and (gGameSettings.GoalLimit > 0) then
1713 begin
1714 // Ñâîåé èëè ÷óæîé êîìàíäû
1715 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1716 begin
1717 p := g_Player_Get(ActivateUID);
1718 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED)) // Red Wins
1719 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1720 begin
1721 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1722 begin
1723 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1725 if tgcScoreCon then
1726 begin
1727 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1728 begin
1729 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1730 if g_Game_IsServer and g_Game_IsNet then
1731 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1732 end else
1733 begin
1734 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1735 if g_Game_IsServer and g_Game_IsNet then
1736 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1737 end;
1738 end;
1740 Result := True;
1741 end;
1742 end;
1743 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE)) // Blue Wins
1744 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1745 begin
1746 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1747 begin
1748 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1750 if tgcScoreCon then
1751 begin
1752 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) then
1753 begin
1754 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1755 if g_Game_IsServer and g_Game_IsNet then
1756 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1757 end else
1758 begin
1759 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1760 if g_Game_IsServer and g_Game_IsNet then
1761 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1762 end;
1763 end;
1765 Result := True;
1766 end;
1767 end;
1768 end;
1769 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1770 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_RED, TRIGGER_SCORE_TEAM_FORCE_BLUE] then
1771 begin
1772 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then // Red Wins
1773 begin
1774 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1775 begin
1776 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1777 Result := True;
1778 end;
1779 end;
1780 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then // Blue Wins
1781 begin
1782 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1783 begin
1784 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1785 Result := True;
1786 end;
1787 end;
1788 end;
1789 end;
1790 // Ïðîèãðûø
1791 if (tgcScoreAction = TRIGGER_SCORE_ACTION_LOOSE) and (gGameSettings.GoalLimit > 0) then
1792 begin
1793 // Ñâîåé èëè ÷óæîé êîìàíäû
1794 if (tgcScoreTeam in [TRIGGER_SCORE_TEAM_MINE_RED, TRIGGER_SCORE_TEAM_MINE_BLUE]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1795 begin
1796 p := g_Player_Get(ActivateUID);
1797 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_BLUE)) // Red Wins
1798 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_RED)) then
1799 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1800 begin
1801 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1803 if tgcScoreCon then
1804 if tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED then
1805 begin
1806 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1807 if g_Game_IsServer and g_Game_IsNet then
1808 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1809 end else
1810 begin
1811 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1812 if g_Game_IsServer and g_Game_IsNet then
1813 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1814 end;
1816 Result := True;
1817 end;
1818 if ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED) and (p.Team = TEAM_RED)) // Blue Wins
1819 or ((tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_BLUE) and (p.Team = TEAM_BLUE)) then
1820 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1821 begin
1822 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1824 if tgcScoreCon then
1825 if tgcScoreTeam = TRIGGER_SCORE_TEAM_MINE_RED then
1826 begin
1827 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1828 if g_Game_IsServer and g_Game_IsNet then
1829 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1830 end else
1831 begin
1832 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1833 if g_Game_IsServer and g_Game_IsNet then
1834 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1835 end;
1837 Result := True;
1838 end;
1839 end;
1840 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1841 if tgcScoreTeam in [TRIGGER_SCORE_TEAM_FORCE_BLUE, TRIGGER_SCORE_TEAM_FORCE_RED] then
1842 begin
1843 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_BLUE) then // Red Wins
1844 begin
1845 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1846 begin
1847 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1848 Result := True;
1849 end;
1850 end;
1851 if (tgcScoreTeam = TRIGGER_SCORE_TEAM_FORCE_RED) then // Blue Wins
1852 begin
1853 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1854 begin
1855 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1856 Result := True;
1857 end;
1858 end;
1859 end;
1860 end;
1861 if Result then begin
1862 if coolDown then
1863 TimeOut := 18
1864 else
1865 TimeOut := 0;
1866 if g_Game_IsServer and g_Game_IsNet then
1867 MH_SEND_GameStats;
1868 end;
1869 end;
1871 TRIGGER_MESSAGE:
1872 begin
1873 Result := tr_Message(tgcKind, tgcText,
1874 tgcMsgDest, tgcMsgTime,
1875 ActivateUID);
1876 TimeOut := 18;
1877 end;
1879 TRIGGER_DAMAGE, TRIGGER_HEALTH:
1880 begin
1881 Result := False;
1882 UIDType := g_GetUIDType(ActivateUID);
1883 if (UIDType = UID_PLAYER) or (UIDType = UID_MONSTER) then
1884 begin
1885 Result := True;
1886 k := -1;
1887 if coolDown then
1888 begin
1889 // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
1890 for idx := 0 to High(Activators) do
1891 if Activators[idx].UID = ActivateUID then
1892 begin
1893 k := idx;
1894 Break;
1895 end;
1896 if k = -1 then
1897 begin // Âèäèì åãî âïåðâûå
1898 // Çàïîìèíàåì åãî
1899 SetLength(Activators, Length(Activators) + 1);
1900 k := High(Activators);
1901 Activators[k].UID := ActivateUID;
1902 end else
1903 begin // Óæå âèäåëè åãî
1904 // Åñëè èíòåðâàë îòêëþ÷¸í, íî îí âñ¸ åù¸ â çîíå ïîðàæåíèÿ, äà¸ì åìó âðåìÿ
1905 if (tgcInterval = 0) and (Activators[k].TimeOut > 0) then
1906 Activators[k].TimeOut := 65535;
1907 // Òàéìàóò ïðîø¸ë - ðàáîòàåì
1908 Result := Activators[k].TimeOut = 0;
1909 end;
1910 end;
1912 if Result then
1913 begin
1914 case UIDType of
1915 UID_PLAYER:
1916 begin
1917 p := g_Player_Get(ActivateUID);
1918 if p = nil then
1919 Exit;
1921 // Íàíîñèì óðîí èãðîêó
1922 if (TriggerType = TRIGGER_DAMAGE) and (tgcAmount > 0) then
1923 p.Damage(tgcAmount, 0, 0, 0, HIT_SOME);
1925 // Ëå÷èì èãðîêà
1926 if (TriggerType = TRIGGER_HEALTH) and (tgcAmount > 0) then
1927 if p.Heal(tgcAmount, not tgcHealMax) and (not tgcSilent) then
1928 begin
1929 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', p.Obj.X, p.Obj.Y);
1930 if g_Game_IsServer and g_Game_IsNet then
1931 MH_SEND_Sound(p.Obj.X, p.Obj.Y, 'SOUND_ITEM_GETITEM');
1932 end;
1933 end;
1935 UID_MONSTER:
1936 begin
1937 m := g_Monsters_ByUID(ActivateUID);
1938 if m = nil then
1939 Exit;
1941 // Íàíîñèì óðîí ìîíñòðó
1942 if (TriggerType = TRIGGER_DAMAGE) and (tgcAmount > 0) then
1943 m.Damage(tgcAmount, 0, 0, 0, HIT_SOME);
1945 // Ëå÷èì ìîíñòðà
1946 if (TriggerType = TRIGGER_HEALTH) and (tgcAmount > 0) then
1947 if m.Heal(tgcAmount) and (not tgcSilent) then
1948 begin
1949 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', m.Obj.X, m.Obj.Y);
1950 if g_Game_IsServer and g_Game_IsNet then
1951 MH_SEND_Sound(m.Obj.X, m.Obj.Y, 'SOUND_ITEM_GETITEM');
1952 end;
1953 end;
1954 end;
1955 // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
1956 idx := tgcInterval;
1957 if coolDown then
1958 if idx > 0 then
1959 Activators[k].TimeOut := idx
1960 else
1961 Activators[k].TimeOut := 65535;
1962 end;
1963 end;
1964 TimeOut := 0;
1965 end;
1967 TRIGGER_SHOT:
1968 begin
1969 if ShotSightTime > 0 then
1970 Exit;
1972 // put this at the beginning so it doesn't trigger itself
1973 TimeOut := tgcWait + 1;
1975 wx := tgcTX;
1976 wy := tgcTY;
1977 pAngle := -DegToRad(tgcAngle);
1978 xd := wx + Round(Cos(pAngle) * 32.0);
1979 yd := wy + Round(Sin(pAngle) * 32.0);
1980 TargetUID := 0;
1982 case tgcShotTarget of
1983 TRIGGER_SHOT_TARGET_MON: // monsters
1984 //TODO: accelerate this!
1985 g_Mons_ForEachAlive(monsShotTarget);
1987 TRIGGER_SHOT_TARGET_PLR: // players
1988 if gPlayers <> nil then
1989 for idx := Low(gPlayers) to High(gPlayers) do
1990 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
1991 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1992 begin
1993 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1994 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1995 TargetUID := gPlayers[idx].UID;
1996 break;
1997 end;
1999 TRIGGER_SHOT_TARGET_RED: // red team
2000 if gPlayers <> nil then
2001 for idx := Low(gPlayers) to High(gPlayers) do
2002 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2003 (gPlayers[idx].Team = TEAM_RED) and
2004 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2005 begin
2006 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2007 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2008 TargetUID := gPlayers[idx].UID;
2009 break;
2010 end;
2012 TRIGGER_SHOT_TARGET_BLUE: // blue team
2013 if gPlayers <> nil then
2014 for idx := Low(gPlayers) to High(gPlayers) do
2015 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2016 (gPlayers[idx].Team = TEAM_BLUE) and
2017 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2018 begin
2019 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2020 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2021 TargetUID := gPlayers[idx].UID;
2022 break;
2023 end;
2025 TRIGGER_SHOT_TARGET_MONPLR: // monsters then players
2026 begin
2027 //TODO: accelerate this!
2028 g_Mons_ForEachAlive(monsShotTargetMonPlr);
2030 if (TargetUID = 0) and (gPlayers <> nil) then
2031 for idx := Low(gPlayers) to High(gPlayers) do
2032 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2033 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2034 begin
2035 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2036 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2037 TargetUID := gPlayers[idx].UID;
2038 break;
2039 end;
2040 end;
2042 TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
2043 begin
2044 if gPlayers <> nil then
2045 for idx := Low(gPlayers) to High(gPlayers) do
2046 if (gPlayers[idx] <> nil) and gPlayers[idx].alive and
2047 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2048 begin
2049 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2050 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2051 TargetUID := gPlayers[idx].UID;
2052 break;
2053 end;
2054 if TargetUID = 0 then
2055 begin
2056 //TODO: accelerate this!
2057 g_Mons_ForEachAlive(monShotTargetPlrMon);
2058 end;
2059 end;
2061 else begin
2062 if (tgcShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
2063 (tgcShotType <> TRIGGER_SHOT_REV) then
2064 TargetUID := ActivateUID;
2065 end;
2066 end;
2068 if (tgcShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
2069 ((tgcShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
2070 begin
2071 Result := True;
2072 if (tgcSight = 0) or
2073 (tgcShotTarget = TRIGGER_SHOT_TARGET_NONE) or
2074 (TargetUID = ShotSightTarget) then
2075 MakeShot(Trigger, wx, wy, xd, yd, TargetUID)
2076 else
2077 begin
2078 ShotSightTime := tgcSight;
2079 ShotSightTargetN := TargetUID;
2080 if tgcShotType = TRIGGER_SHOT_BFG then
2081 begin
2082 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', wx, wy);
2083 if g_Game_IsNet and g_Game_IsServer then
2084 MH_SEND_Sound(wx, wy, 'SOUND_WEAPON_STARTFIREBFG');
2085 end;
2086 end;
2087 end;
2088 end;
2090 TRIGGER_EFFECT:
2091 begin
2092 idx := tgcFXCount;
2094 while idx > 0 do
2095 begin
2096 case tgcFXPos of
2097 TRIGGER_EFFECT_POS_CENTER:
2098 begin
2099 wx := X + Width div 2;
2100 wy := Y + Height div 2;
2101 end;
2102 TRIGGER_EFFECT_POS_AREA:
2103 begin
2104 wx := X + Random(Width);
2105 wy := Y + Random(Height);
2106 end;
2107 else begin
2108 wx := X + Width div 2;
2109 wy := Y + Height div 2;
2110 end;
2111 end;
2112 xd := tgcVelX;
2113 yd := tgcVelY;
2114 if tgcSpreadL > 0 then xd -= Random(tgcSpreadL+1);
2115 if tgcSpreadR > 0 then xd += Random(tgcSpreadR+1);
2116 if tgcSpreadU > 0 then yd -= Random(tgcSpreadU+1);
2117 if tgcSpreadD > 0 then yd += Random(tgcSpreadD+1);
2118 tr_MakeEffect(wx, wy, xd, yd,
2119 tgcFXType, tgcFXSubType,
2120 tgcFXRed, tgcFXGreen, tgcFXBlue, True, False);
2121 Dec(idx);
2122 end;
2123 TimeOut := tgcWait;
2124 end;
2125 end;
2126 end;
2128 if Result {and (Trigger.TexturePanel <> -1)} then
2129 begin
2130 g_Map_SwitchTextureGUID(Trigger.TexturePanelType, Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
2131 end;
2132 end;
2135 function g_Triggers_CreateWithMapIndex (Trigger: TTrigger; arridx, mapidx: Integer): DWORD;
2136 var
2137 triggers: TDynField;
2138 begin
2139 triggers := gCurrentMap['trigger'];
2140 if (triggers = nil) then raise Exception.Create('LOAD: map has no triggers');
2141 if (mapidx < 0) or (mapidx >= triggers.count) then raise Exception.Create('LOAD: invalid map trigger index');
2142 Trigger.trigDataRec := triggers.itemAt[mapidx];
2143 if (Trigger.trigDataRec = nil) then raise Exception.Create('LOAD: internal error in trigger loader');
2144 Trigger.mapId := Trigger.trigDataRec.id;
2145 Trigger.mapIndex := mapidx;
2146 if (Trigger.trigDataRec.trigRec <> nil) then
2147 begin
2148 Trigger.trigDataRec := Trigger.trigDataRec.trigRec.clone(nil);
2149 end
2150 else
2151 begin
2152 Trigger.trigDataRec := nil;
2153 end;
2154 result := g_Triggers_Create(Trigger, arridx);
2155 end;
2158 function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
2159 var
2160 find_id: DWORD;
2161 fn, mapw: AnsiString;
2162 f, olen: Integer;
2163 begin
2164 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà
2165 if (Trigger.TriggerType = TRIGGER_EXIT) and
2166 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2167 Trigger.TriggerType := TRIGGER_NONE;
2169 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð
2170 if (Trigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2171 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2172 (gGameSettings.GameType <> GT_SINGLE) then
2173 Trigger.TriggerType := TRIGGER_NONE;
2175 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå
2176 if Trigger.TriggerType = TRIGGER_SECRET then gSecretsCount += 1;
2178 if (forceInternalIndex < 0) then
2179 begin
2180 find_id := FindTrigger();
2181 end
2182 else
2183 begin
2184 olen := Length(gTriggers);
2185 if (forceInternalIndex >= olen) then
2186 begin
2187 SetLength(gTriggers, forceInternalIndex+1);
2188 for f := olen to High(gTriggers) do gTriggers[f].TriggerType := TRIGGER_NONE;
2189 end;
2190 find_id := DWORD(forceInternalIndex);
2191 end;
2192 gTriggers[find_id] := Trigger;
2194 with gTriggers[find_id] do
2195 begin
2196 ID := find_id;
2197 // if this type of trigger exists both on the client and on the server
2198 // use an uniform numeration
2199 if Trigger.TriggerType = TRIGGER_SOUND then
2200 begin
2201 Inc(gTriggerClientID);
2202 ClientID := gTriggerClientID;
2203 end
2204 else
2205 begin
2206 ClientID := 0;
2207 end;
2208 TimeOut := 0;
2209 ActivateUID := 0;
2210 PlayerCollide := False;
2211 DoorTime := -1;
2212 PressTime := -1;
2213 PressCount := 0;
2214 SoundPlayCount := 0;
2215 Sound := nil;
2216 AutoSpawn := False;
2217 SpawnCooldown := 0;
2218 SpawnedCount := 0;
2219 end;
2221 // update cached trigger variables
2222 trigUpdateCacheData(gTriggers[find_id], gTriggers[find_id].trigDataRec);
2224 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê"
2225 if (Trigger.TriggerType = TRIGGER_SOUND) and (Trigger.tgcSoundName <> '') then
2226 begin
2227 // Åùå íåò òàêîãî çâóêà
2228 if not g_Sound_Exists(Trigger.tgcSoundName) then
2229 begin
2230 fn := g_ExtractWadName(Trigger.tgcSoundName);
2231 if fn = '' then
2232 begin // Çâóê â ôàéëå ñ êàðòîé
2233 mapw := g_ExtractWadName(gMapInfo.Map);
2234 fn := mapw+':'+g_ExtractFilePathName(Trigger.tgcSoundName);
2235 end
2236 else // Çâóê â îòäåëüíîì ôàéëå
2237 begin
2238 fn := GameDir + '/wads/' + Trigger.tgcSoundName;
2239 end;
2241 if not g_Sound_CreateWADEx(Trigger.tgcSoundName, fn) then
2242 begin
2243 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.tgcSoundName]));
2244 end;
2245 end;
2247 // Ñîçäàåì îáúåêò çâóêà
2248 with gTriggers[find_id] do
2249 begin
2250 Sound := TPlayableSound.Create();
2251 if not Sound.SetByName(Trigger.tgcSoundName) then
2252 begin
2253 Sound.Free();
2254 Sound := nil;
2255 end;
2256 end;
2257 end;
2259 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà"
2260 if (Trigger.TriggerType = TRIGGER_MUSIC) and (Trigger.tgcMusicName <> '') then
2261 begin
2262 // Åùå íåò òàêîé ìóçûêè
2263 if not g_Sound_Exists(Trigger.tgcMusicName) then
2264 begin
2265 fn := g_ExtractWadName(Trigger.tgcMusicName);
2267 if fn = '' then
2268 begin // Ìóçûêà â ôàéëå ñ êàðòîé
2269 mapw := g_ExtractWadName(gMapInfo.Map);
2270 fn := mapw+':'+g_ExtractFilePathName(Trigger.tgcMusicName);
2271 end
2272 else // Ìóçûêà â ôàéëå ñ êàðòîé
2273 begin
2274 fn := GameDir+'/wads/'+Trigger.tgcMusicName;
2275 end;
2277 if not g_Sound_CreateWADEx(Trigger.tgcMusicName, fn, True) then
2278 begin
2279 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.tgcMusicName]));
2280 end;
2281 end;
2282 end;
2284 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü"
2285 if Trigger.TriggerType = TRIGGER_SHOT then
2286 begin
2287 with gTriggers[find_id] do
2288 begin
2289 ShotPanelTime := 0;
2290 ShotSightTime := 0;
2291 ShotSightTimeout := 0;
2292 ShotSightTarget := 0;
2293 ShotSightTargetN := 0;
2294 ShotAmmoCount := Trigger.tgcAmmo;
2295 ShotReloadTime := 0;
2296 end;
2297 end;
2299 Result := find_id;
2300 end;
2303 // sorry; grid doesn't support recursive queries, so we have to do this
2304 type
2305 TSimpleMonsterList = specialize TSimpleList<TMonster>;
2307 var
2308 tgMonsList: TSimpleMonsterList = nil;
2310 procedure g_Triggers_Update();
2311 var
2312 a, b, i: Integer;
2313 Affected: array of Integer;
2315 function monsNear (mon: TMonster): Boolean;
2316 begin
2317 result := false; // don't stop
2319 gTriggers[a].ActivateUID := mon.UID;
2320 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2322 tgMonsList.append(mon);
2323 end;
2325 var
2326 mon: TMonster;
2327 pan: TPanel;
2328 begin
2329 if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
2331 if gTriggers = nil then
2332 Exit;
2333 SetLength(Affected, 0);
2335 for a := 0 to High(gTriggers) do
2336 with gTriggers[a] do
2337 // Åñòü òðèããåð:
2338 if TriggerType <> TRIGGER_NONE then
2339 begin
2340 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè)
2341 if DoorTime > 0 then DoorTime := DoorTime - 1;
2342 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ
2343 if PressTime > 0 then PressTime := PressTime - 1;
2344 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2345 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2346 begin
2347 for b := 0 to High(Activators) do
2348 begin
2349 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2350 if Activators[b].TimeOut > 0 then
2351 begin
2352 Dec(Activators[b].TimeOut);
2353 end
2354 else
2355 begin
2356 continue;
2357 end;
2358 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2359 if (tgcInterval = 0) and (Activators[b].TimeOut < 65530) then Activators[b].TimeOut := 0;
2360 end;
2361 end;
2363 // Îáðàáàòûâàåì ñïàâíåðû
2364 if Enabled and AutoSpawn then
2365 begin
2366 if SpawnCooldown = 0 then
2367 begin
2368 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà
2369 if (TriggerType = TRIGGER_SPAWNMONSTER) and (tgcDelay > 0) then
2370 begin
2371 ActivateUID := 0;
2372 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2373 end;
2374 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò
2375 if (TriggerType = TRIGGER_SPAWNITEM) and (tgcDelay > 0) then
2376 begin
2377 ActivateUID := 0;
2378 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2379 end;
2380 end
2381 else
2382 begin
2383 // Óìåíüøàåì âðåìÿ îæèäàíèÿ
2384 Dec(SpawnCooldown);
2385 end;
2386 end;
2388 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü"
2389 if TriggerType = TRIGGER_SHOT then
2390 begin
2391 if ShotPanelTime > 0 then
2392 begin
2393 Dec(ShotPanelTime);
2394 if ShotPanelTime = 0 then g_Map_SwitchTextureGUID(ShotPanelType, trigPanelGUID);
2395 end;
2396 if ShotSightTime > 0 then
2397 begin
2398 Dec(ShotSightTime);
2399 if ShotSightTime = 0 then ShotSightTarget := ShotSightTargetN;
2400 end;
2401 if ShotSightTimeout > 0 then
2402 begin
2403 Dec(ShotSightTimeout);
2404 if ShotSightTimeout = 0 then ShotSightTarget := 0;
2405 end;
2406 if ShotReloadTime > 0 then
2407 begin
2408 Dec(ShotReloadTime);
2409 if ShotReloadTime = 0 then ShotAmmoCount := tgcAmmo;
2410 end;
2411 end;
2413 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì
2414 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2415 begin
2416 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2417 begin
2418 if tgcPlayCount > 0 then SoundPlayCount -= 1; // Åñëè 0 - èãðàåì çâóê áåñêîíå÷íî
2419 if tgcLocal then
2420 begin
2421 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
2422 end
2423 else
2424 begin
2425 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2426 end;
2427 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then MH_SEND_TriggerSound(gTriggers[a]);
2428 end;
2429 end;
2431 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü
2432 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2433 begin
2434 tr_OpenDoor(trigPanelGUID, tgcSilent, tgcD2d);
2435 DoorTime := -1;
2436 end;
2438 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü
2439 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (g_Map_PanelByGUID(trigPanelGUID) <> nil) then
2440 begin
2441 pan := g_Map_PanelByGUID(trigPanelGUID);
2442 if (pan <> nil) and pan.isGWall then
2443 begin
2444 // Óæå çàêðûòà
2445 if {gWalls[trigPanelID].Enabled} pan.Enabled then
2446 begin
2447 DoorTime := -1;
2448 end
2449 else
2450 begin
2451 // Ïîêà îòêðûòà - çàêðûâàåì
2452 if tr_CloseDoor(trigPanelGUID, tgcSilent, tgcD2d) then DoorTime := -1;
2453 end;
2454 end;
2455 end;
2457 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2458 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2459 (PressTime = 0) and (PressCount >= tgcPressCount) then
2460 begin
2461 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2462 PressTime := -1;
2463 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2464 if tgcPressCount > 0 then PressCount -= tgcPressCount else PressCount := 0;
2466 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2467 for b := 0 to High(gTriggers) do
2468 begin
2469 if g_Collide(tgcTX, tgcTY, tgcTWidth, tgcTHeight, gTriggers[b].X, gTriggers[b].Y,
2470 gTriggers[b].Width, gTriggers[b].Height) and
2471 ((b <> a) or (tgcWait > 0)) then
2472 begin // Can be self-activated, if there is Data.Wait
2473 if (not tgcExtRandom) or gTriggers[b].Enabled then
2474 begin
2475 SetLength(Affected, Length(Affected) + 1);
2476 Affected[High(Affected)] := b;
2477 end;
2478 end;
2479 end;
2481 //HACK!
2482 // if we have panelid, assume that it will switch the moving platform
2483 pan := g_Map_PanelByGUID(trigPanelGUID);
2484 if (pan <> nil) then
2485 begin
2486 case TriggerType of
2487 TRIGGER_PRESS: pan.movingActive := true; // what to do here?
2488 TRIGGER_ON: pan.movingActive := true;
2489 TRIGGER_OFF: pan.movingActive := false;
2490 TRIGGER_ONOFF: pan.movingActive := not pan.movingActive;
2491 end;
2492 if not tgcSilent and (Length(tgcSound) > 0) then
2493 begin
2494 g_Sound_PlayExAt(tgcSound, X, Y);
2495 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, tgcSound);
2496 end;
2497 end;
2499 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2500 if (TriggerType = TRIGGER_PRESS) and tgcExtRandom then
2501 begin
2502 if (Length(Affected) > 0) then
2503 begin
2504 b := Affected[Random(Length(Affected))];
2505 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2506 ActivateTrigger(gTriggers[b], 0);
2507 end;
2508 end
2509 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2510 begin
2511 for i := 0 to High(Affected) do
2512 begin
2513 b := Affected[i];
2514 case TriggerType of
2515 TRIGGER_PRESS:
2516 begin
2517 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2518 ActivateTrigger(gTriggers[b], 0);
2519 end;
2520 TRIGGER_ON:
2521 begin
2522 gTriggers[b].Enabled := True;
2523 end;
2524 TRIGGER_OFF:
2525 begin
2526 gTriggers[b].Enabled := False;
2527 gTriggers[b].TimeOut := 0;
2528 if gTriggers[b].AutoSpawn then
2529 begin
2530 gTriggers[b].AutoSpawn := False;
2531 gTriggers[b].SpawnCooldown := 0;
2532 end;
2533 end;
2534 TRIGGER_ONOFF:
2535 begin
2536 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2537 if not gTriggers[b].Enabled then
2538 begin
2539 gTriggers[b].TimeOut := 0;
2540 if gTriggers[b].AutoSpawn then
2541 begin
2542 gTriggers[b].AutoSpawn := False;
2543 gTriggers[b].SpawnCooldown := 0;
2544 end;
2545 end;
2546 end;
2547 end;
2548 end;
2549 end;
2550 SetLength(Affected, 0);
2551 end;
2553 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2554 if TimeOut > 0 then
2555 begin
2556 TimeOut := TimeOut - 1;
2557 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2558 end;
2560 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2561 if not Enabled then
2562 Continue;
2564 // "Èãðîê áëèçêî":
2565 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2566 (TimeOut = 0) then
2567 if gPlayers <> nil then
2568 for b := 0 to High(gPlayers) do
2569 if gPlayers[b] <> nil then
2570 with gPlayers[b] do
2571 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2572 if alive and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2573 Collide(X, Y, Width, Height) then
2574 begin
2575 gTriggers[a].ActivateUID := UID;
2577 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2578 PlayerCollide then
2579 { Don't activate sound/music again if player is here }
2580 else
2581 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2582 end;
2584 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2586 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2587 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2588 (TimeOut = 0) and (Keys = 0) then
2589 begin
2590 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2591 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2592 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2593 gTriggers[a].ActivateUID := 0;
2594 ActivateTrigger(gTriggers[a], 0);
2595 end else
2596 begin
2597 // "Ìîíñòð áëèçêî"
2598 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2599 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2600 begin
2601 //g_Mons_ForEach(monsNear);
2602 //Alive?!
2603 tgMonsList.reset();
2604 g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
2605 for mon in tgMonsList do
2606 begin
2607 gTriggers[a].ActivateUID := mon.UID;
2608 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2609 end;
2610 tgMonsList.reset(); // just in case
2611 end;
2613 // "Ìîíñòðîâ íåò"
2614 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2615 (TimeOut = 0) and (Keys = 0) then
2616 if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
2617 begin
2618 gTriggers[a].ActivateUID := 0;
2619 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2620 end;
2621 end;
2623 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2624 end;
2625 end;
2627 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2628 begin
2629 if (ID >= Length(gTriggers)) then exit;
2630 gTriggers[ID].ActivateUID := ActivateUID;
2631 ActivateTrigger(gTriggers[ID], ActivateType);
2632 end;
2634 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2635 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2636 var
2637 a: Integer;
2638 k: Byte;
2639 p: TPlayer;
2640 begin
2641 Result := nil;
2643 if gTriggers = nil then Exit;
2645 case g_GetUIDType(UID) of
2646 UID_GAME: k := 255;
2647 UID_PLAYER:
2648 begin
2649 p := g_Player_Get(UID);
2650 if p <> nil then
2651 k := p.GetKeys
2652 else
2653 k := 0;
2654 end;
2655 else k := 0;
2656 end;
2658 for a := 0 to High(gTriggers) do
2659 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2660 (gTriggers[a].TimeOut = 0) and
2661 (not InDWArray(a, IgnoreList)) and
2662 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2663 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2664 if g_Collide(X, Y, Width, Height,
2665 gTriggers[a].X, gTriggers[a].Y,
2666 gTriggers[a].Width, gTriggers[a].Height) then
2667 begin
2668 gTriggers[a].ActivateUID := UID;
2669 if ActivateTrigger(gTriggers[a], ActivateType) then
2670 begin
2671 SetLength(Result, Length(Result)+1);
2672 Result[High(Result)] := a;
2673 end;
2674 end;
2675 end;
2677 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2678 var
2679 a: Integer;
2680 k: Byte;
2681 p: TPlayer;
2682 begin
2683 if gTriggers = nil then Exit;
2685 case g_GetUIDType(UID) of
2686 UID_GAME: k := 255;
2687 UID_PLAYER:
2688 begin
2689 p := g_Player_Get(UID);
2690 if p <> nil then
2691 k := p.GetKeys
2692 else
2693 k := 0;
2694 end;
2695 else k := 0;
2696 end;
2698 for a := 0 to High(gTriggers) do
2699 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2700 (gTriggers[a].TimeOut = 0) and
2701 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2702 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2703 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2704 gTriggers[a].Width, gTriggers[a].Height) then
2705 begin
2706 gTriggers[a].ActivateUID := UID;
2707 ActivateTrigger(gTriggers[a], ActivateType);
2708 end;
2709 end;
2711 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2712 var
2713 a: Integer;
2714 k: Byte;
2715 rsq: Word;
2716 p: TPlayer;
2717 begin
2718 if gTriggers = nil then
2719 Exit;
2721 case g_GetUIDType(UID) of
2722 UID_GAME: k := 255;
2723 UID_PLAYER:
2724 begin
2725 p := g_Player_Get(UID);
2726 if p <> nil then
2727 k := p.GetKeys
2728 else
2729 k := 0;
2730 end;
2731 else k := 0;
2732 end;
2734 rsq := Radius * Radius;
2736 for a := 0 to High(gTriggers) do
2737 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
2738 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2739 (gTriggers[a].TimeOut = 0) and
2740 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2741 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2742 with gTriggers[a] do
2743 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
2744 X, Y, Width, Height) then
2745 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
2746 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
2747 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
2748 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
2749 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
2750 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2751 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
2752 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2753 begin
2754 ActivateUID := UID;
2755 ActivateTrigger(gTriggers[a], ActivateType);
2756 end;
2757 end;
2759 procedure g_Triggers_OpenAll();
2760 var
2761 a: Integer;
2762 b: Boolean;
2763 begin
2764 if gTriggers = nil then Exit;
2766 b := False;
2767 for a := 0 to High(gTriggers) do
2768 begin
2769 with gTriggers[a] do
2770 begin
2771 if (TriggerType = TRIGGER_OPENDOOR) or
2772 (TriggerType = TRIGGER_DOOR5) or
2773 (TriggerType = TRIGGER_DOOR) then
2774 begin
2775 tr_OpenDoor(trigPanelGUID, True, tgcD2d);
2776 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
2777 b := True;
2778 end;
2779 end;
2780 end;
2782 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
2783 end;
2785 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
2786 begin
2787 if (gTriggers <> nil) then
2788 if gTriggers[ID].SpawnedCount > 0 then
2789 Dec(gTriggers[ID].SpawnedCount);
2790 end;
2793 procedure g_Triggers_Free ();
2794 var
2795 a: Integer;
2796 begin
2797 for a := 0 to High(gTriggers) do
2798 begin
2799 if (gTriggers[a].TriggerType = TRIGGER_SOUND) then
2800 begin
2801 if g_Sound_Exists(gTriggers[a].tgcSoundName) then
2802 begin
2803 g_Sound_Delete(gTriggers[a].tgcSoundName);
2804 end;
2805 gTriggers[a].Sound.Free();
2806 end;
2807 if (gTriggers[a].Activators <> nil) then
2808 begin
2809 SetLength(gTriggers[a].Activators, 0);
2810 end;
2811 gTriggers[a].trigDataRec.Free();
2812 end;
2814 gTriggers := nil;
2815 gSecretsCount := 0;
2816 SetLength(gMonstersSpawned, 0);
2817 end;
2820 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
2821 var
2822 count, actCount, i, j: Integer;
2823 dw: DWORD;
2824 sg: Single;
2825 b: Boolean;
2826 begin
2827 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
2828 count := Length(gTriggers);
2829 Mem := TBinMemoryWriter.Create((count+1)*200);
2831 // Êîëè÷åñòâî òðèããåðîâ
2832 Mem.WriteInt(count);
2833 if (count = 0) then exit;
2835 for i := 0 to High(gTriggers) do
2836 begin
2837 // Ñèãíàòóðà òðèããåðà
2838 dw := TRIGGER_SIGNATURE; // 'TRGX'
2839 Mem.WriteDWORD(dw);
2840 // Òèï òðèããåðà
2841 Mem.WriteByte(gTriggers[i].TriggerType);
2842 if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
2843 // Ñïåöèàëüíûå äàííûå òðèããåðà: ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
2844 Mem.WriteInt(gTriggers[i].mapIndex);
2845 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
2846 Mem.WriteInt(gTriggers[i].X);
2847 Mem.WriteInt(gTriggers[i].Y);
2848 // Ðàçìåðû
2849 Mem.WriteWord(gTriggers[i].Width);
2850 Mem.WriteWord(gTriggers[i].Height);
2851 // Âêëþ÷åí ëè òðèããåð
2852 Mem.WriteBoolean(gTriggers[i].Enabled);
2853 // Òèï àêòèâàöèè òðèããåðà
2854 Mem.WriteByte(gTriggers[i].ActivateType);
2855 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
2856 Mem.WriteByte(gTriggers[i].Keys);
2857 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
2858 Mem.WriteInt(gTriggers[i].TexturePanelGUID);
2859 // Òèï ýòîé ïàíåëè
2860 Mem.WriteWord(gTriggers[i].TexturePanelType);
2861 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
2862 Mem.WriteInt(gTriggers[i].trigPanelGUID);
2863 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
2864 Mem.WriteWord(gTriggers[i].TimeOut);
2865 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
2866 Mem.WriteWord(gTriggers[i].ActivateUID);
2867 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
2868 actCount := Length(gTriggers[i].Activators);
2869 Mem.WriteInt(actCount);
2870 for j := 0 to actCount-1 do
2871 begin
2872 // UID îáúåêòà
2873 Mem.WriteWord(gTriggers[i].Activators[j].UID);
2874 // Âðåìÿ îæèäàíèÿ
2875 Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
2876 end;
2877 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
2878 Mem.WriteBoolean(gTriggers[i].PlayerCollide);
2879 // Âðåìÿ äî çàêðûòèÿ äâåðè
2880 Mem.WriteInt(gTriggers[i].DoorTime);
2881 // Çàäåðæêà àêòèâàöèè
2882 Mem.WriteInt(gTriggers[i].PressTime);
2883 // Ñ÷åò÷èê íàæàòèé
2884 Mem.WriteInt(gTriggers[i].PressCount);
2885 // Ñïàâíåð àêòèâåí
2886 Mem.WriteBoolean(gTriggers[i].AutoSpawn);
2887 // Çàäåðæêà ñïàâíåðà
2888 Mem.WriteInt(gTriggers[i].SpawnCooldown);
2889 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
2890 Mem.WriteInt(gTriggers[i].SpawnedCount);
2891 // Ñêîëüêî ðàç ïðîèãðàí çâóê
2892 Mem.WriteInt(gTriggers[i].SoundPlayCount);
2893 // Ïðîèãðûâàåòñÿ ëè çâóê?
2894 if (gTriggers[i].Sound <> nil) then b := gTriggers[i].Sound.IsPlaying() else b := false;
2895 Mem.WriteBoolean(b);
2896 if b then
2897 begin
2898 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
2899 dw := gTriggers[i].Sound.GetPosition();
2900 Mem.WriteDWORD(dw);
2901 // Ãðîìêîñòü çâóêà
2902 sg := gTriggers[i].Sound.GetVolume();
2903 sg := sg / (gSoundLevel/255.0);
2904 Mem.WriteSingle(sg);
2905 // Ñòåðåî ñìåùåíèå çâóêà
2906 sg := gTriggers[i].Sound.GetPan();
2907 Mem.WriteSingle(sg);
2908 end;
2909 end;
2910 end;
2913 procedure g_Triggers_LoadState (var Mem: TBinMemoryReader);
2914 var
2915 count, actCount, i, j, a: Integer;
2916 dw: DWORD;
2917 vol, pan: Single;
2918 b: Boolean;
2919 Trig: TTrigger;
2920 mapIndex: Integer;
2921 begin
2922 if (Mem = nil) then exit;
2924 g_Triggers_Free();
2926 // Êîëè÷åñòâî òðèããåðîâ
2927 Mem.ReadInt(count);
2928 if (count = 0) then exit;
2930 for a := 0 to count-1 do
2931 begin
2932 // Ñèãíàòóðà òðèããåðà
2933 Mem.ReadDWORD(dw); // 'TRGX'
2934 if (dw <> TRIGGER_SIGNATURE) then raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
2935 // Òèï òðèããåðà
2936 Mem.ReadByte(Trig.TriggerType);
2937 // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
2938 if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
2939 Mem.ReadInt(mapIndex);
2940 i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
2942 if (gTriggers[i].trigData <> nil) then
2943 begin
2944 tw := TStrTextWriter.Create();
2945 try
2946 gTriggers[i].trigData.writeTo(tw);
2947 e_LogWritefln('=== trigger #%s loaded ==='#10'%s'#10'---', [mapIndex, tw.str]);
2948 finally
2949 tw.Free();
2950 end;
2951 end;
2953 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2954 Mem.ReadInt(gTriggers[i].X);
2955 Mem.ReadInt(gTriggers[i].Y);
2956 // Ðàçìåðû:
2957 Mem.ReadWord(gTriggers[i].Width);
2958 Mem.ReadWord(gTriggers[i].Height);
2959 // Âêëþ÷åí ëè òðèããåð:
2960 Mem.ReadBoolean(gTriggers[i].Enabled);
2961 // Òèï àêòèâàöèè òðèããåðà:
2962 Mem.ReadByte(gTriggers[i].ActivateType);
2963 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2964 Mem.ReadByte(gTriggers[i].Keys);
2965 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2966 Mem.ReadInt(gTriggers[i].TexturePanelGUID);
2967 // Òèï ýòîé ïàíåëè:
2968 Mem.ReadWord(gTriggers[i].TexturePanelType);
2969 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
2970 Mem.ReadInt(gTriggers[i].trigPanelGUID);
2971 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2972 Mem.ReadWord(gTriggers[i].TimeOut);
2973 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2974 Mem.ReadWord(gTriggers[i].ActivateUID);
2975 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2976 Mem.ReadInt(actCount);
2977 if actCount > 0 then
2978 begin
2979 SetLength(gTriggers[i].Activators, actCount);
2980 for j := 0 to actCount-1 do
2981 begin
2982 // UID îáúåêòà
2983 Mem.ReadWord(gTriggers[i].Activators[j].UID);
2984 // Âðåìÿ îæèäàíèÿ
2985 Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
2986 end;
2987 end;
2988 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2989 Mem.ReadBoolean(gTriggers[i].PlayerCollide);
2990 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2991 Mem.ReadInt(gTriggers[i].DoorTime);
2992 // Çàäåðæêà àêòèâàöèè:
2993 Mem.ReadInt(gTriggers[i].PressTime);
2994 // Ñ÷åò÷èê íàæàòèé:
2995 Mem.ReadInt(gTriggers[i].PressCount);
2996 // Ñïàâíåð àêòèâåí:
2997 Mem.ReadBoolean(gTriggers[i].AutoSpawn);
2998 // Çàäåðæêà ñïàâíåðà:
2999 Mem.ReadInt(gTriggers[i].SpawnCooldown);
3000 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
3001 Mem.ReadInt(gTriggers[i].SpawnedCount);
3002 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
3003 Mem.ReadInt(gTriggers[i].SoundPlayCount);
3004 // Ïðîèãðûâàåòñÿ ëè çâóê?
3005 Mem.ReadBoolean(b);
3006 if b then
3007 begin
3008 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
3009 Mem.ReadDWORD(dw);
3010 // Ãðîìêîñòü çâóêà:
3011 Mem.ReadSingle(vol);
3012 // Ñòåðåî ñìåùåíèå çâóêà:
3013 Mem.ReadSingle(pan);
3014 // Çàïóñêàåì çâóê, åñëè åñòü:
3015 if gTriggers[i].Sound <> nil then
3016 begin
3017 gTriggers[i].Sound.PlayPanVolume(pan, vol);
3018 gTriggers[i].Sound.Pause(True);
3019 gTriggers[i].Sound.SetPosition(dw);
3020 end
3021 end;
3022 end;
3023 end;
3025 end.