X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_playermodel.pas;h=142401547a3c6b77d9d01bacd8b1bb13f2b74fde;hb=abda6900c041e39944de6a49aa088a60c170715e;hp=f3e1f8d79e5c7652ba9a002d1216f422016c8198;hpb=f0c98dc86657bdc08f6049872f12b554e1654fa4;p=d2df-sdl.git diff --git a/src/game/g_playermodel.pas b/src/game/g_playermodel.pas index f3e1f8d..1424015 100644 --- a/src/game/g_playermodel.pas +++ b/src/game/g_playermodel.pas @@ -1,9 +1,8 @@ -(* Copyright (C) DooM 2D:Forever Developers +(* Copyright (C) Doom 2D: Forever Developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,13 +12,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$MODE DELPHI} +{$INCLUDE ../shared/a_modes.inc} +{$M+} unit g_playermodel; interface -uses - g_textures, g_basic, g_weapons, e_graphics, wadreader; + uses MAPDEF, g_textures, g_base, g_basic, g_weapons, utils, g_gfx; const A_STAND = 0; @@ -32,16 +31,51 @@ const A_ATTACKUP = 7; A_ATTACKDOWN = 8; A_PAIN = 9; + // EXTENDED + A_WALKATTACK = 10; + A_WALKSEEUP = 11; + A_WALKSEEDOWN = 12; + A_WALKATTACKUP = 13; + A_WALKATTACKDOWN = 14; + A_FISTSTAND = 15; + A_FISTWALK = 16; + A_FISTATTACK = 17; + A_FISTWALKATTACK = 18; + A_FISTSEEUP = 19; + A_FISTSEEDOWN = 20; + A_FISTATTACKUP = 21; + A_FISTATTACKDOWN = 22; + + A_LASTBASE = A_PAIN; + A_LASTEXT = A_FISTATTACKDOWN; + A_LAST = A_LASTEXT; MODELSOUND_PAIN = 0; MODELSOUND_DIE = 1; + W_POS_NORMAL = 0; + W_POS_UP = 1; + W_POS_DOWN = 2; + + W_ACT_NORMAL = 0; + W_ACT_FIRE = 1; + + FLAG_BASEPOINT: TDFPoint = (X:16; Y:43); + type - TModelInfo = record - Name: String; - Author: String; - Description: String; - HaveWeapon: Boolean; + TWeaponPoints = Array [WP_FIRST + 1..WP_LAST, A_STAND..A_LAST, TDirection.D_LEFT..TDirection.D_RIGHT] of Array of TDFPoint; + + TModelMatrix = Array [TDirection.D_LEFT..TDirection.D_RIGHT, A_STAND..A_LAST] of TAnimationState; + + TModelTextures = Array [TDirection.D_LEFT..TDirection.D_RIGHT, A_STAND..A_LAST] of record + Resource: String; + Mask: String; + Frames: Integer; + Back: Boolean; + end; + + TModelBlood = record + R, G, B, Kind: Byte; end; TModelSound = record @@ -49,133 +83,126 @@ type Level: Byte; end; - TGibSprite = record - ID: DWORD; - MaskID: DWORD; - Rect: TRectWH; - OnlyOne: Boolean; - end; - TModelSoundArray = Array of TModelSound; - TGibsArray = Array of TGibSprite; - TWeaponPoints = Array [WP_FIRST + 1..WP_LAST] of - Array [A_STAND..A_PAIN] of - Array [D_LEFT..D_RIGHT] of Array of TPoint; - TPlayerModel = class (TObject) + TGibsArray = Array of Integer; + + TPlayerModel = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF} private - FName: String; FDirection: TDirection; FColor: TRGB; FCurrentAnimation: Byte; - FAnim: Array [D_LEFT..D_RIGHT] of Array [A_STAND..A_PAIN] of TAnimation; - FMaskAnim: Array [D_LEFT..D_RIGHT] of Array [A_STAND..A_PAIN] of TAnimation; - FWeaponPoints: TWeaponPoints; - FPainSounds: TModelSoundArray; - FDieSounds: TModelSoundArray; - FSlopSound: Byte; + FAnimState: TAnimationState; FCurrentWeapon: Byte; - FDrawWeapon: Boolean; FFlag: Byte; - FFlagPoint: TPoint; - FFlagAngle: SmallInt; - FFlagAnim: TAnimation; - FFire: Boolean; FFireCounter: Byte; + FID: Integer; public destructor Destroy(); override; procedure ChangeAnimation(Animation: Byte; Force: Boolean = False); - function GetCurrentAnimation: TAnimation; - function GetCurrentAnimationMask: TAnimation; procedure SetColor(Red, Green, Blue: Byte); procedure SetWeapon(Weapon: Byte); procedure SetFlag(Flag: Byte); - procedure SetFire(Fire: Boolean); + procedure SetFire (Fire: Boolean); + function GetFire (): Boolean; function PlaySound(SoundType, Level: Byte; X, Y: Integer): Boolean; procedure Update(); - procedure Draw(X, Y: Integer; Alpha: Byte = 0); - property Fire: Boolean read FFire; + function GetBlood (): TModelBlood; + function GetName (): String; + + published property Direction: TDirection read FDirection write FDirection; property Animation: Byte read FCurrentAnimation; property Weapon: Byte read FCurrentWeapon; - property Name: String read FName; + + public property Color: TRGB read FColor write FColor; + property AnimState: TAnimationState read FAnimState; + property CurrentAnimation: Byte read FCurrentAnimation; + property CurrentWeapon: Byte read FCurrentWeapon; + property Flag: Byte read FFlag; + property ID: Integer read FID; end; -procedure g_PlayerModel_LoadData(); +procedure g_PlayerModel_LoadAll; procedure g_PlayerModel_FreeData(); function g_PlayerModel_Load(FileName: String): Boolean; -function g_PlayerModel_GetNames(): SArray; -function g_PlayerModel_GetInfo(ModelName: String): TModelInfo; +function g_PlayerModel_GetNames(): SSArray; +function g_PlayerModel_GetBlood(ModelName: String): TModelBlood; function g_PlayerModel_Get(ModelName: String): TPlayerModel; -function g_PlayerModel_GetAnim(ModelName: String; Anim: Byte; var _Anim, _Mask: TAnimation): Boolean; -function g_PlayerModel_GetGibs(ModelName: String; var Gibs: TGibsArray): Boolean; +function g_PlayerModel_GetGibs (ModelID: Integer; var Gibs: TGibsArray): Boolean; +function g_PlayerModel_GetIndex (ModelName: String): Integer; + +(* --- private data --- *) + + type + TPlayerModelInfo = record + Name: String; + Author: String; + Description: String; + HaveWeapon: Boolean; + ModelSpeed: Array [A_STAND..A_PAIN] of Byte; + FlagPoint: TDFPoint; + FlagAngle: SmallInt; + WeaponPoints: TWeaponPoints; + PainSounds: TModelSoundArray; + DieSounds: TModelSoundArray; + SlopSound: Byte; + Blood: TModelBlood; + // ======================= + FileName: String; + Anim: TModelTextures; + GibsCount: Integer; + GibsResource:String; + GibsMask: String; + GibsOnce: Integer; + end; + + var + PlayerModelsArray: Array of TPlayerModelInfo; implementation uses - g_main, g_sound, g_console, SysUtils, g_player, CONFIG, - GL, GLExt, e_sound, g_options, g_map, Math, e_log; - -type - TPlayerModelInfo = record - Info: TModelInfo; - ModelSpeed: Array [A_STAND..A_PAIN] of Byte; - FlagPoint: TPoint; - FlagAngle: SmallInt; - WeaponPoints: TWeaponPoints; - Gibs: TGibsArray; - PainSounds: TModelSoundArray; - DieSounds: TModelSoundArray; - SlopSound: Byte; - end; + g_sound, g_console, SysUtils, g_player, CONFIG, + e_sound, g_options, g_map, Math, e_log, wadreader; const - W_POS_NORMAL = 0; - W_POS_UP = 1; - W_POS_DOWN = 2; - - W_ACT_NORMAL = 0; - W_ACT_FIRE = 1; - - FLAG_BASEPOINT: TPoint = (X:16; Y:43); - FLAG_DEFPOINT: TPoint = (X:32; Y:16); + FLAG_DEFPOINT: TDFPoint = (X:32; Y:16); FLAG_DEFANGLE = -20; - WEAPONBASE: Array [WP_FIRST + 1..WP_LAST] of TPoint = + WEAPONBASE: Array [WP_FIRST + 1..WP_LAST] of TDFPoint = ((X:8; Y:4), (X:8; Y:8), (X:16; Y:16), (X:16; Y:24), (X:16; Y:16), (X:24; Y:24), (X:16; Y:16), (X:24; Y:24), (X:16; Y:16), (X:8; Y:8)); - AnimNames: Array [A_STAND..A_PAIN] of String = + AnimNames: Array [A_STAND..A_LASTEXT] of String = ('StandAnim','WalkAnim','Die1Anim','Die2Anim','AttackAnim', - 'SeeUpAnim','SeeDownAnim','AttackUpAnim','AttackDownAnim','PainAnim'); + 'SeeUpAnim','SeeDownAnim','AttackUpAnim','AttackDownAnim','PainAnim', + // EXTENDED + 'WalkAttackAnim', 'WalkSeeUpAnim', 'WalkSeeDownAnim', + 'WalkAttackUpAnim', 'WalkAttackDownAnim', 'FistStandAnim', 'FistWalkAnim', + 'FistAttackAnim', 'FistWalkAttackAnim', 'FistSeeUpAnim', 'FistSeeDownAnim', + 'FistAttackUpAnim', 'FistAttackDownAnim'); WeapNames: Array [WP_FIRST + 1..WP_LAST] of String = ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm'); -var - WeaponID: Array [WP_FIRST + 1..WP_LAST] of - Array [W_POS_NORMAL..W_POS_DOWN] of - Array [W_ACT_NORMAL..W_ACT_FIRE] of DWORD; - PlayerModelsArray: Array of TPlayerModelInfo; - -procedure g_PlayerModel_LoadData(); -var - a: Integer; -begin - for a := WP_FIRST + 1 to WP_LAST do + function g_PlayerModel_GetIndex (ModelName: String): Integer; + var i: Integer; begin - g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])); - g_Texture_CreateWAD(WeaponID[a][W_POS_NORMAL][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_FIRE'); - g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP'); - g_Texture_CreateWAD(WeaponID[a][W_POS_UP][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_UP_FIRE'); - g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_NORMAL], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN'); - g_Texture_CreateWAD(WeaponID[a][W_POS_DOWN][W_ACT_FIRE], GameWAD+':WEAPONS\'+UpperCase(WeapNames[a])+'_DN_FIRE'); + Result := -1; + if PlayerModelsArray <> nil then + begin + i := 0; + while (i < Length(PlayerModelsArray)) and (PlayerModelsArray[i].Name <> ModelName) do + Inc(i); + if i < Length(PlayerModelsArray) then + Result := i + end end; -end; -function GetPoint(var str: String; var point: TPoint): Boolean; +function GetPoint(var str: String; var point: TDFPoint): Boolean; var a, x, y: Integer; s: String; @@ -231,7 +258,7 @@ begin begin X := X - WEAPONBASE[weapon].X; Y := Y - WEAPONBASE[weapon].Y; - if dir = D_LEFT then + if dir = TDirection.D_LEFT then X := -X; end; end; @@ -244,19 +271,43 @@ begin Result := True; end; + procedure g_PlayerMode_ExtendPoints (id: Integer; AIdx: Integer); + const + CopyAnim: array [A_LASTBASE+1..A_LASTEXT] of Integer = ( + A_WALK, A_WALK, A_WALK, A_WALK, A_WALK, + A_STAND, A_WALK, A_ATTACK, A_WALK, A_SEEUP, A_SEEDOWN, + A_ATTACKUP, A_ATTACKDOWN + ); + var W, I, OIdx: Integer; D: TDirection; + begin + OIdx := CopyAnim[AIdx]; + with PlayerModelsArray[id] do + begin + for W := WP_FIRST + 1 to WP_LAST do + begin + for D := TDirection.D_LEFT to TDirection.D_RIGHT do + begin + SetLength(WeaponPoints[W, AIdx, D], Length(WeaponPoints[W, OIdx, D])); + for I := 0 to High(WeaponPoints[W, AIdx, D]) do + WeaponPoints[W, AIdx, D, I] := WeaponPoints[W, OIdx, D, I] + end; + end; + end; + end; + function g_PlayerModel_Load(FileName: string): Boolean; var ID: DWORD; - a, b, len, lenpd, lenpd2, aa, bb, f: Integer; + a, b, len, aa, bb, f: Integer; cc: TDirection; config: TConfig; - pData, pData2: Pointer; + pData: Pointer; WAD: TWADFile; s: string; prefix: string; - ok, chk: Boolean; + ok, chk, chk2: Boolean; begin - e_WriteLog(Format('Loading player model: %s', [ExtractFileName(FileName)]), MSG_NOTIFY); + e_WriteLog(Format('Loading player model "%s"...', [FileName]), TMsgType.Notify); Result := False; @@ -291,51 +342,62 @@ begin prefix := FileName+':TEXTURES\'; - with PlayerModelsArray[ID].Info do + PlayerModelsArray[ID].Name := s; + PlayerModelsArray[ID].Author := config.ReadStr('Model', 'author', ''); + PlayerModelsArray[ID].Description := config.ReadStr('Model', 'description', ''); + PlayerModelsArray[ID].FileName := FileName; + with PlayerModelsArray[ID] do begin - Name := s; - Author := config.ReadStr('Model', 'author', ''); - Description := config.ReadStr('Model', 'description', ''); + Blood.R := MAX(0, MIN(255, config.ReadInt('Blood', 'R', 150))); + Blood.G := MAX(0, MIN(255, config.ReadInt('Blood', 'G', 0))); + Blood.B := MAX(0, MIN(255, config.ReadInt('Blood', 'B', 0))); + case config.ReadStr('Blood', 'Kind', 'NORMAL') of + 'NORMAL': Blood.Kind := BLOOD_NORMAL; + 'SPARKS': Blood.Kind := BLOOD_CSPARKS; + 'COMBINE': Blood.Kind := BLOOD_COMBINE; + else + Blood.Kind := BLOOD_NORMAL + end end; - for b := A_STAND to A_PAIN do + for b := A_STAND to A_LAST do begin - if not (g_Frames_CreateWAD(nil, s+'_RIGHTANIM'+IntToStr(b), - prefix+config.ReadStr(AnimNames[b], 'resource', ''), - 64, 64, config.ReadInt(AnimNames[b], 'frames', 1), - config.ReadBool(AnimNames[b], 'backanim', False)) and - g_Frames_CreateWAD(nil, s+'_RIGHTANIM'+IntToStr(b)+'_MASK', - prefix+config.ReadStr(AnimNames[b], 'mask', ''), - 64, 64, config.ReadInt(AnimNames[b], 'frames', 1), - config.ReadBool(AnimNames[b], 'backanim', False))) then + with PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b] do begin - config.Free(); - WAD.Free(); - Exit; + Resource := config.ReadStr(AnimNames[b], 'resource', ''); + Mask := config.ReadStr(AnimNames[b], 'mask', ''); + Frames := config.ReadInt(AnimNames[b], 'frames', 1); + Back := config.ReadBool(AnimNames[b], 'backanim', False); + if (Resource = '') or (Mask = '') then + begin + if b <= A_LASTBASE then + begin + config.Free(); + WAD.Free(); + Exit + end + else + begin + g_PlayerMode_ExtendPoints(ID, b); + continue + end + end; end; for aa := WP_FIRST + 1 to WP_LAST do - for bb := A_STAND to A_PAIN do - for cc := D_LEFT to D_RIGHT do + for bb := A_STAND to A_LAST do + for cc := TDirection.D_LEFT to TDirection.D_RIGHT do begin - f := config.ReadInt(AnimNames[bb], 'frames', 1); - if config.ReadBool(AnimNames[bb], 'backanim', False) then - if f > 2 then f := 2*f-2; + f := PlayerModelsArray[ID].Anim[cc, bb].Frames; + if PlayerModelsArray[ID].Anim[cc, bb].Back and (f > 2) then + f := 2 * f - 2; SetLength(PlayerModelsArray[ID].WeaponPoints[aa, bb, cc], f); end; - if (config.ReadStr(AnimNames[b], 'resource2', '') <> '') and - (config.ReadStr(AnimNames[b], 'mask2', '') <> '') then + with PlayerModelsArray[ID].Anim[TDirection.D_LEFT, b] do begin - g_Frames_CreateWAD(nil, s+'_LEFTANIM'+IntToStr(b), - prefix+config.ReadStr(AnimNames[b], 'resource2', ''), - 64, 64, config.ReadInt(AnimNames[b], 'frames', 1), - config.ReadBool(AnimNames[b], 'backanim', False)); - - g_Frames_CreateWAD(nil, s+'_LEFTANIM'+IntToStr(b)+'_MASK', - prefix+config.ReadStr(AnimNames[b], 'mask2', ''), - 64, 64, config.ReadInt(AnimNames[b], 'frames', 1), - config.ReadBool(AnimNames[b], 'backanim', False)); + Frames := PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b].Frames; + Back := PlayerModelsArray[ID].Anim[TDirection.D_RIGHT, b].Back; end; PlayerModelsArray[ID].ModelSpeed[b] := Max(1, config.ReadInt(AnimNames[b], 'waitcount', 1) div 3); @@ -371,104 +433,109 @@ begin SlopSound := Min(Max(config.ReadInt('Sound', 'slop', 0), 0), 2); - SetLength(Gibs, ReadInt('Gibs', 'count', 0)); - - if (Gibs <> nil) and - (WAD.GetResource('TEXTURES/'+config.ReadStr('Gibs', 'resource', 'GIBS'), pData, lenpd)) and - (WAD.GetResource('TEXTURES/'+config.ReadStr('Gibs', 'mask', 'GIBSMASK'), pData2, lenpd2)) then - begin - for a := 0 to High(Gibs) do - if e_CreateTextureMemEx(pData, lenpd, Gibs[a].ID, a*32, 0, 32, 32) and - e_CreateTextureMemEx(pData2, lenpd2, Gibs[a].MaskID, a*32, 0, 32, 32) then - begin - Gibs[a].Rect := e_GetTextureSize2(Gibs[a].ID); - with Gibs[a].Rect do - if Height > 3 then Height := Height-1-Random(2); - Gibs[a].OnlyOne := config.ReadInt('Gibs', 'once', -1) = a+1; - end; - - FreeMem(pData); - FreeMem(pData2); - end; + GibsCount := config.ReadInt('Gibs', 'count', 0); + GibsResource := config.ReadStr('Gibs', 'resource', 'GIBS'); + GibsMask := config.ReadStr('Gibs', 'mask', 'GIBSMASK'); + GibsOnce := config.ReadInt('Gibs', 'once', -1); ok := True; for aa := WP_FIRST + 1 to WP_LAST do - for bb := A_STAND to A_PAIN do + for bb := A_STAND to A_LAST do if not (bb in [A_DIE1, A_DIE2, A_PAIN]) then begin - chk := GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[aa]+'_points', ''), aa, bb, D_RIGHT, - config.ReadInt(AnimNames[bb], 'frames', 0), - config.ReadBool(AnimNames[bb], 'backanim', False), - WeaponPoints); + chk := GetWeapPoints( + config.ReadStr(AnimNames[bb], WeapNames[aa] + '_points', ''), + aa, + bb, + TDirection.D_RIGHT, + Anim[TDirection.D_RIGHT, bb].Frames, + Anim[TDirection.D_RIGHT, bb].Back, + WeaponPoints + ); if ok and (not chk) and (aa = WEAPON_FLAMETHROWER) then begin // workaround for flamethrower - chk := GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[WEAPON_PLASMA]+'_points', ''), aa, bb, D_RIGHT, - config.ReadInt(AnimNames[bb], 'frames', 0), - config.ReadBool(AnimNames[bb], 'backanim', False), - WeaponPoints); + chk := GetWeapPoints( + config.ReadStr(AnimNames[bb], WeapNames[WEAPON_PLASMA] + '_points', ''), + aa, + bb, + TDirection.D_RIGHT, + Anim[TDirection.D_RIGHT, bb].Frames, + Anim[TDirection.D_RIGHT, bb].Back, + WeaponPoints + ); if chk then - for f := 0 to High(WeaponPoints[aa, bb, D_RIGHT]) do + for f := 0 to High(WeaponPoints[aa, bb, TDirection.D_RIGHT]) do begin case bb of A_STAND, A_PAIN: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 6); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 8); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 6); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 8); end; - A_WALK: + A_WALKATTACK, A_WALK: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 9); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 9); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 9); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 9); end; A_ATTACK: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 5); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 8); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 8); end; - A_SEEUP: + A_WALKSEEUP, A_SEEUP: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 5); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 16); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 16); end; - A_SEEDOWN: + A_WALKSEEDOWN, A_SEEDOWN: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 6); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 6); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 5); end; - A_ATTACKUP: + A_WALKATTACKUP, A_ATTACKUP: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 5); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 16); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 16); end; - A_ATTACKDOWN: + A_WALKATTACKDOWN, A_ATTACKDOWN: begin - Dec(WeaponPoints[aa, bb, D_RIGHT, f].X, 6); - Dec(WeaponPoints[aa, bb, D_RIGHT, f].Y, 4); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 6); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 4); end; end; end; end; - ok := ok and chk; - if not GetWeapPoints(config.ReadStr(AnimNames[bb], WeapNames[aa]+'2_points', ''), aa, bb, D_LEFT, - config.ReadInt(AnimNames[bb], 'frames', 0), - config.ReadBool(AnimNames[bb], 'backanim', False), - WeaponPoints) then - for f := 0 to High(WeaponPoints[aa, bb, D_RIGHT]) do + ok := ok and (chk or (bb > A_LASTBASE)); + + chk2 := GetWeapPoints( + config.ReadStr(AnimNames[bb], WeapNames[aa] + '2_points', ''), + aa, + bb, + TDirection.D_LEFT, + Anim[TDirection.D_LEFT, bb].Frames, + Anim[TDirection.D_LEFT, bb].Back, + WeaponPoints + ); + if not chk2 then + begin + for f := 0 to High(WeaponPoints[aa, bb, TDirection.D_RIGHT]) do begin - WeaponPoints[aa, bb, D_LEFT, f].X := -WeaponPoints[aa, bb, D_RIGHT, f].X; - WeaponPoints[aa, bb, D_LEFT, f].Y := WeaponPoints[aa, bb, D_RIGHT, f].Y; + WeaponPoints[aa, bb, TDirection.D_LEFT, f].X := -WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X; + WeaponPoints[aa, bb, TDirection.D_LEFT, f].Y := WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y; end; + end; if not ok then Break; end; {if ok then g_Console_Add(Info.Name+' weapon points ok') else g_Console_Add(Info.Name+' weapon points fail');} - Info.HaveWeapon := ok; + PlayerModelsArray[ID].HaveWeapon := ok; s := config.ReadStr('Model', 'flag_point', ''); - if not GetPoint(s, FlagPoint) then FlagPoint := FLAG_DEFPOINT; + if not GetPoint(s, FlagPoint) then + FlagPoint := FLAG_DEFPOINT; FlagAngle := config.ReadInt('Model', 'flag_angle', FLAG_DEFANGLE); end; @@ -479,138 +546,59 @@ begin Result := True; end; -function g_PlayerModel_Get(ModelName: String): TPlayerModel; -var - a: Integer; - b: Byte; - ID, ID2: DWORD; +function g_PlayerModel_Get (ModelName: String): TPlayerModel; + var a: Integer; begin Result := nil; if PlayerModelsArray = nil then Exit; for a := 0 to High(PlayerModelsArray) do - if AnsiLowerCase(PlayerModelsArray[a].Info.Name) = AnsiLowerCase(ModelName) then + begin + if AnsiLowerCase(PlayerModelsArray[a].Name) = AnsiLowerCase(ModelName) then begin Result := TPlayerModel.Create; with PlayerModelsArray[a] do begin - Result.FName := Info.Name; - - for b := A_STAND to A_PAIN do - begin - if not (g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(b)) and - g_Frames_Get(ID2, Info.Name+'_RIGHTANIM'+IntToStr(b)+'_MASK')) then - begin - Result.Free(); - Result := nil; - Exit; - end; - - Result.FAnim[D_RIGHT][b] := TAnimation.Create(ID, b in [A_STAND, A_WALK], ModelSpeed[b]); - - Result.FMaskAnim[D_RIGHT][b] := TAnimation.Create(ID2, b in [A_STAND, A_WALK], ModelSpeed[b]); - - if g_Frames_Exists(Info.Name+'_LEFTANIM'+IntToStr(b)) and - g_Frames_Exists(Info.Name+'_LEFTANIM'+IntToStr(b)+'_MASK') then - if g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(b)) and - g_Frames_Get(ID2, Info.Name+'_LEFTANIM'+IntToStr(b)+'_MASK') then - begin - Result.FAnim[D_LEFT][b] := TAnimation.Create(ID, b in [A_STAND, A_WALK], ModelSpeed[b]); - - Result.FMaskAnim[D_LEFT][b] := TAnimation.Create(ID2, b in [A_STAND, A_WALK], ModelSpeed[b]); - end; - - Result.FPainSounds := PainSounds; - Result.FDieSounds := DieSounds; - Result.FSlopSound := SlopSound; + Result.FID := a; + Result.ChangeAnimation(A_STAND, True); + Break; end; - - Result.FDrawWeapon := Info.HaveWeapon; - Result.FWeaponPoints := WeaponPoints; - - Result.FFlagPoint := FlagPoint; - Result.FFlagAngle := FlagAngle; - - Break; end; end; end; -function g_PlayerModel_GetAnim(ModelName: string; Anim: Byte; var _Anim, _Mask: TAnimation): Boolean; -var - a: Integer; - c: Boolean; - ID: DWORD; -begin - Result := False; + function g_PlayerModel_GetGibs (ModelID: Integer; var Gibs: TGibsArray): Boolean; + var i, b: Integer; c: Boolean; + begin + Gibs := nil; + Result := False; + if (PlayerModelsArray = nil) or (gGibsCount = 0) then + Exit; - if PlayerModelsArray = nil then Exit; - for a := 0 to High(PlayerModelsArray) do - if PlayerModelsArray[a].Info.Name = ModelName then - with PlayerModelsArray[a] do + c := False; + SetLength(Gibs, gGibsCount); + for i := 0 to High(Gibs) do + begin + if c and (PlayerModelsArray[ModelID].GibsCount = 1) then begin - if Anim in [A_STAND, A_WALK] then c := True else c := False; - - if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(Anim)) then - if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(Anim)) then Exit; - - _Anim := TAnimation.Create(ID, c, ModelSpeed[Anim]); - _Anim.Speed := ModelSpeed[Anim]; - - if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(Anim)+'_MASK') then - if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(Anim)+'_MASK') then Exit; - - _Mask := TAnimation.Create(ID, c, ModelSpeed[Anim]); - _Mask.Speed := ModelSpeed[Anim]; - + SetLength(Gibs, i); Break; end; - Result := True; -end; + repeat + b := Random(PlayerModelsArray[ModelID].GibsCount); + until not ((PlayerModelsArray[ModelID].GibsOnce = b + 1) and c); -function g_PlayerModel_GetGibs(ModelName: string; var Gibs: TGibsArray): Boolean; -var - a, i, b: Integer; - c: Boolean; -begin - Result := False; - - if PlayerModelsArray = nil then Exit; - if gGibsCount = 0 then Exit; - - c := False; - - SetLength(Gibs, gGibsCount); - - for a := 0 to High(PlayerModelsArray) do - if PlayerModelsArray[a].Info.Name = ModelName then - begin - for i := 0 to High(Gibs) do - begin - if c and (Length(PlayerModelsArray[a].Gibs) = 1) then - begin - SetLength(Gibs, i); - Break; - end; + Gibs[i] := b; - repeat - b := Random(Length(PlayerModelsArray[a].Gibs)); - until not (PlayerModelsArray[a].Gibs[b].OnlyOne and c); - - Gibs[i] := PlayerModelsArray[a].Gibs[b]; - - if Gibs[i].OnlyOne then c := True; - end; - - Result := True; - Break; + c := PlayerModelsArray[ModelID].GibsOnce = b + 1; end; -end; + Result := True; + end; -function g_PlayerModel_GetNames(): SArray; +function g_PlayerModel_GetNames(): SSArray; var i: DWORD; begin @@ -621,213 +609,71 @@ begin for i := 0 to High(PlayerModelsArray) do begin SetLength(Result, Length(Result)+1); - Result[High(Result)] := PlayerModelsArray[i].Info.Name; + Result[High(Result)] := PlayerModelsArray[i].Name; end; end; -function g_PlayerModel_GetInfo(ModelName: string): TModelInfo; +function g_PlayerModel_GetBlood(ModelName: string): TModelBlood; var a: Integer; begin - FillChar(Result, SizeOf(Result), 0); + Result.R := 150; + Result.G := 0; + Result.B := 0; + Result.Kind := BLOOD_NORMAL; if PlayerModelsArray = nil then Exit; for a := 0 to High(PlayerModelsArray) do - if PlayerModelsArray[a].Info.Name = ModelName then + if PlayerModelsArray[a].Name = ModelName then begin - Result := PlayerModelsArray[a].Info; + Result := PlayerModelsArray[a].Blood; Break; end; end; procedure g_PlayerModel_FreeData(); -var - i: DWORD; - a, b, c: Integer; + var i, b: Integer; begin - for a := WP_FIRST + 1 to WP_LAST do - for b := W_POS_NORMAL to W_POS_DOWN do - for c := W_ACT_NORMAL to W_ACT_FIRE do - e_DeleteTexture(WeaponID[a][b][c]); - - e_WriteLog('Releasing models...', MSG_NOTIFY); + e_WriteLog('Releasing models...', TMsgType.Notify); if PlayerModelsArray = nil then Exit; for i := 0 to High(PlayerModelsArray) do + begin with PlayerModelsArray[i] do begin - for a := A_STAND to A_PAIN do - begin - g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a)); - g_Frames_DeleteByName(Info.Name+'_LEFTANIM'+IntToStr(a)+'_MASK'); - g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a)); - g_Frames_DeleteByName(Info.Name+'_RIGHTANIM'+IntToStr(a)+'_MASK'); - end; - if PainSounds <> nil then for b := 0 to High(PainSounds) do e_DeleteSound(PainSounds[b].ID); - if DieSounds <> nil then for b := 0 to High(DieSounds) do e_DeleteSound(DieSounds[b].ID); - - if Gibs <> nil then - for b := 0 to High(Gibs) do - begin - e_DeleteTexture(Gibs[b].ID); - e_DeleteTexture(Gibs[b].MaskID); - end; end; - + end; PlayerModelsArray := nil; end; { TPlayerModel } -procedure TPlayerModel.ChangeAnimation(Animation: Byte; Force: Boolean = False); -begin - if not Force then if FCurrentAnimation = Animation then Exit; - - FCurrentAnimation := Animation; - - if (FDirection = D_LEFT) and - (FAnim[D_LEFT][FCurrentAnimation] <> nil) and - (FMaskAnim[D_LEFT][FCurrentAnimation] <> nil) then + procedure TPlayerModel.ChangeAnimation (Animation: Byte; Force: Boolean = False); + var once: Boolean; speed, count: Integer; begin - FAnim[D_LEFT][FCurrentAnimation].Reset; - FMaskAnim[D_LEFT][FCurrentAnimation].Reset; - end - else - begin - FAnim[D_RIGHT][FCurrentAnimation].Reset; - FMaskAnim[D_RIGHT][FCurrentAnimation].Reset; + if not Force then + if FCurrentAnimation = Animation then + Exit; + FCurrentAnimation := Animation; + once := FCurrentAnimation in [A_STAND, A_WALK]; + speed := PlayerModelsArray[FID].ModelSpeed[FCurrentAnimation]; + count := PlayerModelsArray[FID].Anim[FDirection, FCurrentAnimation].Frames; + FAnimState := TAnimationState.Create(once, speed, count); end; -end; destructor TPlayerModel.Destroy(); -var - a: Byte; begin - for a := A_STAND to A_PAIN do - begin - FAnim[D_LEFT][a].Free(); - FMaskAnim[D_LEFT][a].Free(); - FAnim[D_RIGHT][a].Free(); - FMaskAnim[D_RIGHT][a].Free(); - end; - + FAnimState.Free; inherited; end; -procedure TPlayerModel.Draw(X, Y: Integer; Alpha: Byte = 0); -var - Mirror: TMirrorType; - pos, act: Byte; - p: TPoint; -begin -// Ôëàãè: - if Direction = D_LEFT then - Mirror := M_NONE - else - Mirror := M_HORIZONTAL; - - if (FFlag <> FLAG_NONE) and (FFlagAnim <> nil) and - (not (FCurrentAnimation in [A_DIE1, A_DIE2])) then - begin - p.X := IfThen(Direction = D_LEFT, - FLAG_BASEPOINT.X, - 64-FLAG_BASEPOINT.X); - p.Y := FLAG_BASEPOINT.Y; - - FFlagAnim.DrawEx(X+IfThen(Direction = D_LEFT, FFlagPoint.X-1, 2*FLAG_BASEPOINT.X-FFlagPoint.X+1)-FLAG_BASEPOINT.X, - Y+FFlagPoint.Y-FLAG_BASEPOINT.Y+1, Mirror, p, - IfThen(FDirection = D_RIGHT, FFlagAngle, -FFlagAngle)); - end; - -// Îðóæèå: - if Direction = D_RIGHT then - Mirror := M_NONE - else - Mirror := M_HORIZONTAL; - - if FDrawWeapon and - (not (FCurrentAnimation in [A_DIE1, A_DIE2, A_PAIN])) and - (FCurrentWeapon in [WP_FIRST + 1..WP_LAST]) then - begin - if FCurrentAnimation in [A_SEEUP, A_ATTACKUP] then - pos := W_POS_UP - else - if FCurrentAnimation in [A_SEEDOWN, A_ATTACKDOWN] then - pos := W_POS_DOWN - else - pos := W_POS_NORMAL; - - if (FCurrentAnimation in [A_ATTACK, A_ATTACKUP, A_ATTACKDOWN]) or - FFire then - act := W_ACT_FIRE - else - act := W_ACT_NORMAL; - - if Alpha < 201 then - e_Draw(WeaponID[FCurrentWeapon][pos][act], - X+FWeaponPoints[FCurrentWeapon, FCurrentAnimation, FDirection, - FAnim[D_RIGHT][FCurrentAnimation].CurrentFrame].X, - Y+FWeaponPoints[FCurrentWeapon, FCurrentAnimation, FDirection, - FAnim[D_RIGHT][FCurrentAnimation].CurrentFrame].Y, - 0, True, False, Mirror); - end; - -// Ìîäåëü: - if (FDirection = D_LEFT) and - (FAnim[D_LEFT][FCurrentAnimation] <> nil) then - begin - FAnim[D_LEFT][FCurrentAnimation].Alpha := Alpha; - FAnim[D_LEFT][FCurrentAnimation].Draw(X, Y, M_NONE); - end - else - begin - FAnim[D_RIGHT][FCurrentAnimation].Alpha := Alpha; - FAnim[D_RIGHT][FCurrentAnimation].Draw(X, Y, Mirror); - end; - -// Ìàñêà ìîäåëè: - e_Colors := FColor; - - if (FDirection = D_LEFT) and - (FMaskAnim[D_LEFT][FCurrentAnimation] <> nil) then - begin - FMaskAnim[D_LEFT][FCurrentAnimation].Alpha := Alpha; - FMaskAnim[D_LEFT][FCurrentAnimation].Draw(X, Y, M_NONE); - end - else - begin - FMaskAnim[D_RIGHT][FCurrentAnimation].Alpha := Alpha; - FMaskAnim[D_RIGHT][FCurrentAnimation].Draw(X, Y, Mirror); - end; - - e_Colors.R := 255; - e_Colors.G := 255; - e_Colors.B := 255; -end; - -function TPlayerModel.GetCurrentAnimation: TAnimation; -begin - if (FDirection = D_LEFT) and (FAnim[D_LEFT][FCurrentAnimation] <> nil) then - Result := FAnim[D_LEFT][FCurrentAnimation] - else - Result := FAnim[D_RIGHT][FCurrentAnimation]; -end; - -function TPlayerModel.GetCurrentAnimationMask: TAnimation; -begin - if (FDirection = D_LEFT) and (FMaskAnim[D_LEFT][FCurrentAnimation] <> nil) then - Result := FMaskAnim[D_LEFT][FCurrentAnimation] - else - Result := FMaskAnim[D_RIGHT][FCurrentAnimation]; -end; - function TPlayerModel.PlaySound(SoundType, Level: Byte; X, Y: Integer): Boolean; var TempArray: array of DWORD; @@ -838,33 +684,33 @@ begin if SoundType = MODELSOUND_PAIN then begin - if FPainSounds = nil then Exit; + if PlayerModelsArray[FID].PainSounds = nil then Exit; - for a := 0 to High(FPainSounds) do - if FPainSounds[a].Level = Level then + for a := 0 to High(PlayerModelsArray[FID].PainSounds) do + if PlayerModelsArray[FID].PainSounds[a].Level = Level then begin - SetLength(TempArray, Length(TempArray)+1); - TempArray[High(TempArray)] := FPainSounds[a].ID; + SetLength(TempArray, Length(TempArray) + 1); + TempArray[High(TempArray)] := PlayerModelsArray[FID].PainSounds[a].ID; end; end else begin - if (Level in [2, 3, 5]) and (FSlopSound > 0) then + if (Level in [2, 3, 5]) and (PlayerModelsArray[FID].SlopSound > 0) then begin g_Sound_PlayExAt('SOUND_MONSTER_SLOP', X, Y); - if FSlopSound = 1 then + if PlayerModelsArray[FID].SlopSound = 1 then begin Result := True; Exit; end; end; - if FDieSounds = nil then Exit; + if PlayerModelsArray[FID].DieSounds = nil then Exit; - for a := 0 to High(FDieSounds) do - if FDieSounds[a].Level = Level then + for a := 0 to High(PlayerModelsArray[FID].DieSounds) do + if PlayerModelsArray[FID].DieSounds[a].Level = Level then begin - SetLength(TempArray, Length(TempArray)+1); - TempArray[High(TempArray)] := FDieSounds[a].ID; + SetLength(TempArray, Length(TempArray) + 1); + TempArray[High(TempArray)] := PlayerModelsArray[FID].DieSounds[a].ID; end; if (TempArray = nil) and (Level = 5) then begin @@ -888,48 +734,92 @@ begin FColor.B := Blue; end; -procedure TPlayerModel.SetFire(Fire: Boolean); -begin - FFire := Fire; - - if FFire then FFireCounter := FAnim[D_RIGHT, A_ATTACK].Speed*FAnim[D_RIGHT, A_ATTACK].TotalFrames - else FFireCounter := 0; -end; - -procedure TPlayerModel.SetFlag(Flag: Byte); -var - id: DWORD; -begin - FFlag := Flag; - - FFlagAnim.Free(); - FFlagAnim := nil; + procedure TPlayerModel.SetFire (Fire: Boolean); + begin + if Fire then + FFireCounter := PlayerModelsArray[FID].ModelSpeed[A_ATTACK] * PlayerModelsArray[FID].Anim[TDirection.D_RIGHT, A_ATTACK].Frames + else + FFireCounter := 0 + end; - case Flag of - FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED'); - FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE'); - else Exit; + function TPlayerModel.GetFire (): Boolean; + begin + Result := FFireCounter > 0 end; - FFlagAnim := TAnimation.Create(id, True, 8); -end; + procedure TPlayerModel.SetFlag (Flag: Byte); + begin + FFlag := Flag + end; -procedure TPlayerModel.SetWeapon(Weapon: Byte); -begin - FCurrentWeapon := Weapon; -end; + procedure TPlayerModel.SetWeapon (Weapon: Byte); + begin + FCurrentWeapon := Weapon + end; -procedure TPlayerModel.Update(); -begin - if (FDirection = D_LEFT) and (FAnim[D_LEFT][FCurrentAnimation] <> nil) then - FAnim[D_LEFT][FCurrentAnimation].Update else FAnim[D_RIGHT][FCurrentAnimation].Update; + function TPlayerModel.GetBlood (): TModelBlood; + begin + Result := PlayerModelsArray[FID].Blood + end; - if (FDirection = D_LEFT) and (FMaskAnim[D_LEFT][FCurrentAnimation] <> nil) then - FMaskAnim[D_LEFT][FCurrentAnimation].Update else FMaskAnim[D_RIGHT][FCurrentAnimation].Update; + function TPlayerModel.GetName (): String; + begin + Result := PlayerModelsArray[FID].Name + end; - if FFlagAnim <> nil then FFlagAnim.Update; + procedure TPlayerModel.Update; + begin + if FAnimState <> nil then + FAnimState.Update; + if FFireCounter > 0 then + Dec(FFireCounter) + end; - if FFireCounter > 0 then Dec(FFireCounter) else FFire := False; -end; + procedure g_PlayerModel_LoadAll; + var + SR: TSearchRec; + knownFiles: array of AnsiString = nil; + found: Boolean; + wext, s: AnsiString; + f: Integer; + begin + // load models from all possible wad types, in all known directories + // this does a loosy job (linear search, ooph!), but meh + for wext in wadExtensions do + begin + for f := High(ModelDirs) downto Low(ModelDirs) do + begin + if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then + begin + repeat + found := false; + for s in knownFiles do + begin + if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then + begin + found := true; + break; + end; + end; + if not found then + begin + SetLength(knownFiles, length(knownFiles)+1); + knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name; + end; + until (FindNext(SR) <> 0); + end; + FindClose(SR); + end; + end; + if (length(knownFiles) = 0) then + raise Exception.Create('no player models found!'); + if (length(knownFiles) = 1) then + e_LogWriteln('1 player model found.', TMsgType.Notify) + else + e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify); + for s in knownFiles do + if not g_PlayerModel_Load(s) then + e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning); + end; end.