1 (* Copyright (C) DooM 2D:Forever Developers
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.
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.
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/>.
22 e_graphics
, g_playermodel
, g_basic
, g_textures
,
23 g_weapons
, g_phys
, g_sound
, g_saveload
, MAPSTRUCT
,
73 ANGLE_NONE
= Low(SmallInt);
75 CORPSE_STATE_REMOVEME
= 0;
76 CORPSE_STATE_NORMAL
= 1;
77 CORPSE_STATE_MESS
= 2;
79 PLAYER_RECT
: TRectWH
= (X
:15; Y
:12; Width
:34; Height
:52);
80 PLAYER_RECT_CX
= 15+(34 div 2);
81 PLAYER_RECT_CY
= 12+(52 div 2);
82 PLAYER_CORPSERECT
: TRectWH
= (X
:15; Y
:48; Width
:34; Height
:16);
85 PLAYER_HP_LIMIT
= 200;
87 PLAYER_AP_LIMIT
= 200;
90 PLAYER1_DEF_COLOR
: TRGB
= (R
:64; G
:175; B
:48);
91 PLAYER2_DEF_COLOR
: TRGB
= (R
:96; G
:96; B
:96);
107 TPlayerStatArray
= Array of TPlayerStat
;
109 TPlayerSavedState
= record
115 Ammo
: Array [A_BULLETS
..A_CELLS
] of Word;
116 MaxAmmo
: Array [A_BULLETS
..A_CELLS
] of Word;
117 Weapon
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Boolean;
118 Rulez
: Set of R_ITEM_BACKPACK
..R_BERSERK
;
127 TPlayer
= class (TObject
)
135 FDirection
: TDirection
;
143 FMonsterKills
: Integer;
149 FCanJetpack
: Boolean;
154 FBFGFireCounter
: SmallInt;
155 FLastSpawnerUID
: Word;
159 FSpectatePlayer
: Integer;
161 FSavedState
: TPlayerSavedState
;
163 FModel
: TPlayerModel
;
166 FActionForce
: Boolean;
167 FActionChanged
: Boolean;
169 FFireAngle
: SmallInt;
171 FShellTimer
: Integer;
173 FSawSound
: TPlayableSound
;
174 FSawSoundIdle
: TPlayableSound
;
175 FSawSoundHit
: TPlayableSound
;
176 FSawSoundSelect
: TPlayableSound
;
177 FJetSoundOn
: TPlayableSound
;
178 FJetSoundOff
: TPlayableSound
;
179 FJetSoundFly
: TPlayableSound
;
183 FJustTeleported
: Boolean;
186 function CollideLevel(XInc
, YInc
: Integer): Boolean;
187 function StayOnStep(XInc
, YInc
: Integer): Boolean;
188 function HeadInLiquid(XInc
, YInc
: Integer): Boolean;
189 function BodyInLiquid(XInc
, YInc
: Integer): Boolean;
190 function BodyInAcid(XInc
, YInc
: Integer): Boolean;
191 function FullInLift(XInc
, YInc
: Integer): Integer;
192 {procedure CollideItem();}
193 procedure FlySmoke(Times
: DWORD
= 1);
194 function GetAmmoByWeapon(Weapon
: Byte): Word;
195 procedure SetAction(Action
: Byte; Force
: Boolean = False);
196 procedure OnDamage(Angle
: SmallInt); virtual;
197 function firediry(): Integer;
199 procedure Run(Direction
: TDirection
);
200 procedure NextWeapon();
201 procedure PrevWeapon();
209 FDamageBuffer
: Integer;
211 FAmmo
: Array [A_BULLETS
..A_CELLS
] of Word;
212 FMaxAmmo
: Array [A_BULLETS
..A_CELLS
] of Word;
213 FWeapon
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Boolean;
214 FRulez
: Set of R_ITEM_BACKPACK
..R_BERSERK
;
216 FMegaRulez
: Array [MR_SUIT
..MR_MAX
] of DWORD
;
217 FReloading
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Word;
218 FTime
: Array [T_RESPAWN
..T_FLAGCAP
] of DWORD
;
219 FKeys
: Array [KEY_LEFT
..KEY_CHAT
] of TKeyState
;
221 FPreferredTeam
: Byte;
224 FWantsInGame
: Boolean;
228 FActualModelName
: string;
234 constructor Create(); virtual;
235 destructor Destroy(); override;
236 procedure Respawn(Silent
: Boolean; Force
: Boolean = False); virtual;
237 function GetRespawnPoint(): Byte;
238 procedure PressKey(Key
: Byte; Time
: Word = 1);
239 procedure ReleaseKeys();
240 procedure SetModel(ModelName
: String);
241 procedure SetColor(Color
: TRGB
);
242 procedure SetWeapon(W
: Byte);
243 function IsKeyPressed(K
: Byte): Boolean;
244 function GetKeys(): Byte;
245 function PickItem(ItemType
: Byte; respawn
: Boolean; var remove
: Boolean): Boolean; virtual;
246 function Collide(X
, Y
: Integer; Width
, Height
: Word): Boolean; overload
;
247 function Collide(Panel
: TPanel
): Boolean; overload
;
248 function Collide(X
, Y
: Integer): Boolean; overload
;
249 procedure SetDirection(Direction
: TDirection
);
250 procedure GetSecret();
251 function TeleportTo(X
, Y
: Integer; silent
: Boolean; dir
: Byte): Boolean;
253 procedure Push(vx
, vy
: Integer);
254 procedure ChangeModel(ModelName
: String);
255 procedure SwitchTeam
;
256 procedure ChangeTeam(Team
: Byte);
258 function GetFlag(Flag
: Byte): Boolean;
259 procedure SetFlag(Flag
: Byte);
260 function DropFlag(): Boolean;
261 procedure AllRulez(Health
: Boolean);
262 procedure RestoreHealthArmor();
263 procedure FragCombo();
264 procedure GiveItem(ItemType
: Byte);
265 procedure Damage(value
: Word; SpawnerUID
: Word; vx
, vy
: Integer; t
: Byte); virtual;
266 function Heal(value
: Word; Soft
: Boolean): Boolean; virtual;
267 procedure MakeBloodVector(Count
: Word; VelX
, VelY
: Integer);
268 procedure MakeBloodSimple(Count
: Word);
269 procedure Kill(KillType
: Byte; SpawnerUID
: Word; t
: Byte);
270 procedure Reset(Force
: Boolean);
271 procedure Spectate(NoMove
: Boolean = False);
272 procedure SwitchNoClip
;
273 procedure SoftReset();
274 procedure Draw(); virtual;
275 procedure DrawPain();
276 procedure DrawPickup();
277 procedure DrawRulez();
279 procedure DrawBubble();
281 procedure Update(); virtual;
282 procedure RememberState();
283 procedure RecallState();
284 procedure SaveState(var Mem
: TBinMemoryWriter
); virtual;
285 procedure LoadState(var Mem
: TBinMemoryReader
); virtual;
286 procedure PauseSounds(Enable
: Boolean);
287 procedure NetFire(Wpn
: Byte; X
, Y
, AX
, AY
: Integer; WID
: Integer = -1);
288 procedure DoLerp(Level
: Integer = 2);
289 procedure SetLerp(XTo
, YTo
: Integer);
290 procedure ForceWeapon(Weapon
: Byte);
292 procedure JetpackOff
;
294 property Name
: String read FName write FName
;
295 property Model
: TPlayerModel read FModel
;
296 property Health
: Integer read FHealth write FHealth
;
297 property Lives
: Byte read FLives write FLives
;
298 property Armor
: Integer read FArmor write FArmor
;
299 property Air
: Integer read FAir write FAir
;
300 property JetFuel
: Integer read FJetFuel write FJetFuel
;
301 property Frags
: Integer read FFrags write FFrags
;
302 property Death
: Integer read FDeath write FDeath
;
303 property Kills
: Integer read FKills write FKills
;
304 property CurrWeap
: Byte read FCurrWeap write FCurrWeap
;
305 property MonsterKills
: Integer read FMonsterKills write FMonsterKills
;
306 property Secrets
: Integer read FSecrets
;
307 property GodMode
: Boolean read FGodMode write FGodMode
;
308 property NoTarget
: Boolean read FNoTarget write FNoTarget
;
309 property NoReload
: Boolean read FNoReload write FNoReload
;
310 property Live
: Boolean read FLive write FLive
;
311 property Flag
: Byte read FFlag
;
312 property Team
: Byte read FTeam write FTeam
;
313 property Direction
: TDirection read FDirection
;
314 property GameX
: Integer read FObj
.X write FObj
.X
;
315 property GameY
: Integer read FObj
.Y write FObj
.Y
;
316 property GameVelX
: Integer read FObj
.Vel
.X write FObj
.Vel
.X
;
317 property GameVelY
: Integer read FObj
.Vel
.Y write FObj
.Vel
.Y
;
318 property GameAccelX
: Integer read FObj
.Accel
.X write FObj
.Accel
.X
;
319 property GameAccelY
: Integer read FObj
.Accel
.Y write FObj
.Accel
.Y
;
320 property Vel
: TPoint2i read FObj
.Vel
;
321 property Obj
: TObj read FObj
;
322 property IncCam
: Integer read FIncCam write FIncCam
;
323 property UID
: Word read FUID write FUID
;
324 property JustTeleported
: Boolean read FJustTeleported write FJustTeleported
;
325 property NetTime
: LongWord read FNetTime write FNetTime
;
335 WeaponPrior
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte;
336 CloseWeaponPrior
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte;
337 //SafeWeaponPrior: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte;
345 TBot
= class (TPlayer
)
347 FSelectedWeapon
: Byte;
350 FAIFlags
: Array of TAIFlag
;
351 FDifficult
: TDifficult
;
353 function GetRnd(a
: Byte): Boolean;
354 function GetInterval(a
: Byte; radius
: SmallInt): SmallInt;
355 function RunDirection(): TDirection
;
356 function FullInStep(XInc
, YInc
: Integer): Boolean;
357 //function NeedItem(Item: Byte): Byte;
358 procedure SelectWeapon(Dist
: Integer);
359 procedure SetAIFlag(fName
, fValue
: String20
);
360 function GetAIFlag(fName
: String20
): String20
;
361 procedure RemoveAIFlag(fName
: String20
);
362 function Healthy(): Byte;
363 procedure UpdateMove();
364 procedure UpdateCombat();
365 function KeyPressed(Key
: Word): Boolean;
366 procedure ReleaseKey(Key
: Byte);
367 function TargetOnScreen(TX
, TY
: Integer): Boolean;
368 procedure OnDamage(Angle
: SmallInt); override;
371 procedure Respawn(Silent
: Boolean; Force
: Boolean = False); override;
372 constructor Create(); override;
373 destructor Destroy(); override;
374 procedure Draw(); override;
375 function PickItem(ItemType
: Byte; force
: Boolean; var remove
: Boolean): Boolean; override;
376 function Heal(value
: Word; Soft
: Boolean): Boolean; override;
377 procedure Update(); override;
378 procedure SaveState(var Mem
: TBinMemoryWriter
); override;
379 procedure LoadState(var Mem
: TBinMemoryReader
); override;
401 TCorpse
= class (TObject
)
409 FAnimation
: TAnimation
;
410 FAnimationMask
: TAnimation
;
413 constructor Create(X
, Y
: Integer; ModelName
: String; aMess
: Boolean);
414 destructor Destroy(); override;
415 procedure Damage(Value
: Word; vx
, vy
: Integer);
418 procedure SaveState(var Mem
: TBinMemoryWriter
);
419 procedure LoadState(var Mem
: TBinMemoryReader
);
421 property Obj
: TObj read FObj
;
422 property State
: Byte read FState
;
423 property Mess
: Boolean read FMess
;
426 TTeamStat
= Array [TEAM_RED
..TEAM_BLUE
] of
432 gPlayers
: Array of TPlayer
;
433 gCorpses
: Array of TCorpse
;
434 gGibs
: Array of TGib
;
435 gShells
: Array of TShell
;
436 gTeamStat
: TTeamStat
;
437 gFly
: Boolean = False;
438 gAimLine
: Boolean = False;
439 gChatBubble
: Byte = 0;
443 MAX_RUNVEL
: Integer = 8;
444 VEL_JUMP
: Integer = 10;
445 SHELL_TIMEOUT
: Cardinal = 60000;
447 function Lerp(X
, Y
, Factor
: Integer): Integer;
449 procedure g_Gibs_SetMax(Count
: Word);
450 function g_Gibs_GetMax(): Word;
451 procedure g_Corpses_SetMax(Count
: Word);
452 function g_Corpses_GetMax(): Word;
453 procedure g_Shells_SetMax(Count
: Word);
454 function g_Shells_GetMax(): Word;
456 procedure g_Player_Init();
457 procedure g_Player_Free();
458 function g_Player_Create(ModelName
: String; Color
: TRGB
; Team
: Byte; Bot
: Boolean): Word;
459 function g_Player_CreateFromState(var Mem
: TBinMemoryReader
): Word;
460 procedure g_Player_Remove(UID
: Word);
461 procedure g_Player_ResetTeams();
462 procedure g_Player_UpdateAll();
463 procedure g_Player_DrawAll();
464 procedure g_Player_DrawDebug(p
: TPlayer
);
465 procedure g_Player_DrawHealth();
466 procedure g_Player_RememberAll();
467 procedure g_Player_ResetAll(Force
, Silent
: Boolean);
468 function g_Player_Get(UID
: Word): TPlayer
;
469 function g_Player_GetCount(): Byte;
470 function g_Player_GetStats(): TPlayerStatArray
;
471 function g_Player_ValidName(Name
: String): Boolean;
472 procedure g_Player_CreateCorpse(Player
: TPlayer
);
473 procedure g_Player_CreateGibs(fX
, fY
: Integer; ModelName
: String; fColor
: TRGB
);
474 procedure g_Player_CreateShell(fX
, fY
, dX
, dY
: Integer; T
: Byte);
475 procedure g_Player_UpdatePhysicalObjects();
476 procedure g_Player_DrawCorpses();
477 procedure g_Player_DrawShells();
478 procedure g_Player_RemoveAllCorpses();
479 procedure g_Player_Corpses_SaveState(var Mem
: TBinMemoryWriter
);
480 procedure g_Player_Corpses_LoadState(var Mem
: TBinMemoryReader
);
481 procedure g_Bot_Add(Team
, Difficult
: Byte);
482 procedure g_Bot_AddList(Team
: Byte; lname
: ShortString; num
: Integer = -1);
483 procedure g_Bot_MixNames();
484 procedure g_Bot_RemoveAll();
489 e_log
, g_map
, g_items
, g_console
, SysUtils
, g_gfx
, Math
,
490 g_options
, g_triggers
, g_menu
, MAPDEF
, g_game
,
491 wadreader
, g_main
, g_monsters
, CONFIG
, g_language
, g_net
, g_netmsg
;
501 diag_precision
: Byte;
505 w_prior1
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte;
506 w_prior2
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte;
507 w_prior3
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte;
511 TIME_RESPAWN1
= 1500;
512 TIME_RESPAWN2
= 2000;
513 TIME_RESPAWN3
= 3000;
516 JET_MAX
= 540; // ~30 sec
517 PLAYER_SUIT_TIME
= 30000;
518 PLAYER_INVUL_TIME
= 30000;
519 PLAYER_INVIS_TIME
= 35000;
520 FRAG_COMBO_TIME
= 3000;
524 ANGLE_RIGHTDOWN
= -35;
526 ANGLE_LEFTDOWN
= -145;
527 PLAYER_HEADRECT
: TRectWH
= (X
:24; Y
:12; Width
:20; Height
:12);
528 WEAPONPOINT
: Array [TDirection
] of TPoint
= ((X
:16; Y
:32), (X
:47; Y
:32));
531 BOT_UNSAFEDIST
= 128;
532 TEAMCOLOR
: Array [TEAM_RED
..TEAM_BLUE
] of TRGB
= ((R
:255; G
:0; B
:0),
534 DIFFICULT_EASY
: TDifficult
= (DiagFire
: 32; InvisFire
: 32; DiagPrecision
: 32;
535 FlyPrecision
: 32; Cover
: 32; CloseJump
: 32;
536 WeaponPrior
:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior
:(0,0,0,0,0,0,0,0,0,0));
537 DIFFICULT_MEDIUM
: TDifficult
= (DiagFire
: 127; InvisFire
: 127; DiagPrecision
: 127;
538 FlyPrecision
: 127; Cover
: 127; CloseJump
: 127;
539 WeaponPrior
:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior
:(0,0,0,0,0,0,0,0,0,0));
540 DIFFICULT_HARD
: TDifficult
= (DiagFire
: 255; InvisFire
: 255; DiagPrecision
: 255;
541 FlyPrecision
: 255; Cover
: 255; CloseJump
: 255;
542 WeaponPrior
:(0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior
:(0,0,0,0,0,0,0,0,0,0));
543 WEAPON_PRIOR1
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte =
544 (WEAPON_SUPERPULEMET
, WEAPON_SHOTGUN2
, WEAPON_SHOTGUN1
,
545 WEAPON_CHAINGUN
, WEAPON_PLASMA
, WEAPON_ROCKETLAUNCHER
,
546 WEAPON_BFG
, WEAPON_PISTOL
, WEAPON_SAW
, WEAPON_KASTET
);
547 WEAPON_PRIOR2
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte =
548 (WEAPON_SUPERPULEMET
, WEAPON_BFG
, WEAPON_ROCKETLAUNCHER
,
549 WEAPON_SHOTGUN2
, WEAPON_PLASMA
, WEAPON_SHOTGUN1
,
550 WEAPON_CHAINGUN
, WEAPON_PISTOL
, WEAPON_SAW
, WEAPON_KASTET
);
551 //WEAPON_PRIOR3: Array [WEAPON_KASTET..WEAPON_SUPERPULEMET] of Byte =
552 // (WEAPON_SUPERPULEMET, WEAPON_BFG, WEAPON_PLASMA,
553 // WEAPON_SHOTGUN2, WEAPON_CHAINGUN, WEAPON_SHOTGUN1,
554 // WEAPON_SAW, WEAPON_ROCKETLAUNCHER, WEAPON_PISTOL, WEAPON_KASTET);
555 WEAPON_RELOAD
: Array [WEAPON_KASTET
..WEAPON_SUPERPULEMET
] of Byte =
556 (5, 2, 6, 18, 36, 2, 12, 2, 14, 2);
558 PLAYER_SIGNATURE
= $52594C50; // 'PLYR'
559 CORPSE_SIGNATURE
= $50524F43; // 'CORP'
561 BOTNAMES_FILENAME
= 'botnames.txt';
562 BOTLIST_FILENAME
= 'botlist.txt';
566 MaxCorpses
: Word = 20;
567 MaxShells
: Word = 300;
568 CurrentGib
: Integer = 0;
569 CurrentShell
: Integer = 0;
570 BotNames
: Array of String;
571 BotList
: Array of TBotProfile
;
573 function Lerp(X
, Y
, Factor
: Integer): Integer;
575 Result
:= X
+ ((Y
- X
) div Factor
);
578 function SameTeam(UID1
, UID2
: Word): Boolean;
582 if (UID1
> UID_MAX_PLAYER
) or (UID1
<= UID_MAX_GAME
) or
583 (UID2
> UID_MAX_PLAYER
) or (UID2
<= UID_MAX_GAME
) then Exit
;
585 if (g_Player_Get(UID1
) = nil) or (g_Player_Get(UID2
) = nil) then Exit
;
587 if ((g_Player_Get(UID1
).Team
= TEAM_NONE
) or
588 (g_Player_Get(UID2
).Team
= TEAM_NONE
)) then Exit
;
590 Result
:= g_Player_Get(UID1
).FTeam
= g_Player_Get(UID2
).FTeam
;
593 procedure g_Gibs_SetMax(Count
: Word);
596 SetLength(gGibs
, Count
);
598 if CurrentGib
>= Count
then
602 function g_Gibs_GetMax(): Word;
607 procedure g_Shells_SetMax(Count
: Word);
610 SetLength(gShells
, Count
);
612 if CurrentShell
>= Count
then
616 function g_Shells_GetMax(): Word;
622 procedure g_Corpses_SetMax(Count
: Word);
625 SetLength(gCorpses
, Count
);
628 function g_Corpses_GetMax(): Word;
630 Result
:= MaxCorpses
;
633 function g_Player_Create(ModelName
: String; Color
: TRGB
; Team
: Byte; Bot
: Boolean): Word;
643 // Åñòü ëè ìåñòî â gPlayers:
644 if gPlayers
<> nil then
645 for a
:= 0 to High(gPlayers
) do
646 if gPlayers
[a
] = nil then
652 // Íåò ìåñòà - ðàñøèðÿåì gPlayers:
655 SetLength(gPlayers
, Length(gPlayers
)+1);
659 // Ñîçäàåì îáúåêò èãðîêà:
661 gPlayers
[a
] := TBot
.Create()
663 gPlayers
[a
] := TPlayer
.Create();
666 gPlayers
[a
].FActualModelName
:= ModelName
;
667 gPlayers
[a
].SetModel(ModelName
);
669 // Íåò ìîäåëè - ñîçäàíèå íå âîçìîæíî:
670 if gPlayers
[a
].FModel
= nil then
674 g_FatalError(Format(_lc
[I_GAME_ERROR_MODEL
], [ModelName
]));
678 if not (Team
in [TEAM_RED
, TEAM_BLUE
]) then
679 if Random(2) = 0 then
683 gPlayers
[a
].FPreferredTeam
:= Team
;
685 case gGameSettings
.GameMode
of
686 GM_DM
: gPlayers
[a
].FTeam
:= TEAM_NONE
;
688 GM_CTF
: gPlayers
[a
].FTeam
:= gPlayers
[a
].FPreferredTeam
;
690 GM_COOP
: gPlayers
[a
].FTeam
:= TEAM_COOP
;
693 // Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû:
694 gPlayers
[a
].FColor
:= Color
;
695 if gPlayers
[a
].FTeam
in [TEAM_RED
, TEAM_BLUE
] then
696 gPlayers
[a
].FModel
.Color
:= TEAMCOLOR
[gPlayers
[a
].FTeam
]
698 gPlayers
[a
].FModel
.Color
:= Color
;
700 gPlayers
[a
].FUID
:= g_CreateUID(UID_PLAYER
);
701 gPlayers
[a
].FLive
:= False;
703 Result
:= gPlayers
[a
].FUID
;
706 function g_Player_CreateFromState(var Mem
: TBinMemoryReader
): Word;
719 if sig
<> PLAYER_SIGNATURE
then // 'PLYR'
721 raise EBinSizeError
.Create('g_Player_CreateFromState: Wrong Player Signature');
725 Mem
.ReadBoolean(Bot
);
730 // Åñòü ëè ìåñòî â gPlayers:
731 if gPlayers
<> nil then
732 for a
:= 0 to High(gPlayers
) do
733 if gPlayers
[a
] = nil then
739 // Íåò ìåñòà - ðàñøèðÿåì gPlayers:
742 SetLength(gPlayers
, Length(gPlayers
)+1);
746 // Ñîçäàåì îáúåêò èãðîêà:
748 gPlayers
[a
] := TBot
.Create()
750 gPlayers
[a
] := TPlayer
.Create();
751 gPlayers
[a
].FIamBot
:= Bot
;
752 gPlayers
[a
].FPhysics
:= True;
755 Mem
.ReadWord(gPlayers
[a
].FUID
);
757 Mem
.ReadString(gPlayers
[a
].FName
);
759 Mem
.ReadByte(gPlayers
[a
].FTeam
);
760 gPlayers
[a
].FPreferredTeam
:= gPlayers
[a
].FTeam
;
762 Mem
.ReadBoolean(gPlayers
[a
].FLive
);
763 // Èçðàñõîäîâàë ëè âñå æèçíè:
764 Mem
.ReadBoolean(gPlayers
[a
].FNoRespawn
);
768 gPlayers
[a
].FDirection
:= D_LEFT
770 gPlayers
[a
].FDirection
:= D_RIGHT
;
772 Mem
.ReadInt(gPlayers
[a
].FHealth
);
774 Mem
.ReadByte(gPlayers
[a
].FLives
);
776 Mem
.ReadInt(gPlayers
[a
].FArmor
);
778 Mem
.ReadInt(gPlayers
[a
].FAir
);
780 Mem
.ReadInt(gPlayers
[a
].FJetFuel
);
782 Mem
.ReadInt(gPlayers
[a
].FPain
);
784 Mem
.ReadInt(gPlayers
[a
].FKills
);
786 Mem
.ReadInt(gPlayers
[a
].FMonsterKills
);
788 Mem
.ReadInt(gPlayers
[a
].FFrags
);
790 Mem
.ReadByte(gPlayers
[a
].FFragCombo
);
791 // Âðåìÿ ïîñëåäíåãî ôðàãà:
792 Mem
.ReadDWORD(gPlayers
[a
].FLastFrag
);
794 Mem
.ReadInt(gPlayers
[a
].FDeath
);
796 Mem
.ReadByte(gPlayers
[a
].FFlag
);
798 Mem
.ReadInt(gPlayers
[a
].FSecrets
);
800 Mem
.ReadByte(gPlayers
[a
].FCurrWeap
);
801 // Âðåìÿ çàðÿäêè BFG:
802 Mem
.ReadSmallInt(gPlayers
[a
].FBFGFireCounter
);
804 Mem
.ReadInt(gPlayers
[a
].FDamageBuffer
);
805 // Ïîñëåäíèé óäàðèâøèé:
806 Mem
.ReadWord(gPlayers
[a
].FLastSpawnerUID
);
807 // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
808 Mem
.ReadByte(gPlayers
[a
].FLastHit
);
810 Obj_LoadState(@gPlayers
[a
].FObj
, Mem
);
811 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
812 for i
:= A_BULLETS
to A_CELLS
do
813 Mem
.ReadWord(gPlayers
[a
].FAmmo
[i
]);
814 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
815 for i
:= A_BULLETS
to A_CELLS
do
816 Mem
.ReadWord(gPlayers
[a
].FMaxAmmo
[i
]);
818 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
819 Mem
.ReadBoolean(gPlayers
[a
].FWeapon
[i
]);
820 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
821 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
822 Mem
.ReadWord(gPlayers
[a
].FReloading
[i
]);
826 Include(gPlayers
[a
].FRulez
, R_ITEM_BACKPACK
);
827 // Íàëè÷èå êðàñíîãî êëþ÷à:
830 Include(gPlayers
[a
].FRulez
, R_KEY_RED
);
831 // Íàëè÷èå çåëåíîãî êëþ÷à:
834 Include(gPlayers
[a
].FRulez
, R_KEY_GREEN
);
835 // Íàëè÷èå ñèíåãî êëþ÷à:
838 Include(gPlayers
[a
].FRulez
, R_KEY_BLUE
);
842 Include(gPlayers
[a
].FRulez
, R_BERSERK
);
843 // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
844 for i
:= MR_SUIT
to MR_MAX
do
845 Mem
.ReadDWORD(gPlayers
[a
].FMegaRulez
[i
]);
846 // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
847 for i
:= T_RESPAWN
to T_FLAGCAP
do
848 Mem
.ReadDWORD(gPlayers
[a
].FTime
[i
]);
851 Mem
.ReadString(gPlayers
[a
].FActualModelName
);
853 Mem
.ReadByte(gPlayers
[a
].FColor
.R
);
854 Mem
.ReadByte(gPlayers
[a
].FColor
.G
);
855 Mem
.ReadByte(gPlayers
[a
].FColor
.B
);
856 // Îáíîâëÿåì ìîäåëü èãðîêà:
857 gPlayers
[a
].SetModel(gPlayers
[a
].FActualModelName
);
859 // Íåò ìîäåëè - ñîçäàíèå íå âîçìîæíî:
860 if gPlayers
[a
].FModel
= nil then
864 g_FatalError(Format(_lc
[I_GAME_ERROR_MODEL
], [gPlayers
[a
].FActualModelName
]));
868 // Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû:
869 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
870 gPlayers
[a
].FModel
.Color
:= TEAMCOLOR
[gPlayers
[a
].FTeam
]
872 gPlayers
[a
].FModel
.Color
:= gPlayers
[a
].FColor
;
874 Result
:= gPlayers
[a
].FUID
;
877 procedure g_Player_ResetTeams();
881 if g_Game_IsClient
then
883 if gPlayers
= nil then
885 for a
:= Low(gPlayers
) to High(gPlayers
) do
886 if gPlayers
[a
] <> nil then
887 case gGameSettings
.GameMode
of
889 gPlayers
[a
].ChangeTeam(TEAM_NONE
);
891 if not (gPlayers
[a
].Team
in [TEAM_RED
, TEAM_BLUE
]) then
892 if gPlayers
[a
].FPreferredTeam
in [TEAM_RED
, TEAM_BLUE
] then
893 gPlayers
[a
].ChangeTeam(gPlayers
[a
].FPreferredTeam
)
896 gPlayers
[a
].ChangeTeam(TEAM_RED
)
898 gPlayers
[a
].ChangeTeam(TEAM_BLUE
);
901 gPlayers
[a
].ChangeTeam(TEAM_COOP
);
905 procedure g_Bot_Add(Team
, Difficult
: Byte);
908 _name
, _model
: String;
911 if not g_Game_IsServer
then Exit
;
913 // Ñïèñîê íàçâàíèé ìîäåëåé:
914 m
:= g_PlayerModel_GetNames();
919 if (gGameSettings
.GameType
= GT_SINGLE
) or (gGameSettings
.GameMode
= GM_COOP
) then
920 Team
:= TEAM_COOP
// COOP
922 if gGameSettings
.GameMode
= GM_DM
then
923 Team
:= TEAM_NONE
// DM
925 if Team
= TEAM_NONE
then // CTF / TDM
927 // Àâòîáàëàíñ êîìàíä:
931 for a
:= 0 to High(gPlayers
) do
932 if gPlayers
[a
] <> nil then
934 if gPlayers
[a
].Team
= TEAM_RED
then
937 if gPlayers
[a
].Team
= TEAM_BLUE
then
947 if Random(2) = 0 then
953 // Âûáèðàåì áîòó èìÿ:
955 if BotNames
<> nil then
956 for a
:= 0 to High(BotNames
) do
957 if g_Player_ValidName(BotNames
[a
]) then
959 _name
:= BotNames
[a
];
963 // Èìåíè íåò, çàäàåì ñëó÷àéíîå:
966 _name
:= Format('DFBOT%.2d', [Random(100)]);
967 until g_Player_ValidName(_name
);
969 // Âûáèðàåì ñëó÷àéíóþ ìîäåëü:
970 _model
:= m
[Random(Length(m
))];
973 with g_Player_Get(g_Player_Create(_model
,
974 _RGB(Min(Random(9)*32, 255),
975 Min(Random(9)*32, 255),
976 Min(Random(9)*32, 255)),
977 Team
, True)) as TBot
do
982 1: FDifficult
:= DIFFICULT_EASY
;
983 2: FDifficult
:= DIFFICULT_MEDIUM
;
984 else FDifficult
:= DIFFICULT_HARD
;
987 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
989 FDifficult
.WeaponPrior
[a
] := WEAPON_PRIOR1
[a
];
990 FDifficult
.CloseWeaponPrior
[a
] := WEAPON_PRIOR2
[a
];
991 //FDifficult.SafeWeaponPrior[a] := WEAPON_PRIOR3[a];
994 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [Name
]), True);
996 if g_Game_IsNet
then MH_SEND_PlayerCreate(UID
);
997 if g_Game_IsServer
and (gGameSettings
.MaxLives
> 0) then
1002 procedure g_Bot_AddList(Team
: Byte; lName
: ShortString; num
: Integer = -1);
1005 _name
, _model
: String;
1008 if not g_Game_IsServer
then Exit
;
1010 // Ñïèñîê íàçâàíèé ìîäåëåé:
1011 m
:= g_PlayerModel_GetNames();
1016 if (gGameSettings
.GameType
= GT_SINGLE
) or (gGameSettings
.GameMode
= GM_COOP
) then
1017 Team
:= TEAM_COOP
// COOP
1019 if gGameSettings
.GameMode
= GM_DM
then
1020 Team
:= TEAM_NONE
// DM
1022 if Team
= TEAM_NONE
then
1023 Team
:= BotList
[num
].team
; // CTF / TDM
1025 // Âûáèðàåì íàñòðîéêè áîòà èç ñïèñêà ïî íîìåðó èëè èìåíè:
1026 lName
:= AnsiLowerCase(lName
);
1027 if (num
< 0) or (num
> Length(BotList
)-1) then
1029 if (num
= -1) and (lName
<> '') and (BotList
<> nil) then
1030 for a
:= 0 to High(BotList
) do
1031 if AnsiLowerCase(BotList
[a
].name
) = lName
then
1040 _name
:= BotList
[num
].name
;
1041 // Çàíÿòî - âûáèðàåì ñëó÷àéíîå:
1042 if not g_Player_ValidName(_name
) then
1044 _name
:= Format('DFBOT%.2d', [Random(100)]);
1045 until g_Player_ValidName(_name
);
1048 _model
:= BotList
[num
].model
;
1049 // Íåò òàêîé - âûáèðàåì ñëó÷àéíóþ:
1050 if not InSArray(_model
, m
) then
1051 _model
:= m
[Random(Length(m
))];
1054 with g_Player_Get(g_Player_Create(_model
, BotList
[num
].color
, Team
, True)) as TBot
do
1058 FDifficult
.DiagFire
:= BotList
[num
].diag_fire
;
1059 FDifficult
.InvisFire
:= BotList
[num
].invis_fire
;
1060 FDifficult
.DiagPrecision
:= BotList
[num
].diag_precision
;
1061 FDifficult
.FlyPrecision
:= BotList
[num
].fly_precision
;
1062 FDifficult
.Cover
:= BotList
[num
].cover
;
1063 FDifficult
.CloseJump
:= BotList
[num
].close_jump
;
1065 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
1067 FDifficult
.WeaponPrior
[a
] := BotList
[num
].w_prior1
[a
];
1068 FDifficult
.CloseWeaponPrior
[a
] := BotList
[num
].w_prior2
[a
];
1069 //FDifficult.SafeWeaponPrior[a] := BotList[num].w_prior3[a];
1072 g_Console_Add(Format(_lc
[I_PLAYER_JOIN
], [Name
]), True);
1074 if g_Game_IsNet
then MH_SEND_PlayerCreate(UID
);
1078 procedure g_Bot_RemoveAll();
1082 if not g_Game_IsServer
then Exit
;
1083 if gPlayers
= nil then Exit
;
1085 for a
:= 0 to High(gPlayers
) do
1086 if gPlayers
[a
] <> nil then
1087 if gPlayers
[a
] is TBot
then
1089 gPlayers
[a
].Lives
:= 0;
1090 gPlayers
[a
].Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
1091 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [gPlayers
[a
].Name
]), True);
1092 g_Player_Remove(gPlayers
[a
].FUID
);
1098 procedure g_Bot_MixNames();
1103 if BotNames
<> nil then
1104 for a
:= 0 to High(BotNames
) do
1106 b
:= Random(Length(BotNames
));
1108 Botnames
[a
] := BotNames
[b
];
1113 procedure g_Player_Remove(UID
: Word);
1117 if gPlayers
= nil then Exit
;
1119 if g_Game_IsServer
and g_Game_IsNet
then
1120 MH_SEND_PlayerDelete(UID
);
1122 for i
:= 0 to High(gPlayers
) do
1123 if gPlayers
[i
] <> nil then
1124 if gPlayers
[i
].FUID
= UID
then
1126 if gPlayers
[i
] is TPlayer
then
1127 TPlayer(gPlayers
[i
]).Free()
1129 TBot(gPlayers
[i
]).Free();
1135 procedure g_Player_Init();
1145 if not FileExists(DataDir
+ BOTNAMES_FILENAME
) then
1148 // ×èòàåì âîçìîæíûå èìåíà áîòîâ èç ôàéëà:
1149 AssignFile(F
, DataDir
+ BOTNAMES_FILENAME
);
1160 SetLength(BotNames
, Length(BotNames
)+1);
1161 BotNames
[High(BotNames
)] := s
;
1169 // ×èòàåì ôàéë ñ ïàðàìåòðàìè áîòîâ:
1170 config
:= TConfig
.CreateFile(DataDir
+ BOTLIST_FILENAME
);
1174 while config
.SectionExists(IntToStr(a
)) do
1176 SetLength(BotList
, Length(BotList
)+1);
1178 with BotList
[High(BotList
)] do
1181 name
:= config
.ReadStr(IntToStr(a
), 'name', '');
1183 model
:= config
.ReadStr(IntToStr(a
), 'model', '');
1185 if config
.ReadStr(IntToStr(a
), 'team', 'red') = 'red' then
1190 sa
:= parse(config
.ReadStr(IntToStr(a
), 'color', ''));
1191 color
.R
:= StrToIntDef(sa
[0], 0);
1192 color
.G
:= StrToIntDef(sa
[1], 0);
1193 color
.B
:= StrToIntDef(sa
[2], 0);
1194 // Âåðîÿòíîñòü ñòðåëüáû ïîä óãëîì:
1195 diag_fire
:= config
.ReadInt(IntToStr(a
), 'diag_fire', 0);
1196 // Âåðîÿòíîñòü îòâåòíîãî îãíÿ ïî íåâèäèìîìó ñîïåðíèêó:
1197 invis_fire
:= config
.ReadInt(IntToStr(a
), 'invis_fire', 0);
1198 // Òî÷íîñòü ñòðåëüáû ïîä óãëîì:
1199 diag_precision
:= config
.ReadInt(IntToStr(a
), 'diag_precision', 0);
1200 // Òî÷íîñòü ñòðåëüáû â ïîëåòå:
1201 fly_precision
:= config
.ReadInt(IntToStr(a
), 'fly_precision', 0);
1202 // Òî÷íîñòü óêëîíåíèÿ îò ñíàðÿäîâ:
1203 cover
:= config
.ReadInt(IntToStr(a
), 'cover', 0);
1204 // Âåðîÿòíîñòü ïðûæêà ïðè ïðèáëèæåíèè ñîïåðíèêà:
1205 close_jump
:= config
.ReadInt(IntToStr(a
), 'close_jump', 0);
1206 // Ïðèîðèòåòû îðóæèÿ äëÿ äàëüíåãî áîÿ:
1207 sa
:= parse(config
.ReadStr(IntToStr(a
), 'w_prior1', ''));
1208 if Length(sa
) = 10 then
1210 w_prior1
[b
] := EnsureRange(StrToInt(sa
[b
]), 0, 9);
1211 // Ïðèîðèòåòû îðóæèÿ äëÿ áëèæíåãî áîÿ:
1212 sa
:= parse(config
.ReadStr(IntToStr(a
), 'w_prior2', ''));
1213 if Length(sa
) = 10 then
1215 w_prior2
[b
] := EnsureRange(StrToInt(sa
[b
]), 0, 9);
1217 {sa := parse(config.ReadStr(IntToStr(a), 'w_prior3', ''));
1218 if Length(sa) = 10 then
1220 w_prior3[b] := EnsureRange(StrToInt(sa[b]), 0, 9);}
1229 procedure g_Player_Free();
1233 if gPlayers
<> nil then
1235 for i
:= 0 to High(gPlayers
) do
1236 if gPlayers
[i
] <> nil then
1238 if gPlayers
[i
] is TPlayer
then
1239 TPlayer(gPlayers
[i
]).Free()
1241 TBot(gPlayers
[i
]).Free();
1252 procedure g_Player_UpdateAll();
1256 if gPlayers
= nil then Exit
;
1258 for i
:= 0 to High(gPlayers
) do
1259 if gPlayers
[i
] <> nil then
1260 if gPlayers
[i
] is TPlayer
then gPlayers
[i
].Update()
1261 else TBot(gPlayers
[i
]).Update();
1264 procedure g_Player_DrawAll();
1268 if gPlayers
= nil then Exit
;
1270 for i
:= 0 to High(gPlayers
) do
1271 if gPlayers
[i
] <> nil then
1272 if gPlayers
[i
] is TPlayer
then gPlayers
[i
].Draw()
1273 else TBot(gPlayers
[i
]).Draw();
1276 procedure g_Player_DrawDebug(p
: TPlayer
);
1280 if p
= nil then Exit
;
1281 if (@p
.FObj
) = nil then Exit
;
1283 e_TextureFontGetSize(gStdFont
, fW
, fH
);
1285 e_TextureFontPrint(0, 0 , 'Pos X: ' + IntToStr(p
.FObj
.X
), gStdFont
);
1286 e_TextureFontPrint(0, fH
, 'Pos Y: ' + IntToStr(p
.FObj
.Y
), gStdFont
);
1287 e_TextureFontPrint(0, fH
* 2, 'Vel X: ' + IntToStr(p
.FObj
.Vel
.X
), gStdFont
);
1288 e_TextureFontPrint(0, fH
* 3, 'Vel Y: ' + IntToStr(p
.FObj
.Vel
.Y
), gStdFont
);
1289 e_TextureFontPrint(0, fH
* 4, 'Acc X: ' + IntToStr(p
.FObj
.Accel
.X
), gStdFont
);
1290 e_TextureFontPrint(0, fH
* 5, 'Acc Y: ' + IntToStr(p
.FObj
.Accel
.Y
), gStdFont
);
1293 procedure g_Player_DrawHealth();
1298 if gPlayers
= nil then Exit
;
1299 e_TextureFontGetSize(gStdFont
, fW
, fH
);
1301 for i
:= 0 to High(gPlayers
) do
1302 if gPlayers
[i
] <> nil then
1304 e_TextureFontPrint(gPlayers
[i
].FObj
.X
+ gPlayers
[i
].FObj
.Rect
.X
,
1305 gPlayers
[i
].FObj
.Y
+ gPlayers
[i
].FObj
.Rect
.Y
+ gPlayers
[i
].FObj
.Rect
.Height
- fH
* 2,
1306 IntToStr(gPlayers
[i
].FHealth
), gStdFont
);
1307 e_TextureFontPrint(gPlayers
[i
].FObj
.X
+ gPlayers
[i
].FObj
.Rect
.X
,
1308 gPlayers
[i
].FObj
.Y
+ gPlayers
[i
].FObj
.Rect
.Y
+ gPlayers
[i
].FObj
.Rect
.Height
- fH
,
1309 IntToStr(gPlayers
[i
].FArmor
), gStdFont
);
1313 function g_Player_Get(UID
: Word): TPlayer
;
1319 if gPlayers
= nil then
1322 for a
:= 0 to High(gPlayers
) do
1323 if gPlayers
[a
] <> nil then
1324 if gPlayers
[a
].FUID
= UID
then
1326 Result
:= gPlayers
[a
];
1331 function g_Player_GetCount(): Byte;
1337 if gPlayers
= nil then
1340 for a
:= 0 to High(gPlayers
) do
1341 if gPlayers
[a
] <> nil then
1342 Result
:= Result
+ 1;
1345 function g_Player_GetStats(): TPlayerStatArray
;
1351 if gPlayers
= nil then Exit
;
1353 for a
:= 0 to High(gPlayers
) do
1354 if gPlayers
[a
] <> nil then
1356 SetLength(Result
, Length(Result
)+1);
1357 with Result
[High(Result
)] do
1359 Ping
:= gPlayers
[a
].FPing
;
1360 Loss
:= gPlayers
[a
].FLoss
;
1361 Name
:= gPlayers
[a
].FName
;
1362 Team
:= gPlayers
[a
].FTeam
;
1363 Frags
:= gPlayers
[a
].FFrags
;
1364 Deaths
:= gPlayers
[a
].FDeath
;
1365 Kills
:= gPlayers
[a
].FKills
;
1366 Color
:= gPlayers
[a
].FModel
.Color
;
1367 Lives
:= gPlayers
[a
].FLives
;
1368 Spectator
:= gPlayers
[a
].FSpectator
;
1373 procedure g_Player_RememberAll
;
1377 for i
:= Low(gPlayers
) to High(gPlayers
) do
1378 if (gPlayers
[i
] <> nil) and gPlayers
[i
].Live
then
1379 gPlayers
[i
].RememberState
;
1382 procedure g_Player_ResetAll(Force
, Silent
: Boolean);
1386 gTeamStat
[TEAM_RED
].Goals
:= 0;
1387 gTeamStat
[TEAM_BLUE
].Goals
:= 0;
1389 if gPlayers
<> nil then
1390 for i
:= 0 to High(gPlayers
) do
1391 if gPlayers
[i
] <> nil then
1393 gPlayers
[i
].Reset(Force
);
1395 if gPlayers
[i
] is TPlayer
then
1397 if (not gPlayers
[i
].FSpectator
) or gPlayers
[i
].FWantsInGame
then
1398 gPlayers
[i
].Respawn(Silent
)
1400 gPlayers
[i
].Spectate();
1403 TBot(gPlayers
[i
]).Respawn(Silent
);
1407 procedure g_Player_CreateCorpse(Player
: TPlayer
);
1414 if Player
.FObj
.Y
>= gMapInfo
.Height
+128 then
1419 if (FHealth
>= -50) or (gGibsCount
= 0) then
1421 if (gCorpses
= nil) or (Length(gCorpses
) = 0) then
1425 for find_id
:= 0 to High(gCorpses
) do
1426 if gCorpses
[find_id
] = nil then
1433 find_id
:= Random(Length(gCorpses
));
1435 gCorpses
[find_id
] := TCorpse
.Create(FObj
.X
, FObj
.Y
, FModel
.Name
, FHealth
< -20);
1436 gCorpses
[find_id
].FColor
:= FModel
.Color
;
1437 gCorpses
[find_id
].FObj
.Vel
:= FObj
.Vel
;
1438 gCorpses
[find_id
].FObj
.Accel
:= FObj
.Accel
;
1441 g_Player_CreateGibs(FObj
.X
+ PLAYER_RECT_CX
,
1442 FObj
.Y
+ PLAYER_RECT_CY
,
1443 FModel
.Name
, FModel
.Color
);
1447 procedure g_Player_CreateShell(fX
, fY
, dX
, dY
: Integer; T
: Byte);
1451 if (gShells
= nil) or (Length(gShells
) = 0) then
1454 with gShells
[CurrentShell
] do
1460 if T
= SHELL_BULLET
then
1462 if g_Texture_Get('TEXTURE_SHELL_BULLET', SID
) then
1466 Obj
.Rect
.Width
:= 4;
1467 Obj
.Rect
.Height
:= 2;
1471 if g_Texture_Get('TEXTURE_SHELL_SHELL', SID
) then
1475 Obj
.Rect
.Width
:= 7;
1476 Obj
.Rect
.Height
:= 3;
1482 g_Obj_Push(@Obj
, dX
+ Random(4)-Random(4), dY
-Random(4));
1483 RAngle
:= Random(360);
1484 Timeout
:= gTime
+ SHELL_TIMEOUT
;
1486 if CurrentShell
>= High(gShells
) then
1493 procedure g_Player_CreateGibs(fX
, fY
: Integer; ModelName
: string; fColor
: TRGB
);
1496 GibsArray
: TGibsArray
;
1498 if (gGibs
= nil) or (Length(gGibs
) = 0) then
1500 if not g_PlayerModel_GetGibs(ModelName
, GibsArray
) then
1503 for a
:= 0 to High(GibsArray
) do
1504 with gGibs
[CurrentGib
] do
1507 ID
:= GibsArray
[a
].ID
;
1508 MaskID
:= GibsArray
[a
].MaskID
;
1511 Obj
.Rect
:= GibsArray
[a
].Rect
;
1512 Obj
.X
:= fX
-GibsArray
[a
].Rect
.X
-(GibsArray
[a
].Rect
.Width
div 2);
1513 Obj
.Y
:= fY
-GibsArray
[a
].Rect
.Y
-(GibsArray
[a
].Rect
.Height
div 2);
1514 g_Obj_PushA(@Obj
, 25 + Random(10), Random(361));
1515 RAngle
:= Random(360);
1517 if gBloodCount
> 0 then
1518 g_GFX_Blood(fX
, fY
, 16*gBloodCount
+Random(5*gBloodCount
), -16+Random(33), -16+Random(33),
1519 Random(48), Random(48), 150, 0, 0);
1521 if CurrentGib
>= High(gGibs
) then
1528 procedure g_Player_UpdatePhysicalObjects();
1534 procedure ShellSound_Bounce(X
, Y
: Integer; T
: Byte);
1539 if T
= SHELL_BULLET
then
1540 g_Sound_PlayExAt('SOUND_PLAYER_CASING' + IntToStr(k
), X
, Y
)
1542 g_Sound_PlayExAt('SOUND_PLAYER_SHELL' + IntToStr(k
), X
, Y
);
1547 if gGibs
<> nil then
1548 for i
:= 0 to High(gGibs
) do
1549 if gGibs
[i
].Live
then
1553 mr
:= g_Obj_Move(@Obj
, True, False, True);
1555 if WordBool(mr
and MOVE_FALLOUT
) then
1561 // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë:
1562 if WordBool(mr
and MOVE_HITWALL
) then
1563 Obj
.Vel
.X
:= -(vel
.X
div 2);
1564 if WordBool(mr
and (MOVE_HITCEIL
or MOVE_HITLAND
)) then
1565 Obj
.Vel
.Y
:= -(vel
.Y
div 2);
1567 if (Obj
.Vel
.X
>= 0) then
1569 RAngle
:= RAngle
+ Abs(Obj
.Vel
.X
)*6 + Abs(Obj
.Vel
.Y
);
1570 if RAngle
>= 360 then
1571 RAngle
:= RAngle
mod 360;
1572 end else begin // Counter-clockwise
1573 RAngle
:= RAngle
- Abs(Obj
.Vel
.X
)*6 - Abs(Obj
.Vel
.Y
);
1575 RAngle
:= (360 - (Abs(RAngle
) mod 360)) mod 360;
1578 // Ñîïðîòèâëåíèå âîçäóõà äëÿ êóñêà òðóïà:
1579 if gTime
mod (GAME_TICK
*3) = 0 then
1580 Obj
.Vel
.X
:= z_dec(Obj
.Vel
.X
, 1);
1584 if gCorpses
<> nil then
1585 for i
:= 0 to High(gCorpses
) do
1586 if gCorpses
[i
] <> nil then
1587 if gCorpses
[i
].State
= CORPSE_STATE_REMOVEME
then
1593 gCorpses
[i
].Update();
1596 if gShells
<> nil then
1597 for i
:= 0 to High(gShells
) do
1598 if gShells
[i
].Live
then
1602 mr
:= g_Obj_Move(@Obj
, True, False, True);
1604 if WordBool(mr
and MOVE_FALLOUT
) or (gShells
[i
].Timeout
< gTime
) then
1610 // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë:
1611 if WordBool(mr
and MOVE_HITWALL
) then
1613 Obj
.Vel
.X
:= -(vel
.X
div 2);
1614 if not WordBool(mr
and MOVE_INWATER
) then
1615 ShellSound_Bounce(Obj
.X
, Obj
.Y
, SType
);
1617 if WordBool(mr
and (MOVE_HITCEIL
or MOVE_HITLAND
)) then
1619 Obj
.Vel
.Y
:= -(vel
.Y
div 2);
1620 if Obj
.Vel
.X
<> 0 then Obj
.Vel
.X
:= Obj
.Vel
.X
div 2;
1621 if (Obj
.Vel
.X
= 0) and (Obj
.Vel
.Y
= 0) then
1623 if RAngle
mod 90 <> 0 then
1624 RAngle
:= (RAngle
div 90) * 90;
1626 else if not WordBool(mr
and MOVE_INWATER
) then
1627 ShellSound_Bounce(Obj
.X
, Obj
.Y
, SType
);
1630 if (Obj
.Vel
.X
>= 0) then
1632 RAngle
:= RAngle
+ Abs(Obj
.Vel
.X
)*8 + Abs(Obj
.Vel
.Y
);
1633 if RAngle
>= 360 then
1634 RAngle
:= RAngle
mod 360;
1635 end else begin // Counter-clockwise
1636 RAngle
:= RAngle
- Abs(Obj
.Vel
.X
)*8 - Abs(Obj
.Vel
.Y
);
1638 RAngle
:= (360 - (Abs(RAngle
) mod 360)) mod 360;
1643 procedure g_Player_DrawCorpses();
1648 if gGibs
<> nil then
1649 for i
:= 0 to High(gGibs
) do
1650 if gGibs
[i
].Live
then
1653 if not g_Obj_Collide(sX
, sY
, sWidth
, sHeight
, @Obj
) then
1656 a
.X
:= Obj
.Rect
.X
+(Obj
.Rect
.Width
div 2);
1657 a
.y
:= Obj
.Rect
.Y
+(Obj
.Rect
.Height
div 2);
1659 e_DrawAdv(ID
, Obj
.X
, Obj
.Y
, 0, True, False, RAngle
, @a
, M_NONE
);
1662 e_DrawAdv(MaskID
, Obj
.X
, Obj
.Y
, 0, True, False, RAngle
, @a
, M_NONE
);
1668 if gCorpses
<> nil then
1669 for i
:= 0 to High(gCorpses
) do
1670 if gCorpses
[i
] <> nil then
1674 procedure g_Player_DrawShells();
1679 if gShells
<> nil then
1680 for i
:= 0 to High(gShells
) do
1681 if gShells
[i
].Live
then
1684 if not g_Obj_Collide(sX
, sY
, sWidth
, sHeight
, @Obj
) then
1690 e_DrawAdv(SpriteID
, Obj
.X
, Obj
.Y
, 0, True, False, RAngle
, @a
, M_NONE
);
1694 procedure g_Player_RemoveAllCorpses();
1700 SetLength(gGibs
, MaxGibs
);
1701 SetLength(gShells
, MaxGibs
);
1705 if gCorpses
<> nil then
1706 for i
:= 0 to High(gCorpses
) do
1710 SetLength(gCorpses
, MaxCorpses
);
1713 procedure g_Player_Corpses_SaveState(var Mem
: TBinMemoryWriter
);
1718 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðóïîâ:
1720 if gCorpses
<> nil then
1721 for i
:= 0 to High(gCorpses
) do
1722 if gCorpses
[i
] <> nil then
1725 Mem
:= TBinMemoryWriter
.Create((count
+1) * 128);
1727 // Êîëè÷åñòâî òðóïîâ:
1728 Mem
.WriteInt(count
);
1734 for i
:= 0 to High(gCorpses
) do
1735 if gCorpses
[i
] <> nil then
1738 Mem
.WriteString(gCorpses
[i
].FModelName
);
1740 b
:= gCorpses
[i
].Mess
;
1741 Mem
.WriteBoolean(b
);
1742 // Ñîõðàíÿåì äàííûå òðóïà:
1743 gCorpses
[i
].SaveState(Mem
);
1747 procedure g_Player_Corpses_LoadState(var Mem
: TBinMemoryReader
);
1756 g_Player_RemoveAllCorpses();
1758 // Êîëè÷åñòâî òðóïîâ:
1761 if count
> Length(gCorpses
) then
1763 raise EBinSizeError
.Create('g_Player_Corpses_LoadState: Too Many Corpses');
1770 for i
:= 0 to count
-1 do
1773 Mem
.ReadString(str
);
1777 gCorpses
[i
] := TCorpse
.Create(0, 0, str
, b
);
1778 // Çàãðóæàåì äàííûå òðóïà:
1779 gCorpses
[i
].LoadState(Mem
);
1785 procedure TPlayer
.BFGHit();
1787 g_Weapon_BFGHit(FObj
.X
+FObj
.Rect
.X
+(FObj
.Rect
.Width
div 2),
1788 FObj
.Y
+FObj
.Rect
.Y
+(FObj
.Rect
.Height
div 2));
1789 if g_Game_IsServer
and g_Game_IsNet
then
1790 MH_SEND_Effect(FObj
.X
+FObj
.Rect
.X
+(FObj
.Rect
.Width
div 2),
1791 FObj
.Y
+FObj
.Rect
.Y
+(FObj
.Rect
.Height
div 2),
1795 procedure TPlayer
.ChangeModel(ModelName
: string);
1797 Model
: TPlayerModel
;
1799 Model
:= g_PlayerModel_Get(ModelName
);
1800 if Model
= nil then Exit
;
1806 procedure TPlayer
.SetModel(ModelName
: string);
1810 m
:= g_PlayerModel_Get(ModelName
);
1813 g_SimpleError(Format(_lc
[I_GAME_ERROR_MODEL_FALLBACK
], [ModelName
]));
1814 m
:= g_PlayerModel_Get('doomer');
1817 g_FatalError(Format(_lc
[I_GAME_ERROR_MODEL
], ['doomer']));
1822 if FModel
<> nil then
1827 if not (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
1828 FModel
.Color
:= FColor
1830 FModel
.Color
:= TEAMCOLOR
[FTeam
];
1831 FModel
.SetWeapon(FCurrWeap
);
1832 FModel
.SetFlag(FFlag
);
1833 SetDirection(FDirection
);
1836 procedure TPlayer
.SetColor(Color
: TRGB
);
1839 if not (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
1840 if FModel
<> nil then FModel
.Color
:= Color
;
1843 procedure TPlayer
.SwitchTeam
;
1845 if g_Game_IsClient
then
1847 if not (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then Exit
;
1849 if gGameOn
and FLive
then
1850 Kill(K_SIMPLEKILL
, FUID
, HIT_SELF
);
1852 if FTeam
= TEAM_RED
then
1854 ChangeTeam(TEAM_BLUE
);
1855 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_BLUE
], [FName
]), True);
1856 if g_Game_IsNet
then
1857 MH_SEND_GameEvent(NET_EV_CHANGE_TEAM
, TEAM_BLUE
, FName
);
1861 ChangeTeam(TEAM_RED
);
1862 g_Console_Add(Format(_lc
[I_PLAYER_CHTEAM_RED
], [FName
]), True);
1863 if g_Game_IsNet
then
1864 MH_SEND_GameEvent(NET_EV_CHANGE_TEAM
, TEAM_RED
, FName
);
1866 FPreferredTeam
:= FTeam
;
1869 procedure TPlayer
.ChangeTeam(Team
: Byte);
1876 TEAM_RED
, TEAM_BLUE
:
1877 FModel
.Color
:= TEAMCOLOR
[Team
];
1879 FModel
.Color
:= FColor
;
1881 if (FTeam
<> OldTeam
) and g_Game_IsNet
and g_Game_IsServer
then
1882 MH_SEND_PlayerStats(FUID
);
1886 procedure TPlayer.CollideItem();
1891 if gItems = nil then Exit;
1892 if not FLive then Exit;
1894 for i := 0 to High(gItems) do
1897 if (ItemType <> ITEM_NONE) and Live then
1898 if g_Obj_Collide(FObj.X+PLAYER_RECT.X, FObj.Y+PLAYER_RECT.Y, PLAYER_RECT.Width,
1899 PLAYER_RECT.Height, @Obj) then
1901 if not PickItem(ItemType, gItems[i].Respawnable, r) then Continue;
1903 if ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL] then
1904 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ', FObj.X, FObj.Y)
1905 else if ItemType in [ITEM_MEDKIT_SMALL, ITEM_MEDKIT_LARGE, ITEM_MEDKIT_BLACK] then
1906 g_Sound_PlayExAt('SOUND_ITEM_GETMED', FObj.X, FObj.Y)
1907 else g_Sound_PlayExAt('SOUND_ITEM_GETITEM', FObj.X, FObj.Y);
1909 // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòñÿ ñ äðóãèì èãðîêîì:
1910 if r and not ((ItemType in [ITEM_KEY_RED, ITEM_KEY_GREEN, ITEM_KEY_BLUE]) and
1911 (gGameSettings.GameType = GT_SINGLE) and
1912 (g_Player_GetCount() > 1)) then
1913 if not Respawnable then g_Items_Remove(i) else g_Items_Pick(i);
1919 function TPlayer
.CollideLevel(XInc
, YInc
: Integer): Boolean;
1921 Result
:= g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
,
1922 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
, PANEL_WALL
,
1926 constructor TPlayer
.Create();
1932 FSawSound
:= TPlayableSound
.Create();
1933 FSawSoundIdle
:= TPlayableSound
.Create();
1934 FSawSoundHit
:= TPlayableSound
.Create();
1935 FSawSoundSelect
:= TPlayableSound
.Create();
1936 FJetSoundFly
:= TPlayableSound
.Create();
1937 FJetSoundOn
:= TPlayableSound
.Create();
1938 FJetSoundOff
:= TPlayableSound
.Create();
1940 FSawSound
.SetByName('SOUND_WEAPON_FIRESAW');
1941 FSawSoundIdle
.SetByName('SOUND_WEAPON_IDLESAW');
1942 FSawSoundHit
.SetByName('SOUND_WEAPON_HITSAW');
1943 FSawSoundSelect
.SetByName('SOUND_WEAPON_SELECTSAW');
1944 FJetSoundFly
.SetByName('SOUND_PLAYER_JETFLY');
1945 FJetSoundOn
.SetByName('SOUND_PLAYER_JETON');
1946 FJetSoundOff
.SetByName('SOUND_PLAYER_JETOFF');
1948 FSpectatePlayer
:= -1;
1952 FSavedState
.WaitRecall
:= False;
1955 FActualModelName
:= 'doomer';
1958 FObj
.Rect
:= PLAYER_RECT
;
1960 FBFGFireCounter
:= -1;
1961 FJustTeleported
:= False;
1965 procedure TPlayer
.Damage(value
: Word; SpawnerUID
: Word; vx
, vy
: Integer; t
: Byte);
1969 if (not g_Game_IsClient
) and (not FLive
) then
1974 // Íåóÿçâèìîñòü íå ñïàñàåò îò ëîâóøåê:
1975 if ((t
= HIT_TRAP
) or (t
= HIT_SELF
)) and (not FGodMode
) then
1977 if not g_Game_IsClient
then
1980 if t
= HIT_TRAP
then
1982 // Ëîâóøêà óáèâàåò ñðàçó:
1984 Kill(K_EXTRAHARDKILL
, SpawnerUID
, t
);
1986 if t
= HIT_SELF
then
1990 Kill(K_SIMPLEKILL
, SpawnerUID
, t
);
1993 // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë
1994 FMegaRulez
[MR_SUIT
] := 0;
1995 FMegaRulez
[MR_INVUL
] := 0;
1996 FMegaRulez
[MR_INVIS
] := 0;
2000 // Íî îò îñòàëüíîãî ñïàñàåò:
2001 if FMegaRulez
[MR_INVUL
] >= gTime
then
2008 // Åñëè åñòü óðîí ñâîèì, èëè ðàíèë ñàì ñåáÿ, èëè òåáÿ ðàíèë ïðîòèâíèê:
2009 if LongBool(gGameSettings
.Options
and GAME_OPTION_TEAMDAMAGE
) or
2010 (SpawnerUID
= FUID
) or
2011 (not SameTeam(FUID
, SpawnerUID
)) then
2013 FLastSpawnerUID
:= SpawnerUID
;
2015 // Êðîâü (ïóçûðüêè, åñëè â âîäå):
2016 if gBloodCount
> 0 then
2018 c
:= Min(value
, 200)*gBloodCount
+ Random(Min(value
, 200) div 2);
2019 if value
div 4 <= c
then
2020 c
:= c
- (value
div 4)
2024 if (t
= HIT_SOME
) and (vx
= 0) and (vy
= 0) then
2028 HIT_TRAP
, HIT_ACID
, HIT_FLAME
, HIT_SELF
: MakeBloodSimple(c
);
2029 HIT_BFG
, HIT_ROCKET
, HIT_SOME
: MakeBloodVector(c
, vx
, vy
);
2032 if t
= HIT_WATER
then
2033 g_GFX_Bubbles(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2),
2034 FObj
.Y
+PLAYER_RECT
.Y
-4, value
div 2, 8, 4);
2039 Inc(FDamageBuffer
, value
);
2043 FPain
:= FPain
+ value
;
2046 if g_Game_IsServer
and g_Game_IsNet
then
2048 MH_SEND_PlayerDamage(FUID
, t
, SpawnerUID
, value
, vx
, vy
);
2049 MH_SEND_PlayerStats(FUID
);
2050 MH_SEND_PlayerPos(False, FUID
);
2054 function TPlayer
.Heal(value
: Word; Soft
: Boolean): Boolean;
2057 if g_Game_IsClient
then
2062 if Soft
and (FHealth
< PLAYER_HP_SOFT
) then
2064 IncMax(FHealth
, value
, PLAYER_HP_SOFT
);
2067 if (not Soft
) and (FHealth
< PLAYER_HP_LIMIT
) then
2069 IncMax(FHealth
, value
, PLAYER_HP_LIMIT
);
2073 if Result
and g_Game_IsServer
and g_Game_IsNet
then
2074 MH_SEND_PlayerStats(FUID
);
2077 destructor TPlayer
.Destroy();
2079 if (gPlayer1
<> nil) and (gPlayer1
.FUID
= FUID
) then
2081 if (gPlayer2
<> nil) and (gPlayer2
.FUID
= FUID
) then
2085 FSawSoundIdle
.Free();
2086 FSawSoundHit
.Free();
2087 FJetSoundFly
.Free();
2089 FJetSoundOff
.Free();
2095 procedure TPlayer
.DrawBubble();
2097 bubX
, bubY
: Integer;
2100 Rw
, Gw
, Bw
: SmallInt;
2103 bubX
:= FObj
.X
+FObj
.Rect
.X
+ IfThen(FDirection
= D_LEFT
, -4, 18);
2104 bubY
:= FObj
.Y
+FObj
.Rect
.Y
- 18;
2112 1: // simple textual non-bubble
2114 bubX
:= FObj
.X
+FObj
.Rect
.X
- 11;
2115 bubY
:= FObj
.Y
+FObj
.Rect
.Y
- 17;
2116 e_TextureFontPrint(bubX
, bubY
, '[...]', gStdFont
);
2119 2: // advanced pixel-perfect bubble
2121 if FTeam
= TEAM_RED
then
2124 if FTeam
= TEAM_BLUE
then
2127 3: // colored bubble
2129 Rb
:= FModel
.Color
.R
;
2130 Gb
:= FModel
.Color
.G
;
2131 Bb
:= FModel
.Color
.B
;
2132 Rw
:= Min(Rb
* 2 + 64, 255);
2133 Gw
:= Min(Gb
* 2 + 64, 255);
2134 Bw
:= Min(Bb
* 2 + 64, 255);
2135 if (Abs(Rw
- Rb
) < 32)
2136 or (Abs(Gw
- Gb
) < 32)
2137 or (Abs(Bw
- Bb
) < 32) then
2139 Rb
:= Max(Rw
div 2 - 16, 0);
2140 Gb
:= Max(Gw
div 2 - 16, 0);
2141 Bb
:= Max(Bw
div 2 - 16, 0);
2144 4: // custom textured bubble
2146 if g_Texture_Get('TEXTURE_PLAYER_TALKBUBBLE', ID
) then
2147 if FDirection
= D_RIGHT
then
2148 e_Draw(ID
, bubX
- 6, bubY
- 7, 0, True, False)
2150 e_Draw(ID
, bubX
- 6, bubY
- 7, 0, True, False, M_HORIZONTAL
);
2156 e_DrawQuad(bubX
+ 1, bubY
, bubX
+ 18, bubY
+ 13, Rb
, Gb
, Bb
);
2157 e_DrawQuad(bubX
, bubY
+ 1, bubX
+ 19, bubY
+ 12, Rb
, Gb
, Bb
);
2159 e_DrawFillQuad(bubX
+ 1, bubY
+ 1, bubX
+ 18, bubY
+ 12, Rw
, Gw
, Bw
, 0);
2162 Dot
:= IfThen(FDirection
= D_LEFT
, 14, 5);
2163 e_DrawLine(1, bubX
+ Dot
, bubY
+ 14, bubX
+ Dot
, bubY
+ 16, Rb
, Gb
, Bb
);
2164 e_DrawLine(1, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 1, Dot
+ 1), bubY
+ 13, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 1, Dot
+ 1), bubY
+ 15, Rw
, Gw
, Bw
);
2165 e_DrawLine(1, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 2, Dot
+ 2), bubY
+ 13, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 2, Dot
+ 2), bubY
+ 14, Rw
, Gw
, Bw
);
2166 e_DrawLine(1, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 3, Dot
+ 3), bubY
+ 13, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 3, Dot
+ 3), bubY
+ 13, Rw
, Gw
, Bw
);
2167 e_DrawLine(1, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 3, Dot
+ 3), bubY
+ 14, bubX
+ IfThen(FDirection
= D_LEFT
, Dot
- 1, Dot
+ 1), bubY
+ 16, Rb
, Gb
, Bb
);
2171 e_DrawFillQuad(bubX
+ Dot
, bubY
+ 8, bubX
+ Dot
+ 1, bubY
+ 9, Rb
, Gb
, Bb
, 0);
2172 e_DrawFillQuad(bubX
+ Dot
+ 3, bubY
+ 8, bubX
+ Dot
+ 4, bubY
+ 9, Rb
, Gb
, Bb
, 0);
2173 e_DrawFillQuad(bubX
+ Dot
+ 6, bubY
+ 8, bubX
+ Dot
+ 7, bubY
+ 9, Rb
, Gb
, Bb
, 0);
2176 procedure TPlayer
.Draw();
2184 if (FMegaRulez
[MR_INVUL
] > gTime
) and (gPlayerDrawn
<> Self
) then
2185 if g_Texture_Get('TEXTURE_PLAYER_INVULPENTA', ID
) then
2187 e_GetTextureSize(ID
, @w
, @h
);
2188 if FDirection
= D_LEFT
then
2189 e_Draw(ID
, FObj
.X
+FObj
.Rect
.X
+(FObj
.Rect
.Width
div 2)-(w
div 2)+4,
2190 FObj
.Y
+FObj
.Rect
.Y
+(FObj
.Rect
.Height
div 2)-(h
div 2)-7, 0, True, False)
2192 e_Draw(ID
, FObj
.X
+FObj
.Rect
.X
+(FObj
.Rect
.Width
div 2)-(w
div 2)-2,
2193 FObj
.Y
+FObj
.Rect
.Y
+(FObj
.Rect
.Height
div 2)-(h
div 2)-7, 0, True, False);
2196 if FMegaRulez
[MR_INVIS
] > gTime
then
2198 if (gPlayerDrawn
<> nil) and ((Self
= gPlayerDrawn
) or
2199 ((FTeam
= gPlayerDrawn
.Team
) and (gGameSettings
.GameMode
<> GM_DM
))) then
2201 if (FMegaRulez
[MR_INVIS
] - gTime
) <= 2100 then
2202 dr
:= not Odd((FMegaRulez
[MR_INVIS
] - gTime
) div 300)
2206 FModel
.Draw(FObj
.X
, FObj
.Y
, 200)
2208 FModel
.Draw(FObj
.X
, FObj
.Y
);
2211 FModel
.Draw(FObj
.X
, FObj
.Y
, 254);
2214 FModel
.Draw(FObj
.X
, FObj
.Y
);
2217 if g_debug_Frames
then
2219 e_DrawQuad(FObj
.X
+FObj
.Rect
.X
,
2221 FObj
.X
+FObj
.Rect
.X
+FObj
.Rect
.Width
-1,
2222 FObj
.Y
+FObj
.Rect
.Y
+FObj
.Rect
.Height
-1,
2226 if (gChatBubble
> 0) and (FKeys
[KEY_CHAT
].Pressed
) and not FGhost
then
2228 // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR
2229 if gAimLine
and Live
and
2230 ((Self
= gPlayer1
) or (Self
= gPlayer2
)) then
2234 procedure TPlayer
.DrawAim();
2236 wx
, wy
, xx
, yy
: Integer;
2240 wx
:= FObj
.X
+ WEAPONPOINT
[FDirection
].X
+ IfThen(FDirection
= D_LEFT
, 7, -7);
2241 wy
:= FObj
.Y
+ WEAPONPOINT
[FDirection
].Y
;
2250 1: begin // Chainsaw
2257 if angle
= ANGLE_RIGHTUP
then Dec(angle
, 2);
2258 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2259 if angle
= ANGLE_LEFTUP
then Inc(angle
, 2);
2260 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2265 if angle
= ANGLE_RIGHTUP
then Dec(angle
, 2);
2266 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2267 if angle
= ANGLE_LEFTUP
then Inc(angle
, 2);
2268 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2270 4: begin // Double Shotgun
2273 if angle
= ANGLE_RIGHTUP
then Dec(angle
, 2);
2274 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2275 if angle
= ANGLE_LEFTUP
then Inc(angle
, 2);
2276 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2278 5: begin // Chaingun
2281 if angle
= ANGLE_RIGHTUP
then Dec(angle
, 2);
2282 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2283 if angle
= ANGLE_LEFTUP
then Inc(angle
, 2);
2284 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2286 6: begin // Rocket Launcher
2289 if angle
= ANGLE_RIGHTUP
then Inc(angle
, 2);
2290 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2291 if angle
= ANGLE_LEFTUP
then Dec(angle
, 2);
2292 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2294 7: begin // Plasmagun
2297 if angle
= ANGLE_RIGHTUP
then Inc(angle
);
2298 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 3);
2299 if angle
= ANGLE_LEFTUP
then Dec(angle
);
2300 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 3);
2305 if angle
= ANGLE_RIGHTUP
then Inc(angle
, 1);
2306 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 2);
2307 if angle
= ANGLE_LEFTUP
then Dec(angle
, 1);
2308 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 2);
2310 9: begin // Super Chaingun
2313 if angle
= ANGLE_RIGHTUP
then Dec(angle
, 2);
2314 if angle
= ANGLE_RIGHTDOWN
then Inc(angle
, 4);
2315 if angle
= ANGLE_LEFTUP
then Inc(angle
, 2);
2316 if angle
= ANGLE_LEFTDOWN
then Dec(angle
, 4);
2319 xx
:= Trunc(Cos(-DegToRad(angle
)) * len
) + wx
;
2320 yy
:= Trunc(Sin(-DegToRad(angle
)) * len
) + wy
;
2321 e_DrawLine(sz
, wx
, wy
, xx
, yy
, 255, 0, 0, 96);
2324 procedure TPlayer
.DrawGUI();
2327 X
, Y
, SY
, a
, p
, m
: Integer;
2331 stat
: TPlayerStatArray
;
2333 X
:= gPlayerScreenSize
.X
;
2334 SY
:= gPlayerScreenSize
.Y
;
2337 if gShowGoals
and (gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
]) then
2339 if gGameSettings
.GameMode
= GM_CTF
then
2343 if gGameSettings
.GameMode
= GM_CTF
then
2345 s
:= 'TEXTURE_PLAYER_REDFLAG';
2346 if gFlags
[FLAG_RED
].State
= FLAG_STATE_CAPTURED
then
2347 s
:= 'TEXTURE_PLAYER_REDFLAG_S';
2348 if gFlags
[FLAG_RED
].State
= FLAG_STATE_DROPPED
then
2349 s
:= 'TEXTURE_PLAYER_REDFLAG_D';
2350 if g_Texture_Get(s
, ID
) then
2351 e_Draw(ID
, X
-16-32, 240-72-4, 0, True, False);
2354 s
:= IntToStr(gTeamStat
[TEAM_RED
].Goals
);
2355 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2356 e_CharFont_PrintEx(gMenuFont
, X
-16-a
-tw
, 240-72-4, s
, TEAMCOLOR
[TEAM_RED
]);
2358 if gGameSettings
.GameMode
= GM_CTF
then
2360 s
:= 'TEXTURE_PLAYER_BLUEFLAG';
2361 if gFlags
[FLAG_BLUE
].State
= FLAG_STATE_CAPTURED
then
2362 s
:= 'TEXTURE_PLAYER_BLUEFLAG_S';
2363 if gFlags
[FLAG_BLUE
].State
= FLAG_STATE_DROPPED
then
2364 s
:= 'TEXTURE_PLAYER_BLUEFLAG_D';
2365 if g_Texture_Get(s
, ID
) then
2366 e_Draw(ID
, X
-16-32, 240-32-4, 0, True, False);
2369 s
:= IntToStr(gTeamStat
[TEAM_BLUE
].Goals
);
2370 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2371 e_CharFont_PrintEx(gMenuFont
, X
-16-a
-tw
, 240-32-4, s
, TEAMCOLOR
[TEAM_BLUE
]);
2374 if g_Texture_Get('TEXTURE_PLAYER_HUDBG', ID
) then
2375 e_DrawFill(ID
, X
, 0, 1, (gPlayerScreenSize
.Y
div 256)+IfThen(gPlayerScreenSize
.Y
mod 256 > 0, 1, 0),
2378 if g_Texture_Get('TEXTURE_PLAYER_HUD', ID
) then
2379 e_Draw(ID
, X
+2, Y
, 0, True, False);
2381 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
2385 s
:= IntToStr(Frags
);
2386 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2387 e_CharFont_PrintEx(gMenuFont
, X
-16-tw
, Y
, s
, _RGB(255, 0, 0));
2392 stat
:= g_Player_GetStats();
2397 for a
:= 0 to High(stat
) do
2398 if stat
[a
].Name
<> Name
then
2400 if stat
[a
].Frags
> m
then m
:= stat
[a
].Frags
;
2401 if stat
[a
].Frags
> Frags
then p
:= p
+1;
2405 s
:= IntToStr(p
)+' / '+IntToStr(Length(stat
))+' ';
2406 if Frags
>= m
then s
:= s
+'+' else s
:= s
+'-';
2407 s
:= s
+IntToStr(Abs(Frags
-m
));
2409 e_CharFont_GetSize(gMenuSmallFont
, s
, tw
, th
);
2410 e_CharFont_PrintEx(gMenuSmallFont
, X
-16-tw
, Y
+32, s
, _RGB(255, 0, 0));
2413 if gShowLives
and (gGameSettings
.MaxLives
> 0) then
2415 s
:= IntToStr(Lives
);
2416 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2417 e_CharFont_PrintEx(gMenuFont
, X
-16-tw
, SY
-32, s
, _RGB(0, 255, 0));
2421 e_CharFont_GetSize(gMenuSmallFont
, FName
, tw
, th
);
2422 e_CharFont_PrintEx(gMenuSmallFont
, X
+98-(tw
div 2), Y
+8, FName
, _RGB(255, 0, 0));
2424 if R_BERSERK
in FRulez
then
2425 e_Draw(gItemsTexturesID
[ITEM_MEDKIT_BLACK
], X
+37, Y
+45, 0, True, False)
2427 e_Draw(gItemsTexturesID
[ITEM_MEDKIT_LARGE
], X
+37, Y
+45, 0, True, False);
2429 if g_Texture_Get('TEXTURE_PLAYER_ARMORHUD', ID
) then
2430 e_Draw(ID
, X
+36, Y
+77, 0, True, False);
2432 s
:= IntToStr(IfThen(FHealth
> 0, FHealth
, 0));
2433 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2434 e_CharFont_PrintEx(gMenuFont
, X
+178-tw
, Y
+40, s
, _RGB(255, 0, 0));
2436 s
:= IntToStr(FArmor
);
2437 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2438 e_CharFont_PrintEx(gMenuFont
, X
+178-tw
, Y
+68, s
, _RGB(255, 0, 0));
2440 s
:= IntToStr(GetAmmoByWeapon(FCurrWeap
));
2446 ID
:= gItemsTexturesID
[ITEM_WEAPON_KASTET
];
2451 ID
:= gItemsTexturesID
[ITEM_WEAPON_SAW
];
2453 WEAPON_PISTOL
: ID
:= gItemsTexturesID
[ITEM_WEAPON_PISTOL
];
2454 WEAPON_CHAINGUN
: ID
:= gItemsTexturesID
[ITEM_WEAPON_CHAINGUN
];
2455 WEAPON_SHOTGUN1
: ID
:= gItemsTexturesID
[ITEM_WEAPON_SHOTGUN1
];
2456 WEAPON_SHOTGUN2
: ID
:= gItemsTexturesID
[ITEM_WEAPON_SHOTGUN2
];
2457 WEAPON_SUPERPULEMET
: ID
:= gItemsTexturesID
[ITEM_WEAPON_SUPERPULEMET
];
2458 WEAPON_ROCKETLAUNCHER
: ID
:= gItemsTexturesID
[ITEM_WEAPON_ROCKETLAUNCHER
];
2459 WEAPON_PLASMA
: ID
:= gItemsTexturesID
[ITEM_WEAPON_PLASMA
];
2460 WEAPON_BFG
: ID
:= gItemsTexturesID
[ITEM_WEAPON_BFG
];
2463 e_CharFont_GetSize(gMenuFont
, s
, tw
, th
);
2464 e_CharFont_PrintEx(gMenuFont
, X
+178-tw
, Y
+158, s
, _RGB(255, 0, 0));
2465 e_Draw(ID
, X
+20, Y
+160, 0, True, False);
2467 if R_KEY_RED
in FRulez
then
2468 e_Draw(gItemsTexturesID
[ITEM_KEY_RED
], X
+78, Y
+214, 0, True, False);
2470 if R_KEY_GREEN
in FRulez
then
2471 e_Draw(gItemsTexturesID
[ITEM_KEY_GREEN
], X
+95, Y
+214, 0, True, False);
2473 if R_KEY_BLUE
in FRulez
then
2474 e_Draw(gItemsTexturesID
[ITEM_KEY_BLUE
], X
+112, Y
+214, 0, True, False);
2476 if FJetFuel
> 0 then
2478 if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID
) then
2479 e_Draw(ID
, X
+2, Y
+116, 0, True, False);
2480 if g_Texture_Get('TEXTURE_PLAYER_HUDJET', ID
) then
2481 e_Draw(ID
, X
+2, Y
+126, 0, True, False);
2482 e_DrawLine(4, X
+16, Y
+122, X
+16+Trunc(168*IfThen(FAir
> 0, FAir
, 0)/AIR_MAX
), Y
+122, 0, 0, 196);
2483 e_DrawLine(4, X
+16, Y
+132, X
+16+Trunc(168*FJetFuel
/JET_MAX
), Y
+132, 208, 0, 0);
2487 if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID
) then
2488 e_Draw(ID
, X
+2, Y
+124, 0, True, False);
2489 e_DrawLine(4, X
+16, Y
+130, X
+16+Trunc(168*IfThen(FAir
> 0, FAir
, 0)/AIR_MAX
), Y
+130, 0, 0, 196);
2492 if gShowPing
and g_Game_IsClient
then
2494 s
:= _lc
[I_GAME_PING_HUD
] + IntToStr(NetPeer
.lastRoundTripTime
) + _lc
[I_NET_SLIST_PING_MS
];
2495 e_TextureFontPrint(X
+ 4, Y
+ 242, s
, gStdFont
);
2501 e_TextureFontPrint(X
+ 4, Y
+ 242, _lc
[I_PLAYER_SPECT
], gStdFont
);
2502 e_TextureFontPrint(X
+ 4, Y
+ 258, _lc
[I_PLAYER_SPECT2
], gStdFont
);
2503 e_TextureFontPrint(X
+ 4, Y
+ 274, _lc
[I_PLAYER_SPECT1
], gStdFont
);
2506 e_TextureFontGetSize(gStdFont
, cw
, ch
);
2507 s
:= _lc
[I_PLAYER_SPECT4
];
2508 e_TextureFontPrintEx(gScreenWidth
div 2 - cw
*(Length(s
) div 2),
2509 gScreenHeight
-4-ch
, s
, gStdFont
, 255, 255, 255, 1, True);
2510 e_TextureFontPrint(X
+ 4, Y
+ 290, _lc
[I_PLAYER_SPECT1S
], gStdFont
);
2516 procedure TPlayer
.DrawRulez();
2520 // Ïðè âçÿòèè íåóÿçâèìîñòè ðèñóåòñÿ èíâåðñèîííûé áåëûé ôîí
2521 if FMegaRulez
[MR_INVUL
] >= gTime
then
2523 if (FMegaRulez
[MR_INVUL
]-gTime
) <= 2100 then
2524 dr
:= not Odd((FMegaRulez
[MR_INVUL
]-gTime
) div 300)
2529 e_DrawFillQuad(0, 0, gPlayerScreenSize
.X
-1, gPlayerScreenSize
.Y
-1,
2530 191, 191, 191, 0, B_INVERT
);
2533 // Ïðè âçÿòèè çàùèòíîãî êîñòþìà ðèñóåòñÿ çåëåíîâàòûé ôîí
2534 if FMegaRulez
[MR_SUIT
] >= gTime
then
2536 if (FMegaRulez
[MR_SUIT
]-gTime
) <= 2100 then
2537 dr
:= not Odd((FMegaRulez
[MR_SUIT
]-gTime
) div 300)
2542 e_DrawFillQuad(0, 0, gPlayerScreenSize
.X
-1, gPlayerScreenSize
.Y
-1,
2543 0, 96, 0, 200, B_NONE
);
2546 // Ïðè âçÿòèè áåðñåðêà ðèñóåòñÿ êðàñíîâàòûé ôîí
2547 if (FBerserk
>= 0) and (LongWord(FBerserk
) >= gTime
) and (gFlash
= 2) then
2549 e_DrawFillQuad(0, 0, gPlayerScreenSize
.X
-1, gPlayerScreenSize
.Y
-1,
2550 255, 0, 0, 200, B_NONE
);
2554 procedure TPlayer
.DrawPain();
2558 if FPain
= 0 then Exit
;
2562 if a
< 15 then h
:= 0
2563 else if a
< 35 then h
:= 1
2564 else if a
< 55 then h
:= 2
2565 else if a
< 75 then h
:= 3
2566 else if a
< 95 then h
:= 4
2569 //if a > 255 then a := 255;
2571 e_DrawFillQuad(0, 0, gPlayerScreenSize
.X
-1, gPlayerScreenSize
.Y
-1, 255, 0, 0, 255-h
*50);
2572 //e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255-min(128, a), 255-a, 255-a, 0, B_FILTER);
2575 procedure TPlayer
.DrawPickup();
2579 if FPickup
= 0 then Exit
;
2583 if a
< 15 then h
:= 1
2584 else if a
< 35 then h
:= 2
2585 else if a
< 55 then h
:= 3
2586 else if a
< 75 then h
:= 4
2589 e_DrawFillQuad(0, 0, gPlayerScreenSize
.X
-1, gPlayerScreenSize
.Y
-1, 150, 200, 150, 255-h
*50);
2592 procedure TPlayer
.Fire();
2594 f
, DidFire
: Boolean;
2595 wx
, wy
, xd
, yd
: Integer;
2598 if g_Game_IsClient
then Exit
;
2599 // FBFGFireCounter - âðåìÿ ïåðåä âûñòðåëîì (äëÿ BFG)
2600 // FReloading - âðåìÿ ïîñëå âûñòðåëà (äëÿ âñåãî)
2608 if FReloading
[FCurrWeap
] <> 0 then Exit
;
2613 wx
:= FObj
.X
+WEAPONPOINT
[FDirection
].X
;
2614 wy
:= FObj
.Y
+WEAPONPOINT
[FDirection
].Y
;
2615 xd
:= wx
+IfThen(FDirection
= D_LEFT
, -30, 30);
2616 yd
:= wy
+firediry();
2621 if R_BERSERK
in FRulez
then
2623 //g_Weapon_punch(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, 75, FUID);
2624 obj
.X
:= FObj
.X
+FObj
.Rect
.X
;
2625 obj
.Y
:= FObj
.Y
+FObj
.Rect
.Y
;
2628 obj
.rect
.Width
:= 39;
2629 obj
.rect
.Height
:= 52;
2630 obj
.Vel
.X
:= (xd
-wx
) div 2;
2631 obj
.Vel
.Y
:= (yd
-wy
) div 2;
2632 obj
.Accel
.X
:= xd
-wx
;
2633 obj
.Accel
.y
:= yd
-wy
;
2635 if g_Weapon_Hit(@obj
, 50, FUID
, HIT_SOME
) <> 0 then
2636 g_Sound_PlayExAt('SOUND_WEAPON_HITBERSERK', FObj
.X
, FObj
.Y
)
2638 g_Sound_PlayExAt('SOUND_WEAPON_MISSBERSERK', FObj
.X
, FObj
.Y
);
2642 FPain
:= min(FPain
+ 25, 50);
2643 end else g_Weapon_punch(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
, 3, FUID
);
2646 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2651 if g_Weapon_chainsaw(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
,
2652 IfThen(gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
], 9, 3), FUID
) <> 0 then
2654 FSawSoundSelect
.Stop();
2656 FSawSoundHit
.PlayAt(FObj
.X
, FObj
.Y
);
2658 else if not FSawSoundHit
.IsPlaying() then
2660 FSawSoundSelect
.Stop();
2661 FSawSound
.PlayAt(FObj
.X
, FObj
.Y
);
2664 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2670 if FAmmo
[A_BULLETS
] > 0 then
2672 g_Weapon_pistol(wx
, wy
, xd
, yd
, FUID
);
2673 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2674 Dec(FAmmo
[A_BULLETS
]);
2675 FFireAngle
:= FAngle
;
2678 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
2679 GameVelX
, GameVelY
-2, SHELL_BULLET
);
2683 if FAmmo
[A_SHELLS
] > 0 then
2685 g_Weapon_shotgun(wx
, wy
, xd
, yd
, FUID
);
2686 if not gSoundEffectsDF
then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', wx
, wy
);
2687 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2688 Dec(FAmmo
[A_SHELLS
]);
2689 FFireAngle
:= FAngle
;
2693 FShellType
:= SHELL_SHELL
;
2697 if FAmmo
[A_SHELLS
] >= 2 then
2699 g_Weapon_dshotgun(wx
, wy
, xd
, yd
, FUID
);
2700 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2701 Dec(FAmmo
[A_SHELLS
], 2);
2702 FFireAngle
:= FAngle
;
2706 FShellType
:= SHELL_DBLSHELL
;
2710 if FAmmo
[A_BULLETS
] > 0 then
2712 g_Weapon_mgun(wx
, wy
, xd
, yd
, FUID
);
2713 if not gSoundEffectsDF
then g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', wx
, wy
);
2714 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2715 Dec(FAmmo
[A_BULLETS
]);
2716 FFireAngle
:= FAngle
;
2719 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
2720 GameVelX
, GameVelY
-2, SHELL_BULLET
);
2723 WEAPON_ROCKETLAUNCHER
:
2724 if FAmmo
[A_ROCKETS
] > 0 then
2726 g_Weapon_rocket(wx
, wy
, xd
, yd
, FUID
);
2727 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2728 Dec(FAmmo
[A_ROCKETS
]);
2729 FFireAngle
:= FAngle
;
2735 if FAmmo
[A_CELLS
] > 0 then
2737 g_Weapon_plasma(wx
, wy
, xd
, yd
, FUID
);
2738 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2739 Dec(FAmmo
[A_CELLS
]);
2740 FFireAngle
:= FAngle
;
2746 if (FAmmo
[A_CELLS
] >= 40) and (FBFGFireCounter
= -1) then
2748 FBFGFireCounter
:= 17;
2749 if not FNoReload
then
2750 g_Sound_PlayExAt('SOUND_WEAPON_STARTFIREBFG', FObj
.X
, FObj
.Y
);
2751 Dec(FAmmo
[A_CELLS
], 40);
2755 WEAPON_SUPERPULEMET
:
2756 if FAmmo
[A_SHELLS
] > 0 then
2758 g_Weapon_shotgun(wx
, wy
, xd
, yd
, FUID
);
2759 if not gSoundEffectsDF
then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', wx
, wy
);
2760 FReloading
[FCurrWeap
] := WEAPON_RELOAD
[FCurrWeap
];
2761 Dec(FAmmo
[A_SHELLS
]);
2762 FFireAngle
:= FAngle
;
2765 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
2766 GameVelX
, GameVelY
-2, SHELL_SHELL
);
2770 if g_Game_IsNet
then
2774 if FCurrWeap
<> WEAPON_BFG
then
2775 MH_SEND_PlayerFire(FUID
, FCurrWeap
, wx
, wy
, xd
, yd
, LastShotID
)
2777 if not FNoReload
then
2778 MH_SEND_Sound(FObj
.X
, FObj
.Y
, 'SOUND_WEAPON_STARTFIREBFG');
2781 MH_SEND_PlayerStats(FUID
);
2786 if (FAngle
= 0) or (FAngle
= 180) then SetAction(A_ATTACK
)
2787 else if (FAngle
= ANGLE_LEFTDOWN
) or (FAngle
= ANGLE_RIGHTDOWN
) then SetAction(A_ATTACKDOWN
)
2788 else if (FAngle
= ANGLE_LEFTUP
) or (FAngle
= ANGLE_RIGHTUP
) then SetAction(A_ATTACKUP
);
2791 function TPlayer
.GetAmmoByWeapon(Weapon
: Byte): Word;
2794 WEAPON_PISTOL
, WEAPON_CHAINGUN
: Result
:= FAmmo
[A_BULLETS
];
2795 WEAPON_SHOTGUN1
, WEAPON_SHOTGUN2
, WEAPON_SUPERPULEMET
: Result
:= FAmmo
[A_SHELLS
];
2796 WEAPON_ROCKETLAUNCHER
: Result
:= FAmmo
[A_ROCKETS
];
2797 WEAPON_PLASMA
, WEAPON_BFG
: Result
:= FAmmo
[A_CELLS
];
2802 function TPlayer
.HeadInLiquid(XInc
, YInc
: Integer): Boolean;
2804 Result
:= g_Map_CollidePanel(FObj
.X
+PLAYER_HEADRECT
.X
+XInc
, FObj
.Y
+PLAYER_HEADRECT
.Y
+YInc
,
2805 PLAYER_HEADRECT
.Width
, PLAYER_HEADRECT
.Height
,
2806 PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
, True);
2809 procedure TPlayer
.JetpackOn
;
2813 FJetSoundOn
.SetPosition(0);
2814 FJetSoundOn
.PlayAt(FObj
.X
, FObj
.Y
);
2818 procedure TPlayer
.JetpackOff
;
2822 FJetSoundOff
.SetPosition(0);
2823 FJetSoundOff
.PlayAt(FObj
.X
, FObj
.Y
);
2826 procedure TPlayer
.Jump();
2828 if gFly
or FJetpack
then
2830 // Ïîëåò (÷èò-êîä èëè äæåòïàê):
2831 if FObj
.Vel
.Y
> -VEL_FLY
then
2832 FObj
.Vel
.Y
:= FObj
.Vel
.Y
- 3;
2835 if FJetFuel
> 0 then
2837 if (FJetFuel
< 1) and g_Game_IsServer
then
2841 if g_Game_IsNet
then
2842 MH_SEND_PlayerStats(FUID
);
2848 // Íå âêëþ÷àòü äæåòïàê â ðåæèìå ïðîõîæäåíèÿ ñêâîçü ñòåíû
2850 FCanJetpack
:= False;
2852 // Ïðûãàåì èëè âñïëûâàåì:
2853 if (CollideLevel(0, 1) or
2854 g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+PLAYER_RECT
.Y
+36, PLAYER_RECT
.Width
,
2855 PLAYER_RECT
.Height
-33, PANEL_STEP
, False)
2856 ) and (FObj
.Accel
.Y
= 0) then // Íå ïðûãàòü, åñëè åñòü âåðòèêàëüíîå óñêîðåíèå
2858 FObj
.Vel
.Y
:= -VEL_JUMP
;
2859 FCanJetpack
:= False;
2863 if BodyInLiquid(0, 0) then
2864 FObj
.Vel
.Y
:= -VEL_SW
2865 else if (FJetFuel
> 0) and FCanJetpack
and
2866 g_Game_IsServer
and (not g_Obj_CollideLiquid(@FObj
, 0, 0)) then
2870 if g_Game_IsNet
then
2871 MH_SEND_PlayerStats(FUID
);
2876 procedure TPlayer
.Kill(KillType
: Byte; SpawnerUID
: Word; t
: Byte);
2878 a
, i
, k
, ab
, ar
: Byte;
2882 srv
, netsrv
: Boolean;
2887 procedure PushItem(t
: Byte);
2891 id
:= g_Items_Create(FObj
.X
, FObj
.Y
, t
, True, False);
2892 if KillType
= K_EXTRAHARDKILL
then // -7..+7; -8..0
2893 g_Obj_Push(@gItems
[id
].Obj
, (FObj
.Vel
.X
div 2)-7+Random(15),
2894 (FObj
.Vel
.Y
div 2)-Random(9))
2896 if KillType
= K_HARDKILL
then // -5..+5; -5..0
2897 g_Obj_Push(@gItems
[id
].Obj
, (FObj
.Vel
.X
div 2)-5+Random(11),
2898 (FObj
.Vel
.Y
div 2)-Random(6))
2899 else // -3..+3; -3..0
2900 g_Obj_Push(@gItems
[id
].Obj
, (FObj
.Vel
.X
div 2)-3+Random(7),
2901 (FObj
.Vel
.Y
div 2)-Random(4));
2903 if g_Game_IsNet
and g_Game_IsServer
then
2904 MH_SEND_ItemSpawn(True, id
);
2908 DoFrags
:= (gGameSettings
.MaxLives
= 0) or (gGameSettings
.GameMode
= GM_COOP
);
2909 Srv
:= g_Game_IsServer
;
2910 Netsrv
:= g_Game_IsServer
and g_Game_IsNet
;
2911 if Srv
then FDeath
:= FDeath
+ 1;
2916 if not FPhysics
then
2922 if (gGameSettings
.MaxLives
> 0) and Srv
and (gLMSRespawn
= LMS_RESPAWN_NONE
) then
2924 if FLives
> 0 then FLives
:= FLives
- 1;
2925 if FLives
= 0 then FNoRespawn
:= True;
2928 // Íîìåð òèïà ñìåðòè:
2931 K_SIMPLEKILL
: a
:= 1;
2933 K_EXTRAHARDKILL
: a
:= 3;
2938 if not FModel
.PlaySound(MODELSOUND_DIE
, a
, FObj
.X
, FObj
.Y
) then
2940 if FModel
.PlaySound(MODELSOUND_DIE
, i
, FObj
.X
, FObj
.Y
) then
2947 FTime
[T_RESPAWN
] := gTime
+ TIME_RESPAWN1
;
2949 FTime
[T_RESPAWN
] := gTime
+ TIME_RESPAWN2
;
2950 K_EXTRAHARDKILL
, K_FALLKILL
:
2951 FTime
[T_RESPAWN
] := gTime
+ TIME_RESPAWN3
;
2954 // Ïåðåêëþ÷àåì ñîñòîÿíèå:
2958 K_HARDKILL
, K_EXTRAHARDKILL
:
2962 // Ðåàêöèÿ ìîíñòðîâ íà ñìåðòü èãðîêà:
2963 if (KillType
<> K_FALLKILL
) and (Srv
) then
2964 g_Monsters_killedp();
2966 if SpawnerUID
= FUID
then
2968 if Srv
and (DoFrags
or (gGameSettings
.GameMode
= GM_TDM
)) then
2973 g_Console_Add(Format(_lc
[I_PLAYER_KILL_SELF
], [FName
]), True);
2976 if g_GetUIDType(SpawnerUID
) = UID_PLAYER
then
2977 begin // Óáèò äðóãèì èãðîêîì
2978 KP
:= g_Player_Get(SpawnerUID
);
2979 if (KP
<> nil) and Srv
then
2981 if (DoFrags
or (gGameSettings
.GameMode
= GM_TDM
)) then
2982 if SameTeam(FUID
, SpawnerUID
) then
2992 if (gGameSettings
.GameMode
= GM_TDM
) and DoFrags
then
2993 Inc(gTeamStat
[KP
.Team
].Goals
,
2994 IfThen(SameTeam(FUID
, SpawnerUID
), -1, 1));
2996 if netsrv
then MH_SEND_PlayerStats(SpawnerUID
);
2999 plr
:= g_Player_Get(SpawnerUID
);
3007 g_Console_Add(Format(_lc
[I_PLAYER_KILL_EXTRAHARD_2
],
3011 g_Console_Add(Format(_lc
[I_PLAYER_KILL_EXTRAHARD_1
],
3015 g_Console_Add(Format(_lc
[I_PLAYER_KILL
],
3020 else if g_GetUIDType(SpawnerUID
) = UID_MONSTER
then
3021 begin // Óáèò ìîíñòðîì
3022 mon
:= g_Monsters_Get(SpawnerUID
);
3026 s
:= g_Monsters_GetKilledBy(mon
.MonsterType
);
3030 g_Console_Add(Format(_lc
[I_PLAYER_KILL_EXTRAHARD_2
],
3034 g_Console_Add(Format(_lc
[I_PLAYER_KILL_EXTRAHARD_1
],
3038 g_Console_Add(Format(_lc
[I_PLAYER_KILL
],
3043 else // Îñîáûå òèïû ñìåðòè
3046 HIT_SELF
: g_Console_Add(Format(_lc
[I_PLAYER_KILL_SELF
], [FName
]), True);
3047 HIT_FALL
: g_Console_Add(Format(_lc
[I_PLAYER_KILL_FALL
], [FName
]), True);
3048 HIT_WATER
: g_Console_Add(Format(_lc
[I_PLAYER_KILL_WATER
], [FName
]), True);
3049 HIT_ACID
: g_Console_Add(Format(_lc
[I_PLAYER_KILL_ACID
], [FName
]), True);
3050 HIT_TRAP
: g_Console_Add(Format(_lc
[I_PLAYER_KILL_TRAP
], [FName
]), True);
3051 else g_Console_Add(Format(_lc
[I_PLAYER_DIED
], [FName
]), True);
3057 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
3061 WEAPON_SAW
: i
:= ITEM_WEAPON_SAW
;
3062 WEAPON_SHOTGUN1
: i
:= ITEM_WEAPON_SHOTGUN1
;
3063 WEAPON_SHOTGUN2
: i
:= ITEM_WEAPON_SHOTGUN2
;
3064 WEAPON_CHAINGUN
: i
:= ITEM_WEAPON_CHAINGUN
;
3065 WEAPON_ROCKETLAUNCHER
: i
:= ITEM_WEAPON_ROCKETLAUNCHER
;
3066 WEAPON_PLASMA
: i
:= ITEM_WEAPON_PLASMA
;
3067 WEAPON_BFG
: i
:= ITEM_WEAPON_BFG
;
3068 WEAPON_SUPERPULEMET
: i
:= ITEM_WEAPON_SUPERPULEMET
;
3077 if R_ITEM_BACKPACK
in FRulez
then
3078 PushItem(ITEM_AMMO_BACKPACK
);
3080 // Âûáðîñ ðàêåòíîãî ðàíöà:
3081 if FJetFuel
> 0 then
3082 PushItem(ITEM_JETPACK
);
3085 if not (gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
]) then
3087 if R_KEY_RED
in FRulez
then
3088 PushItem(ITEM_KEY_RED
);
3090 if R_KEY_GREEN
in FRulez
then
3091 PushItem(ITEM_KEY_GREEN
);
3093 if R_KEY_BLUE
in FRulez
then
3094 PushItem(ITEM_KEY_BLUE
);
3101 g_Player_CreateCorpse(Self
);
3103 if Srv
and (gGameSettings
.MaxLives
> 0) and FNoRespawn
and
3104 (gLMSRespawn
= LMS_RESPAWN_NONE
) then
3110 for i
:= Low(gPlayers
) to High(gPlayers
) do
3112 if gPlayers
[i
] = nil then continue
;
3113 if (not gPlayers
[i
].FNoRespawn
) and (not gPlayers
[i
].FSpectator
) then
3116 if gPlayers
[i
].FTeam
= TEAM_RED
then Inc(ar
)
3117 else if gPlayers
[i
].FTeam
= TEAM_BLUE
then Inc(ab
);
3122 OldLR
:= gLMSRespawn
;
3123 if (gGameSettings
.GameMode
= GM_COOP
) then
3127 // everyone is dead, restart the map
3128 g_Game_Message(_lc
[I_MESSAGE_LMS_LOSE
], 144);
3130 MH_SEND_GameEvent(NET_EV_LMS_LOSE
);
3131 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3132 gLMSRespawnTime
:= gTime
+ 5000;
3134 else if (a
= 1) then
3136 if (gPlayers
[k
] <> nil) and not (gPlayers
[k
] is TBot
) then
3137 if (gPlayers
[k
] = gPlayer1
) or
3138 (gPlayers
[k
] = gPlayer2
) then
3139 g_Console_Add('*** ' + _lc
[I_MESSAGE_LMS_SURVIVOR
] + ' ***', True)
3140 else if Netsrv
and (gPlayers
[k
].FClientID
>= 0) then
3141 MH_SEND_GameEvent(NET_EV_LMS_SURVIVOR
, 0, 'N', gPlayers
[k
].FClientID
);
3144 else if (gGameSettings
.GameMode
= GM_TDM
) then
3146 if (ab
= 0) and (ar
<> 0) then
3149 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_RED
])]), 144);
3151 MH_SEND_GameEvent(NET_EV_TLMS_WIN
, TEAM_RED
);
3152 Inc(gTeamStat
[TEAM_RED
].Goals
);
3153 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3154 gLMSRespawnTime
:= gTime
+ 5000;
3156 else if (ar
= 0) and (ab
<> 0) then
3159 g_Game_Message(Format(_lc
[I_MESSAGE_TLMS_WIN
], [AnsiUpperCase(_lc
[I_GAME_TEAM_BLUE
])]), 144);
3161 MH_SEND_GameEvent(NET_EV_TLMS_WIN
, TEAM_BLUE
);
3162 Inc(gTeamStat
[TEAM_BLUE
].Goals
);
3163 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3164 gLMSRespawnTime
:= gTime
+ 5000;
3166 else if (ar
= 0) and (ab
= 0) then
3169 g_Game_Message(_lc
[I_GAME_WIN_DRAW
], 144);
3171 MH_SEND_GameEvent(NET_EV_LMS_DRAW
, 0, FName
);
3172 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3173 gLMSRespawnTime
:= gTime
+ 5000;
3176 else if (gGameSettings
.GameMode
= GM_DM
) then
3180 if gPlayers
[k
] <> nil then
3183 // survivor is the winner
3184 g_Game_Message(Format(_lc
[I_MESSAGE_LMS_WIN
], [AnsiUpperCase(FName
)]), 144);
3186 MH_SEND_GameEvent(NET_EV_LMS_WIN
, 0, FName
);
3189 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3190 gLMSRespawnTime
:= gTime
+ 5000;
3192 else if (a
= 0) then
3194 // everyone is dead, restart the map
3195 g_Game_Message(_lc
[I_GAME_WIN_DRAW
], 144);
3197 MH_SEND_GameEvent(NET_EV_LMS_DRAW
, 0, FName
);
3198 gLMSRespawn
:= LMS_RESPAWN_FINAL
;
3199 gLMSRespawnTime
:= gTime
+ 5000;
3202 if srv
and (OldLR
= LMS_RESPAWN_NONE
) and (gLMSRespawn
> LMS_RESPAWN_NONE
) then
3204 if NetMode
= NET_SERVER
then
3205 MH_SEND_GameEvent(NET_EV_LMS_WARMUP
, (gLMSRespawnTime
- gTime
) div 1000)
3207 g_Console_Add(Format(_lc
[I_MSG_WARMUP_START
], [(gLMSRespawnTime
- gTime
) div 1000]), True);
3213 MH_SEND_PlayerStats(FUID
);
3214 MH_SEND_PlayerDeath(FUID
, KillType
, t
, SpawnerUID
);
3215 if gGameSettings
.GameMode
= GM_TDM
then MH_SEND_GameStats
;
3218 if srv
and FNoRespawn
then Spectate(True);
3219 FWantsInGame
:= True;
3222 function TPlayer
.BodyInLiquid(XInc
, YInc
: Integer): Boolean;
3224 Result
:= g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
, PLAYER_RECT
.Width
,
3225 PLAYER_RECT
.Height
-20, PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
, False);
3228 function TPlayer
.BodyInAcid(XInc
, YInc
: Integer): Boolean;
3230 Result
:= g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
, PLAYER_RECT
.Width
,
3231 PLAYER_RECT
.Height
-20, PANEL_ACID1
or PANEL_ACID2
, False);
3234 procedure TPlayer
.MakeBloodSimple(Count
: Word);
3236 g_GFX_Blood(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)+8,
3237 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2),
3238 Count
div 2, 3, -1, 16, (PLAYER_RECT
.Height
*2 div 3),
3240 g_GFX_Blood(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-8,
3241 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2),
3242 Count
div 2, -3, -1, 16, (PLAYER_RECT
.Height
*2) div 3,
3246 procedure TPlayer
.MakeBloodVector(Count
: Word; VelX
, VelY
: Integer);
3248 g_GFX_Blood(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2),
3249 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2),
3250 Count
, VelX
, VelY
, 16, (PLAYER_RECT
.Height
*2) div 3,
3254 procedure TPlayer
.ForceWeapon(Weapon
: Byte);
3258 if g_Game_IsClient
then Exit
;
3259 if Weapon
> High(FWeapon
) then Exit
;
3260 if FBFGFireCounter
<> -1 then Exit
;
3262 if FTime
[T_SWITCH
] > gTime
then Exit
;
3264 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
3265 if FReloading
[i
] > 0 then Exit
;
3267 if FWeapon
[Weapon
] then
3269 FCurrWeap
:= Weapon
;
3270 FTime
[T_SWITCH
] := gTime
+156;
3271 if FCurrWeap
= WEAPON_SAW
then
3272 FSawSoundSelect
.PlayAt(FObj
.X
, FObj
.Y
);
3273 FModel
.SetWeapon(FCurrWeap
);
3274 if g_Game_IsNet
then MH_SEND_PlayerStats(FUID
);
3278 procedure TPlayer
.NextWeapon();
3283 if g_Game_IsClient
then Exit
;
3284 if FBFGFireCounter
<> -1 then Exit
;
3286 if FTime
[T_SWITCH
] > gTime
then Exit
;
3288 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
3289 if FReloading
[i
] > 0 then Exit
;
3293 for i
:= FCurrWeap
+1 to WEAPON_SUPERPULEMET
do
3302 for i
:= WEAPON_KASTET
to FCurrWeap
-1 do
3309 FTime
[T_SWITCH
] := gTime
+156;
3311 if FCurrWeap
= WEAPON_SAW
then
3312 FSawSoundSelect
.PlayAt(FObj
.X
, FObj
.Y
);
3314 FModel
.SetWeapon(FCurrWeap
);
3316 if g_Game_IsNet
then MH_SEND_PlayerStats(FUID
);
3319 procedure TPlayer
.PrevWeapon();
3324 if g_Game_IsClient
then Exit
;
3325 if FBFGFireCounter
<> -1 then Exit
;
3327 if FTime
[T_SWITCH
] > gTime
then Exit
;
3329 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
3330 if FReloading
[i
] > 0 then Exit
;
3334 if FCurrWeap
> 0 then
3335 for i
:= FCurrWeap
-1 downto WEAPON_KASTET
do
3344 for i
:= WEAPON_SUPERPULEMET
downto FCurrWeap
+1 do
3351 FTime
[T_SWITCH
] := gTime
+156;
3353 if FCurrWeap
= WEAPON_SAW
then
3354 FSawSoundSelect
.PlayAt(FObj
.X
, FObj
.Y
);
3356 FModel
.SetWeapon(FCurrWeap
);
3358 if g_Game_IsNet
then MH_SEND_PlayerStats(FUID
);
3361 procedure TPlayer
.SetWeapon(W
: Byte);
3363 if FCurrWeap
<> W
then
3364 if W
= WEAPON_SAW
then
3365 FSawSoundSelect
.PlayAt(FObj
.X
, FObj
.Y
);
3368 FModel
.SetWeapon(CurrWeap
);
3371 function TPlayer
.PickItem(ItemType
: Byte; respawn
: Boolean; var remove
: Boolean): Boolean;
3376 if g_Game_IsClient
then Exit
;
3378 // a = true - ìåñòî ñïàâíà ïðåäìåòà:
3379 a
:= LongBool(gGameSettings
.Options
and GAME_OPTION_WEAPONSTAY
) and respawn
;
3384 if FHealth
< PLAYER_HP_SOFT
then
3386 IncMax(FHealth
, 10, PLAYER_HP_SOFT
);
3389 if gFlash
= 2 then Inc(FPickup
, 5);
3393 if FHealth
< PLAYER_HP_SOFT
then
3395 IncMax(FHealth
, 25, PLAYER_HP_SOFT
);
3398 if gFlash
= 2 then Inc(FPickup
, 5);
3402 if FArmor
< PLAYER_AP_SOFT
then
3404 FArmor
:= PLAYER_AP_SOFT
;
3407 if gFlash
= 2 then Inc(FPickup
, 5);
3411 if FArmor
< PLAYER_AP_LIMIT
then
3413 FArmor
:= PLAYER_AP_LIMIT
;
3416 if gFlash
= 2 then Inc(FPickup
, 5);
3420 if FHealth
< PLAYER_HP_LIMIT
then
3422 IncMax(FHealth
, 100, PLAYER_HP_LIMIT
);
3425 if gFlash
= 2 then Inc(FPickup
, 5);
3429 if (FHealth
< PLAYER_HP_LIMIT
) or (FArmor
< PLAYER_AP_LIMIT
) then
3431 if FHealth
< PLAYER_HP_LIMIT
then
3432 FHealth
:= PLAYER_HP_LIMIT
;
3433 if FArmor
< PLAYER_AP_LIMIT
then
3434 FArmor
:= PLAYER_AP_LIMIT
;
3437 if gFlash
= 2 then Inc(FPickup
, 5);
3441 if (not FWeapon
[WEAPON_SAW
]) or ((not respawn
) and (gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
])) then
3443 FWeapon
[WEAPON_SAW
] := True;
3445 if gFlash
= 2 then Inc(FPickup
, 5);
3446 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3449 ITEM_WEAPON_SHOTGUN1
:
3450 if (FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
]) or not FWeapon
[WEAPON_SHOTGUN1
] then
3452 // Íóæíî, ÷òîáû íå âçÿòü âñå ïóëè ñðàçó:
3453 if a
and FWeapon
[WEAPON_SHOTGUN1
] then Exit
;
3455 IncMax(FAmmo
[A_SHELLS
], 4, FMaxAmmo
[A_SHELLS
]);
3456 FWeapon
[WEAPON_SHOTGUN1
] := True;
3458 if gFlash
= 2 then Inc(FPickup
, 5);
3459 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3462 ITEM_WEAPON_SHOTGUN2
:
3463 if (FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
]) or not FWeapon
[WEAPON_SHOTGUN2
] then
3465 if a
and FWeapon
[WEAPON_SHOTGUN2
] then Exit
;
3467 IncMax(FAmmo
[A_SHELLS
], 4, FMaxAmmo
[A_SHELLS
]);
3468 FWeapon
[WEAPON_SHOTGUN2
] := True;
3470 if gFlash
= 2 then Inc(FPickup
, 5);
3471 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3474 ITEM_WEAPON_CHAINGUN
:
3475 if (FAmmo
[A_BULLETS
] < FMaxAmmo
[A_BULLETS
]) or not FWeapon
[WEAPON_CHAINGUN
] then
3477 if a
and FWeapon
[WEAPON_CHAINGUN
] then Exit
;
3479 IncMax(FAmmo
[A_BULLETS
], 50, FMaxAmmo
[A_BULLETS
]);
3480 FWeapon
[WEAPON_CHAINGUN
] := True;
3482 if gFlash
= 2 then Inc(FPickup
, 5);
3483 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3486 ITEM_WEAPON_ROCKETLAUNCHER
:
3487 if (FAmmo
[A_ROCKETS
] < FMaxAmmo
[A_ROCKETS
]) or not FWeapon
[WEAPON_ROCKETLAUNCHER
] then
3489 if a
and FWeapon
[WEAPON_ROCKETLAUNCHER
] then Exit
;
3491 IncMax(FAmmo
[A_ROCKETS
], 2, FMaxAmmo
[A_ROCKETS
]);
3492 FWeapon
[WEAPON_ROCKETLAUNCHER
] := True;
3494 if gFlash
= 2 then Inc(FPickup
, 5);
3495 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3499 if (FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
]) or not FWeapon
[WEAPON_PLASMA
] then
3501 if a
and FWeapon
[WEAPON_PLASMA
] then Exit
;
3503 IncMax(FAmmo
[A_CELLS
], 40, FMaxAmmo
[A_CELLS
]);
3504 FWeapon
[WEAPON_PLASMA
] := True;
3506 if gFlash
= 2 then Inc(FPickup
, 5);
3507 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3511 if (FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
]) or not FWeapon
[WEAPON_BFG
] then
3513 if a
and FWeapon
[WEAPON_BFG
] then Exit
;
3515 IncMax(FAmmo
[A_CELLS
], 40, FMaxAmmo
[A_CELLS
]);
3516 FWeapon
[WEAPON_BFG
] := True;
3518 if gFlash
= 2 then Inc(FPickup
, 5);
3519 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3522 ITEM_WEAPON_SUPERPULEMET
:
3523 if (FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
]) or not FWeapon
[WEAPON_SUPERPULEMET
] then
3525 if a
and FWeapon
[WEAPON_SUPERPULEMET
] then Exit
;
3527 IncMax(FAmmo
[A_SHELLS
], 4, FMaxAmmo
[A_SHELLS
]);
3528 FWeapon
[WEAPON_SUPERPULEMET
] := True;
3530 if gFlash
= 2 then Inc(FPickup
, 5);
3531 if a
and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETWEAPON');
3535 if FAmmo
[A_BULLETS
] < FMaxAmmo
[A_BULLETS
] then
3537 IncMax(FAmmo
[A_BULLETS
], 10, FMaxAmmo
[A_BULLETS
]);
3540 if gFlash
= 2 then Inc(FPickup
, 5);
3543 ITEM_AMMO_BULLETS_BOX
:
3544 if FAmmo
[A_BULLETS
] < FMaxAmmo
[A_BULLETS
] then
3546 IncMax(FAmmo
[A_BULLETS
], 50, FMaxAmmo
[A_BULLETS
]);
3549 if gFlash
= 2 then Inc(FPickup
, 5);
3553 if FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
] then
3555 IncMax(FAmmo
[A_SHELLS
], 4, FMaxAmmo
[A_SHELLS
]);
3558 if gFlash
= 2 then Inc(FPickup
, 5);
3561 ITEM_AMMO_SHELLS_BOX
:
3562 if FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
] then
3564 IncMax(FAmmo
[A_SHELLS
], 25, FMaxAmmo
[A_SHELLS
]);
3567 if gFlash
= 2 then Inc(FPickup
, 5);
3571 if FAmmo
[A_ROCKETS
] < FMaxAmmo
[A_ROCKETS
] then
3573 IncMax(FAmmo
[A_ROCKETS
], 1, FMaxAmmo
[A_ROCKETS
]);
3576 if gFlash
= 2 then Inc(FPickup
, 5);
3579 ITEM_AMMO_ROCKET_BOX
:
3580 if FAmmo
[A_ROCKETS
] < FMaxAmmo
[A_ROCKETS
] then
3582 IncMax(FAmmo
[A_ROCKETS
], 5, FMaxAmmo
[A_ROCKETS
]);
3585 if gFlash
= 2 then Inc(FPickup
, 5);
3589 if FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
] then
3591 IncMax(FAmmo
[A_CELLS
], 40, FMaxAmmo
[A_CELLS
]);
3594 if gFlash
= 2 then Inc(FPickup
, 5);
3598 if FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
] then
3600 IncMax(FAmmo
[A_CELLS
], 100, FMaxAmmo
[A_CELLS
]);
3603 if gFlash
= 2 then Inc(FPickup
, 5);
3607 if not(R_ITEM_BACKPACK
in FRulez
) or
3608 (FAmmo
[A_BULLETS
] < FMaxAmmo
[A_BULLETS
]) or
3609 (FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
]) or
3610 (FAmmo
[A_ROCKETS
] < FMaxAmmo
[A_ROCKETS
]) or
3611 (FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
]) then
3613 FMaxAmmo
[A_BULLETS
] := 400;
3614 FMaxAmmo
[A_SHELLS
] := 100;
3615 FMaxAmmo
[A_ROCKETS
] := 100;
3616 FMaxAmmo
[A_CELLS
] := 600;
3618 if FAmmo
[A_BULLETS
] < FMaxAmmo
[A_BULLETS
] then
3619 IncMax(FAmmo
[A_BULLETS
], 10, FMaxAmmo
[A_BULLETS
]);
3620 if FAmmo
[A_SHELLS
] < FMaxAmmo
[A_SHELLS
] then
3621 IncMax(FAmmo
[A_SHELLS
], 4, FMaxAmmo
[A_SHELLS
]);
3622 if FAmmo
[A_ROCKETS
] < FMaxAmmo
[A_ROCKETS
] then
3623 IncMax(FAmmo
[A_ROCKETS
], 1, FMaxAmmo
[A_ROCKETS
]);
3624 if FAmmo
[A_CELLS
] < FMaxAmmo
[A_CELLS
] then
3625 IncMax(FAmmo
[A_CELLS
], 40, FMaxAmmo
[A_CELLS
]);
3627 FRulez
:= FRulez
+ [R_ITEM_BACKPACK
];
3630 if gFlash
= 2 then Inc(FPickup
, 5);
3634 if not(R_KEY_RED
in FRulez
) then
3636 Include(FRulez
, R_KEY_RED
);
3638 remove
:= (gGameSettings
.GameMode
<> GM_COOP
) and (g_Player_GetCount() < 2);
3639 if gFlash
= 2 then Inc(FPickup
, 5);
3640 if (not remove
) and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETITEM');
3644 if not(R_KEY_GREEN
in FRulez
) then
3646 Include(FRulez
, R_KEY_GREEN
);
3648 remove
:= (gGameSettings
.GameMode
<> GM_COOP
) and (g_Player_GetCount() < 2);
3649 if gFlash
= 2 then Inc(FPickup
, 5);
3650 if (not remove
) and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETITEM');
3654 if not(R_KEY_BLUE
in FRulez
) then
3656 Include(FRulez
, R_KEY_BLUE
);
3658 remove
:= (gGameSettings
.GameMode
<> GM_COOP
) and (g_Player_GetCount() < 2);
3659 if gFlash
= 2 then Inc(FPickup
, 5);
3660 if (not remove
) and g_Game_IsNet
then MH_SEND_Sound(GameX
, GameY
, 'SOUND_ITEM_GETITEM');
3664 if FMegaRulez
[MR_SUIT
] < gTime
+PLAYER_SUIT_TIME
then
3666 FMegaRulez
[MR_SUIT
] := gTime
+PLAYER_SUIT_TIME
;
3669 if gFlash
= 2 then Inc(FPickup
, 5);
3673 if FAir
< AIR_MAX
then
3678 if gFlash
= 2 then Inc(FPickup
, 5);
3683 if not (R_BERSERK
in FRulez
) then
3685 Include(FRulez
, R_BERSERK
);
3686 if FBFGFireCounter
= -1 then
3688 FCurrWeap
:= WEAPON_KASTET
;
3689 FModel
.SetWeapon(WEAPON_KASTET
);
3693 if gFlash
= 2 then Inc(FPickup
, 5);
3694 FBerserk
:= gTime
+30000;
3698 if FHealth
< PLAYER_HP_SOFT
then
3700 FHealth
:= PLAYER_HP_SOFT
;
3701 FBerserk
:= gTime
+30000;
3708 if FMegaRulez
[MR_INVUL
] < gTime
+PLAYER_INVUL_TIME
then
3710 FMegaRulez
[MR_INVUL
] := gTime
+PLAYER_INVUL_TIME
;
3713 if gFlash
= 2 then Inc(FPickup
, 5);
3717 if FHealth
< PLAYER_HP_LIMIT
then
3719 IncMax(FHealth
, 4, PLAYER_HP_LIMIT
);
3722 if gFlash
= 2 then Inc(FPickup
, 5);
3726 if FArmor
< PLAYER_AP_LIMIT
then
3728 IncMax(FArmor
, 5, PLAYER_AP_LIMIT
);
3731 if gFlash
= 2 then Inc(FPickup
, 5);
3735 if FJetFuel
< JET_MAX
then
3737 FJetFuel
:= JET_MAX
;
3740 if gFlash
= 2 then Inc(FPickup
, 5);
3744 if FMegaRulez
[MR_INVIS
] < gTime
+PLAYER_INVIS_TIME
then
3746 FMegaRulez
[MR_INVIS
] := gTime
+PLAYER_INVIS_TIME
;
3749 if gFlash
= 2 then Inc(FPickup
, 5);
3754 procedure TPlayer
.Touch();
3758 //FModel.PlaySound(MODELSOUND_PAIN, 1, FObj.X, FObj.Y);
3761 // Áðîñèòü ôëàã òîâàðèùó:
3762 if gGameSettings
.GameMode
= GM_CTF
then
3767 procedure TPlayer
.Push(vx
, vy
: Integer);
3769 if (not FPhysics
) and FGhost
then
3771 FObj
.Accel
.X
:= FObj
.Accel
.X
+ vx
;
3772 FObj
.Accel
.Y
:= FObj
.Accel
.Y
+ vy
;
3773 if g_Game_IsNet
and g_Game_IsServer
then
3774 MH_SEND_PlayerPos(True, FUID
, NET_EVERYONE
);
3777 procedure TPlayer
.Reset(Force
: Boolean);
3783 FTime
[T_RESPAWN
] := 0;
3784 FTime
[T_FLAGCAP
] := 0;
3797 FSpectator
:= False;
3800 FSpectatePlayer
:= -1;
3801 FNoRespawn
:= False;
3803 FLives
:= gGameSettings
.MaxLives
;
3808 procedure TPlayer
.SoftReset();
3814 FBFGFireCounter
:= -1;
3822 SetAction(A_STAND
, True);
3825 function TPlayer
.GetRespawnPoint(): Byte;
3830 // Íà áóäóùåå: FSpawn - èãðîê óæå èãðàë è ïåðåðîæäàåòñÿ
3832 // Îäèíî÷íàÿ èãðà/êîîïåðàòèâ
3833 if gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
] then
3835 if (Self
= gPlayer1
) or (Self
= gPlayer2
) then
3837 // Òî÷êà ïîÿâëåíèÿ ñâîåãî èãðîêà
3838 if Self
= gPlayer1
then
3839 c
:= RESPAWNPOINT_PLAYER1
3841 c
:= RESPAWNPOINT_PLAYER2
;
3842 if g_Map_GetPointCount(c
) > 0 then
3848 // Òî÷êà ïîÿâëåíèÿ äðóãîãî èãðîêà
3849 if Self
= gPlayer1
then
3850 c
:= RESPAWNPOINT_PLAYER2
3852 c
:= RESPAWNPOINT_PLAYER1
;
3853 if g_Map_GetPointCount(c
) > 0 then
3860 // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà (áîòà)
3861 if Random(2) = 0 then
3862 c
:= RESPAWNPOINT_PLAYER1
3864 c
:= RESPAWNPOINT_PLAYER2
;
3865 if g_Map_GetPointCount(c
) > 0 then
3872 // Òî÷êà ëþáîé èç êîìàíä
3873 if Random(2) = 0 then
3874 c
:= RESPAWNPOINT_RED
3876 c
:= RESPAWNPOINT_BLUE
;
3877 if g_Map_GetPointCount(c
) > 0 then
3884 c
:= RESPAWNPOINT_DM
;
3885 if g_Map_GetPointCount(c
) > 0 then
3893 if gGameSettings
.GameMode
= GM_DM
then
3896 c
:= RESPAWNPOINT_DM
;
3897 if g_Map_GetPointCount(c
) > 0 then
3903 // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà
3904 if Random(2) = 0 then
3905 c
:= RESPAWNPOINT_PLAYER1
3907 c
:= RESPAWNPOINT_PLAYER2
;
3908 if g_Map_GetPointCount(c
) > 0 then
3914 // Òî÷êà ëþáîé èç êîìàíä
3915 if Random(2) = 0 then
3916 c
:= RESPAWNPOINT_RED
3918 c
:= RESPAWNPOINT_BLUE
;
3919 if g_Map_GetPointCount(c
) > 0 then
3927 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
3929 // Òî÷êà ñâîåé êîìàíäû
3930 c
:= RESPAWNPOINT_DM
;
3931 if FTeam
= TEAM_RED
then
3932 c
:= RESPAWNPOINT_RED
;
3933 if FTeam
= TEAM_BLUE
then
3934 c
:= RESPAWNPOINT_BLUE
;
3935 if g_Map_GetPointCount(c
) > 0 then
3942 c
:= RESPAWNPOINT_DM
;
3943 if g_Map_GetPointCount(c
) > 0 then
3949 // Òî÷êà ïîÿâëåíèÿ ëþáîãî èãðîêà
3950 if Random(2) = 0 then
3951 c
:= RESPAWNPOINT_PLAYER1
3953 c
:= RESPAWNPOINT_PLAYER2
;
3954 if g_Map_GetPointCount(c
) > 0 then
3960 // Òî÷êà äðóãîé êîìàíäû
3961 c
:= RESPAWNPOINT_DM
;
3962 if FTeam
= TEAM_RED
then
3963 c
:= RESPAWNPOINT_BLUE
;
3964 if FTeam
= TEAM_BLUE
then
3965 c
:= RESPAWNPOINT_RED
;
3966 if g_Map_GetPointCount(c
) > 0 then
3974 procedure TPlayer
.Respawn(Silent
: Boolean; Force
: Boolean = False);
3976 RespawnPoint
: TRespawnPoint
;
3981 if not g_Game_IsServer
then
3985 FWantsInGame
:= True;
3986 FJustTeleported
:= True;
3989 FTime
[T_RESPAWN
] := 0;
3993 // if server changes MaxLives we gotta be ready
3994 if gGameSettings
.MaxLives
= 0 then FNoRespawn
:= False;
3996 // Åùå íåëüçÿ âîçðîäèòüñÿ:
3997 if FTime
[T_RESPAWN
] > gTime
then
4000 // Ïðîñðàë âñå æèçíè:
4003 if not FSpectator
then Spectate(True);
4004 FWantsInGame
:= True;
4008 if (gGameSettings
.GameType
<> GT_SINGLE
) and (gGameSettings
.GameMode
<> GM_COOP
) then
4009 begin // "Ñâîÿ èãðà"
4010 // Áåðñåðê íå ñîõðàíÿåòñÿ ìåæäó óðîâíÿìè:
4011 FRulez
:= FRulez
-[R_BERSERK
];
4013 else // "Îäèíî÷íàÿ èãðà"/"Êîîï"
4015 // Áåðñåðê è êëþ÷è íå ñîõðàíÿþòñÿ ìåæäó óðîâíÿìè:
4016 FRulez
:= FRulez
-[R_KEY_RED
, R_KEY_GREEN
, R_KEY_BLUE
, R_BERSERK
];
4019 // Ïîëó÷àåì òî÷êó ñïàóíà èãðîêà:
4020 c
:= GetRespawnPoint();
4025 // Âîñêðåøåíèå áåç îðóæèÿ:
4028 FHealth
:= PLAYER_HP_SOFT
;
4034 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
4036 FWeapon
[a
] := False;
4040 FWeapon
[WEAPON_PISTOL
] := True;
4041 FWeapon
[WEAPON_KASTET
] := True;
4042 FCurrWeap
:= WEAPON_PISTOL
;
4044 FModel
.SetWeapon(FCurrWeap
);
4046 for b
:= A_BULLETS
to A_CELLS
do
4049 FAmmo
[A_BULLETS
] := 50;
4051 FMaxAmmo
[A_BULLETS
] := 200;
4052 FMaxAmmo
[A_SHELLS
] := 50;
4053 FMaxAmmo
[A_ROCKETS
] := 50;
4054 FMaxAmmo
[A_CELLS
] := 300;
4056 if gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
] then
4057 FRulez
:= [R_KEY_RED
, R_KEY_GREEN
, R_KEY_BLUE
]
4062 // Ïîëó÷àåì êîîðäèíàòû òî÷êè âîçðîæäåíèÿ:
4063 if not g_Map_GetPoint(c
, RespawnPoint
) then
4065 g_FatalError(_lc
[I_GAME_ERROR_GET_SPAWN
]);
4069 // Óñòàíîâêà êîîðäèíàò è ñáðîñ âñåõ ïàðàìåòðîâ:
4070 FObj
.X
:= RespawnPoint
.X
-PLAYER_RECT
.X
;
4071 FObj
.Y
:= RespawnPoint
.Y
-PLAYER_RECT
.Y
;
4077 FDirection
:= RespawnPoint
.Direction
;
4078 if FDirection
= D_LEFT
then
4084 FBFGFireCounter
:= -1;
4089 SetAction(A_STAND
, True);
4090 FModel
.Direction
:= FDirection
;
4092 for a
:= Low(FTime
) to High(FTime
) do
4095 for a
:= Low(FMegaRulez
) to High(FMegaRulez
) do
4100 FCanJetpack
:= False;
4102 // Àíèìàöèÿ âîçðîæäåíèÿ:
4103 if (not gLoadGameMode
) and (not Silent
) then
4104 if g_Frames_Get(ID
, 'FRAMES_TELEPORT') then
4106 Anim
:= TAnimation
.Create(ID
, False, 3);
4107 g_GFX_OnceAnim(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4108 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32, Anim
);
4112 FSpectator
:= False;
4115 FSpectatePlayer
:= -1;
4118 if g_Game_IsNet
then
4120 MH_SEND_PlayerPos(True, FUID
, NET_EVERYONE
);
4121 MH_SEND_PlayerStats(FUID
, NET_EVERYONE
);
4123 MH_SEND_Effect(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4124 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32,
4129 procedure TPlayer
.Spectate(NoMove
: Boolean = False);
4132 Kill(K_EXTRAHARDKILL
, FUID
, HIT_SOME
)
4133 else if (not NoMove
) then
4135 GameX
:= gMapInfo
.Width
div 2;
4136 GameY
:= gMapInfo
.Height
div 2;
4145 FWantsInGame
:= False;
4150 if Self
= gPlayer1
then
4155 if Self
= gPlayer2
then
4162 if g_Game_IsNet
then
4163 MH_SEND_PlayerStats(FUID
);
4166 procedure TPlayer
.SwitchNoClip
;
4170 FGhost
:= not FGhost
;
4171 FPhysics
:= not FGhost
;
4183 procedure TPlayer
.Run(Direction
: TDirection
);
4187 if MAX_RUNVEL
> 8 then
4191 if Direction
= D_LEFT
then
4193 if FObj
.Vel
.X
> -MAX_RUNVEL
then
4194 FObj
.Vel
.X
:= FObj
.Vel
.X
- (MAX_RUNVEL
shr 3);
4197 if FObj
.Vel
.X
< MAX_RUNVEL
then
4198 FObj
.Vel
.X
:= FObj
.Vel
.X
+ (MAX_RUNVEL
shr 3);
4200 // Âîçìîæíî, ïèíàåì êóñêè:
4201 if (FObj
.Vel
.X
<> 0) and (gGibs
<> nil) then
4203 b
:= Abs(FObj
.Vel
.X
);
4204 if b
> 1 then b
:= b
* (Random(8 div b
) + 1);
4205 for a
:= 0 to High(gGibs
) do
4206 if gGibs
[a
].Live
and
4207 g_Obj_Collide(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
+FObj
.Rect
.Height
-4,
4208 FObj
.Rect
.Width
, 8, @gGibs
[a
].Obj
) and (Random(3) = 0) then
4210 if FObj
.Vel
.X
< 0 then
4211 g_Obj_PushA(@gGibs
[a
].Obj
, b
, Random(61)+120) // íàëåâî
4213 g_Obj_PushA(@gGibs
[a
].Obj
, b
, Random(61)); // íàïðàâî
4219 procedure TPlayer
.SeeDown();
4221 SetAction(A_SEEDOWN
);
4223 if FDirection
= D_LEFT
then FAngle
:= ANGLE_LEFTDOWN
else FAngle
:= ANGLE_RIGHTDOWN
;
4225 if FIncCam
> -120 then DecMin(FIncCam
, 5, -120);
4228 procedure TPlayer
.SeeUp();
4232 if FDirection
= D_LEFT
then FAngle
:= ANGLE_LEFTUP
else FAngle
:= ANGLE_RIGHTUP
;
4234 if FIncCam
< 120 then IncMax(FIncCam
, 5, 120);
4237 procedure TPlayer
.SetAction(Action
: Byte; Force
: Boolean = False);
4245 A_ATTACK
: Prior
:= 2;
4246 A_SEEUP
: Prior
:= 1;
4247 A_SEEDOWN
: Prior
:= 1;
4248 A_ATTACKUP
: Prior
:= 2;
4249 A_ATTACKDOWN
: Prior
:= 2;
4254 if (Prior
> FActionPrior
) or Force
then
4255 if not ((Prior
= 2) and (FCurrWeap
= WEAPON_SAW
)) then
4257 FActionPrior
:= Prior
;
4258 FActionAnim
:= Action
;
4259 FActionForce
:= Force
;
4260 FActionChanged
:= True;
4263 if Action
in [A_ATTACK
, A_ATTACKUP
, A_ATTACKDOWN
] then FModel
.SetFire(True);
4266 function TPlayer
.StayOnStep(XInc
, YInc
: Integer): Boolean;
4268 Result
:= not g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+YInc
+PLAYER_RECT
.Y
+PLAYER_RECT
.Height
-1,
4269 PLAYER_RECT
.Width
, 1, PANEL_STEP
, False)
4270 and g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+YInc
+PLAYER_RECT
.Y
+PLAYER_RECT
.Height
,
4271 PLAYER_RECT
.Width
, 1, PANEL_STEP
, False);
4274 function TPlayer
.TeleportTo(X
, Y
: Integer; silent
: Boolean; dir
: Byte): Boolean;
4281 if g_CollideLevel(X
, Y
, PLAYER_RECT
.Width
, PLAYER_RECT
.Height
) then
4283 g_Sound_PlayExAt('SOUND_GAME_NOTELEPORT', FObj
.X
, FObj
.Y
);
4284 if g_Game_IsServer
and g_Game_IsNet
then
4285 MH_SEND_Sound(FObj
.X
, FObj
.Y
, 'SOUND_GAME_NOTELEPORT');
4289 FJustTeleported
:= True;
4294 if g_Frames_Get(ID
, 'FRAMES_TELEPORT') then
4296 Anim
:= TAnimation
.Create(ID
, False, 3);
4299 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', FObj
.X
, FObj
.Y
);
4300 g_GFX_OnceAnim(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4301 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32, Anim
);
4302 if g_Game_IsServer
and g_Game_IsNet
then
4303 MH_SEND_Effect(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4304 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32, 1,
4308 FObj
.X
:= X
-PLAYER_RECT
.X
;
4309 FObj
.Y
:= Y
-PLAYER_RECT
.Y
;
4310 if FLive
and FGhost
then
4316 if not g_Game_IsNet
then
4320 SetDirection(D_LEFT
);
4326 SetDirection(D_RIGHT
);
4332 if FDirection
= D_RIGHT
then
4334 SetDirection(D_LEFT
);
4339 SetDirection(D_RIGHT
);
4345 if not silent
and (Anim
<> nil) then
4347 g_GFX_OnceAnim(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4348 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32, Anim
);
4351 if g_Game_IsServer
and g_Game_IsNet
then
4352 MH_SEND_Effect(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2)-32,
4353 FObj
.Y
+PLAYER_RECT
.Y
+(PLAYER_RECT
.Height
div 2)-32, 0,
4360 function nonz(a
: Single): Single;
4368 procedure TPlayer
.Update();
4371 i
, ii
, wx
, wy
, xd
, yd
, k
: Integer;
4372 blockmon
, headwater
, dospawn
: Boolean;
4377 NetServer
:= g_Game_IsNet
and g_Game_IsServer
;
4378 AnyServer
:= g_Game_IsServer
;
4380 if g_Game_IsClient
and (NetInterpLevel
> 0) then
4381 DoLerp(NetInterpLevel
+ 1)
4387 if FClientID
>= 0 then
4389 FPing
:= NetClients
[FClientID
].Peer
^.lastRoundTripTime
;
4390 if NetClients
[FClientID
].Peer
^.packetsSent
> 0 then
4391 FLoss
:= Round(100*NetClients
[FClientID
].Peer
^.packetsLost
/NetClients
[FClientID
].Peer
^.packetsSent
)
4400 if FLive
and (gFly
or FJetpack
) then
4403 if FDirection
= D_LEFT
then
4408 if FLive
and (not FGhost
) then
4410 if FKeys
[KEY_UP
].Pressed
then
4412 if FKeys
[KEY_DOWN
].Pressed
then
4416 if (not (FKeys
[KEY_UP
].Pressed
or FKeys
[KEY_DOWN
].Pressed
)) and
4419 i
:= g_basic
.Sign(FIncCam
);
4420 FIncCam
:= Abs(FIncCam
);
4421 DecMin(FIncCam
, 5, 0);
4422 FIncCam
:= FIncCam
*i
;
4425 if gTime
mod (GAME_TICK
*2) <> 0 then
4427 if (FObj
.Vel
.X
= 0) and FLive
then
4429 if FKeys
[KEY_LEFT
].Pressed
then
4431 if FKeys
[KEY_RIGHT
].Pressed
then
4436 g_Obj_Move(@FObj
, True, True, True);
4441 FActionChanged
:= False;
4445 // Let alive player do some actions
4446 if FKeys
[KEY_LEFT
].Pressed
then Run(D_LEFT
);
4447 if FKeys
[KEY_RIGHT
].Pressed
then Run(D_RIGHT
);
4448 if FKeys
[KEY_NEXTWEAPON
].Pressed
and AnyServer
then NextWeapon();
4449 if FKeys
[KEY_PREVWEAPON
].Pressed
and AnyServer
then PrevWeapon();
4450 if FKeys
[KEY_FIRE
].Pressed
and AnyServer
then Fire();
4451 if FKeys
[KEY_OPEN
].Pressed
and AnyServer
then Use();
4452 if FKeys
[KEY_JUMP
].Pressed
then Jump()
4455 if AnyServer
and FJetpack
then
4459 if NetServer
then MH_SEND_PlayerStats(FUID
);
4461 FCanJetpack
:= True;
4468 for k
:= Low(FKeys
) to KEY_CHAT
-1 do
4470 if FKeys
[k
].Pressed
then
4478 if gGameSettings
.GameType
in [GT_CUSTOM
, GT_SERVER
, GT_CLIENT
] then
4481 if (FTime
[T_RESPAWN
] <= gTime
) and
4482 gGameOn
and (not FLive
) then
4484 if (g_Player_GetCount() > 1) then
4488 gExit
:= EXIT_RESTART
;
4493 // Dead spectator actions
4496 if FKeys
[KEY_OPEN
].Pressed
and AnyServer
then Fire();
4497 if FKeys
[KEY_FIRE
].Pressed
and AnyServer
then
4501 if (FSpectatePlayer
>= High(gPlayers
)) then
4502 FSpectatePlayer
:= -1
4506 for I
:= FSpectatePlayer
+ 1 to High(gPlayers
) do
4507 if gPlayers
[I
] <> nil then
4508 if gPlayers
[I
].Live
then
4509 if gPlayers
[I
].UID
<> FUID
then
4511 FSpectatePlayer
:= I
;
4516 if not SetSpect
then FSpectatePlayer
:= -1;
4527 if FKeys
[KEY_UP
].Pressed
or FKeys
[KEY_JUMP
].Pressed
then
4529 FYTo
:= FObj
.Y
- 32;
4530 FSpectatePlayer
:= -1;
4532 if FKeys
[KEY_DOWN
].Pressed
then
4534 FYTo
:= FObj
.Y
+ 32;
4535 FSpectatePlayer
:= -1;
4537 if FKeys
[KEY_LEFT
].Pressed
then
4539 FXTo
:= FObj
.X
- 32;
4540 FSpectatePlayer
:= -1;
4542 if FKeys
[KEY_RIGHT
].Pressed
then
4544 FXTo
:= FObj
.X
+ 32;
4545 FSpectatePlayer
:= -1;
4548 if (FXTo
< -64) then
4550 else if (FXTo
> gMapInfo
.Width
+ 32) then
4551 FXTo
:= gMapInfo
.Width
+ 32;
4552 if (FYTo
< -72) then
4554 else if (FYTo
> gMapInfo
.Height
+ 32) then
4555 FYTo
:= gMapInfo
.Height
+ 32;
4559 g_Obj_Move(@FObj
, True, True, True)
4565 if (FSpectatePlayer
<= High(gPlayers
)) and (FSpectatePlayer
>= 0) then
4566 if gPlayers
[FSpectatePlayer
] <> nil then
4567 if gPlayers
[FSpectatePlayer
].Live
then
4569 FXTo
:= gPlayers
[FSpectatePlayer
].GameX
;
4570 FYTo
:= gPlayers
[FSpectatePlayer
].GameY
;
4574 blockmon
:= g_Map_CollidePanel(FObj
.X
+PLAYER_HEADRECT
.X
, FObj
.Y
+PLAYER_HEADRECT
.Y
,
4575 PLAYER_HEADRECT
.Width
, PLAYER_HEADRECT
.Height
,
4576 PANEL_BLOCKMON
, True);
4577 headwater
:= HeadInLiquid(0, 0);
4579 // Ñîïðîòèâëåíèå âîçäóõà:
4580 if (not FLive
) or not (FKeys
[KEY_LEFT
].Pressed
or FKeys
[KEY_RIGHT
].Pressed
) then
4581 if FObj
.Vel
.X
<> 0 then
4582 FObj
.Vel
.X
:= z_dec(FObj
.Vel
.X
, 1);
4584 if (FLastHit
= HIT_TRAP
) and (FPain
> 90) then FPain
:= 90;
4585 DecMin(FPain
, 5, 0);
4586 DecMin(FPickup
, 1, 0);
4588 if FLive
and (FObj
.Y
> gMapInfo
.Height
+128) and AnyServer
then
4590 // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë
4591 FMegaRulez
[MR_SUIT
] := 0;
4592 FMegaRulez
[MR_INVUL
] := 0;
4593 FMegaRulez
[MR_INVIS
] := 0;
4594 Kill(K_FALLKILL
, 0, HIT_FALL
);
4601 if FCurrWeap
= WEAPON_SAW
then
4602 if not (FSawSound
.IsPlaying() or FSawSoundHit
.IsPlaying() or
4603 FSawSoundSelect
.IsPlaying()) then
4604 FSawSoundIdle
.PlayAt(FObj
.X
, FObj
.Y
);
4607 if (not FJetSoundFly
.IsPlaying()) and (not FJetSoundOn
.IsPlaying()) and
4608 (not FJetSoundOff
.IsPlaying()) then
4610 FJetSoundFly
.SetPosition(0);
4611 FJetSoundFly
.PlayAt(FObj
.X
, FObj
.Y
);
4614 for b
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
4615 if FReloading
[b
] > 0 then
4621 if FShellTimer
> -1 then
4622 if FShellTimer
= 0 then
4624 if FShellType
= SHELL_SHELL
then
4625 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4626 GameVelX
, GameVelY
-2, SHELL_SHELL
)
4627 else if FShellType
= SHELL_DBLSHELL
then
4629 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4630 GameVelX
+1, GameVelY
-2, SHELL_SHELL
);
4631 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4632 GameVelX
-1, GameVelY
-2, SHELL_SHELL
);
4635 end else Dec(FShellTimer
);
4637 if (FBFGFireCounter
> -1) then
4638 if FBFGFireCounter
= 0 then
4642 wx
:= FObj
.X
+WEAPONPOINT
[FDirection
].X
;
4643 wy
:= FObj
.Y
+WEAPONPOINT
[FDirection
].Y
;
4644 xd
:= wx
+IfThen(FDirection
= D_LEFT
, -30, 30);
4645 yd
:= wy
+firediry();
4646 g_Weapon_bfgshot(wx
, wy
, xd
, yd
, FUID
);
4647 if NetServer
then MH_SEND_PlayerFire(FUID
, WEAPON_BFG
, wx
, wy
, xd
, yd
);
4648 if (FAngle
= 0) or (FAngle
= 180) then SetAction(A_ATTACK
)
4649 else if (FAngle
= ANGLE_LEFTDOWN
) or (FAngle
= ANGLE_RIGHTDOWN
) then SetAction(A_ATTACKDOWN
)
4650 else if (FAngle
= ANGLE_LEFTUP
) or (FAngle
= ANGLE_RIGHTUP
) then SetAction(A_ATTACKUP
);
4653 FReloading
[WEAPON_BFG
] := WEAPON_RELOAD
[WEAPON_BFG
];
4654 FBFGFireCounter
:= -1;
4657 FBFGFireCounter
:= 0
4659 Dec(FBFGFireCounter
);
4661 if (FMegaRulez
[MR_SUIT
] < gTime
) and AnyServer
then
4663 b
:= g_GetAcidHit(FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+PLAYER_RECT
.Y
, PLAYER_RECT
.Width
, PLAYER_RECT
.Height
);
4665 if (b
> 0) and (gTime
mod (15*GAME_TICK
) = 0) then Damage(b
, 0, 0, 0, HIT_ACID
);
4668 if (headwater
or blockmon
) then
4674 if AnyServer
then Damage(10, 0, 0, 0, HIT_WATER
);
4677 else if (FAir
mod 31 = 0) and not blockmon
then
4679 g_GFX_Bubbles(FObj
.X
+PLAYER_RECT
.X
+(PLAYER_RECT
.Width
div 2), FObj
.Y
+PLAYER_RECT
.Y
-4, 5+Random(6), 8, 4);
4680 if Random(2) = 0 then
4681 g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj
.X
, FObj
.Y
)
4683 g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj
.X
, FObj
.Y
);
4685 end else if FAir
< AIR_DEF
then
4688 if FDamageBuffer
> 0 then
4690 if FDamageBuffer
>= 9 then
4694 if FDamageBuffer
< 30 then i
:= 9
4695 else if FDamageBuffer
< 100 then i
:= 18
4699 ii
:= Round(FDamageBuffer
*FHealth
/ nonz(FArmor
*(3/4)+FHealth
));
4700 FArmor
:= FArmor
-(FDamageBuffer
-ii
);
4701 FHealth
:= FHealth
-ii
;
4704 FHealth
:= FHealth
+FArmor
;
4709 if FHealth
<= 0 then
4710 if FHealth
> -30 then Kill(K_SIMPLEKILL
, FLastSpawnerUID
, FLastHit
)
4711 else if FHealth
> -50 then Kill(K_HARDKILL
, FLastSpawnerUID
, FLastHit
)
4712 else Kill(K_EXTRAHARDKILL
, FLastSpawnerUID
, FLastHit
);
4716 if FDamageBuffer
<= 20 then FModel
.PlaySound(MODELSOUND_PAIN
, 1, FObj
.X
, FObj
.Y
)
4717 else if FDamageBuffer
<= 55 then FModel
.PlaySound(MODELSOUND_PAIN
, 2, FObj
.X
, FObj
.Y
)
4718 else if FDamageBuffer
<= 120 then FModel
.PlaySound(MODELSOUND_PAIN
, 3, FObj
.X
, FObj
.Y
)
4719 else FModel
.PlaySound(MODELSOUND_PAIN
, 4, FObj
.X
, FObj
.Y
);
4726 end; // if FLive then ...
4728 if (FActionAnim
= A_PAIN
) and (FModel
.Animation
<> A_PAIN
) then
4730 FModel
.ChangeAnimation(FActionAnim
, FActionForce
);
4731 FModel
.GetCurrentAnimation
.MinLength
:= i
;
4732 FModel
.GetCurrentAnimationMask
.MinLength
:= i
;
4733 end else FModel
.ChangeAnimation(FActionAnim
, FActionForce
and (FModel
.Animation
<> A_STAND
));
4735 if (FModel
.GetCurrentAnimation
.Played
or ((not FActionChanged
) and (FModel
.Animation
= A_WALK
)))
4736 then SetAction(A_STAND
, True);
4738 if not ((FModel
.Animation
= A_WALK
) and (Abs(FObj
.Vel
.X
) < 4) and not FModel
.Fire
) then FModel
.Update
;
4740 for b
:= Low(FKeys
) to High(FKeys
) do
4741 if FKeys
[b
].Time
= 0 then FKeys
[b
].Pressed
:= False else Dec(FKeys
[b
].Time
);
4744 function TPlayer
.Collide(X
, Y
: Integer; Width
, Height
: Word): Boolean;
4746 Result
:= g_Collide(FObj
.X
+PLAYER_RECT
.X
,
4747 FObj
.Y
+PLAYER_RECT
.Y
,
4754 function TPlayer
.Collide(Panel
: TPanel
): Boolean;
4756 Result
:= g_Collide(FObj
.X
+PLAYER_RECT
.X
,
4757 FObj
.Y
+PLAYER_RECT
.Y
,
4761 Panel
.Width
, Panel
.Height
);
4764 function TPlayer
.Collide(X
, Y
: Integer): Boolean;
4766 X
:= X
-FObj
.X
-PLAYER_RECT
.X
;
4767 Y
:= Y
-FObj
.Y
-PLAYER_RECT
.Y
;
4768 Result
:= (x
>= 0) and (x
<= PLAYER_RECT
.Width
) and
4769 (y
>= 0) and (y
<= PLAYER_RECT
.Height
);
4772 function g_Player_ValidName(Name
: string): Boolean;
4778 if gPlayers
= nil then Exit
;
4780 for a
:= 0 to High(gPlayers
) do
4781 if gPlayers
[a
] <> nil then
4782 if LowerCase(Name
) = LowerCase(gPlayers
[a
].FName
) then
4789 procedure TPlayer
.SetDirection(Direction
: TDirection
);
4793 d
:= FModel
.Direction
;
4795 FModel
.Direction
:= Direction
;
4796 if d
<> Direction
then FModel
.ChangeAnimation(FModel
.Animation
, True);
4798 FDirection
:= Direction
;
4801 function TPlayer
.GetKeys(): Byte;
4805 if R_KEY_RED
in FRulez
then Result
:= KEY_RED
;
4806 if R_KEY_GREEN
in FRulez
then Result
:= Result
or KEY_GREEN
;
4807 if R_KEY_BLUE
in FRulez
then Result
:= Result
or KEY_BLUE
;
4809 if FTeam
= TEAM_RED
then Result
:= Result
or KEY_REDTEAM
;
4810 if FTeam
= TEAM_BLUE
then Result
:= Result
or KEY_BLUETEAM
;
4813 procedure TPlayer
.Use();
4817 if FTime
[T_USE
] > gTime
then Exit
;
4819 g_Triggers_PressR(FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+PLAYER_RECT
.Y
, PLAYER_RECT
.Width
,
4820 PLAYER_RECT
.Height
, FUID
, ACTIVATE_PLAYERPRESS
);
4822 for a
:= 0 to High(gPlayers
) do
4823 if (gPlayers
[a
] <> nil) and (gPlayers
[a
] <> Self
) and
4824 gPlayers
[a
].Live
and SameTeam(FUID
, gPlayers
[a
].FUID
) and
4825 g_Obj_Collide(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
,
4826 FObj
.Rect
.Width
, FObj
.Rect
.Height
, @gPlayers
[a
].FObj
) then
4828 gPlayers
[a
].Touch();
4829 if g_Game_IsNet
and g_Game_IsServer
then
4830 MH_SEND_GameEvent(NET_EV_PLAYER_TOUCH
, gPlayers
[a
].FUID
);
4833 FTime
[T_USE
] := gTime
+120;
4836 procedure TPlayer
.NetFire(Wpn
: Byte; X
, Y
, AX
, AY
: Integer; WID
: Integer = -1);
4840 WX
, WY
, XD
, YD
: Integer;
4851 if R_BERSERK
in FRulez
then
4853 //g_Weapon_punch(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, 75, FUID);
4854 obj
.X
:= FObj
.X
+FObj
.Rect
.X
;
4855 obj
.Y
:= FObj
.Y
+FObj
.Rect
.Y
;
4858 obj
.rect
.Width
:= 39;
4859 obj
.rect
.Height
:= 52;
4860 obj
.Vel
.X
:= (xd
-wx
) div 2;
4861 obj
.Vel
.Y
:= (yd
-wy
) div 2;
4862 obj
.Accel
.X
:= xd
-wx
;
4863 obj
.Accel
.y
:= yd
-wy
;
4865 if g_Weapon_Hit(@obj
, 50, FUID
, HIT_SOME
) <> 0 then
4866 g_Sound_PlayExAt('SOUND_WEAPON_HITBERSERK', FObj
.X
, FObj
.Y
)
4868 g_Sound_PlayExAt('SOUND_WEAPON_MISSBERSERK', FObj
.X
, FObj
.Y
);
4872 FPain
:= min(FPain
+ 25, 50);
4874 g_Weapon_punch(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
, 3, FUID
);
4879 if g_Weapon_chainsaw(FObj
.X
+FObj
.Rect
.X
, FObj
.Y
+FObj
.Rect
.Y
,
4880 IfThen(gGameSettings
.GameMode
in [GM_DM
, GM_TDM
, GM_CTF
], 9, 3), FUID
) <> 0 then
4882 FSawSoundSelect
.Stop();
4884 FSawSoundHit
.PlayAt(FObj
.X
, FObj
.Y
);
4886 else if not FSawSoundHit
.IsPlaying() then
4888 FSawSoundSelect
.Stop();
4889 FSawSound
.PlayAt(FObj
.X
, FObj
.Y
);
4896 g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', GameX
, Gamey
);
4897 FFireAngle
:= FAngle
;
4899 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4900 GameVelX
, GameVelY
-2, SHELL_BULLET
);
4905 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', Gamex
, Gamey
);
4906 FFireAngle
:= FAngle
;
4909 FShellType
:= SHELL_SHELL
;
4914 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', Gamex
, Gamey
);
4915 FFireAngle
:= FAngle
;
4918 FShellType
:= SHELL_DBLSHELL
;
4923 g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', Gamex
, Gamey
);
4924 FFireAngle
:= FAngle
;
4926 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4927 GameVelX
, GameVelY
-2, SHELL_BULLET
);
4930 WEAPON_ROCKETLAUNCHER
:
4932 g_Weapon_Rocket(wx
, wy
, xd
, yd
, FUID
, WID
);
4933 FFireAngle
:= FAngle
;
4939 g_Weapon_Plasma(wx
, wy
, xd
, yd
, FUID
, WID
);
4940 FFireAngle
:= FAngle
;
4946 g_Weapon_BFGShot(wx
, wy
, xd
, yd
, FUID
, WID
);
4947 FFireAngle
:= FAngle
;
4951 WEAPON_SUPERPULEMET
:
4953 g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', Gamex
, Gamey
);
4954 FFireAngle
:= FAngle
;
4956 g_Player_CreateShell(GameX
+PLAYER_RECT_CX
, GameY
+PLAYER_RECT_CX
,
4957 GameVelX
, GameVelY
-2, SHELL_SHELL
);
4963 if (FAngle
= 0) or (FAngle
= 180) then SetAction(A_ATTACK
)
4964 else if (FAngle
= ANGLE_LEFTDOWN
) or (FAngle
= ANGLE_RIGHTDOWN
) then SetAction(A_ATTACKDOWN
)
4965 else if (FAngle
= ANGLE_LEFTUP
) or (FAngle
= ANGLE_RIGHTUP
) then SetAction(A_ATTACKUP
);
4968 procedure TPlayer
.DoLerp(Level
: Integer = 2);
4970 if FObj
.X
<> FXTo
then FObj
.X
:= Lerp(FObj
.X
, FXTo
, Level
);
4971 if FObj
.Y
<> FYTo
then FObj
.Y
:= Lerp(FObj
.Y
, FYTo
, Level
);
4974 procedure TPlayer
.SetLerp(XTo
, YTo
: Integer);
4978 if NetInterpLevel
< 1 then
4988 AX
:= Abs(FXTo
- FObj
.X
);
4989 AY
:= Abs(FYTo
- FObj
.Y
);
4990 if (AX
> 32) or (AX
<= NetInterpLevel
) then
4992 if (AY
> 32) or (AY
<= NetInterpLevel
) then
4997 function TPlayer
.FullInLift(XInc
, YInc
: Integer): Integer;
4999 if g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
,
5000 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
-8,
5001 PANEL_LIFTUP
, False) then Result
:= -1
5003 if g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
,
5004 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
-8,
5005 PANEL_LIFTDOWN
, False) then Result
:= 1
5009 function TPlayer
.GetFlag(Flag
: Byte): Boolean;
5016 if Flag
= FLAG_NONE
then
5019 if not g_Game_IsServer
then Exit
;
5021 // Ïðèíåñ ÷óæîé ôëàã íà ñâîþ áàçó:
5022 if (Flag
= FTeam
) and
5023 (gFlags
[Flag
].State
= FLAG_STATE_NORMAL
) and
5024 (FFlag
<> FLAG_NONE
) then
5026 if FFlag
= FLAG_RED
then
5027 s
:= _lc
[I_PLAYER_FLAG_RED
]
5029 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
5031 evtype
:= FLAG_STATE_SCORED
;
5033 ts
:= Format('%.4d', [gFlags
[FFlag
].CaptureTime
]);
5034 Insert('.', ts
, Length(ts
) + 1 - 3);
5035 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_CAPTURE
], [FName
, s
, ts
]), True);
5037 g_Map_ResetFlag(FFlag
);
5038 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_CAPTURE
], [AnsiUpperCase(s
)]), 144);
5040 gTeamStat
[FTeam
].Goals
:= gTeamStat
[FTeam
].Goals
+ 1;
5043 if g_Game_IsNet
then
5045 MH_SEND_FlagEvent(evtype
, FFlag
, FUID
, False);
5049 gFlags
[FFlag
].CaptureTime
:= 0;
5054 // Ïîäîáðàë ñâîé ôëàã - âåðíóë åãî íà áàçó:
5055 if (Flag
= FTeam
) and
5056 (gFlags
[Flag
].State
= FLAG_STATE_DROPPED
) then
5058 if Flag
= FLAG_RED
then
5059 s
:= _lc
[I_PLAYER_FLAG_RED
]
5061 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
5063 evtype
:= FLAG_STATE_RETURNED
;
5064 gFlags
[Flag
].CaptureTime
:= 0;
5066 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_RETURN
], [FName
, s
]), True);
5068 g_Map_ResetFlag(Flag
);
5069 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_RETURN
], [AnsiUpperCase(s
)]), 144);
5072 if g_Game_IsNet
then
5074 MH_SEND_FlagEvent(evtype
, Flag
, FUID
, False);
5080 // Ïîäîáðàë ÷óæîé ôëàã:
5081 if (Flag
<> FTeam
) and (FTime
[T_FLAGCAP
] <= gTime
) then
5085 if Flag
= FLAG_RED
then
5086 s
:= _lc
[I_PLAYER_FLAG_RED
]
5088 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
5090 evtype
:= FLAG_STATE_CAPTURED
;
5092 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_GET
], [FName
, s
]), True);
5094 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_GET
], [AnsiUpperCase(s
)]), 144);
5096 gFlags
[Flag
].State
:= FLAG_STATE_CAPTURED
;
5099 if g_Game_IsNet
then
5101 MH_SEND_FlagEvent(evtype
, Flag
, FUID
, False);
5107 procedure TPlayer
.SetFlag(Flag
: Byte);
5110 if FModel
<> nil then
5111 FModel
.SetFlag(FFlag
);
5114 function TPlayer
.DropFlag(): Boolean;
5119 if (not g_Game_IsServer
) or (FFlag
= FLAG_NONE
) then
5121 FTime
[T_FLAGCAP
] := gTime
+ 2000;
5122 with gFlags
[FFlag
] do
5126 Direction
:= FDirection
;
5127 State
:= FLAG_STATE_DROPPED
;
5129 g_Obj_Push(@Obj
, (FObj
.Vel
.X
div 2)-2+Random(5),
5130 (FObj
.Vel
.Y
div 2)-2+Random(5));
5132 if FFlag
= FLAG_RED
then
5133 s
:= _lc
[I_PLAYER_FLAG_RED
]
5135 s
:= _lc
[I_PLAYER_FLAG_BLUE
];
5137 g_Console_Add(Format(_lc
[I_PLAYER_FLAG_DROP
], [FName
, s
]), True);
5138 g_Game_Message(Format(_lc
[I_MESSAGE_FLAG_DROP
], [AnsiUpperCase(s
)]), 144);
5140 if g_Game_IsNet
then
5141 MH_SEND_FlagEvent(FLAG_STATE_DROPPED
, Flag
, FUID
, False);
5147 procedure TPlayer
.GetSecret();
5152 procedure TPlayer
.PressKey(Key
: Byte; Time
: Word = 1);
5154 Assert(Key
<= High(FKeys
));
5156 FKeys
[Key
].Pressed
:= True;
5157 FKeys
[Key
].Time
:= Time
;
5160 function TPlayer
.IsKeyPressed(K
: Byte): Boolean;
5162 Result
:= FKeys
[K
].Pressed
;
5165 procedure TPlayer
.ReleaseKeys();
5169 for a
:= Low(FKeys
) to High(FKeys
) do
5171 FKeys
[a
].Pressed
:= False;
5176 procedure TPlayer
.OnDamage(Angle
: SmallInt);
5180 function TPlayer
.firediry(): Integer;
5182 if FKeys
[KEY_UP
].Pressed
then Result
:= -42
5183 else if FKeys
[KEY_DOWN
].Pressed
then Result
:= 19
5187 procedure TPlayer
.RememberState();
5191 FSavedState
.Health
:= FHealth
;
5192 FSavedState
.Armor
:= FArmor
;
5193 FSavedState
.Air
:= FAir
;
5194 FSavedState
.JetFuel
:= FJetFuel
;
5195 FSavedState
.CurrWeap
:= FCurrWeap
;
5198 FSavedState
.Ammo
[i
] := FAmmo
[i
];
5200 FSavedState
.MaxAmmo
[i
] := FMaxAmmo
[i
];
5202 FSavedState
.Rulez
:= FRulez
;
5203 FSavedState
.WaitRecall
:= True;
5206 procedure TPlayer
.RecallState();
5210 if not FSavedState
.WaitRecall
then Exit
;
5212 FHealth
:= FSavedState
.Health
;
5213 FArmor
:= FSavedState
.Armor
;
5214 FAir
:= FSavedState
.Air
;
5215 FJetFuel
:= FSavedState
.JetFuel
;
5216 FCurrWeap
:= FSavedState
.CurrWeap
;
5219 FAmmo
[i
] := FSavedState
.Ammo
[i
];
5221 FMaxAmmo
[i
] := FSavedState
.MaxAmmo
[i
];
5223 FRulez
:= FSavedState
.Rulez
;
5224 FSavedState
.WaitRecall
:= False;
5226 if gGameSettings
.GameType
= GT_SERVER
then
5227 MH_SEND_PlayerStats(FUID
);
5230 procedure TPlayer
.SaveState(var Mem
: TBinMemoryWriter
);
5242 Mem
:= TBinMemoryWriter
.Create(i
);
5244 // Ñèãíàòóðà èãðîêà:
5245 sig
:= PLAYER_SIGNATURE
; // 'PLYR'
5246 Mem
.WriteDWORD(sig
);
5248 Mem
.WriteBoolean(FIamBot
);
5250 Mem
.WriteWord(FUID
);
5252 Mem
.WriteString(FName
, 32);
5254 Mem
.WriteByte(FTeam
);
5256 Mem
.WriteBoolean(FLive
);
5257 // Èçðàñõîäîâàë ëè âñå æèçíè:
5258 Mem
.WriteBoolean(FNoRespawn
);
5260 if FDirection
= D_LEFT
then
5266 Mem
.WriteInt(FHealth
);
5268 Mem
.WriteByte(FLives
);
5270 Mem
.WriteInt(FArmor
);
5274 Mem
.WriteInt(FJetFuel
);
5276 Mem
.WriteInt(FPain
);
5278 Mem
.WriteInt(FKills
);
5280 Mem
.WriteInt(FMonsterKills
);
5282 Mem
.WriteInt(FFrags
);
5284 Mem
.WriteByte(FFragCombo
);
5285 // Âðåìÿ ïîñëåäíåãî ôðàãà:
5286 Mem
.WriteDWORD(FLastFrag
);
5288 Mem
.WriteInt(FDeath
);
5289 // Êàêîé ôëàã íåñåò:
5290 Mem
.WriteByte(FFlag
);
5292 Mem
.WriteInt(FSecrets
);
5294 Mem
.WriteByte(FCurrWeap
);
5295 // Âðåìÿ çàðÿäêè BFG:
5296 Mem
.WriteSmallInt(FBFGFireCounter
);
5298 Mem
.WriteInt(FDamageBuffer
);
5299 // Ïîñëåäíèé óäàðèâøèé:
5300 Mem
.WriteWord(FLastSpawnerUID
);
5301 // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
5302 Mem
.WriteByte(FLastHit
);
5304 Obj_SaveState(@FObj
, Mem
);
5305 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
5306 for i
:= A_BULLETS
to A_CELLS
do
5307 Mem
.WriteWord(FAmmo
[i
]);
5308 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
5309 for i
:= A_BULLETS
to A_CELLS
do
5310 Mem
.WriteWord(FMaxAmmo
[i
]);
5312 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
5313 Mem
.WriteBoolean(FWeapon
[i
]);
5314 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
5315 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
5316 Mem
.WriteWord(FReloading
[i
]);
5318 if R_ITEM_BACKPACK
in FRulez
then
5323 // Íàëè÷èå êðàñíîãî êëþ÷à:
5324 if R_KEY_RED
in FRulez
then
5329 // Íàëè÷èå çåëåíîãî êëþ÷à:
5330 if R_KEY_GREEN
in FRulez
then
5335 // Íàëè÷èå ñèíåãî êëþ÷à:
5336 if R_KEY_BLUE
in FRulez
then
5341 // Íàëè÷èå áåðñåðêà:
5342 if R_BERSERK
in FRulez
then
5347 // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
5348 for i
:= MR_SUIT
to MR_MAX
do
5349 Mem
.WriteDWORD(FMegaRulez
[i
]);
5350 // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
5351 for i
:= T_RESPAWN
to T_FLAGCAP
do
5352 Mem
.WriteDWORD(FTime
[i
]);
5355 Mem
.WriteString(str
);
5365 procedure TPlayer
.LoadState(var Mem
: TBinMemoryReader
);
5375 // Ñèãíàòóðà èãðîêà:
5377 if sig
<> PLAYER_SIGNATURE
then // 'PLYR'
5379 raise EBinSizeError
.Create('TPlayer.LoadState: Wrong Player Signature');
5382 Mem
.ReadBoolean(FIamBot
);
5386 Mem
.ReadString(str
);
5387 if (Self
<> gPlayer1
) and (Self
<> gPlayer2
) then
5390 Mem
.ReadByte(FTeam
);
5392 Mem
.ReadBoolean(FLive
);
5393 // Èçðàñõîäîâàë ëè âñå æèçíè:
5394 Mem
.ReadBoolean(FNoRespawn
);
5398 FDirection
:= D_LEFT
5400 FDirection
:= D_RIGHT
;
5402 Mem
.ReadInt(FHealth
);
5404 Mem
.ReadByte(FLives
);
5406 Mem
.ReadInt(FArmor
);
5410 Mem
.ReadInt(FJetFuel
);
5414 Mem
.ReadInt(FKills
);
5416 Mem
.ReadInt(FMonsterKills
);
5418 Mem
.ReadInt(FFrags
);
5420 Mem
.ReadByte(FFragCombo
);
5421 // Âðåìÿ ïîñëåäíåãî ôðàãà:
5422 Mem
.ReadDWORD(FLastFrag
);
5424 Mem
.ReadInt(FDeath
);
5425 // Êàêîé ôëàã íåñåò:
5426 Mem
.ReadByte(FFlag
);
5428 Mem
.ReadInt(FSecrets
);
5430 Mem
.ReadByte(FCurrWeap
);
5431 // Âðåìÿ çàðÿäêè BFG:
5432 Mem
.ReadSmallInt(FBFGFireCounter
);
5434 Mem
.ReadInt(FDamageBuffer
);
5435 // Ïîñëåäíèé óäàðèâøèé:
5436 Mem
.ReadWord(FLastSpawnerUID
);
5437 // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
5438 Mem
.ReadByte(FLastHit
);
5440 Obj_LoadState(@FObj
, Mem
);
5441 // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
5442 for i
:= A_BULLETS
to A_CELLS
do
5443 Mem
.ReadWord(FAmmo
[i
]);
5444 // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
5445 for i
:= A_BULLETS
to A_CELLS
do
5446 Mem
.ReadWord(FMaxAmmo
[i
]);
5448 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
5449 Mem
.ReadBoolean(FWeapon
[i
]);
5450 // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
5451 for i
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
5452 Mem
.ReadWord(FReloading
[i
]);
5456 Include(FRulez
, R_ITEM_BACKPACK
);
5457 // Íàëè÷èå êðàñíîãî êëþ÷à:
5460 Include(FRulez
, R_KEY_RED
);
5461 // Íàëè÷èå çåëåíîãî êëþ÷à:
5464 Include(FRulez
, R_KEY_GREEN
);
5465 // Íàëè÷èå ñèíåãî êëþ÷à:
5468 Include(FRulez
, R_KEY_BLUE
);
5469 // Íàëè÷èå áåðñåðêà:
5472 Include(FRulez
, R_BERSERK
);
5473 // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
5474 for i
:= MR_SUIT
to MR_MAX
do
5475 Mem
.ReadDWORD(FMegaRulez
[i
]);
5476 // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
5477 for i
:= T_RESPAWN
to T_FLAGCAP
do
5478 Mem
.ReadDWORD(FTime
[i
]);
5480 Mem
.ReadString(str
);
5482 Mem
.ReadByte(FColor
.R
);
5483 Mem
.ReadByte(FColor
.G
);
5484 Mem
.ReadByte(FColor
.B
);
5485 if Self
= gPlayer1
then
5487 str
:= gPlayer1Settings
.Model
;
5488 FColor
:= gPlayer1Settings
.Color
;
5490 if Self
= gPlayer2
then
5492 str
:= gPlayer2Settings
.Model
;
5493 FColor
:= gPlayer2Settings
.Color
;
5495 // Îáíîâëÿåì ìîäåëü èãðîêà:
5497 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
5498 FModel
.Color
:= TEAMCOLOR
[FTeam
]
5500 FModel
.Color
:= FColor
;
5503 procedure TPlayer
.AllRulez(Health
: Boolean);
5509 FHealth
:= PLAYER_HP_LIMIT
;
5510 FArmor
:= PLAYER_AP_LIMIT
;
5514 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do FWeapon
[a
] := True;
5515 for a
:= A_BULLETS
to A_CELLS
do FAmmo
[a
] := 30000;
5516 FRulez
:= FRulez
+[R_KEY_RED
, R_KEY_GREEN
, R_KEY_BLUE
];
5519 procedure TPlayer
.RestoreHealthArmor();
5521 FHealth
:= PLAYER_HP_LIMIT
;
5522 FArmor
:= PLAYER_AP_LIMIT
;
5525 procedure TPlayer
.FragCombo();
5529 if (gGameSettings
.GameMode
in [GM_COOP
, GM_SINGLE
]) or g_Game_IsClient
then
5531 if gTime
- FLastFrag
< FRAG_COMBO_TIME
then
5533 if FFragCombo
< 5 then
5535 Param
:= FUID
or (FFragCombo
shl 16);
5536 if (FComboEvnt
>= Low(gDelayedEvents
)) and
5537 (FComboEvnt
<= High(gDelayedEvents
)) and
5538 gDelayedEvents
[FComboEvnt
].Pending
and
5539 (gDelayedEvents
[FComboEvnt
].DEType
= DE_KILLCOMBO
) and
5540 (gDelayedEvents
[FComboEvnt
].DENum
and $FFFF = FUID
) then
5542 gDelayedEvents
[FComboEvnt
].Time
:= gTime
+ 500;
5543 gDelayedEvents
[FComboEvnt
].DENum
:= Param
;
5546 FComboEvnt
:= g_Game_DelayEvent(DE_KILLCOMBO
, 500, Param
);
5554 procedure TPlayer
.GiveItem(ItemType
: Byte);
5558 if FMegaRulez
[MR_SUIT
] < gTime
+PLAYER_SUIT_TIME
then
5560 FMegaRulez
[MR_SUIT
] := gTime
+PLAYER_SUIT_TIME
;
5564 if FAir
< AIR_MAX
then
5571 if not (R_BERSERK
in FRulez
) then
5573 Include(FRulez
, R_BERSERK
);
5574 if FBFGFireCounter
< 1 then
5576 FCurrWeap
:= WEAPON_KASTET
;
5577 FModel
.SetWeapon(WEAPON_KASTET
);
5581 FBerserk
:= gTime
+30000;
5583 if FHealth
< PLAYER_HP_SOFT
then
5585 FHealth
:= PLAYER_HP_SOFT
;
5586 FBerserk
:= gTime
+30000;
5591 if FMegaRulez
[MR_INVUL
] < gTime
+PLAYER_INVUL_TIME
then
5593 FMegaRulez
[MR_INVUL
] := gTime
+PLAYER_INVUL_TIME
;
5597 if FMegaRulez
[MR_INVIS
] < gTime
+PLAYER_INVIS_TIME
then
5599 FMegaRulez
[MR_INVIS
] := gTime
+PLAYER_INVIS_TIME
;
5603 if FJetFuel
< JET_MAX
then
5605 FJetFuel
:= JET_MAX
;
5611 if g_Game_IsNet
and g_Game_IsServer
then
5612 MH_SEND_PlayerStats(FUID
);
5615 procedure TPlayer
.FlySmoke(Times
: DWORD
= 1);
5620 if (Random(5) = 1) and (Times
= 1) then
5623 if BodyInLiquid(0, 0) then
5625 g_GFX_Bubbles(Obj
.X
+Obj
.Rect
.X
+(Obj
.Rect
.Width
div 2)+Random(3)-1,
5626 Obj
.Y
+Obj
.Rect
.Height
+8, 1, 8, 4);
5627 if Random(2) = 0 then
5628 g_Sound_PlayExAt('SOUND_GAME_BUBBLE1', FObj
.X
, FObj
.Y
)
5630 g_Sound_PlayExAt('SOUND_GAME_BUBBLE2', FObj
.X
, FObj
.Y
);
5634 if g_Frames_Get(id
, 'FRAMES_SMOKE') then
5636 for i
:= 1 to Times
do
5638 Anim
:= TAnimation
.Create(id
, False, 3);
5640 g_GFX_OnceAnim(Obj
.X
+Obj
.Rect
.X
+Random(Obj
.Rect
.Width
+Times
*2)-(Anim
.Width
div 2),
5641 Obj
.Y
+Obj
.Rect
.Height
-4+Random(8+Times
*2), Anim
, ONCEANIM_SMOKE
);
5647 procedure TPlayer
.PauseSounds(Enable
: Boolean);
5649 FSawSound
.Pause(Enable
);
5650 FSawSoundIdle
.Pause(Enable
);
5651 FSawSoundHit
.Pause(Enable
);
5652 FSawSoundSelect
.Pause(Enable
);
5657 constructor TCorpse
.Create(X
, Y
: Integer; ModelName
: String; aMess
: Boolean);
5662 FObj
.Rect
:= PLAYER_CORPSERECT
;
5663 FModelName
:= ModelName
;
5668 FState
:= CORPSE_STATE_MESS
;
5669 g_PlayerModel_GetAnim(ModelName
, A_DIE2
, FAnimation
, FAnimationMask
);
5673 FState
:= CORPSE_STATE_NORMAL
;
5674 g_PlayerModel_GetAnim(ModelName
, A_DIE1
, FAnimation
, FAnimationMask
);
5678 destructor TCorpse
.Destroy();
5685 procedure TCorpse
.Damage(Value
: Word; vx
, vy
: Integer);
5689 if FState
= CORPSE_STATE_REMOVEME
then
5692 FDamage
:= FDamage
+ Value
;
5694 if FDamage
> 150 then
5696 if FAnimation
<> nil then
5701 FState
:= CORPSE_STATE_REMOVEME
;
5703 g_Player_CreateGibs(FObj
.X
+FObj
.Rect
.X
+(FObj
.Rect
.Width
div 2),
5704 FObj
.Y
+FObj
.Rect
.Y
+(FObj
.Rect
.Height
div 2),
5705 FModelName
, FColor
);
5706 // Çâóê ìÿñà îò òðóïà:
5707 pm
:= g_PlayerModel_Get(FModelName
);
5708 pm
.PlaySound(MODELSOUND_DIE
, 5, FObj
.X
, FObj
.Y
);
5714 FObj
.Vel
.X
:= FObj
.Vel
.X
+ vx
;
5715 FObj
.Vel
.Y
:= FObj
.Vel
.Y
+ vy
;
5716 g_GFX_Blood(FObj
.X
+PLAYER_CORPSERECT
.X
+(PLAYER_CORPSERECT
.Width
div 2),
5717 FObj
.Y
+PLAYER_CORPSERECT
.Y
+(PLAYER_CORPSERECT
.Height
div 2),
5718 Value
, vx
, vy
, 16, (PLAYER_CORPSERECT
.Height
*2) div 3,
5723 procedure TCorpse
.Draw();
5725 if FState
= CORPSE_STATE_REMOVEME
then
5728 if FAnimation
<> nil then
5729 FAnimation
.Draw(FObj
.X
, FObj
.Y
, M_NONE
);
5731 if FAnimationMask
<> nil then
5734 FAnimationMask
.Draw(FObj
.X
, FObj
.Y
, M_NONE
);
5741 procedure TCorpse
.Update();
5745 if FState
= CORPSE_STATE_REMOVEME
then
5748 if gTime
mod (GAME_TICK
*2) <> 0 then
5750 g_Obj_Move(@FObj
, True, True, True);
5755 // Ñîïðîòèâëåíèå âîçäóõà äëÿ òðóïà:
5756 FObj
.Vel
.X
:= z_dec(FObj
.Vel
.X
, 1);
5758 st
:= g_Obj_Move(@FObj
, True, True, True);
5760 if WordBool(st
and MOVE_FALLOUT
) then
5762 FState
:= CORPSE_STATE_REMOVEME
;
5766 if FAnimation
<> nil then
5767 FAnimation
.Update();
5768 if FAnimationMask
<> nil then
5769 FAnimationMask
.Update();
5772 procedure TCorpse
.SaveState(var Mem
: TBinMemoryWriter
);
5781 sig
:= CORPSE_SIGNATURE
; // 'CORP'
5782 Mem
.WriteDWORD(sig
);
5784 Mem
.WriteByte(FState
);
5785 // Íàêîïëåííûé óðîí:
5786 Mem
.WriteByte(FDamage
);
5788 Mem
.WriteByte(FColor
.R
);
5789 Mem
.WriteByte(FColor
.G
);
5790 Mem
.WriteByte(FColor
.B
);
5792 Obj_SaveState(@FObj
, Mem
);
5793 // Åñòü ëè àíèìàöèÿ:
5794 anim
:= FAnimation
<> nil;
5795 Mem
.WriteBoolean(anim
);
5796 // Åñëè åñòü - ñîõðàíÿåì:
5798 FAnimation
.SaveState(Mem
);
5799 // Åñòü ëè ìàñêà àíèìàöèè:
5800 anim
:= FAnimationMask
<> nil;
5801 Mem
.WriteBoolean(anim
);
5802 // Åñëè åñòü - ñîõðàíÿåì:
5804 FAnimationMask
.SaveState(Mem
);
5807 procedure TCorpse
.LoadState(var Mem
: TBinMemoryReader
);
5817 if sig
<> CORPSE_SIGNATURE
then // 'CORP'
5819 raise EBinSizeError
.Create('TCorpse.LoadState: Wrong Corpse Signature');
5822 Mem
.ReadByte(FState
);
5823 // Íàêîïëåííûé óðîí:
5824 Mem
.ReadByte(FDamage
);
5826 Mem
.ReadByte(FColor
.R
);
5827 Mem
.ReadByte(FColor
.G
);
5828 Mem
.ReadByte(FColor
.B
);
5830 Obj_LoadState(@FObj
, Mem
);
5831 // Åñòü ëè àíèìàöèÿ:
5832 Mem
.ReadBoolean(anim
);
5833 // Åñëè åñòü - çàãðóæàåì:
5836 Assert(FAnimation
<> nil, 'TCorpse.LoadState: no FAnimation');
5837 FAnimation
.LoadState(Mem
);
5839 // Åñòü ëè ìàñêà àíèìàöèè:
5840 Mem
.ReadBoolean(anim
);
5841 // Åñëè åñòü - çàãðóæàåì:
5844 Assert(FAnimationMask
<> nil, 'TCorpse.LoadState: no FAnimationMask');
5845 FAnimationMask
.LoadState(Mem
);
5851 constructor TBot
.Create();
5858 FSpectator
:= False;
5865 for a
:= WEAPON_KASTET
to WEAPON_SUPERPULEMET
do
5867 FDifficult
.WeaponPrior
[a
] := WEAPON_PRIOR1
[a
];
5868 FDifficult
.CloseWeaponPrior
[a
] := WEAPON_PRIOR2
[a
];
5869 //FDifficult.SafeWeaponPrior[a] := WEAPON_PRIOR3[a];
5873 destructor TBot
.Destroy();
5876 inherited Destroy();
5879 procedure TBot
.Draw();
5883 //if FTargetUID <> 0 then e_DrawLine(1, FObj.X, FObj.Y, g_Player_Get(FTargetUID).FObj.X,
5884 // g_Player_Get(FTargetUID).FObj.Y, 255, 0, 0);
5887 procedure TBot
.Respawn(Silent
: Boolean; Force
: Boolean = False);
5889 inherited Respawn(Silent
, Force
);
5892 FSelectedWeapon
:= FCurrWeap
;
5896 procedure TBot
.UpdateCombat();
5909 TTargetRecord
= array of TTarget
;
5911 function Compare(a
, b
: TTarget
): Integer;
5913 if a
.Line
and not b
.Line
then // A íà ëèíèè îãíÿ
5916 if not a
.Line
and b
.Line
then // B íà ëèíèè îãíÿ
5918 else // È A, è B íà ëèíèè èëè íå íà ëèíèè îãíÿ
5919 if (a
.Line
and b
.Line
) or ((not a
.Line
) and (not b
.Line
)) then
5921 if a
.Dist
> b
.Dist
then // B áëèæå
5923 else // A áëèæå èëè ðàâíîóäàëåííî ñ B
5926 else // Ñòðàííî -> A
5931 a
, x1
, y1
, x2
, y2
: Integer;
5932 targets
: TTargetRecord
;
5934 Target
, BestTarget
: TTarget
;
5935 firew
, fireh
: Integer;
5939 vsPlayer
, vsMonster
, ok
: Boolean;
5941 vsPlayer
:= LongBool(gGameSettings
.Options
and GAME_OPTION_BOTVSPLAYER
);
5942 vsMonster
:= LongBool(gGameSettings
.Options
and GAME_OPTION_BOTVSMONSTER
);
5944 // Åñëè òåêóùåå îðóæèå íå òî, ÷òî íóæíî, òî ìåíÿåì:
5945 if FCurrWeap
<> FSelectedWeapon
then
5948 // Åñëè íóæíî ñòðåëÿòü è íóæíîå îðóæèå, òî íàæàòü "Ñòðåëÿòü":
5949 if (GetAIFlag('NEEDFIRE') <> '') and (FCurrWeap
= FSelectedWeapon
) then
5951 RemoveAIFlag('NEEDFIRE');
5954 WEAPON_PLASMA
, WEAPON_SUPERPULEMET
, WEAPON_CHAINGUN
: PressKey(KEY_FIRE
, 20);
5955 WEAPON_SAW
, WEAPON_KASTET
, WEAPON_MEGAKASTET
: PressKey(KEY_FIRE
, 40);
5956 else PressKey(KEY_FIRE
);
5960 // Êîîðäèíàòû ñòâîëà:
5961 x1
:= FObj
.X
+ WEAPONPOINT
[FDirection
].X
;
5962 y1
:= FObj
.Y
+ WEAPONPOINT
[FDirection
].Y
;
5964 Target
.UID
:= FTargetUID
;
5967 if Target
.UID
<> 0 then
5968 begin // Öåëü åñòü - íàñòðàèâàåì
5969 if (g_GetUIDType(Target
.UID
) = UID_PLAYER
) and
5972 with g_Player_Get(Target
.UID
) do
5974 if (@FObj
) <> nil then
5981 Target
.cX
:= Target
.X
+ PLAYER_RECT_CX
;
5982 Target
.cY
:= Target
.Y
+ PLAYER_RECT_CY
;
5983 Target
.Rect
:= PLAYER_RECT
;
5984 Target
.Visible
:= g_TraceVector(x1
, y1
, Target
.cX
, Target
.cY
);
5985 Target
.Line
:= (y1
+4 < Target
.Y
+PLAYER_RECT
.Y
+PLAYER_RECT
.Height
) and
5986 (y1
-4 > Target
.Y
+PLAYER_RECT
.Y
);
5987 Target
.IsPlayer
:= True;
5991 if (g_GetUIDType(Target
.UID
) = UID_MONSTER
) and
5994 mon
:= g_Monsters_Get(Target
.UID
);
5997 Target
.X
:= mon
.Obj
.X
;
5998 Target
.Y
:= mon
.Obj
.Y
;
6000 Target
.cX
:= Target
.X
+ mon
.Obj
.Rect
.X
+ (mon
.Obj
.Rect
.Width
div 2);
6001 Target
.cY
:= Target
.Y
+ mon
.Obj
.Rect
.Y
+ (mon
.Obj
.Rect
.Height
div 2);
6002 Target
.Rect
:= mon
.Obj
.Rect
;
6003 Target
.Visible
:= g_TraceVector(x1
, y1
, Target
.cX
, Target
.cY
);
6004 Target
.Line
:= (y1
+4 < Target
.Y
+ mon
.Obj
.Rect
.Y
+ mon
.Obj
.Rect
.Height
) and
6005 (y1
-4 > Target
.Y
+ mon
.Obj
.Rect
.Y
);
6006 Target
.IsPlayer
:= False;
6013 begin // Öåëè íåò - îáíóëÿåì
6018 Target
.Visible
:= False;
6019 Target
.Line
:= False;
6020 Target
.IsPlayer
:= False;
6025 // Åñëè öåëü íå âèäèìà èëè íå íà ëèíèè îãíÿ, òî èùåì âñå âîçìîæíûå öåëè:
6026 if (not Target
.Line
) or (not Target
.Visible
) then
6030 for a
:= 0 to High(gPlayers
) do
6031 if (gPlayers
[a
] <> nil) and (gPlayers
[a
].Live
) and
6032 (gPlayers
[a
].FUID
<> FUID
) and
6033 (not SameTeam(FUID
, gPlayers
[a
].FUID
)) and
6034 (not gPlayers
[a
].NoTarget
) and
6035 (gPlayers
[a
].FMegaRulez
[MR_INVIS
] < gTime
) then
6037 if not TargetOnScreen(gPlayers
[a
].FObj
.X
+ PLAYER_RECT
.X
,
6038 gPlayers
[a
].FObj
.Y
+ PLAYER_RECT
.Y
) then
6041 x2
:= gPlayers
[a
].FObj
.X
+ PLAYER_RECT_CX
;
6042 y2
:= gPlayers
[a
].FObj
.Y
+ PLAYER_RECT_CY
;
6044 // Åñëè èãðîê íà ýêðàíå è íå ïðèêðûò ñòåíîé:
6045 if g_TraceVector(x1
, y1
, x2
, y2
) then
6047 // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé:
6048 SetLength(targets
, Length(targets
)+1);
6049 with targets
[High(targets
)] do
6051 UID
:= gPlayers
[a
].FUID
;
6052 X
:= gPlayers
[a
].FObj
.X
;
6053 Y
:= gPlayers
[a
].FObj
.Y
;
6056 Rect
:= PLAYER_RECT
;
6057 Dist
:= g_PatchLength(x1
, y1
, x2
, y2
);
6058 Line
:= (y1
+4 < Target
.Y
+PLAYER_RECT
.Y
+PLAYER_RECT
.Height
) and
6059 (y1
-4 > Target
.Y
+PLAYER_RECT
.Y
);
6067 if vsMonster
and (gMonsters
<> nil) then
6068 for a
:= 0 to High(gMonsters
) do
6069 if (gMonsters
[a
] <> nil) and (gMonsters
[a
].Live
) and
6070 (gMonsters
[a
].MonsterType
<> MONSTER_BARREL
) then
6072 mon
:= gMonsters
[a
];
6074 if not TargetOnScreen(mon
.Obj
.X
+ mon
.Obj
.Rect
.X
,
6075 mon
.Obj
.Y
+ mon
.Obj
.Rect
.Y
) then
6078 x2
:= mon
.Obj
.X
+ mon
.Obj
.Rect
.X
+ (mon
.Obj
.Rect
.Width
div 2);
6079 y2
:= mon
.Obj
.Y
+ mon
.Obj
.Rect
.Y
+ (mon
.Obj
.Rect
.Height
div 2);
6081 // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé:
6082 if g_TraceVector(x1
, y1
, x2
, y2
) then
6084 // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé:
6085 SetLength(targets
, Length(targets
)+1);
6086 with targets
[High(targets
)] do
6093 Rect
:= mon
.Obj
.Rect
;
6094 Dist
:= g_PatchLength(x1
, y1
, x2
, y2
);
6095 Line
:= (y1
+4 < Target
.Y
+ mon
.Obj
.Rect
.Y
+ mon
.Obj
.Rect
.Height
) and
6096 (y1
-4 > Target
.Y
+ mon
.Obj
.Rect
.Y
);
6104 // Åñëè åñòü âîçìîæíûå öåëè:
6105 // (Âûáèðàåì ëó÷øóþ, ìåíÿåì îðóæèå è áåæèì ê íåé/îò íåå)
6106 if targets
<> nil then
6108 // Âûáèðàåì íàèëó÷øóþ öåëü:
6109 BestTarget
:= targets
[0];
6110 if Length(targets
) > 1 then
6111 for a
:= 1 to High(targets
) do
6112 if Compare(BestTarget
, targets
[a
]) = 1 then
6113 BestTarget
:= targets
[a
];
6115 // Åñëè ëó÷øàÿ öåëü "âèäíåå" òåêóùåé, òî òåêóùàÿ := ëó÷øàÿ:
6116 if ((not Target
.Visible
) and BestTarget
.Visible
and (Target
.UID
<> BestTarget
.UID
)) or
6117 ((not Target
.Line
) and BestTarget
.Line
and BestTarget
.Visible
) then
6119 Target
:= BestTarget
;
6121 if (Healthy() = 3) or ((Healthy() = 2)) then
6122 begin // Åñëè çäîðîâû - äîãîíÿåì
6123 if ((RunDirection() = D_LEFT
) and (Target
.X
> FObj
.X
)) then
6124 SetAIFlag('GORIGHT', '1');
6125 if ((RunDirection() = D_RIGHT
) and (Target
.X
< FObj
.X
)) then
6126 SetAIFlag('GOLEFT', '1');
6129 begin // Åñëè ïîáèòû - óáåãàåì
6130 if ((RunDirection() = D_LEFT
) and (Target
.X
< FObj
.X
)) then
6131 SetAIFlag('GORIGHT', '1');
6132 if ((RunDirection() = D_RIGHT
) and (Target
.X
> FObj
.X
)) then
6133 SetAIFlag('GOLEFT', '1');
6136 // Âûáèðàåì îðóæèå íà îñíîâå ðàññòîÿíèÿ è ïðèîðèòåòîâ:
6137 SelectWeapon(Abs(x1
-Target
.cX
));
6142 // (Äîãîíÿåì/óáåãàåì, ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëè)
6143 // (Åñëè öåëü äàëåêî, òî õâàòèò ñëåäèòü çà íåé)
6144 if Target
.UID
<> 0 then
6146 if not TargetOnScreen(Target
.X
+ Target
.Rect
.X
,
6147 Target
.Y
+ Target
.Rect
.Y
) then
6148 begin // Öåëü ñáåæàëà ñ "ýêðàíà"
6149 if (Healthy() = 3) or ((Healthy() = 2)) then
6150 begin // Åñëè çäîðîâû - äîãîíÿåì
6151 if ((RunDirection() = D_LEFT
) and (Target
.X
> FObj
.X
)) then
6152 SetAIFlag('GORIGHT', '1');
6153 if ((RunDirection() = D_RIGHT
) and (Target
.X
< FObj
.X
)) then
6154 SetAIFlag('GOLEFT', '1');
6157 begin // Åñëè ïîáèòû - çàáûâàåì î öåëè è óáåãàåì
6159 if ((RunDirection() = D_LEFT
) and (Target
.X
< FObj
.X
)) then
6160 SetAIFlag('GORIGHT', '1');
6161 if ((RunDirection() = D_RIGHT
) and (Target
.X
> FObj
.X
)) then
6162 SetAIFlag('GOLEFT', '1');
6166 begin // Öåëü ïîêà íà "ýêðàíå"
6167 // Åñëè öåëü íå çàãîðîæåíà ñòåíîé, òî îòìå÷àåì, êîãäà åå âèäåëè:
6168 if g_TraceVector(x1
, y1
, Target
.cX
, Target
.cY
) then
6169 FLastVisible
:= gTime
;
6170 // Åñëè ðàçíèöà âûñîò íå âåëèêà, òî äîãîíÿåì:
6171 if (Abs(FObj
.Y
-Target
.Y
) <= 128) then
6173 if ((RunDirection() = D_LEFT
) and (Target
.X
> FObj
.X
)) then
6174 SetAIFlag('GORIGHT', '1');
6175 if ((RunDirection() = D_RIGHT
) and (Target
.X
< FObj
.X
)) then
6176 SetAIFlag('GOLEFT', '1');
6180 // Âûáèðàåì óãîë ââåðõ:
6181 if FDirection
= D_LEFT
then
6182 angle
:= ANGLE_LEFTUP
6184 angle
:= ANGLE_RIGHTUP
;
6186 firew
:= Trunc(Cos(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6187 fireh
:= Trunc(Sin(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6189 // Åñëè ïðè óãëå ââåðõ ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè:
6190 if g_CollideLine(x1
, y1
, x1
+firew
, y1
+fireh
,
6191 Target
.X
+Target
.Rect
.X
+GetInterval(FDifficult
.DiagPrecision
, 128), //96
6192 Target
.Y
+Target
.Rect
.Y
+GetInterval(FDifficult
.DiagPrecision
, 128),
6193 Target
.Rect
.Width
, Target
.Rect
.Height
) and
6194 g_TraceVector(x1
, y1
, Target
.cX
, Target
.cY
) then
6195 begin // òî íóæíî ñòðåëÿòü ââåðõ
6196 SetAIFlag('NEEDFIRE', '1');
6197 SetAIFlag('NEEDSEEUP', '1');
6200 // Âûáèðàåì óãîë âíèç:
6201 if FDirection
= D_LEFT
then
6202 angle
:= ANGLE_LEFTDOWN
6204 angle
:= ANGLE_RIGHTDOWN
;
6206 firew
:= Trunc(Cos(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6207 fireh
:= Trunc(Sin(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6209 // Åñëè ïðè óãëå âíèç ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè:
6210 if g_CollideLine(x1
, y1
, x1
+firew
, y1
+fireh
,
6211 Target
.X
+Target
.Rect
.X
+GetInterval(FDifficult
.DiagPrecision
, 128),
6212 Target
.Y
+Target
.Rect
.Y
+GetInterval(FDifficult
.DiagPrecision
, 128),
6213 Target
.Rect
.Width
, Target
.Rect
.Height
) and
6214 g_TraceVector(x1
, y1
, Target
.cX
, Target
.cY
) then
6215 begin // òî íóæíî ñòðåëÿòü âíèç
6216 SetAIFlag('NEEDFIRE', '1');
6217 SetAIFlag('NEEDSEEDOWN', '1');
6220 // Åñëè öåëü âèäíî è îíà íà òàêîé æå âûñîòå:
6221 if Target
.Visible
and
6222 (y1
+4 < Target
.Y
+Target
.Rect
.Y
+Target
.Rect
.Height
) and
6223 (y1
-4 > Target
.Y
+Target
.Rect
.Y
) then
6225 // Åñëè èäåì â ñòîðîíó öåëè, òî íàäî ñòðåëÿòü:
6226 if ((FDirection
= D_LEFT
) and (Target
.X
< FObj
.X
)) or
6227 ((FDirection
= D_RIGHT
) and (Target
.X
> FObj
.X
)) then
6228 begin // òî íóæíî ñòðåëÿòü âïåðåä
6229 SetAIFlag('NEEDFIRE', '1');
6230 SetAIFlag('NEEDSEEDOWN', '');
6231 SetAIFlag('NEEDSEEUP', '');
6233 // Åñëè öåëü â ïðåäåëàõ "ýêðàíà" è ñëîæíîñòü ïîçâîëÿåò ïðûæêè ñáëèæåíèÿ:
6234 if Abs(FObj
.X
-Target
.X
) < Trunc(gPlayerScreenSize
.X
*0.75) then
6235 if GetRnd(FDifficult
.CloseJump
) then
6236 begin // òî åñëè ïîâåçåò - ïðûãàåì (îñîáåííî, åñëè áëèçêî)
6237 if Abs(FObj
.X
-Target
.X
) < 128 then
6241 if Random(a
) = 0 then
6242 SetAIFlag('NEEDJUMP', '1');
6246 // Åñëè öåëü âñå åùå åñòü:
6247 if Target
.UID
<> 0 then
6248 if gTime
-FLastVisible
> 2000 then // Åñëè âèäåëè äàâíî
6249 Target
.UID
:= 0 // òî çàáûòü öåëü
6250 else // Åñëè âèäåëè íåäàâíî
6251 begin // íî öåëü óáèëè
6252 if Target
.IsPlayer
then
6253 begin // Öåëü - èãðîê
6254 pla
:= g_Player_Get(Target
.UID
);
6255 if (pla
= nil) or (not pla
.Live
) or pla
.NoTarget
or
6256 (pla
.FMegaRulez
[MR_INVIS
] >= gTime
) then
6257 Target
.UID
:= 0; // òî çàáûòü öåëü
6260 begin // Öåëü - ìîíñòð
6261 mon
:= g_Monsters_Get(Target
.UID
);
6262 if (mon
= nil) or (not mon
.Live
) then
6263 Target
.UID
:= 0; // òî çàáûòü öåëü
6266 end; // if Target.UID <> 0
6268 FTargetUID
:= Target
.UID
;
6270 // Åñëè âîçìîæíûõ öåëåé íåò:
6271 // (Àòàêà ÷åãî-íèáóäü ñëåâà èëè ñïðàâà)
6272 if targets
= nil then
6273 if GetAIFlag('ATTACKLEFT') <> '' then
6274 begin // Åñëè íóæíî àòàêîâàòü íàëåâî
6275 RemoveAIFlag('ATTACKLEFT');
6277 SetAIFlag('NEEDJUMP', '1');
6279 if RunDirection() = D_RIGHT
then
6280 begin // Èäåì íå â òó ñòîðîíó
6281 if (Healthy() > 1) and GetRnd(FDifficult
.InvisFire
) then
6282 begin // Åñëè çäîðîâû, òî, âîçìîæíî, ñòðåëÿåì áåæèì âëåâî è ñòðåëÿåì
6283 SetAIFlag('NEEDFIRE', '1');
6284 SetAIFlag('GOLEFT', '1');
6288 begin // Èäåì â íóæíóþ ñòîðîíó
6289 if GetRnd(FDifficult
.InvisFire
) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ
6290 SetAIFlag('NEEDFIRE', '1');
6291 if Healthy() <= 1 then // Ïîáèòû - óáåãàåì
6292 SetAIFlag('GORIGHT', '1');
6296 if GetAIFlag('ATTACKRIGHT') <> '' then
6297 begin // Åñëè íóæíî àòàêîâàòü íàïðàâî
6298 RemoveAIFlag('ATTACKRIGHT');
6300 SetAIFlag('NEEDJUMP', '1');
6302 if RunDirection() = D_LEFT
then
6303 begin // Èäåì íå â òó ñòîðîíó
6304 if (Healthy() > 1) and GetRnd(FDifficult
.InvisFire
) then
6305 begin // Åñëè çäîðîâû, òî, âîçìîæíî, áåæèì âïðàâî è ñòðåëÿåì
6306 SetAIFlag('NEEDFIRE', '1');
6307 SetAIFlag('GORIGHT', '1');
6312 if GetRnd(FDifficult
.InvisFire
) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ
6313 SetAIFlag('NEEDFIRE', '1');
6314 if Healthy() <= 1 then // Ïîáèòû - óáåãàåì
6315 SetAIFlag('GOLEFT', '1');
6319 // Åñëè åñòü âîçìîæíûå öåëè:
6320 // (Ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëÿì)
6321 if (targets
<> nil) and (GetAIFlag('NEEDFIRE') <> '') then
6322 for a
:= 0 to High(targets
) do
6324 // Åñëè ìîæåì ñòðåëÿòü ïî äèàãîíàëè:
6325 if GetRnd(FDifficult
.DiagFire
) then
6327 // Èùåì öåëü ñâåðõó è ñòðåëÿåì, åñëè åñòü:
6328 if FDirection
= D_LEFT
then
6329 angle
:= ANGLE_LEFTUP
6331 angle
:= ANGLE_RIGHTUP
;
6333 firew
:= Trunc(Cos(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6334 fireh
:= Trunc(Sin(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6336 if g_CollideLine(x1
, y1
, x1
+firew
, y1
+fireh
,
6337 targets
[a
].X
+targets
[a
].Rect
.X
+GetInterval(FDifficult
.DiagPrecision
, 128),
6338 targets
[a
].Y
+targets
[a
].Rect
.Y
+GetInterval(FDifficult
.DiagPrecision
, 128),
6339 targets
[a
].Rect
.Width
, targets
[a
].Rect
.Height
) and
6340 g_TraceVector(x1
, y1
, targets
[a
].cX
, targets
[a
].cY
) then
6342 SetAIFlag('NEEDFIRE', '1');
6343 SetAIFlag('NEEDSEEUP', '1');
6346 // Èùåì öåëü ñíèçó è ñòðåëÿåì, åñëè åñòü:
6347 if FDirection
= D_LEFT
then
6348 angle
:= ANGLE_LEFTDOWN
6350 angle
:= ANGLE_RIGHTDOWN
;
6352 firew
:= Trunc(Cos(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6353 fireh
:= Trunc(Sin(DegToRad(-angle
))*gPlayerScreenSize
.X
*0.6);
6355 if g_CollideLine(x1
, y1
, x1
+firew
, y1
+fireh
,
6356 targets
[a
].X
+targets
[a
].Rect
.X
+GetInterval(FDifficult
.DiagPrecision
, 128),
6357 targets
[a
].Y
+targets
[a
].Rect
.Y
+GetInterval(FDifficult
.DiagPrecision
, 128),
6358 targets
[a
].Rect
.Width
, targets
[a
].Rect
.Height
) and
6359 g_TraceVector(x1
, y1
, targets
[a
].cX
, targets
[a
].cY
) then
6361 SetAIFlag('NEEDFIRE', '1');
6362 SetAIFlag('NEEDSEEDOWN', '1');
6366 // Åñëè öåëü "ïåðåä íîñîì", òî ñòðåëÿåì:
6367 if targets
[a
].Line
and targets
[a
].Visible
and
6368 (((FDirection
= D_LEFT
) and (targets
[a
].X
< FObj
.X
)) or
6369 ((FDirection
= D_RIGHT
) and (targets
[a
].X
> FObj
.X
))) then
6371 SetAIFlag('NEEDFIRE', '1');
6376 // Åñëè ëåòèò ïóëÿ, òî, âîçìîæíî, ïîäïðûãèâàåì:
6377 if g_Weapon_Danger(FUID
, FObj
.X
+PLAYER_RECT
.X
, FObj
.Y
+PLAYER_RECT
.Y
,
6378 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
,
6379 40+GetInterval(FDifficult
.Cover
, 40)) then
6380 SetAIFlag('NEEDJUMP', '1');
6382 // Åñëè êîí÷èëèñü ïàòîðíû, òî íóæíî ñìåíèòü îðóæèå:
6383 ammo
:= GetAmmoByWeapon(FCurrWeap
);
6384 if ((FCurrWeap
= WEAPON_SHOTGUN2
) and (ammo
< 2)) or
6385 ((FCurrWeap
= WEAPON_BFG
) and (ammo
< 40)) or
6387 SetAIFlag('SELECTWEAPON', '1');
6389 // Åñëè íóæíî ñìåíèòü îðóæèå, òî âûáèðàåì íóæíîå:
6390 if GetAIFlag('SELECTWEAPON') = '1' then
6393 RemoveAIFlag('SELECTWEAPON');
6397 procedure TBot
.Update();
6410 // Ïðîâåðÿåì, îòêëþ÷¸í ëè AI áîòîâ
6411 if (g_debug_BotAIOff
= 1) and (Team
= TEAM_RED
) then
6413 if (g_debug_BotAIOff
= 2) and (Team
= TEAM_BLUE
) then
6415 if g_debug_BotAIOff
= 3 then
6428 procedure TBot
.ReleaseKey(Key
: Byte);
6437 function TBot
.KeyPressed(Key
: Word): Boolean;
6439 Result
:= FKeys
[Key
].Pressed
;
6442 function TBot
.GetAIFlag(fName
: String20
): String20
;
6448 fName
:= LowerCase(fName
);
6450 if FAIFlags
<> nil then
6451 for a
:= 0 to High(FAIFlags
) do
6452 if LowerCase(FAIFlags
[a
].Name
) = fName
then
6454 Result
:= FAIFlags
[a
].Value
;
6459 procedure TBot
.RemoveAIFlag(fName
: String20
);
6463 if FAIFlags
= nil then Exit
;
6465 fName
:= LowerCase(fName
);
6467 for a
:= 0 to High(FAIFlags
) do
6468 if LowerCase(FAIFlags
[a
].Name
) = fName
then
6470 if a
<> High(FAIFlags
) then
6471 for b
:= a
to High(FAIFlags
)-1 do
6472 FAIFlags
[b
] := FAIFlags
[b
+1];
6474 SetLength(FAIFlags
, Length(FAIFlags
)-1);
6479 procedure TBot
.SetAIFlag(fName
, fValue
: String20
);
6487 fName
:= LowerCase(fName
);
6489 if FAIFlags
<> nil then
6490 for a
:= 0 to High(FAIFlags
) do
6491 if LowerCase(FAIFlags
[a
].Name
) = fName
then
6497 if ok
then FAIFlags
[a
].Value
:= fValue
6500 SetLength(FAIFlags
, Length(FAIFlags
)+1);
6501 with FAIFlags
[High(FAIFlags
)] do
6509 procedure TBot
.UpdateMove
;
6511 procedure GoLeft(Time
: Word = 1);
6513 ReleaseKey(KEY_LEFT
);
6514 ReleaseKey(KEY_RIGHT
);
6515 PressKey(KEY_LEFT
, Time
);
6516 SetDirection(D_LEFT
);
6519 procedure GoRight(Time
: Word = 1);
6521 ReleaseKey(KEY_LEFT
);
6522 ReleaseKey(KEY_RIGHT
);
6523 PressKey(KEY_RIGHT
, Time
);
6524 SetDirection(D_RIGHT
);
6527 function Rnd(a
: Word): Boolean;
6529 Result
:= Random(a
) = 0;
6532 procedure Turn(Time
: Word = 1200);
6534 if RunDirection() = D_LEFT
then GoRight(Time
) else GoLeft(Time
);
6539 ReleaseKey(KEY_LEFT
);
6540 ReleaseKey(KEY_RIGHT
);
6543 function CanRunLeft(): Boolean;
6545 Result
:= not CollideLevel(-1, 0);
6548 function CanRunRight(): Boolean;
6550 Result
:= not CollideLevel(1, 0);
6553 function CanRun(): Boolean;
6555 if RunDirection() = D_LEFT
then Result
:= CanRunLeft() else Result
:= CanRunRight();
6558 procedure Jump(Time
: Word = 30);
6560 PressKey(KEY_JUMP
, Time
);
6563 function NearHole(): Boolean;
6567 { TODO 5 : Ëåñòíèöû }
6568 sx
:= IfThen(RunDirection() = D_LEFT
, -1, 1);
6569 for x
:= 1 to PLAYER_RECT
.Width
do
6570 if (not StayOnStep(x
*sx
, 0)) and
6571 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
)) and
6572 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
*2)) then
6581 function BorderHole(): Boolean;
6585 { TODO 5 : Ëåñòíèöû }
6586 sx
:= IfThen(RunDirection() = D_LEFT
, -1, 1);
6587 for x
:= 1 to PLAYER_RECT
.Width
do
6588 if (not StayOnStep(x
*sx
, 0)) and
6589 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
)) and
6590 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
*2)) then
6592 for xx
:= x
to x
+32 do
6593 if CollideLevel(xx
*sx
, PLAYER_RECT
.Height
) then
6603 function NearDeepHole(): Boolean;
6609 sx
:= IfThen(RunDirection() = D_LEFT
, -1, 1);
6612 for x
:= 1 to PLAYER_RECT
.Width
do
6613 if (not StayOnStep(x
*sx
, 0)) and
6614 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
)) and
6615 (not CollideLevel(x
*sx
, PLAYER_RECT
.Height
*2)) then
6617 while FObj
.Y
+y
*PLAYER_RECT
.Height
< gMapInfo
.Height
do
6619 if CollideLevel(x
*sx
, PLAYER_RECT
.Height
*y
) then Exit
;
6624 end else Result
:= False;
6627 function OverDeepHole(): Boolean;
6634 while FObj
.Y
+y
*PLAYER_RECT
.Height
< gMapInfo
.Height
do
6636 if CollideLevel(0, PLAYER_RECT
.Height
*y
) then Exit
;
6643 function OnGround(): Boolean;
6645 Result
:= StayOnStep(0, 0) or CollideLevel(0, 1);
6648 function OnLadder(): Boolean;
6650 Result
:= FullInStep(0, 0);
6653 function BelowLadder(): Boolean;
6655 Result
:= (FullInStep(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -PLAYER_RECT
.Height
) and
6656 not CollideLevel(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -PLAYER_RECT
.Height
)) or
6657 (FullInStep(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -BOT_MAXJUMP
) and
6658 not CollideLevel(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -BOT_MAXJUMP
));
6661 function BelowLiftUp(): Boolean;
6663 Result
:= ((FullInLift(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -PLAYER_RECT
.Height
) = -1) and
6664 not CollideLevel(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -PLAYER_RECT
.Height
)) or
6665 ((FullInLift(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -BOT_MAXJUMP
) = -1) and
6666 not CollideLevel(IfThen(RunDirection() = D_LEFT
, -1, 1)*(PLAYER_RECT
.Width
div 2), -BOT_MAXJUMP
));
6669 function OnTopLift(): Boolean;
6671 Result
:= (FullInLift(0, 0) = -1) and (FullInLift(0, -32) = 0);
6674 function CanJumpOver(): Boolean;
6678 sx
:= IfThen(RunDirection() = D_LEFT
, -1, 1);
6682 if not CollideLevel(sx
, 0) then Exit
;
6684 for y
:= 1 to BOT_MAXJUMP
do
6685 if CollideLevel(0, -y
) then Exit
else
6686 if not CollideLevel(sx
, -y
) then
6693 function CanJumpUp(Dist
: ShortInt): Boolean;
6700 if CollideLevel(Dist
, 0) then Exit
;
6703 for y
:= 0 to BOT_MAXJUMP
do
6704 if CollideLevel(Dist
, -y
) then
6713 for yy
:= y
+1 to BOT_MAXJUMP
do
6714 if not CollideLevel(Dist
, -yy
) then
6723 for y
:= 0 to BOT_MAXJUMP
do
6724 if CollideLevel(0, -y
) then
6732 if y
< yy
then Exit
;
6737 function IsSafeTrigger(): Boolean;
6742 if gTriggers
= nil then
6744 for a
:= 0 to High(gTriggers
) do
6745 if Collide(gTriggers
[a
].X
,
6748 gTriggers
[a
].Height
) and
6749 (gTriggers
[a
].TriggerType
in [TRIGGER_EXIT
, TRIGGER_CLOSEDOOR
,
6750 TRIGGER_CLOSETRAP
, TRIGGER_TRAP
,
6751 TRIGGER_PRESS
, TRIGGER_ON
, TRIGGER_OFF
,
6752 TRIGGER_ONOFF
, TRIGGER_SPAWNMONSTER
,
6753 TRIGGER_DAMAGE
, TRIGGER_SHOT
]) then
6758 // Âîçìîæíî, íàæèìàåì êíîïêó:
6759 if Rnd(16) and IsSafeTrigger() then
6762 // Åñëè ïîä ëèôòîì èëè ñòóïåíüêàìè, òî, âîçìîæíî, ïðûãàåì:
6763 if OnLadder() or ((BelowLadder() or BelowLiftUp()) and Rnd(8)) then
6765 ReleaseKey(KEY_LEFT
);
6766 ReleaseKey(KEY_RIGHT
);
6770 // Èäåì âëåâî, åñëè íàäî áûëî:
6771 if GetAIFlag('GOLEFT') <> '' then
6773 RemoveAIFlag('GOLEFT');
6774 if CanRunLeft() then
6778 // Èäåì âïðàâî, åñëè íàäî áûëî:
6779 if GetAIFlag('GORIGHT') <> '' then
6781 RemoveAIFlag('GORIGHT');
6782 if CanRunRight() then
6786 // Åñëè âûëåòåëè çà êàðòó, òî ïðîáóåì âåðíóòüñÿ:
6787 if FObj
.X
< -32 then
6790 if FObj
.X
+32 > gMapInfo
.Width
then
6793 // Ïðûãàåì, åñëè íàäî áûëî:
6794 if GetAIFlag('NEEDJUMP') <> '' then
6797 RemoveAIFlag('NEEDJUMP');
6800 // Ñìîòðèì ââåðõ, åñëè íàäî áûëî:
6801 if GetAIFlag('NEEDSEEUP') <> '' then
6804 ReleaseKey(KEY_DOWN
);
6805 PressKey(KEY_UP
, 20);
6806 RemoveAIFlag('NEEDSEEUP');
6809 // Ñìîòðèì âíèç, åñëè íàäî áûëî:
6810 if GetAIFlag('NEEDSEEDOWN') <> '' then
6813 ReleaseKey(KEY_DOWN
);
6814 PressKey(KEY_DOWN
, 20);
6815 RemoveAIFlag('NEEDSEEDOWN');
6818 // Åñëè íóæíî áûëî â äûðó è ìû íå íà çåìëå, òî ïîêîðíî ëåòèì:
6819 if GetAIFlag('GOINHOLE') <> '' then
6820 if not OnGround() then
6822 ReleaseKey(KEY_LEFT
);
6823 ReleaseKey(KEY_RIGHT
);
6824 RemoveAIFlag('GOINHOLE');
6825 SetAIFlag('FALLINHOLE', '1');
6828 // Åñëè ïàäàëè è äîñòèãëè çåìëè, òî õâàòèò ïàäàòü:
6829 if GetAIFlag('FALLINHOLE') <> '' then
6831 RemoveAIFlag('FALLINHOLE');
6833 // Åñëè ëåòåëè ïðÿìî è ñåé÷àñ íå íà ëåñòíèöå èëè íà âåðøèíå ëèôòà, òî îòõîäèì â ñòîðîíó:
6834 if not (KeyPressed(KEY_LEFT
) or KeyPressed(KEY_RIGHT
)) then
6835 if GetAIFlag('FALLINHOLE') = '' then
6836 if (not OnLadder()) or (FObj
.Vel
.Y
>= 0) or (OnTopLift()) then
6842 // Åñëè íà çåìëå è ìîæíî ïîäïðûãíóòü, òî, âîçìîæíî, ïðûãàåì:
6844 CanJumpUp(IfThen(RunDirection() = D_LEFT
, -1, 1)*32) and
6848 // Åñëè íà çåìëå è âîçëå äûðû (ãëóáèíà > 2 ðîñòîâ èãðîêà):
6849 if OnGround() and NearHole() then
6850 if NearDeepHole() then // Åñëè ýòî áåçäíà
6852 0..3: Turn(); // Áåæèì îáðàòíî
6853 4: Jump(); // Ïðûãàåì
6854 5: begin // Ïðûãàåì îáðàòíî
6859 else // Ýòî íå áåçäíà è ìû åùå íå ëåòèì òóäà
6860 if GetAIFlag('GOINHOLE') = '' then
6862 0: Turn(); // Íå íóæíî òóäà
6863 1: Jump(); // Âäðóã ïîâåçåò - ïðûãàåì
6864 else // Åñëè ÿìà ñ ãðàíèöåé, òî ïðè ñëó÷àå ìîæíî òóäà ïðûãíóòü
6865 if BorderHole() then
6866 SetAIFlag('GOINHOLE', '1');
6869 // Åñëè íà çåìëå, íî íåêóäà èäòè:
6870 if (not CanRun()) and OnGround() then
6872 // Åñëè ìû íà ëåñòíèöå èëè ìîæíî ïåðåïðûãíóòü, òî ïðûãàåì:
6873 if CanJumpOver() or OnLadder() then
6875 else // èíà÷å ïîïûòàåìñÿ â äðóãóþ ñòîðîíó
6876 if Random(2) = 0 then
6878 if IsSafeTrigger() then
6884 // Îñòàëîñü ìàëî âîçäóõà:
6885 if FAir
< 36 * 2 then
6888 // Âûáèðàåìñÿ èç êèñëîòû, åñëè íåò êîñòþìà, îáîæãëèñü, èëè ìàëî çäîðîâüÿ:
6889 if (FMegaRulez
[MR_SUIT
] < gTime
) and ((FLastHit
= HIT_ACID
) or (Healthy() <= 1)) then
6890 if BodyInAcid(0, 0) then
6894 function TBot
.FullInStep(XInc
, YInc
: Integer): Boolean;
6896 Result
:= g_Map_CollidePanel(FObj
.X
+PLAYER_RECT
.X
+XInc
, FObj
.Y
+PLAYER_RECT
.Y
+YInc
,
6897 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
, PANEL_STEP
, False);
6900 {function TBot.NeedItem(Item: Byte): Byte;
6905 procedure TBot
.SelectWeapon(Dist
: Integer);
6909 function HaveAmmo(weapon
: Byte): Boolean;
6912 WEAPON_PISTOL
: Result
:= FAmmo
[A_BULLETS
] >= 1;
6913 WEAPON_SHOTGUN1
: Result
:= FAmmo
[A_SHELLS
] >= 1;
6914 WEAPON_SHOTGUN2
: Result
:= FAmmo
[A_SHELLS
] >= 2;
6915 WEAPON_CHAINGUN
: Result
:= FAmmo
[A_BULLETS
] >= 10;
6916 WEAPON_ROCKETLAUNCHER
: Result
:= FAmmo
[A_ROCKETS
] >= 1;
6917 WEAPON_PLASMA
: Result
:= FAmmo
[A_CELLS
] >= 10;
6918 WEAPON_BFG
: Result
:= FAmmo
[A_CELLS
] >= 40;
6919 WEAPON_SUPERPULEMET
: Result
:= FAmmo
[A_SHELLS
] >= 1;
6920 else Result
:= True;
6925 if Dist
= -1 then Dist
:= BOT_LONGDIST
;
6927 if Dist
> BOT_LONGDIST
then
6928 begin // Äàëüíèé áîé
6930 if FWeapon
[FDifficult
.WeaponPrior
[a
]] and HaveAmmo(FDifficult
.WeaponPrior
[a
]) then
6932 FSelectedWeapon
:= FDifficult
.WeaponPrior
[a
];
6936 else //if Dist > BOT_UNSAFEDIST then
6937 begin // Áëèæíèé áîé
6939 if FWeapon
[FDifficult
.CloseWeaponPrior
[a
]] and HaveAmmo(FDifficult
.CloseWeaponPrior
[a
]) then
6941 FSelectedWeapon
:= FDifficult
.CloseWeaponPrior
[a
];
6948 if FWeapon[FDifficult.SafeWeaponPrior[a]] and HaveAmmo(FDifficult.SafeWeaponPrior[a]) then
6950 FSelectedWeapon := FDifficult.SafeWeaponPrior[a];
6956 function TBot
.PickItem(ItemType
: Byte; force
: Boolean; var remove
: Boolean): Boolean;
6958 Result
:= inherited PickItem(ItemType
, force
, remove
);
6960 if Result
then SetAIFlag('SELECTWEAPON', '1');
6963 function TBot
.Heal(value
: Word; Soft
: Boolean): Boolean;
6965 Result
:= inherited Heal(value
, Soft
);
6968 function TBot
.Healthy(): Byte;
6970 if FMegaRulez
[MR_INVUL
] >= gTime
then Result
:= 3
6971 else if (FHealth
> 80) or ((FHealth
> 50) and (FArmor
> 20)) then Result
:= 3
6972 else if (FHealth
> 50) then Result
:= 2
6973 else if (FHealth
> 20) then Result
:= 1
6977 function TBot
.TargetOnScreen(TX
, TY
: Integer): Boolean;
6979 Result
:= (Abs(FObj
.X
-TX
) <= Trunc(gPlayerScreenSize
.X
*0.6)) and
6980 (Abs(FObj
.Y
-TY
) <= Trunc(gPlayerScreenSize
.Y
*0.6));
6983 procedure TBot
.OnDamage(Angle
: SmallInt);
6991 if (Angle
= 0) or (Angle
= 180) then
6994 if (g_GetUIDType(FLastSpawnerUID
) = UID_PLAYER
) and
6995 LongBool(gGameSettings
.Options
and GAME_OPTION_BOTVSPLAYER
) then
6997 pla
:= g_Player_Get(FLastSpawnerUID
);
6998 ok
:= not TargetOnScreen(pla
.FObj
.X
+ PLAYER_RECT
.X
,
6999 pla
.FObj
.Y
+ PLAYER_RECT
.Y
);
7002 if (g_GetUIDType(FLastSpawnerUID
) = UID_MONSTER
) and
7003 LongBool(gGameSettings
.Options
and GAME_OPTION_BOTVSMONSTER
) then
7005 mon
:= g_Monsters_Get(FLastSpawnerUID
);
7006 ok
:= not TargetOnScreen(mon
.Obj
.X
+ mon
.Obj
.Rect
.X
,
7007 mon
.Obj
.Y
+ mon
.Obj
.Rect
.Y
);
7012 SetAIFlag('ATTACKLEFT', '1')
7014 SetAIFlag('ATTACKRIGHT', '1');
7018 function TBot
.RunDirection(): TDirection
;
7020 if Abs(Vel
.X
) >= 1 then
7022 if Vel
.X
> 0 then Result
:= D_RIGHT
else Result
:= D_LEFT
;
7024 Result
:= FDirection
;
7027 function TBot
.GetRnd(a
: Byte): Boolean;
7029 if a
= 0 then Result
:= False
7030 else if a
= 255 then Result
:= True
7031 else Result
:= Random(256) > 255-a
;
7034 function TBot
.GetInterval(a
: Byte; radius
: SmallInt): SmallInt;
7036 Result
:= Round((255-a
)/255*radius
*(Random(2)-1));
7039 procedure TBot
.SaveState(var Mem
: TBinMemoryWriter
);
7045 inherited SaveState(Mem
);
7047 // Âûáðàííîå îðóæèå:
7048 Mem
.WriteByte(FSelectedWeapon
);
7050 Mem
.WriteWord(FTargetUID
);
7051 // Âðåìÿ ïîòåðè öåëè:
7052 Mem
.WriteDWORD(FLastVisible
);
7053 // Êîëè÷åñòâî ôëàãîâ ÈÈ:
7054 dw
:= Length(FAIFlags
);
7057 for i
:= 0 to Integer(dw
)-1 do
7059 Mem
.WriteString(FAIFlags
[i
].Name
, 20);
7060 Mem
.WriteString(FAIFlags
[i
].Value
, 20);
7062 // Íàñòðîéêè ñëîæíîñòè:
7064 Mem
.WriteMemory(p
, SizeOf(TDifficult
));
7067 procedure TBot
.LoadState(var Mem
: TBinMemoryReader
);
7073 inherited LoadState(Mem
);
7075 // Âûáðàííîå îðóæèå:
7076 Mem
.ReadByte(FSelectedWeapon
);
7078 Mem
.ReadWord(FTargetUID
);
7079 // Âðåìÿ ïîòåðè öåëè:
7080 Mem
.ReadDWORD(FLastVisible
);
7081 // Êîëè÷åñòâî ôëàãîâ ÈÈ:
7083 SetLength(FAIFlags
, dw
);
7085 for i
:= 0 to Integer(dw
)-1 do
7087 Mem
.ReadString(FAIFlags
[i
].Name
);
7088 Mem
.ReadString(FAIFlags
[i
].Value
);
7090 // Íàñòðîéêè ñëîæíîñòè:
7091 Mem
.ReadMemory(p
, dw
);
7092 if dw
<> SizeOf(TDifficult
) then
7094 raise EBinSizeError
.Create('TBot.LoadState: Wrong FDifficult Size');
7096 FDifficult
:= TDifficult(p
^);