DEADSOFTWARE

save/load seems to work now
[d2df-sdl.git] / src / game / g_triggers.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_triggers;
19 interface
21 uses
22 MAPDEF, e_graphics, g_basic, g_sound,
23 BinEditor, xdynrec;
25 type
26 TActivator = record
27 UID: Word;
28 TimeOut: Word;
29 end;
30 PTrigger = ^TTrigger;
31 TTrigger = record
32 public
33 ID: DWORD;
34 ClientID: DWORD;
35 TriggerType: Byte;
36 X, Y: Integer;
37 Width, Height: Word;
38 Enabled: Boolean;
39 ActivateType: Byte;
40 Keys: Byte;
41 TexturePanel: Integer;
42 TexturePanelType: Word;
44 TimeOut: Word;
45 ActivateUID: Word;
46 Activators: array of TActivator;
47 PlayerCollide: Boolean;
48 DoorTime: Integer;
49 PressTime: Integer;
50 PressCount: Integer;
51 SoundPlayCount: Integer;
52 Sound: TPlayableSound;
53 AutoSpawn: Boolean;
54 SpawnCooldown: Integer;
55 SpawnedCount: Integer;
56 ShotPanelType: Word;
57 ShotPanelTime: Integer;
58 ShotSightTime: Integer;
59 ShotSightTimeout: Integer;
60 ShotSightTarget: Word;
61 ShotSightTargetN: Word;
62 ShotAmmoCount: Word;
63 ShotReloadTime: Integer;
65 mapId: AnsiString; // trigger id, from map
66 mapIndex: Integer; // index in fields['trigger'], used in save/load
67 //trigShotPanelId: Integer;
68 trigPanelId: Integer;
70 //TrigData: TTriggerData;
71 trigData: TDynRecord; // triggerdata; owned by trigger
73 public
74 function trigCenter (): TDFPoint; inline;
76 public
77 property trigShotPanelId: Integer read trigPanelId write trigPanelId;
78 end;
80 function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
81 procedure g_Triggers_Update();
82 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
83 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
84 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
85 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
86 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
87 procedure g_Triggers_OpenAll();
88 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
89 procedure g_Triggers_Free();
90 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
91 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
93 function tr_Message(MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
95 function tr_CloseDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
96 function tr_OpenDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
97 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
98 function tr_SetLift(PanelID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
100 function tr_Teleport(ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
101 function tr_Push(ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
103 procedure tr_MakeEffect(X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
104 function tr_SpawnShot(ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
106 var
107 gTriggerClientID: Integer = 0;
108 gTriggers: array of TTrigger;
109 gSecretsCount: Integer = 0;
110 gMonstersSpawned: array of LongInt = nil;
112 implementation
114 uses
115 g_player, g_map, Math, g_gfx, g_game, g_textures,
116 g_console, g_monsters, g_items, g_phys, g_weapons,
117 wadreader, g_main, SysUtils, e_log, g_language,
118 g_options, g_net, g_netmsg, utils, xparser;
120 const
121 TRIGGER_SIGNATURE = $58475254; // 'TRGX'
122 TRAP_DAMAGE = 1000;
125 function TTrigger.trigCenter (): TDFPoint; inline;
126 begin
127 result := TDFPoint.Create(x+width div 2, y+height div 2);
128 end;
131 function FindTrigger(): DWORD;
132 var
133 i: Integer;
134 begin
135 for i := 0 to High(gTriggers) do
136 begin
137 if gTriggers[i].TriggerType = TRIGGER_NONE then begin result := i; exit; end;
138 end;
140 if (gTriggers = nil) then
141 begin
142 SetLength(gTriggers, 8);
143 result := 0;
144 end
145 else
146 begin
147 result := Length(gTriggers);
148 SetLength(gTriggers, result+8);
149 for i := result to High(gTriggers) do gTriggers[i].TriggerType := TRIGGER_NONE;
150 end;
151 end;
154 function tr_CloseDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
155 var
156 a, b, c: Integer;
157 begin
158 Result := False;
160 if PanelID = -1 then Exit;
162 if not d2d then
163 begin
164 with gWalls[PanelID] do
165 begin
166 if g_CollidePlayer(X, Y, Width, Height) or
167 g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
169 if not Enabled then
170 begin
171 if not NoSound then
172 begin
173 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
174 if g_Game_IsServer and g_Game_IsNet then
175 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
176 end;
177 g_Map_EnableWall(PanelID);
178 Result := True;
179 end;
180 end;
181 end
182 else
183 begin
184 if gDoorMap = nil then Exit;
186 c := -1;
187 for a := 0 to High(gDoorMap) do
188 begin
189 for b := 0 to High(gDoorMap[a]) do
190 if gDoorMap[a, b] = DWORD(PanelID) then
191 begin
192 c := a;
193 Break;
194 end;
196 if c <> -1 then Break;
197 end;
198 if c = -1 then Exit;
200 for b := 0 to High(gDoorMap[c]) do
201 with gWalls[gDoorMap[c, b]] do
202 begin
203 if g_CollidePlayer(X, Y, Width, Height) or
204 g_Mons_IsAnyAliveAt(X, Y, Width, Height) then Exit;
205 end;
207 if not NoSound then
208 for b := 0 to High(gDoorMap[c]) do
209 if not gWalls[gDoorMap[c, b]].Enabled then
210 begin
211 with gWalls[PanelID] do
212 begin
213 g_Sound_PlayExAt('SOUND_GAME_DOORCLOSE', X, Y);
214 if g_Game_IsServer and g_Game_IsNet then
215 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOORCLOSE');
216 end;
217 Break;
218 end;
220 for b := 0 to High(gDoorMap[c]) do
221 if not gWalls[gDoorMap[c, b]].Enabled then
222 begin
223 g_Map_EnableWall(gDoorMap[c, b]);
224 Result := True;
225 end;
226 end;
227 end;
229 procedure tr_CloseTrap(PanelID: Integer; NoSound: Boolean; d2d: Boolean);
230 var
231 a, b, c: Integer;
232 wx, wy, wh, ww: Integer;
234 function monsDamage (mon: TMonster): Boolean;
235 begin
236 result := false; // don't stop
237 if g_Obj_Collide(wx, wy, ww, wh, @mon.Obj) then mon.Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
238 end;
240 begin
241 if PanelID = -1 then Exit;
243 if not d2d then
244 begin
245 with gWalls[PanelID] do
246 begin
247 if (not NoSound) and (not Enabled) then
248 begin
249 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
250 if g_Game_IsServer and g_Game_IsNet then
251 MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
252 end;
253 end;
255 wx := gWalls[PanelID].X;
256 wy := gWalls[PanelID].Y;
257 ww := gWalls[PanelID].Width;
258 wh := gWalls[PanelID].Height;
260 with gWalls[PanelID] do
261 begin
262 if gPlayers <> nil then
263 for a := 0 to High(gPlayers) do
264 if (gPlayers[a] <> nil) and gPlayers[a].Live and
265 gPlayers[a].Collide(X, Y, Width, Height) then
266 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
268 //g_Mons_ForEach(monsDamage);
269 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
271 if not Enabled then g_Map_EnableWall(PanelID);
272 end;
273 end
274 else
275 begin
276 if gDoorMap = nil then Exit;
278 c := -1;
279 for a := 0 to High(gDoorMap) do
280 begin
281 for b := 0 to High(gDoorMap[a]) do
282 begin
283 if gDoorMap[a, b] = DWORD(PanelID) then
284 begin
285 c := a;
286 Break;
287 end;
288 end;
290 if c <> -1 then Break;
291 end;
292 if c = -1 then Exit;
294 if not NoSound then
295 begin
296 for b := 0 to High(gDoorMap[c]) do
297 begin
298 if not gWalls[gDoorMap[c, b]].Enabled then
299 begin
300 with gWalls[PanelID] do
301 begin
302 g_Sound_PlayExAt('SOUND_GAME_SWITCH1', X, Y);
303 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH1');
304 end;
305 Break;
306 end;
307 end;
308 end;
310 for b := 0 to High(gDoorMap[c]) do
311 begin
312 wx := gWalls[gDoorMap[c, b]].X;
313 wy := gWalls[gDoorMap[c, b]].Y;
314 ww := gWalls[gDoorMap[c, b]].Width;
315 wh := gWalls[gDoorMap[c, b]].Height;
317 with gWalls[gDoorMap[c, b]] do
318 begin
319 if gPlayers <> nil then
320 for a := 0 to High(gPlayers) do
321 if (gPlayers[a] <> nil) and gPlayers[a].Live and
322 gPlayers[a].Collide(X, Y, Width, Height) then
323 gPlayers[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
325 //g_Mons_ForEach(monsDamage);
326 g_Mons_ForEachAliveAt(wx, wy, ww, wh, monsDamage);
327 (*
328 if gMonsters <> nil then
329 for a := 0 to High(gMonsters) do
330 if (gMonsters[a] <> nil) and gMonsters[a].Live and
331 g_Obj_Collide(X, Y, Width, Height, @gMonsters[a].Obj) then
332 gMonsters[a].Damage(TRAP_DAMAGE, 0, 0, 0, HIT_TRAP);
333 *)
335 if not Enabled then g_Map_EnableWall(gDoorMap[c, b]);
336 end;
337 end;
338 end;
339 end;
341 function tr_OpenDoor(PanelID: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
342 var
343 a, b, c: Integer;
344 begin
345 Result := False;
347 if PanelID = -1 then Exit;
349 if not d2d then
350 begin
351 with gWalls[PanelID] do
352 if Enabled then
353 begin
354 if not NoSound then
355 begin
356 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
357 if g_Game_IsServer and g_Game_IsNet then
358 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
359 end;
360 g_Map_DisableWall(PanelID);
361 Result := True;
362 end;
363 end
364 else
365 begin
366 if gDoorMap = nil then Exit;
368 c := -1;
369 for a := 0 to High(gDoorMap) do
370 begin
371 for b := 0 to High(gDoorMap[a]) do
372 if gDoorMap[a, b] = DWORD(PanelID) then
373 begin
374 c := a;
375 Break;
376 end;
378 if c <> -1 then Break;
379 end;
380 if c = -1 then Exit;
382 if not NoSound then
383 for b := 0 to High(gDoorMap[c]) do
384 if gWalls[gDoorMap[c, b]].Enabled then
385 begin
386 with gWalls[PanelID] do
387 begin
388 g_Sound_PlayExAt('SOUND_GAME_DOOROPEN', X, Y);
389 if g_Game_IsServer and g_Game_IsNet then
390 MH_SEND_Sound(X, Y, 'SOUND_GAME_DOOROPEN');
391 end;
392 Break;
393 end;
395 for b := 0 to High(gDoorMap[c]) do
396 if gWalls[gDoorMap[c, b]].Enabled then
397 begin
398 g_Map_DisableWall(gDoorMap[c, b]);
399 Result := True;
400 end;
401 end;
402 end;
404 function tr_SetLift(PanelID: Integer; d: Integer; NoSound: Boolean; d2d: Boolean): Boolean;
405 var
406 a, b, c, t: Integer;
407 begin
408 t := 0;
409 Result := False;
411 if PanelID = -1 then Exit;
413 if (gLifts[PanelID].PanelType = PANEL_LIFTUP) or
414 (gLifts[PanelID].PanelType = PANEL_LIFTDOWN) then
415 case d of
416 0: t := 0;
417 1: t := 1;
418 else t := IfThen(gLifts[PanelID].LiftType = 1, 0, 1);
419 end
420 else if (gLifts[PanelID].PanelType = PANEL_LIFTLEFT) or
421 (gLifts[PanelID].PanelType = PANEL_LIFTRIGHT) then
422 case d of
423 0: t := 2;
424 1: t := 3;
425 else t := IfThen(gLifts[PanelID].LiftType = 2, 3, 2);
426 end;
428 if not d2d then
429 begin
430 with gLifts[PanelID] do
431 if LiftType <> t then
432 begin
433 g_Map_SetLift(PanelID, t);
435 {if not NoSound then
436 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);}
437 Result := True;
438 end;
439 end
440 else // Êàê â D2d
441 begin
442 if gLiftMap = nil then Exit;
444 c := -1;
445 for a := 0 to High(gLiftMap) do
446 begin
447 for b := 0 to High(gLiftMap[a]) do
448 if gLiftMap[a, b] = DWORD(PanelID) then
449 begin
450 c := a;
451 Break;
452 end;
454 if c <> -1 then Break;
455 end;
456 if c = -1 then Exit;
458 {if not NoSound then
459 for b := 0 to High(gLiftMap[c]) do
460 if gLifts[gLiftMap[c, b]].LiftType <> t then
461 begin
462 with gLifts[PanelID] do
463 g_Sound_PlayExAt('SOUND_GAME_SWITCH0', X, Y);
464 Break;
465 end;}
467 for b := 0 to High(gLiftMap[c]) do
468 with gLifts[gLiftMap[c, b]] do
469 if LiftType <> t then
470 begin
471 g_Map_SetLift(gLiftMap[c, b], t);
473 Result := True;
474 end;
475 end;
476 end;
478 function tr_SpawnShot(ShotType: Integer; wx, wy, dx, dy: Integer; ShotSound: Boolean; ShotTarget: Word): Integer;
479 var
480 snd: string;
481 Projectile: Boolean;
482 TextureID: DWORD;
483 Anim: TAnimation;
484 begin
485 Result := -1;
486 TextureID := DWORD(-1);
487 snd := 'SOUND_WEAPON_FIREROCKET';
488 Projectile := True;
489 case ShotType of
490 TRIGGER_SHOT_PISTOL:
491 begin
492 g_Weapon_pistol(wx, wy, dx, dy, 0, True);
493 snd := 'SOUND_WEAPON_FIREPISTOL';
494 Projectile := False;
495 if ShotSound then
496 begin
497 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
498 if g_Game_IsNet then
499 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
500 end;
501 end;
503 TRIGGER_SHOT_BULLET:
504 begin
505 g_Weapon_mgun(wx, wy, dx, dy, 0, True);
506 if gSoundEffectsDF then snd := 'SOUND_WEAPON_FIRECGUN'
507 else snd := 'SOUND_WEAPON_FIREPISTOL';
508 Projectile := False;
509 if ShotSound then
510 begin
511 g_Player_CreateShell(wx, wy, 0, -2, SHELL_BULLET);
512 if g_Game_IsNet then
513 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL1);
514 end;
515 end;
517 TRIGGER_SHOT_SHOTGUN:
518 begin
519 g_Weapon_Shotgun(wx, wy, dx, dy, 0, True);
520 snd := 'SOUND_WEAPON_FIRESHOTGUN';
521 Projectile := False;
522 if ShotSound then
523 begin
524 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
525 if g_Game_IsNet then
526 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL2);
527 end;
528 end;
530 TRIGGER_SHOT_SSG:
531 begin
532 g_Weapon_DShotgun(wx, wy, dx, dy, 0, True);
533 snd := 'SOUND_WEAPON_FIRESHOTGUN2';
534 Projectile := False;
535 if ShotSound then
536 begin
537 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
538 g_Player_CreateShell(wx, wy, 0, -2, SHELL_SHELL);
539 if g_Game_IsNet then
540 MH_SEND_Effect(wx, wy, 0, NET_GFX_SHELL3);
541 end;
542 end;
544 TRIGGER_SHOT_IMP:
545 begin
546 g_Weapon_ball1(wx, wy, dx, dy, 0, -1, True);
547 snd := 'SOUND_WEAPON_FIREBALL';
548 end;
550 TRIGGER_SHOT_PLASMA:
551 begin
552 g_Weapon_Plasma(wx, wy, dx, dy, 0, -1, True);
553 snd := 'SOUND_WEAPON_FIREPLASMA';
554 end;
556 TRIGGER_SHOT_SPIDER:
557 begin
558 g_Weapon_aplasma(wx, wy, dx, dy, 0, -1, True);
559 snd := 'SOUND_WEAPON_FIREPLASMA';
560 end;
562 TRIGGER_SHOT_CACO:
563 begin
564 g_Weapon_ball2(wx, wy, dx, dy, 0, -1, True);
565 snd := 'SOUND_WEAPON_FIREBALL';
566 end;
568 TRIGGER_SHOT_BARON:
569 begin
570 g_Weapon_ball7(wx, wy, dx, dy, 0, -1, True);
571 snd := 'SOUND_WEAPON_FIREBALL';
572 end;
574 TRIGGER_SHOT_MANCUB:
575 begin
576 g_Weapon_manfire(wx, wy, dx, dy, 0, -1, True);
577 snd := 'SOUND_WEAPON_FIREBALL';
578 end;
580 TRIGGER_SHOT_REV:
581 begin
582 g_Weapon_revf(wx, wy, dx, dy, 0, ShotTarget, -1, True);
583 snd := 'SOUND_WEAPON_FIREREV';
584 end;
586 TRIGGER_SHOT_ROCKET:
587 begin
588 g_Weapon_Rocket(wx, wy, dx, dy, 0, -1, True);
589 snd := 'SOUND_WEAPON_FIREROCKET';
590 end;
592 TRIGGER_SHOT_BFG:
593 begin
594 g_Weapon_BFGShot(wx, wy, dx, dy, 0, -1, True);
595 snd := 'SOUND_WEAPON_FIREBFG';
596 end;
598 TRIGGER_SHOT_EXPL:
599 begin
600 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then
601 begin
602 Anim := TAnimation.Create(TextureID, False, 6);
603 Anim.Blending := False;
604 g_GFX_OnceAnim(wx-64, wy-64, Anim);
605 Anim.Free();
606 end;
607 Projectile := False;
608 g_Weapon_Explode(wx, wy, 60, 0);
609 snd := 'SOUND_WEAPON_EXPLODEROCKET';
610 end;
612 TRIGGER_SHOT_BFGEXPL:
613 begin
614 if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then
615 begin
616 Anim := TAnimation.Create(TextureID, False, 6);
617 Anim.Blending := False;
618 g_GFX_OnceAnim(wx-64, wy-64, Anim);
619 Anim.Free();
620 end;
621 Projectile := False;
622 g_Weapon_BFG9000(wx, wy, 0);
623 snd := 'SOUND_WEAPON_EXPLODEBFG';
624 end;
626 else exit;
627 end;
629 if g_Game_IsNet and g_Game_IsServer then
630 case ShotType of
631 TRIGGER_SHOT_EXPL:
632 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_EXPLODE);
633 TRIGGER_SHOT_BFGEXPL:
634 MH_SEND_Effect(wx, wy, Byte(ShotSound), NET_GFX_BFGEXPL);
635 else
636 begin
637 if Projectile then
638 MH_SEND_CreateShot(LastShotID);
639 if ShotSound then
640 MH_SEND_Sound(wx, wy, snd);
641 end;
642 end;
644 if ShotSound then
645 g_Sound_PlayExAt(snd, wx, wy);
647 if Projectile then
648 Result := LastShotID;
649 end;
651 procedure MakeShot(var Trigger: TTrigger; wx, wy, dx, dy: Integer; TargetUID: Word);
652 begin
653 with Trigger do
654 if (trigData.trigShotAmmo = 0) or
655 ((trigData.trigShotAmmo > 0) and (ShotAmmoCount > 0)) then
656 begin
657 if (trigShotPanelID <> -1) and (ShotPanelTime = 0) then
658 begin
659 g_Map_SwitchTexture(ShotPanelType, trigShotPanelID);
660 ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
661 end;
663 if trigData.trigShotIntSight > 0 then
664 ShotSightTimeout := 180; // ~= 5 ñåêóíä
666 if ShotAmmoCount > 0 then Dec(ShotAmmoCount);
668 dx := dx + Random(trigData.trigShotAccuracy) - Random(trigData.trigShotAccuracy);
669 dy := dy + Random(trigData.trigShotAccuracy) - Random(trigData.trigShotAccuracy);
671 tr_SpawnShot(trigData.trigShotType, wx, wy, dx, dy, trigData.trigShotSound, TargetUID);
672 end
673 else
674 if (trigData.trigShotIntReload > 0) and (ShotReloadTime = 0) then
675 ShotReloadTime := trigData.trigShotIntReload; // òèêîâ íà ïåðåçàðÿäêó ïóøêè
676 end;
678 procedure tr_MakeEffect(X, Y, VX, VY: Integer; T, ST, CR, CG, CB: Byte; Silent, Send: Boolean);
679 var
680 FramesID: DWORD;
681 Anim: TAnimation;
682 begin
683 if T = TRIGGER_EFFECT_PARTICLE then
684 case ST of
685 TRIGGER_EFFECT_SLIQUID:
686 begin
687 if (CR = 255) and (CG = 0) and (CB = 0) then
688 g_GFX_SimpleWater(X, Y, 1, VX, VY, 1, 0, 0, 0)
689 else if (CR = 0) and (CG = 255) and (CB = 0) then
690 g_GFX_SimpleWater(X, Y, 1, VX, VY, 2, 0, 0, 0)
691 else if (CR = 0) and (CG = 0) and (CB = 255) then
692 g_GFX_SimpleWater(X, Y, 1, VX, VY, 3, 0, 0, 0)
693 else
694 g_GFX_SimpleWater(X, Y, 1, VX, VY, 0, 0, 0, 0);
695 end;
696 TRIGGER_EFFECT_LLIQUID:
697 g_GFX_SimpleWater(X, Y, 1, VX, VY, 4, CR, CG, CB);
698 TRIGGER_EFFECT_DLIQUID:
699 g_GFX_SimpleWater(X, Y, 1, VX, VY, 5, CR, CG, CB);
700 TRIGGER_EFFECT_BLOOD:
701 g_GFX_Blood(X, Y, 1, VX, VY, 0, 0, CR, CG, CB);
702 TRIGGER_EFFECT_SPARK:
703 g_GFX_Spark(X, Y, 1, GetAngle2(VX, VY), 0, 0);
704 TRIGGER_EFFECT_BUBBLE:
705 g_GFX_Bubbles(X, Y, 1, 0, 0);
706 end;
707 if T = TRIGGER_EFFECT_ANIMATION then
708 case ST of
709 EFFECT_TELEPORT: begin
710 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
711 begin
712 Anim := TAnimation.Create(FramesID, False, 3);
713 if not Silent then
714 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
715 g_GFX_OnceAnim(X-32, Y-32, Anim);
716 Anim.Free();
717 end;
718 if Send and g_Game_IsServer and g_Game_IsNet then
719 MH_SEND_Effect(X, Y, Byte(not Silent), NET_GFX_TELE);
720 end;
721 EFFECT_RESPAWN: begin
722 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
723 begin
724 Anim := TAnimation.Create(FramesID, False, 4);
725 if not Silent then
726 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
727 g_GFX_OnceAnim(X-16, Y-16, Anim);
728 Anim.Free();
729 end;
730 if Send and g_Game_IsServer and g_Game_IsNet then
731 MH_SEND_Effect(X-16, Y-16, Byte(not Silent), NET_GFX_RESPAWN);
732 end;
733 EFFECT_FIRE: begin
734 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
735 begin
736 Anim := TAnimation.Create(FramesID, False, 4);
737 if not Silent then
738 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
739 g_GFX_OnceAnim(X-32, Y-128, Anim);
740 Anim.Free();
741 end;
742 if Send and g_Game_IsServer and g_Game_IsNet then
743 MH_SEND_Effect(X-32, Y-128, Byte(not Silent), NET_GFX_FIRE);
744 end;
745 end;
746 end;
748 function tr_Teleport(ActivateUID: Integer; TX, TY: Integer; TDir: Integer; Silent: Boolean; D2D: Boolean): Boolean;
749 var
750 p: TPlayer;
751 m: TMonster;
752 begin
753 Result := False;
754 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
755 case g_GetUIDType(ActivateUID) of
756 UID_PLAYER:
757 begin
758 p := g_Player_Get(ActivateUID);
759 if p = nil then
760 Exit;
762 if D2D then
763 begin
764 if p.TeleportTo(TX-(p.Obj.Rect.Width div 2),
765 TY-p.Obj.Rect.Height,
766 Silent,
767 TDir) then
768 Result := True;
769 end
770 else
771 if p.TeleportTo(TX, TY, Silent, TDir) then
772 Result := True;
773 end;
775 UID_MONSTER:
776 begin
777 m := g_Monsters_ByUID(ActivateUID);
778 if m = nil then
779 Exit;
781 if D2D then
782 begin
783 if m.TeleportTo(TX-(m.Obj.Rect.Width div 2),
784 TY-m.Obj.Rect.Height,
785 Silent,
786 TDir) then
787 Result := True;
788 end
789 else
790 if m.TeleportTo(TX, TY, Silent, TDir) then
791 Result := True;
792 end;
793 end;
794 end;
796 function tr_Push(ActivateUID: Integer; VX, VY: Integer; ResetVel: Boolean): Boolean;
797 var
798 p: TPlayer;
799 m: TMonster;
800 begin
801 Result := True;
802 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
803 case g_GetUIDType(ActivateUID) of
804 UID_PLAYER:
805 begin
806 p := g_Player_Get(ActivateUID);
807 if p = nil then
808 Exit;
810 if ResetVel then
811 begin
812 p.GameVelX := 0;
813 p.GameVelY := 0;
814 p.GameAccelX := 0;
815 p.GameAccelY := 0;
816 end;
818 p.Push(VX, VY);
819 end;
821 UID_MONSTER:
822 begin
823 m := g_Monsters_ByUID(ActivateUID);
824 if m = nil then
825 Exit;
827 if ResetVel then
828 begin
829 m.GameVelX := 0;
830 m.GameVelY := 0;
831 m.GameAccelX := 0;
832 m.GameAccelY := 0;
833 end;
835 m.Push(VX, VY);
836 end;
837 end;
838 end;
840 function tr_Message(MKind: Integer; MText: string; MSendTo: Integer; MTime: Integer; ActivateUID: Integer): Boolean;
841 var
842 msg: string;
843 p: TPlayer;
844 i: Integer;
845 begin
846 Result := True;
847 if (ActivateUID < 0) or (ActivateUID > $FFFF) then Exit;
848 msg := b_Text_Format(MText);
849 case MSendTo of
850 0: // activator
851 begin
852 if g_GetUIDType(ActivateUID) = UID_PLAYER then
853 begin
854 if g_Game_IsWatchedPlayer(ActivateUID) then
855 begin
856 if MKind = 0 then
857 g_Console_Add(msg, True)
858 else if MKind = 1 then
859 g_Game_Message(msg, MTime);
860 end
861 else
862 begin
863 p := g_Player_Get(ActivateUID);
864 if g_Game_IsNet and (p.FClientID >= 0) then
865 if MKind = 0 then
866 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, p.FClientID)
867 else if MKind = 1 then
868 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, p.FClientID);
869 end;
870 end;
871 end;
873 1: // activator's team
874 begin
875 if g_GetUIDType(ActivateUID) = UID_PLAYER then
876 begin
877 p := g_Player_Get(ActivateUID);
878 if g_Game_IsWatchedTeam(p.Team) then
879 if MKind = 0 then
880 g_Console_Add(msg, True)
881 else if MKind = 1 then
882 g_Game_Message(msg, MTime);
884 if g_Game_IsNet then
885 begin
886 for i := Low(gPlayers) to High(gPlayers) do
887 if (gPlayers[i].Team = p.Team) and (gPlayers[i].FClientID >= 0) then
888 if MKind = 0 then
889 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
890 else if MKind = 1 then
891 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
892 end;
893 end;
894 end;
896 2: // activator's enemy team
897 begin
898 if g_GetUIDType(ActivateUID) = UID_PLAYER then
899 begin
900 p := g_Player_Get(ActivateUID);
901 if g_Game_IsWatchedTeam(p.Team) 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 <> p.Team) 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;
917 end;
919 3: // red team
920 begin
921 if g_Game_IsWatchedTeam(TEAM_RED) 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_RED) 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 4: // blue team
939 begin
940 if g_Game_IsWatchedTeam(TEAM_BLUE) then
941 if MKind = 0 then
942 g_Console_Add(msg, True)
943 else if MKind = 1 then
944 g_Game_Message(msg, MTime);
946 if g_Game_IsNet then
947 begin
948 for i := Low(gPlayers) to High(gPlayers) do
949 if (gPlayers[i].Team = TEAM_BLUE) and (gPlayers[i].FClientID >= 0) then
950 if MKind = 0 then
951 MH_SEND_Chat(msg, NET_CHAT_SYSTEM, gPlayers[i].FClientID)
952 else if MKind = 1 then
953 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg, gPlayers[i].FClientID);
954 end;
955 end;
957 5: // everyone
958 begin
959 if MKind = 0 then
960 g_Console_Add(msg, True)
961 else if MKind = 1 then
962 g_Game_Message(msg, MTime);
964 if g_Game_IsNet then
965 begin
966 if MKind = 0 then
967 MH_SEND_Chat(msg, NET_CHAT_SYSTEM)
968 else if MKind = 1 then
969 MH_SEND_GameEvent(NET_EV_BIGTEXT, MTime, msg);
970 end;
971 end;
972 end;
973 end;
975 function tr_ShotAimCheck(var Trigger: TTrigger; Obj: PObj): Boolean;
976 begin
977 result := false;
978 with Trigger do
979 begin
980 if TriggerType <> TRIGGER_SHOT then
981 Exit;
982 Result := (trigData.trigShotAim and TRIGGER_SHOT_AIM_ALLMAP > 0)
983 or g_Obj_Collide(X, Y, Width, Height, Obj);
984 if Result and (trigData.trigShotAim and TRIGGER_SHOT_AIM_TRACE > 0) then
985 Result := g_TraceVector(trigData.trigShotPos.X,
986 trigData.trigShotPos.Y,
987 Obj^.X + Obj^.Rect.X + (Obj^.Rect.Width div 2),
988 Obj^.Y + Obj^.Rect.Y + (Obj^.Rect.Height div 2));
989 end;
990 end;
992 function ActivateTrigger(var Trigger: TTrigger; actType: Byte): Boolean;
993 var
994 animonce: Boolean;
995 p: TPlayer;
996 m: TMonster;
997 idx, k, wx, wy, xd, yd: Integer;
998 iid: LongWord;
999 coolDown: Boolean;
1000 pAngle: Real;
1001 FramesID: DWORD;
1002 Anim: TAnimation;
1003 UIDType: Byte;
1004 TargetUID: Word;
1005 it: PItem;
1006 mon: TMonster;
1008 function monsShotTarget (mon: TMonster): Boolean;
1009 begin
1010 result := false; // don't stop
1011 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1012 begin
1013 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1014 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1015 TargetUID := mon.UID;
1016 result := true; // stop
1017 end;
1018 end;
1020 function monsShotTargetMonPlr (mon: TMonster): Boolean;
1021 begin
1022 result := false; // don't stop
1023 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1024 begin
1025 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1026 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1027 TargetUID := mon.UID;
1028 result := true; // stop
1029 end;
1030 end;
1032 function monShotTargetPlrMon (mon: TMonster): Boolean;
1033 begin
1034 result := false; // don't stop
1035 if mon.Live and tr_ShotAimCheck(Trigger, @(mon.Obj)) then
1036 begin
1037 xd := mon.GameX + mon.Obj.Rect.Width div 2;
1038 yd := mon.GameY + mon.Obj.Rect.Height div 2;
1039 TargetUID := mon.UID;
1040 result := true; // stop
1041 end;
1042 end;
1044 begin
1045 Result := False;
1046 if g_Game_IsClient then
1047 Exit;
1049 if not Trigger.Enabled then
1050 Exit;
1051 if (Trigger.TimeOut <> 0) and (actType <> ACTIVATE_CUSTOM) then
1052 Exit;
1053 if gLMSRespawn = LMS_RESPAWN_WARMUP then
1054 Exit;
1056 animonce := False;
1058 coolDown := (actType <> 0);
1060 with Trigger do
1061 begin
1062 case TriggerType of
1063 TRIGGER_EXIT:
1064 begin
1065 g_Sound_PlayEx('SOUND_GAME_SWITCH0');
1066 if g_Game_IsNet then MH_SEND_Sound(X, Y, 'SOUND_GAME_SWITCH0');
1067 gExitByTrigger := True;
1068 g_Game_ExitLevel(trigData.trigMapName);
1069 TimeOut := 18;
1070 Result := True;
1072 Exit;
1073 end;
1075 TRIGGER_TELEPORT:
1076 begin
1077 Result := tr_Teleport(ActivateUID,
1078 trigData.trigTargetPoint.X, trigData.trigTargetPoint.Y,
1079 trigData.trigTlpDir, trigData.trigsilent_teleport,
1080 trigData.trigd2d_teleport);
1081 TimeOut := 0;
1082 end;
1084 TRIGGER_OPENDOOR:
1085 begin
1086 Result := tr_OpenDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
1087 TimeOut := 0;
1088 end;
1090 TRIGGER_CLOSEDOOR:
1091 begin
1092 Result := tr_CloseDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
1093 TimeOut := 0;
1094 end;
1096 TRIGGER_DOOR, TRIGGER_DOOR5:
1097 begin
1098 if trigPanelID <> -1 then
1099 begin
1100 if gWalls[trigPanelID].Enabled then
1101 begin
1102 Result := tr_OpenDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
1104 if TriggerType = TRIGGER_DOOR5 then
1105 DoorTime := 180;
1106 end
1107 else
1108 Result := tr_CloseDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
1110 if Result then
1111 TimeOut := 18;
1112 end;
1113 end;
1115 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1116 begin
1117 tr_CloseTrap(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
1119 if TriggerType = TRIGGER_TRAP then
1120 begin
1121 DoorTime := 40;
1122 TimeOut := 76;
1123 end
1124 else
1125 begin
1126 DoorTime := -1;
1127 TimeOut := 0;
1128 end;
1130 Result := True;
1131 end;
1133 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
1134 begin
1135 PressCount := PressCount + 1;
1137 if PressTime = -1 then
1138 PressTime := trigData.trigWait;
1140 if coolDown then
1141 TimeOut := 18
1142 else
1143 TimeOut := 0;
1144 Result := True;
1145 end;
1147 TRIGGER_SECRET:
1148 if g_GetUIDType(ActivateUID) = UID_PLAYER then
1149 begin
1150 Enabled := False;
1151 Result := True;
1152 if gLMSRespawn = LMS_RESPAWN_NONE then
1153 begin
1154 g_Player_Get(ActivateUID).GetSecret();
1155 Inc(gCoopSecretsFound);
1156 if g_Game_IsNet then MH_SEND_GameStats();
1157 end;
1158 end;
1160 TRIGGER_LIFTUP:
1161 begin
1162 Result := tr_SetLift(trigPanelID, 0, trigData.trigNoSound, trigData.trigd2d_doors);
1163 TimeOut := 0;
1165 if (not trigData.trigNoSound) and Result then begin
1166 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1167 X + (Width div 2),
1168 Y + (Height div 2));
1169 if g_Game_IsServer and g_Game_IsNet then
1170 MH_SEND_Sound(X + (Width div 2),
1171 Y + (Height div 2),
1172 'SOUND_GAME_SWITCH0');
1173 end;
1174 end;
1176 TRIGGER_LIFTDOWN:
1177 begin
1178 Result := tr_SetLift(trigPanelID, 1, trigData.trigNoSound, trigData.trigd2d_doors);
1179 TimeOut := 0;
1181 if (not trigData.trigNoSound) 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;
1192 TRIGGER_LIFT:
1193 begin
1194 Result := tr_SetLift(trigPanelID, 3, trigData.trigNoSound, trigData.trigd2d_doors);
1196 if Result then
1197 begin
1198 TimeOut := 18;
1200 if (not trigData.trigNoSound) and Result then begin
1201 g_Sound_PlayExAt('SOUND_GAME_SWITCH0',
1202 X + (Width div 2),
1203 Y + (Height div 2));
1204 if g_Game_IsServer and g_Game_IsNet then
1205 MH_SEND_Sound(X + (Width div 2),
1206 Y + (Height div 2),
1207 'SOUND_GAME_SWITCH0');
1208 end;
1209 end;
1210 end;
1212 TRIGGER_TEXTURE:
1213 begin
1214 if trigData.trigActivateOnce then
1215 begin
1216 Enabled := False;
1217 TriggerType := TRIGGER_NONE;
1218 end
1219 else
1220 if coolDown then
1221 TimeOut := 6
1222 else
1223 TimeOut := 0;
1225 animonce := trigData.trigAnimOnce;
1226 Result := True;
1227 end;
1229 TRIGGER_SOUND:
1230 begin
1231 if Sound <> nil then
1232 begin
1233 if trigData.trigSoundSwitch and Sound.IsPlaying() then
1234 begin // Íóæíî âûêëþ÷èòü, åñëè èãðàë
1235 Sound.Stop();
1236 SoundPlayCount := 0;
1237 Result := True;
1238 end
1239 else // (not Data.SoundSwitch) or (not Sound.IsPlaying())
1240 if (trigData.trigPlayCount > 0) or (not Sound.IsPlaying()) then
1241 begin
1242 if trigData.trigPlayCount > 0 then
1243 SoundPlayCount := trigData.trigPlayCount
1244 else // 0 - èãðàåì áåñêîíå÷íî
1245 SoundPlayCount := 1;
1246 Result := True;
1247 end;
1248 if g_Game_IsNet then MH_SEND_TriggerSound(Trigger);
1249 end;
1250 end;
1252 TRIGGER_SPAWNMONSTER:
1253 if (trigData.trigMonType in [MONSTER_DEMON..MONSTER_MAN]) then
1254 begin
1255 Result := False;
1256 if (trigData.trigMonDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1257 begin
1258 AutoSpawn := not AutoSpawn;
1259 SpawnCooldown := 0;
1260 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1261 Result := True;
1262 end;
1264 if ((trigData.trigMonDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1265 or ((trigData.trigMonDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1266 for k := 1 to trigData.trigMonCount do
1267 begin
1268 if (actType = ACTIVATE_CUSTOM) and (trigData.trigMonDelay > 0) then
1269 SpawnCooldown := trigData.trigMonDelay;
1270 if (trigData.trigMonMax > 0) and (SpawnedCount >= trigData.trigMonMax) then
1271 Break;
1273 mon := g_Monsters_Create(trigData.trigMonType,
1274 trigData.trigMonPos.X, trigData.trigMonPos.Y,
1275 TDirection(trigData.trigMonDir), True);
1277 Result := True;
1279 // Çäîðîâüå:
1280 if (trigData.trigMonHealth > 0) then
1281 mon.SetHealth(trigData.trigMonHealth);
1282 // Óñòàíàâëèâàåì ïîâåäåíèå:
1283 mon.MonsterBehaviour := trigData.trigMonBehav;
1284 mon.FNoRespawn := True;
1285 if g_Game_IsNet then
1286 MH_SEND_MonsterSpawn(mon.UID);
1287 // Èäåì èñêàòü öåëü, åñëè íàäî:
1288 if trigData.trigMonActive then
1289 mon.WakeUp();
1291 if trigData.trigMonType <> MONSTER_BARREL then Inc(gTotalMonsters);
1293 if g_Game_IsNet then
1294 begin
1295 SetLength(gMonstersSpawned, Length(gMonstersSpawned)+1);
1296 gMonstersSpawned[High(gMonstersSpawned)] := mon.UID;
1297 end;
1299 if trigData.trigMonMax > 0 then
1300 begin
1301 mon.SpawnTrigger := ID;
1302 Inc(SpawnedCount);
1303 end;
1305 case trigData.trigMonEffect of
1306 EFFECT_TELEPORT: begin
1307 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1308 begin
1309 Anim := TAnimation.Create(FramesID, False, 3);
1310 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', trigData.trigMonPos.X, trigData.trigMonPos.Y);
1311 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1312 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, Anim);
1313 Anim.Free();
1314 end;
1315 if g_Game_IsServer and g_Game_IsNet then
1316 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1317 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-32, 1,
1318 NET_GFX_TELE);
1319 end;
1320 EFFECT_RESPAWN: begin
1321 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1322 begin
1323 Anim := TAnimation.Create(FramesID, False, 4);
1324 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', trigData.trigMonPos.X, trigData.trigMonPos.Y);
1325 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1326 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, Anim);
1327 Anim.Free();
1328 end;
1329 if g_Game_IsServer and g_Game_IsNet then
1330 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-16,
1331 mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2)-16, 1,
1332 NET_GFX_RESPAWN);
1333 end;
1334 EFFECT_FIRE: begin
1335 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1336 begin
1337 Anim := TAnimation.Create(FramesID, False, 4);
1338 g_Sound_PlayExAt('SOUND_FIRE', trigData.trigMonPos.X, trigData.trigMonPos.Y);
1339 g_GFX_OnceAnim(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1340 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, Anim);
1341 Anim.Free();
1342 end;
1343 if g_Game_IsServer and g_Game_IsNet then
1344 MH_SEND_Effect(mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2)-32,
1345 mon.Obj.Y+mon.Obj.Rect.Y+mon.Obj.Rect.Height-128, 1,
1346 NET_GFX_FIRE);
1347 end;
1348 end;
1349 end;
1350 if g_Game_IsNet then
1351 begin
1352 MH_SEND_GameStats();
1353 MH_SEND_CoopStats();
1354 end;
1356 if coolDown then
1357 TimeOut := 18
1358 else
1359 TimeOut := 0;
1360 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1361 if actType = ACTIVATE_CUSTOM then
1362 Result := False;
1363 end;
1365 TRIGGER_SPAWNITEM:
1366 if (trigData.trigItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX]) then
1367 begin
1368 Result := False;
1369 if (trigData.trigItemDelay > 0) and (actType <> ACTIVATE_CUSTOM) then
1370 begin
1371 AutoSpawn := not AutoSpawn;
1372 SpawnCooldown := 0;
1373 // Àâòîñïàâíåð ïåðåêëþ÷åí - ìåíÿåì òåêñòóðó
1374 Result := True;
1375 end;
1377 if ((trigData.trigItemDelay = 0) and (actType <> ACTIVATE_CUSTOM))
1378 or ((trigData.trigItemDelay > 0) and (actType = ACTIVATE_CUSTOM)) then
1379 if (not trigData.trigItemOnlyDM) or
1380 (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then
1381 for k := 1 to trigData.trigItemCount do
1382 begin
1383 if (actType = ACTIVATE_CUSTOM) and (trigData.trigItemDelay > 0) then
1384 SpawnCooldown := trigData.trigItemDelay;
1385 if (trigData.trigItemMax > 0) and (SpawnedCount >= trigData.trigItemMax) then
1386 Break;
1388 iid := g_Items_Create(trigData.trigItemPos.X, trigData.trigItemPos.Y,
1389 trigData.trigItemType, trigData.trigItemFalls, False, True);
1391 Result := True;
1393 if trigData.trigItemMax > 0 then
1394 begin
1395 it := g_Items_ByIdx(iid);
1396 it.SpawnTrigger := ID;
1397 Inc(SpawnedCount);
1398 end;
1400 case trigData.trigItemEffect of
1401 EFFECT_TELEPORT: begin
1402 it := g_Items_ByIdx(iid);
1403 if g_Frames_Get(FramesID, 'FRAMES_TELEPORT') then
1404 begin
1405 Anim := TAnimation.Create(FramesID, False, 3);
1406 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', trigData.trigItemPos.X, trigData.trigItemPos.Y);
1407 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1408 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, Anim);
1409 Anim.Free();
1410 end;
1411 if g_Game_IsServer and g_Game_IsNet then
1412 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1413 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-32, 1,
1414 NET_GFX_TELE);
1415 end;
1416 EFFECT_RESPAWN: begin
1417 it := g_Items_ByIdx(iid);
1418 if g_Frames_Get(FramesID, 'FRAMES_ITEM_RESPAWN') then
1419 begin
1420 Anim := TAnimation.Create(FramesID, False, 4);
1421 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', trigData.trigItemPos.X, trigData.trigItemPos.Y);
1422 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1423 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, Anim);
1424 Anim.Free();
1425 end;
1426 if g_Game_IsServer and g_Game_IsNet then
1427 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-16,
1428 it.Obj.Y+it.Obj.Rect.Y+(it.Obj.Rect.Height div 2)-16, 1,
1429 NET_GFX_RESPAWN);
1430 end;
1431 EFFECT_FIRE: begin
1432 it := g_Items_ByIdx(iid);
1433 if g_Frames_Get(FramesID, 'FRAMES_FIRE') then
1434 begin
1435 Anim := TAnimation.Create(FramesID, False, 4);
1436 g_Sound_PlayExAt('SOUND_FIRE', trigData.trigItemPos.X, trigData.trigItemPos.Y);
1437 g_GFX_OnceAnim(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1438 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, Anim);
1439 Anim.Free();
1440 end;
1441 if g_Game_IsServer and g_Game_IsNet then
1442 MH_SEND_Effect(it.Obj.X+it.Obj.Rect.X+(it.Obj.Rect.Width div 2)-32,
1443 it.Obj.Y+it.Obj.Rect.Y+it.Obj.Rect.Height-128, 1,
1444 NET_GFX_FIRE);
1445 end;
1446 end;
1448 if g_Game_IsNet then
1449 MH_SEND_ItemSpawn(True, iid);
1450 end;
1452 if coolDown then
1453 TimeOut := 18
1454 else
1455 TimeOut := 0;
1456 // Åñëè àêòèâèðîâàí àâòîñïàâíåðîì, íå ìåíÿåì òåêñòóðó
1457 if actType = ACTIVATE_CUSTOM then
1458 Result := False;
1459 end;
1461 TRIGGER_MUSIC:
1462 begin
1463 // Ìåíÿåì ìóçûêó, åñëè åñòü íà ÷òî:
1464 if (Trigger.trigData.trigMusicName <> '') then
1465 begin
1466 gMusic.SetByName(Trigger.trigData.trigMusicName);
1467 gMusic.SpecPause := True;
1468 gMusic.Play();
1469 end;
1471 if Trigger.trigData.trigMusicAction = 1 then
1472 begin // Âêëþ÷èòü
1473 if gMusic.SpecPause then // Áûëà íà ïàóçå => èãðàòü
1474 gMusic.SpecPause := False
1475 else // Èãðàëà => ñíà÷àëà
1476 gMusic.SetPosition(0);
1477 end
1478 else // Âûêëþ÷èòü
1479 begin
1480 // Ïàóçà:
1481 gMusic.SpecPause := True;
1482 end;
1484 if coolDown then
1485 TimeOut := 36
1486 else
1487 TimeOut := 0;
1488 Result := True;
1489 if g_Game_IsNet then MH_SEND_TriggerMusic;
1490 end;
1492 TRIGGER_PUSH:
1493 begin
1494 pAngle := -DegToRad(trigData.trigPushAngle);
1495 Result := tr_Push(ActivateUID,
1496 Floor(Cos(pAngle)*trigData.trigPushForce),
1497 Floor(Sin(pAngle)*trigData.trigPushForce),
1498 trigData.trigResetVel);
1499 TimeOut := 0;
1500 end;
1502 TRIGGER_SCORE:
1503 begin
1504 Result := False;
1505 // Ïðèáàâèòü èëè îòíÿòü î÷êî
1506 if (trigData.trigScoreAction in [0..1]) and (trigData.trigScoreCount > 0) then
1507 begin
1508 // Ñâîåé èëè ÷óæîé êîìàíäå
1509 if (trigData.trigScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1510 begin
1511 p := g_Player_Get(ActivateUID);
1512 if ((trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 0) and (p.Team = TEAM_RED))
1513 or ((trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1514 begin
1515 Inc(gTeamStat[TEAM_RED].Goals, trigData.trigScoreCount); // Red Scores
1517 if trigData.trigScoreCon then
1518 if trigData.trigScoreTeam = 0 then
1519 begin
1520 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1521 if g_Game_IsServer and g_Game_IsNet then
1522 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '+r');
1523 end else
1524 begin
1525 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, trigData.trigScoreCount, _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 (trigData.trigScoreCount shl 16), '+re');
1528 end;
1530 if trigData.trigScoreMsg then
1531 begin
1532 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1533 if g_Game_IsServer and g_Game_IsNet then
1534 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1535 end;
1536 end;
1537 if ((trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 0) and (p.Team = TEAM_RED))
1538 or ((trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1539 begin
1540 Dec(gTeamStat[TEAM_RED].Goals, trigData.trigScoreCount); // Red Fouls
1542 if trigData.trigScoreCon then
1543 if trigData.trigScoreTeam = 0 then
1544 begin
1545 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1546 if g_Game_IsServer and g_Game_IsNet then
1547 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '-r');
1548 end else
1549 begin
1550 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1551 if g_Game_IsServer and g_Game_IsNet then
1552 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '-re');
1553 end;
1555 if trigData.trigScoreMsg then
1556 begin
1557 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1558 if g_Game_IsServer and g_Game_IsNet then
1559 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1560 end;
1561 end;
1562 if ((trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 0) and (p.Team = TEAM_BLUE))
1563 or ((trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 1) and (p.Team = TEAM_RED)) then
1564 begin
1565 Inc(gTeamStat[TEAM_BLUE].Goals, trigData.trigScoreCount); // Blue Scores
1567 if trigData.trigScoreCon then
1568 if trigData.trigScoreTeam = 0 then
1569 begin
1570 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_OWN], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1571 if g_Game_IsServer and g_Game_IsNet then
1572 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '+b');
1573 end else
1574 begin
1575 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_ENEMY], [p.Name, trigData.trigScoreCount, _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 (trigData.trigScoreCount shl 16), '+be');
1578 end;
1580 if trigData.trigScoreMsg then
1581 begin
1582 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1583 if g_Game_IsServer and g_Game_IsNet then
1584 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1585 end;
1586 end;
1587 if ((trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 0) and (p.Team = TEAM_BLUE))
1588 or ((trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 1) and (p.Team = TEAM_RED)) then
1589 begin
1590 Dec(gTeamStat[TEAM_BLUE].Goals, trigData.trigScoreCount); // Blue Fouls
1592 if trigData.trigScoreCon then
1593 if trigData.trigScoreTeam = 0 then
1594 begin
1595 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_OWN], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1596 if g_Game_IsServer and g_Game_IsNet then
1597 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '-b');
1598 end else
1599 begin
1600 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_ENEMY], [p.Name, trigData.trigScoreCount, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1601 if g_Game_IsServer and g_Game_IsNet then
1602 MH_SEND_GameEvent(NET_EV_SCORE, p.UID or (trigData.trigScoreCount shl 16), '-be');
1603 end;
1605 if trigData.trigScoreMsg then
1606 begin
1607 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1608 if g_Game_IsServer and g_Game_IsNet then
1609 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1610 end;
1611 end;
1612 Result := (p.Team = TEAM_RED) or (p.Team = TEAM_BLUE);
1613 end;
1614 // Êàêîé-òî êîíêðåòíîé êîìàíäå
1615 if trigData.trigScoreTeam in [2..3] then
1616 begin
1617 if (trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 2) then
1618 begin
1619 Inc(gTeamStat[TEAM_RED].Goals, trigData.trigScoreCount); // Red Scores
1621 if trigData.trigScoreCon then
1622 begin
1623 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_RED], trigData.trigScoreCount]), True);
1624 if g_Game_IsServer and g_Game_IsNet then
1625 MH_SEND_GameEvent(NET_EV_SCORE, trigData.trigScoreCount shl 16, '+tr');
1626 end;
1628 if trigData.trigScoreMsg then
1629 begin
1630 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1631 if g_Game_IsServer and g_Game_IsNet then
1632 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_RED);
1633 end;
1634 end;
1635 if (trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 2) then
1636 begin
1637 Dec(gTeamStat[TEAM_RED].Goals, trigData.trigScoreCount); // Red Fouls
1639 if trigData.trigScoreCon then
1640 begin
1641 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_RED], trigData.trigScoreCount]), True);
1642 if g_Game_IsServer and g_Game_IsNet then
1643 MH_SEND_GameEvent(NET_EV_SCORE, trigData.trigScoreCount shl 16, '-tr');
1644 end;
1646 if trigData.trigScoreMsg then
1647 begin
1648 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1649 if g_Game_IsServer and g_Game_IsNet then
1650 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_RED);
1651 end;
1652 end;
1653 if (trigData.trigScoreAction = 0) and (trigData.trigScoreTeam = 3) then
1654 begin
1655 Inc(gTeamStat[TEAM_BLUE].Goals, trigData.trigScoreCount); // Blue Scores
1657 if trigData.trigScoreCon then
1658 begin
1659 g_Console_Add(Format(_lc[I_PLAYER_SCORE_ADD_TEAM], [_lc[I_PLAYER_SCORE_BLUE], trigData.trigScoreCount]), True);
1660 if g_Game_IsServer and g_Game_IsNet then
1661 MH_SEND_GameEvent(NET_EV_SCORE, trigData.trigScoreCount shl 16, '+tb');
1662 end;
1664 if trigData.trigScoreMsg then
1665 begin
1666 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1667 if g_Game_IsServer and g_Game_IsNet then
1668 MH_SEND_GameEvent(NET_EV_SCORE_MSG, TEAM_BLUE);
1669 end;
1670 end;
1671 if (trigData.trigScoreAction = 1) and (trigData.trigScoreTeam = 3) then
1672 begin
1673 Dec(gTeamStat[TEAM_BLUE].Goals, trigData.trigScoreCount); // Blue Fouls
1675 if trigData.trigScoreCon then
1676 begin
1677 g_Console_Add(Format(_lc[I_PLAYER_SCORE_SUB_TEAM], [_lc[I_PLAYER_SCORE_BLUE], trigData.trigScoreCount]), True);
1678 if g_Game_IsServer and g_Game_IsNet then
1679 MH_SEND_GameEvent(NET_EV_SCORE, trigData.trigScoreCount shl 16, '-tb');
1680 end;
1682 if trigData.trigScoreMsg then
1683 begin
1684 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1685 if g_Game_IsServer and g_Game_IsNet then
1686 MH_SEND_GameEvent(NET_EV_SCORE_MSG, -TEAM_BLUE);
1687 end;
1688 end;
1689 Result := True;
1690 end;
1691 end;
1692 // Âûèãðûø
1693 if (trigData.trigScoreAction = 2) and (gGameSettings.GoalLimit > 0) then
1694 begin
1695 // Ñâîåé èëè ÷óæîé êîìàíäû
1696 if (trigData.trigScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1697 begin
1698 p := g_Player_Get(ActivateUID);
1699 if ((trigData.trigScoreTeam = 0) and (p.Team = TEAM_RED)) // Red Wins
1700 or ((trigData.trigScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1701 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1702 begin
1703 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1705 if trigData.trigScoreCon then
1706 if trigData.trigScoreTeam = 0 then
1707 begin
1708 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1709 if g_Game_IsServer and g_Game_IsNet then
1710 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1711 end else
1712 begin
1713 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1714 if g_Game_IsServer and g_Game_IsNet then
1715 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1716 end;
1718 Result := True;
1719 end;
1720 if ((trigData.trigScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Blue Wins
1721 or ((trigData.trigScoreTeam = 1) and (p.Team = TEAM_RED)) then
1722 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1723 begin
1724 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1726 if trigData.trigScoreCon then
1727 if trigData.trigScoreTeam = 0 then
1728 begin
1729 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1730 if g_Game_IsServer and g_Game_IsNet then
1731 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1732 end else
1733 begin
1734 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1735 if g_Game_IsServer and g_Game_IsNet then
1736 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1737 end;
1739 Result := True;
1740 end;
1741 end;
1742 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1743 if trigData.trigScoreTeam in [2..3] then
1744 begin
1745 if trigData.trigScoreTeam = 2 then // Red Wins
1746 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1747 begin
1748 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1749 Result := True;
1750 end;
1751 if trigData.trigScoreTeam = 3 then // Blue Wins
1752 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1753 begin
1754 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1755 Result := True;
1756 end;
1757 end;
1758 end;
1759 // Ïðîèãðûø
1760 if (trigData.trigScoreAction = 3) and (gGameSettings.GoalLimit > 0) then
1761 begin
1762 // Ñâîåé èëè ÷óæîé êîìàíäû
1763 if (trigData.trigScoreTeam in [0..1]) and (g_GetUIDType(ActivateUID) = UID_PLAYER) then
1764 begin
1765 p := g_Player_Get(ActivateUID);
1766 if ((trigData.trigScoreTeam = 0) and (p.Team = TEAM_BLUE)) // Red Wins
1767 or ((trigData.trigScoreTeam = 1) and (p.Team = TEAM_RED)) then
1768 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1769 begin
1770 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1772 if trigData.trigScoreCon then
1773 if trigData.trigScoreTeam = 0 then
1774 begin
1775 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1776 if g_Game_IsServer and g_Game_IsNet then
1777 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wre');
1778 end else
1779 begin
1780 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_RED]]), True);
1781 if g_Game_IsServer and g_Game_IsNet then
1782 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wr');
1783 end;
1785 Result := True;
1786 end;
1787 if ((trigData.trigScoreTeam = 0) and (p.Team = TEAM_RED)) // Blue Wins
1788 or ((trigData.trigScoreTeam = 1) and (p.Team = TEAM_BLUE)) then
1789 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1790 begin
1791 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1793 if trigData.trigScoreCon then
1794 if trigData.trigScoreTeam = 0 then
1795 begin
1796 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_ENEMY], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1797 if g_Game_IsServer and g_Game_IsNet then
1798 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wbe');
1799 end else
1800 begin
1801 g_Console_Add(Format(_lc[I_PLAYER_SCORE_WIN_OWN], [p.Name, _lc[I_PLAYER_SCORE_TO_BLUE]]), True);
1802 if g_Game_IsServer and g_Game_IsNet then
1803 MH_SEND_GameEvent(NET_EV_SCORE, p.UID, 'wb');
1804 end;
1806 Result := True;
1807 end;
1808 end;
1809 // Êàêîé-òî êîíêðåòíîé êîìàíäû
1810 if trigData.trigScoreTeam in [2..3] then
1811 begin
1812 if trigData.trigScoreTeam = 3 then // Red Wins
1813 if gTeamStat[TEAM_RED].Goals < SmallInt(gGameSettings.GoalLimit) then
1814 begin
1815 gTeamStat[TEAM_RED].Goals := gGameSettings.GoalLimit;
1816 Result := True;
1817 end;
1818 if trigData.trigScoreTeam = 2 then // Blue Wins
1819 if gTeamStat[TEAM_BLUE].Goals < SmallInt(gGameSettings.GoalLimit) then
1820 begin
1821 gTeamStat[TEAM_BLUE].Goals := gGameSettings.GoalLimit;
1822 Result := True;
1823 end;
1824 end;
1825 end;
1826 if Result then begin
1827 if coolDown then
1828 TimeOut := 18
1829 else
1830 TimeOut := 0;
1831 if g_Game_IsServer and g_Game_IsNet then
1832 MH_SEND_GameStats;
1833 end;
1834 end;
1836 TRIGGER_MESSAGE:
1837 begin
1838 Result := tr_Message(trigData.trigMessageKind, trigData.trigMessageText,
1839 trigData.trigMessageSendTo, trigData.trigMessageTime,
1840 ActivateUID);
1841 TimeOut := 18;
1842 end;
1844 TRIGGER_DAMAGE, TRIGGER_HEALTH:
1845 begin
1846 Result := False;
1847 UIDType := g_GetUIDType(ActivateUID);
1848 if (UIDType = UID_PLAYER) or (UIDType = UID_MONSTER) then
1849 begin
1850 Result := True;
1851 k := -1;
1852 if coolDown then
1853 begin
1854 // Âñïîìèíàåì, àêòèâèðîâàë ëè îí ìåíÿ ðàíüøå
1855 for idx := 0 to High(Activators) do
1856 if Activators[idx].UID = ActivateUID then
1857 begin
1858 k := idx;
1859 Break;
1860 end;
1861 if k = -1 then
1862 begin // Âèäèì åãî âïåðâûå
1863 // Çàïîìèíàåì åãî
1864 SetLength(Activators, Length(Activators) + 1);
1865 k := High(Activators);
1866 Activators[k].UID := ActivateUID;
1867 end else
1868 begin // Óæå âèäåëè åãî
1869 // Åñëè èíòåðâàë îòêëþ÷¸í, íî îí âñ¸ åù¸ â çîíå ïîðàæåíèÿ, äà¸ì åìó âðåìÿ
1870 if (trigData.trigDamageInterval = 0) and (Activators[k].TimeOut > 0) then
1871 Activators[k].TimeOut := 65535;
1872 // Òàéìàóò ïðîø¸ë - ðàáîòàåì
1873 Result := Activators[k].TimeOut = 0;
1874 end;
1875 end;
1877 if Result then
1878 begin
1879 case UIDType of
1880 UID_PLAYER:
1881 begin
1882 p := g_Player_Get(ActivateUID);
1883 if p = nil then
1884 Exit;
1886 // Íàíîñèì óðîí èãðîêó
1887 if (TriggerType = TRIGGER_DAMAGE) and (trigData.trigDamageValue > 0) then
1888 p.Damage(trigData.trigDamageValue, 0, 0, 0, HIT_SOME);
1890 // Ëå÷èì èãðîêà
1891 if (TriggerType = TRIGGER_HEALTH) and (trigData.trigHealValue > 0) then
1892 if p.Heal(trigData.trigHealValue, not trigData.trigHealMax) and (not trigData.trigHealSilent) then
1893 begin
1894 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', p.Obj.X, p.Obj.Y);
1895 if g_Game_IsServer and g_Game_IsNet then
1896 MH_SEND_Sound(p.Obj.X, p.Obj.Y, 'SOUND_ITEM_GETITEM');
1897 end;
1898 end;
1900 UID_MONSTER:
1901 begin
1902 m := g_Monsters_ByUID(ActivateUID);
1903 if m = nil then
1904 Exit;
1906 // Íàíîñèì óðîí ìîíñòðó
1907 if (TriggerType = TRIGGER_DAMAGE) and (trigData.trigDamageValue > 0) then
1908 m.Damage(trigData.trigDamageValue, 0, 0, 0, HIT_SOME);
1910 // Ëå÷èì ìîíñòðà
1911 if (TriggerType = TRIGGER_HEALTH) and (trigData.trigHealValue > 0) then
1912 if m.Heal(trigData.trigHealValue) and (not trigData.trigHealSilent) then
1913 begin
1914 g_Sound_PlayExAt('SOUND_ITEM_GETITEM', m.Obj.X, m.Obj.Y);
1915 if g_Game_IsServer and g_Game_IsNet then
1916 MH_SEND_Sound(m.Obj.X, m.Obj.Y, 'SOUND_ITEM_GETITEM');
1917 end;
1918 end;
1919 end;
1920 // Íàçíà÷àåì âðåìÿ ñëåäóþùåãî âîçäåéñòâèÿ
1921 if TriggerType = TRIGGER_DAMAGE then
1922 idx := trigData.trigDamageInterval
1923 else
1924 idx := trigData.trigHealInterval;
1925 if coolDown then
1926 if idx > 0 then
1927 Activators[k].TimeOut := idx
1928 else
1929 Activators[k].TimeOut := 65535;
1930 end;
1931 end;
1932 TimeOut := 0;
1933 end;
1935 TRIGGER_SHOT:
1936 begin
1937 if ShotSightTime > 0 then
1938 Exit;
1940 // put this at the beginning so it doesn't trigger itself
1941 TimeOut := trigData.trigShotWait + 1;
1943 wx := trigData.trigShotPos.X;
1944 wy := trigData.trigShotPos.Y;
1945 pAngle := -DegToRad(trigData.trigShotAngle);
1946 xd := wx + Round(Cos(pAngle) * 32.0);
1947 yd := wy + Round(Sin(pAngle) * 32.0);
1948 TargetUID := 0;
1950 case trigData.trigShotTarget of
1951 TRIGGER_SHOT_TARGET_MON: // monsters
1952 //TODO: accelerate this!
1953 g_Mons_ForEachAlive(monsShotTarget);
1955 TRIGGER_SHOT_TARGET_PLR: // players
1956 if gPlayers <> nil then
1957 for idx := Low(gPlayers) to High(gPlayers) do
1958 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1959 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1960 begin
1961 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1962 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1963 TargetUID := gPlayers[idx].UID;
1964 break;
1965 end;
1967 TRIGGER_SHOT_TARGET_RED: // red team
1968 if gPlayers <> nil then
1969 for idx := Low(gPlayers) to High(gPlayers) do
1970 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1971 (gPlayers[idx].Team = TEAM_RED) and
1972 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1973 begin
1974 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1975 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1976 TargetUID := gPlayers[idx].UID;
1977 break;
1978 end;
1980 TRIGGER_SHOT_TARGET_BLUE: // blue team
1981 if gPlayers <> nil then
1982 for idx := Low(gPlayers) to High(gPlayers) do
1983 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
1984 (gPlayers[idx].Team = TEAM_BLUE) and
1985 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
1986 begin
1987 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
1988 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
1989 TargetUID := gPlayers[idx].UID;
1990 break;
1991 end;
1993 TRIGGER_SHOT_TARGET_MONPLR: // monsters then players
1994 begin
1995 //TODO: accelerate this!
1996 g_Mons_ForEachAlive(monsShotTargetMonPlr);
1998 if (TargetUID = 0) and (gPlayers <> nil) then
1999 for idx := Low(gPlayers) to High(gPlayers) do
2000 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
2001 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2002 begin
2003 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2004 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2005 TargetUID := gPlayers[idx].UID;
2006 break;
2007 end;
2008 end;
2010 TRIGGER_SHOT_TARGET_PLRMON: // players then monsters
2011 begin
2012 if gPlayers <> nil then
2013 for idx := Low(gPlayers) to High(gPlayers) do
2014 if (gPlayers[idx] <> nil) and gPlayers[idx].Live and
2015 tr_ShotAimCheck(Trigger, @(gPlayers[idx].Obj)) then
2016 begin
2017 xd := gPlayers[idx].GameX + PLAYER_RECT_CX;
2018 yd := gPlayers[idx].GameY + PLAYER_RECT_CY;
2019 TargetUID := gPlayers[idx].UID;
2020 break;
2021 end;
2022 if TargetUID = 0 then
2023 begin
2024 //TODO: accelerate this!
2025 g_Mons_ForEachAlive(monShotTargetPlrMon);
2026 end;
2027 end;
2029 else begin
2030 if (trigData.trigShotTarget <> TRIGGER_SHOT_TARGET_NONE) or
2031 (trigData.trigShotType <> TRIGGER_SHOT_REV) then
2032 TargetUID := ActivateUID;
2033 end;
2034 end;
2036 if (trigData.trigShotTarget = TRIGGER_SHOT_TARGET_NONE) or (TargetUID > 0) or
2037 ((trigData.trigShotTarget > TRIGGER_SHOT_TARGET_NONE) and (TargetUID = 0)) then
2038 begin
2039 Result := True;
2040 if (trigData.trigShotIntSight = 0) or
2041 (trigData.trigShotTarget = TRIGGER_SHOT_TARGET_NONE) or
2042 (TargetUID = ShotSightTarget) then
2043 MakeShot(Trigger, wx, wy, xd, yd, TargetUID)
2044 else
2045 begin
2046 ShotSightTime := trigData.trigShotIntSight;
2047 ShotSightTargetN := TargetUID;
2048 if trigData.trigShotType = TRIGGER_SHOT_BFG then
2049 begin
2050 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', wx, wy);
2051 if g_Game_IsNet and g_Game_IsServer then
2052 MH_SEND_Sound(wx, wy, 'SOUND_WEAPON_STARTFIREBFG');
2053 end;
2054 end;
2055 end;
2056 end;
2058 TRIGGER_EFFECT:
2059 begin
2060 idx := trigData.trigFXCount;
2062 while idx > 0 do
2063 begin
2064 case trigData.trigFXPos of
2065 TRIGGER_EFFECT_POS_CENTER:
2066 begin
2067 wx := X + Width div 2;
2068 wy := Y + Height div 2;
2069 end;
2070 TRIGGER_EFFECT_POS_AREA:
2071 begin
2072 wx := X + Random(Width);
2073 wy := Y + Random(Height);
2074 end;
2075 else begin
2076 wx := X + Width div 2;
2077 wy := Y + Height div 2;
2078 end;
2079 end;
2080 xd := trigData.trigFXVelX;
2081 yd := trigData.trigFXVelY;
2082 if trigData.trigFXSpreadL > 0 then xd := xd - Random(trigData.trigFXSpreadL + 1);
2083 if trigData.trigFXSpreadR > 0 then xd := xd + Random(trigData.trigFXSpreadR + 1);
2084 if trigData.trigFXSpreadU > 0 then yd := yd - Random(trigData.trigFXSpreadU + 1);
2085 if trigData.trigFXSpreadD > 0 then yd := yd + Random(trigData.trigFXSpreadD + 1);
2086 tr_MakeEffect(wx, wy, xd, yd,
2087 trigData.trigFXType, trigData.trigFXSubType,
2088 trigData.trigFXColorR, trigData.trigFXColorG, trigData.trigFXColorB, True, False);
2089 Dec(idx);
2090 end;
2091 TimeOut := trigData.trigFXWait;
2092 end;
2093 end;
2094 end;
2096 if Result and (Trigger.TexturePanel <> -1) then
2097 g_Map_SwitchTexture(Trigger.TexturePanelType, Trigger.TexturePanel, IfThen(animonce, 2, 1));
2098 end;
2101 function g_Triggers_CreateWithMapIndex (Trigger: TTrigger; arridx, mapidx: Integer): DWORD;
2102 var
2103 triggers: TDynField;
2104 begin
2105 triggers := gCurrentMap['trigger'];
2106 if (triggers = nil) then raise Exception.Create('LOAD: map has no triggers');
2107 if (mapidx < 0) or (mapidx >= triggers.count) then raise Exception.Create('LOAD: invalid map trigger index');
2108 Trigger.trigData := triggers.item[mapidx];
2109 if (Trigger.trigData = nil) then raise Exception.Create('LOAD: internal error in trigger loader');
2110 Trigger.mapId := Trigger.trigData.id;
2111 Trigger.mapIndex := mapidx;
2112 if (Trigger.trigData.trigRec <> nil) then
2113 begin
2114 Trigger.trigData := Trigger.trigData.trigRec.clone();
2115 end
2116 else
2117 begin
2118 Trigger.trigData := nil;
2119 end;
2120 result := g_Triggers_Create(Trigger, arridx);
2121 end;
2124 function g_Triggers_Create(Trigger: TTrigger; forceInternalIndex: Integer=-1): DWORD;
2125 var
2126 find_id: DWORD;
2127 fn, mapw: AnsiString;
2128 f, olen: Integer;
2129 begin
2130 // Íå ñîçäàâàòü âûõîä, åñëè èãðà áåç âûõîäà:
2131 if (Trigger.TriggerType = TRIGGER_EXIT) and
2132 (not LongBool(gGameSettings.Options and GAME_OPTION_ALLOWEXIT)) then
2133 Trigger.TriggerType := TRIGGER_NONE;
2135 // Åñëè ìîíñòðû çàïðåùåíû, îòìåíÿåì òðèããåð:
2136 if (Trigger.TriggerType = TRIGGER_SPAWNMONSTER) and
2137 (not LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS)) and
2138 (gGameSettings.GameType <> GT_SINGLE) then
2139 Trigger.TriggerType := TRIGGER_NONE;
2141 // Ñ÷èòàåì êîëè÷åñòâî ñåêðåòîâ íà êàðòå:
2142 if Trigger.TriggerType = TRIGGER_SECRET then
2143 gSecretsCount := gSecretsCount + 1;
2145 if (forceInternalIndex < 0) then
2146 begin
2147 find_id := FindTrigger();
2148 end
2149 else
2150 begin
2151 olen := Length(gTriggers);
2152 if (forceInternalIndex >= olen) then
2153 begin
2154 SetLength(gTriggers, forceInternalIndex+1);
2155 for f := olen to High(gTriggers) do gTriggers[f].TriggerType := TRIGGER_NONE;
2156 end;
2157 find_id := DWORD(forceInternalIndex);
2158 end;
2159 gTriggers[find_id] := Trigger;
2161 e_LogWritefln('created trigger with map index %s, findid=%s (%s)', [Trigger.mapIndex, find_id, Trigger.mapId]);
2164 writeln('trigger #', find_id, ': pos=(', Trigger.x, ',', Trigger.y, ')-(', Trigger.width, 'x', Trigger.height, ')',
2165 '; TexturePanel=', Trigger.TexturePanel,
2166 '; TexturePanelType=', Trigger.TexturePanelType,
2167 '; ShotPanelType=', Trigger.ShotPanelType,
2168 '; TriggerType=', Trigger.TriggerType,
2169 '; ActivateType=', Trigger.ActivateType,
2170 '; Keys=', Trigger.Keys,
2171 '; trigPanelId=', Trigger.trigPanelId,
2172 '; trigShotPanelId=', Trigger.trigShotPanelId
2173 );
2176 with gTriggers[find_id] do
2177 begin
2178 ID := find_id;
2179 // if this type of trigger exists both on the client and on the server
2180 // use an uniform numeration
2181 if Trigger.TriggerType = TRIGGER_SOUND then
2182 begin
2183 Inc(gTriggerClientID);
2184 ClientID := gTriggerClientID;
2185 end
2186 else
2187 ClientID := 0;
2188 TimeOut := 0;
2189 ActivateUID := 0;
2190 PlayerCollide := False;
2191 DoorTime := -1;
2192 PressTime := -1;
2193 PressCount := 0;
2194 SoundPlayCount := 0;
2195 Sound := nil;
2196 AutoSpawn := False;
2197 SpawnCooldown := 0;
2198 SpawnedCount := 0;
2199 end;
2201 // Çàãðóæàåì çâóê, åñëè ýòî òðèããåð "Çâóê":
2202 if (Trigger.TriggerType = TRIGGER_SOUND) and
2203 (Trigger.trigData.trigSoundName <> '') then
2204 begin
2205 // Åùå íåò òàêîãî çâóêà:
2206 if not g_Sound_Exists(Trigger.trigData.trigSoundName) then
2207 begin
2208 fn := g_ExtractWadName(Trigger.trigData.trigSoundName);
2210 if fn = '' then
2211 begin // Çâóê â ôàéëå ñ êàðòîé
2212 mapw := g_ExtractWadName(gMapInfo.Map);
2213 fn := mapw+':'+g_ExtractFilePathName(Trigger.trigData.trigSoundName);
2214 end
2215 else // Çâóê â îòäåëüíîì ôàéëå
2216 fn := GameDir + '/wads/' + Trigger.trigData.trigSoundName;
2218 if not g_Sound_CreateWADEx(Trigger.trigData.trigSoundName, fn) then
2219 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.trigData.trigSoundName]));
2220 end;
2222 // Ñîçäàåì îáúåêò çâóêà:
2223 with gTriggers[find_id] do
2224 begin
2225 Sound := TPlayableSound.Create();
2226 if not Sound.SetByName(Trigger.trigData.trigSoundName) then
2227 begin
2228 Sound.Free();
2229 Sound := nil;
2230 end;
2231 end;
2232 end;
2234 // Çàãðóæàåì ìóçûêó, åñëè ýòî òðèããåð "Ìóçûêà":
2235 if (Trigger.TriggerType = TRIGGER_MUSIC) and
2236 (Trigger.trigData.trigMusicName <> '') then
2237 begin
2238 // Åùå íåò òàêîé ìóçûêè:
2239 if not g_Sound_Exists(Trigger.trigData.trigMusicName) then
2240 begin
2241 fn := g_ExtractWadName(Trigger.trigData.trigMusicName);
2243 if fn = '' then
2244 begin // Ìóçûêà â ôàéëå ñ êàðòîé
2245 mapw := g_ExtractWadName(gMapInfo.Map);
2246 fn := mapw+':'+g_ExtractFilePathName(Trigger.trigData.trigMusicName);
2247 end
2248 else // Ìóçûêà â ôàéëå ñ êàðòîé
2249 fn := GameDir+'/wads/'+Trigger.trigData.trigMusicName;
2251 if not g_Sound_CreateWADEx(Trigger.trigData.trigMusicName, fn, True) then
2252 g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, Trigger.trigData.trigMusicName]));
2253 end;
2254 end;
2256 // Çàãðóæàåì äàííûå òðèããåðà "Òóðåëü":
2257 if Trigger.TriggerType = TRIGGER_SHOT then
2258 with gTriggers[find_id] do
2259 begin
2260 ShotPanelTime := 0;
2261 ShotSightTime := 0;
2262 ShotSightTimeout := 0;
2263 ShotSightTarget := 0;
2264 ShotSightTargetN := 0;
2265 ShotAmmoCount := Trigger.trigData.trigShotAmmo;
2266 ShotReloadTime := 0;
2267 end;
2269 Result := find_id;
2270 end;
2273 // sorry; grid doesn't support recursive queries, so we have to do this
2274 type
2275 TSimpleMonsterList = specialize TSimpleList<TMonster>;
2277 var
2278 tgMonsList: TSimpleMonsterList = nil;
2280 procedure g_Triggers_Update();
2281 var
2282 a, b, i: Integer;
2283 Affected: array of Integer;
2285 function monsNear (mon: TMonster): Boolean;
2286 begin
2287 result := false; // don't stop
2289 gTriggers[a].ActivateUID := mon.UID;
2290 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2292 tgMonsList.append(mon);
2293 end;
2295 var
2296 mon: TMonster;
2297 begin
2298 if (tgMonsList = nil) then tgMonsList := TSimpleMonsterList.Create();
2300 if gTriggers = nil then
2301 Exit;
2302 SetLength(Affected, 0);
2304 for a := 0 to High(gTriggers) do
2305 with gTriggers[a] do
2306 // Åñòü òðèããåð:
2307 if TriggerType <> TRIGGER_NONE then
2308 begin
2309 // Óìåíüøàåì âðåìÿ äî çàêðûòèÿ äâåðè (îòêðûòèÿ ëîâóøêè):
2310 if DoorTime > 0 then
2311 DoorTime := DoorTime - 1;
2312 // Óìåíüøàåì âðåìÿ îæèäàíèÿ ïîñëå íàæàòèÿ:
2313 if PressTime > 0 then
2314 PressTime := PressTime - 1;
2315 // Ïðîâåðÿåì èãðîêîâ è ìîíñòðîâ, êîòîðûõ ðàíåå çàïîìíèëè:
2316 if (TriggerType = TRIGGER_DAMAGE) or (TriggerType = TRIGGER_HEALTH) then
2317 for b := 0 to High(Activators) do
2318 begin
2319 // Óìåíüøàåì âðåìÿ äî ïîâòîðíîãî âîçäåéñòâèÿ:
2320 if Activators[b].TimeOut > 0 then
2321 Dec(Activators[b].TimeOut)
2322 else
2323 Continue;
2324 // Ñ÷èòàåì, ÷òî îáúåêò ïîêèíóë çîíó äåéñòâèÿ òðèããåðà
2325 if (trigData.trigDamageInterval = 0) and (Activators[b].TimeOut < 65530) then
2326 Activators[b].TimeOut := 0;
2327 end;
2329 // Îáðàáàòûâàåì ñïàâíåðû:
2330 if Enabled and AutoSpawn then
2331 if SpawnCooldown = 0 then
2332 begin
2333 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ìîíñòðà:
2334 if (TriggerType = TRIGGER_SPAWNMONSTER) and (trigData.trigMonDelay > 0) then
2335 begin
2336 ActivateUID := 0;
2337 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2338 end;
2339 // Åñëè ïðèøëî âðåìÿ, ñïàâíèì ïðåäìåò:
2340 if (TriggerType = TRIGGER_SPAWNITEM) and (trigData.trigItemDelay > 0) then
2341 begin
2342 ActivateUID := 0;
2343 ActivateTrigger(gTriggers[a], ACTIVATE_CUSTOM);
2344 end;
2345 end else // Óìåíüøàåì âðåìÿ îæèäàíèÿ:
2346 Dec(SpawnCooldown);
2348 // Îáðàáàòûâàåì ñîáûòèÿ òðèããåðà "Òóðåëü":
2349 if TriggerType = TRIGGER_SHOT then
2350 begin
2351 if ShotPanelTime > 0 then
2352 begin
2353 Dec(ShotPanelTime);
2354 if ShotPanelTime = 0 then
2355 g_Map_SwitchTexture(ShotPanelType, trigShotPanelID);
2356 end;
2357 if ShotSightTime > 0 then
2358 begin
2359 Dec(ShotSightTime);
2360 if ShotSightTime = 0 then
2361 ShotSightTarget := ShotSightTargetN;
2362 end;
2363 if ShotSightTimeout > 0 then
2364 begin
2365 Dec(ShotSightTimeout);
2366 if ShotSightTimeout = 0 then
2367 ShotSightTarget := 0;
2368 end;
2369 if ShotReloadTime > 0 then
2370 begin
2371 Dec(ShotReloadTime);
2372 if ShotReloadTime = 0 then
2373 ShotAmmoCount := trigData.trigShotAmmo;
2374 end;
2375 end;
2377 // Òðèããåð "Çâóê" óæå îòûãðàë, åñëè íóæíî åùå - ïåðåçàïóñêàåì:
2378 if Enabled and (TriggerType = TRIGGER_SOUND) and (Sound <> nil) then
2379 if (SoundPlayCount > 0) and (not Sound.IsPlaying()) then
2380 begin
2381 if trigData.trigPlayCount > 0 then // Åñëè 0 - èãðàåì çâóê áåñêîíå÷íî
2382 SoundPlayCount := SoundPlayCount - 1;
2383 if trigData.trigLocal then
2384 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), trigData.trigVolume/255.0)
2385 else
2386 Sound.PlayPanVolume((trigData.trigPan-127.0)/128.0, trigData.trigVolume/255.0);
2387 if Sound.IsPlaying() and g_Game_IsNet and g_Game_IsServer then
2388 MH_SEND_TriggerSound(gTriggers[a]);
2389 end;
2391 // Òðèããåð "Ëîâóøêà" - ïîðà îòêðûâàòü:
2392 if (TriggerType = TRIGGER_TRAP) and (DoorTime = 0) and (trigPanelID <> -1) then
2393 begin
2394 tr_OpenDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors);
2395 DoorTime := -1;
2396 end;
2398 // Òðèããåð "Äâåðü 5 ñåê" - ïîðà çàêðûâàòü:
2399 if (TriggerType = TRIGGER_DOOR5) and (DoorTime = 0) and (trigPanelID <> -1) then
2400 begin
2401 // Óæå çàêðûòà:
2402 if gWalls[trigPanelID].Enabled then
2403 DoorTime := -1
2404 else // Ïîêà îòêðûòà - çàêðûâàåì
2405 if tr_CloseDoor(trigPanelID, trigData.trigNoSound, trigData.trigd2d_doors) then
2406 DoorTime := -1;
2407 end;
2409 // Òðèããåð - ðàñøèðèòåëü èëè ïåðåêëþ÷àòåëü, è ïðîøëà çàäåðæêà, è íàæàëè íóæíîå ÷èñëî ðàç:
2410 if (TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) and
2411 (PressTime = 0) and (PressCount >= trigData.trigCount) then
2412 begin
2413 // Ñáðàñûâàåì çàäåðæêó àêòèâàöèè:
2414 PressTime := -1;
2415 // Ñáðàñûâàåì ñ÷åò÷èê íàæàòèé:
2416 if trigData.trigCount > 0 then
2417 PressCount := PressCount - trigData.trigCount
2418 else
2419 PressCount := 0;
2421 // Îïðåäåëÿåì èçìåíÿåìûå èì òðèããåðû:
2422 for b := 0 to High(gTriggers) do
2423 if g_Collide(trigData.trigtX, trigData.trigtY, trigData.trigtWidth, trigData.trigtHeight, gTriggers[b].X, gTriggers[b].Y,
2424 gTriggers[b].Width, gTriggers[b].Height) and
2425 ((b <> a) or (trigData.trigWait > 0)) then
2426 begin // Can be self-activated, if there is Data.Wait
2427 if (not trigData.trigExtRandom) or gTriggers[b].Enabled then
2428 begin
2429 SetLength(Affected, Length(Affected) + 1);
2430 Affected[High(Affected)] := b;
2431 end;
2432 end;
2433 // Âûáèðàåì îäèí èç òðèããåðîâ äëÿ ðàñøèðèòåëÿ, åñëè âêëþ÷åí ðàíäîì:
2434 if (TriggerType = TRIGGER_PRESS) and trigData.trigExtRandom then
2435 begin
2436 if (Length(Affected) > 0) then
2437 begin
2438 b := Affected[Random(Length(Affected))];
2439 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2440 ActivateTrigger(gTriggers[b], 0);
2441 end;
2442 end
2443 else //  ïðîòèâíîì ñëó÷àå ðàáîòàåì êàê îáû÷íî:
2444 for i := 0 to High(Affected) do
2445 begin
2446 b := Affected[i];
2447 case TriggerType of
2448 TRIGGER_PRESS:
2449 begin
2450 gTriggers[b].ActivateUID := gTriggers[a].ActivateUID;
2451 ActivateTrigger(gTriggers[b], 0);
2452 end;
2453 TRIGGER_ON:
2454 begin
2455 gTriggers[b].Enabled := True;
2456 end;
2457 TRIGGER_OFF:
2458 begin
2459 gTriggers[b].Enabled := False;
2460 gTriggers[b].TimeOut := 0;
2461 if gTriggers[b].AutoSpawn then
2462 begin
2463 gTriggers[b].AutoSpawn := False;
2464 gTriggers[b].SpawnCooldown := 0;
2465 end;
2466 end;
2467 TRIGGER_ONOFF:
2468 begin
2469 gTriggers[b].Enabled := not gTriggers[b].Enabled;
2470 if not gTriggers[b].Enabled then
2471 begin
2472 gTriggers[b].TimeOut := 0;
2473 if gTriggers[b].AutoSpawn then
2474 begin
2475 gTriggers[b].AutoSpawn := False;
2476 gTriggers[b].SpawnCooldown := 0;
2477 end;
2478 end;
2479 end;
2480 end;
2481 end;
2482 SetLength(Affected, 0);
2483 end;
2485 // Óìåíüøàåì âðåìÿ äî âîçìîæíîñòè ïîâòîðíîé àêòèâàöèè:
2486 if TimeOut > 0 then
2487 begin
2488 TimeOut := TimeOut - 1;
2489 Continue; // ×òîáû íå ïîòåðÿòü 1 åäèíèöó çàäåðæêè
2490 end;
2492 // Íèæå èäóò òèïû àêòèâàöèè, åñëè òðèããåð îòêëþ÷¸í - èä¸ì äàëüøå
2493 if not Enabled then
2494 Continue;
2496 // "Èãðîê áëèçêî":
2497 if ByteBool(ActivateType and ACTIVATE_PLAYERCOLLIDE) and
2498 (TimeOut = 0) then
2499 if gPlayers <> nil then
2500 for b := 0 to High(gPlayers) do
2501 if gPlayers[b] <> nil then
2502 with gPlayers[b] do
2503 // Æèâ, åñòü íóæíûå êëþ÷è è îí ðÿäîì:
2504 if Live and ((gTriggers[a].Keys and GetKeys) = gTriggers[a].Keys) and
2505 Collide(X, Y, Width, Height) then
2506 begin
2507 gTriggers[a].ActivateUID := UID;
2509 if (gTriggers[a].TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) and
2510 PlayerCollide then
2511 { Don't activate sound/music again if player is here }
2512 else
2513 ActivateTrigger(gTriggers[a], ACTIVATE_PLAYERCOLLIDE);
2514 end;
2516 { TODO 5 : àêòèâàöèÿ ìîíñòðàìè òðèããåðîâ ñ êëþ÷àìè }
2518 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2519 ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2520 (TimeOut = 0) and (Keys = 0) then
2521 begin
2522 // Åñëè "Ìîíñòð áëèçêî" è "Ìîíñòðîâ íåò",
2523 // çàïóñêàåì òðèããåð íà ñòàðòå êàðòû è ñíèìàåì îáà ôëàãà
2524 ActivateType := ActivateType and not (ACTIVATE_MONSTERCOLLIDE or ACTIVATE_NOMONSTER);
2525 gTriggers[a].ActivateUID := 0;
2526 ActivateTrigger(gTriggers[a], 0);
2527 end else
2528 begin
2529 // "Ìîíñòð áëèçêî"
2530 if ByteBool(ActivateType and ACTIVATE_MONSTERCOLLIDE) and
2531 (TimeOut = 0) and (Keys = 0) then // Åñëè íå íóæíû êëþ÷è
2532 begin
2533 //g_Mons_ForEach(monsNear);
2534 //Alive?!
2535 tgMonsList.reset();
2536 g_Mons_ForEachAt(gTriggers[a].X, gTriggers[a].Y, gTriggers[a].Width, gTriggers[a].Height, monsNear);
2537 for mon in tgMonsList do
2538 begin
2539 gTriggers[a].ActivateUID := mon.UID;
2540 ActivateTrigger(gTriggers[a], ACTIVATE_MONSTERCOLLIDE);
2541 end;
2542 tgMonsList.reset(); // just in case
2543 end;
2545 // "Ìîíñòðîâ íåò"
2546 if ByteBool(ActivateType and ACTIVATE_NOMONSTER) and
2547 (TimeOut = 0) and (Keys = 0) then
2548 if not g_Mons_IsAnyAliveAt(X, Y, Width, Height) then
2549 begin
2550 gTriggers[a].ActivateUID := 0;
2551 ActivateTrigger(gTriggers[a], ACTIVATE_NOMONSTER);
2552 end;
2553 end;
2555 PlayerCollide := g_CollidePlayer(X, Y, Width, Height);
2556 end;
2557 end;
2559 procedure g_Triggers_Press(ID: DWORD; ActivateType: Byte; ActivateUID: Word = 0);
2560 begin
2561 gTriggers[ID].ActivateUID := ActivateUID;
2562 ActivateTrigger(gTriggers[ID], ActivateType);
2563 end;
2565 function g_Triggers_PressR(X, Y: Integer; Width, Height: Word; UID: Word;
2566 ActivateType: Byte; IgnoreList: DWArray = nil): DWArray;
2567 var
2568 a: Integer;
2569 k: Byte;
2570 p: TPlayer;
2571 begin
2572 Result := nil;
2574 if gTriggers = nil then Exit;
2576 case g_GetUIDType(UID) of
2577 UID_GAME: k := 255;
2578 UID_PLAYER:
2579 begin
2580 p := g_Player_Get(UID);
2581 if p <> nil then
2582 k := p.GetKeys
2583 else
2584 k := 0;
2585 end;
2586 else k := 0;
2587 end;
2589 for a := 0 to High(gTriggers) do
2590 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2591 (gTriggers[a].TimeOut = 0) and
2592 (not InDWArray(a, IgnoreList)) and
2593 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2594 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2595 if g_Collide(X, Y, Width, Height,
2596 gTriggers[a].X, gTriggers[a].Y,
2597 gTriggers[a].Width, gTriggers[a].Height) then
2598 begin
2599 gTriggers[a].ActivateUID := UID;
2600 if ActivateTrigger(gTriggers[a], ActivateType) then
2601 begin
2602 SetLength(Result, Length(Result)+1);
2603 Result[High(Result)] := a;
2604 end;
2605 end;
2606 end;
2608 procedure g_Triggers_PressL(X1, Y1, X2, Y2: Integer; UID: DWORD; ActivateType: Byte);
2609 var
2610 a: Integer;
2611 k: Byte;
2612 p: TPlayer;
2613 begin
2614 if gTriggers = nil then Exit;
2616 case g_GetUIDType(UID) of
2617 UID_GAME: k := 255;
2618 UID_PLAYER:
2619 begin
2620 p := g_Player_Get(UID);
2621 if p <> nil then
2622 k := p.GetKeys
2623 else
2624 k := 0;
2625 end;
2626 else k := 0;
2627 end;
2629 for a := 0 to High(gTriggers) do
2630 if (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2631 (gTriggers[a].TimeOut = 0) and
2632 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2633 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2634 if g_CollideLine(x1, y1, x2, y2, gTriggers[a].X, gTriggers[a].Y,
2635 gTriggers[a].Width, gTriggers[a].Height) then
2636 begin
2637 gTriggers[a].ActivateUID := UID;
2638 ActivateTrigger(gTriggers[a], ActivateType);
2639 end;
2640 end;
2642 procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateType: Byte; IgnoreTrigger: Integer = -1);
2643 var
2644 a: Integer;
2645 k: Byte;
2646 rsq: Word;
2647 p: TPlayer;
2648 begin
2649 if gTriggers = nil then
2650 Exit;
2652 case g_GetUIDType(UID) of
2653 UID_GAME: k := 255;
2654 UID_PLAYER:
2655 begin
2656 p := g_Player_Get(UID);
2657 if p <> nil then
2658 k := p.GetKeys
2659 else
2660 k := 0;
2661 end;
2662 else k := 0;
2663 end;
2665 rsq := Radius * Radius;
2667 for a := 0 to High(gTriggers) do
2668 if (gTriggers[a].ID <> DWORD(IgnoreTrigger)) and
2669 (gTriggers[a].TriggerType <> TRIGGER_NONE) and
2670 (gTriggers[a].TimeOut = 0) and
2671 ((gTriggers[a].Keys and k) = gTriggers[a].Keys) and
2672 ByteBool(gTriggers[a].ActivateType and ActivateType) then
2673 with gTriggers[a] do
2674 if g_Collide(CX-Radius, CY-Radius, 2*Radius, 2*Radius,
2675 X, Y, Width, Height) then
2676 if ((Sqr(CX-X)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ëåâîìó óãëó
2677 ((Sqr(CX-X-Width)+Sqr(CY-Y)) < rsq) or // Öåíòð êðóãà áëèçîê ê âåðõíåìó ïðàâîìó óãëó
2678 ((Sqr(CX-X-Width)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ïðàâîìó óãëó
2679 ((Sqr(CX-X)+Sqr(CY-Y-Height)) < rsq) or // Öåíòð êðóãà áëèçîê ê íèæíåìó ëåâîìó óãëó
2680 ( (CX > (X-Radius)) and (CX < (X+Width+Radius)) and
2681 (CY > Y) and (CY < (Y+Height)) ) or // Öåíòð êðóãà íåäàëåêî îò âåðòèêàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2682 ( (CY > (Y-Radius)) and (CY < (Y+Height+Radius)) and
2683 (CX > X) and (CX < (X+Width)) ) then // Öåíòð êðóãà íåäàëåêî îò ãîðèçîíòàëüíûõ ãðàíèö ïðÿìîóãîëüíèêà
2684 begin
2685 ActivateUID := UID;
2686 ActivateTrigger(gTriggers[a], ActivateType);
2687 end;
2688 end;
2690 procedure g_Triggers_OpenAll();
2691 var
2692 a: Integer;
2693 b: Boolean;
2694 begin
2695 if gTriggers = nil then Exit;
2697 b := False;
2698 for a := 0 to High(gTriggers) do
2699 with gTriggers[a] do
2700 if (TriggerType = TRIGGER_OPENDOOR) or
2701 (TriggerType = TRIGGER_DOOR5) or
2702 (TriggerType = TRIGGER_DOOR) then
2703 begin
2704 tr_OpenDoor(trigPanelID, True, trigData.trigd2d_doors);
2705 if TriggerType = TRIGGER_DOOR5 then DoorTime := 180;
2706 b := True;
2707 end;
2709 if b then g_Sound_PlayEx('SOUND_GAME_DOOROPEN');
2710 end;
2712 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
2713 begin
2714 if (gTriggers <> nil) then
2715 if gTriggers[ID].SpawnedCount > 0 then
2716 Dec(gTriggers[ID].SpawnedCount);
2717 end;
2719 procedure g_Triggers_Free();
2720 var
2721 a: Integer;
2722 begin
2723 for a := 0 to High(gTriggers) do
2724 begin
2725 if (gTriggers[a].TriggerType = TRIGGER_SOUND) then
2726 begin
2727 if g_Sound_Exists(gTriggers[a].trigData.trigSoundName) then
2728 begin
2729 g_Sound_Delete(gTriggers[a].trigData.trigSoundName);
2730 end;
2731 gTriggers[a].Sound.Free();
2732 end;
2733 if (gTriggers[a].Activators <> nil) then
2734 begin
2735 SetLength(gTriggers[a].Activators, 0);
2736 end;
2737 gTriggers[a].trigData.Free();
2738 end;
2740 gTriggers := nil;
2741 gSecretsCount := 0;
2742 SetLength(gMonstersSpawned, 0);
2743 end;
2745 procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
2746 var
2747 count, act_count, i, j: Integer;
2748 dw: DWORD;
2749 sg: Single;
2750 b: Boolean;
2751 begin
2752 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
2753 count := Length(gTriggers);
2755 Mem := TBinMemoryWriter.Create((count+1) * 200);
2757 // Êîëè÷åñòâî òðèããåðîâ:
2758 Mem.WriteInt(count);
2760 e_LogWritefln('saving %s triggers (count=%s)', [Length(gTriggers), count]);
2762 if count = 0 then exit;
2764 for i := 0 to High(gTriggers) do
2765 begin
2766 // Ñèãíàòóðà òðèããåðà:
2767 dw := TRIGGER_SIGNATURE; // 'TRGX'
2768 Mem.WriteDWORD(dw);
2769 // Òèï òðèããåðà:
2770 Mem.WriteByte(gTriggers[i].TriggerType);
2771 if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
2772 // Ñïåöèàëüíûå äàííûå òðèããåðà: äà â æîïó, ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
2773 e_LogWritefln('=== trigger #%s saved ===', [gTriggers[i].mapIndex]);
2774 Mem.WriteInt(gTriggers[i].mapIndex);
2775 //p := @gTriggers[i].Data;
2776 //Mem.WriteMemory(p, SizeOf(TTriggerData));
2777 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2778 Mem.WriteInt(gTriggers[i].X);
2779 Mem.WriteInt(gTriggers[i].Y);
2780 // Ðàçìåðû:
2781 Mem.WriteWord(gTriggers[i].Width);
2782 Mem.WriteWord(gTriggers[i].Height);
2783 // Âêëþ÷åí ëè òðèããåð:
2784 Mem.WriteBoolean(gTriggers[i].Enabled);
2785 // Òèï àêòèâàöèè òðèããåðà:
2786 Mem.WriteByte(gTriggers[i].ActivateType);
2787 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2788 Mem.WriteByte(gTriggers[i].Keys);
2789 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2790 Mem.WriteInt(gTriggers[i].TexturePanel);
2791 // Òèï ýòîé ïàíåëè:
2792 Mem.WriteWord(gTriggers[i].TexturePanelType);
2793 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
2794 Mem.WriteInt(gTriggers[i].trigPanelId);
2795 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2796 Mem.WriteWord(gTriggers[i].TimeOut);
2797 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2798 Mem.WriteWord(gTriggers[i].ActivateUID);
2799 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2800 act_count := Length(gTriggers[i].Activators);
2801 Mem.WriteInt(act_count);
2802 for j := 0 to act_count-1 do
2803 begin
2804 // UID îáúåêòà
2805 Mem.WriteWord(gTriggers[i].Activators[j].UID);
2806 // Âðåìÿ îæèäàíèÿ
2807 Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
2808 end;
2809 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2810 Mem.WriteBoolean(gTriggers[i].PlayerCollide);
2811 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2812 Mem.WriteInt(gTriggers[i].DoorTime);
2813 // Çàäåðæêà àêòèâàöèè:
2814 Mem.WriteInt(gTriggers[i].PressTime);
2815 // Ñ÷åò÷èê íàæàòèé:
2816 Mem.WriteInt(gTriggers[i].PressCount);
2817 // Ñïàâíåð àêòèâåí:
2818 Mem.WriteBoolean(gTriggers[i].AutoSpawn);
2819 // Çàäåðæêà ñïàâíåðà:
2820 Mem.WriteInt(gTriggers[i].SpawnCooldown);
2821 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2822 Mem.WriteInt(gTriggers[i].SpawnedCount);
2823 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2824 Mem.WriteInt(gTriggers[i].SoundPlayCount);
2825 // Ïðîèãðûâàåòñÿ ëè çâóê?
2826 if gTriggers[i].Sound <> nil then
2827 b := gTriggers[i].Sound.IsPlaying()
2828 else
2829 b := False;
2830 Mem.WriteBoolean(b);
2831 if b then
2832 begin
2833 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2834 dw := gTriggers[i].Sound.GetPosition();
2835 Mem.WriteDWORD(dw);
2836 // Ãðîìêîñòü çâóêà:
2837 sg := gTriggers[i].Sound.GetVolume();
2838 sg := sg / (gSoundLevel/255.0);
2839 Mem.WriteSingle(sg);
2840 // Ñòåðåî ñìåùåíèå çâóêà:
2841 sg := gTriggers[i].Sound.GetPan();
2842 Mem.WriteSingle(sg);
2843 end;
2844 end;
2845 end;
2847 procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
2848 var
2849 count, act_count, i, j, a: Integer;
2850 dw: DWORD;
2851 vol, pan: Single;
2852 b: Boolean;
2853 //p: Pointer;
2854 Trig: TTrigger;
2855 mapIndex: Integer;
2856 //tw: TStrTextWriter;
2857 begin
2858 if Mem = nil then
2859 Exit;
2861 g_Triggers_Free();
2863 // Êîëè÷åñòâî òðèããåðîâ:
2864 Mem.ReadInt(count);
2866 if (count = 0) then exit;
2868 for a := 0 to count-1 do
2869 begin
2870 // Ñèãíàòóðà òðèããåðà:
2871 Mem.ReadDWORD(dw);
2872 if (dw <> TRIGGER_SIGNATURE) then // 'TRGX'
2873 begin
2874 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
2875 end;
2876 // Òèï òðèããåðà:
2877 Mem.ReadByte(Trig.TriggerType);
2878 // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
2879 if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
2880 Mem.ReadInt(mapIndex);
2881 //!!!FIXME!!!
2883 Mem.ReadMemory(p, dw);
2884 if dw <> SizeOf(TTriggerData) then
2885 begin
2886 raise EBinSizeError.Create('g_Triggers_LoadState: Wrong TriggerData Size');
2887 end;
2888 Trig.Data := TTriggerData(p^);
2890 // Ñîçäàåì òðèããåð:
2891 i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
2893 if (gTriggers[i].trigData <> nil) then
2894 begin
2895 tw := TStrTextWriter.Create();
2896 try
2897 gTriggers[i].trigData.writeTo(tw);
2898 e_LogWritefln('=== trigger #%s loaded ==='#10'%s'#10'---', [mapIndex, tw.str]);
2899 finally
2900 tw.Free();
2901 end;
2902 end;
2904 // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
2905 Mem.ReadInt(gTriggers[i].X);
2906 Mem.ReadInt(gTriggers[i].Y);
2907 // Ðàçìåðû:
2908 Mem.ReadWord(gTriggers[i].Width);
2909 Mem.ReadWord(gTriggers[i].Height);
2910 // Âêëþ÷åí ëè òðèããåð:
2911 Mem.ReadBoolean(gTriggers[i].Enabled);
2912 // Òèï àêòèâàöèè òðèããåðà:
2913 Mem.ReadByte(gTriggers[i].ActivateType);
2914 // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
2915 Mem.ReadByte(gTriggers[i].Keys);
2916 // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
2917 Mem.ReadInt(gTriggers[i].TexturePanel);
2918 // Òèï ýòîé ïàíåëè:
2919 Mem.ReadWord(gTriggers[i].TexturePanelType);
2920 // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
2921 Mem.ReadInt(gTriggers[i].trigPanelId);
2922 // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
2923 Mem.ReadWord(gTriggers[i].TimeOut);
2924 // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
2925 Mem.ReadWord(gTriggers[i].ActivateUID);
2926 // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
2927 Mem.ReadInt(act_count);
2928 if act_count > 0 then
2929 begin
2930 SetLength(gTriggers[i].Activators, act_count);
2931 for j := 0 to act_count-1 do
2932 begin
2933 // UID îáúåêòà
2934 Mem.ReadWord(gTriggers[i].Activators[j].UID);
2935 // Âðåìÿ îæèäàíèÿ
2936 Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
2937 end;
2938 end;
2939 // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
2940 Mem.ReadBoolean(gTriggers[i].PlayerCollide);
2941 // Âðåìÿ äî çàêðûòèÿ äâåðè:
2942 Mem.ReadInt(gTriggers[i].DoorTime);
2943 // Çàäåðæêà àêòèâàöèè:
2944 Mem.ReadInt(gTriggers[i].PressTime);
2945 // Ñ÷åò÷èê íàæàòèé:
2946 Mem.ReadInt(gTriggers[i].PressCount);
2947 // Ñïàâíåð àêòèâåí:
2948 Mem.ReadBoolean(gTriggers[i].AutoSpawn);
2949 // Çàäåðæêà ñïàâíåðà:
2950 Mem.ReadInt(gTriggers[i].SpawnCooldown);
2951 // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
2952 Mem.ReadInt(gTriggers[i].SpawnedCount);
2953 // Ñêîëüêî ðàç ïðîèãðàí çâóê:
2954 Mem.ReadInt(gTriggers[i].SoundPlayCount);
2955 // Ïðîèãðûâàåòñÿ ëè çâóê?
2956 Mem.ReadBoolean(b);
2957 if b then
2958 begin
2959 // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
2960 Mem.ReadDWORD(dw);
2961 // Ãðîìêîñòü çâóêà:
2962 Mem.ReadSingle(vol);
2963 // Ñòåðåî ñìåùåíèå çâóêà:
2964 Mem.ReadSingle(pan);
2965 // Çàïóñêàåì çâóê, åñëè åñòü:
2966 if gTriggers[i].Sound <> nil then
2967 begin
2968 gTriggers[i].Sound.PlayPanVolume(pan, vol);
2969 gTriggers[i].Sound.Pause(True);
2970 gTriggers[i].Sound.SetPosition(dw);
2971 end
2972 end;
2973 end;
2974 end;
2976 end.