Skip to content

Instantly share code, notes, and snippets.

@fireundubh
Forked from yggdrasil75/templatesection.pas
Last active January 9, 2021 14:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fireundubh/b607858acfda6506cc360ddf993393be to your computer and use it in GitHub Desktop.
Save fireundubh/b607858acfda6506cc360ddf993393be to your computer and use it in GitHub Desktop.
procedure TemplateLists;
var
i : Integer;
j : Integer;
memIniFile : TMemIniFile;
bOverrides : Boolean;
bOverridePrimary : Boolean;
bDebug : Boolean;
IniFileStreams : TStringList;
IniTemplates : TStringList;
s : String;
begin
templateMegaList := TStringList.Create;
bDebug := False;
// setup template files
// ALLATemplate.ini
// ALLAUserTemplate.ini
// ALLA+<modname>+template.ini in data folder (ie: ALLAYggKeywords.espTemplate.ini)
IniTemplates := TStringList.Create;
IniTemplates.Add(ScriptsPath + 'ALLATemplate.ini');
IniTemplates.Add(ScriptsPath + 'ALLAUserTemplate.ini');
for i := 0 to Pred(FileCount) do
if FileExists(DataPath + 'alla' + GetFileName(FileByIndex(i)) + 'template.ini') then
IniTemplates.Add(DataPath + 'alla' + GetFileName(FileByIndex(i)) + 'template.ini');
AddMessage('[TemplateLists] detected ' + IniTemplates.Count + ' Template Inis');
for i := Pred(IniTemplates.Count) downto 2 do
begin
memIniFile := TMemIniFile.Create(IniTemplates[i]);
bOverrides := memIniFile.ReadBool('explanation', 'ignorePrimary', False);
// this is for waccf and similar to "fix" leveled lists
if bOverrides then
bOverridePrimary := True;
IniFileStreams.AddObject(IniTemplates[i], memIniFile);
end;
if not bOverridePrimary then
begin
AddMessage('[TemplateLists] Primary INI files are used as WACCF or similar has not been detected - if you believe this is in error, please check your inis for ''ignorePrimary''');
for i := 2 downto 0 do
begin
memIniFile := TMemIniFile.Create(IniTemplates[i]);
IniFileStreams.AddObject(IniTemplates[i], memIniFile);
end;
end
else
AddMessage('[TemplateLists] Primary INI files are ignored as WACCF or similar has been detected - if you believe this is in error, please check your inis for ''ignorePrimary''');
if bDebug then
AddMessage('[TemplateLists] loaded all detected INIs');
if bDebug then
AddMessage('[TemplateLists] begin detection of major groupings');
groupings := TStringList.Create;
groupings.Sorted := True;
groupings.Duplicates := dupIgnore;
// use AppendDelimited
for i := Pred(IniFileStreams.Count) downto 0 do
AppendDelimited(groupings, IniFileStreams.Objects[i].ReadString('explanation', 'groupings', ''));
AddMessage('[TemplateLists] detected ' + groupings.Count + ' different primary categories of items');
for i := Pred(groupings.Count) downto 0 do
begin
s := groupings[i];
// this contains the info stored in the inis temporarily.
aStringList := TStringList.Create;
// this is the "types" of items, it splits up for instance helmet, pauldron, poleyn, and sabaton
types := TStringList.Create;
types.Sorted := True;
types.Duplicates := dupIgnore;
// this is the grup(s) that the grouping is used on. for instance, heavy and light are armo, weapon is weap, misc is possible as well eventually
grup := TStringList.Create;
grup.Sorted := True;
grup.Duplicates := dupIgnore;
// this determines words/phrases that will be used to attempt to determine whether an r belonds in this grouping.
ident := TStringList.Create;
ident.Sorted := True;
ident.Duplicates := dupIgnore;
// this is the list of comparable elements used to determine iTier association. secondary is able to shift the iTier logarithmically up or down a certain number (bigger changes required each shift)
secondaryPath := TStringList.Create;
secondaryPath.Sorted := True;
secondaryPath.Duplicates := dupIgnore;
for j := 0 to Pred(IniFileStreams.Count) do
begin
memIniFile := IniFileStreams.Objects[j];
AppendDelimited(types, memIniFile.ReadString(s, 'types', ''));
AppendDelimited(grup, memIniFile.ReadString(s, 'grup', ''));
AppendDelimited(ident, memIniFile.ReadString(s, 'ident', ''));
// this is used to set the iTier of a given r initially, secondary can be used to determine a further shift (the last will be the only kept)
PrimaryPath := memIniFile.ReadString(s, 'primaryPath', '');
AppendDelimited(secondaryPath, memIniFile.ReadString(s, 'secondaryPath', ''));
// this is the upper limit for the tiers only the last version will be kept
MaxLevel := memIniFile.ReadInt(s, 'maxLevel', '');
end;
end;
aStringList.AddObject('types', types);
aStringList.AddObject('grup', grup);
aStringList.AddObject('identifier', ident);
aStringList.AddObject('sPath1', PrimaryPath);
aStringList.AddObject('slPath2', secondaryPath);
aStringList.AddObject('iMax', MaxLevel);
groupings2.AddObject(s, aStringList);
if bDebug then
AddMessage('groupings pass 1 has been completed.');
for i := Pred(groupings2.Count) downto 0 do
begin
cgs := groupings2[i];
cg2 := groupings2.Objects[i];
cgti := cg2.Objects[cg2.IndexOf(types)];
tml := TStringList.Create;
tml.AddObject('types', groupings2.Objects[groupings2.IndexOf('types')]);
tml.AddObject('grup', groupings2.Objects[groupings2.IndexOf('grup')]);
tml.AddObject('identifier', groupings2.Objects[groupings2.IndexOf('identifier')]);
tml.AddObject('sPath1', groupings2.Objects[groupings2.IndexOf('sPath1')]);
tml.AddObject('slPath2', groupings2.Objects[groupings2.IndexOf('slPath2')]);
tml.AddObject('iMax', groupings2.Objects[groupings2.IndexOf('iMax')]);
for j := Pred(cgti.Count) downto 0 do
begin
iCT := cgti[j];
TempItemList := TStringList.Create;
TempKywdList := TStringList.Create;
TempDuolist := TStringList.Create;
for k := 0 to Pred(IniFileStreams.Count) do
begin
memIniFile := IniFileStreams.Objects[i];
AppendDelimited(TempItemList, memIniFile.ReadString(cgs, iCT, ''));
AppendDelimited(TempKywdList, memIniFile.ReadString(cgs, iCT + 'aKeywords', ''));
end;
TempDuolist.AddObject(cgs, TempItemList);
TempDuolist.AddObject(cgs + 'aKeywords', TempKywdList);
tml.AddObject(iCT, TempDuolist);
end;
Compact(tml);
templateMegaList.AddObject(cgs, tml);
if bDebug then
AddMessage('groupings pass 2 completed.');
end;
end;
procedure AppendDelimited(out aStringList: TStringList; aSourceText: String);
var
sl: TStringList;
begin
sl := TStringList.Create;
sl.DelimitedText := aSourceText;
aStringList.AddStrings(sl);
sl.Free;
end;
procedure Compact(out aStringList: TStringList);
var
slValueTiers : TStringList;
slCIL2 : TStringList;
slCIL : TStringList;
slValues : TStringList;
slEmpty : TStringList;
slPath2Temp : TStringList;
slPaths : TStringList;
sCIPp : String;
sCIN : String;
sCIS : String;
sTemp : String;
iCT : Integer;
iCIT : Integer;
iMax : Integer;
i : Integer;
j : Integer;
k : Integer;
bDebug : Boolean;
kCI : IInterface;
begin
bDebug := False;
slPaths := TStringList.Create;
// primary path
sTemp := aStringList.Objects[3];
slPaths.AddObject(Copy(sTemp, 0, Pos(':', sTemp) - 1), TryStrToFloat(Copy(sTemp, Pos(':', sTemp) + 1, sTemp.Length), 1));
// secondary slPaths
slPath2Temp := aStringList.Objects[4];
for i := Pred(slPath2Temp.Count) downto 0 do
begin
sTemp := slPath2Temp.Objects[i];
slPaths.AddObject(Copy(sTemp, 0, Pos(':', sTemp) - 1), TryStrToFloat(Copy(sTemp, Pos(':', sTemp) + 1, sTemp.Length), 1));
end;
slValues := TStringList.Create;
iMax := aStringList.Objects[5];
for i := Pred(slPaths.Count) downto 0 do
begin
// initialize the slValues to have a proper name
slEmpty := TStringList.Create;
for i := 0 to Pred(slValues.Count) do
slEmpty.AddObject(IntToStr(i), 0);
slValues.AddObject(slPaths[i], slEmpty);
end;
i := 6;
repeat
if bDebug then
AddMessage('generating compacted comparison slValues');
// get the value of sPath1 and slPath2 for each, and average them.
slCIL := aStringList.Objects[i]; // current r list
slCIL2 := TStringList.Create;
// process all items
for j := Pred(slCIL.Count) downto 0 do
begin
sCIS := slCIL.Objects[j];
sCIP := Copy(sCIS, 0, Pos('|') - 1);
sCIN := Copy(sCIS, Pos('|') + 1, Pos(':') - 1);
iCIT := TryStrToInt(Copy(sCIS, Pos(':') + 1, sCIS.Length), 15);
kCI := RecordByEDID(fileByName(sCIP), sCIN);
slCIL2[j] := iCIT;
slCIL2.Objects[j] := kCI;
end;
slCIL := slCIL2;
slCIL2.Free;
aStringList.AddObject(aStringList[i] + 'slValues', slValues);
i := i + 2;
until i > aStringList.Count - 2;
i := 6;
repeat
if bDebug then
AddMessage('generating compacted comparison slValues');
// get the value of sPath1 and slPath2 for each, and average them.
slCIL := aStringList.Objects[i]; // current r list
for j := Pred(slCIL.Count) downto 0 do
begin // process all items
kCI := ObjectToElement(slCIL.Objects[j]);
iCT := slCIL[j];
// process all items over all slPaths
for k := Pred(slPaths.Count) downto 0 do begin
slValueTiers := slValues.Objects[k];
if iCT > iMax then
begin
AddMessage('an r breached iMax level of selected template list');
continue;
end;
// the extra math is for smoothing the curve a bit. probably could drop it easily.
slValueTiers.Objects[iCT] := slValueTiers.Objects[iCT] + (TryStrToFloat(GetElementEditValues(kCI, slPaths[k]), 1) * slPaths.Objects[k]);
end;
end;
aStringList.AddObject(aStringList[i] + 'slValues', slValues);
i := i + 2;
until i > aStringList.Count - 2;
end;
function GetTemplate(aRecord: IInterface): IInterface;
// where the record is actually compared.
var
slPartType : TStringList;
slValues : TStringList;
iTier : Integer;
begin
// get slValues of record, compare with expected template.
slValues := TemplateListSelector(aRecord);
// find which part type (ie: helmet, chestplate, etc) list to use
slPartType := SubTemplateSelector(aRecord, slValues);
// find "most similar" by a few things. dunno what all yet, probably shared keyword count, and difference of slValues listed in the 2 slPaths options.
iTier := GetTier(aRecord, slPartType, slValues);
Result := GetTemplateItem(aRecord, slPartType, slValues, iTier);
end;
function GetTemplateItem(aRecord: IInterface; aTemplateList: TStringList; aTemplateMain: TStringList; aTier: Integer): IInterface;
var
slItems : TStringList;
i : Integer;
iNearest : Integer;
iLikeness : Integer;
iLikenessNew : Integer;
iStartCount : Integer;
iEndCount : Integer;
kCI : IInterface;
begin
slItems := aTemplateList.Objects[0];
slItems.Sorted := True;
iStartCount := slItems.IndexOf(aTier);
iEndCount := slItems.IndexOf(aTier + 1);
for i := Pred(iStartCount) to Pred(iEndCount) do
begin
iLikenessNew := 0;
kCI := ObjectToElement(slItems.Objects[i]);
iLikenessNew := SameKeywordCount(aRecord, kCI);
aStringList := TStringList.Create;
aStringList.Delimiter := ' ';
aOtherStringList := TStringList.Create;
aOtherStringList.Delimiter := ' ';
aStringList.DelimitedText := DisplayName(aRecord);
aOtherStringList.DelimitedText := DisplayName(kCI);
iLikenessNew := iLikenessNew + CompareStringLists(aStringList, aOtherStringList);
aStringList.Delimiter := '_';
aOtherStringList.Delimiter := '_';
aStringList.DelimitedText := EditorID(aRecord);
aOtherStringList.DelimitedText := EditorID(kCI);
iLikenessNew := iLikenessNew + CompareStringLists(aStringList, aOtherStringList);
// todo: change up iLikeness to be more?
if iLikenessNew > iLikeness then begin
iLikeness := iLikenessNew;
iNearest := i;
end;
end;
Result := ObjectToElement(slItems.Objects[iNearest]);
end;
function CompareStringLists(aStringList: TStringList; aOtherStringList: TStringList): Integer;
var
i : Integer;
iCount : Integer;
iIndex : Integer;
begin
aStringList.Sort;
aOtherStringList.Sort;
for i := 0 to Pred(aStringList.Count) do
begin
iIndex := aOtherStringList.IndexOf(aStringList[i]);
if not iIndex < 0 then begin
aStringList.delete[iIndex];
iCount := iCount + 1;
end;
end;
Result := iCount;
end;
function SameKeywordCount(aRecord: IInterface; aOtherRecord: IInterface): Integer;
var
bDebug : Boolean;
i : Integer;
begin
// Begin debugMsg section
bDebug := False;
Result := False;
kTempRecord := ElementBySignature(aRecord, 'KWDA');
for i := 0 to Pred(ElementCount(kTempRecord)) do
if HasKeyword(aOtherRecord, ElementByIndex(kTempRecord, i)) then
total := total + 1;
bDebug := False;
// End debugMsg section
end;
function SubTemplateSelector(aRecord: IInterface; aTemplateList: TStringList): TStringList;
var
i : Integer;
slSubTemplate : TStringList;
slPartType : TStringList;
sCTLN : String;
sCTLN1 : String;
begin
repeat
i := i + 1;
until ContainsText(aTemplateList[i], 'aKeywords');
while i < aTemplateList.Count - 2 do
begin
if not ContainsText(aTemplateList[i], aKeywords) then
break;
slPartType := aTemplateList.Objects[i];
sCTLN := aTemplateList[i];
sCTLN1 := aTemplateList[i - 1];
slSubTemplate := TStringList.Create;
if HasAKeyword(aRecord, slPartType) then
if ContainsText(sCTLN, sCTLN1) then
begin
slSubTemplate.AddObjects(sCTLN1, aTemplateList.Objects[i - 1]);
slSubTemplate.AddObjects(sCTLN1 + 'slValues', aTemplateList.Objects[aTemplateList.IndexOf(sCTLN1 + 'slValues')]);
Result := slSubTemplate;
exit;
end;
i := i + 2;
end;
end;
function GetTier(aRecord: IInterface; aTemplateList: TStringList; aTemplateMain: TStringList): TStringList;
var
slPath2Temp : TStringList;
slPath2 : TStringList;
slValueSublist : TStringList;
slValue : TStringList;
sTemp : String;
sPath1 : String;
dPrimePathCR : Double;
dPath1Comp : Double;
dPath2T : Double;
dPath2A : Double;
i : Integer;
j : Integer;
iTier : Integer;
begin
// Result here will be a list of items in the iTier
slValue := aTemplateList.Objects[1];
// primary path for comparison
sPath1 := Copy(aTemplateMain.Objects[3], 0, Pos(':', aTemplateMain.Objects[3]) - 1);
// sPath1 mult value
dPath1Comp := TryStrToFloat(Copy(sTemp, Pos(':', sTemp) + 1, sTemp.Length), 1);
slPath2Temp := aTemplateMain.Objects[4];
for i := Pred(slPath2Temp.Count) downto 0 do
begin
sTemp := slPath2Temp.Objects[i];
slPath2.AddObject(Copy(sTemp, 0, Pos(':', sTemp) - 1), TryStrToFloat(Copy(sTemp, Pos(':', sTemp) + 1, sTemp.Length), 1));
end;
slValueSublist := slValue.Objects[slValue.IndexOf(sPath1)];
dPrimePathCR := TryStrToFloat(GetElementEditValues(aRecord, sPath1), 1) * dPath1Comp;
for i := 0 to Pred(slValueSublist.Count) do
begin
if dPrimePathCR > slValueSublist.Objects[i] + 1 then
continue;
if dPrimePathCR < slValueSublist.Objects[i] - 1 then
begin
i := i - 1;
break;
end;
break;
end;
iTier := i;
AddMessage('primary iTier for ' + DisplayName(aRecord) + ' is ' + iTier + ' processing secondary modifiers now');
for i := 0 to Pred(slPath2.Count) do
begin
slValueSublist := slValue.Objects[slValue.IndexOf(slPath2[i])];
for j := 0 to Pred(slValueSublist.Count) do
begin
if dPrimePathCR > slValueSublist.Objects[j] + 1 then
continue;
if dPrimePathCR < slValueSublist.Objects[j] - 1 then
begin
j := j - 1;
break;
end;
break;
end;
dPath2T := dPath2T + j;
end;
dPath2A := dPath2T / slPath2.Count;
if iTier < dPath2A - 10 then
iTier := iTier - 3
else if iTier < dPath2A - 7 then
iTier := iTier - 2
else if iTier < dPath2A - 3 then
iTier := iTier - 1
else if iTier > dPath2A + 10 then
iTier := iTier + 3
else if iTier < dPath2A + 7 then
iTier := iTier + 2
else if iTier < dPath2A + 3 then
iTier := iTier + 1;
AddMessage('with alternative slPaths, iTier is now ' + iTier);
end;
function TemplateListSelector(aRecord: IInterface): TStringList;
var
slValues : TStringList;
i : Integer;
begin
// 2 options, find first applicable template section based on aIdents
// find the most applicable. first easier.
for i := 0 to Pred(templateMegaList.Count) do
begin
slValues := templateMegaList.Objects[i];
if not signature(aRecord) = slValues.Objects[2] then
continue;
if not HasIdent(aRecord, slValues.Objects[3]) then
continue;
Result := slValues;
exit;
end;
end;
function HasAKeyword(aRecord: IInterface; aKeywords: TStringList): Boolean;
var
bDebug : Boolean;
kTempRecord : IInterface;
i : Integer;
j : Integer;
begin
// Begin debugMsg section
bDebug := False;
Result := False;
kTempRecord := ElementBySignature(aRecord, 'KWDA');
for i := 0 to Pred(ElementCount(kTempRecord)) do
begin
for j := 0 to Pred(aKeywords.Count) do
begin
if bDebug then
AddMessage('[HasAKeyword] if (' + EditorID(LinksTo(ElementByIndex(kTempRecord, i))) + ' = ' + aKeywords[j] + ' ) then begin');
if SameText(EditorID(LinksTo(ElementByIndex(kTempRecord, i))), aKeywords[j]) then
begin
if bDebug then
AddMessage('[HasAKeyword] Result := True');
Result := True;
exit;
end;
end;
end;
bDebug := False;
// End debugMsg section
end;
function HasIdent(aRecord: IInterface; aIdents: TStringList): Boolean;
var
sTempEDID : String;
sTempName : String;
i : Integer;
begin
sTempEDID := EditorID(aRecord);
sTempName := DisplayName(aRecord);
Result := False;
for i := Pred(aIdents.Count) downto 0 do
if ContainsText(sTempEDID, aIdents[i]) or ContainsText(sTempName, aIdents[i]) then
begin
Result := True;
exit;
end;
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment