c3bfdac266dcd9090cf9df3b418b988364357a22
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/>.
16 {$INCLUDE ../shared/a_modes.inc}
23 {$IFDEF USE_MEMPOOL}mempool
,{$ENDIF}
24 MAPDEF
, g_textures
, g_basic
, g_weapons
, e_graphics
, utils
;
42 A_WALKATTACKDOWN
= 14;
46 A_FISTWALKATTACK
= 18;
50 A_FISTATTACKDOWN
= 22;
53 A_LASTEXT
= A_FISTATTACKDOWN
;
79 TModelSoundArray
= Array of TModelSound
;
80 TGibsArray
= Array of TGibSprite
;
81 TWeaponPoints
= Array [WP_FIRST
+ 1..WP_LAST
] of
82 Array [A_STAND
..A_LAST
] of
83 Array [TDirection
.D_LEFT
..TDirection
.D_RIGHT
] of Array of TDFPoint
;
85 TPlayerModel
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
88 FDirection
: TDirection
;
90 FCurrentAnimation
: Byte;
91 FAnim
: Array [TDirection
.D_LEFT
..TDirection
.D_RIGHT
] of Array [A_STAND
..A_LAST
] of TAnimation
;
92 FMaskAnim
: Array [TDirection
.D_LEFT
..TDirection
.D_RIGHT
] of Array [A_STAND
..A_LAST
] of TAnimation
;
93 FWeaponPoints
: TWeaponPoints
;
94 FPainSounds
: TModelSoundArray
;
95 FDieSounds
: TModelSoundArray
;
100 FFlagPoint
: TDFPoint
;
101 FFlagAngle
: SmallInt;
102 FFlagAnim
: TAnimation
;
107 destructor Destroy(); override;
108 procedure ChangeAnimation(Animation
: Byte; Force
: Boolean = False);
109 function GetCurrentAnimation
: TAnimation
;
110 function GetCurrentAnimationMask
: TAnimation
;
111 procedure SetColor(Red
, Green
, Blue
: Byte);
112 procedure SetWeapon(Weapon
: Byte);
113 procedure SetFlag(Flag
: Byte);
114 procedure SetFire(Fire
: Boolean);
115 function PlaySound(SoundType
, Level
: Byte; X
, Y
: Integer): Boolean;
117 procedure Draw(X
, Y
: Integer; Alpha
: Byte = 0);
120 property Fire
: Boolean read FFire
;
121 property Direction
: TDirection read FDirection write FDirection
;
122 property Animation
: Byte read FCurrentAnimation
;
123 property Weapon
: Byte read FCurrentWeapon
;
124 property Name
: String read FName
;
127 property Color
: TRGB read FColor write FColor
;
130 procedure g_PlayerModel_LoadData();
131 procedure g_PlayerModel_FreeData();
132 function g_PlayerModel_Load(FileName
: String): Boolean;
133 function g_PlayerModel_GetNames(): SSArray
;
134 function g_PlayerModel_GetInfo(ModelName
: String): TModelInfo
;
135 function g_PlayerModel_Get(ModelName
: String): TPlayerModel
;
136 function g_PlayerModel_GetAnim(ModelName
: String; Anim
: Byte; var _Anim
, _Mask
: TAnimation
): Boolean;
137 function g_PlayerModel_GetGibs(ModelName
: String; var Gibs
: TGibsArray
): Boolean;
148 g_main
, g_sound
, g_console
, SysUtils
, g_player
, CONFIG
,
149 e_sound
, g_options
, g_map
, Math
, e_log
, wadreader
;
152 TPlayerModelInfo
= record
154 ModelSpeed
: Array [A_STAND
..A_PAIN
] of Byte;
157 WeaponPoints
: TWeaponPoints
;
159 PainSounds
: TModelSoundArray
;
160 DieSounds
: TModelSoundArray
;
172 FLAG_BASEPOINT
: TDFPoint
= (X
:16; Y
:43);
173 FLAG_DEFPOINT
: TDFPoint
= (X
:32; Y
:16);
175 WEAPONBASE
: Array [WP_FIRST
+ 1..WP_LAST
] of TDFPoint
=
176 ((X
:8; Y
:4), (X
:8; Y
:8), (X
:16; Y
:16), (X
:16; Y
:24),
177 (X
:16; Y
:16), (X
:24; Y
:24), (X
:16; Y
:16), (X
:24; Y
:24),
178 (X
:16; Y
:16), (X
:8; Y
:8));
180 AnimNames
: Array [A_STAND
..A_LASTEXT
] of String =
181 ('StandAnim','WalkAnim','Die1Anim','Die2Anim','AttackAnim',
182 'SeeUpAnim','SeeDownAnim','AttackUpAnim','AttackDownAnim','PainAnim',
184 'WalkAttackAnim', 'WalkSeeUpAnim', 'WalkSeeDownAnim',
185 'WalkAttackUpAnim', 'WalkAttackDownAnim', 'FistStandAnim', 'FistWalkAnim',
186 'FistAttackAnim', 'FistWalkAttackAnim', 'FistSeeUpAnim', 'FistSeeDownAnim',
187 'FistAttackUpAnim', 'FistAttackDownAnim');
188 WeapNames
: Array [WP_FIRST
+ 1..WP_LAST
] of String =
189 ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm');
192 WeaponID
: Array [WP_FIRST
+ 1..WP_LAST
] of
193 Array [W_POS_NORMAL
..W_POS_DOWN
] of
194 Array [W_ACT_NORMAL
..W_ACT_FIRE
] of DWORD
;
195 PlayerModelsArray
: Array of TPlayerModelInfo
;
197 procedure g_PlayerModel_LoadData();
201 for a
:= WP_FIRST
+ 1 to WP_LAST
do
203 g_Texture_CreateWAD(WeaponID
[a
][W_POS_NORMAL
][W_ACT_NORMAL
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
]));
204 g_Texture_CreateWAD(WeaponID
[a
][W_POS_NORMAL
][W_ACT_FIRE
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
])+'_FIRE');
205 g_Texture_CreateWAD(WeaponID
[a
][W_POS_UP
][W_ACT_NORMAL
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
])+'_UP');
206 g_Texture_CreateWAD(WeaponID
[a
][W_POS_UP
][W_ACT_FIRE
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
])+'_UP_FIRE');
207 g_Texture_CreateWAD(WeaponID
[a
][W_POS_DOWN
][W_ACT_NORMAL
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
])+'_DN');
208 g_Texture_CreateWAD(WeaponID
[a
][W_POS_DOWN
][W_ACT_FIRE
], GameWAD
+':WEAPONS\'+UpperCase(WeapNames
[a
])+'_DN_FIRE');
212 function GetPoint(var str
: String; var point
: TDFPoint
): Boolean;
222 if Length(str
) < 3 then
225 for a
:= 1 to Length(str
) do
226 if (str
[a
] = ',') or (a
= Length(str
)) then
228 s
:= Copy(str
, 1, a
);
229 if s
[Length(s
)] = ',' then
230 SetLength(s
, Length(s
)-1);
233 if (Sscanf(s
, '%d:%d', [@x
, @y
]) < 2) or
234 (x
< -64) or (x
> 128) or
235 (y
< -64) or (y
> 128) then
247 function GetWeapPoints(str
: String; weapon
: Byte; anim
: Byte; dir
: TDirection
;
248 frames
: Word; backanim
: Boolean; var wpoints
: TWeaponPoints
): Boolean;
257 backanim
:= backanim
and (frames
> 2);
259 for a
:= 1 to frames
do
261 if not GetPoint(str
, wpoints
[weapon
, anim
, dir
, a
-1]) then
264 with wpoints
[weapon
, anim
, dir
, a
-1] do
266 X
:= X
- WEAPONBASE
[weapon
].X
;
267 Y
:= Y
- WEAPONBASE
[weapon
].Y
;
268 if dir
= TDirection
.D_LEFT
then
273 h
:= High(wpoints
[weapon
, anim
, dir
]);
275 for b
:= h
downto frames
do
276 wpoints
[weapon
, anim
, dir
, b
] := wpoints
[weapon
, anim
, dir
, h
-b
+1];
281 procedure ExtAnimFromBaseAnim(MName
: String; AIdx
: Integer);
283 CopyAnim
: array [A_LASTBASE
+1..A_LASTEXT
] of Integer = (
284 A_WALK
, A_WALK
, A_WALK
, A_WALK
, A_WALK
,
285 A_STAND
, A_WALK
, A_ATTACK
, A_WALK
, A_SEEUP
, A_SEEDOWN
,
286 A_ATTACKUP
, A_ATTACKDOWN
291 AName
, OName
: String;
293 // HACK: shitty workaround to duplicate base animations
294 // in place of extended, replace with something better later
296 Assert((AIdx
> A_LASTBASE
) and (AIdx
<= A_LASTEXT
));
297 OIdx
:= CopyAnim
[AIdx
];
299 AName
:= MName
+ '_RIGHTANIM' + IntToStr(AIdx
);
300 OName
:= MName
+ '_RIGHTANIM' + IntToStr(OIdx
);
301 Assert(g_Frames_Dup(AName
, OName
));
302 Assert(g_Frames_Dup(AName
+ '_MASK', OName
+ '_MASK'));
303 AName
:= MName
+ '_LEFTANIM' + IntToStr(AIdx
);
304 OName
:= MName
+ '_LEFTANIM' + IntToStr(OIdx
);
305 if g_Frames_Exists(AName
) then
307 g_Frames_Dup(AName
, OName
);
308 g_Frames_Dup(AName
+ '_MASK', OName
+ '_MASK');
311 with PlayerModelsArray
[High(PlayerModelsArray
)] do
313 for W
:= WP_FIRST
+ 1 to WP_LAST
do
315 for D
:= TDirection
.D_LEFT
to TDirection
.D_RIGHT
do
317 SetLength(WeaponPoints
[W
, AIdx
, D
], Length(WeaponPoints
[W
, OIdx
, D
]));
318 for I
:= 0 to High(WeaponPoints
[W
, AIdx
, D
]) do
319 WeaponPoints
[W
, AIdx
, D
, I
] := WeaponPoints
[W
, OIdx
, D
, I
]
325 function g_PlayerModel_Load(FileName
: string): Boolean;
328 a
, b
, len
, lenpd
, lenpd2
, aa
, bb
, f
: Integer;
331 pData
, pData2
: Pointer;
337 e_WriteLog(Format('Loading player model: %s', [ExtractFileName(FileName
)]), TMsgType
.Notify
);
341 WAD
:= TWADFile
.Create
;
342 WAD
.ReadFile(FileName
);
344 if {WAD.GetLastError <> DFWAD_NOERROR} not WAD
.isOpen
then
350 if not WAD
.GetResource('TEXT/MODEL', pData
, len
) then
356 config
:= TConfig
.CreateMem(pData
, len
);
359 s
:= config
.ReadStr('Model', 'name', '');
367 SetLength(PlayerModelsArray
, Length(PlayerModelsArray
)+1);
368 ID
:= High(PlayerModelsArray
);
370 prefix
:= FileName
+':TEXTURES\';
372 with PlayerModelsArray
[ID
].Info
do
375 Author
:= config
.ReadStr('Model', 'author', '');
376 Description
:= config
.ReadStr('Model', 'description', '');
379 for b
:= A_STAND
to A_LAST
do
381 aname
:= s
+'_RIGHTANIM'+IntToStr(b
);
382 //e_LogWritefln('### MODEL FILE: [%s]', [prefix+config.ReadStr(AnimNames[b], 'resource', '')]);
383 if not (g_Frames_CreateWAD(nil, aname
,
384 prefix
+config
.ReadStr(AnimNames
[b
], 'resource', ''),
385 64, 64, config
.ReadInt(AnimNames
[b
], 'frames', 1),
386 config
.ReadBool(AnimNames
[b
], 'backanim', False)) and
387 g_Frames_CreateWAD(nil, aname
+'_MASK',
388 prefix
+config
.ReadStr(AnimNames
[b
], 'mask', ''),
389 64, 64, config
.ReadInt(AnimNames
[b
], 'frames', 1),
390 config
.ReadBool(AnimNames
[b
], 'backanim', False))) then
392 if b
<= A_LASTBASE
then
400 ExtAnimFromBaseAnim(s
, b
);
405 for aa
:= WP_FIRST
+ 1 to WP_LAST
do
406 for bb
:= A_STAND
to A_LAST
do
407 for cc
:= TDirection
.D_LEFT
to TDirection
.D_RIGHT
do
409 f
:= config
.ReadInt(AnimNames
[bb
], 'frames', 1);
410 if config
.ReadBool(AnimNames
[bb
], 'backanim', False) then
411 if f
> 2 then f
:= 2*f
-2;
412 SetLength(PlayerModelsArray
[ID
].WeaponPoints
[aa
, bb
, cc
], f
);
415 if (config
.ReadStr(AnimNames
[b
], 'resource2', '') <> '') and
416 (config
.ReadStr(AnimNames
[b
], 'mask2', '') <> '') then
418 aname
:= s
+'_LEFTANIM'+IntToStr(b
);
419 g_Frames_CreateWAD(nil, aname
,
420 prefix
+config
.ReadStr(AnimNames
[b
], 'resource2', ''),
421 64, 64, config
.ReadInt(AnimNames
[b
], 'frames', 1),
422 config
.ReadBool(AnimNames
[b
], 'backanim', False));
424 g_Frames_CreateWAD(nil, aname
+'_MASK',
425 prefix
+config
.ReadStr(AnimNames
[b
], 'mask2', ''),
426 64, 64, config
.ReadInt(AnimNames
[b
], 'frames', 1),
427 config
.ReadBool(AnimNames
[b
], 'backanim', False));
430 PlayerModelsArray
[ID
].ModelSpeed
[b
] := Max(1, config
.ReadInt(AnimNames
[b
], 'waitcount', 1) div 3);
433 with PlayerModelsArray
[ID
], config
do
435 prefix
:= FileName
+':SOUNDS\';
439 s
:= config
.ReadStr('Sound', 'pain'+IntToStr(a
), '');
442 SetLength(PainSounds
, Length(PainSounds
)+1);
443 g_Sound_CreateWAD(PainSounds
[High(PainSounds
)].ID
, prefix
+s
);
444 PainSounds
[High(PainSounds
)].Level
:= config
.ReadInt('Sound', 'painlevel'+IntToStr(a
), 1);
451 s
:= config
.ReadStr('Sound', 'die'+IntToStr(a
), '');
454 SetLength(DieSounds
, Length(DieSounds
)+1);
455 g_Sound_CreateWAD(DieSounds
[High(DieSounds
)].ID
, prefix
+s
);
456 DieSounds
[High(DieSounds
)].Level
:= config
.ReadInt('Sound', 'dielevel'+IntToStr(a
), 1);
461 SlopSound
:= Min(Max(config
.ReadInt('Sound', 'slop', 0), 0), 2);
463 SetLength(Gibs
, ReadInt('Gibs', 'count', 0));
466 (WAD
.GetResource('TEXTURES/'+config
.ReadStr('Gibs', 'resource', 'GIBS'), pData
, lenpd
)) and
467 (WAD
.GetResource('TEXTURES/'+config
.ReadStr('Gibs', 'mask', 'GIBSMASK'), pData2
, lenpd2
)) then
469 for a
:= 0 to High(Gibs
) do
470 if e_CreateTextureMemEx(pData
, lenpd
, Gibs
[a
].ID
, a
*32, 0, 32, 32) and
471 e_CreateTextureMemEx(pData2
, lenpd2
, Gibs
[a
].MaskID
, a
*32, 0, 32, 32) then
473 Gibs
[a
].Rect
:= e_GetTextureSize2(Gibs
[a
].ID
);
475 if Height
> 3 then Height
:= Height
-1-Random(2);
476 Gibs
[a
].OnlyOne
:= config
.ReadInt('Gibs', 'once', -1) = a
+1;
484 for aa
:= WP_FIRST
+ 1 to WP_LAST
do
485 for bb
:= A_STAND
to A_LAST
do
486 if not (bb
in [A_DIE1
, A_DIE2
, A_PAIN
]) then
488 chk
:= GetWeapPoints(config
.ReadStr(AnimNames
[bb
], WeapNames
[aa
]+'_points', ''), aa
, bb
, TDirection
.D_RIGHT
,
489 config
.ReadInt(AnimNames
[bb
], 'frames', 0),
490 config
.ReadBool(AnimNames
[bb
], 'backanim', False),
492 if ok
and (not chk
) and (aa
= WEAPON_FLAMETHROWER
) then
494 // workaround for flamethrower
495 chk
:= GetWeapPoints(config
.ReadStr(AnimNames
[bb
], WeapNames
[WEAPON_PLASMA
]+'_points', ''), aa
, bb
, TDirection
.D_RIGHT
,
496 config
.ReadInt(AnimNames
[bb
], 'frames', 0),
497 config
.ReadBool(AnimNames
[bb
], 'backanim', False),
500 for f
:= 0 to High(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
]) do
505 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
506 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 8);
508 A_WALKATTACK
, A_WALK
:
510 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 9);
511 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 9);
515 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
516 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 8);
518 A_WALKSEEUP
, A_SEEUP
:
520 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
521 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 16);
523 A_WALKSEEDOWN
, A_SEEDOWN
:
525 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
526 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 5);
528 A_WALKATTACKUP
, A_ATTACKUP
:
530 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 5);
531 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 16);
533 A_WALKATTACKDOWN
, A_ATTACKDOWN
:
535 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
, 6);
536 Dec(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
, 4);
541 ok
:= ok
and (chk
or (bb
> A_LASTBASE
));
543 if not GetWeapPoints(config
.ReadStr(AnimNames
[bb
], WeapNames
[aa
]+'2_points', ''), aa
, bb
, TDirection
.D_LEFT
,
544 config
.ReadInt(AnimNames
[bb
], 'frames', 0),
545 config
.ReadBool(AnimNames
[bb
], 'backanim', False),
547 for f
:= 0 to High(WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
]) do
549 WeaponPoints
[aa
, bb
, TDirection
.D_LEFT
, f
].X
:= -WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].X
;
550 WeaponPoints
[aa
, bb
, TDirection
.D_LEFT
, f
].Y
:= WeaponPoints
[aa
, bb
, TDirection
.D_RIGHT
, f
].Y
;
553 if not ok
then Break
;
555 {if ok then g_Console_Add(Info.Name+' weapon points ok')
556 else g_Console_Add(Info.Name+' weapon points fail');}
557 Info
.HaveWeapon
:= ok
;
559 s
:= config
.ReadStr('Model', 'flag_point', '');
560 if not GetPoint(s
, FlagPoint
) then FlagPoint
:= FLAG_DEFPOINT
;
562 FlagAngle
:= config
.ReadInt('Model', 'flag_angle', FLAG_DEFANGLE
);
571 function g_PlayerModel_Get(ModelName
: String): TPlayerModel
;
579 if PlayerModelsArray
= nil then Exit
;
581 for a
:= 0 to High(PlayerModelsArray
) do
582 if AnsiLowerCase(PlayerModelsArray
[a
].Info
.Name
) = AnsiLowerCase(ModelName
) then
584 Result
:= TPlayerModel
.Create
;
586 with PlayerModelsArray
[a
] do
588 Result
.FName
:= Info
.Name
;
590 for b
:= A_STAND
to A_LAST
do
592 if not (g_Frames_Get(ID
, Info
.Name
+'_RIGHTANIM'+IntToStr(b
)) and
593 g_Frames_Get(ID2
, Info
.Name
+'_RIGHTANIM'+IntToStr(b
)+'_MASK')) then
600 Result
.FAnim
[TDirection
.D_RIGHT
][b
] := TAnimation
.Create(ID
, b
in [A_STAND
, A_WALK
], ModelSpeed
[b
]);
602 Result
.FMaskAnim
[TDirection
.D_RIGHT
][b
] := TAnimation
.Create(ID2
, b
in [A_STAND
, A_WALK
], ModelSpeed
[b
]);
604 if g_Frames_Exists(Info
.Name
+'_LEFTANIM'+IntToStr(b
)) and
605 g_Frames_Exists(Info
.Name
+'_LEFTANIM'+IntToStr(b
)+'_MASK') then
606 if g_Frames_Get(ID
, Info
.Name
+'_LEFTANIM'+IntToStr(b
)) and
607 g_Frames_Get(ID2
, Info
.Name
+'_LEFTANIM'+IntToStr(b
)+'_MASK') then
609 Result
.FAnim
[TDirection
.D_LEFT
][b
] := TAnimation
.Create(ID
, b
in [A_STAND
, A_WALK
], ModelSpeed
[b
]);
611 Result
.FMaskAnim
[TDirection
.D_LEFT
][b
] := TAnimation
.Create(ID2
, b
in [A_STAND
, A_WALK
], ModelSpeed
[b
]);
615 Result
.FPainSounds
:= PainSounds
;
616 Result
.FDieSounds
:= DieSounds
;
617 Result
.FSlopSound
:= SlopSound
;
618 Result
.FDrawWeapon
:= Info
.HaveWeapon
;
619 Result
.FWeaponPoints
:= WeaponPoints
;
621 Result
.FFlagPoint
:= FlagPoint
;
622 Result
.FFlagAngle
:= FlagAngle
;
629 function g_PlayerModel_GetAnim(ModelName
: string; Anim
: Byte; var _Anim
, _Mask
: TAnimation
): Boolean;
637 if PlayerModelsArray
= nil then Exit
;
638 for a
:= 0 to High(PlayerModelsArray
) do
639 if PlayerModelsArray
[a
].Info
.Name
= ModelName
then
640 with PlayerModelsArray
[a
] do
642 if Anim
in [A_STAND
, A_WALK
] then c
:= True else c
:= False;
644 if not g_Frames_Get(ID
, Info
.Name
+'_RIGHTANIM'+IntToStr(Anim
)) then
645 if not g_Frames_Get(ID
, Info
.Name
+'_LEFTANIM'+IntToStr(Anim
)) then Exit
;
647 _Anim
:= TAnimation
.Create(ID
, c
, ModelSpeed
[Anim
]);
648 _Anim
.Speed
:= ModelSpeed
[Anim
];
650 if not g_Frames_Get(ID
, Info
.Name
+'_RIGHTANIM'+IntToStr(Anim
)+'_MASK') then
651 if not g_Frames_Get(ID
, Info
.Name
+'_LEFTANIM'+IntToStr(Anim
)+'_MASK') then Exit
;
653 _Mask
:= TAnimation
.Create(ID
, c
, ModelSpeed
[Anim
]);
654 _Mask
.Speed
:= ModelSpeed
[Anim
];
662 function g_PlayerModel_GetGibs(ModelName
: string; var Gibs
: TGibsArray
): Boolean;
669 if PlayerModelsArray
= nil then Exit
;
670 if gGibsCount
= 0 then Exit
;
674 SetLength(Gibs
, gGibsCount
);
676 for a
:= 0 to High(PlayerModelsArray
) do
677 if PlayerModelsArray
[a
].Info
.Name
= ModelName
then
679 for i
:= 0 to High(Gibs
) do
681 if c
and (Length(PlayerModelsArray
[a
].Gibs
) = 1) then
688 b
:= Random(Length(PlayerModelsArray
[a
].Gibs
));
689 until not (PlayerModelsArray
[a
].Gibs
[b
].OnlyOne
and c
);
691 Gibs
[i
] := PlayerModelsArray
[a
].Gibs
[b
];
693 if Gibs
[i
].OnlyOne
then c
:= True;
701 function g_PlayerModel_GetNames(): SSArray
;
707 if PlayerModelsArray
= nil then Exit
;
709 for i
:= 0 to High(PlayerModelsArray
) do
711 SetLength(Result
, Length(Result
)+1);
712 Result
[High(Result
)] := PlayerModelsArray
[i
].Info
.Name
;
716 function g_PlayerModel_GetInfo(ModelName
: string): TModelInfo
;
720 FillChar(Result
, SizeOf(Result
), 0);
721 if PlayerModelsArray
= nil then Exit
;
723 for a
:= 0 to High(PlayerModelsArray
) do
724 if PlayerModelsArray
[a
].Info
.Name
= ModelName
then
726 Result
:= PlayerModelsArray
[a
].Info
;
731 procedure g_PlayerModel_FreeData();
736 for a
:= WP_FIRST
+ 1 to WP_LAST
do
737 for b
:= W_POS_NORMAL
to W_POS_DOWN
do
738 for c
:= W_ACT_NORMAL
to W_ACT_FIRE
do
739 e_DeleteTexture(WeaponID
[a
][b
][c
]);
741 e_WriteLog('Releasing models...', TMsgType
.Notify
);
743 if PlayerModelsArray
= nil then Exit
;
745 for i
:= 0 to High(PlayerModelsArray
) do
746 with PlayerModelsArray
[i
] do
748 for a
:= A_STAND
to A_LAST
do
750 g_Frames_DeleteByName(Info
.Name
+'_LEFTANIM'+IntToStr(a
));
751 g_Frames_DeleteByName(Info
.Name
+'_LEFTANIM'+IntToStr(a
)+'_MASK');
752 g_Frames_DeleteByName(Info
.Name
+'_RIGHTANIM'+IntToStr(a
));
753 g_Frames_DeleteByName(Info
.Name
+'_RIGHTANIM'+IntToStr(a
)+'_MASK');
756 if PainSounds
<> nil then
757 for b
:= 0 to High(PainSounds
) do
758 e_DeleteSound(PainSounds
[b
].ID
);
760 if DieSounds
<> nil then
761 for b
:= 0 to High(DieSounds
) do
762 e_DeleteSound(DieSounds
[b
].ID
);
765 for b
:= 0 to High(Gibs
) do
767 e_DeleteTexture(Gibs
[b
].ID
);
768 e_DeleteTexture(Gibs
[b
].MaskID
);
772 PlayerModelsArray
:= nil;
777 procedure TPlayerModel
.ChangeAnimation(Animation
: Byte; Force
: Boolean = False);
779 if not Force
then if FCurrentAnimation
= Animation
then Exit
;
781 FCurrentAnimation
:= Animation
;
783 if (FDirection
= TDirection
.D_LEFT
) and
784 (FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) and
785 (FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
787 FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Reset
;
788 FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Reset
;
792 FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Reset
;
793 FMaskAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Reset
;
797 destructor TPlayerModel
.Destroy();
801 for a
:= A_STAND
to A_LAST
do
803 FAnim
[TDirection
.D_LEFT
][a
].Free();
804 FMaskAnim
[TDirection
.D_LEFT
][a
].Free();
805 FAnim
[TDirection
.D_RIGHT
][a
].Free();
806 FMaskAnim
[TDirection
.D_RIGHT
][a
].Free();
812 procedure TPlayerModel
.Draw(X
, Y
: Integer; Alpha
: Byte = 0);
819 if Direction
= TDirection
.D_LEFT
then
820 Mirror
:= TMirrorType
.None
822 Mirror
:= TMirrorType
.Horizontal
;
824 if (FFlag
<> FLAG_NONE
) and (FFlagAnim
<> nil) and
825 (not (FCurrentAnimation
in [A_DIE1
, A_DIE2
])) then
827 p
.X
:= IfThen(Direction
= TDirection
.D_LEFT
,
829 64-FLAG_BASEPOINT
.X
);
830 p
.Y
:= FLAG_BASEPOINT
.Y
;
832 FFlagAnim
.DrawEx(X
+IfThen(Direction
= TDirection
.D_LEFT
, FFlagPoint
.X
-1, 2*FLAG_BASEPOINT
.X
-FFlagPoint
.X
+1)-FLAG_BASEPOINT
.X
,
833 Y
+FFlagPoint
.Y
-FLAG_BASEPOINT
.Y
+1, Mirror
, p
,
834 IfThen(FDirection
= TDirection
.D_RIGHT
, FFlagAngle
, -FFlagAngle
));
838 if Direction
= TDirection
.D_RIGHT
then
839 Mirror
:= TMirrorType
.None
841 Mirror
:= TMirrorType
.Horizontal
;
844 (not (FCurrentAnimation
in [A_DIE1
, A_DIE2
, A_PAIN
])) and
845 (FCurrentWeapon
in [WP_FIRST
+ 1..WP_LAST
]) then
847 if FCurrentAnimation
in [A_SEEUP
, A_ATTACKUP
] then
850 if FCurrentAnimation
in [A_SEEDOWN
, A_ATTACKDOWN
] then
855 if (FCurrentAnimation
in [A_ATTACK
, A_ATTACKUP
, A_ATTACKDOWN
]) or
862 e_Draw(WeaponID
[FCurrentWeapon
][pos
][act
],
863 X
+FWeaponPoints
[FCurrentWeapon
, FCurrentAnimation
, FDirection
,
864 FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].CurrentFrame
].X
,
865 Y
+FWeaponPoints
[FCurrentWeapon
, FCurrentAnimation
, FDirection
,
866 FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].CurrentFrame
].Y
,
867 0, True, False, Mirror
);
871 if (FDirection
= TDirection
.D_LEFT
) and
872 (FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
874 FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Alpha
:= Alpha
;
875 FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Draw(X
, Y
, TMirrorType
.None
);
879 FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Alpha
:= Alpha
;
880 FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Draw(X
, Y
, Mirror
);
886 if (FDirection
= TDirection
.D_LEFT
) and
887 (FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
889 FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Alpha
:= Alpha
;
890 FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Draw(X
, Y
, TMirrorType
.None
);
894 FMaskAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Alpha
:= Alpha
;
895 FMaskAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Draw(X
, Y
, Mirror
);
903 function TPlayerModel
.GetCurrentAnimation
: TAnimation
;
905 if (FDirection
= TDirection
.D_LEFT
) and (FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
906 Result
:= FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
]
908 Result
:= FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
];
911 function TPlayerModel
.GetCurrentAnimationMask
: TAnimation
;
913 if (FDirection
= TDirection
.D_LEFT
) and (FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
914 Result
:= FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
]
916 Result
:= FMaskAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
];
919 function TPlayerModel
.PlaySound(SoundType
, Level
: Byte; X
, Y
: Integer): Boolean;
921 TempArray
: array of DWORD
;
925 SetLength(TempArray
, 0);
927 if SoundType
= MODELSOUND_PAIN
then
929 if FPainSounds
= nil then Exit
;
931 for a
:= 0 to High(FPainSounds
) do
932 if FPainSounds
[a
].Level
= Level
then
934 SetLength(TempArray
, Length(TempArray
)+1);
935 TempArray
[High(TempArray
)] := FPainSounds
[a
].ID
;
940 if (Level
in [2, 3, 5]) and (FSlopSound
> 0) then
942 g_Sound_PlayExAt('SOUND_MONSTER_SLOP', X
, Y
);
943 if FSlopSound
= 1 then
949 if FDieSounds
= nil then Exit
;
951 for a
:= 0 to High(FDieSounds
) do
952 if FDieSounds
[a
].Level
= Level
then
954 SetLength(TempArray
, Length(TempArray
)+1);
955 TempArray
[High(TempArray
)] := FDieSounds
[a
].ID
;
957 if (TempArray
= nil) and (Level
= 5) then
959 g_Sound_PlayExAt('SOUND_MONSTER_SLOP', X
, Y
);
965 if TempArray
= nil then Exit
;
967 g_Sound_PlayAt(TempArray
[Random(Length(TempArray
))], X
, Y
);
972 procedure TPlayerModel
.SetColor(Red
, Green
, Blue
: Byte);
979 procedure TPlayerModel
.SetFire(Fire
: Boolean);
983 if FFire
then FFireCounter
:= FAnim
[TDirection
.D_RIGHT
, A_ATTACK
].Speed
*FAnim
[TDirection
.D_RIGHT
, A_ATTACK
].TotalFrames
984 else FFireCounter
:= 0;
987 procedure TPlayerModel
.SetFlag(Flag
: Byte);
997 FLAG_RED
: g_Frames_Get(id
, 'FRAMES_FLAG_RED');
998 FLAG_BLUE
: g_Frames_Get(id
, 'FRAMES_FLAG_BLUE');
1002 FFlagAnim
:= TAnimation
.Create(id
, True, 8);
1005 procedure TPlayerModel
.SetWeapon(Weapon
: Byte);
1007 FCurrentWeapon
:= Weapon
;
1010 procedure TPlayerModel
.Update();
1012 if (FDirection
= TDirection
.D_LEFT
) and (FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
1013 FAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Update
else FAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Update
;
1015 if (FDirection
= TDirection
.D_LEFT
) and (FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
] <> nil) then
1016 FMaskAnim
[TDirection
.D_LEFT
][FCurrentAnimation
].Update
else FMaskAnim
[TDirection
.D_RIGHT
][FCurrentAnimation
].Update
;
1018 if FFlagAnim
<> nil then FFlagAnim
.Update
;
1020 if FFireCounter
> 0 then Dec(FFireCounter
) else FFire
:= False;