Complete BigTask
Sync changed files to Google Drive / sync (push) Successful in 36s

This commit is contained in:
admin_d committed 2026-10-13 13:09:13 +05:00
1 parent f1b253e842
commit 039dcaa381
3 files changed
+300 -1

No files matched your search

+2 -1
View File
@@ -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"
+1
View File
@@ -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"},
@@ -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.