DEADSOFTWARE

no more public `gMonsters`
[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;
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_CollideMonster(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_CollideMonster(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 (monidx: Integer; mon: TMonster): Boolean;
215 begin
216 result := false; // don't stop
217 if (mon <> nil) and mon.Live and g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then
218 begin
219 mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
220 end;
221 end;
223 begin
224 if PanelID = -1 then Exit;
226 if not d2d then
227 begin
228 with gWalls[PanelID] do
229 begin
230 if (not NoSound) and (not Enabled) then
231 begin
232 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
233 if g_Game_IsServer and g_Game_IsNet then
234 MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
235 end;
236 end;
238 wx := gWalls[PanelID].X;
239 wy := gWalls[PanelID].Y;
240 ww := gWalls[PanelID].Width;
241 wh := gWalls[PanelID].Height;
243 with gWalls[PanelID] do
244 begin
245 if gPlayers <> nil then
246 for a := 0 to High(gPlayers) do
247 if (gPlayers[a] <> nil) and gPlayers[a].Live and
248 gPlayers[a].Collide(X, Y, Width, Height) then
249 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
251 g_Mons_ForEach(monsDamage);
253 if not Enabled then g_Map_EnableWall(PanelID);
254 end;
255 end
256 else
257 begin
258 if gDoorMap = nil then Exit;
260 c := -1;
261 for a := 0 to High(gDoorMap) do
262 begin
263 for b := 0 to High(gDoorMap[a]) do
264 begin
265 if gDoorMap[a, b] = DWORD(PanelID) then
266 begin
267 c := a;
268 Break;
269 end;
270 end;
272 if c <> -1 then Break;
273 end;
274 if c = -1 then Exit;
276 if not NoSound then
277 begin
278 for b := 0 to High(gDoorMap[c]) do
279 begin
280 if not gWalls[gDoorMap[c, b]].Enabled then
281 begin
282 with gWalls[PanelID] do
283 begin
284 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
285 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
286 end;
287 Break;
288 end;
289 end;
290 end;
292 for b := 0 to High(gDoorMap[c]) do
293 begin
294 wx := gWalls[gDoorMap[c, b]].X;
295 wy := gWalls[gDoorMap[c, b]].Y;
296 ww := gWalls[gDoorMap[c, b]].Width;
297 wh := gWalls[gDoorMap[c, b]].Height;
299 with gWalls[gDoorMap[c, b]] do
300 begin
301 if gPlayers <> nil then
302 for a := 0 to High(gPlayers) do
303 if (gPlayers[a] <> nil) and gPlayers[a].Live and
304 gPlayers[a].Collide(X, Y, Width, Height) then
305 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
307 g_Mons_ForEach(monsDamage);
308 (*
309 if gMonsters <> nil then
310 for a := 0 to High(gMonsters) do
311 if (gMonsters[a] <> nil) and gMonsters[a].Live and
312 g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
313 gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
314 *)
316 if not Enabled then g_Map_EnableWall(gDoorMap[c, b]);
317 end;
318 end;
319 end;
320 end;
322 function tr_OpenDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
323 var
324 a, b, c: Integer;
325 begin
326 Result := False;
328 if PanelID = -1 then Exit;
330 if not d2d then
331 begin
332 with gWalls[PanelID] do
333 if Enabled then
334 begin
335 if not NoSound then
336 begin
337 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
338 if g_Game_IsServer and g_Game_IsNet then
339 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
340 end;
341 g_Map_DisableWall(PanelID);
342 Result := True;
343 end;
344 end
345 else
346 begin
347 if gDoorMap = nil then Exit;
349 c := -1;
350 for a := 0 to High(gDoorMap) do
351 begin
352 for b := 0 to High(gDoorMap[a]) do
353 if gDoorMap[a, b] = DWORD(PanelID) then
354 begin
355 c := a;
356 Break;
357 end;
359 if c <> -1 then Break;
360 end;
361 if c = -1 then Exit;
363 if not NoSound then
364 for b := 0 to High(gDoorMap[c]) do
365 if gWalls[gDoorMap[c, b]].Enabled then
366 begin
367 with gWalls[PanelID] do
368 begin
369 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
370 if g_Game_IsServer and g_Game_IsNet then
371 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
372 end;
373 Break;
374 end;
376 for b := 0 to High(gDoorMap[c]) do
377 if gWalls[gDoorMap[c, b]].Enabled then
378 begin
379 g_Map_DisableWall(gDoorMap[c, b]);
380 Result := True;
381 end;
382 end;
383 end;
385 function tr_SetLift(PanelID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
386 var
387 a, b, c, t: Integer;
388 begin
389 t := 0;
390 Result := False;
392 if PanelID = -1 then Exit;
394 if (gLifts[PanelID].PanelType = PANEL_LIFTUP) or
395 (gLifts[PanelID].PanelType = PANEL_LIFTDOWN) then
396 case d of
397 0: t := 0;
398 1: t := 1;
399 else t := IfThen(gLifts[PanelID].LiftType = 1, 0, 1);
400 end
401 else if (gLifts[PanelID].PanelType = PANEL_LIFTLEFT) or
402 (gLifts[PanelID].PanelType = PANEL_LIFTRIGHT) then
403 case d of
404 0: t := 2;
405 1: t := 3;
406 else t := IfThen(gLifts[PanelID].LiftType = 2, 3, 2);
407 end;
409 if not d2d then
410 begin
411 with gLifts[PanelID] do
412 if LiftType <> t then
413 begin
414 g_Map_SetLift(PanelID, t);
416 {if not NoSound then
417 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);}
418 Result := True;
419 end;
420 end
421 else // Êàê â D2d
422 begin
423 if gLiftMap = nil then Exit;
425 c := -1;
426 for a := 0 to High(gLiftMap) do
427 begin
428 for b := 0 to High(gLiftMap[a]) do
429 if gLiftMap[a, b] = DWORD(PanelID) then
430 begin
431 c := a;
432 Break;
433 end;
435 if c <> -1 then Break;
436 end;
437 if c = -1 then Exit;
439 {if not NoSound then
440 for b := 0 to High(gLiftMap[c]) do
441 if gLifts[gLiftMap[c, b]].LiftType <> t then
442 begin
443 with gLifts[PanelID] do
444 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
445 Break;
446 end;}
448 for b := 0 to High(gLiftMap[c]) do
449 with gLifts[gLiftMap[c, b]] do
450 if LiftType <> t then
451 begin
452 g_Map_SetLift(gLiftMap[c, b], t);
454 Result := True;
455 end;
456 end;
457 end;
459 function tr_SpawnShot(ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
460 var
461 snd: string;
462 Projectile: Boolean;
463 TextureID: DWORD;
464 Anim: TAnimation;
465 begin
466 Result := -1;
467 TextureID := DWORD(-1);
468 snd := 'SOUND_WEAPON_FIREROCKET';
469 Projectile := True;
470 case ShotType of
471 TRIGGER_SHOT_PISTOL:
472 begin
473 g_Weapon_pistol(wx, wy, dx, dy, 0, True);
474 snd := 'SOUND_WEAPON_FIREPISTOL';
475 Projectile := False;
476 if ShotSound then
477 begin
478 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
479 if g_Game_IsNet then
480 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
481 end;
482 end;
484 TRIGGER_SHOT_BULLET:
485 begin
486 g_Weapon_mgun(wx, wy, dx, dy, 0, True);
487 if gSoundEffectsDF then snd := 'SOUND_WEAPON_FIRECGUN'
488 else snd := 'SOUND_WEAPON_FIREPISTOL';
489 Projectile := False;
490 if ShotSound then
491 begin
492 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
493 if g_Game_IsNet then
494 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
495 end;
496 end;
498 TRIGGER_SHOT_SHOTGUN:
499 begin
500 g_Weapon_Shotgun(wx, wy, dx, dy, 0, True);
501 snd := 'SOUND_WEAPON_FIRESHOTGUN';
502 Projectile := False;
503 if ShotSound then
504 begin
505 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
506 if g_Game_IsNet then
507 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL2);
508 end;
509 end;
511 TRIGGER_SHOT_SSG:
512 begin
513 g_Weapon_DShotgun(wx, wy, dx, dy, 0, True);
514 snd := 'SOUND_WEAPON_FIRESHOTGUN2';
515 Projectile := False;
516 if ShotSound then
517 begin
518 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
519 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
520 if g_Game_IsNet then
521 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL3);
522 end;
523 end;
525 TRIGGER_SHOT_IMP:
526 begin
527 g_Weapon_ball1(wx, wy, dx, dy, 0, -1, True);
528 snd := 'SOUND_WEAPON_FIREBALL';
529 end;
531 TRIGGER_SHOT_PLASMA:
532 begin
533 g_Weapon_Plasma(wx, wy, dx, dy, 0, -1, True);
534 snd := 'SOUND_WEAPON_FIREPLASMA';
535 end;
537 TRIGGER_SHOT_SPIDER:
538 begin
539 g_Weapon_aplasma(wx, wy, dx, dy, 0, -1, True);
540 snd := 'SOUND_WEAPON_FIREPLASMA';
541 end;
543 TRIGGER_SHOT_CACO:
544 begin
545 g_Weapon_ball2(wx, wy, dx, dy, 0, -1, True);
546 snd := 'SOUND_WEAPON_FIREBALL';
547 end;
549 TRIGGER_SHOT_BARON:
550 begin
551 g_Weapon_ball7(wx, wy, dx, dy, 0, -1, True);
552 snd := 'SOUND_WEAPON_FIREBALL';
553 end;
555 TRIGGER_SHOT_MANCUB:
556 begin
557 g_Weapon_manfire(wx, wy, dx, dy, 0, -1, True);
558 snd := 'SOUND_WEAPON_FIREBALL';
559 end;
561 TRIGGER_SHOT_REV:
562 begin
563 g_Weapon_revf(wx, wy, dx, dy, 0, ShotTarget, -1, True);
564 snd := 'SOUND_WEAPON_FIREREV';
565 end;
567 TRIGGER_SHOT_ROCKET:
568 begin
569 g_Weapon_Rocket(wx, wy, dx, dy, 0, -1, True);
570 snd := 'SOUND_WEAPON_FIREROCKET';
571 end;
573 TRIGGER_SHOT_BFG:
574 begin
575 g_Weapon_BFGShot(wx, wy, dx, dy, 0, -1, True);
576 snd := 'SOUND_WEAPON_FIREBFG';
577 end;
579 TRIGGER_SHOT_EXPL:
580 begin
581 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
582 begin
583 Anim := TAnimation.Create(TextureID, False, 6);
584 Anim.Blending := False;
585 g_GFX_OnceAnim(wx-64, wy-64, Anim);
586 Anim.Free();
587 end;
588 Projectile := False;
589 g_Weapon_Explode(wx, wy, 60, 0);
590 snd := 'SOUND_WEAPON_EXPLODEROCKET';
591 end;
593 TRIGGER_SHOT_BFGEXPL:
594 begin
595 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
596 begin
597 Anim := TAnimation.Create(TextureID, False, 6);
598 Anim.Blending := False;
599 g_GFX_OnceAnim(wx-64, wy-64, Anim);
600 Anim.Free();
601 end;
602 Projectile := False;
603 g_Weapon_BFG9000(wx, wy, 0);
604 snd := 'SOUND_WEAPON_EXPLODEBFG';
605 end;
607 else exit;
608 end;
610 if g_Game_IsNet and g_Game_IsServer then
611 case ShotType of
612 TRIGGER_SHOT_EXPL:
613 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_EXPLODE);
614 TRIGGER_SHOT_BFGEXPL:
615 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_BFGEXPL);
616 else
617 begin
618 if Projectile then
619 MH_SEND_CreateShot(LastShotID);
620 if ShotSound then
621 MH_SEND_Sound(wx, wy, snd);
622 end;
623 end;
625 if ShotSound then
626 g_Sound_PlayExAt(snd, wx, wy);
628 if Projectile then
629 Result := LastShotID;
630 end;
632 procedure MakeShot(var Trigger: TTrigger; wx, wy, dx, dy: Integer; TargetUID: Word);
633 begin
634 with Trigger do
635 if (Data.ShotAmmo = 0) or
636 ((Data.ShotAmmo > 0) and (ShotAmmoCount > 0)) then
637 begin
638 if (Data.ShotPanelID <> -1) and (ShotPanelTime = 0) then
639 begin
640 g_Map_SwitchTexture(ShotPanelType, Data.ShotPanelID);
641 ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
642 end;
644 if Data.ShotIntSight > 0 then
645 ShotSightTimeout := 180; // ~= 5 ñåêóíä
647 if ShotAmmoCount > 0 then Dec(ShotAmmoCount);
649 dx := dx + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
650 dy := dy + Random(Data.ShotAccuracy) - Random(Data.ShotAccuracy);
652 tr_SpawnShot(Data.ShotType, wx, wy, dx, dy, Data.ShotSound, TargetUID);
653 end
654 else
655 if (Data.ShotIntReload > 0) and (ShotReloadTime = 0) then
656 ShotReloadTime := Data.ShotIntReload; // òèêîâ íà ïåðåçàðÿäêó ïóøêè
657 end;
659 procedure tr_MakeEffect(X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
660 var
661 FramesID: DWORD;
662 Anim: TAnimation;
663 begin
664 if T = TRIGGER_EFFECT_PARTICLE then
665 case ST of
666 TRIGGER_EFFECT_SLIQUID:
667 begin
668 if (CR = 255) and (CG = 0) and (CB = 0) then
669 g_GFX_SimpleWater(X, Y, 1, VX, VY, 1, 0, 0, 0)
670 else if (CR = 0) and (CG = 255) and (CB = 0) then
671 g_GFX_SimpleWater(X, Y, 1, VX, VY, 2, 0, 0, 0)
672 else if (CR = 0) and (CG = 0) and (CB = 255) then
673 g_GFX_SimpleWater(X, Y, 1, VX, VY, 3, 0, 0, 0)
674 else
675 g_GFX_SimpleWater(X, Y, 1, VX, VY, 0, 0, 0, 0);
676 end;
677 TRIGGER_EFFECT_LLIQUID:
678 g_GFX_SimpleWater(X, Y, 1, VX, VY, 4, CR, CG, CB);
679 TRIGGER_EFFECT_DLIQUID:
680 g_GFX_SimpleWater(X, Y, 1, VX, VY, 5, CR, CG, CB);
681 TRIGGER_EFFECT_BLOOD:
682 g_GFX_Blood(X, Y, 1, VX, VY, 0, 0, CR, CG, CB);
683 TRIGGER_EFFECT_SPARK:
684 g_GFX_Spark(X, Y, 1, GetAngle2(VX, VY), 0, 0);
685 TRIGGER_EFFECT_BUBBLE:
686 g_GFX_Bubbles(X, Y, 1, 0, 0);
687 end;
688 if T = TRIGGER_EFFECT_ANIMATION then
689 case ST of
690 EFFECT_TELEPORT: begin
691 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
692 begin
693 Anim := TAnimation.Create(FramesID, False, 3);
694 if not Silent then
695 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
696 g_GFX_OnceAnim(X-32, Y-32, Anim);
697 Anim.Free();
698 end;
699 if Send and g_Game_IsServer and g_Game_IsNet then
700 MH_SEND_Effect(X, Y, Byte(not Silent), NET_GFX_TELE);
701 end;
702 EFFECT_RESPAWN: begin
703 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
704 begin
705 Anim := TAnimation.Create(FramesID, False, 4);
706 if not Silent then
707 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
708 g_GFX_OnceAnim(X-16, Y-16, Anim);
709 Anim.Free();
710 end;
711 if Send and g_Game_IsServer and g_Game_IsNet then
712 MH_SEND_Effect(X-16, Y-16, Byte(not Silent), NET_GFX_RESPAWN);
713 end;
714 EFFECT_FIRE: begin
715 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
716 begin
717 Anim := TAnimation.Create(FramesID, False, 4);
718 if not Silent then
719 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
720 g_GFX_OnceAnim(X-32, Y-128, Anim);
721 Anim.Free();
722 end;
723 if Send and g_Game_IsServer and g_Game_IsNet then
724 MH_SEND_Effect(X-32, Y-128, Byte(not Silent), NET_GFX_FIRE);
725 end;
726 end;
727 end;
729 function tr_Teleport(ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
730 var
731 p: TPlayer;
732 m: TMonster;
733 begin
734 Result := False;
735 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
736 case g_GetUIDType(ActivateUID) of
737 UID_PLAYER:
738 begin
739 p := g_Player_Get(ActivateUID);
740 if p = nil then
741 Exit;
743 if D2D then
744 begin
745 if p.TeleportTo(TX-(p.Obj.Rect.Width div 2),
746 TY-p.Obj.Rect.Height,
747 Silent,
748 TDir) then
749 Result := True;
750 end
751 else
752 if p.TeleportTo(TX, TY, Silent, TDir) then
753 Result := True;
754 end;
756 UID_MONSTER:
757 begin
758 m := g_Monsters_Get(ActivateUID);
759 if m = nil then
760 Exit;
762 if D2D then
763 begin
764 if m.TeleportTo(TX-(m.Obj.Rect.Width div 2),
765 TY-m.Obj.Rect.Height,
766 Silent,
767 TDir) then
768 Result := True;
769 end
770 else
771 if m.TeleportTo(TX, TY, Silent, TDir) then
772 Result := True;
773 end;
774 end;
775 end;
777 function tr_Push(ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
778 var
779 p: TPlayer;
780 m: TMonster;
781 begin
782 Result := True;
783 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
784 case g_GetUIDType(ActivateUID) of
785 UID_PLAYER:
786 begin
787 p := g_Player_Get(ActivateUID);
788 if p = nil then
789 Exit;
791 if ResetVel then
792 begin
793 p.GameVelX := 0;
794 p.GameVelY := 0;
795 p.GameAccelX := 0;
796 p.GameAccelY := 0;
797 end;
799 p.Push(VX, VY);
800 end;
802 UID_MONSTER:
803 begin
804 m := g_Monsters_Get(ActivateUID);
805 if m = nil then
806 Exit;
808 if ResetVel then
809 begin
810 m.GameVelX := 0;
811 m.GameVelY := 0;
812 m.GameAccelX := 0;
813 m.GameAccelY := 0;
814 end;
816 m.Push(VX, VY);
817 end;
818 end;
819 end;
821 function tr_Message(MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
822 var
823 msg: string;
824 p: TPlayer;
825 i: Integer;
826 begin
827 Result := True;
828 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
829 msg := b_Text_Format(MText);
830 case MSendTo of
831 0: // activator
832 begin
833 if g_GetUIDType(ActivateUID) = UID_PLAYER then
834 begin
835 if g_Game_IsWatchedPlayer(ActivateUID) then
836 begin
837 if MKind = 0 then
838 g_Console_Add(msg, True)
839 else if MKind = 1 then
840 g_Game_Message(msg, MTime);
841 end
842 else
843 begin
844 p := g_Player_Get(ActivateUID);
845 if g_Game_IsNet and (p.FClientID >= 0) then
846 if MKind = 0 then
847 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, p.FClientID)
848 else if MKind = 1 then
849 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, p.FClientID);
850 end;
851 end;
852 end;
854 1: // activator's team
855 begin
856 if g_GetUIDType(ActivateUID) = UID_PLAYER then
857 begin
858 p := g_Player_Get(ActivateUID);
859 if g_Game_IsWatchedTeam(p.Team) then
860 if MKind = 0 then
861 g_Console_Add(msg, True)
862 else if MKind = 1 then
863 g_Game_Message(msg, MTime);
865 if g_Game_IsNet then
866 begin
867 for i := Low(gPlayers) to High(gPlayers) do
868 if (gPlayers[i].Team = p.Team) and (gPlayers[i].FClientID >= 0) then
869 if MKind = 0 then
870 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
871 else if MKind = 1 then
872 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
873 end;
874 end;
875 end;
877 2: // activator's enemy team
878 begin
879 if g_GetUIDType(ActivateUID) = UID_PLAYER then
880 begin
881 p := g_Player_Get(ActivateUID);
882 if g_Game_IsWatchedTeam(p.Team) then
883 if MKind = 0 then
884 g_Console_Add(msg, True)
885 else if MKind = 1 then
886 g_Game_Message(msg, MTime);
888 if g_Game_IsNet then
889 begin
890 for i := Low(gPlayers) to High(gPlayers) do
891 if (gPlayers[i].Team <> p.Team) and (gPlayers[i].FClientID >= 0) then
892 if MKind = 0 then
893 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
894 else if MKind = 1 then
895 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
896 end;
897 end;
898 end;
900 3: // red team
901 begin
902 if g_Game_IsWatchedTeam(TEAM_RED) then
903 if MKind = 0 then
904 g_Console_Add(msg, True)
905 else if MKind = 1 then
906 g_Game_Message(msg, MTime);
908 if g_Game_IsNet then
909 begin
910 for i := Low(gPlayers) to High(gPlayers) do
911 if (gPlayers[i].Team = TEAM_RED) and (gPlayers[i].FClientID >= 0) then
912 if MKind = 0 then
913 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
914 else if MKind = 1 then
915 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
916 end;
917 end;
919 4: // blue team
920 begin
921 if g_Game_IsWatchedTeam(TEAM_BLUE) then
922 if MKind = 0 then
923 g_Console_Add(msg, True)
924 else if MKind = 1 then
925 g_Game_Message(msg, MTime);
927 if g_Game_IsNet then
928 begin
929 for i := Low(gPlayers) to High(gPlayers) do
930 if (gPlayers[i].Team = TEAM_BLUE) and (gPlayers[i].FClientID >= 0) then
931 if MKind = 0 then
932 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
933 else if MKind = 1 then
934 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
935 end;
936 end;
938 5: // everyone
939 begin
940 if MKind = 0 then
941 g_Console_Add(msg, True)
942 else if MKind = 1 then
943 g_Game_Message(msg, MTime);
945 if g_Game_IsNet then
946 begin
947 if MKind = 0 then
948 MH_SEND_Chat(msg, NET_CHAT_SYSTEM)
949 else if MKind = 1 then
950 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg);
951 end;
952 end;
953 end;
954 end;
956 function tr_ShotAimCheck(var Trigger: TTrigger; Obj: PObj): Boolean;
957 begin
958 result := false;
959 with Trigger do
960 begin
961 if TriggerType <> TRIGGER_SHOT then
962 Exit;
963 Result := (Data.ShotAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
964 or g_Obj_Collide(X, Y, Width, Height, Obj);
965 if Result and (Data.ShotAim and TRIGGER_SHOT_AIM_TRACE > 0) then
966 Result := g_TraceVector(Data.ShotPos.X,
967 Data.ShotPos.Y,
968 Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
969 Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
970 end;
971 end;
973 function ActivateTrigger(var Trigger: TTrigger; actType: Byte): Boolean;
974 var
975 animonce: Boolean;
976 p: TPlayer;
977 m: TMonster;
978 idx, k, wx, wy, xd, yd: Integer;
979 iid: LongWord;
980 coolDown: Boolean;
981 pAngle: Real;
982 FramesID: DWORD;
983 Anim: TAnimation;
984 UIDType: Byte;
985 TargetUID: Word;
986 it: PItem;
987 mon: TMonster;
989 function monsShotTarget (monidx: Integer; mon: TMonster): Boolean;
990 begin
991 result := false; // don't stop
992 if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
993 begin
994 xd := mon.GameX + mon.Obj.Rect.Width div 2;
995 yd := mon.GameY + mon.Obj.Rect.Height div 2;
996 TargetUID := mon.UID;
997 result := true; // stop
998 end;
999 end;
1001 function monsShotTargetMonPlr (monidx: Integer; mon: TMonster): Boolean;
1002 begin
1003 result := false; // don't stop
1004 if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1005 begin
1006 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1007 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1008 TargetUID := mon.UID;
1009 result := true; // stop
1010 end;
1011 end;
1013 function monShotTargetPlrMon (monidx: Integer; mon: TMonster): Boolean;
1014 begin
1015 result := false; // don't stop
1016 if (mon <> nil) and mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1017 begin
1018 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1019 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1020 TargetUID := mon.UID;
1021 result := true; // stop
1022 end;
1023 end;
1025 begin
1026 Result := False;
1027 if g_Game_IsClient then
1028 Exit;
1030 if not Trigger.Enabled then
1031 Exit;
1032 if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then
1033 Exit;
1034 if gLMSRespawn = LMS_RESPAWN_WARMUP then
1035 Exit;
1037 animonce := False;
1039 coolDown := (actType <> 0);
1041 with Trigger do
1042 begin
1043 case TriggerType of
1044 TRIGGER_EXIT:
1045 begin
1046 g_Sound_PlayEx('SOUND_GAME_SWITCH0');
1047 if g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH0');
1048 gExitByTrigger := True;
1049 g_Game_ExitLevel(Data.MapName);
1050 TimeOut := 18;
1051 Result := True;
1053 Exit;
1054 end;
1056 TRIGGER_TELEPORT:
1057 begin
1058 Result := tr_Teleport(ActivateUID,
1059 Data.TargetPoint.X, Data.TargetPoint.Y,
1060 Data.TlpDir, Data.silent_teleport,
1061 Data.d2d_teleport);
1062 TimeOut := 0;
1063 end;
1065 TRIGGER_OPENDOOR:
1066 begin
1067 Result := tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1068 TimeOut := 0;
1069 end;
1071 TRIGGER_CLOSEDOOR:
1072 begin
1073 Result := tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1074 TimeOut := 0;
1075 end;
1077 TRIGGER_DOOR, TRIGGER_DOOR5:
1078 begin
1079 if Data.PanelID <> -1 then
1080 begin
1081 if gWalls[Data.PanelID].Enabled then
1082 begin
1083 Result := tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1085 if TriggerType = TRIGGER_DOOR5 then
1086 DoorTime := 180;
1087 end
1088 else
1089 Result := tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
1091 if Result then
1092 TimeOut := 18;
1093 end;
1094 end;
1096 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1097 begin
1098 tr_CloseTrap(Data.PanelID, Data.NoSound, Data.d2d_doors);
1100 if TriggerType = TRIGGER_TRAP then
1101 begin
1102 DoorTime := 40;
1103 TimeOut := 76;
1104 end
1105 else
1106 begin
1107 DoorTime := -1;
1108 TimeOut := 0;
1109 end;
1111 Result := True;
1112 end;
1114 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
1115 begin
1116 PressCount := PressCount + 1;
1118 if PressTime = -1 then
1119 PressTime := Data.Wait;
1121 if coolDown then
1122 TimeOut := 18
1123 else
1124 TimeOut := 0;
1125 Result := True;
1126 end;
1128 TRIGGER_SECRET:
1129 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1130 begin
1131 Enabled := False;
1132 Result := True;
1133 if gLMSRespawn = LMS_RESPAWN_NONE then
1134 begin
1135 g_Player_Get(ActivateUID).GetSecret();
1136 Inc(gCoopSecretsFound);
1137 if g_Game_IsNet then MH_SEND_GameStats();
1138 end;
1139 end;
1141 TRIGGER_LIFTUP:
1142 begin
1143 Result := tr_SetLift(Data.PanelID, 0, Data.NoSound, Data.d2d_doors);
1144 TimeOut := 0;
1146 if (not Data.NoSound) and Result then begin
1147 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1148 X + (Width div 2),
1149 Y + (Height div 2));
1150 if g_Game_IsServer and g_Game_IsNet then
1151 MH_SEND_Sound(X + (Width div 2),
1152 Y + (Height div 2),
1153 'SOUND_GAME_SWITCH0');
1154 end;
1155 end;
1157 TRIGGER_LIFTDOWN:
1158 begin
1159 Result := tr_SetLift(Data.PanelID, 1, Data.NoSound, Data.d2d_doors);
1160 TimeOut := 0;
1162 if (not Data.NoSound) and Result then begin
1163 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1164 X + (Width div 2),
1165 Y + (Height div 2));
1166 if g_Game_IsServer and g_Game_IsNet then
1167 MH_SEND_Sound(X + (Width div 2),
1168 Y + (Height div 2),
1169 'SOUND_GAME_SWITCH0');
1170 end;
1171 end;
1173 TRIGGER_LIFT:
1174 begin
1175 Result := tr_SetLift(Data.PanelID, 3, Data.NoSound, Data.d2d_doors);
1177 if Result then
1178 begin
1179 TimeOut := 18;
1181 if (not Data.NoSound) and Result then begin
1182 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1183 X + (Width div 2),
1184 Y + (Height div 2));
1185 if g_Game_IsServer and g_Game_IsNet then
1186 MH_SEND_Sound(X + (Width div 2),
1187 Y + (Height div 2),
1188 'SOUND_GAME_SWITCH0');
1189 end;
1190 end;
1191 end;
1193 TRIGGER_TEXTURE:
1194 begin
1195 if ByteBool(Data.ActivateOnce) then
1196 begin
1197 Enabled := False;
1198 TriggerType := TRIGGER_NONE;
1199 end
1200 else
1201 if coolDown then
1202 TimeOut := 6
1203 else
1204 TimeOut := 0;
1206 animonce := Data.AnimOnce;
1207 Result := True;
1208 end;
1210 TRIGGER_SOUND:
1211 begin
1212 if Sound <> nil then
1213 begin
1214 if Data.SoundSwitch and Sound.IsPlaying() then
1215 begin // Íóæíî âûêëþ÷èòü, åñëè èãðàë
1216 Sound.Stop();
1217 SoundPlayCount := 0;
1218 Result := True;
1219 end
1220 else // (not Data.SoundSwitch) or (not Sound.IsPlaying())
1221 if (Data.PlayCount > 0) or (not Sound.IsPlaying()) then
1222 begin
1223 if Data.PlayCount > 0 then
1224 SoundPlayCount := Data.PlayCount
1225 else // 0 - èãðàåì áåñêîíå÷íî
1226 SoundPlayCount := 1;
1227 Result := True;
1228 end;
1229 if g_Game_IsNet then MH_SEND_TriggerSound(Trigger);
1230 end;
1231 end;
1233 TRIGGER_SPAWNMONSTER:
1234 if (Data.MonType in [MONSTER_DEMON..MONSTER_MAN]) then
1235 begin
1236 Result := False;
1237 if (Data.MonDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1238 begin
1239 AutoSpawn := not AutoSpawn;
1240 SpawnCooldown := 0;
1241 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1242 Result := True;
1243 end;
1245 if ((Data.MonDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1246 or ((Data.MonDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1247 for k := 1 to Data.MonCount do
1248 begin
1249 if (actType = ACTIVATE_CUSTOM) and (Data.MonDelay > 0) then
1250 SpawnCooldown := Data.MonDelay;
1251 if (Data.MonMax > 0) and (SpawnedCount >= Data.MonMax) then
1252 Break;
1254 mon := g_Monsters_Create(Data.MonType,
1255 Data.MonPos.X, Data.MonPos.Y,
1256 TDirection(Data.MonDir), True);
1258 Result := True;
1260 // Çäîðîâüå:
1261 if (Data.MonHealth > 0) then
1262 mon.SetHealth(Data.MonHealth);
1263 // Óñòàíàâëèâàåì ïîâåäåíèå:
1264 mon.MonsterBehaviour := Data.MonBehav;
1265 mon.FNoRespawn := True;
1266 if g_Game_IsNet then
1267 MH_SEND_MonsterSpawn(mon.UID);
1268 // Èäåì èñêàòü öåëü, åñëè íàäî:
1269 if Data.MonActive then
1270 mon.WakeUp();
1272 if Data.MonType <> MONSTER_BARREL then Inc(gTotalMonsters);
1274 if g_Game_IsNet then
1275 begin
1276 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
1277 gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
1278 end;
1280 if Data.MonMax > 0 then
1281 begin
1282 mon.SpawnTrigger := ID;
1283 Inc(SpawnedCount);
1284 end;
1286 case Data.MonEffect of
1287 EFFECT_TELEPORT: begin
1288 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1289 begin
1290 Anim := TAnimation.Create(FramesID, False, 3);
1291 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.MonPos.X, Data.MonPos.Y);
1292 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1293 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, Anim);
1294 Anim.Free();
1295 end;
1296 if g_Game_IsServer and g_Game_IsNet then
1297 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1298 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
1299 NET_GFX_TELE);
1300 end;
1301 EFFECT_RESPAWN: begin
1302 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1303 begin
1304 Anim := TAnimation.Create(FramesID, False, 4);
1305 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.MonPos.X, Data.MonPos.Y);
1306 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1307 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, Anim);
1308 Anim.Free();
1309 end;
1310 if g_Game_IsServer and g_Game_IsNet then
1311 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1312 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
1313 NET_GFX_RESPAWN);
1314 end;
1315 EFFECT_FIRE: begin
1316 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1317 begin
1318 Anim := TAnimation.Create(FramesID, False, 4);
1319 g_Sound_PlayExAt('SOUND_FIRE', Data.MonPos.X, Data.MonPos.Y);
1320 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1321 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, Anim);
1322 Anim.Free();
1323 end;
1324 if g_Game_IsServer and g_Game_IsNet then
1325 MH_SEND_Effect(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-128, 1,
1327 NET_GFX_FIRE);
1328 end;
1329 end;
1330 end;
1331 if g_Game_IsNet then
1332 begin
1333 MH_SEND_GameStats();
1334 MH_SEND_CoopStats();
1335 end;
1337 if coolDown then
1338 TimeOut := 18
1339 else
1340 TimeOut := 0;
1341 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1342 if actType = ACTIVATE_CUSTOM then
1343 Result := False;
1344 end;
1346 TRIGGER_SPAWNITEM:
1347 if (Data.ItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX]) then
1348 begin
1349 Result := False;
1350 if (Data.ItemDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1351 begin
1352 AutoSpawn := not AutoSpawn;
1353 SpawnCooldown := 0;
1354 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1355 Result := True;
1356 end;
1358 if ((Data.ItemDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1359 or ((Data.ItemDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1360 if (not Data.ItemOnlyDM) or
1361 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1362 for k := 1 to Data.ItemCount do
1363 begin
1364 if (actType = ACTIVATE_CUSTOM) and (Data.ItemDelay > 0) then
1365 SpawnCooldown := Data.ItemDelay;
1366 if (Data.ItemMax > 0) and (SpawnedCount >= Data.ItemMax) then
1367 Break;
1369 iid := g_Items_Create(Data.ItemPos.X, Data.ItemPos.Y,
1370 Data.ItemType, Data.ItemFalls, False, True);
1372 Result := True;
1374 if Data.ItemMax > 0 then
1375 begin
1376 it := g_ItemByIdx(iid);
1377 it.SpawnTrigger := ID;
1378 Inc(SpawnedCount);
1379 end;
1381 case Data.ItemEffect of
1382 EFFECT_TELEPORT: begin
1383 it := g_ItemByIdx(iid);
1384 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1385 begin
1386 Anim := TAnimation.Create(FramesID, False, 3);
1387 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', Data.ItemPos.X, Data.ItemPos.Y);
1388 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1389 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
1390 Anim.Free();
1391 end;
1392 if g_Game_IsServer and g_Game_IsNet then
1393 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1394 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
1395 NET_GFX_TELE);
1396 end;
1397 EFFECT_RESPAWN: begin
1398 it := g_ItemByIdx(iid);
1399 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1400 begin
1401 Anim := TAnimation.Create(FramesID, False, 4);
1402 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', Data.ItemPos.X, Data.ItemPos.Y);
1403 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1404 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
1405 Anim.Free();
1406 end;
1407 if g_Game_IsServer and g_Game_IsNet then
1408 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1409 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
1410 NET_GFX_RESPAWN);
1411 end;
1412 EFFECT_FIRE: begin
1413 it := g_ItemByIdx(iid);
1414 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1415 begin
1416 Anim := TAnimation.Create(FramesID, False, 4);
1417 g_Sound_PlayExAt('SOUND_FIRE', Data.ItemPos.X, Data.ItemPos.Y);
1418 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1419 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
1420 Anim.Free();
1421 end;
1422 if g_Game_IsServer and g_Game_IsNet then
1423 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1424 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
1425 NET_GFX_FIRE);
1426 end;
1427 end;
1429 if g_Game_IsNet then
1430 MH_SEND_ItemSpawn(True, iid);
1431 end;
1433 if coolDown then
1434 TimeOut := 18
1435 else
1436 TimeOut := 0;
1437 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1438 if actType = ACTIVATE_CUSTOM then
1439 Result := False;
1440 end;
1442 TRIGGER_MUSIC:
1443 begin
1444 // Ìåíÿåì ìóçûêó, åñëè åñòü íà ÷òî:
1445 if (Trigger.Data.MusicName <> '') then
1446 begin
1447 gMusic.SetByName(Trigger.Data.MusicName);
1448 gMusic.SpecPause := True;
1449 gMusic.Play();
1450 end;
1452 if Trigger.Data.MusicAction = 1 then
1453 begin // Âêëþ÷èòü
1454 if gMusic.SpecPause then // Áûëà íà ïàóçå => èãðàòü
1455 gMusic.SpecPause := False
1456 else // Èãðàëà => ñíà÷àëà
1457 gMusic.SetPosition(0);
1458 end
1459 else // Âûêëþ÷èòü
1460 begin
1461 // Ïàóçà:
1462 gMusic.SpecPause := True;
1463 end;
1465 if coolDown then
1466 TimeOut := 36
1467 else
1468 TimeOut := 0;
1469 Result := True;
1470 if g_Game_IsNet then MH_SEND_TriggerMusic;
1471 end;
1473 TRIGGER_PUSH:
1474 begin
1475 pAngle := -DegToRad(Data.PushAngle);
1476 Result := tr_Push(ActivateUID,
1477 Floor(Cos(pAngle)*Data.PushForce),
1478 Floor(Sin(pAngle)*Data.PushForce),
1479 Data.ResetVel);
1480 TimeOut := 0;
1481 end;
1483 TRIGGER_SCORE:
1484 begin
1485 Result := False;
1486 // Ïðèáàâèòü èëè îòíÿòü î÷êî
1487 if (Data.ScoreAction in [0..1]) and (Data.ScoreCount > 0) then
1488 begin
1489 // Ñâîåé èëè ÷óæîé êîìàíäå
1490 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1491 begin
1492 p := g_Player_Get(ActivateUID);
1493 if ((Data.ScoreAction = 0) and (Data.ScoreTeam = 0) and (p.Team = TEAM_RED))
1494 or ((Data.ScoreAction = 0) and (Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1495 begin
1496 Inc(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Scores
1498 if Data.ScoreCon then
1499 if Data.ScoreTeam = 0 then
1500 begin
1501 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1502 if g_Game_IsServer and g_Game_IsNet then
1503 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+r');
1504 end else
1505 begin
1506 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1507 if g_Game_IsServer and g_Game_IsNet then
1508 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+re');
1509 end;
1511 if Data.ScoreMsg then
1512 begin
1513 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1514 if g_Game_IsServer and g_Game_IsNet then
1515 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1516 end;
1517 end;
1518 if ((Data.ScoreAction = 1) and (Data.ScoreTeam = 0) and (p.Team = TEAM_RED))
1519 or ((Data.ScoreAction = 1) and (Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1520 begin
1521 Dec(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Fouls
1523 if Data.ScoreCon then
1524 if Data.ScoreTeam = 0 then
1525 begin
1526 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1527 if g_Game_IsServer and g_Game_IsNet then
1528 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-r');
1529 end else
1530 begin
1531 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1532 if g_Game_IsServer and g_Game_IsNet then
1533 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-re');
1534 end;
1536 if Data.ScoreMsg then
1537 begin
1538 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1539 if g_Game_IsServer and g_Game_IsNet then
1540 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1541 end;
1542 end;
1543 if ((Data.ScoreAction = 0) and (Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE))
1544 or ((Data.ScoreAction = 0) and (Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1545 begin
1546 Inc(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Scores
1548 if Data.ScoreCon then
1549 if Data.ScoreTeam = 0 then
1550 begin
1551 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1552 if g_Game_IsServer and g_Game_IsNet then
1553 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+b');
1554 end else
1555 begin
1556 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1557 if g_Game_IsServer and g_Game_IsNet then
1558 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '+be');
1559 end;
1561 if Data.ScoreMsg then
1562 begin
1563 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1564 if g_Game_IsServer and g_Game_IsNet then
1565 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1566 end;
1567 end;
1568 if ((Data.ScoreAction = 1) and (Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE))
1569 or ((Data.ScoreAction = 1) and (Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1570 begin
1571 Dec(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Fouls
1573 if Data.ScoreCon then
1574 if Data.ScoreTeam = 0 then
1575 begin
1576 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1577 if g_Game_IsServer and g_Game_IsNet then
1578 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-b');
1579 end else
1580 begin
1581 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, Data.ScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1582 if g_Game_IsServer and g_Game_IsNet then
1583 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (Data.ScoreCount shl 16), '-be');
1584 end;
1586 if Data.ScoreMsg then
1587 begin
1588 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1589 if g_Game_IsServer and g_Game_IsNet then
1590 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1591 end;
1592 end;
1593 Result := (p.Team = TEAM_RED) or (p.Team = TEAM_BLUE);
1594 end;
1595 // Êàêîé-òî êîíêðåòíîé êîìàíäå
1596 if Data.ScoreTeam in [2..3] then
1597 begin
1598 if (Data.ScoreAction = 0) and (Data.ScoreTeam = 2) then
1599 begin
1600 Inc(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Scores
1602 if Data.ScoreCon then
1603 begin
1604 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_RED], Data.ScoreCount]), True);
1605 if g_Game_IsServer and g_Game_IsNet then
1606 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '+tr');
1607 end;
1609 if Data.ScoreMsg then
1610 begin
1611 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1612 if g_Game_IsServer and g_Game_IsNet then
1613 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1614 end;
1615 end;
1616 if (Data.ScoreAction = 1) and (Data.ScoreTeam = 2) then
1617 begin
1618 Dec(gTeamStat[TEAM_RED].Goals, Data.ScoreCount); // Red Fouls
1620 if Data.ScoreCon then
1621 begin
1622 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_RED], Data.ScoreCount]), True);
1623 if g_Game_IsServer and g_Game_IsNet then
1624 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '-tr');
1625 end;
1627 if Data.ScoreMsg then
1628 begin
1629 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1630 if g_Game_IsServer and g_Game_IsNet then
1631 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1632 end;
1633 end;
1634 if (Data.ScoreAction = 0) and (Data.ScoreTeam = 3) then
1635 begin
1636 Inc(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Scores
1638 if Data.ScoreCon then
1639 begin
1640 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_BLUE], Data.ScoreCount]), True);
1641 if g_Game_IsServer and g_Game_IsNet then
1642 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '+tb');
1643 end;
1645 if Data.ScoreMsg then
1646 begin
1647 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1648 if g_Game_IsServer and g_Game_IsNet then
1649 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1650 end;
1651 end;
1652 if (Data.ScoreAction = 1) and (Data.ScoreTeam = 3) then
1653 begin
1654 Dec(gTeamStat[TEAM_BLUE].Goals, Data.ScoreCount); // Blue Fouls
1656 if Data.ScoreCon then
1657 begin
1658 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_BLUE], Data.ScoreCount]), True);
1659 if g_Game_IsServer and g_Game_IsNet then
1660 MH_SEND_GameEvent(NET_EV_SCORE, Data.ScoreCount shl 16, '-tb');
1661 end;
1663 if Data.ScoreMsg then
1664 begin
1665 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1666 if g_Game_IsServer and g_Game_IsNet then
1667 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1668 end;
1669 end;
1670 Result := True;
1671 end;
1672 end;
1673 // Âûèãðûø
1674 if (Data.ScoreAction = 2) and (gGameSettings.GoalLimit > 0) then
1675 begin
1676 // Ñâîåé èëè ÷óæîé êîìàíäû
1677 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1678 begin
1679 p := g_Player_Get(ActivateUID);
1680 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_RED)) // Red Wins
1681 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1682 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1683 begin
1684 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1686 if Data.ScoreCon then
1687 if Data.ScoreTeam = 0 then
1688 begin
1689 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1690 if g_Game_IsServer and g_Game_IsNet then
1691 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1692 end else
1693 begin
1694 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1695 if g_Game_IsServer and g_Game_IsNet then
1696 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1697 end;
1699 Result := True;
1700 end;
1701 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Blue Wins
1702 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1703 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1704 begin
1705 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1707 if Data.ScoreCon then
1708 if Data.ScoreTeam = 0 then
1709 begin
1710 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1711 if g_Game_IsServer and g_Game_IsNet then
1712 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1713 end else
1714 begin
1715 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1716 if g_Game_IsServer and g_Game_IsNet then
1717 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1718 end;
1720 Result := True;
1721 end;
1722 end;
1723 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1724 if Data.ScoreTeam in [2..3] then
1725 begin
1726 if Data.ScoreTeam = 2 then // Red Wins
1727 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1728 begin
1729 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1730 Result := True;
1731 end;
1732 if Data.ScoreTeam = 3 then // Blue Wins
1733 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1734 begin
1735 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1736 Result := True;
1737 end;
1738 end;
1739 end;
1740 // Ïðîèãðûø
1741 if (Data.ScoreAction = 3) and (gGameSettings.GoalLimit > 0) then
1742 begin
1743 // Ñâîåé èëè ÷óæîé êîìàíäû
1744 if (Data.ScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1745 begin
1746 p := g_Player_Get(ActivateUID);
1747 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Red Wins
1748 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_RED)) then
1749 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1750 begin
1751 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1753 if Data.ScoreCon then
1754 if Data.ScoreTeam = 0 then
1755 begin
1756 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1757 if g_Game_IsServer and g_Game_IsNet then
1758 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1759 end else
1760 begin
1761 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1762 if g_Game_IsServer and g_Game_IsNet then
1763 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1764 end;
1766 Result := True;
1767 end;
1768 if ((Data.ScoreTeam = 0) and (p.Team = TEAM_RED)) // Blue Wins
1769 or ((Data.ScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1770 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1771 begin
1772 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1774 if Data.ScoreCon then
1775 if Data.ScoreTeam = 0 then
1776 begin
1777 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1778 if g_Game_IsServer and g_Game_IsNet then
1779 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1780 end else
1781 begin
1782 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1783 if g_Game_IsServer and g_Game_IsNet then
1784 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1785 end;
1787 Result := True;
1788 end;
1789 end;
1790 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1791 if Data.ScoreTeam in [2..3] then
1792 begin
1793 if Data.ScoreTeam = 3 then // Red Wins
1794 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1795 begin
1796 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1797 Result := True;
1798 end;
1799 if Data.ScoreTeam = 2 then // Blue Wins
1800 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1801 begin
1802 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1803 Result := True;
1804 end;
1805 end;
1806 end;
1807 if Result then begin
1808 if coolDown then
1809 TimeOut := 18
1810 else
1811 TimeOut := 0;
1812 if g_Game_IsServer and g_Game_IsNet then
1813 MH_SEND_GameStats;
1814 end;
1815 end;
1817 TRIGGER_MESSAGE:
1818 begin
1819 Result := tr_Message(Data.MessageKind, Data.MessageText,
1820 Data.MessageSendTo, Data.MessageTime,
1821 ActivateUID);
1822 TimeOut := 18;
1823 end;
1825 TRIGGER_DAMAGE, TRIGGER_HEALTH:
1826 begin
1827 Result := False;
1828 UIDType := g_GetUIDType(ActivateUID);
1829 if (UIDType = UID_PLAYER) or (UIDType = UID_MONSTER) then
1830 begin
1831 Result := True;
1832 k := -1;
1833 if coolDown then
1834 begin
1835 // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
1836 for idx := 0 to High(Activators) do
1837 if Activators[idx].UID = ActivateUID then
1838 begin
1839 k := idx;
1840 Break;
1841 end;
1842 if k = -1 then
1843 begin // Âèäèì åãî âïåðâûå
1844 // Çàïîìèíàåì åãî
1845 SetLength(Activators, Length(Activators) + 1);
1846 k := High(Activators);
1847 Activators[k].UID := ActivateUID;
1848 end else
1849 begin // Óæå âèäåëè åãî
1850 // Åñëè èíòåðâàë îòêëþ÷¸í, íî îí âñ¸ åù¸ â çîíå ïîðàæåíèÿ, äà¸ì åìó âðåìÿ
1851 if (Data.DamageInterval = 0) and (Activators[k].TimeOut > 0) then
1852 Activators[k].TimeOut := 65535;
1853 // Òàéìàóò ïðîø¸ë - ðàáîòàåì
1854 Result := Activators[k].TimeOut = 0;
1855 end;
1856 end;
1858 if Result then
1859 begin
1860 case UIDType of
1861 UID_PLAYER:
1862 begin
1863 p := g_Player_Get(ActivateUID);
1864 if p = nil then
1865 Exit;
1867 // Íàíîñèì óðîí èãðîêó
1868 if (TriggerType = TRIGGER_DAMAGE) and (Data.DamageValue > 0) then
1869 p.Damage(Data.DamageValue, 0, 0, 0, HIT_SOME);
1871 // Ëå÷èì èãðîêà
1872 if (TriggerType = TRIGGER_HEALTH) and (Data.HealValue > 0) then
1873 if p.Heal(Data.HealValue, not Data.HealMax) and (not Data.HealSilent) then
1874 begin
1875 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', p.Obj.X, p.Obj.Y);
1876 if g_Game_IsServer and g_Game_IsNet then
1877 MH_SEND_Sound(p.Obj.X, p.Obj.Y, 'SOUND_ITEM_GETITEM');
1878 end;
1879 end;
1881 UID_MONSTER:
1882 begin
1883 m := g_Monsters_Get(ActivateUID);
1884 if m = nil then
1885 Exit;
1887 // Íàíîñèì óðîí ìîíñòðó
1888 if (TriggerType = TRIGGER_DAMAGE) and (Data.DamageValue > 0) then
1889 m.Damage(Data.DamageValue, 0, 0, 0, HIT_SOME);
1891 // Ëå÷èì ìîíñòðà
1892 if (TriggerType = TRIGGER_HEALTH) and (Data.HealValue > 0) then
1893 if m.Heal(Data.HealValue) and (not Data.HealSilent) then
1894 begin
1895 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', m.Obj.X, m.Obj.Y);
1896 if g_Game_IsServer and g_Game_IsNet then
1897 MH_SEND_Sound(m.Obj.X, m.Obj.Y, 'SOUND_ITEM_GETITEM');
1898 end;
1899 end;
1900 end;
1901 // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
1902 if TriggerType = TRIGGER_DAMAGE then
1903 idx := Data.DamageInterval
1904 else
1905 idx := Data.HealInterval;
1906 if coolDown then
1907 if idx > 0 then
1908 Activators[k].TimeOut := idx
1909 else
1910 Activators[k].TimeOut := 65535;
1911 end;
1912 end;
1913 TimeOut := 0;
1914 end;
1916 TRIGGER_SHOT:
1917 begin
1918 if ShotSightTime > 0 then
1919 Exit;
1921 // put this at the beginning so it doesn't trigger itself
1922 TimeOut := Data.ShotWait + 1;
1924 wx := Data.ShotPos.X;
1925 wy := Data.ShotPos.Y;
1926 pAngle := -DegToRad(Data.ShotAngle);
1927 xd := wx + Round(Cos(pAngle) * 32.0);
1928 yd := wy + Round(Sin(pAngle) * 32.0);
1929 TargetUID := 0;
1931 case Data.ShotTarget of
1932 TRIGGER_SHOT_TARGET_MON: // monsters
1933 g_Mons_ForEach(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 g_Mons_ForEach(monsShotTargetMonPlr);
1977 if (TargetUID = 0) and (gPlayers <> nil) then
1978 for idx := Low(gPlayers) to High(gPlayers) do
1979 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1980 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1981 begin
1982 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1983 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1984 TargetUID := gPlayers[idx].UID;
1985 break;
1986 end;
1987 end;
1989 TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
1990 begin
1991 if gPlayers <> nil then
1992 for idx := Low(gPlayers) to High(gPlayers) do
1993 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1994 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1995 begin
1996 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1997 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1998 TargetUID := gPlayers[idx].UID;
1999 break;
2000 end;
2001 if TargetUID = 0 then g_Mons_ForEach(monShotTargetPlrMon);
2002 end;
2004 else begin
2005 if (Data.ShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
2006 (Data.ShotType <> TRIGGER_SHOT_REV) then
2007 TargetUID := ActivateUID;
2008 end;
2009 end;
2011 if (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
2012 ((Data.ShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
2013 begin
2014 Result := True;
2015 if (Data.ShotIntSight = 0) or
2016 (Data.ShotTarget = TRIGGER_SHOT_TARGET_NONE) or
2017 (TargetUID = ShotSightTarget) then
2018 MakeShot(Trigger, wx, wy, xd, yd, TargetUID)
2019 else
2020 begin
2021 ShotSightTime := Data.ShotIntSight;
2022 ShotSightTargetN := TargetUID;
2023 if Data.ShotType = TRIGGER_SHOT_BFG then
2024 begin
2025 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', wx, wy);
2026 if g_Game_IsNet and g_Game_IsServer then
2027 MH_SEND_Sound(wx, wy, 'SOUND_WEAPON_STARTFIREBFG');
2028 end;
2029 end;
2030 end;
2031 end;
2033 TRIGGER_EFFECT:
2034 begin
2035 idx := Data.FXCount;
2037 while idx > 0 do
2038 begin
2039 case Data.FXPos of
2040 TRIGGER_EFFECT_POS_CENTER:
2041 begin
2042 wx := X + Width div 2;
2043 wy := Y + Height div 2;
2044 end;
2045 TRIGGER_EFFECT_POS_AREA:
2046 begin
2047 wx := X + Random(Width);
2048 wy := Y + Random(Height);
2049 end;
2050 else begin
2051 wx := X + Width div 2;
2052 wy := Y + Height div 2;
2053 end;
2054 end;
2055 xd := Data.FXVelX;
2056 yd := Data.FXVelY;
2057 if Data.FXSpreadL > 0 then xd := xd - Random(Data.FXSpreadL + 1);
2058 if Data.FXSpreadR > 0 then xd := xd + Random(Data.FXSpreadR + 1);
2059 if Data.FXSpreadU > 0 then yd := yd - Random(Data.FXSpreadU + 1);
2060 if Data.FXSpreadD > 0 then yd := yd + Random(Data.FXSpreadD + 1);
2061 tr_MakeEffect(wx, wy, xd, yd,
2062 Data.FXType, Data.FXSubType,
2063 Data.FXColorR, Data.FXColorG, Data.FXColorB, True, False);
2064 Dec(idx);
2065 end;
2066 TimeOut := Data.FXWait;
2067 end;
2068 end;
2069 end;
2071 if Result and (Trigger.TexturePanel <> -1) then
2072 g_Map_SwitchTexture(Trigger.TexturePanelType, Trigger.TexturePanel, IfThen(animonce, 2, 1));
2073 end;
2075 function g_Triggers_Create(Trigger: TTrigger): DWORD;
2076 var
2077 find_id: DWORD;
2078 fn, mapw: String;
2079 begin
2080 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà:
2081 if (Trigger.TriggerType = TRIGGER_EXIT) and
2082 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2083 Trigger.TriggerType := TRIGGER_NONE;
2085 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð:
2086 if (Trigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2087 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2088 (gGameSettings.GameType <> GT_SINGLE) then
2089 Trigger.TriggerType := TRIGGER_NONE;
2091 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå:
2092 if Trigger.TriggerType = TRIGGER_SECRET then
2093 gSecretsCount := gSecretsCount + 1;
2095 find_id := FindTrigger();
2096 gTriggers[find_id] := Trigger;
2098 with gTriggers[find_id] do
2099 begin
2100 ID := find_id;
2101 // if this type of trigger exists both on the client and on the server
2102 // use an uniform numeration
2103 if Trigger.TriggerType = TRIGGER_SOUND then
2104 begin
2105 Inc(gTriggerClientID);
2106 ClientID := gTriggerClientID;
2107 end
2108 else
2109 ClientID := 0;
2110 TimeOut := 0;
2111 ActivateUID := 0;
2112 PlayerCollide := False;
2113 DoorTime := -1;
2114 PressTime := -1;
2115 PressCount := 0;
2116 SoundPlayCount := 0;
2117 Sound := nil;
2118 AutoSpawn := False;
2119 SpawnCooldown := 0;
2120 SpawnedCount := 0;
2121 end;
2123 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê":
2124 if (Trigger.TriggerType = TRIGGER_SOUND) and
2125 (Trigger.Data.SoundName <> '') then
2126 begin
2127 // Åùå íåò òàêîãî çâóêà:
2128 if not g_Sound_Exists(Trigger.Data.SoundName) then
2129 begin
2130 fn := g_ExtractWadName(Trigger.Data.SoundName);
2132 if fn = '' then
2133 begin // Çâóê â ôàéëå ñ êàðòîé
2134 mapw := g_ExtractWadName(gMapInfo.Map);
2135 fn := mapw+':'+g_ExtractFilePathName(Trigger.Data.SoundName);
2136 end
2137 else // Çâóê â îòäåëüíîì ôàéëå
2138 fn := GameDir + '/wads/' + Trigger.Data.SoundName;
2140 if not g_Sound_CreateWADEx(Trigger.Data.SoundName, fn) then
2141 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.Data.SoundName]));
2142 end;
2144 // Ñîçäàåì îáúåêò çâóêà:
2145 with gTriggers[find_id] do
2146 begin
2147 Sound := TPlayableSound.Create();
2148 if not Sound.SetByName(Trigger.Data.SoundName) then
2149 begin
2150 Sound.Free();
2151 Sound := nil;
2152 end;
2153 end;
2154 end;
2156 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà":
2157 if (Trigger.TriggerType = TRIGGER_MUSIC) and
2158 (Trigger.Data.MusicName <> '') then
2159 begin
2160 // Åùå íåò òàêîé ìóçûêè:
2161 if not g_Sound_Exists(Trigger.Data.MusicName) then
2162 begin
2163 fn := g_ExtractWadName(Trigger.Data.MusicName);
2165 if fn = '' then
2166 begin // Ìóçûêà â ôàéëå ñ êàðòîé
2167 mapw := g_ExtractWadName(gMapInfo.Map);
2168 fn := mapw+':'+g_ExtractFilePathName(Trigger.Data.MusicName);
2169 end
2170 else // Ìóçûêà â ôàéëå ñ êàðòîé
2171 fn := GameDir+'/wads/'+Trigger.Data.MusicName;
2173 if not g_Sound_CreateWADEx(Trigger.Data.MusicName, fn, True) then
2174 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.Data.MusicName]));
2175 end;
2176 end;
2178 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü":
2179 if Trigger.TriggerType = TRIGGER_SHOT then
2180 with gTriggers[find_id] do
2181 begin
2182 ShotPanelTime := 0;
2183 ShotSightTime := 0;
2184 ShotSightTimeout := 0;
2185 ShotSightTarget := 0;
2186 ShotSightTargetN := 0;
2187 ShotAmmoCount := Trigger.Data.ShotAmmo;
2188 ShotReloadTime := 0;
2189 end;
2191 Result := find_id;
2192 end;
2194 procedure g_Triggers_Update();
2195 var
2196 a, b, i: Integer;
2197 Affected: array of Integer;
2199 function monsNear (monidx: Integer; mon: TMonster): Boolean;
2200 begin
2201 result := false; // don't stop
2202 if mon.Collide(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height) then
2203 begin
2204 gTriggers[a].ActivateUID := mon.UID;
2205 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2206 end;
2207 end;
2209 begin
2210 if gTriggers = nil then
2211 Exit;
2212 SetLength(Affected, 0);
2214 for a := 0 to High(gTriggers) do
2215 with gTriggers[a] do
2216 // Åñòü òðèããåð:
2217 if TriggerType <> TRIGGER_NONE then
2218 begin
2219 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè):
2220 if DoorTime > 0 then
2221 DoorTime := DoorTime - 1;
2222 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ:
2223 if PressTime > 0 then
2224 PressTime := PressTime - 1;
2225 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2226 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2227 for b := 0 to High(Activators) do
2228 begin
2229 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2230 if Activators[b].TimeOut > 0 then
2231 Dec(Activators[b].TimeOut)
2232 else
2233 Continue;
2234 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2235 if (Data.DamageInterval = 0) and (Activators[b].TimeOut < 65530) then
2236 Activators[b].TimeOut := 0;
2237 end;
2239 // Îáðàáàòûâàåì ñïàâíåðû:
2240 if Enabled and AutoSpawn then
2241 if SpawnCooldown = 0 then
2242 begin
2243 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà:
2244 if (TriggerType = TRIGGER_SPAWNMONSTER) and (Data.MonDelay > 0) then
2245 begin
2246 ActivateUID := 0;
2247 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2248 end;
2249 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò:
2250 if (TriggerType = TRIGGER_SPAWNITEM) and (Data.ItemDelay > 0) then
2251 begin
2252 ActivateUID := 0;
2253 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2254 end;
2255 end else // Óìåíüøàåì âðåìÿ îæèäàíèÿ:
2256 Dec(SpawnCooldown);
2258 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü":
2259 if TriggerType = TRIGGER_SHOT then
2260 begin
2261 if ShotPanelTime > 0 then
2262 begin
2263 Dec(ShotPanelTime);
2264 if ShotPanelTime = 0 then
2265 g_Map_SwitchTexture(ShotPanelType, Data.ShotPanelID);
2266 end;
2267 if ShotSightTime > 0 then
2268 begin
2269 Dec(ShotSightTime);
2270 if ShotSightTime = 0 then
2271 ShotSightTarget := ShotSightTargetN;
2272 end;
2273 if ShotSightTimeout > 0 then
2274 begin
2275 Dec(ShotSightTimeout);
2276 if ShotSightTimeout = 0 then
2277 ShotSightTarget := 0;
2278 end;
2279 if ShotReloadTime > 0 then
2280 begin
2281 Dec(ShotReloadTime);
2282 if ShotReloadTime = 0 then
2283 ShotAmmoCount := Data.ShotAmmo;
2284 end;
2285 end;
2287 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì:
2288 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2289 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2290 begin
2291 if Data.PlayCount > 0 then // Åñëè 0 - èãðàåì çâóê áåñêîíå÷íî
2292 SoundPlayCount := SoundPlayCount - 1;
2293 if Data.Local then
2294 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0)
2295 else
2296 Sound.PlayPanVolume((Data.Pan-127.0)/128.0, Data.Volume/255.0);
2297 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then
2298 MH_SEND_TriggerSound(gTriggers[a]);
2299 end;
2301 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü:
2302 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (Data.PanelID <> -1) then
2303 begin
2304 tr_OpenDoor(Data.PanelID, Data.NoSound, Data.d2d_doors);
2305 DoorTime := -1;
2306 end;
2308 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü:
2309 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (Data.PanelID <> -1) then
2310 begin
2311 // Óæå çàêðûòà:
2312 if gWalls[Data.PanelID].Enabled then
2313 DoorTime := -1
2314 else // Ïîêà îòêðûòà - çàêðûâàåì
2315 if tr_CloseDoor(Data.PanelID, Data.NoSound, Data.d2d_doors) then
2316 DoorTime := -1;
2317 end;
2319 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2320 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2321 (PressTime = 0) and (PressCount >= Data.Count) then
2322 begin
2323 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2324 PressTime := -1;
2325 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2326 if Data.Count > 0 then
2327 PressCount := PressCount - Data.Count
2328 else
2329 PressCount := 0;
2331 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2332 for b := 0 to High(gTriggers) do
2333 if g_Collide(Data.tX, Data.tY, Data.tWidth, Data.tHeight, gTriggers[b].X, gTriggers[b].Y,
2334 gTriggers[b].Width, gTriggers[b].Height) and
2335 ((b <> a) or (Data.Wait > 0)) then
2336 begin // Can be self-activated, if there is Data.Wait
2337 if (not Data.ExtRandom) or gTriggers[b].Enabled then
2338 begin
2339 SetLength(Affected, Length(Affected) + 1);
2340 Affected[High(Affected)] := b;
2341 end;
2342 end;
2343 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2344 if (TriggerType = TRIGGER_PRESS) and Data.ExtRandom then
2345 begin
2346 if (Length(Affected) > 0) then
2347 begin
2348 b := Affected[Random(Length(Affected))];
2349 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2350 ActivateTrigger(gTriggers[b], 0);
2351 end;
2352 end
2353 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2354 for i := 0 to High(Affected) do
2355 begin
2356 b := Affected[i];
2357 case TriggerType of
2358 TRIGGER_PRESS:
2359 begin
2360 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2361 ActivateTrigger(gTriggers[b], 0);
2362 end;
2363 TRIGGER_ON:
2364 begin
2365 gTriggers[b].Enabled := True;
2366 end;
2367 TRIGGER_OFF:
2368 begin
2369 gTriggers[b].Enabled := False;
2370 gTriggers[b].TimeOut := 0;
2371 if gTriggers[b].AutoSpawn then
2372 begin
2373 gTriggers[b].AutoSpawn := False;
2374 gTriggers[b].SpawnCooldown := 0;
2375 end;
2376 end;
2377 TRIGGER_ONOFF:
2378 begin
2379 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2380 if not gTriggers[b].Enabled then
2381 begin
2382 gTriggers[b].TimeOut := 0;
2383 if gTriggers[b].AutoSpawn then
2384 begin
2385 gTriggers[b].AutoSpawn := False;
2386 gTriggers[b].SpawnCooldown := 0;
2387 end;
2388 end;
2389 end;
2390 end;
2391 end;
2392 SetLength(Affected, 0);
2393 end;
2395 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2396 if TimeOut > 0 then
2397 begin
2398 TimeOut := TimeOut - 1;
2399 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2400 end;
2402 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2403 if not Enabled then
2404 Continue;
2406 // "Èãðîê áëèçêî":
2407 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2408 (TimeOut = 0) then
2409 if gPlayers <> nil then
2410 for b := 0 to High(gPlayers) do
2411 if gPlayers[b] <> nil then
2412 with gPlayers[b] do
2413 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2414 if Live and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2415 Collide(X, Y, Width, Height) then
2416 begin
2417 gTriggers[a].ActivateUID := UID;
2419 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2420 PlayerCollide then
2421 { Don't activate sound/music again if player is here }
2422 else
2423 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2424 end;
2426 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2428 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2429 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2430 (TimeOut = 0) and (Keys = 0) then
2431 begin
2432 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2433 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2434 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2435 gTriggers[a].ActivateUID := 0;
2436 ActivateTrigger(gTriggers[a], 0);
2437 end else
2438 begin
2439 // "Ìîíñòð áëèçêî"
2440 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2441 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2442 begin
2443 g_Mons_ForEach(monsNear);
2444 end;
2446 // "Ìîíñòðîâ íåò"
2447 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2448 (TimeOut = 0) and (Keys = 0) then
2449 if not g_CollideMonster(X, Y, Width, Height) then
2450 begin
2451 gTriggers[a].ActivateUID := 0;
2452 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2453 end;
2454 end;
2456 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2457 end;
2458 end;
2460 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2461 begin
2462 gTriggers[ID].ActivateUID := ActivateUID;
2463 ActivateTrigger(gTriggers[ID], ActivateType);
2464 end;
2466 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2467 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2468 var
2469 a: Integer;
2470 k: Byte;
2471 p: TPlayer;
2472 begin
2473 Result := nil;
2475 if gTriggers = nil then Exit;
2477 case g_GetUIDType(UID) of
2478 UID_GAME: k := 255;
2479 UID_PLAYER:
2480 begin
2481 p := g_Player_Get(UID);
2482 if p <> nil then
2483 k := p.GetKeys
2484 else
2485 k := 0;
2486 end;
2487 else k := 0;
2488 end;
2490 for a := 0 to High(gTriggers) do
2491 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2492 (gTriggers[a].TimeOut = 0) and
2493 (not InDWArray(a, IgnoreList)) and
2494 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2495 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2496 if g_Collide(X, Y, Width, Height,
2497 gTriggers[a].X, gTriggers[a].Y,
2498 gTriggers[a].Width, gTriggers[a].Height) then
2499 begin
2500 gTriggers[a].ActivateUID := UID;
2501 if ActivateTrigger(gTriggers[a], ActivateType) then
2502 begin
2503 SetLength(Result, Length(Result)+1);
2504 Result[High(Result)] := a;
2505 end;
2506 end;
2507 end;
2509 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2510 var
2511 a: Integer;
2512 k: Byte;
2513 p: TPlayer;
2514 begin
2515 if gTriggers = nil then Exit;
2517 case g_GetUIDType(UID) of
2518 UID_GAME: k := 255;
2519 UID_PLAYER:
2520 begin
2521 p := g_Player_Get(UID);
2522 if p <> nil then
2523 k := p.GetKeys
2524 else
2525 k := 0;
2526 end;
2527 else k := 0;
2528 end;
2530 for a := 0 to High(gTriggers) do
2531 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2532 (gTriggers[a].TimeOut = 0) and
2533 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2534 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2535 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2536 gTriggers[a].Width, gTriggers[a].Height) then
2537 begin
2538 gTriggers[a].ActivateUID := UID;
2539 ActivateTrigger(gTriggers[a], ActivateType);
2540 end;
2541 end;
2543 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2544 var
2545 a: Integer;
2546 k: Byte;
2547 rsq: Word;
2548 p: TPlayer;
2549 begin
2550 if gTriggers = nil then
2551 Exit;
2553 case g_GetUIDType(UID) of
2554 UID_GAME: k := 255;
2555 UID_PLAYER:
2556 begin
2557 p := g_Player_Get(UID);
2558 if p <> nil then
2559 k := p.GetKeys
2560 else
2561 k := 0;
2562 end;
2563 else k := 0;
2564 end;
2566 rsq := Radius * Radius;
2568 for a := 0 to High(gTriggers) do
2569 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
2570 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2571 (gTriggers[a].TimeOut = 0) and
2572 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2573 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2574 with gTriggers[a] do
2575 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
2576 X, Y, Width, Height) then
2577 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
2578 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
2579 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
2580 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
2581 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
2582 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2583 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
2584 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2585 begin
2586 ActivateUID := UID;
2587 ActivateTrigger(gTriggers[a], ActivateType);
2588 end;
2589 end;
2591 procedure g_Triggers_OpenAll();
2592 var
2593 a: Integer;
2594 b: Boolean;
2595 begin
2596 if gTriggers = nil then Exit;
2598 b := False;
2599 for a := 0 to High(gTriggers) do
2600 with gTriggers[a] do
2601 if (TriggerType = TRIGGER_OPENDOOR) or
2602 (TriggerType = TRIGGER_DOOR5) or
2603 (TriggerType = TRIGGER_DOOR) then
2604 begin
2605 tr_OpenDoor(Data.PanelID, True, Data.d2d_doors);
2606 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
2607 b := True;
2608 end;
2610 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
2611 end;
2613 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
2614 begin
2615 if (gTriggers <> nil) then
2616 if gTriggers[ID].SpawnedCount > 0 then
2617 Dec(gTriggers[ID].SpawnedCount);
2618 end;
2620 procedure g_Triggers_Free();
2621 var
2622 a: Integer;
2623 begin
2624 if gTriggers <> nil then
2625 for a := 0 to High(gTriggers) do
2626 begin
2627 if gTriggers[a].TriggerType = TRIGGER_SOUND then
2628 begin
2629 if g_Sound_Exists(gTriggers[a].Data.SoundName) then
2630 g_Sound_Delete(gTriggers[a].Data.SoundName);
2632 gTriggers[a].Sound.Free();
2633 end;
2634 if gTriggers[a].Activators <> nil then
2635 SetLength(gTriggers[a].Activators, 0);
2636 end;
2638 gTriggers := nil;
2639 gSecretsCount := 0;
2640 SetLength(gMonstersSpawned, 0);
2641 end;
2643 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
2644 var
2645 count, act_count, i, j: Integer;
2646 dw: DWORD;
2647 sg: Single;
2648 b: Boolean;
2649 p: Pointer;
2650 begin
2651 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ:
2652 count := 0;
2653 if gTriggers <> nil then
2654 for i := 0 to High(gTriggers) do
2655 count := count + 1;
2657 Mem := TBinMemoryWriter.Create((count+1) * 200);
2659 // Êîëè÷åñòâî òðèããåðîâ:
2660 Mem.WriteInt(count);
2662 if count = 0 then
2663 Exit;
2665 for i := 0 to High(gTriggers) do
2666 begin
2667 // Ñèãíàòóðà òðèããåðà:
2668 dw := TRIGGER_SIGNATURE; // 'TRGR'
2669 Mem.WriteDWORD(dw);
2670 // Òèï òðèããåðà:
2671 Mem.WriteByte(gTriggers[i].TriggerType);
2672 // Ñïåöèàëüíûå äàííûå òðèããåðà:
2673 p := @gTriggers[i].Data;
2674 Mem.WriteMemory(p, SizeOf(TTriggerData));
2675 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2676 Mem.WriteInt(gTriggers[i].X);
2677 Mem.WriteInt(gTriggers[i].Y);
2678 // Ðàçìåðû:
2679 Mem.WriteWord(gTriggers[i].Width);
2680 Mem.WriteWord(gTriggers[i].Height);
2681 // Âêëþ÷åí ëè òðèããåð:
2682 Mem.WriteBoolean(gTriggers[i].Enabled);
2683 // Òèï àêòèâàöèè òðèããåðà:
2684 Mem.WriteByte(gTriggers[i].ActivateType);
2685 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2686 Mem.WriteByte(gTriggers[i].Keys);
2687 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2688 Mem.WriteInt(gTriggers[i].TexturePanel);
2689 // Òèï ýòîé ïàíåëè:
2690 Mem.WriteWord(gTriggers[i].TexturePanelType);
2691 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2692 Mem.WriteWord(gTriggers[i].TimeOut);
2693 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2694 Mem.WriteWord(gTriggers[i].ActivateUID);
2695 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2696 act_count := Length(gTriggers[i].Activators);
2697 Mem.WriteInt(act_count);
2698 for j := 0 to act_count-1 do
2699 begin
2700 // UID îáúåêòà
2701 Mem.WriteWord(gTriggers[i].Activators[j].UID);
2702 // Âðåìÿ îæèäàíèÿ
2703 Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
2704 end;
2705 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2706 Mem.WriteBoolean(gTriggers[i].PlayerCollide);
2707 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2708 Mem.WriteInt(gTriggers[i].DoorTime);
2709 // Çàäåðæêà àêòèâàöèè:
2710 Mem.WriteInt(gTriggers[i].PressTime);
2711 // Ñ÷åò÷èê íàæàòèé:
2712 Mem.WriteInt(gTriggers[i].PressCount);
2713 // Ñïàâíåð àêòèâåí:
2714 Mem.WriteBoolean(gTriggers[i].AutoSpawn);
2715 // Çàäåðæêà ñïàâíåðà:
2716 Mem.WriteInt(gTriggers[i].SpawnCooldown);
2717 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2718 Mem.WriteInt(gTriggers[i].SpawnedCount);
2719 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2720 Mem.WriteInt(gTriggers[i].SoundPlayCount);
2721 // Ïðîèãðûâàåòñÿ ëè çâóê?
2722 if gTriggers[i].Sound <> nil then
2723 b := gTriggers[i].Sound.IsPlaying()
2724 else
2725 b := False;
2726 Mem.WriteBoolean(b);
2727 if b then
2728 begin
2729 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2730 dw := gTriggers[i].Sound.GetPosition();
2731 Mem.WriteDWORD(dw);
2732 // Ãðîìêîñòü çâóêà:
2733 sg := gTriggers[i].Sound.GetVolume();
2734 sg := sg / (gSoundLevel/255.0);
2735 Mem.WriteSingle(sg);
2736 // Ñòåðåî ñìåùåíèå çâóêà:
2737 sg := gTriggers[i].Sound.GetPan();
2738 Mem.WriteSingle(sg);
2739 end;
2740 end;
2741 end;
2743 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
2744 var
2745 count, act_count, i, j, a: Integer;
2746 dw: DWORD;
2747 vol, pan: Single;
2748 b: Boolean;
2749 p: Pointer;
2750 Trig: TTrigger;
2751 begin
2752 if Mem = nil then
2753 Exit;
2755 g_Triggers_Free();
2757 // Êîëè÷åñòâî òðèããåðîâ:
2758 Mem.ReadInt(count);
2760 if count = 0 then
2761 Exit;
2763 for a := 0 to count-1 do
2764 begin
2765 // Ñèãíàòóðà òðèããåðà:
2766 Mem.ReadDWORD(dw);
2767 if dw <> TRIGGER_SIGNATURE then // 'TRGR'
2768 begin
2769 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
2770 end;
2771 // Òèï òðèããåðà:
2772 Mem.ReadByte(Trig.TriggerType);
2773 // Ñïåöèàëüíûå äàííûå òðèããåðà:
2774 Mem.ReadMemory(p, dw);
2775 if dw <> SizeOf(TTriggerData) then
2776 begin
2777 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong TriggerData Size');
2778 end;
2779 Trig.Data := TTriggerData(p^);
2780 // Ñîçäàåì òðèããåð:
2781 i := g_Triggers_Create(Trig);
2782 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2783 Mem.ReadInt(gTriggers[i].X);
2784 Mem.ReadInt(gTriggers[i].Y);
2785 // Ðàçìåðû:
2786 Mem.ReadWord(gTriggers[i].Width);
2787 Mem.ReadWord(gTriggers[i].Height);
2788 // Âêëþ÷åí ëè òðèããåð:
2789 Mem.ReadBoolean(gTriggers[i].Enabled);
2790 // Òèï àêòèâàöèè òðèããåðà:
2791 Mem.ReadByte(gTriggers[i].ActivateType);
2792 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2793 Mem.ReadByte(gTriggers[i].Keys);
2794 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2795 Mem.ReadInt(gTriggers[i].TexturePanel);
2796 // Òèï ýòîé ïàíåëè:
2797 Mem.ReadWord(gTriggers[i].TexturePanelType);
2798 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2799 Mem.ReadWord(gTriggers[i].TimeOut);
2800 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2801 Mem.ReadWord(gTriggers[i].ActivateUID);
2802 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2803 Mem.ReadInt(act_count);
2804 if act_count > 0 then
2805 begin
2806 SetLength(gTriggers[i].Activators, act_count);
2807 for j := 0 to act_count-1 do
2808 begin
2809 // UID îáúåêòà
2810 Mem.ReadWord(gTriggers[i].Activators[j].UID);
2811 // Âðåìÿ îæèäàíèÿ
2812 Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
2813 end;
2814 end;
2815 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2816 Mem.ReadBoolean(gTriggers[i].PlayerCollide);
2817 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2818 Mem.ReadInt(gTriggers[i].DoorTime);
2819 // Çàäåðæêà àêòèâàöèè:
2820 Mem.ReadInt(gTriggers[i].PressTime);
2821 // Ñ÷åò÷èê íàæàòèé:
2822 Mem.ReadInt(gTriggers[i].PressCount);
2823 // Ñïàâíåð àêòèâåí:
2824 Mem.ReadBoolean(gTriggers[i].AutoSpawn);
2825 // Çàäåðæêà ñïàâíåðà:
2826 Mem.ReadInt(gTriggers[i].SpawnCooldown);
2827 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2828 Mem.ReadInt(gTriggers[i].SpawnedCount);
2829 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2830 Mem.ReadInt(gTriggers[i].SoundPlayCount);
2831 // Ïðîèãðûâàåòñÿ ëè çâóê?
2832 Mem.ReadBoolean(b);
2833 if b then
2834 begin
2835 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2836 Mem.ReadDWORD(dw);
2837 // Ãðîìêîñòü çâóêà:
2838 Mem.ReadSingle(vol);
2839 // Ñòåðåî ñìåùåíèå çâóêà:
2840 Mem.ReadSingle(pan);
2841 // Çàïóñêàåì çâóê, åñëè åñòü:
2842 if gTriggers[i].Sound <> nil then
2843 begin
2844 gTriggers[i].Sound.PlayPanVolume(pan, vol);
2845 gTriggers[i].Sound.Pause(True);
2846 gTriggers[i].Sound.SetPosition(dw);
2847 end
2848 end;
2849 end;
2850 end;
2852 end.