DEADSOFTWARE

some fixes for recursive grid queries (grid doesn't support recursive queries, but...
[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 MAPSTRUCT, e_graphics, MAPDEF, g_basic, g_sound,
23 BinEditor;
25 type
26 TActivator = record
27 UID: Word;
28 TimeOut: Word;
29 end;
30 TTrigger = record
31 ID: DWORD;
32 ClientID: DWORD;
33 TriggerType: Byte;
34 X, Y: Integer;
35 Width, Height: Word;
36 Enabled: Boolean;
37 ActivateType: Byte;
38 Keys: Byte;
39 TexturePanel: Integer;
40 TexturePanelType: Word;
42 TimeOut: Word;
43 ActivateUID: Word;
44 Activators: array of TActivator;
45 PlayerCollide: Boolean;
46 DoorTime: Integer;
47 PressTime: Integer;
48 PressCount: Integer;
49 SoundPlayCount: Integer;
50 Sound: TPlayableSound;
51 AutoSpawn: Boolean;
52 SpawnCooldown: Integer;
53 SpawnedCount: Integer;
54 ShotPanelType: Word;
55 ShotPanelTime: Integer;
56 ShotSightTime: Integer;
57 ShotSightTimeout: Integer;
58 ShotSightTarget: Word;
59 ShotSightTargetN: Word;
60 ShotAmmoCount: Word;
61 ShotReloadTime: Integer;
63 Data: TTriggerData;
64 end;
66 function g_Triggers_Create(Trigger: TTrigger): DWORD;
67 procedure g_Triggers_Update();
68 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
69 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
70 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
71 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
72 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
73 procedure g_Triggers_OpenAll();
74 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
75 procedure g_Triggers_Free();
76 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
77 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
79 function tr_Message(MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
81 function tr_CloseDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
82 function tr_OpenDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
83 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
84 function tr_SetLift(PanelID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
86 function tr_Teleport(ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
87 function tr_Push(ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
89 procedure tr_MakeEffect(X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
90 function tr_SpawnShot(ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
92 var
93 gTriggerClientID: Integer = 0;
94 gTriggers: array of TTrigger;
95 gSecretsCount: Integer = 0;
96 gMonstersSpawned: array of LongInt = nil;
98 implementation
100 uses
101 g_player, g_map, Math, g_gfx, g_game, g_textures,
102 g_console, g_monsters, g_items, g_phys, g_weapons,
103 wadreader, g_main, SysUtils, e_log, g_language,
104 g_options, g_net, g_netmsg, utils;
106 const
107 TRIGGER_SIGNATURE = $52475254; // 'TRGR'
108 TRAP_DAMAGE = 1000;
110 function FindTrigger(): DWORD;
111 var
112 i: Integer;
113 begin
114 if gTriggers <> nil then
115 for i := 0 to High(gTriggers) do
116 if gTriggers[i].TriggerType = TRIGGER_NONE then
117 begin
118 Result := i;
119 Exit;
120 end;
122 if gTriggers = nil then
123 begin
124 SetLength(gTriggers, 8);
125 Result := 0;
126 end
127 else
128 begin
129 Result := High(gTriggers) + 1;
130 SetLength(gTriggers, Length(gTriggers) + 8);
131 end;
132 end;
134 function tr_CloseDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
135 var
136 a, b, c: Integer;
137 begin
138 Result := False;
140 if PanelID = -1 then Exit;
142 if not d2d then
143 begin
144 with gWalls[PanelID] do
145 begin
146 if g_CollidePlayer(X, Y, Width, Height) or
147 g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
149 if not Enabled then
150 begin
151 if not NoSound then
152 begin
153 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
154 if g_Game_IsServer and g_Game_IsNet then
155 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
156 end;
157 g_Map_EnableWall(PanelID);
158 Result := True;
159 end;
160 end;
161 end
162 else
163 begin
164 if gDoorMap = nil then Exit;
166 c := -1;
167 for a := 0 to High(gDoorMap) do
168 begin
169 for b := 0 to High(gDoorMap[a]) do
170 if gDoorMap[a, b] = DWORD(PanelID) then
171 begin
172 c := a;
173 Break;
174 end;
176 if c <> -1 then Break;
177 end;
178 if c = -1 then Exit;
180 for b := 0 to High(gDoorMap[c]) do
181 with gWalls[gDoorMap[c, b]] do
182 begin
183 if g_CollidePlayer(X, Y, Width, Height) or
184 g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
185 end;
187 if not NoSound then
188 for b := 0 to High(gDoorMap[c]) do
189 if not gWalls[gDoorMap[c, b]].Enabled then
190 begin
191 with gWalls[PanelID] do
192 begin
193 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
194 if g_Game_IsServer and g_Game_IsNet then
195 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
196 end;
197 Break;
198 end;
200 for b := 0 to High(gDoorMap[c]) do
201 if not gWalls[gDoorMap[c, b]].Enabled then
202 begin
203 g_Map_EnableWall(gDoorMap[c, b]);
204 Result := True;
205 end;
206 end;
207 end;
209 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
210 var
211 a, b, c: Integer;
212 wx, wy, wh, ww: Integer;
214 function monsDamage (mon: TMonster): Boolean;
215 begin
216 result := false; // don't stop
217 if g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
218 end;
220 begin
221 if PanelID = -1 then Exit;
223 if not d2d then
224 begin
225 with gWalls[PanelID] do
226 begin
227 if (not NoSound) and (not Enabled) then
228 begin
229 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
230 if g_Game_IsServer and g_Game_IsNet then
231 MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
232 end;
233 end;
235 wx := gWalls[PanelID].X;
236 wy := gWalls[PanelID].Y;
237 ww := gWalls[PanelID].Width;
238 wh := gWalls[PanelID].Height;
240 with gWalls[PanelID] do
241 begin
242 if gPlayers <> nil then
243 for a := 0 to High(gPlayers) do
244 if (gPlayers[a] <> nil) and gPlayers[a].Live and
245 gPlayers[a].Collide(X, Y, Width, Height) then
246 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
248 //g_Mons_ForEach(monsDamage);
249 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
251 if not Enabled then g_Map_EnableWall(PanelID);
252 end;
253 end
254 else
255 begin
256 if gDoorMap = nil then Exit;
258 c := -1;
259 for a := 0 to High(gDoorMap) do
260 begin
261 for b := 0 to High(gDoorMap[a]) do
262 begin
263 if gDoorMap[a, b] = DWORD(PanelID) then
264 begin
265 c := a;
266 Break;
267 end;
268 end;
270 if c <> -1 then Break;
271 end;
272 if c = -1 then Exit;
274 if not NoSound then
275 begin
276 for b := 0 to High(gDoorMap[c]) do
277 begin
278 if not gWalls[gDoorMap[c, b]].Enabled then
279 begin
280 with gWalls[PanelID] do
281 begin
282 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
283 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
284 end;
285 Break;
286 end;
287 end;
288 end;
290 for b := 0 to High(gDoorMap[c]) do
291 begin
292 wx := gWalls[gDoorMap[c, b]].X;
293 wy := gWalls[gDoorMap[c, b]].Y;
294 ww := gWalls[gDoorMap[c, b]].Width;
295 wh := gWalls[gDoorMap[c, b]].Height;
297 with gWalls[gDoorMap[c, b]] do
298 begin
299 if gPlayers <> nil then
300 for a := 0 to High(gPlayers) do
301 if (gPlayers[a] <> nil) and gPlayers[a].Live and
302 gPlayers[a].Collide(X, Y, Width, Height) then
303 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
305 //g_Mons_ForEach(monsDamage);
306 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
307 (*
308 if gMonsters <> nil then
309 for a := 0 to High(gMonsters) do
310 if (gMonsters[a] <> nil) and gMonsters[a].Live and
311 g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
312 gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
313 *)
315 if not Enabled then g_Map_EnableWall(gDoorMap[c, b]);
316 end;
317 end;
318 end;
319 end;
321 function tr_OpenDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
322 var
323 a, b, c: Integer;
324 begin
325 Result := False;
327 if PanelID = -1 then Exit;
329 if not d2d then
330 begin
331 with gWalls[PanelID] do
332 if Enabled then
333 begin
334 if not NoSound then
335 begin
336 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
337 if g_Game_IsServer and g_Game_IsNet then
338 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
339 end;
340 g_Map_DisableWall(PanelID);
341 Result := True;
342 end;
343 end
344 else
345 begin
346 if gDoorMap = nil then Exit;
348 c := -1;
349 for a := 0 to High(gDoorMap) do
350 begin
351 for b := 0 to High(gDoorMap[a]) do
352 if gDoorMap[a, b] = DWORD(PanelID) then
353 begin
354 c := a;
355 Break;
356 end;
358 if c <> -1 then Break;
359 end;
360 if c = -1 then Exit;
362 if not NoSound then
363 for b := 0 to High(gDoorMap[c]) do
364 if gWalls[gDoorMap[c, b]].Enabled then
365 begin
366 with gWalls[PanelID] do
367 begin
368 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
369 if g_Game_IsServer and g_Game_IsNet then
370 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
371 end;
372 Break;
373 end;
375 for b := 0 to High(gDoorMap[c]) do
376 if gWalls[gDoorMap[c, b]].Enabled then
377 begin
378 g_Map_DisableWall(gDoorMap[c, b]);
379 Result := True;
380 end;
381 end;
382 end;
384 function tr_SetLift(PanelID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
385 var
386 a, b, c, t: Integer;
387 begin
388 t := 0;
389 Result := False;
391 if PanelID = -1 then Exit;
393 if (gLifts[PanelID].PanelType = PANEL_LIFTUP) or
394 (gLifts[PanelID].PanelType = PANEL_LIFTDOWN) then
395 case d of
396 0: t := 0;
397 1: t := 1;
398 else t := IfThen(gLifts[PanelID].LiftType = 1, 0, 1);
399 end
400 else if (gLifts[PanelID].PanelType = PANEL_LIFTLEFT) or
401 (gLifts[PanelID].PanelType = PANEL_LIFTRIGHT) then
402 case d of
403 0: t := 2;
404 1: t := 3;
405 else t := IfThen(gLifts[PanelID].LiftType = 2, 3, 2);
406 end;
408 if not d2d then
409 begin
410 with gLifts[PanelID] do
411 if LiftType <> t then
412 begin
413 g_Map_SetLift(PanelID, t);
415 {if not NoSound then
416 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);}
417 Result := True;
418 end;
419 end
420 else // Êàê â D2d
421 begin
422 if gLiftMap = nil then Exit;
424 c := -1;
425 for a := 0 to High(gLiftMap) do
426 begin
427 for b := 0 to High(gLiftMap[a]) do
428 if gLiftMap[a, b] = DWORD(PanelID) then
429 begin
430 c := a;
431 Break;
432 end;
434 if c <> -1 then Break;
435 end;
436 if c = -1 then Exit;
438 {if not NoSound then
439 for b := 0 to High(gLiftMap[c]) do
440 if gLifts[gLiftMap[c, b]].LiftType <> t then
441 begin
442 with gLifts[PanelID] do
443 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
444 Break;
445 end;}
447 for b := 0 to High(gLiftMap[c]) do
448 with gLifts[gLiftMap[c, b]] do
449 if LiftType <> t then
450 begin
451 g_Map_SetLift(gLiftMap[c, b], t);
453 Result := True;
454 end;
455 end;
456 end;
458 function tr_SpawnShot(ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
459 var
460 snd: string;
461 Projectile: Boolean;
462 TextureID: DWORD;
463 Anim: TAnimation;
464 begin
465 Result := -1;
466 TextureID := DWORD(-1);
467 snd := 'SOUND_WEAPON_FIREROCKET';
468 Projectile := True;
469 case ShotType of
470 TRIGGER_SHOT_PISTOL:
471 begin
472 g_Weapon_pistol(wx, wy, dx, dy, 0, True);
473 snd := 'SOUND_WEAPON_FIREPISTOL';
474 Projectile := False;
475 if ShotSound then
476 begin
477 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
478 if g_Game_IsNet then
479 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
480 end;
481 end;
483 TRIGGER_SHOT_BULLET:
484 begin
485 g_Weapon_mgun(wx, wy, dx, dy, 0, True);
486 if gSoundEffectsDF then snd := 'SOUND_WEAPON_FIRECGUN'
487 else snd := 'SOUND_WEAPON_FIREPISTOL';
488 Projectile := False;
489 if ShotSound then
490 begin
491 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
492 if g_Game_IsNet then
493 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
494 end;
495 end;
497 TRIGGER_SHOT_SHOTGUN:
498 begin
499 g_Weapon_Shotgun(wx, wy, dx, dy, 0, True);
500 snd := 'SOUND_WEAPON_FIRESHOTGUN';
501 Projectile := False;
502 if ShotSound then
503 begin
504 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
505 if g_Game_IsNet then
506 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL2);
507 end;
508 end;
510 TRIGGER_SHOT_SSG:
511 begin
512 g_Weapon_DShotgun(wx, wy, dx, dy, 0, True);
513 snd := 'SOUND_WEAPON_FIRESHOTGUN2';
514 Projectile := False;
515 if ShotSound then
516 begin
517 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
518 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
519 if g_Game_IsNet then
520 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL3);
521 end;
522 end;
524 TRIGGER_SHOT_IMP:
525 begin
526 g_Weapon_ball1(wx, wy, dx, dy, 0, -1, True);
527 snd := 'SOUND_WEAPON_FIREBALL';
528 end;
530 TRIGGER_SHOT_PLASMA:
531 begin
532 g_Weapon_Plasma(wx, wy, dx, dy, 0, -1, True);
533 snd := 'SOUND_WEAPON_FIREPLASMA';
534 end;
536 TRIGGER_SHOT_SPIDER:
537 begin
538 g_Weapon_aplasma(wx, wy, dx, dy, 0, -1, True);
539 snd := 'SOUND_WEAPON_FIREPLASMA';
540 end;
542 TRIGGER_SHOT_CACO:
543 begin
544 g_Weapon_ball2(wx, wy, dx, dy, 0, -1, True);
545 snd := 'SOUND_WEAPON_FIREBALL';
546 end;
548 TRIGGER_SHOT_BARON:
549 begin
550 g_Weapon_ball7(wx, wy, dx, dy, 0, -1, True);
551 snd := 'SOUND_WEAPON_FIREBALL';
552 end;
554 TRIGGER_SHOT_MANCUB:
555 begin
556 g_Weapon_manfire(wx, wy, dx, dy, 0, -1, True);
557 snd := 'SOUND_WEAPON_FIREBALL';
558 end;
560 TRIGGER_SHOT_REV:
561 begin
562 g_Weapon_revf(wx, wy, dx, dy, 0, ShotTarget, -1, True);
563 snd := 'SOUND_WEAPON_FIREREV';
564 end;
566 TRIGGER_SHOT_ROCKET:
567 begin
568 g_Weapon_Rocket(wx, wy, dx, dy, 0, -1, True);
569 snd := 'SOUND_WEAPON_FIREROCKET';
570 end;
572 TRIGGER_SHOT_BFG:
573 begin
574 g_Weapon_BFGShot(wx, wy, dx, dy, 0, -1, True);
575 snd := 'SOUND_WEAPON_FIREBFG';
576 end;
578 TRIGGER_SHOT_EXPL:
579 begin
580 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
581 begin
582 Anim := TAnimation.Create(TextureID, False, 6);
583 Anim.Blending := False;
584 g_GFX_OnceAnim(wx-64, wy-64, Anim);
585 Anim.Free();
586 end;
587 Projectile := False;
588 g_Weapon_Explode(wx, wy, 60, 0);
589 snd := 'SOUND_WEAPON_EXPLODEROCKET';
590 end;
592 TRIGGER_SHOT_BFGEXPL:
593 begin
594 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
595 begin
596 Anim := TAnimation.Create(TextureID, False, 6);
597 Anim.Blending := False;
598 g_GFX_OnceAnim(wx-64, wy-64, Anim);
599 Anim.Free();
600 end;
601 Projectile := False;
602 g_Weapon_BFG9000(wx, wy, 0);
603 snd := 'SOUND_WEAPON_EXPLODEBFG';
604 end;
606 else exit;
607 end;
609 if g_Game_IsNet and g_Game_IsServer then
610 case ShotType of
611 TRIGGER_SHOT_EXPL:
612 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_EXPLODE);
613 TRIGGER_SHOT_BFGEXPL:
614 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_BFGEXPL);
615 else
616 begin
617 if Projectile then
618 MH_SEND_CreateShot(LastShotID);
619 if ShotSound then
620 MH_SEND_Sound(wx, wy, snd);
621 end;
622 end;
624 if ShotSound then
625 g_Sound_PlayExAt(snd, wx, wy);
627 if Projectile then
628 Result := LastShotID;
629 end;
631 procedure MakeShot(var Trigger: TTrigger; wx, wy, dx, dy: Integer; TargetUID: Word);
632 begin
633 with Trigger do
634 if (Data.ShotAmmo = 0) or
635 ((Data.ShotAmmo > 0) and (ShotAmmoCount > 0)) then
636 begin
637 if (Data.ShotPanelID <> -1) and (ShotPanelTime = 0) then
638 begin
639 g_Map_SwitchTexture(ShotPanelType, Data.ShotPanelID);
640 ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
641 end;
643 if Data.ShotIntSight > 0 then
644 ShotSightTimeout := 180; // ~= 5 ñåêóíä
646 if ShotAmmoCount > 0 then Dec(ShotAmmoCount);
648 dx := dx + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
649 dy := dy + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
651 tr_SpawnShot(Data.ShotType, wx, wy, dx, dy, Data.ShotSound, TargetUID);
652 end
653 else
654 if (Data.ShotIntReload > 0) and (ShotReloadTime = 0) then
655 ShotReloadTime := Data.ShotIntReload; // òèêîâ íà ïåðåçàðÿäêó ïóøêè
656 end;
658 procedure tr_MakeEffect(X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
659 var
660 FramesID: DWORD;
661 Anim: TAnimation;
662 begin
663 if T = TRIGGER_EFFECT_PARTICLE then
664 case ST of
665 TRIGGER_EFFECT_SLIQUID:
666 begin
667 if (CR = 255) and (CG = 0) and (CB = 0) then
668 g_GFX_SimpleWater(X, Y, 1, VX, VY, 1, 0, 0, 0)
669 else if (CR = 0) and (CG = 255) and (CB = 0) then
670 g_GFX_SimpleWater(X, Y, 1, VX, VY, 2, 0, 0, 0)
671 else if (CR = 0) and (CG = 0) and (CB = 255) then
672 g_GFX_SimpleWater(X, Y, 1, VX, VY, 3, 0, 0, 0)
673 else
674 g_GFX_SimpleWater(X, Y, 1, VX, VY, 0, 0, 0, 0);
675 end;
676 TRIGGER_EFFECT_LLIQUID:
677 g_GFX_SimpleWater(X, Y, 1, VX, VY, 4, CR, CG, CB);
678 TRIGGER_EFFECT_DLIQUID:
679 g_GFX_SimpleWater(X, Y, 1, VX, VY, 5, CR, CG, CB);
680 TRIGGER_EFFECT_BLOOD:
681 g_GFX_Blood(X, Y, 1, VX, VY, 0, 0, CR, CG, CB);
682 TRIGGER_EFFECT_SPARK:
683 g_GFX_Spark(X, Y, 1, GetAngle2(VX, VY), 0, 0);
684 TRIGGER_EFFECT_BUBBLE:
685 g_GFX_Bubbles(X, Y, 1, 0, 0);
686 end;
687 if T = TRIGGER_EFFECT_ANIMATION then
688 case ST of
689 EFFECT_TELEPORT: begin
690 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
691 begin
692 Anim := TAnimation.Create(FramesID, False, 3);
693 if not Silent then
694 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
695 g_GFX_OnceAnim(X-32, Y-32, Anim);
696 Anim.Free();
697 end;
698 if Send and g_Game_IsServer and g_Game_IsNet then
699 MH_SEND_Effect(X, Y, Byte(not Silent), NET_GFX_TELE);
700 end;
701 EFFECT_RESPAWN: begin
702 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
703 begin
704 Anim := TAnimation.Create(FramesID, False, 4);
705 if not Silent then
706 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
707 g_GFX_OnceAnim(X-16, Y-16, Anim);
708 Anim.Free();
709 end;
710 if Send and g_Game_IsServer and g_Game_IsNet then
711 MH_SEND_Effect(X-16, Y-16, Byte(not Silent), NET_GFX_RESPAWN);
712 end;
713 EFFECT_FIRE: begin
714 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
715 begin
716 Anim := TAnimation.Create(FramesID, False, 4);
717 if not Silent then
718 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
719 g_GFX_OnceAnim(X-32, Y-128, Anim);
720 Anim.Free();
721 end;
722 if Send and g_Game_IsServer and g_Game_IsNet then
723 MH_SEND_Effect(X-32, Y-128, Byte(not Silent), NET_GFX_FIRE);
724 end;
725 end;
726 end;
728 function tr_Teleport(ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
729 var
730 p: TPlayer;
731 m: TMonster;
732 begin
733 Result := False;
734 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
735 case g_GetUIDType(ActivateUID) of
736 UID_PLAYER:
737 begin
738 p := g_Player_Get(ActivateUID);
739 if p = nil then
740 Exit;
742 if D2D then
743 begin
744 if p.TeleportTo(TX-(p.Obj.Rect.Width div 2),
745 TY-p.Obj.Rect.Height,
746 Silent,
747 TDir) then
748 Result := True;
749 end
750 else
751 if p.TeleportTo(TX, TY, Silent, TDir) then
752 Result := True;
753 end;
755 UID_MONSTER:
756 begin
757 m := g_Monsters_ByUID(ActivateUID);
758 if m = nil then
759 Exit;
761 if D2D then
762 begin
763 if m.TeleportTo(TX-(m.Obj.Rect.Width div 2),
764 TY-m.Obj.Rect.Height,
765 Silent,
766 TDir) then
767 Result := True;
768 end
769 else
770 if m.TeleportTo(TX, TY, Silent, TDir) then
771 Result := True;
772 end;
773 end;
774 end;
776 function tr_Push(ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
777 var
778 p: TPlayer;
779 m: TMonster;
780 begin
781 Result := True;
782 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
783 case g_GetUIDType(ActivateUID) of
784 UID_PLAYER:
785 begin
786 p := g_Player_Get(ActivateUID);
787 if p = nil then
788 Exit;
790 if ResetVel then
791 begin
792 p.GameVelX := 0;
793 p.GameVelY := 0;
794 p.GameAccelX := 0;
795 p.GameAccelY := 0;
796 end;
798 p.Push(VX, VY);
799 end;
801 UID_MONSTER:
802 begin
803 m := g_Monsters_ByUID(ActivateUID);
804 if m = nil then
805 Exit;
807 if ResetVel then
808 begin
809 m.GameVelX := 0;
810 m.GameVelY := 0;
811 m.GameAccelX := 0;
812 m.GameAccelY := 0;
813 end;
815 m.Push(VX, VY);
816 end;
817 end;
818 end;
820 function tr_Message(MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
821 var
822 msg: string;
823 p: TPlayer;
824 i: Integer;
825 begin
826 Result := True;
827 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
828 msg := b_Text_Format(MText);
829 case MSendTo of
830 0: // activator
831 begin
832 if g_GetUIDType(ActivateUID) = UID_PLAYER then
833 begin
834 if g_Game_IsWatchedPlayer(ActivateUID) then
835 begin
836 if MKind = 0 then
837 g_Console_Add(msg, True)
838 else if MKind = 1 then
839 g_Game_Message(msg, MTime);
840 end
841 else
842 begin
843 p := g_Player_Get(ActivateUID);
844 if g_Game_IsNet and (p.FClientID >= 0) then
845 if MKind = 0 then
846 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, p.FClientID)
847 else if MKind = 1 then
848 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, p.FClientID);
849 end;
850 end;
851 end;
853 1: // activator's team
854 begin
855 if g_GetUIDType(ActivateUID) = UID_PLAYER then
856 begin
857 p := g_Player_Get(ActivateUID);
858 if g_Game_IsWatchedTeam(p.Team) then
859 if MKind = 0 then
860 g_Console_Add(msg, True)
861 else if MKind = 1 then
862 g_Game_Message(msg, MTime);
864 if g_Game_IsNet then
865 begin
866 for i := Low(gPlayers) to High(gPlayers) do
867 if (gPlayers[i].Team = p.Team) and (gPlayers[i].FClientID >= 0) then
868 if MKind = 0 then
869 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
870 else if MKind = 1 then
871 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
872 end;
873 end;
874 end;
876 2: // activator's enemy team
877 begin
878 if g_GetUIDType(ActivateUID) = UID_PLAYER then
879 begin
880 p := g_Player_Get(ActivateUID);
881 if g_Game_IsWatchedTeam(p.Team) then
882 if MKind = 0 then
883 g_Console_Add(msg, True)
884 else if MKind = 1 then
885 g_Game_Message(msg, MTime);
887 if g_Game_IsNet then
888 begin
889 for i := Low(gPlayers) to High(gPlayers) do
890 if (gPlayers[i].Team <> p.Team) and (gPlayers[i].FClientID >= 0) then
891 if MKind = 0 then
892 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
893 else if MKind = 1 then
894 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
895 end;
896 end;
897 end;
899 3: // red team
900 begin
901 if g_Game_IsWatchedTeam(TEAM_RED) then
902 if MKind = 0 then
903 g_Console_Add(msg, True)
904 else if MKind = 1 then
905 g_Game_Message(msg, MTime);
907 if g_Game_IsNet then
908 begin
909 for i := Low(gPlayers) to High(gPlayers) do
910 if (gPlayers[i].Team = TEAM_RED) and (gPlayers[i].FClientID >= 0) then
911 if MKind = 0 then
912 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
913 else if MKind = 1 then
914 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
915 end;
916 end;
918 4: // blue team
919 begin
920 if g_Game_IsWatchedTeam(TEAM_BLUE) then
921 if MKind = 0 then
922 g_Console_Add(msg, True)
923 else if MKind = 1 then
924 g_Game_Message(msg, MTime);
926 if g_Game_IsNet then
927 begin
928 for i := Low(gPlayers) to High(gPlayers) do
929 if (gPlayers[i].Team = TEAM_BLUE) and (gPlayers[i].FClientID >= 0) then
930 if MKind = 0 then
931 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
932 else if MKind = 1 then
933 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
934 end;
935 end;
937 5: // everyone
938 begin
939 if MKind = 0 then
940 g_Console_Add(msg, True)
941 else if MKind = 1 then
942 g_Game_Message(msg, MTime);
944 if g_Game_IsNet then
945 begin
946 if MKind = 0 then
947 MH_SEND_Chat(msg, NET_CHAT_SYSTEM)
948 else if MKind = 1 then
949 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg);
950 end;
951 end;
952 end;
953 end;
955 function tr_ShotAimCheck(var Trigger: TTrigger; Obj: PObj): Boolean;
956 begin
957 result := false;
958 with Trigger do
959 begin
960 if TriggerType <> TRIGGER_SHOT then
961 Exit;
962 Result := (Data.ShotAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
963 or g_Obj_Collide(X, Y, Width, Height, Obj);
964 if Result and (Data.ShotAim and TRIGGER_SHOT_AIM_TRACE > 0) then
965 Result := g_TraceVector(Data.ShotPos.X,
966 Data.ShotPos.Y,
967 Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
968 Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
969 end;
970 end;
972 function ActivateTrigger(var Trigger: TTrigger; actType: Byte): Boolean;
973 var
974 animonce: Boolean;
975 p: TPlayer;
976 m: TMonster;
977 idx, k, wx, wy, xd, yd: Integer;
978 iid: LongWord;
979 coolDown: Boolean;
980 pAngle: Real;
981 FramesID: DWORD;
982 Anim: TAnimation;
983 UIDType: Byte;
984 TargetUID: Word;
985 it: PItem;
986 mon: TMonster;
988 function monsShotTarget (mon: TMonster): Boolean;
989 begin
990 result := false; // don't stop
991 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
992 begin
993 xd := mon.GameX + mon.Obj.Rect.Width div 2;
994 yd := mon.GameY + mon.Obj.Rect.Height div 2;
995 TargetUID := mon.UID;
996 result := true; // stop
997 end;
998 end;
1000 function monsShotTargetMonPlr (mon: TMonster): Boolean;
1001 begin
1002 result := false; // don't stop
1003 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1004 begin
1005 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1006 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1007 TargetUID := mon.UID;
1008 result := true; // stop
1009 end;
1010 end;
1012 function monShotTargetPlrMon (mon: TMonster): Boolean;
1013 begin
1014 result := false; // don't stop
1015 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1016 begin
1017 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1018 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1019 TargetUID := mon.UID;
1020 result := true; // stop
1021 end;
1022 end;
1024 begin
1025 Result := False;
1026 if g_Game_IsClient then
1027 Exit;
1029 if not Trigger.Enabled then
1030 Exit;
1031 if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then
1032 Exit;
1033 if gLMSRespawn = LMS_RESPAWN_WARMUP then
1034 Exit;
1036 animonce := False;
1038 coolDown := (actType <> 0);
1040 with Trigger do
1041 begin
1042 case TriggerType of
1043 TRIGGER_EXIT:
1044 begin
1045 g_Sound_PlayEx('SOUND_GAME_SWITCH0');
1046 if g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH0');
1047 gExitByTrigger := True;
1048 g_Game_ExitLevel(Data.MapName);
1049 TimeOut := 18;
1050 Result := True;
1052 Exit;
1053 end;
1055 TRIGGER_TELEPORT:
1056 begin
1057 Result := tr_Teleport(ActivateUID,
1058 Data.TargetPoint.X, Data.TargetPoint.Y,
1059 Data.TlpDir, Data.silent_teleport,
1060 Data.d2d_teleport);
1061 TimeOut := 0;
1062 end;
1064 TRIGGER_OPENDOOR:
1065 begin
1066 Result := tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1067 TimeOut := 0;
1068 end;
1070 TRIGGER_CLOSEDOOR:
1071 begin
1072 Result := tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1073 TimeOut := 0;
1074 end;
1076 TRIGGER_DOOR, TRIGGER_DOOR5:
1077 begin
1078 if Data.PanelID <> -1 then
1079 begin
1080 if gWalls[Data.PanelID].Enabled then
1081 begin
1082 Result := tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1084 if TriggerType = TRIGGER_DOOR5 then
1085 DoorTime := 180;
1086 end
1087 else
1088 Result := tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1090 if Result then
1091 TimeOut := 18;
1092 end;
1093 end;
1095 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1096 begin
1097 tr_CloseTrap(Data.PanelID, Data.NoSound, Data.d2d_doors);
1099 if TriggerType = TRIGGER_TRAP then
1100 begin
1101 DoorTime := 40;
1102 TimeOut := 76;
1103 end
1104 else
1105 begin
1106 DoorTime := -1;
1107 TimeOut := 0;
1108 end;
1110 Result := True;
1111 end;
1113 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
1114 begin
1115 PressCount := PressCount + 1;
1117 if PressTime = -1 then
1118 PressTime := Data.Wait;
1120 if coolDown then
1121 TimeOut := 18
1122 else
1123 TimeOut := 0;
1124 Result := True;
1125 end;
1127 TRIGGER_SECRET:
1128 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1129 begin
1130 Enabled := False;
1131 Result := True;
1132 if gLMSRespawn = LMS_RESPAWN_NONE then
1133 begin
1134 g_Player_Get(ActivateUID).GetSecret();
1135 Inc(gCoopSecretsFound);
1136 if g_Game_IsNet then MH_SEND_GameStats();
1137 end;
1138 end;
1140 TRIGGER_LIFTUP:
1141 begin
1142 Result := tr_SetLift(Data.PanelID, 0, Data.NoSound, Data.d2d_doors);
1143 TimeOut := 0;
1145 if (not Data.NoSound) and Result then begin
1146 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1147 X + (Width div 2),
1148 Y + (Height div 2));
1149 if g_Game_IsServer and g_Game_IsNet then
1150 MH_SEND_Sound(X + (Width div 2),
1151 Y + (Height div 2),
1152 'SOUND_GAME_SWITCH0');
1153 end;
1154 end;
1156 TRIGGER_LIFTDOWN:
1157 begin
1158 Result := tr_SetLift(Data.PanelID, 1, Data.NoSound, Data.d2d_doors);
1159 TimeOut := 0;
1161 if (not Data.NoSound) and Result then begin
1162 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1163 X + (Width div 2),
1164 Y + (Height div 2));
1165 if g_Game_IsServer and g_Game_IsNet then
1166 MH_SEND_Sound(X + (Width div 2),
1167 Y + (Height div 2),
1168 'SOUND_GAME_SWITCH0');
1169 end;
1170 end;
1172 TRIGGER_LIFT:
1173 begin
1174 Result := tr_SetLift(Data.PanelID, 3, Data.NoSound, Data.d2d_doors);
1176 if Result then
1177 begin
1178 TimeOut := 18;
1180 if (not Data.NoSound) and Result then begin
1181 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1182 X + (Width div 2),
1183 Y + (Height div 2));
1184 if g_Game_IsServer and g_Game_IsNet then
1185 MH_SEND_Sound(X + (Width div 2),
1186 Y + (Height div 2),
1187 'SOUND_GAME_SWITCH0');
1188 end;
1189 end;
1190 end;
1192 TRIGGER_TEXTURE:
1193 begin
1194 if ByteBool(Data.ActivateOnce) then
1195 begin
1196 Enabled := False;
1197 TriggerType := TRIGGER_NONE;
1198 end
1199 else
1200 if coolDown then
1201 TimeOut := 6
1202 else
1203 TimeOut := 0;
1205 animonce := Data.AnimOnce;
1206 Result := True;
1207 end;
1209 TRIGGER_SOUND:
1210 begin
1211 if Sound <> nil then
1212 begin
1213 if Data.SoundSwitch and Sound.IsPlaying() then
1214 begin // Íóæíî âûêëþ÷èòü, åñëè èãðàë
1215 Sound.Stop();
1216 SoundPlayCount := 0;
1217 Result := True;
1218 end
1219 else // (not Data.SoundSwitch) or (not Sound.IsPlaying())
1220 if (Data.PlayCount > 0) or (not Sound.IsPlaying()) then
1221 begin
1222 if Data.PlayCount > 0 then
1223 SoundPlayCount := Data.PlayCount
1224 else // 0 - èãðàåì áåñêîíå÷íî
1225 SoundPlayCount := 1;
1226 Result := True;
1227 end;
1228 if g_Game_IsNet then MH_SEND_TriggerSound(Trigger);
1229 end;
1230 end;
1232 TRIGGER_SPAWNMONSTER:
1233 if (Data.MonType in [MONSTER_DEMON..MONSTER_MAN]) then
1234 begin
1235 Result := False;
1236 if (Data.MonDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1237 begin
1238 AutoSpawn := not AutoSpawn;
1239 SpawnCooldown := 0;
1240 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1241 Result := True;
1242 end;
1244 if ((Data.MonDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1245 or ((Data.MonDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1246 for k := 1 to Data.MonCount do
1247 begin
1248 if (actType = ACTIVATE_CUSTOM) and (Data.MonDelay > 0) then
1249 SpawnCooldown := Data.MonDelay;
1250 if (Data.MonMax > 0) and (SpawnedCount >= Data.MonMax) then
1251 Break;
1253 mon := g_Monsters_Create(Data.MonType,
1254 Data.MonPos.X, Data.MonPos.Y,
1255 TDirection(Data.MonDir), True);
1257 Result := True;
1259 // Çäîðîâüå:
1260 if (Data.MonHealth > 0) then
1261 mon.SetHealth(Data.MonHealth);
1262 // Óñòàíàâëèâàåì ïîâåäåíèå:
1263 mon.MonsterBehaviour := Data.MonBehav;
1264 mon.FNoRespawn := True;
1265 if g_Game_IsNet then
1266 MH_SEND_MonsterSpawn(mon.UID);
1267 // Èäåì èñêàòü öåëü, åñëè íàäî:
1268 if Data.MonActive then
1269 mon.WakeUp();
1271 if Data.MonType <> MONSTER_BARREL then Inc(gTotalMonsters);
1273 if g_Game_IsNet then
1274 begin
1275 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
1276 gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
1277 end;
1279 if Data.MonMax > 0 then
1280 begin
1281 mon.SpawnTrigger := ID;
1282 Inc(SpawnedCount);
1283 end;
1285 case Data.MonEffect of
1286 EFFECT_TELEPORT: begin
1287 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1288 begin
1289 Anim := TAnimation.Create(FramesID, False, 3);
1290 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.MonPos.X, Data.MonPos.Y);
1291 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1292 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, Anim);
1293 Anim.Free();
1294 end;
1295 if g_Game_IsServer and g_Game_IsNet then
1296 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1297 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
1298 NET_GFX_TELE);
1299 end;
1300 EFFECT_RESPAWN: begin
1301 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1302 begin
1303 Anim := TAnimation.Create(FramesID, False, 4);
1304 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.MonPos.X, Data.MonPos.Y);
1305 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1306 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, Anim);
1307 Anim.Free();
1308 end;
1309 if g_Game_IsServer and g_Game_IsNet then
1310 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1311 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
1312 NET_GFX_RESPAWN);
1313 end;
1314 EFFECT_FIRE: begin
1315 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1316 begin
1317 Anim := TAnimation.Create(FramesID, False, 4);
1318 g_Sound_PlayExAt('SOUND_FIRE', Data.MonPos.X, Data.MonPos.Y);
1319 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1320 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, Anim);
1321 Anim.Free();
1322 end;
1323 if g_Game_IsServer and g_Game_IsNet then
1324 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1325 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, 1,
1326 NET_GFX_FIRE);
1327 end;
1328 end;
1329 end;
1330 if g_Game_IsNet then
1331 begin
1332 MH_SEND_GameStats();
1333 MH_SEND_CoopStats();
1334 end;
1336 if coolDown then
1337 TimeOut := 18
1338 else
1339 TimeOut := 0;
1340 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1341 if actType = ACTIVATE_CUSTOM then
1342 Result := False;
1343 end;
1345 TRIGGER_SPAWNITEM:
1346 if (Data.ItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX]) then
1347 begin
1348 Result := False;
1349 if (Data.ItemDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1350 begin
1351 AutoSpawn := not AutoSpawn;
1352 SpawnCooldown := 0;
1353 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1354 Result := True;
1355 end;
1357 if ((Data.ItemDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1358 or ((Data.ItemDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1359 if (not Data.ItemOnlyDM) or
1360 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1361 for k := 1 to Data.ItemCount do
1362 begin
1363 if (actType = ACTIVATE_CUSTOM) and (Data.ItemDelay > 0) then
1364 SpawnCooldown := Data.ItemDelay;
1365 if (Data.ItemMax > 0) and (SpawnedCount >= Data.ItemMax) then
1366 Break;
1368 iid := g_Items_Create(Data.ItemPos.X, Data.ItemPos.Y,
1369 Data.ItemType, Data.ItemFalls, False, True);
1371 Result := True;
1373 if Data.ItemMax > 0 then
1374 begin
1375 it := g_Items_ByIdx(iid);
1376 it.SpawnTrigger := ID;
1377 Inc(SpawnedCount);
1378 end;
1380 case Data.ItemEffect of
1381 EFFECT_TELEPORT: begin
1382 it := g_Items_ByIdx(iid);
1383 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1384 begin
1385 Anim := TAnimation.Create(FramesID, False, 3);
1386 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.ItemPos.X, Data.ItemPos.Y);
1387 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1388 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
1389 Anim.Free();
1390 end;
1391 if g_Game_IsServer and g_Game_IsNet then
1392 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1393 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
1394 NET_GFX_TELE);
1395 end;
1396 EFFECT_RESPAWN: begin
1397 it := g_Items_ByIdx(iid);
1398 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1399 begin
1400 Anim := TAnimation.Create(FramesID, False, 4);
1401 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.ItemPos.X, Data.ItemPos.Y);
1402 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1403 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
1404 Anim.Free();
1405 end;
1406 if g_Game_IsServer and g_Game_IsNet then
1407 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1408 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
1409 NET_GFX_RESPAWN);
1410 end;
1411 EFFECT_FIRE: begin
1412 it := g_Items_ByIdx(iid);
1413 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1414 begin
1415 Anim := TAnimation.Create(FramesID, False, 4);
1416 g_Sound_PlayExAt('SOUND_FIRE', Data.ItemPos.X, Data.ItemPos.Y);
1417 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1418 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
1419 Anim.Free();
1420 end;
1421 if g_Game_IsServer and g_Game_IsNet then
1422 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1423 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
1424 NET_GFX_FIRE);
1425 end;
1426 end;
1428 if g_Game_IsNet then
1429 MH_SEND_ItemSpawn(True, iid);
1430 end;
1432 if coolDown then
1433 TimeOut := 18
1434 else
1435 TimeOut := 0;
1436 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1437 if actType = ACTIVATE_CUSTOM then
1438 Result := False;
1439 end;
1441 TRIGGER_MUSIC:
1442 begin
1443 // Ìåíÿåì ìóçûêó, åñëè åñòü íà ÷òî:
1444 if (Trigger.Data.MusicName <> '') then
1445 begin
1446 gMusic.SetByName(Trigger.Data.MusicName);
1447 gMusic.SpecPause := True;
1448 gMusic.Play();
1449 end;
1451 if Trigger.Data.MusicAction = 1 then
1452 begin // Âêëþ÷èòü
1453 if gMusic.SpecPause then // Áûëà íà ïàóçå => èãðàòü
1454 gMusic.SpecPause := False
1455 else // Èãðàëà => ñíà÷àëà
1456 gMusic.SetPosition(0);
1457 end
1458 else // Âûêëþ÷èòü
1459 begin
1460 // Ïàóçà:
1461 gMusic.SpecPause := True;
1462 end;
1464 if coolDown then
1465 TimeOut := 36
1466 else
1467 TimeOut := 0;
1468 Result := True;
1469 if g_Game_IsNet then MH_SEND_TriggerMusic;
1470 end;
1472 TRIGGER_PUSH:
1473 begin
1474 pAngle := -DegToRad(Data.PushAngle);
1475 Result := tr_Push(ActivateUID,
1476 Floor(Cos(pAngle)*Data.PushForce),
1477 Floor(Sin(pAngle)*Data.PushForce),
1478 Data.ResetVel);
1479 TimeOut := 0;
1480 end;
1482 TRIGGER_SCORE:
1483 begin
1484 Result := False;
1485 // Ïðèáàâèòü èëè îòíÿòü î÷êî
1486 if (Data.ScoreAction in [0..1]) and (Data.ScoreCount > 0) then
1487 begin
1488 // Ñâîåé èëè ÷óæîé êîìàíäå
1489 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1490 begin
1491 p := g_Player_Get(ActivateUID);
1492 if ((Data.ScoreAction = 0) and (Data.ScoreTeam = 0) and (p.Team = TEAM_RED))
1493 or ((Data.ScoreAction = 0) and (Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1494 begin
1495 Inc(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Scores
1497 if Data.ScoreCon then
1498 if Data.ScoreTeam = 0 then
1499 begin
1500 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1501 if g_Game_IsServer and g_Game_IsNet then
1502 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+r');
1503 end else
1504 begin
1505 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1506 if g_Game_IsServer and g_Game_IsNet then
1507 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+re');
1508 end;
1510 if Data.ScoreMsg then
1511 begin
1512 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1513 if g_Game_IsServer and g_Game_IsNet then
1514 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1515 end;
1516 end;
1517 if ((Data.ScoreAction = 1) and (Data.ScoreTeam = 0) and (p.Team = TEAM_RED))
1518 or ((Data.ScoreAction = 1) and (Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1519 begin
1520 Dec(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Fouls
1522 if Data.ScoreCon then
1523 if Data.ScoreTeam = 0 then
1524 begin
1525 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1526 if g_Game_IsServer and g_Game_IsNet then
1527 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-r');
1528 end else
1529 begin
1530 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1531 if g_Game_IsServer and g_Game_IsNet then
1532 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-re');
1533 end;
1535 if Data.ScoreMsg then
1536 begin
1537 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1538 if g_Game_IsServer and g_Game_IsNet then
1539 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1540 end;
1541 end;
1542 if ((Data.ScoreAction = 0) and (Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE))
1543 or ((Data.ScoreAction = 0) and (Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1544 begin
1545 Inc(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Scores
1547 if Data.ScoreCon then
1548 if Data.ScoreTeam = 0 then
1549 begin
1550 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1551 if g_Game_IsServer and g_Game_IsNet then
1552 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+b');
1553 end else
1554 begin
1555 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1556 if g_Game_IsServer and g_Game_IsNet then
1557 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+be');
1558 end;
1560 if Data.ScoreMsg then
1561 begin
1562 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1563 if g_Game_IsServer and g_Game_IsNet then
1564 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1565 end;
1566 end;
1567 if ((Data.ScoreAction = 1) and (Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE))
1568 or ((Data.ScoreAction = 1) and (Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1569 begin
1570 Dec(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Fouls
1572 if Data.ScoreCon then
1573 if Data.ScoreTeam = 0 then
1574 begin
1575 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1576 if g_Game_IsServer and g_Game_IsNet then
1577 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-b');
1578 end else
1579 begin
1580 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1581 if g_Game_IsServer and g_Game_IsNet then
1582 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-be');
1583 end;
1585 if Data.ScoreMsg then
1586 begin
1587 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1588 if g_Game_IsServer and g_Game_IsNet then
1589 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1590 end;
1591 end;
1592 Result := (p.Team = TEAM_RED) or (p.Team = TEAM_BLUE);
1593 end;
1594 // Êàêîé-òî êîíêðåòíîé êîìàíäå
1595 if Data.ScoreTeam in [2..3] then
1596 begin
1597 if (Data.ScoreAction = 0) and (Data.ScoreTeam = 2) then
1598 begin
1599 Inc(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Scores
1601 if Data.ScoreCon then
1602 begin
1603 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_RED], Data.ScoreCount]), True);
1604 if g_Game_IsServer and g_Game_IsNet then
1605 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '+tr');
1606 end;
1608 if Data.ScoreMsg then
1609 begin
1610 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1611 if g_Game_IsServer and g_Game_IsNet then
1612 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1613 end;
1614 end;
1615 if (Data.ScoreAction = 1) and (Data.ScoreTeam = 2) then
1616 begin
1617 Dec(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Fouls
1619 if Data.ScoreCon then
1620 begin
1621 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_RED], Data.ScoreCount]), True);
1622 if g_Game_IsServer and g_Game_IsNet then
1623 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '-tr');
1624 end;
1626 if Data.ScoreMsg then
1627 begin
1628 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1629 if g_Game_IsServer and g_Game_IsNet then
1630 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1631 end;
1632 end;
1633 if (Data.ScoreAction = 0) and (Data.ScoreTeam = 3) then
1634 begin
1635 Inc(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Scores
1637 if Data.ScoreCon then
1638 begin
1639 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_BLUE], Data.ScoreCount]), True);
1640 if g_Game_IsServer and g_Game_IsNet then
1641 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '+tb');
1642 end;
1644 if Data.ScoreMsg then
1645 begin
1646 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1647 if g_Game_IsServer and g_Game_IsNet then
1648 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1649 end;
1650 end;
1651 if (Data.ScoreAction = 1) and (Data.ScoreTeam = 3) then
1652 begin
1653 Dec(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Fouls
1655 if Data.ScoreCon then
1656 begin
1657 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_BLUE], Data.ScoreCount]), True);
1658 if g_Game_IsServer and g_Game_IsNet then
1659 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '-tb');
1660 end;
1662 if Data.ScoreMsg then
1663 begin
1664 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1665 if g_Game_IsServer and g_Game_IsNet then
1666 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1667 end;
1668 end;
1669 Result := True;
1670 end;
1671 end;
1672 // Âûèãðûø
1673 if (Data.ScoreAction = 2) and (gGameSettings.GoalLimit > 0) then
1674 begin
1675 // Ñâîåé èëè ÷óæîé êîìàíäû
1676 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1677 begin
1678 p := g_Player_Get(ActivateUID);
1679 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_RED)) // Red Wins
1680 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1681 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1682 begin
1683 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1685 if Data.ScoreCon then
1686 if Data.ScoreTeam = 0 then
1687 begin
1688 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1689 if g_Game_IsServer and g_Game_IsNet then
1690 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1691 end else
1692 begin
1693 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1694 if g_Game_IsServer and g_Game_IsNet then
1695 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1696 end;
1698 Result := True;
1699 end;
1700 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Blue Wins
1701 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1702 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1703 begin
1704 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1706 if Data.ScoreCon then
1707 if Data.ScoreTeam = 0 then
1708 begin
1709 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1710 if g_Game_IsServer and g_Game_IsNet then
1711 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1712 end else
1713 begin
1714 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1715 if g_Game_IsServer and g_Game_IsNet then
1716 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1717 end;
1719 Result := True;
1720 end;
1721 end;
1722 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1723 if Data.ScoreTeam in [2..3] then
1724 begin
1725 if Data.ScoreTeam = 2 then // Red Wins
1726 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1727 begin
1728 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1729 Result := True;
1730 end;
1731 if Data.ScoreTeam = 3 then // Blue Wins
1732 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1733 begin
1734 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1735 Result := True;
1736 end;
1737 end;
1738 end;
1739 // Ïðîèãðûø
1740 if (Data.ScoreAction = 3) and (gGameSettings.GoalLimit > 0) then
1741 begin
1742 // Ñâîåé èëè ÷óæîé êîìàíäû
1743 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1744 begin
1745 p := g_Player_Get(ActivateUID);
1746 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Red Wins
1747 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1748 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1749 begin
1750 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1752 if Data.ScoreCon then
1753 if Data.ScoreTeam = 0 then
1754 begin
1755 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1756 if g_Game_IsServer and g_Game_IsNet then
1757 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1758 end else
1759 begin
1760 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1761 if g_Game_IsServer and g_Game_IsNet then
1762 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1763 end;
1765 Result := True;
1766 end;
1767 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_RED)) // Blue Wins
1768 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1769 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1770 begin
1771 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1773 if Data.ScoreCon then
1774 if Data.ScoreTeam = 0 then
1775 begin
1776 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1777 if g_Game_IsServer and g_Game_IsNet then
1778 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1779 end else
1780 begin
1781 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1782 if g_Game_IsServer and g_Game_IsNet then
1783 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1784 end;
1786 Result := True;
1787 end;
1788 end;
1789 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1790 if Data.ScoreTeam in [2..3] then
1791 begin
1792 if Data.ScoreTeam = 3 then // Red Wins
1793 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1794 begin
1795 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1796 Result := True;
1797 end;
1798 if Data.ScoreTeam = 2 then // Blue Wins
1799 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1800 begin
1801 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1802 Result := True;
1803 end;
1804 end;
1805 end;
1806 if Result then begin
1807 if coolDown then
1808 TimeOut := 18
1809 else
1810 TimeOut := 0;
1811 if g_Game_IsServer and g_Game_IsNet then
1812 MH_SEND_GameStats;
1813 end;
1814 end;
1816 TRIGGER_MESSAGE:
1817 begin
1818 Result := tr_Message(Data.MessageKind, Data.MessageText,
1819 Data.MessageSendTo, Data.MessageTime,
1820 ActivateUID);
1821 TimeOut := 18;
1822 end;
1824 TRIGGER_DAMAGE, TRIGGER_HEALTH:
1825 begin
1826 Result := False;
1827 UIDType := g_GetUIDType(ActivateUID);
1828 if (UIDType = UID_PLAYER) or (UIDType = UID_MONSTER) then
1829 begin
1830 Result := True;
1831 k := -1;
1832 if coolDown then
1833 begin
1834 // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
1835 for idx := 0 to High(Activators) do
1836 if Activators[idx].UID = ActivateUID then
1837 begin
1838 k := idx;
1839 Break;
1840 end;
1841 if k = -1 then
1842 begin // Âèäèì åãî âïåðâûå
1843 // Çàïîìèíàåì åãî
1844 SetLength(Activators, Length(Activators) + 1);
1845 k := High(Activators);
1846 Activators[k].UID := ActivateUID;
1847 end else
1848 begin // Óæå âèäåëè åãî
1849 // Åñëè èíòåðâàë îòêëþ÷¸í, íî îí âñ¸ åù¸ â çîíå ïîðàæåíèÿ, äà¸ì åìó âðåìÿ
1850 if (Data.DamageInterval = 0) and (Activators[k].TimeOut > 0) then
1851 Activators[k].TimeOut := 65535;
1852 // Òàéìàóò ïðîø¸ë - ðàáîòàåì
1853 Result := Activators[k].TimeOut = 0;
1854 end;
1855 end;
1857 if Result then
1858 begin
1859 case UIDType of
1860 UID_PLAYER:
1861 begin
1862 p := g_Player_Get(ActivateUID);
1863 if p = nil then
1864 Exit;
1866 // Íàíîñèì óðîí èãðîêó
1867 if (TriggerType = TRIGGER_DAMAGE) and (Data.DamageValue > 0) then
1868 p.Damage(Data.DamageValue, 0, 0, 0, HIT_SOME);
1870 // Ëå÷èì èãðîêà
1871 if (TriggerType = TRIGGER_HEALTH) and (Data.HealValue > 0) then
1872 if p.Heal(Data.HealValue, not Data.HealMax) and (not Data.HealSilent) then
1873 begin
1874 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', p.Obj.X, p.Obj.Y);
1875 if g_Game_IsServer and g_Game_IsNet then
1876 MH_SEND_Sound(p.Obj.X, p.Obj.Y, 'SOUND_ITEM_GETITEM');
1877 end;
1878 end;
1880 UID_MONSTER:
1881 begin
1882 m := g_Monsters_ByUID(ActivateUID);
1883 if m = nil then
1884 Exit;
1886 // Íàíîñèì óðîí ìîíñòðó
1887 if (TriggerType = TRIGGER_DAMAGE) and (Data.DamageValue > 0) then
1888 m.Damage(Data.DamageValue, 0, 0, 0, HIT_SOME);
1890 // Ëå÷èì ìîíñòðà
1891 if (TriggerType = TRIGGER_HEALTH) and (Data.HealValue > 0) then
1892 if m.Heal(Data.HealValue) and (not Data.HealSilent) then
1893 begin
1894 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', m.Obj.X, m.Obj.Y);
1895 if g_Game_IsServer and g_Game_IsNet then
1896 MH_SEND_Sound(m.Obj.X, m.Obj.Y, 'SOUND_ITEM_GETITEM');
1897 end;
1898 end;
1899 end;
1900 // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
1901 if TriggerType = TRIGGER_DAMAGE then
1902 idx := Data.DamageInterval
1903 else
1904 idx := Data.HealInterval;
1905 if coolDown then
1906 if idx > 0 then
1907 Activators[k].TimeOut := idx
1908 else
1909 Activators[k].TimeOut := 65535;
1910 end;
1911 end;
1912 TimeOut := 0;
1913 end;
1915 TRIGGER_SHOT:
1916 begin
1917 if ShotSightTime > 0 then
1918 Exit;
1920 // put this at the beginning so it doesn't trigger itself
1921 TimeOut := Data.ShotWait + 1;
1923 wx := Data.ShotPos.X;
1924 wy := Data.ShotPos.Y;
1925 pAngle := -DegToRad(Data.ShotAngle);
1926 xd := wx + Round(Cos(pAngle) * 32.0);
1927 yd := wy + Round(Sin(pAngle) * 32.0);
1928 TargetUID := 0;
1930 case Data.ShotTarget of
1931 TRIGGER_SHOT_TARGET_MON: // monsters
1932 //TODO: accelerate this!
1933 g_Mons_ForEachAlive(monsShotTarget);
1935 TRIGGER_SHOT_TARGET_PLR: // players
1936 if gPlayers <> nil then
1937 for idx := Low(gPlayers) to High(gPlayers) do
1938 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1939 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1940 begin
1941 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1942 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1943 TargetUID := gPlayers[idx].UID;
1944 break;
1945 end;
1947 TRIGGER_SHOT_TARGET_RED: // red team
1948 if gPlayers <> nil then
1949 for idx := Low(gPlayers) to High(gPlayers) do
1950 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1951 (gPlayers[idx].Team = TEAM_RED) and
1952 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1953 begin
1954 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1955 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1956 TargetUID := gPlayers[idx].UID;
1957 break;
1958 end;
1960 TRIGGER_SHOT_TARGET_BLUE: // blue team
1961 if gPlayers <> nil then
1962 for idx := Low(gPlayers) to High(gPlayers) do
1963 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1964 (gPlayers[idx].Team = TEAM_BLUE) and
1965 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1966 begin
1967 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1968 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1969 TargetUID := gPlayers[idx].UID;
1970 break;
1971 end;
1973 TRIGGER_SHOT_TARGET_MONPLR: // monsters then players
1974 begin
1975 //TODO: accelerate this!
1976 g_Mons_ForEachAlive(monsShotTargetMonPlr);
1978 if (TargetUID = 0) and (gPlayers <> nil) then
1979 for idx := Low(gPlayers) to High(gPlayers) do
1980 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1981 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1982 begin
1983 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1984 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1985 TargetUID := gPlayers[idx].UID;
1986 break;
1987 end;
1988 end;
1990 TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
1991 begin
1992 if gPlayers <> nil then
1993 for idx := Low(gPlayers) to High(gPlayers) do
1994 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1995 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1996 begin
1997 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1998 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1999 TargetUID := gPlayers[idx].UID;
2000 break;
2001 end;
2002 if TargetUID = 0 then
2003 begin
2004 //TODO: accelerate this!
2005 g_Mons_ForEachAlive(monShotTargetPlrMon);
2006 end;
2007 end;
2009 else begin
2010 if (Data.ShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
2011 (Data.ShotType <> TRIGGER_SHOT_REV) then
2012 TargetUID := ActivateUID;
2013 end;
2014 end;
2016 if (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
2017 ((Data.ShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
2018 begin
2019 Result := True;
2020 if (Data.ShotIntSight = 0) or
2021 (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or
2022 (TargetUID = ShotSightTarget) then
2023 MakeShot(Trigger, wx, wy, xd, yd, TargetUID)
2024 else
2025 begin
2026 ShotSightTime := Data.ShotIntSight;
2027 ShotSightTargetN := TargetUID;
2028 if Data.ShotType = TRIGGER_SHOT_BFG then
2029 begin
2030 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', wx, wy);
2031 if g_Game_IsNet and g_Game_IsServer then
2032 MH_SEND_Sound(wx, wy, 'SOUND_WEAPON_STARTFIREBFG');
2033 end;
2034 end;
2035 end;
2036 end;
2038 TRIGGER_EFFECT:
2039 begin
2040 idx := Data.FXCount;
2042 while idx > 0 do
2043 begin
2044 case Data.FXPos of
2045 TRIGGER_EFFECT_POS_CENTER:
2046 begin
2047 wx := X + Width div 2;
2048 wy := Y + Height div 2;
2049 end;
2050 TRIGGER_EFFECT_POS_AREA:
2051 begin
2052 wx := X + Random(Width);
2053 wy := Y + Random(Height);
2054 end;
2055 else begin
2056 wx := X + Width div 2;
2057 wy := Y + Height div 2;
2058 end;
2059 end;
2060 xd := Data.FXVelX;
2061 yd := Data.FXVelY;
2062 if Data.FXSpreadL > 0 then xd := xd - Random(Data.FXSpreadL + 1);
2063 if Data.FXSpreadR > 0 then xd := xd + Random(Data.FXSpreadR + 1);
2064 if Data.FXSpreadU > 0 then yd := yd - Random(Data.FXSpreadU + 1);
2065 if Data.FXSpreadD > 0 then yd := yd + Random(Data.FXSpreadD + 1);
2066 tr_MakeEffect(wx, wy, xd, yd,
2067 Data.FXType, Data.FXSubType,
2068 Data.FXColorR, Data.FXColorG, Data.FXColorB, True, False);
2069 Dec(idx);
2070 end;
2071 TimeOut := Data.FXWait;
2072 end;
2073 end;
2074 end;
2076 if Result and (Trigger.TexturePanel <> -1) then
2077 g_Map_SwitchTexture(Trigger.TexturePanelType, Trigger.TexturePanel, IfThen(animonce, 2, 1));
2078 end;
2080 function g_Triggers_Create(Trigger: TTrigger): DWORD;
2081 var
2082 find_id: DWORD;
2083 fn, mapw: String;
2084 begin
2085 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà:
2086 if (Trigger.TriggerType = TRIGGER_EXIT) and
2087 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2088 Trigger.TriggerType := TRIGGER_NONE;
2090 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð:
2091 if (Trigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2092 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2093 (gGameSettings.GameType <> GT_SINGLE) then
2094 Trigger.TriggerType := TRIGGER_NONE;
2096 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå:
2097 if Trigger.TriggerType = TRIGGER_SECRET then
2098 gSecretsCount := gSecretsCount + 1;
2100 find_id := FindTrigger();
2101 gTriggers[find_id] := Trigger;
2103 with gTriggers[find_id] do
2104 begin
2105 ID := find_id;
2106 // if this type of trigger exists both on the client and on the server
2107 // use an uniform numeration
2108 if Trigger.TriggerType = TRIGGER_SOUND then
2109 begin
2110 Inc(gTriggerClientID);
2111 ClientID := gTriggerClientID;
2112 end
2113 else
2114 ClientID := 0;
2115 TimeOut := 0;
2116 ActivateUID := 0;
2117 PlayerCollide := False;
2118 DoorTime := -1;
2119 PressTime := -1;
2120 PressCount := 0;
2121 SoundPlayCount := 0;
2122 Sound := nil;
2123 AutoSpawn := False;
2124 SpawnCooldown := 0;
2125 SpawnedCount := 0;
2126 end;
2128 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê":
2129 if (Trigger.TriggerType = TRIGGER_SOUND) and
2130 (Trigger.Data.SoundName <> '') then
2131 begin
2132 // Åùå íåò òàêîãî çâóêà:
2133 if not g_Sound_Exists(Trigger.Data.SoundName) then
2134 begin
2135 fn := g_ExtractWadName(Trigger.Data.SoundName);
2137 if fn = '' then
2138 begin // Çâóê â ôàéëå ñ êàðòîé
2139 mapw := g_ExtractWadName(gMapInfo.Map);
2140 fn := mapw+':'+g_ExtractFilePathName(Trigger.Data.SoundName);
2141 end
2142 else // Çâóê â îòäåëüíîì ôàéëå
2143 fn := GameDir + '/wads/' + Trigger.Data.SoundName;
2145 if not g_Sound_CreateWADEx(Trigger.Data.SoundName, fn) then
2146 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.Data.SoundName]));
2147 end;
2149 // Ñîçäàåì îáúåêò çâóêà:
2150 with gTriggers[find_id] do
2151 begin
2152 Sound := TPlayableSound.Create();
2153 if not Sound.SetByName(Trigger.Data.SoundName) then
2154 begin
2155 Sound.Free();
2156 Sound := nil;
2157 end;
2158 end;
2159 end;
2161 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà":
2162 if (Trigger.TriggerType = TRIGGER_MUSIC) and
2163 (Trigger.Data.MusicName <> '') then
2164 begin
2165 // Åùå íåò òàêîé ìóçûêè:
2166 if not g_Sound_Exists(Trigger.Data.MusicName) then
2167 begin
2168 fn := g_ExtractWadName(Trigger.Data.MusicName);
2170 if fn = '' then
2171 begin // Ìóçûêà â ôàéëå ñ êàðòîé
2172 mapw := g_ExtractWadName(gMapInfo.Map);
2173 fn := mapw+':'+g_ExtractFilePathName(Trigger.Data.MusicName);
2174 end
2175 else // Ìóçûêà â ôàéëå ñ êàðòîé
2176 fn := GameDir+'/wads/'+Trigger.Data.MusicName;
2178 if not g_Sound_CreateWADEx(Trigger.Data.MusicName, fn, True) then
2179 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.Data.MusicName]));
2180 end;
2181 end;
2183 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü":
2184 if Trigger.TriggerType = TRIGGER_SHOT then
2185 with gTriggers[find_id] do
2186 begin
2187 ShotPanelTime := 0;
2188 ShotSightTime := 0;
2189 ShotSightTimeout := 0;
2190 ShotSightTarget := 0;
2191 ShotSightTargetN := 0;
2192 ShotAmmoCount := Trigger.Data.ShotAmmo;
2193 ShotReloadTime := 0;
2194 end;
2196 Result := find_id;
2197 end;
2200 // sorry; grid doesn't support recursive queries, so we have to do this
2201 type
2202 TSimpleMonsterList = specialize TSimpleList<TMonster>;
2204 var
2205 tgMonsList: TSimpleMonsterList = nil;
2207 procedure g_Triggers_Update();
2208 var
2209 a, b, i: Integer;
2210 Affected: array of Integer;
2212 function monsNear (mon: TMonster): Boolean;
2213 begin
2214 result := false; // don't stop
2216 gTriggers[a].ActivateUID := mon.UID;
2217 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2219 tgMonsList.append(mon);
2220 end;
2222 var
2223 mon: TMonster;
2224 begin
2225 if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
2227 if gTriggers = nil then
2228 Exit;
2229 SetLength(Affected, 0);
2231 for a := 0 to High(gTriggers) do
2232 with gTriggers[a] do
2233 // Åñòü òðèããåð:
2234 if TriggerType <> TRIGGER_NONE then
2235 begin
2236 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè):
2237 if DoorTime > 0 then
2238 DoorTime := DoorTime - 1;
2239 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ:
2240 if PressTime > 0 then
2241 PressTime := PressTime - 1;
2242 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2243 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2244 for b := 0 to High(Activators) do
2245 begin
2246 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2247 if Activators[b].TimeOut > 0 then
2248 Dec(Activators[b].TimeOut)
2249 else
2250 Continue;
2251 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2252 if (Data.DamageInterval = 0) and (Activators[b].TimeOut < 65530) then
2253 Activators[b].TimeOut := 0;
2254 end;
2256 // Îáðàáàòûâàåì ñïàâíåðû:
2257 if Enabled and AutoSpawn then
2258 if SpawnCooldown = 0 then
2259 begin
2260 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà:
2261 if (TriggerType = TRIGGER_SPAWNMONSTER) and (Data.MonDelay > 0) then
2262 begin
2263 ActivateUID := 0;
2264 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2265 end;
2266 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò:
2267 if (TriggerType = TRIGGER_SPAWNITEM) and (Data.ItemDelay > 0) then
2268 begin
2269 ActivateUID := 0;
2270 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2271 end;
2272 end else // Óìåíüøàåì âðåìÿ îæèäàíèÿ:
2273 Dec(SpawnCooldown);
2275 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü":
2276 if TriggerType = TRIGGER_SHOT then
2277 begin
2278 if ShotPanelTime > 0 then
2279 begin
2280 Dec(ShotPanelTime);
2281 if ShotPanelTime = 0 then
2282 g_Map_SwitchTexture(ShotPanelType, Data.ShotPanelID);
2283 end;
2284 if ShotSightTime > 0 then
2285 begin
2286 Dec(ShotSightTime);
2287 if ShotSightTime = 0 then
2288 ShotSightTarget := ShotSightTargetN;
2289 end;
2290 if ShotSightTimeout > 0 then
2291 begin
2292 Dec(ShotSightTimeout);
2293 if ShotSightTimeout = 0 then
2294 ShotSightTarget := 0;
2295 end;
2296 if ShotReloadTime > 0 then
2297 begin
2298 Dec(ShotReloadTime);
2299 if ShotReloadTime = 0 then
2300 ShotAmmoCount := Data.ShotAmmo;
2301 end;
2302 end;
2304 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì:
2305 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2306 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2307 begin
2308 if Data.PlayCount > 0 then // Åñëè 0 - èãðàåì çâóê áåñêîíå÷íî
2309 SoundPlayCount := SoundPlayCount - 1;
2310 if Data.Local then
2311 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0)
2312 else
2313 Sound.PlayPanVolume((Data.Pan-127.0)/128.0, Data.Volume/255.0);
2314 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then
2315 MH_SEND_TriggerSound(gTriggers[a]);
2316 end;
2318 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü:
2319 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (Data.PanelID <> -1) then
2320 begin
2321 tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
2322 DoorTime := -1;
2323 end;
2325 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü:
2326 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (Data.PanelID <> -1) then
2327 begin
2328 // Óæå çàêðûòà:
2329 if gWalls[Data.PanelID].Enabled then
2330 DoorTime := -1
2331 else // Ïîêà îòêðûòà - çàêðûâàåì
2332 if tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors) then
2333 DoorTime := -1;
2334 end;
2336 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2337 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2338 (PressTime = 0) and (PressCount >= Data.Count) then
2339 begin
2340 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2341 PressTime := -1;
2342 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2343 if Data.Count > 0 then
2344 PressCount := PressCount - Data.Count
2345 else
2346 PressCount := 0;
2348 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2349 for b := 0 to High(gTriggers) do
2350 if g_Collide(Data.tX, Data.tY, Data.tWidth, Data.tHeight, gTriggers[b].X, gTriggers[b].Y,
2351 gTriggers[b].Width, gTriggers[b].Height) and
2352 ((b <> a) or (Data.Wait > 0)) then
2353 begin // Can be self-activated, if there is Data.Wait
2354 if (not Data.ExtRandom) or gTriggers[b].Enabled then
2355 begin
2356 SetLength(Affected, Length(Affected) + 1);
2357 Affected[High(Affected)] := b;
2358 end;
2359 end;
2360 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2361 if (TriggerType = TRIGGER_PRESS) and Data.ExtRandom then
2362 begin
2363 if (Length(Affected) > 0) then
2364 begin
2365 b := Affected[Random(Length(Affected))];
2366 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2367 ActivateTrigger(gTriggers[b], 0);
2368 end;
2369 end
2370 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2371 for i := 0 to High(Affected) do
2372 begin
2373 b := Affected[i];
2374 case TriggerType of
2375 TRIGGER_PRESS:
2376 begin
2377 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2378 ActivateTrigger(gTriggers[b], 0);
2379 end;
2380 TRIGGER_ON:
2381 begin
2382 gTriggers[b].Enabled := True;
2383 end;
2384 TRIGGER_OFF:
2385 begin
2386 gTriggers[b].Enabled := False;
2387 gTriggers[b].TimeOut := 0;
2388 if gTriggers[b].AutoSpawn then
2389 begin
2390 gTriggers[b].AutoSpawn := False;
2391 gTriggers[b].SpawnCooldown := 0;
2392 end;
2393 end;
2394 TRIGGER_ONOFF:
2395 begin
2396 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2397 if not gTriggers[b].Enabled then
2398 begin
2399 gTriggers[b].TimeOut := 0;
2400 if gTriggers[b].AutoSpawn then
2401 begin
2402 gTriggers[b].AutoSpawn := False;
2403 gTriggers[b].SpawnCooldown := 0;
2404 end;
2405 end;
2406 end;
2407 end;
2408 end;
2409 SetLength(Affected, 0);
2410 end;
2412 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2413 if TimeOut > 0 then
2414 begin
2415 TimeOut := TimeOut - 1;
2416 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2417 end;
2419 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2420 if not Enabled then
2421 Continue;
2423 // "Èãðîê áëèçêî":
2424 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2425 (TimeOut = 0) then
2426 if gPlayers <> nil then
2427 for b := 0 to High(gPlayers) do
2428 if gPlayers[b] <> nil then
2429 with gPlayers[b] do
2430 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2431 if Live and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2432 Collide(X, Y, Width, Height) then
2433 begin
2434 gTriggers[a].ActivateUID := UID;
2436 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2437 PlayerCollide then
2438 { Don't activate sound/music again if player is here }
2439 else
2440 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2441 end;
2443 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2445 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2446 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2447 (TimeOut = 0) and (Keys = 0) then
2448 begin
2449 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2450 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2451 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2452 gTriggers[a].ActivateUID := 0;
2453 ActivateTrigger(gTriggers[a], 0);
2454 end else
2455 begin
2456 // "Ìîíñòð áëèçêî"
2457 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2458 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2459 begin
2460 //g_Mons_ForEach(monsNear);
2461 //Alive?!
2462 tgMonsList.reset();
2463 g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
2464 for mon in tgMonsList do
2465 begin
2466 gTriggers[a].ActivateUID := mon.UID;
2467 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2468 end;
2469 tgMonsList.reset(); // just in case
2470 end;
2472 // "Ìîíñòðîâ íåò"
2473 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2474 (TimeOut = 0) and (Keys = 0) then
2475 if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
2476 begin
2477 gTriggers[a].ActivateUID := 0;
2478 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2479 end;
2480 end;
2482 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2483 end;
2484 end;
2486 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2487 begin
2488 gTriggers[ID].ActivateUID := ActivateUID;
2489 ActivateTrigger(gTriggers[ID], ActivateType);
2490 end;
2492 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2493 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2494 var
2495 a: Integer;
2496 k: Byte;
2497 p: TPlayer;
2498 begin
2499 Result := nil;
2501 if gTriggers = nil then Exit;
2503 case g_GetUIDType(UID) of
2504 UID_GAME: k := 255;
2505 UID_PLAYER:
2506 begin
2507 p := g_Player_Get(UID);
2508 if p <> nil then
2509 k := p.GetKeys
2510 else
2511 k := 0;
2512 end;
2513 else k := 0;
2514 end;
2516 for a := 0 to High(gTriggers) do
2517 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2518 (gTriggers[a].TimeOut = 0) and
2519 (not InDWArray(a, IgnoreList)) and
2520 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2521 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2522 if g_Collide(X, Y, Width, Height,
2523 gTriggers[a].X, gTriggers[a].Y,
2524 gTriggers[a].Width, gTriggers[a].Height) then
2525 begin
2526 gTriggers[a].ActivateUID := UID;
2527 if ActivateTrigger(gTriggers[a], ActivateType) then
2528 begin
2529 SetLength(Result, Length(Result)+1);
2530 Result[High(Result)] := a;
2531 end;
2532 end;
2533 end;
2535 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2536 var
2537 a: Integer;
2538 k: Byte;
2539 p: TPlayer;
2540 begin
2541 if gTriggers = nil then Exit;
2543 case g_GetUIDType(UID) of
2544 UID_GAME: k := 255;
2545 UID_PLAYER:
2546 begin
2547 p := g_Player_Get(UID);
2548 if p <> nil then
2549 k := p.GetKeys
2550 else
2551 k := 0;
2552 end;
2553 else k := 0;
2554 end;
2556 for a := 0 to High(gTriggers) do
2557 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2558 (gTriggers[a].TimeOut = 0) and
2559 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2560 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2561 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2562 gTriggers[a].Width, gTriggers[a].Height) then
2563 begin
2564 gTriggers[a].ActivateUID := UID;
2565 ActivateTrigger(gTriggers[a], ActivateType);
2566 end;
2567 end;
2569 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2570 var
2571 a: Integer;
2572 k: Byte;
2573 rsq: Word;
2574 p: TPlayer;
2575 begin
2576 if gTriggers = nil then
2577 Exit;
2579 case g_GetUIDType(UID) of
2580 UID_GAME: k := 255;
2581 UID_PLAYER:
2582 begin
2583 p := g_Player_Get(UID);
2584 if p <> nil then
2585 k := p.GetKeys
2586 else
2587 k := 0;
2588 end;
2589 else k := 0;
2590 end;
2592 rsq := Radius * Radius;
2594 for a := 0 to High(gTriggers) do
2595 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
2596 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2597 (gTriggers[a].TimeOut = 0) and
2598 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2599 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2600 with gTriggers[a] do
2601 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
2602 X, Y, Width, Height) then
2603 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
2604 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
2605 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
2606 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
2607 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
2608 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2609 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
2610 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2611 begin
2612 ActivateUID := UID;
2613 ActivateTrigger(gTriggers[a], ActivateType);
2614 end;
2615 end;
2617 procedure g_Triggers_OpenAll();
2618 var
2619 a: Integer;
2620 b: Boolean;
2621 begin
2622 if gTriggers = nil then Exit;
2624 b := False;
2625 for a := 0 to High(gTriggers) do
2626 with gTriggers[a] do
2627 if (TriggerType = TRIGGER_OPENDOOR) or
2628 (TriggerType = TRIGGER_DOOR5) or
2629 (TriggerType = TRIGGER_DOOR) then
2630 begin
2631 tr_OpenDoor(Data.PanelID, True, Data.d2d_doors);
2632 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
2633 b := True;
2634 end;
2636 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
2637 end;
2639 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
2640 begin
2641 if (gTriggers <> nil) then
2642 if gTriggers[ID].SpawnedCount > 0 then
2643 Dec(gTriggers[ID].SpawnedCount);
2644 end;
2646 procedure g_Triggers_Free();
2647 var
2648 a: Integer;
2649 begin
2650 if gTriggers <> nil then
2651 for a := 0 to High(gTriggers) do
2652 begin
2653 if gTriggers[a].TriggerType = TRIGGER_SOUND then
2654 begin
2655 if g_Sound_Exists(gTriggers[a].Data.SoundName) then
2656 g_Sound_Delete(gTriggers[a].Data.SoundName);
2658 gTriggers[a].Sound.Free();
2659 end;
2660 if gTriggers[a].Activators <> nil then
2661 SetLength(gTriggers[a].Activators, 0);
2662 end;
2664 gTriggers := nil;
2665 gSecretsCount := 0;
2666 SetLength(gMonstersSpawned, 0);
2667 end;
2669 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
2670 var
2671 count, act_count, i, j: Integer;
2672 dw: DWORD;
2673 sg: Single;
2674 b: Boolean;
2675 p: Pointer;
2676 begin
2677 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ:
2678 count := 0;
2679 if gTriggers <> nil then
2680 for i := 0 to High(gTriggers) do
2681 count := count + 1;
2683 Mem := TBinMemoryWriter.Create((count+1) * 200);
2685 // Êîëè÷åñòâî òðèããåðîâ:
2686 Mem.WriteInt(count);
2688 if count = 0 then
2689 Exit;
2691 for i := 0 to High(gTriggers) do
2692 begin
2693 // Ñèãíàòóðà òðèããåðà:
2694 dw := TRIGGER_SIGNATURE; // 'TRGR'
2695 Mem.WriteDWORD(dw);
2696 // Òèï òðèããåðà:
2697 Mem.WriteByte(gTriggers[i].TriggerType);
2698 // Ñïåöèàëüíûå äàííûå òðèããåðà:
2699 p := @gTriggers[i].Data;
2700 Mem.WriteMemory(p, SizeOf(TTriggerData));
2701 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2702 Mem.WriteInt(gTriggers[i].X);
2703 Mem.WriteInt(gTriggers[i].Y);
2704 // Ðàçìåðû:
2705 Mem.WriteWord(gTriggers[i].Width);
2706 Mem.WriteWord(gTriggers[i].Height);
2707 // Âêëþ÷åí ëè òðèããåð:
2708 Mem.WriteBoolean(gTriggers[i].Enabled);
2709 // Òèï àêòèâàöèè òðèããåðà:
2710 Mem.WriteByte(gTriggers[i].ActivateType);
2711 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2712 Mem.WriteByte(gTriggers[i].Keys);
2713 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2714 Mem.WriteInt(gTriggers[i].TexturePanel);
2715 // Òèï ýòîé ïàíåëè:
2716 Mem.WriteWord(gTriggers[i].TexturePanelType);
2717 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2718 Mem.WriteWord(gTriggers[i].TimeOut);
2719 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2720 Mem.WriteWord(gTriggers[i].ActivateUID);
2721 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2722 act_count := Length(gTriggers[i].Activators);
2723 Mem.WriteInt(act_count);
2724 for j := 0 to act_count-1 do
2725 begin
2726 // UID îáúåêòà
2727 Mem.WriteWord(gTriggers[i].Activators[j].UID);
2728 // Âðåìÿ îæèäàíèÿ
2729 Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
2730 end;
2731 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2732 Mem.WriteBoolean(gTriggers[i].PlayerCollide);
2733 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2734 Mem.WriteInt(gTriggers[i].DoorTime);
2735 // Çàäåðæêà àêòèâàöèè:
2736 Mem.WriteInt(gTriggers[i].PressTime);
2737 // Ñ÷åò÷èê íàæàòèé:
2738 Mem.WriteInt(gTriggers[i].PressCount);
2739 // Ñïàâíåð àêòèâåí:
2740 Mem.WriteBoolean(gTriggers[i].AutoSpawn);
2741 // Çàäåðæêà ñïàâíåðà:
2742 Mem.WriteInt(gTriggers[i].SpawnCooldown);
2743 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2744 Mem.WriteInt(gTriggers[i].SpawnedCount);
2745 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2746 Mem.WriteInt(gTriggers[i].SoundPlayCount);
2747 // Ïðîèãðûâàåòñÿ ëè çâóê?
2748 if gTriggers[i].Sound <> nil then
2749 b := gTriggers[i].Sound.IsPlaying()
2750 else
2751 b := False;
2752 Mem.WriteBoolean(b);
2753 if b then
2754 begin
2755 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2756 dw := gTriggers[i].Sound.GetPosition();
2757 Mem.WriteDWORD(dw);
2758 // Ãðîìêîñòü çâóêà:
2759 sg := gTriggers[i].Sound.GetVolume();
2760 sg := sg / (gSoundLevel/255.0);
2761 Mem.WriteSingle(sg);
2762 // Ñòåðåî ñìåùåíèå çâóêà:
2763 sg := gTriggers[i].Sound.GetPan();
2764 Mem.WriteSingle(sg);
2765 end;
2766 end;
2767 end;
2769 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
2770 var
2771 count, act_count, i, j, a: Integer;
2772 dw: DWORD;
2773 vol, pan: Single;
2774 b: Boolean;
2775 p: Pointer;
2776 Trig: TTrigger;
2777 begin
2778 if Mem = nil then
2779 Exit;
2781 g_Triggers_Free();
2783 // Êîëè÷åñòâî òðèããåðîâ:
2784 Mem.ReadInt(count);
2786 if count = 0 then
2787 Exit;
2789 for a := 0 to count-1 do
2790 begin
2791 // Ñèãíàòóðà òðèããåðà:
2792 Mem.ReadDWORD(dw);
2793 if dw <> TRIGGER_SIGNATURE then // 'TRGR'
2794 begin
2795 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
2796 end;
2797 // Òèï òðèããåðà:
2798 Mem.ReadByte(Trig.TriggerType);
2799 // Ñïåöèàëüíûå äàííûå òðèããåðà:
2800 Mem.ReadMemory(p, dw);
2801 if dw <> SizeOf(TTriggerData) then
2802 begin
2803 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong TriggerData Size');
2804 end;
2805 Trig.Data := TTriggerData(p^);
2806 // Ñîçäàåì òðèããåð:
2807 i := g_Triggers_Create(Trig);
2808 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2809 Mem.ReadInt(gTriggers[i].X);
2810 Mem.ReadInt(gTriggers[i].Y);
2811 // Ðàçìåðû:
2812 Mem.ReadWord(gTriggers[i].Width);
2813 Mem.ReadWord(gTriggers[i].Height);
2814 // Âêëþ÷åí ëè òðèããåð:
2815 Mem.ReadBoolean(gTriggers[i].Enabled);
2816 // Òèï àêòèâàöèè òðèããåðà:
2817 Mem.ReadByte(gTriggers[i].ActivateType);
2818 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2819 Mem.ReadByte(gTriggers[i].Keys);
2820 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2821 Mem.ReadInt(gTriggers[i].TexturePanel);
2822 // Òèï ýòîé ïàíåëè:
2823 Mem.ReadWord(gTriggers[i].TexturePanelType);
2824 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2825 Mem.ReadWord(gTriggers[i].TimeOut);
2826 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2827 Mem.ReadWord(gTriggers[i].ActivateUID);
2828 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2829 Mem.ReadInt(act_count);
2830 if act_count > 0 then
2831 begin
2832 SetLength(gTriggers[i].Activators, act_count);
2833 for j := 0 to act_count-1 do
2834 begin
2835 // UID îáúåêòà
2836 Mem.ReadWord(gTriggers[i].Activators[j].UID);
2837 // Âðåìÿ îæèäàíèÿ
2838 Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
2839 end;
2840 end;
2841 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2842 Mem.ReadBoolean(gTriggers[i].PlayerCollide);
2843 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2844 Mem.ReadInt(gTriggers[i].DoorTime);
2845 // Çàäåðæêà àêòèâàöèè:
2846 Mem.ReadInt(gTriggers[i].PressTime);
2847 // Ñ÷åò÷èê íàæàòèé:
2848 Mem.ReadInt(gTriggers[i].PressCount);
2849 // Ñïàâíåð àêòèâåí:
2850 Mem.ReadBoolean(gTriggers[i].AutoSpawn);
2851 // Çàäåðæêà ñïàâíåðà:
2852 Mem.ReadInt(gTriggers[i].SpawnCooldown);
2853 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2854 Mem.ReadInt(gTriggers[i].SpawnedCount);
2855 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2856 Mem.ReadInt(gTriggers[i].SoundPlayCount);
2857 // Ïðîèãðûâàåòñÿ ëè çâóê?
2858 Mem.ReadBoolean(b);
2859 if b then
2860 begin
2861 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2862 Mem.ReadDWORD(dw);
2863 // Ãðîìêîñòü çâóêà:
2864 Mem.ReadSingle(vol);
2865 // Ñòåðåî ñìåùåíèå çâóêà:
2866 Mem.ReadSingle(pan);
2867 // Çàïóñêàåì çâóê, åñëè åñòü:
2868 if gTriggers[i].Sound <> nil then
2869 begin
2870 gTriggers[i].Sound.PlayPanVolume(pan, vol);
2871 gTriggers[i].Sound.Pause(True);
2872 gTriggers[i].Sound.SetPosition(dw);
2873 end
2874 end;
2875 end;
2876 end;
2878 end.