X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_playermodel.pas;h=51dd6c0deecf8cb6215a419f96e04aef8e3af02e;hp=8e895a9245645c84a11c56fc9319779c199c389c;hb=b07cc041c0c2cbb70ddddaddf81e3b03533d3ac8;hpb=ba177dd9743397cb43dad787fbe7748c8ea51cba diff --git a/src/game/g_playermodel.pas b/src/game/g_playermodel.pas index 8e895a9..51dd6c0 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,15 @@ * 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; + MAPDEF, g_textures, g_base, g_basic, g_weapons, r_graphics, utils, g_gfx, + ImagingTypes, Imaging, ImagingUtility; const A_STAND = 0; @@ -32,11 +33,53 @@ 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 + 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; + TModelInfo = record Name: String; Author: String; @@ -58,123 +101,112 @@ type 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) + 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; + FAnimState: TAnimationState; FPainSounds: TModelSoundArray; FDieSounds: TModelSoundArray; FSlopSound: Byte; 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); function PlaySound(SoundType, Level: Byte; X, Y: Integer): Boolean; procedure Update(); - procedure Draw(X, Y: Integer; Alpha: Byte = 0); + function GetBlood (): TModelBlood; + function GetName (): String; + + published property Fire: Boolean read FFire; 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_GetNames(): SSArray; function g_PlayerModel_GetInfo(ModelName: String): TModelInfo; +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_GetAnim(ModelName: String; AnimTyp: Byte; var _Anim, _Mask: TAnimation): Boolean; function g_PlayerModel_GetGibs(ModelName: String; var Gibs: TGibsArray): Boolean; +(* --- private data --- *) + + type + TPlayerModelInfo = record + Info: TModelInfo; + ModelSpeed: Array [A_STAND..A_PAIN] of Byte; + FlagPoint: TDFPoint; + FlagAngle: SmallInt; + WeaponPoints: TWeaponPoints; + Gibs: TGibsArray; // !!! move to render + 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, r_textures, r_animations, + 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: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'); - -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 - 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'); - end; -end; + ('csaw', 'hgun', 'sg', 'ssg', 'mgun', 'rkt', 'plz', 'bfg', 'spl', 'flm'); -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; @@ -230,7 +262,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; @@ -243,6 +275,89 @@ 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_CalcGibSize (pData: Pointer; dataSize, x, y, w, h: Integer): TRectWH; + var i, j: Integer; done: Boolean; img: TImageData; + + function IsVoid (i, j: Integer): Boolean; + begin + result := Byte((PByte(img.bits) + (y+j)*img.width*4 + (x+i)*4 + 3)^) = 0 + end; + +begin + InitImage(img); + assert(LoadImageFromMemory(pData, dataSize, img)); + + (* trace x from right to left *) + done := false; i := 0; + while not done and (i < w) do + begin + j := 0; + while (j < h) and IsVoid(i, j) do inc(j); + done := (j < h) and (IsVoid(i, j) = false); + result.x := i; + inc(i); + end; + + (* trace y from up to down *) + done := false; j := 0; + while not done and (j < h) do + begin + i := 0; + while (i < w) and IsVoid(i, j) do inc(i); + done := (i < w) and (IsVoid(i, j) = false); + result.y := j; + inc(j); + end; + + (* trace x from right to left *) + done := false; i := w - 1; + while not done and (i >= 0) do + begin + j := 0; + while (j < h) and IsVoid(i, j) do inc(j); + done := (j < h) and (IsVoid(i, j) = false); + result.width := i - result.x + 1; + dec(i); + end; + + (* trace y from down to up *) + done := false; j := h - 1; + while not done and (j >= 0) do + begin + i := 0; + while (i < w) and IsVoid(i, j) do inc(i); + done := (i < w) and (IsVoid(i, j) = false); + result.height := j - result.y + 1; + dec(j); + end; + + FreeImage(img); +end; + function g_PlayerModel_Load(FileName: string): Boolean; var ID: DWORD; @@ -253,9 +368,9 @@ var WAD: TWADFile; s: string; prefix: string; - ok: 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; @@ -297,44 +412,59 @@ begin Description := config.ReadStr('Model', 'description', ''); end; - for b := A_STAND to A_PAIN do + PlayerModelsArray[ID].FileName := FileName; + with PlayerModelsArray[ID] 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 + 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_LAST do + begin + 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); @@ -370,20 +500,25 @@ begin SlopSound := Min(Max(config.ReadInt('Sound', 'slop', 0), 0), 2); - SetLength(Gibs, ReadInt('Gibs', 'count', 0)); + GibsCount := config.ReadInt('Gibs', 'count', 0); + GibsResource := config.ReadStr('Gibs', 'resource', 'GIBS'); + GibsMask := config.ReadStr('Gibs', 'mask', 'GIBSMASK'); + GibsOnce := config.ReadInt('Gibs', 'once', -1); + SetLength(Gibs, GibsCount); // !!! remove load 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 + (WAD.GetResource('TEXTURES/' + GibsResource, pData, lenpd)) and + (WAD.GetResource('TEXTURES/' + 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); + //Gibs[a].Rect := e_GetTextureSize2(Gibs[a].ID); + Gibs[a].Rect := g_PlayerModel_CalcGibSize(pData, lenpd, a*32, 0, 32, 32); with Gibs[a].Rect do if Height > 3 then Height := Height-1-Random(2); - Gibs[a].OnlyOne := config.ReadInt('Gibs', 'once', -1) = a+1; + Gibs[a].OnlyOne := GibsOnce = a + 1; end; FreeMem(pData); @@ -392,23 +527,92 @@ begin 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 - ok := ok and 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); - - 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 + 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, + 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, 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; + case bb of + A_STAND, A_PAIN: + begin + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 6); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 8); + end; + A_WALKATTACK, A_WALK: + begin + 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, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 8); + end; + A_WALKSEEUP, A_SEEUP: + begin + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 16); + end; + A_WALKSEEDOWN, A_SEEDOWN: + begin + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 6); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 5); + end; + A_WALKATTACKUP, A_ATTACKUP: + begin + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].X, 5); + Dec(WeaponPoints[aa, bb, TDirection.D_RIGHT, f].Y, 16); + end; + A_WALKATTACKDOWN, A_ATTACKDOWN: + begin + 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 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, 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; @@ -417,7 +621,8 @@ begin Info.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; @@ -429,65 +634,32 @@ begin end; function g_PlayerModel_Get(ModelName: String): TPlayerModel; -var - a: Integer; - b: Byte; - ID, ID2: DWORD; + var a: Integer; begin Result := nil; if PlayerModelsArray = nil then Exit; for a := 0 to High(PlayerModelsArray) do + begin if AnsiLowerCase(PlayerModelsArray[a].Info.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; +function g_PlayerModel_GetAnim(ModelName: string; AnimTyp: Byte; var _Anim, _Mask: TAnimation): Boolean; var a: Integer; c: Boolean; @@ -500,19 +672,19 @@ begin if PlayerModelsArray[a].Info.Name = ModelName then with PlayerModelsArray[a] do begin - if Anim in [A_STAND, A_WALK] then c := True else c := False; + if AnimTyp 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; + if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(AnimTyp)) then + if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(AnimTyp)) then Exit; - _Anim := TAnimation.Create(ID, c, ModelSpeed[Anim]); - _Anim.Speed := ModelSpeed[Anim]; + _Anim := TAnimation.Create(ID, c, ModelSpeed[AnimTyp]); + _Anim.Speed := ModelSpeed[AnimTyp]; - 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; + if not g_Frames_Get(ID, Info.Name+'_RIGHTANIM'+IntToStr(AnimTyp)+'_MASK') then + if not g_Frames_Get(ID, Info.Name+'_LEFTANIM'+IntToStr(AnimTyp)+'_MASK') then Exit; - _Mask := TAnimation.Create(ID, c, ModelSpeed[Anim]); - _Mask.Speed := ModelSpeed[Anim]; + _Mask := TAnimation.Create(ID, c, ModelSpeed[AnimTyp]); + _Mask.Speed := ModelSpeed[AnimTyp]; Break; end; @@ -559,7 +731,7 @@ begin end; end; -function g_PlayerModel_GetNames(): SArray; +function g_PlayerModel_GetNames(): SSArray; var i: DWORD; begin @@ -589,194 +761,67 @@ begin end; end; -procedure g_PlayerModel_FreeData(); +function g_PlayerModel_GetBlood(ModelName: string): TModelBlood; var - i: DWORD; - a, b, c: Integer; + a: 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]); + Result.R := 150; + Result.G := 0; + Result.B := 0; + Result.Kind := BLOOD_NORMAL; + if PlayerModelsArray = nil then Exit; - e_WriteLog('Releasing models...', MSG_NOTIFY); + for a := 0 to High(PlayerModelsArray) do + if PlayerModelsArray[a].Info.Name = ModelName then + begin + Result := PlayerModelsArray[a].Blood; + Break; + end; +end; + +procedure g_PlayerModel_FreeData(); + var i, b: Integer; +begin + 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 - begin - FAnim[D_LEFT][FCurrentAnimation].Reset; - FMaskAnim[D_LEFT][FCurrentAnimation].Reset; - end - else + procedure TPlayerModel.ChangeAnimation (Animation: Byte; Force: Boolean = False); + var once: Boolean; speed, count: Integer; 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; @@ -840,45 +885,87 @@ 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; + if FFire then + FFireCounter := PlayerModelsArray[FID].ModelSpeed[A_ATTACK] * PlayerModelsArray[FID].Anim[TDirection.D_RIGHT, A_ATTACK].Frames + else + FFireCounter := 0 end; -procedure TPlayerModel.SetFlag(Flag: Byte); -var - id: DWORD; -begin - FFlag := Flag; - - FFlagAnim.Free(); - FFlagAnim := nil; - - case Flag of - FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED'); - FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE'); - else Exit; + procedure TPlayerModel.SetFlag (Flag: Byte); + begin + FFlag := Flag end; - FFlagAnim := TAnimation.Create(id, True, 8); -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].Info.Name + end; - if FFlagAnim <> nil then FFlagAnim.Update; + procedure TPlayerModel.Update; + begin + if FAnimState <> nil then + FAnimState.Update; + if FFireCounter > 0 then + Dec(FFireCounter) + else + FFire := False + 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.