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, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
22 MAPDEF
, g_textures
, g_base
, g_basic
, g_weapons
, r_graphics
, utils
, g_gfx
,
23 ImagingTypes
, Imaging
, ImagingUtility
;
41 A_WALKATTACKDOWN
= 14;
45 A_FISTWALKATTACK
= 18;
49 A_FISTATTACKDOWN
= 22;
52 A_LASTEXT
= A_FISTATTACKDOWN
;
65 FLAG_BASEPOINT
: TDFPoint
= (X
:16; Y
:43);
68 TWeaponPoints
= Array [WP_FIRST
+ 1..WP_LAST
, A_STAND
..A_LAST
, TDirection
.D_LEFT
..TDirection
.D_RIGHT
] of Array of TDFPoint
;
70 TModelMatrix
= Array [TDirection
.D_LEFT
..TDirection
.D_RIGHT
, A_STAND
..A_LAST
] of TAnimationState
;
72 TModelTextures
= Array [TDirection
.D_LEFT
..TDirection
.D_RIGHT
, A_STAND
..A_LAST
] of record
88 TModelSoundArray
= Array of TModelSound
;
90 TGibsArray
= Array of Integer;
92 TPlayerModel
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
94 FDirection
: TDirection
;
96 FCurrentAnimation
: Byte;
97 FAnimState
: TAnimationState
;
104 destructor Destroy(); override;
105 procedure ChangeAnimation(Animation
: Byte; Force
: Boolean = False);
106 procedure SetColor(Red
, Green
, Blue
: Byte);
107 procedure SetWeapon(Weapon
: Byte);
108 procedure SetFlag(Flag
: Byte);
109 procedure SetFire (Fire
: Boolean);
110 function GetFire (): Boolean;
111 function PlaySound(SoundType
, Level
: Byte; X
, Y
: Integer): Boolean;
114 function GetBlood (): TModelBlood
;
115 function GetName (): String;
118 property Direction
: TDirection read FDirection write FDirection
;
119 property Animation
: Byte read FCurrentAnimation
;
120 property Weapon
: Byte read FCurrentWeapon
;
123 property Color
: TRGB read FColor write FColor
;
124 property AnimState
: TAnimationState read FAnimState
;
125 property CurrentAnimation
: Byte read FCurrentAnimation
;
126 property CurrentWeapon
: Byte read FCurrentWeapon
;
127 property Flag
: Byte read FFlag
;
128 property ID
: Integer read FID
;
131 procedure g_PlayerModel_LoadAll
;
132 procedure g_PlayerModel_FreeData();
133 function g_PlayerModel_Load(FileName
: String): Boolean;
134 function g_PlayerModel_GetNames(): SSArray
;
135 function g_PlayerModel_GetBlood(ModelName
: String): TModelBlood
;
136 function g_PlayerModel_Get(ModelName
: String): TPlayerModel
;
137 function g_PlayerModel_GetGibs (ModelID
: Integer; var Gibs
: TGibsArray
): Boolean;
138 function g_PlayerModel_GetIndex (ModelName
: String): Integer;
140 (* --- private data --- *)
143 TPlayerModelInfo
= record
148 ModelSpeed
: Array [A_STAND
..A_PAIN
] of Byte;
151 WeaponPoints
: TWeaponPoints
;
152 PainSounds
: TModelSoundArray
;
153 DieSounds
: TModelSoundArray
;
156 // =======================
158 Anim
: TModelTextures
;
166 PlayerModelsArray
: Array of TPlayerModelInfo
;
171 g_sound
, g_console
, SysUtils
, g_player
, CONFIG
, r_textures
, r_animations
,
172 e_sound
, g_options
, g_map
, Math
, e_log
, wadreader
;
175 FLAG_DEFPOINT
: TDFPoint
= (X
:32; Y
:16);
177 WEAPONBASE
: Array [WP_FIRST
+ 1..WP_LAST
] of TDFPoint
=
178 ((X
:8; Y
:4), (X
:8; Y
:8), (X
:16; Y
:16), (X
:16; Y
:24),
179 (X
:16; Y
:16), (X
:24; Y
:24), (X
:16; Y
:16), (X
:24; Y
:24),
180 (X
:16; Y
:16), (X
:8; Y
:8));
182 AnimNames
: Array [A_STAND
..A_LASTEXT
] of String =
183 ('StandAnim','WalkAnim','Die1Anim','Die2Anim','AttackAnim',
184 'SeeUpAnim','SeeDownAnim','AttackUpAnim','AttackDownAnim','PainAnim',
186 'WalkAttackAnim', 'WalkSeeUpAnim', 'WalkSeeDownAnim',
187 'WalkAttackUpAnim', 'WalkAttackDownAnim', 'FistStandAnim', 'FistWalkAnim',
188 'FistAttackAnim', 'FistWalkAttackAnim', 'FistSeeUpAnim', 'FistSeeDownAnim',
189 'FistAttackUpAnim', 'FistAttackDownAnim');
190 WeapNames
: Array [WP_FIRST
+ 1..WP_LAST
] of String =
191 ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm');
193 function g_PlayerModel_GetIndex (ModelName
: String): Integer;
197 if PlayerModelsArray
<> nil then
200 while (i
< Length(PlayerModelsArray
)) and (PlayerModelsArray
[i
].Name
<> ModelName
) do
202 if i
< Length(PlayerModelsArray
) then
207 function GetPoint(var str
: String; var point
: TDFPoint
): Boolean;
217 if Length(str
) < 3 then
220 for a
:= 1 to Length(str
) do
221 if (str
[a
] = ',') or (a
= Length(str
)) then
223 s
:= Copy(str
, 1, a
);
224 if s
[Length(s
)] = ',' then
225 SetLength(s
, Length(s
)-1);
228 if (Sscanf(s
, '%d:%d', [@x
, @y
]) < 2) or
229 (x
< -64) or (x
> 128) or
230 (y
< -64) or (y
> 128) then
242 function GetWeapPoints(str
: String; weapon
: Byte; anim
: Byte; dir
: TDirection
;
243 frames
: Word; backanim
: Boolean; var wpoints
: TWeaponPoints
): Boolean;
252 backanim
:= backanim
and (frames
> 2);
254 for a
:= 1 to frames
do
256 if not GetPoint(str
, wpoints
[weapon
, anim
, dir
, a
-1]) then
259 with wpoints
[weapon
, anim
, dir
, a
-1] do
261 X
:= X
- WEAPONBASE
[weapon
].X
;
262 Y
:= Y
- WEAPONBASE
[weapon
].Y
;
263 if dir
= TDirection
.D_LEFT
then
268 h
:= High(wpoints
[weapon
, anim
, dir
]);
270 for b
:= h
downto frames
do
271 wpoints
[weapon
, anim
, dir
, b
] := wpoints
[weapon
, anim
, dir
, h
-b
+1];
276 procedure g_PlayerMode_ExtendPoints (id
: Integer; AIdx
: Integer);
278 CopyAnim
: array [A_LASTBASE
+1..A_LASTEXT
] of Integer = (
279 A_WALK
, A_WALK
, A_WALK
, A_WALK
, A_WALK
,
280 A_STAND
, A_WALK
, A_ATTACK
, A_WALK
, A_SEEUP
, A_SEEDOWN
,
281 A_ATTACKUP
, A_ATTACKDOWN
283 var W
, I
, OIdx
: Integer; D
: TDirection
;
285 OIdx
:= CopyAnim
[AIdx
];
286 with PlayerModelsArray
[id
] do
288 for W
:= WP_FIRST
+ 1 to WP_LAST
do
290 for D
:= TDirection
.D_LEFT
to TDirection
.D_RIGHT
do
292 SetLength(WeaponPoints
[W
, AIdx
, D
], Length(WeaponPoints
[W
, OIdx
, D
]));
293 for I
:= 0 to High(WeaponPoints
[W
, AIdx
, D
]) do
294 WeaponPoints
[W
, AIdx
, D
, I
] := WeaponPoints
[W
, OIdx
, D
, I
]
300 function g_PlayerModel_CalcGibSize (pData
: Pointer; dataSize
, x
, y
, w
, h
: Integer): TRectWH
;
301 var i
, j
: Integer; done
: Boolean; img
: TImageData
;
303 function IsVoid (i
, j
: Integer): Boolean;
305 result
:= Byte((PByte(img
.bits
) + (y
+j
)*img
.width
*4 + (x
+i
)*4 + 3)^) = 0
310 assert(LoadImageFromMemory(pData
, dataSize
, img
));
312 (* trace x from right to left *)
313 done
:= false; i
:= 0;
314 while not done
and (i
< w
) do
317 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
318 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
323 (* trace y from up to down *)
324 done
:= false; j
:= 0;
325 while not done
and (j
< h
) do
328 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
329 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
334 (* trace x from right to left *)
335 done
:= false; i
:= w
- 1;
336 while not done
and (i
>= 0) do
339 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
340 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
341 result
.width
:= i
- result
.x
+ 1;
345 (* trace y from down to up *)
346 done
:= false; j
:= h
- 1;
347 while not done
and (j
>= 0) do
350 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
351 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
352 result
.height
:= j
- result
.y
+ 1;
359 function g_PlayerModel_Load(FileName
: string): Boolean;
362 a
, b
, len
, aa
, bb
, f
: Integer;
369 ok
, chk
, chk2
: Boolean;
371 e_WriteLog(Format('Loading player model "%s"...', [FileName
]), TMsgType
.Notify
);
375 WAD
:= TWADFile
.Create
;
376 WAD
.ReadFile(FileName
);
378 if {WAD.GetLastError <> DFWAD_NOERROR} not WAD
.isOpen
then
384 if not WAD
.GetResource('TEXT/MODEL', pData
, len
) then
390 config
:= TConfig
.CreateMem(pData
, len
);
393 s
:= config
.ReadStr('Model', 'name', '');
401 SetLength(PlayerModelsArray
, Length(PlayerModelsArray
)+1);
402 ID
:= High(PlayerModelsArray
);
404 prefix
:= FileName
+':TEXTURES\';
406 PlayerModelsArray
[ID
].Name
:= s
;
407 PlayerModelsArray
[ID
].Author
:= config
.ReadStr('Model', 'author', '');
408 PlayerModelsArray
[ID
].Description
:= config
.ReadStr('Model', 'description', '');
409 PlayerModelsArray
[ID
].FileName
:= FileName
;
410 with PlayerModelsArray
[ID
] do
412 Blood
.R
:= MAX(0, MIN(255, config
.ReadInt('Blood', 'R', 150)));
413 Blood
.G
:= MAX(0, MIN(255, config
.ReadInt('Blood', 'G', 0)));
414 Blood
.B
:= MAX(0, MIN(255, config
.ReadInt('Blood', 'B', 0)));
415 case config
.ReadStr('Blood', 'Kind', 'NORMAL') of
416 'NORMAL': Blood
.Kind
:= BLOOD_NORMAL
;
417 'SPARKS': Blood
.Kind
:= BLOOD_CSPARKS
;
418 'COMBINE': Blood
.Kind
:= BLOOD_COMBINE
;
420 Blood
.Kind
:= BLOOD_NORMAL
424 for b
:= A_STAND
to A_LAST
do
426 with PlayerModelsArray
[ID
].Anim
[TDirection
.D_RIGHT
, b
] do
428 Resource
:= config
.ReadStr(AnimNames
[b
], 'resource', '');
429 Mask
:= config
.ReadStr(AnimNames
[b
], 'mask', '');
430 Frames
:= config
.ReadInt(AnimNames
[b
], 'frames', 1);
431 Back
:= config
.ReadBool(AnimNames
[b
], 'backanim', False);
432 if (Resource
= '') or (Mask
= '') then
434 if b
<= A_LASTBASE
then
442 g_PlayerMode_ExtendPoints(ID
, b
);
448 for aa
:= WP_FIRST
+ 1 to WP_LAST
do
449 for bb
:= A_STAND
to A_LAST
do
450 for cc
:= TDirection
.D_LEFT
to TDirection
.D_RIGHT
do
452 f
:= PlayerModelsArray
[ID
].Anim
[cc
, bb
].Frames
;
453 if PlayerModelsArray
[ID
].Anim
[cc
, bb
].Back
and (f
> 2) then
455 SetLength(PlayerModelsArray
[ID
].WeaponPoints
[aa
, bb
, cc
], f
);
458 with PlayerModelsArray
[ID
].Anim
[TDirection
.D_LEFT
, b
] do
460 Frames
:= PlayerModelsArray
[ID
].Anim
[TDirection
.D_RIGHT
, b
].Frames
;
461 Back
:= PlayerModelsArray
[ID
].Anim
[TDirection
.D_RIGHT
, b
].Back
;
464 PlayerModelsArray
[ID
].ModelSpeed
[b
] := Max(1, config
.ReadInt(AnimNames
[b
], 'waitcount', 1) div 3);
467 with PlayerModelsArray
[ID
], config
do
469 prefix
:= FileName
+':SOUNDS\';
473 s
:= config
.ReadStr('Sound', 'pain'+IntToStr(a
), '');
476 SetLength(PainSounds
, Length(PainSounds
)+1);
477 g_Sound_CreateWAD(PainSounds
[High(PainSounds
)].ID
, prefix
+s
);
478 PainSounds
[High(PainSounds
)].Level
:= config
.ReadInt('Sound', 'painlevel'+IntToStr(a
), 1);
485 s
:= config
.ReadStr('Sound', 'die'+IntToStr(a
), '');
488 SetLength(DieSounds
, Length(DieSounds
)+1);
489 g_Sound_CreateWAD(DieSounds
[High(DieSounds
)].ID
, prefix
+s
);
490 DieSounds
[High(DieSounds
)].Level
:= config
.ReadInt('Sound', 'dielevel'+IntToStr(a
), 1);
495 SlopSound
:= Min(Max(config
.ReadInt('Sound', 'slop', 0), 0), 2);
497 GibsCount
:= config
.ReadInt('Gibs', 'count', 0);
498 GibsResource
:= config
.ReadStr('Gibs', 'resource', 'GIBS');
499 GibsMask
:= config
.ReadStr('Gibs', 'mask', 'GIBSMASK');
500 GibsOnce
:= config
.ReadInt('Gibs', 'once', -1);
503 for aa
:= WP_FIRST
+ 1 to WP_LAST
do
504 for bb
:= A_STAND
to A_LAST
do
505 if not (bb
in [A_DIE1
, A_DIE2
, A_PAIN
]) then
507 chk
:= GetWeapPoints(
508 config
.ReadStr(AnimNames
[bb
], WeapNames
[aa
] + '_points', ''),
512 Anim
[TDirection
.D_RIGHT
, bb
].Frames
,
513 Anim
[TDirection
.D_RIGHT
, bb
].Back
,
516 if ok
and (not chk
) and (aa
= WEAPON_FLAMETHROWER
) then
518 // workaround for flamethrower
519 chk
:= GetWeapPoints(
520 config
.ReadStr(AnimNames
[bb
], WeapNames
[WEAPON_PLASMA
] + '_points', ''),
524 Anim
[TDirection
.D_RIGHT
, bb
].Frames
,
525 Anim
[TDirection
.D_RIGHT
, bb
].Back
,
529 for f
:= 0 to High(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
]) do
534 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
535 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 8);
537 A_WALKATTACK
, A_WALK
:
539 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 9);
540 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 9);
544 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
545 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 8);
547 A_WALKSEEUP
, A_SEEUP
:
549 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
550 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 16);
552 A_WALKSEEDOWN
, A_SEEDOWN
:
554 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
555 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 5);
557 A_WALKATTACKUP
, A_ATTACKUP
:
559 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
560 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 16);
562 A_WALKATTACKDOWN
, A_ATTACKDOWN
:
564 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
565 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 4);
571 ok
:= ok
and (chk
or (bb
> A_LASTBASE
));
573 chk2
:= GetWeapPoints(
574 config
.ReadStr(AnimNames
[bb
], WeapNames
[aa
] + '2_points', ''),
578 Anim
[TDirection
.D_LEFT
, bb
].Frames
,
579 Anim
[TDirection
.D_LEFT
, bb
].Back
,
584 for f
:= 0 to High(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
]) do
586 WeaponPoints
[aa
, bb
, TDirection
.D_LEFT
, f
].X
:= -WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
;
587 WeaponPoints
[aa
, bb
, TDirection
.D_LEFT
, f
].Y
:= WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
;
591 if not ok
then Break
;
593 {if ok then g_Console_Add(Info.Name+' weapon points ok')
594 else g_Console_Add(Info.Name+' weapon points fail');}
595 PlayerModelsArray
[ID
].HaveWeapon
:= ok
;
597 s
:= config
.ReadStr('Model', 'flag_point', '');
598 if not GetPoint(s
, FlagPoint
) then
599 FlagPoint
:= FLAG_DEFPOINT
;
601 FlagAngle
:= config
.ReadInt('Model', 'flag_angle', FLAG_DEFANGLE
);
610 function g_PlayerModel_Get (ModelName
: String): TPlayerModel
;
615 if PlayerModelsArray
= nil then Exit
;
617 for a
:= 0 to High(PlayerModelsArray
) do
619 if AnsiLowerCase(PlayerModelsArray
[a
].Name
) = AnsiLowerCase(ModelName
) then
621 Result
:= TPlayerModel
.Create
;
623 with PlayerModelsArray
[a
] do
626 Result
.ChangeAnimation(A_STAND
, True);
633 function g_PlayerModel_GetGibs (ModelID
: Integer; var Gibs
: TGibsArray
): Boolean;
634 var i
, b
: Integer; c
: Boolean;
638 if (PlayerModelsArray
= nil) or (gGibsCount
= 0) then
642 SetLength(Gibs
, gGibsCount
);
643 for i
:= 0 to High(Gibs
) do
645 if c
and (PlayerModelsArray
[ModelID
].GibsCount
= 1) then
652 b
:= Random(PlayerModelsArray
[ModelID
].GibsCount
);
653 until not ((PlayerModelsArray
[ModelID
].GibsOnce
= b
+ 1) and c
);
657 c
:= PlayerModelsArray
[ModelID
].GibsOnce
= b
+ 1;
662 function g_PlayerModel_GetNames(): SSArray
;
668 if PlayerModelsArray
= nil then Exit
;
670 for i
:= 0 to High(PlayerModelsArray
) do
672 SetLength(Result
, Length(Result
)+1);
673 Result
[High(Result
)] := PlayerModelsArray
[i
].Name
;
677 function g_PlayerModel_GetBlood(ModelName
: string): TModelBlood
;
684 Result
.Kind
:= BLOOD_NORMAL
;
685 if PlayerModelsArray
= nil then Exit
;
687 for a
:= 0 to High(PlayerModelsArray
) do
688 if PlayerModelsArray
[a
].Name
= ModelName
then
690 Result
:= PlayerModelsArray
[a
].Blood
;
695 procedure g_PlayerModel_FreeData();
698 e_WriteLog('Releasing models...', TMsgType
.Notify
);
700 if PlayerModelsArray
= nil then Exit
;
702 for i
:= 0 to High(PlayerModelsArray
) do
704 with PlayerModelsArray
[i
] do
706 if PainSounds
<> nil then
707 for b
:= 0 to High(PainSounds
) do
708 e_DeleteSound(PainSounds
[b
].ID
);
709 if DieSounds
<> nil then
710 for b
:= 0 to High(DieSounds
) do
711 e_DeleteSound(DieSounds
[b
].ID
);
714 PlayerModelsArray
:= nil;
719 procedure TPlayerModel
.ChangeAnimation (Animation
: Byte; Force
: Boolean = False);
720 var once
: Boolean; speed
, count
: Integer;
723 if FCurrentAnimation
= Animation
then
725 FCurrentAnimation
:= Animation
;
726 once
:= FCurrentAnimation
in [A_STAND
, A_WALK
];
727 speed
:= PlayerModelsArray
[FID
].ModelSpeed
[FCurrentAnimation
];
728 count
:= PlayerModelsArray
[FID
].Anim
[FDirection
, FCurrentAnimation
].Frames
;
729 FAnimState
:= TAnimationState
.Create(once
, speed
, count
);
732 destructor TPlayerModel
.Destroy();
738 function TPlayerModel
.PlaySound(SoundType
, Level
: Byte; X
, Y
: Integer): Boolean;
740 TempArray
: array of DWORD
;
744 SetLength(TempArray
, 0);
746 if SoundType
= MODELSOUND_PAIN
then
748 if PlayerModelsArray
[FID
].PainSounds
= nil then Exit
;
750 for a
:= 0 to High(PlayerModelsArray
[FID
].PainSounds
) do
751 if PlayerModelsArray
[FID
].PainSounds
[a
].Level
= Level
then
753 SetLength(TempArray
, Length(TempArray
) + 1);
754 TempArray
[High(TempArray
)] := PlayerModelsArray
[FID
].PainSounds
[a
].ID
;
759 if (Level
in [2, 3, 5]) and (PlayerModelsArray
[FID
].SlopSound
> 0) then
761 g_Sound_PlayExAt('SOUND_MONSTER_SLOP', X
, Y
);
762 if PlayerModelsArray
[FID
].SlopSound
= 1 then
768 if PlayerModelsArray
[FID
].DieSounds
= nil then Exit
;
770 for a
:= 0 to High(PlayerModelsArray
[FID
].DieSounds
) do
771 if PlayerModelsArray
[FID
].DieSounds
[a
].Level
= Level
then
773 SetLength(TempArray
, Length(TempArray
) + 1);
774 TempArray
[High(TempArray
)] := PlayerModelsArray
[FID
].DieSounds
[a
].ID
;
776 if (TempArray
= nil) and (Level
= 5) then
778 g_Sound_PlayExAt('SOUND_MONSTER_SLOP', X
, Y
);
784 if TempArray
= nil then Exit
;
786 g_Sound_PlayAt(TempArray
[Random(Length(TempArray
))], X
, Y
);
791 procedure TPlayerModel
.SetColor(Red
, Green
, Blue
: Byte);
798 procedure TPlayerModel
.SetFire (Fire
: Boolean);
801 FFireCounter
:= PlayerModelsArray
[FID
].ModelSpeed
[A_ATTACK
] * PlayerModelsArray
[FID
].Anim
[TDirection
.D_RIGHT
, A_ATTACK
].Frames
806 function TPlayerModel
.GetFire (): Boolean;
808 Result
:= FFireCounter
> 0
811 procedure TPlayerModel
.SetFlag (Flag
: Byte);
816 procedure TPlayerModel
.SetWeapon (Weapon
: Byte);
818 FCurrentWeapon
:= Weapon
821 function TPlayerModel
.GetBlood (): TModelBlood
;
823 Result
:= PlayerModelsArray
[FID
].Blood
826 function TPlayerModel
.GetName (): String;
828 Result
:= PlayerModelsArray
[FID
].Name
831 procedure TPlayerModel
.Update
;
833 if FAnimState
<> nil then
835 if FFireCounter
> 0 then
839 procedure g_PlayerModel_LoadAll
;
842 knownFiles
: array of AnsiString = nil;
847 // load models from all possible wad types, in all known directories
848 // this does a loosy job (linear search, ooph!), but meh
849 for wext
in wadExtensions
do
851 for f
:= High(ModelDirs
) downto Low(ModelDirs
) do
853 if (FindFirst(ModelDirs
[f
]+DirectorySeparator
+'*'+wext
, faAnyFile
, SR
) = 0) then
857 for s
in knownFiles
do
859 if (strEquCI1251(forceFilenameExt(SR
.Name
, ''), forceFilenameExt(ExtractFileName(s
), ''))) then
867 SetLength(knownFiles
, length(knownFiles
)+1);
868 knownFiles
[High(knownFiles
)] := ModelDirs
[f
]+DirectorySeparator
+SR
.Name
;
870 until (FindNext(SR
) <> 0);
875 if (length(knownFiles
) = 0) then
876 raise Exception
.Create('no player models found!');
877 if (length(knownFiles
) = 1) then
878 e_LogWriteln('1 player model found.', TMsgType
.Notify
)
880 e_LogWritefln('%d player models found.', [Integer(length(knownFiles
))], TMsgType
.Notify
);
881 for s
in knownFiles
do
882 if not g_PlayerModel_Load(s
) then
883 e_LogWritefln('Error loading model "%s"', [s
], TMsgType
.Warning
);