Skip to content

Instantly share code, notes, and snippets.

@yggdrasil75
Last active January 9, 2021 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yggdrasil75/4ae8f8455b36d87b2bcee4623b3b89af to your computer and use it in GitHub Desktop.
Save yggdrasil75/4ae8f8455b36d87b2bcee4623b3b89af to your computer and use it in GitHub Desktop.
procedure templatelists;
var
j, i: int;
cini: TMemIniFile;
overrides, overrideprimary, debugmsg: boolean;
IniFilestreams, IniTemplates:TStringlist;
cg:string;
begin
templateMegaList := tstringlist.create;
debugmsg := 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 filecount - 1 do begin
if FileExists(DataPath + 'alla' + GetFileName(FileByIndex(i)) + 'template.ini') then
IniTemplates.add(DataPath + 'alla' + GetFileName(fileByIndex(i)) + 'template.ini');
end;
msg('[TemplateLists] detected ' + IniTemplates.count + ' Template Inis');
for i := IniTemplates.count - 1 downto 2 do begin
cini := TMemIniFile.create(IniTemplates[i]);
overrides := cini.Readbool('explanation', 'ignorePrimary', false); // this is for waccf and similar to "fix" leveled lists
if overrides then overrideprimary := true;
IniFilestreams.addobject(initemplates[i],cini);
end;
if not overrideprimary then begin
msg('[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
cini := TMemIniFile.create(IniTemplates[i]);
IniFilestreams.addobject(initemplates[i],cini);
end;
end else msg('[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''');
{debug} if debugmsg then msg('[TemplateLists] loaded all detected INIs');
{debug} if debugmsg then msg('[TemplateLists] begin detection of major groupings');
groupings := TStringlist.create;
groupings.Sorted := True;
groupings.Duplicates := dupIgnore;
for i := IniFilestreams.count - 1 downto 0 do begin // use appenddelimted
AppendDelimited(groupings,IniFilestreams.objects[i].readstring('explanation','groupings',''));
end;
msg('[TemplateLists] detected ' + groupings.count + ' different primary categories of items');
for i := groupings.count - 1 downto 0 do begin
cg := groupings[i];
templist := tstringlist.create; //this contains the info stored in the inis temporarily.
types := tstringlist.create; //this is the "types" of items, it splits up for instance helmet, pauldron, poleyn, and sabaton
types.sorted := true;
types.duplicates := dupIgnore;
grup := tstringlist.create; //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.sorted := true;
grup.duplicates := dupIgnore;
ident := tstringlist.create; //this determines words/phrases that will be used to attempt to determine whether an item belonds in this grouping.
ident.sorted := true;
ident.duplicates := dupIgnore;
secondaryPath := tstringlist.create; //this is the list of comparable elements used to determine tier association. secondary is able to shift the tier logarithmically up or down a certain number (bigger changes required each shift)
secondaryPath.sorted := true;
secondaryPath.duplicates := dupIgnore;
for j := 0 to IniFilestreams.count - 1 begin
cini := IniFilestreams.objects[j];
appenddelimted(types,cini.readstring(cg,'types',''));
appenddelimted(grup,cini.readstring(cg,'grup',''));
appenddelimted(ident,cini.readstring(cg,'ident',''));
PrimaryPath := cini.readstring(cg,'primaryPath',''); //this is used to set the tier of a given item initially, secondary can be used to determine a further shift (the last will be the only kept)
appenddelimted(secondaryPath,cini.readstring(cg,'secondaryPath',''));
MaxLevel := cini.readint(cg,'maxLevel',''); // this is the upper limit for the tiers only the last version will be kept
end;
templist.addobject('types',types);
templist.addobject('grup',grup);
templist.addobject('identifier',ident);
templist.addobject('path1',primaryPath);
templist.addobject('path2',secondaryPath);
templist.addobject('max',MaxLevel);
groupings2.addobject(cg,templist);
end;
{debug} if debugmsg then msg('groupings pass 1 has been completed.');
for i := groupings2.count - 1 downto 0 do begin
cgs := groupings2[i];
cg2 := groupings2.objects[i];
cgti := cg2.objects[cg2.indexof(types)];
tml := tstring.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('path1', groupings2.objects[groupings2.indexof('path1')]);
tml.addobject('path2', groupings2.objects[groupings2.indexof('path2')]);
tml.addobject('max', groupings2.objects[groupings2.indexof('max')]);
for j := cgti.count - 1 downto 0 do begin
ct := cgti[j];
TempItemList := TStringlist.Create;
TempKywdList := TStringlist.Create;
TempDuolist := TStringlist.Create;
for k := 0 to IniFilestreams.count - 1 do begin
cini := IniFilestreams.objects[i];
AppendDelimited(TempItemList, cini.readstring(cgs,ct,''));
appenddelimted(TempKywdList, cini.readstring(cgs,ct+'keywords',''));
end;
TempDuolist.addobject(cgs,TempItemList);
TempDuolist.addobject(cgs+'keywords',TempKywdList);
tml.addobject(ct,TempDuolist);
end;
compact(tml);
templateMegaList.addobject(cgs,tml);
end;
{debug} if debugmsg then msg('groupings pass 2 completed.');
end;
procedure AppendDelimited(out templist: TStringlist; tempstring: string);
var
templist2: tstringlist;
begin
templist2 := tstringlist.create;
templist2.delimitedText := tempstring;
templist.addstrings(templist2);
templist2.free;
end;
procedure compact(out templist: tstringlist);
var
valuetiers,cil2,cil,values,empty,temppath2list,paths: TStringList;
cip,cin,cis,temps: string;
ct,cit,max,i,j,k: integer;
debugmsg: boolean;
ci: IInterface;
begin
debugmsg := false;
paths := tstringlist.create;
temps := templist.objects[3]; //primary path
paths.addobject(copy(temps,0,pos(':',temps)-1), tryStrToFloat(copy(temps,pos(':',temps)+1,temps.length),1));
temppath2list := templist.objects[4]; //secondary paths.
for i := temppath2list.count - 1 downto 0 do begin
temps := temppath2list.objects[i];
paths.addobject(copy(temps,0,pos(':',temps)-1), tryStrToFloat(copy(temps,pos(':',temps)+1,temps.length),1));
end;
values := tstringlist.create;
max := templist.objects[5];
for i := paths.count - 1 downto 0 do begin //initialize the values to have a proper name
empty := tstringlist.create;
for i := 0 to values - 1 do begin
empty.addobject(IntToStr(i),0);
end;
values.addobject(paths[i],empty);
end;
i := 6;
repeat
{debug} if debugmsg then msg('generating compacted comparison values');
//get the value of path1 and path2 for each, and average them.
cil := templist.objects[i]; //current item list
cil2 := tstringlist.create;
for j := cil.count - 1 downto 0 do begin //process all items
cis := cil.objects[j];
cip := copy(cis, 0,pos('|')-1);
cin := copy(cis, pos('|') + 1, pos(':') - 1);
cit := tryStrToInt(copy(cis, pos(':') + 1, cis.length), 15);
ci := RecordByEDID(fileByName(cip), cin);
cil2[j] := cit
cil2.objects[j] := ci;
end;
cil := cil2;
cil2.free;
templist.addobject(templist[i]+'values',values);
i := i + 2;
until i > templist.count - 2;
i := 6;
repeat
{debug} if debugmsg then msg('generating compacted comparison values');
//get the value of path1 and path2 for each, and average them.
cil := templist.objects[i]; //current item list
for j := cil.count - 1 downto 0 do begin //process all items
ci := objecttoelement(cil.objects[j]);
ct := cil[j];
for k := paths.count - 1 downto 0 do begin //process all items over all paths
valueTiers := values.objects[k];
if ct > max then begin
msg('an item breached max level of selected template list');
continue;
end;
valueTiers.objects[ct] := valueTiers.objects[ct] + (tryStrToFloat(getelementeditvalues(ci, paths[k]), 1) * paths.objects[k]); //the extra math is for smoothing the curve a bit. probably could drop it easily.
end;
end;
templist.addobject(templist[i]+'values',values);
i := i + 2;
until i > templist.count - 2;
end;
function GetTemplate(aRecord: iinterface): IInterface; //where the record is actually compared.
var
ctl,ctml: tstringlist;
tier:integer;
item:IInterface;
begin
// get values of record, compare with expected template.
ctml := templatelistselector(aRecord);
// find which part type (ie: helmet, chestplate, etc) list to use
ctl := subtemplateSelector(aRecord, ctml);
//find "most similar" by a few things. dunno what all yet, probably shared keyword count, and difference of values listed in the 2 paths options.
tier := getTier(aRecord, ctl,ctml);
item := getTemplateItem(aRecord, ctl, ctml, tier);
result :=item;
end;
function getTemplateItem(aRecord: IInterface; templateList, templateMain: TStringlist, tier: integer): IInterface;
var
itemlist:tstringlist;
nearest,likeness,likenessnew,i,startcount,endcount:integer;
ci:IInterface;
begin
itemList := templateList.objects[0];
itemlist.sorted := true;
startcount := itemlist.indexOf(tier);
endcount := itemlist.indexof(tier+1);
for i := startcount - 1 to endcount - 1 do begin
likenessnew := 0
ci := ote(itemlist.objects[i]);
likenessnew := SameKeywordCount(aRecord, ci);
list1 := tstringlist.create;
list1.delimiter := ' ';
list2 := tstringlist.create;
list2.delimiter := ' ';
list1.delimitedText := displayName(aRecord);
list2.delimitedText := displayName(ci);
likenessnew := likenessnew + CompareStringLists(list1,list2);
list1.delimiter := '_';
list2.delimiter := '_';
list1.delimitedText := EditorID(aRecord);
list2.delimitedText := EditorID(ci);
likenessnew := likenessnew + CompareStringLists(list1,list2);
//todo: change up likeness to be more?
if likenessnew > likeness then begin
likeness := likenessnew;
nearest := i;
end;
end;
result := ote(itemlist.objects[nearest]);
end;
function CompareStringLists(List1, List2: TStringList;): integer;
var
l1i,count,i:integer;
begin
List1.Sort;
List2.Sort;
for i := 0 to list1.count - 1 do begin
l1i := list2.indexOf(list1[i]);
if not l1i < 0 then begin
list1.delete[l1i];
count := count + 1;
end;
end;
result := count;
end;
function SameKeywordCount(aRecord,bRecord: IInterface): integer;
var
debugmsg:boolean;
i:integer;
begin
// Begin debugMsg section
debugMsg := false;
Result := False;
tempRecord := ebp(aRecord, 'KWDA');
for i := 0 to ec(tempRecord) - 1 do begin
if hasKeyword(bRecord, elementbyIndex(temprecord,i)) then total := total + 1;
end;
debugMsg := false;
// End debugMsg section
end;
function subtemplateSelector(aRecord: IInterface; templateList: tstringlist): tstringlist;
var
i:integer;
subTemplate,ctl:tstringlist;
ctln,ctln1:string;
begin
repeat
i := i + 1;
until containsText(templateList[i],'keywords');
while i < templateList.count - 2 do begin
if not containsText(templateList[i],keywords) then break;
ctl := templateList.objects[i];
ctln := templateList[i];
ctln1 := templateList[i-1];
subTemplate := tstringlist.create;
if hasakeyword(aRecord,ctl) then begin
if containsText(ctln,ctln1) then begin
subTemplate.addobjects(ctln1,templateList.objects[i-1]);
subTemplate.addobjects(ctln1+'values',templateList.objects[templateList.indexof(ctln1+'values')]);
result := subTemplate;
exit;
end;
end;
i := i + 2;
end;
end;
function getTier(aRecord: IInterface; templateList, templateMain: TStringlist): tStringList;
var
temppath2list,path2,valuesublist,valuelist: tstringlist;
temps,path1:string;
primepathCR,path1comp,p2t,p2a:float;
tier,i,j: integer;
begin
//result here will be a list of items in the tier
valuelist := templatelist.objects[1];
path1 := copy(templateMain.objects[3],0,pos(':',templateMain.objects[3])-1) //primary path for comparison
path1comp := tryStrToFloat(copy(temps,pos(':',temps)+1,temps.length),1) //path1 mult value
temppath2list := templatemain.objects[4];
for i := temppath2list.count - 1 downto 0 do begin
temps := temppath2list.objects[i];
path2.addobject(copy(temps,0,pos(':',temps)-1), tryStrToFloat(copy(temps,pos(':',temps)+1,temps.length),1));
end;
valuesublist := valuelist.objects[valuelist.indexOf(path1)];
primepathCR := tryStrToFloat(getelementeditvalues(aRecord, path1),1) * path1comp;
for i := 0 to valuesublist.count - 1 do begin
if primepathCR > valuesublist.objects[i] + 1 then continue
else if primepathCR < valuesublist.objects[i] - 1 then begin
i := i - 1;
break;
end else begin
break;
end;
end;
tier := i;
msg('primary tier for ' + displayName(aRecord) + ' is ' + tier + ' processing secondary modifiers now');
for i := 0 to path2.count - 1 do begin
valuesublist := valuelist.objects[valuelist.indexOf(path2[i])];
for j := 0 to valuesublist.count - 1 do begin
if primepathCR > valuesublist.objects[j] + 1 then continue
else if primepathCR < valuesublist.objects[j] - 1 then begin
j := j - 1;
break;
end else begin
break;
end;
end;
p2t := p2t + j;
end;
p2a := p2t / path2.count;
if tier < p2a - 10 then begin
tier := tier - 3;
end else if tier < p2a - 7 then begin
tier := tier - 2;
end else if tier < p2a - 3 then begin
tier := tier - 1;
end else if tier > p2a + 10 then begin
tier := tier + 3;
end else if tier < p2a + 7 then begin
tier := tier + 2;
end else if tier < p2a + 3 then begin
tier := tier + 1;
end;
msg('with alternative paths, tier is now ' + tier);
end;
function templatelistselector(aRecord: IInterface): tstringlist;
var
i:integer;
ctml:tstringlist;
begin
//2 options, find first applicable template section based on idents
//find the most applicable. first easier.
for i := 0 to templateMegaList.Count - 1 do begin
ctml := templateMegaList.objects[i];
if not signature(aRecord) = ctml.objects[2] then continue;
if not hasIdent(aRecord, ctml.objects[3]) then continue;
result := ctml;
exit;
end;
end;
function hasakeyword(aRecord: IInterface; keywords: tstringlist): boolean;
var
debugmsg: boolean;
temprecord: IInterface;
i,j:integer;
begin
// Begin debugMsg section
debugMsg := false;
Result := False;
tempRecord := ebp(aRecord, 'KWDA');
for i := 0 to Pred(ec(tempRecord)) do begin
for j := 0 to keywords.count - 1 do begin
{Debug} if debugMsg then msg('[HasaKeyword] if ('+EditorID(LinksTo(ebi(tempRecord, i)))+' = '+keywords[j]+' ) then begin');
if (EditorID(LinksTo(ebi(tempRecord, i))) = keywords[j]) then begin
{Debug} if debugMsg then msg('[HasaKeyword] Result := True');
Result := True;
exit;
end;
end;
end;
debugMsg := false;
// End debugMsg section
end;
function hasident(aRecord: IInterface; idents: tstringlist): boolean;
var
tempname,tempedid: string;
i:integer;
begin
tempEDID := EditorID(aRecord);
tempName := displayName(aRecord);
result := false;
for i := idents.count - 1 downto 0 do begin
if containsText(tempEDID, idents[i]) or if containsText(tempName, idents[i]) then begin
result := true;
exit;
end;
end;
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment