Skip to content

Instantly share code, notes, and snippets.

@yggdrasil75
Created December 13, 2021 02:40
Show Gist options
  • Select an option

  • Save yggdrasil75/174e8e5d7a638f6aa93ec4866f399040 to your computer and use it in GitHub Desktop.

Select an option

Save yggdrasil75/174e8e5d7a638f6aa93ec4866f399040 to your computer and use it in GitHub Desktop.
//quick add to list. simple solution for reuseability.
procedure AddItemToLVLI(level: integer; item: IInterface; count: integer; List: IInterface);
var
tempRecord: IInterface;
begin
tempRecord := ElementAssign(ElementByPath(currentList, 'Leveled List Entries'), HighInteger, nil, false);
beginUpdate(tempRecord);
try
SetElementEditValues(tempRecord, 'LVLO\Level', level);
SetElementEditValues(tempRecord, 'LVLO\Reference', name(item));
SetElementEditValues(tempRecord, 'LVLO\Count', count);
finally
endUpdate(tempRecord);
end;
end;
//quick remove from a single list. simple solution for reuseability.
procedure RemoveItemFromLvli(item, List: IInterface);
var
ListItems: IInterface;
ElementIterator: integer;
CurrentItem: IInterface;
begin
ListItems := ElementByPath(List, 'Leveled List Entries');
beginUpdate(ListItems);
try
for ElementIterator := 0 to ElementCount(ListItems) - 1 do begin
CurrentItem := ElementByIndex(ListItems, ElementIterator);
if equals(LinksTo(ElementByPath(CurrentItem, 'LVLO\Reference')), item) then begin
remove(CurrentItem);
end;
end;
finally
endUpdate(ListItems);
end;
end;
//quick creation of empty lists.
//you need to set Patch as a global IInterface before calling this. patch generally should be the Patch plugin, not the source plugin, but you can technically use the source if you dont care about corruption
function CreateLVLI(name: string): IInterface;
var
newLVLI: IInterface;
begin
newLVLI := Add(Patch, 'LVLI', true);
SetEditorID(newLVLI, name);
result := newLVLI;
end;
//quick creation of empty lists.
//you need to set Patch as a global IInterface before calling this. patch generally should be the Patch plugin, not the source plugin, but you can technically use the source if you dont care about corruption
function CreateLVLIFlagged(name: string; PLevel, Count, Use, Special: Boolean): IInterface;
var
newLVLI: IInterface;
begin
newLVLI := Add(Patch, 'LVLI', true);
beginUpdate(newLVLI);
try
SetEditorID(newLVLI, name);
if PLevel then begin
ElementAssign(ElementByPath(newLVLI, 'LVLF\Calculate from all levels <= player''s level'), HighInteger, nil, false);
end;
if Count then begin
ElementAssign(ElementByPath(newLVLI, 'Calculate for each item in count'), HighInteger, nil, false);
end;
if Use then begin
ElementAssign(ElementByPath(newLVLI, 'Use All'), HighInteger, nil, false);
end;
if Special then begin
ElementAssign(ElementByPath(newLVLI, 'Special Loot'), HighInteger, nil, false);
end;
finally
endUpdate(newLVLI);
end;
result := newLVLI;
end;
//returns stringlist of items in lvli. if full is true then returns a tree with makeup of:
//name
//\item-item object
//\count=count
//\level=level
//if false, then returns list of name-item object.
function LVLIToTString(lvli: IInterface; Full: Boolean): TStringList;
var
i: integer;
ListItems, currentEntry, currentItem: IInterface;
templist, OutList: TStringList;
ALLASublist: IInterface;
begin
listItems := ElementByPath(lvli, 'Leveled List Entries');
OutList := TStringList.Create;
if Full then begin
for i := 0 to ElementCount(listItems) - 1 do begin
currentEntry := ElementByIndex(listItems, i);
CurrentItem := LinksTo(ElementByPath(currentEntry, 'Reference'));
templist := TStringList.Create;
if Signature(CurrentItem) = 'LVLI' then begin
ALLASublist := CurrentItem;
if ContainsText(EditorID(ALLASublist), 'ALLASublist') then OutList.AddStrings(LVLIToTString(ALLASublist, FULL));
itemLVLI := LVLIToTString(ALLASublist, Full);
templist.AddObject('nestedLVLI', itemLVLI);
end;
templist.AddObject('item', currentItem);
templist.Add('count='+IntToStr(GetElementEditValue(ElementByPath(currentEntry, 'count'))));
templist.Add('level='+IntToStr(GetElementEditValue(ElementByPath(currentEntry, 'level'))));
OutList.AddObject(EditorID(currentItem), templist);
end;
end else begin
for i := 0 to ElementCount(listItems) - 1 do begin
CurrentItem := LinksTo(ElementByPath(ElementByIndex(listItems, i), 'Reference'));
OutList.AddObject(EditorID(currentItem), currentItem);
end;
end;
result := OutList;
end;
//this takes a list made by LVLIToTString or similar and makes an lvli.
function TStringToLVLI(Name: String; List: TStringList; Full: Boolean): IInterface;
var
LVLI, item: IInterface;
count, level: integer;
begin
LVLI := CreateLVLI(name);
itemcount := list.count;
beginUpdate(LVLI);
try
while itemcount > 200 do begin
for j := 0 to itemcount / 200 do begin
tempList := TStringList.Create;
for i := itemcount - 1 - j * 200 downto 0 do begin
templist.AddObject(list[i], list.objects[i]);
list.remove[i];
end;
tempLVLI := TStringToLVLI(name + 'ALLASublist' + IntToLetter(j+1), templist, Full);
end;
itemcount := list.count;
AddItemToLVLI(1, tempLVLI, 1, LVLI);
end else begin
if Full then begin
for i := 0 to List.Count - 1 do begin
tempList := List.objects[i];
count := tempList.values[templist.IndexOf('count')];
level := templist.values[templist.IndexOf('level')];
item := ObjectToElement(templist.objects[templist.IndexOf('item')]);
AddItemToLVLI(level, item, count, LVLI);
end;
end else begin
for i := 0 to List.Count - 1 do begin
item := ObjectToElement(List.objects[i]);
AddItemToLVLI(1, item, 1, LVLI);
end;
end;
end;
finally
endUpdate(LVLI);
end;
result := LVLI;
end;
//this is elms function to generate a character based on a number between 1 and 26 (hopefully not 0 and 25…)
function IntToLetter(int: integer): string;
begin
result := Chr(Ord('a') + Pred(int));
end;
//this actually deletes all items and readds them. it is easier than finding items to remove and adding items that didnt previously exist.
//this also requires a FULL LVLI String List instead of a shrunken.
procedure EditLVLIAsTStringList(LVLI: IInterface; List: TStringList);
var
i: integer;
listItems, CurrentEntry, CurrentItem: IInterface;
templist: TStringList;
count, level: Integer;
item: IInterface;
begin
itemcount := list.count;
//freeze the list
beginUpdate(LVLI);
try
listItems := ElementByPath(lvli, 'Leveled List Entries');
//this removes all items
for i := 0 to ElementCount(listItems) - 1 do begin
currentEntry := ElementByIndex(listItems, i);
CurrentItem := ElementByPath(currentEntry, 'Reference');
linkeditem := LinksTo(CurrentItem);
if Signature(linkeditem) = 'LVLI' then begin
if ContainsText(linkeditem, 'ALLASublist') then begin
sublists.addObject(EditorID(linkeditem), linkeditem);
end;
end;
RemoveItemFromLvli(CurrentItem, LVLI);
end;
//this creates sublists for the lists to prevent issues with large lists (this will probably be put into an ini later and pulled from that so it is configurable if you have overloaded lvli issues)
while itemcount > 200 do begin
for j := 0 to itemcount / 200 do begin
tempList := TStringList.Create;
for i := itemcount - 1 - j * 200 downto 0 do begin
templist.AddObject(list[i], ObjectToElement(list.objects[i]));
list.remove[i];
end;
if sublists.count > 0 then begin
tempLVLI := objectToElement(sublist.objects[0]);
EditLVLIAsTStringList(tempLVLI, templist);
SetEditorID(tempLVLI, name + 'ALLASublist' + IntToLetter(j+1))
end else
tempLVLI := TStringToLVLI(name + 'ALLASublist' + IntToLetter(j+1), templist, Full);
end;
sublist.delete[0];
itemcount := list.count;
AddItemToLVLI(1, tempLVLI, 1, LVLI);
end;
//this will add the items back.
for i := 0 to List.Count - 1 do begin
tempList := List.objects[i];
AddItemToLVLI(templist.values('level'), ObjectToElement(templist.objects[templist.IndexOf('item')]), tempList.values('count'), LVLI);
end;
finally
endUpdate(LVLI);
end;
end;
//quickly generate enchanted version of an armor using a child (is there a better word for it?) version of an enchant.
//relies on ValidENCH and by extension HasKeyword. this is the single item, it doesnt add all variations.
//returns the created item, or the original item (if not valid)
//you need to set Patch as a global IInterface before calling this. patch generally should be the Patch plugin, not the source plugin, but you can technically use the source if you dont care about corruption
//this also doesnt validate that there isnt a copy of this already. use CreateArmoENCHs for full functionality.
//CheckTiers is a way to make sure that the item material value is of the proper tier to accept the enchantments. each tier is 3 enchanments (rank 1, 2, 3 for leather for instance, daedric would be 4, 5, 6. if this is used, then the enchantment must have 6 tiers.
function CreateArmoENCH(item, enchant: IInterface; CheckTiers: Boolean): IInterface;
var
WornRestrict, ResItem, TNAM: IInterface;
FULLName: String;
begin
WornRestrict := ElementByPath(item, 'ENIT\Worn Restrictions');
if not ValidENCH(item, WornRestrict, CheckTiers) OR not WornRestrict = 'NULL' then begin
result := item;
addMessage('Item is not compatible with Enchantment.');
exit;
end;
ResItem := wbCopyElementToFile(item, Patch, true, true);
beginUpdate(ResItem);
try
TNAM := ElementByPath(ResItem, 'TNAM')
ElementAssign(TNAM, HighInteger, nil, false);
SetElementEditValues(TNAM, enchant);
FULLName := GetElementEditValues(enchant, 'FULL');
SetElementEditValues(ResItem, 'FULL', GetElementEditValues(item, 'FULL') + ' of ' + FULLName);
finally
endUpdate(ResItem);
end;
result := ResItem;
end;
//quickly generates enchanted versions of an armor using the base version of an enchant.
//relies on CreateArmoENCH, ValidENCH, HasKeyword.
//this is 6 items (usually) it does not create new enchanments to meet the regular quota of 6, but rather uses the existing ones which have the base as their base.
//returns a stringlist of the created items.
//you need to set Patch as a global IInterface before calling this. patch generally should be the Patch plugin, not the source plugin, but you can technically use the source if you dont care about corruption
function CreateArmoENCHs(item, enchant: IInterface): TStringList;
begin
for i := 0 to ReferencedByCount(enchant) - 1 do begin
CurrentReference := ReferencedByIndex(enchant, i);
if signature(CurrentReference) = 'ENCH' then begin
if equals(LinksTo(ElementByPath(CurrentReference, 'ENIT\Base Enchantment')), enchant) then begin
ENCHList.AddObject(EditorID(CurrentReference), CurrentReference);
end;
end;
end;
if
end;
procedure GenerateAndAddEnchVersion(item, ench: IInterface);
begin
end;
//very simple check for keyword.
function HasKeyword(item: IInterface; aString: string): boolean;
var
tempRecord: IInterface;
i: integer;
begin
result := false;
tempRecord := ElementByPath(item, 'KWDA');
for i := 0 to ElementCount(tempRecord) - 1 do
begin
if EditorID(LinksTo(elementbyindex(tempRecord, i))) = aString then
begin
result := true;
exit;
end;
end;
end;
//this replaces 1 item in a list with another. this will mostly be used to swap an item with an lvli.
procedure ReplaceItemWithItemLVLI(Original, New, LVLI: IInterface);
begin
ListItems := ElementByPath(LVLI, 'Leveled List Entries');
beginUpdate(ListItems);
try
for ElementIterator := 0 to ElementCount(ListItems) - 1 do begin
CurrentItem := ElementByIndex(ListItems, ElementIterator);
CurrentSpecific := ElementByPath(CurrentItem, 'LVLO\Reference');
if equals(LinksTo(CurrentSpecific), Original) then begin
SetEditValue(CurrentSpecific, New);
end;
end;
finally
endUpdate(ListItems);
end;
end;
//checks if the "worn restrictions" keyword list is in the item that an enchantment is being added to. for use with CreateArmoENCH and CreateWeapENCH. requires HasKeyword
//CheckTiers is a way to make sure that the item material value is of the proper tier to accept the enchantments.
//each tier is 3 enchanments (rank 1, 2, 3 for leather for instance, daedric would be 4, 5, 6.
//tiers use the ini file to determine which ranks they use.
function ValidENCH(item, FLST: IInterface; CheckTiers: Boolean): boolean;
var
list, CurrentKeyword: IInterface;
ElementIterator: integer;
probresult: boolean;
begin
result := false;
list := ElementByPath(FLST, 'FormIDs');
for ElementIterator := 0 to elementCount(list) - 1 do begin
CurrentKeyword := EditorID(LinksTo(ElementByIndex(list, ElementIterator)));
if hasKeyword(item, CurrentKeyword) then begin
probresult := true; //dont want to set result too early and find the next section kills it. so setting "probable result" here.
exit;
end;
end;
if CheckTiers then begin
Ini := TMemIniFile.Create('YGGAutomation.ini');
end;
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment