From 039dcaa381be7b6eb040b5eb5ef4ec6ac57bb28f Mon Sep 17 00:00:00 2001 From: admin_d Date: Tue, 13 Oct 2026 13:09:13 +0500 Subject: [PATCH] Complete BigTask --- config/extra-paths.json | 3 +- config/runtime-resources.json | 1 + .../src/res/KM_ResInterpolation.pas | 297 ++++++++++++++++++ 3 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 port-source/kam_remake/src/res/KM_ResInterpolation.pas diff --git a/config/extra-paths.json b/config/extra-paths.json index 4dee6da..b8b9157 100755 --- a/config/extra-paths.json +++ b/config/extra-paths.json @@ -28,7 +28,8 @@ "src/utils/KromOGLUtils.pas", "src/render/KM_RenderPool.pas", "src/gui/KM_InterfaceGame.pas", - "src/render/KM_RenderTerrain.pas" + "src/render/KM_RenderTerrain.pas", + "src/res/KM_ResInterpolation.pas" ], "source_paths": [ "code/resource" diff --git a/config/runtime-resources.json b/config/runtime-resources.json index fb7ffb0..3c9cc8b 100644 --- a/config/runtime-resources.json +++ b/config/runtime-resources.json @@ -5,6 +5,7 @@ {"kind": "file", "path": "data/Sprites/Tileset.rxx"}, {"kind": "file", "path": "data/defines/unit.dat"}, {"kind": "file", "path": "data/defines/houses.dat"}, + {"kind": "file", "path": "data/defines/interp.dat"}, {"kind": "file", "path": "data/defines/tiles.json"}, {"kind": "file", "path": "data/gfx/pal0.bbm"}, {"kind": "file", "path": "data/defines/mapelem.dat"}, diff --git a/port-source/kam_remake/src/res/KM_ResInterpolation.pas b/port-source/kam_remake/src/res/KM_ResInterpolation.pas new file mode 100644 index 0000000..c56fcfb --- /dev/null +++ b/port-source/kam_remake/src/res/KM_ResInterpolation.pas @@ -0,0 +1,297 @@ +unit KM_ResInterpolation; +{$I KaM_Remake.inc} +interface +uses + Classes, SysUtils, Math, + KM_Defaults, KM_Points, KM_ResTypes, KM_ResMapElements, KM_ResHouses; + +const + INTERP_LEVEL = 8; + +type + TKMInterpolation = array[1..30, 0..INTERP_LEVEL-1] of Integer; + TKMUnitActionInterp = array[UNIT_MIN..UNIT_MAX, UNIT_ACT_MIN..UNIT_ACT_MAX, dirN..dirNW] of TKMInterpolation; + TKMSerfCarryInterp = array[WARE_MIN..WARE_MAX, dirN..dirNW] of TKMInterpolation; + TKMUnitThoughtInterp = array[TKMUnitThought] of TKMInterpolation; + TKMTreeInterp = array[0..OBJECTS_CNT] of TKMInterpolation; + TKMHouseInterp = array[HOUSE_MIN..HOUSE_MAX, TKMHouseActionType] of TKMInterpolation; + TKMBeastInterp = array[1..3,1..5,1..3] of TKMInterpolation; + + TKMResInterpolation = class + private + fUnitActions: TKMUnitActionInterp; + fSerfCarry: TKMSerfCarryInterp; + fUnitThoughts: TKMUnitThoughtInterp; + fTrees: TKMTreeInterp; + fHouses: TKMHouseInterp; + fBeasts: TKMBeastInterp; + fInterpolationDataEligible: Boolean; + public + procedure LoadFromFile(const FileName: string); + + function UnitAction(aUnit: TKMUnitType; aAct: TKMUnitActionType; aDir: TKMDirection; aStep: Integer; aStepFrac: Single): Integer; + function UnitActionByPercent(aUnit: TKMUnitType; aAct: TKMUnitActionType; aDir: TKMDirection; aPercent: Single): Integer; + function SerfCarry(aWare: TKMWareType; aDir: TKMDirection; aStep: Integer; aStepFrac: Single): Integer; + function UnitThought(aTh: TKMUnitThought; aStep: Integer; aStepFrac: Single): Integer; + function Tree(aObject: Integer; aStep: Integer; aStepFrac: Single; aLoop: Boolean): Integer; + function House(aHT: TKMHouseType; aAct: TKMHouseActionType; aStep: Integer; aStepFrac: Single): Integer; + function Beast(aHT: TKMHouseType; BeastId, BeastAge, aStep: Integer; aStepFrac: Single): Integer; + end; + + +implementation +uses + KM_CommonClasses, KM_Resource, KM_CommonTypes, KM_Pics, KM_ResUnits, KM_GameSettings, + KaMResourceInput; + + +type + EInterpolationDataFormatError = class(Exception); + + TInterpolationStaging = record + UnitActions: TKMUnitActionInterp; + SerfCarry: TKMSerfCarryInterp; + UnitThoughts: TKMUnitThoughtInterp; + Trees: TKMTreeInterp; + Houses: TKMHouseInterp; + Beasts: TKMBeastInterp; + end; + PInterpolationStaging = ^TInterpolationStaging; + + +procedure CheckInterpolationMarker(aStream: TKMemoryStreamBinary; const aExpected: string); +var + markerLength: Word; + markerBytes: array[0..13] of Byte; + i: Integer; +begin + aStream.ReadBuffer(markerLength, SizeOf(Word)); + if markerLength <> Length(aExpected) then + raise EInterpolationDataFormatError.Create('Invalid interpolation data marker'); + + aStream.ReadBuffer(markerBytes[0], markerLength); + for i := 1 to Length(aExpected) do + if markerBytes[i - 1] <> Ord(aExpected[i]) then + raise EInterpolationDataFormatError.Create('Invalid interpolation data marker'); +end; + + +{ TKMResInterpolation } +procedure TKMResInterpolation.LoadFromFile(const FileName: string); +var + S: TKMemoryStreamBinary; + stagingPtr: PInterpolationStaging; + effectiveReference: string; +begin + fInterpolationDataEligible := False; + + if FileName = ExeDir + 'data' + PathDelim + 'defines' + PathDelim + 'interp.dat' then + effectiveReference := KaMResolvePackagedResource('data/defines/interp.dat') + else + effectiveReference := FileName; + + S := TKMemoryStreamBinary.Create; + try + S.LoadFromFile(effectiveReference); + New(stagingPtr); + try + CheckInterpolationMarker(S, 'UnitAction'); + S.ReadBuffer(stagingPtr^.UnitActions, SizeOf(TKMUnitActionInterp)); + + CheckInterpolationMarker(S, 'SerfCarry '); + S.ReadBuffer(stagingPtr^.SerfCarry, SizeOf(TKMSerfCarryInterp)); + + CheckInterpolationMarker(S, 'UnitThoughts '); + S.ReadBuffer(stagingPtr^.UnitThoughts, SizeOf(TKMUnitThoughtInterp)); + + CheckInterpolationMarker(S, 'Trees '); + S.ReadBuffer(stagingPtr^.Trees, SizeOf(TKMTreeInterp)); + + CheckInterpolationMarker(S, 'Houses'); + S.ReadBuffer(stagingPtr^.Houses, SizeOf(TKMHouseInterp)); + + CheckInterpolationMarker(S, 'Beasts'); + S.ReadBuffer(stagingPtr^.Beasts, SizeOf(TKMBeastInterp)); + + System.Move(stagingPtr^.UnitActions, fUnitActions, SizeOf(TKMUnitActionInterp)); + System.Move(stagingPtr^.SerfCarry, fSerfCarry, SizeOf(TKMSerfCarryInterp)); + System.Move(stagingPtr^.UnitThoughts, fUnitThoughts, SizeOf(TKMUnitThoughtInterp)); + System.Move(stagingPtr^.Trees, fTrees, SizeOf(TKMTreeInterp)); + System.Move(stagingPtr^.Houses, fHouses, SizeOf(TKMHouseInterp)); + System.Move(stagingPtr^.Beasts, fBeasts, SizeOf(TKMBeastInterp)); + fInterpolationDataEligible := True; + finally + Dispose(stagingPtr); + end; + finally + S.Free; + end; +end; + + +function TKMResInterpolation.UnitAction(aUnit: TKMUnitType; aAct: TKMUnitActionType; aDir: TKMDirection; aStep: Integer; + aStepFrac: Single): Integer; +var + A: TKMAnimLoop; + step, subStep, baseFrame, candidate: Integer; +begin + A := gRes.Units[aUnit].UnitAnim[aAct, aDir]; + + step := aStep mod Byte(A.Count) + 1; + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1); + baseFrame := A.Step[step] + 1; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + candidate := fUnitActions[aUnit, aAct, aDir, step, subStep]; + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxUnits].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxUnits].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +function TKMResInterpolation.UnitActionByPercent(aUnit: TKMUnitType; aAct: TKMUnitActionType; aDir: TKMDirection; aPercent: Single): Integer; +var + count: Integer; + fracStep: Single; +begin + count := gRes.Units[aUnit].UnitAnim[aAct, aDir].Count; + fracStep := Min(aPercent, 1.0) * (count-1); + Result := UnitAction(aUnit, aAct, aDir, Trunc(fracStep), Frac(fracStep)); +end; + + +function TKMResInterpolation.UnitThought(aTh: TKMUnitThought; aStep: Integer; aStepFrac: Single): Integer; +var + animCount, step, subStep: Integer; + baseFrame, candidate: Integer; +begin + animCount := UNIT_THOUGHT_BOUNDS[aTh, 2] - UNIT_THOUGHT_BOUNDS[aTh, 1]; + step := aStep mod Byte(animCount) + 1; + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1); + baseFrame := UNIT_THOUGHT_BOUNDS[aTh, 2] + 1 - step; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + candidate := fUnitThoughts[aTh, step, subStep]; + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxUnits].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxUnits].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +function TKMResInterpolation.SerfCarry(aWare: TKMWareType; aDir: TKMDirection; aStep: Integer; aStepFrac: Single): Integer; +var + A: TKMAnimLoop; + step, subStep, baseFrame, candidate: Integer; +begin + A := gRes.Units.SerfCarry[aWare, aDir]; + + step := aStep mod Byte(A.Count) + 1; + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1); + baseFrame := A.Step[step] + 1; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + candidate := fSerfCarry[aWare, aDir, step, subStep]; + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxUnits].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxUnits].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +function TKMResInterpolation.Tree(aObject, aStep: Integer; aStepFrac: Single; aLoop: Boolean): Integer; +var + A: TKMAnimLoop; + step, subStep, baseFrame, candidate: Integer; +begin + A := gMapElements[aObject].Anim; + + step := aStep mod Byte(A.Count) + 1; + + if aLoop or (step < A.Count) then + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1) + else + subStep := 0; + baseFrame := A.Step[step] + 1; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + candidate := fTrees[aObject, step, subStep]; + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxTrees].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxTrees].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +function TKMResInterpolation.House(aHT: TKMHouseType; aAct: TKMHouseActionType; aStep: Integer; aStepFrac: Single): Integer; +var + A: TKMAnimLoop; + step, subStep, baseFrame, candidate: Integer; +begin + A := gRes.Houses[aHT].Anim[aAct]; + + step := aStep mod Byte(A.Count) + 1; + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1); + baseFrame := A.Step[step] + 1; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + candidate := fHouses[aHT, aAct, step, subStep]; + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxHouses].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxHouses].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +function TKMResInterpolation.Beast(aHT: TKMHouseType; BeastId, BeastAge, aStep: Integer; aStepFrac: Single): Integer; +var + A: TKMAnimLoop; + step, subStep, baseFrame, candidate: Integer; +begin + A := gRes.Houses.BeastAnim[aHT, BeastId, BeastAge]; + + step := aStep mod Byte(A.Count) + 1; + subStep := EnsureRange(Floor(INTERP_LEVEL*aStepFrac), 0, INTERP_LEVEL-1); + baseFrame := A.Step[step] + 1; + + if not fInterpolationDataEligible then + Exit(baseFrame); + if not gGameSettings.GFX.InterpolatedAnimations then + Exit(baseFrame); + + case aHT of + htSwine: candidate := fBeasts[1, BeastId, BeastAge, step, subStep]; + htStables: candidate := fBeasts[2, BeastId, BeastAge, step, subStep]; + htMarket: candidate := fBeasts[3, BeastId, 1, step, subStep]; + else candidate := -1; + end; + + if candidate <= 0 then Exit(baseFrame); + if candidate > gRes.Sprites[rxHouses].RXData.Count then Exit(baseFrame); + if gRes.Sprites[rxHouses].RXData.Size[candidate].X = 0 then Exit(baseFrame); + Result := candidate; +end; + + +end.