Last active
February 27, 2023 22:59
-
-
Save dalen/16a1ef58c8c02e7a0eca3899c84d238b to your computer and use it in GitHub Desktop.
Requiem - WACCF Patch generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
Patch generator for Requiem and | |
Weapons, Armor, Clothing and Clutter Fixes | |
} | |
unit RequiemWACCFPatcher; | |
const | |
requiemFile = 'Requiem.esp'; | |
waccfFile = 'Weapons Armor Clothing & Clutter Fixes.esp'; | |
var | |
patch, requiem, waccf: IwbFile; | |
// Check if a record is already reqtified | |
function IsAlreadyReqtified(r: IInterface): boolean; | |
var | |
i: integer; | |
keywords: IwbElement; | |
begin | |
Result := false; | |
keywords := ElementByPath(r, 'KWDA'); | |
if keywords <> nil then begin | |
for i := 0 to Pred(ElementCount(keywords)) do begin | |
if StartsStr('REQ_KW_AlreadyReqtified', GetEditValue(ElementByIndex(keywords, i))) then Result := true; | |
end; | |
end; | |
end; | |
// If element at path has not changed in r1 compared to master, but has in r2, then copy it from r2 to patch | |
// Used to copy requiem changes when WACCF just has the same as Skyrim | |
procedure CopyIfChanged(path: string; r1: IInterface; r2: IInterface; patch: IwbMainRecord); | |
var | |
masterRecord: IwbMainRecord; | |
masterElement: IwbElement; | |
r1Element: IwbElement; | |
r2Element: IwbElement; | |
begin | |
masterRecord := Master(ContainingMainRecord(r1)); | |
if not Assigned(masterRecord) then exit; | |
masterElement := ElementByPath(masterRecord, path); | |
if not Assigned(masterElement) then exit; | |
r1Element := ElementByPath(ContainingMainRecord(r1), path); | |
if not Assigned(r1Element) then exit; | |
r2Element := ElementByPath(ContainingMainRecord(r2), path); | |
if not Assigned(r2Element) then exit; | |
if (GetEditValue(masterElement) = GetEditValue(r1Element)) and (GetEditValue(masterElement) <> GetEditValue(r2Element)) then begin | |
wbCopyElementToRecord(r2Element, patch, false, true); | |
end; | |
end; | |
// Merge keyword list, but make sure to only use WeapMaterial from WACCF if they differ from Requiem | |
// Same handling of ArmorType keyword as with material | |
// VendorItem keywords are only copied from WACCF | |
procedure MergeKeyWords(e1: IwbElement; e2: IwbElement; eP: IInterface); | |
var | |
i: integer; | |
keywords: TStringList; | |
k: IInterface; | |
dest: IwbElement; | |
material: boolean; | |
armorType: boolean; | |
begin | |
material := false; | |
armorType := false; | |
keywords := TStringList.Create; | |
keywords.Sorted := true; | |
keywords.Duplicates := dupIgnore; | |
for i := 0 to Pred(ElementCount(e1)) do begin | |
if StartsStr('WeapMaterial', GetEditValue(ElementByIndex(e1, i))) then material := true; | |
if StartsStr('WAF_WeapMaterial', GetEditValue(ElementByIndex(e1, i))) then material := true; | |
if StartsStr('ArmorMaterial', GetEditValue(ElementByIndex(e1, i))) then material := true; | |
if StartsStr('ArmorHeavy', GetEditValue(ElementByIndex(e1, i))) then armorType := true; | |
if StartsStr('ArmorLight', GetEditValue(ElementByIndex(e1, i))) then armorType := true; | |
if StartsStr('ArmorClothing', GetEditValue(ElementByIndex(e1, i))) then armorType := true; | |
keywords.Add(GetEditValue(ElementByIndex(e1, i))); | |
end; | |
for i := 0 to Pred(ElementCount(e2)) do begin | |
if StartsStr('VendorItem', GetEditValue(ElementByIndex(e2, i))) then continue; | |
if StartsStr('WeapMaterial', GetEditValue(ElementByIndex(e2, i))) and material then continue; | |
if StartsStr('WAF_WeapMaterial', GetEditValue(ElementByIndex(e2, i))) and material then continue; | |
if StartsStr('ArmorMaterial', GetEditValue(ElementByIndex(e2, i))) and material then continue; | |
if StartsStr('ArmorHeavy', GetEditValue(ElementByIndex(e2, i))) and armorType then continue; | |
if StartsStr('ArmorLight', GetEditValue(ElementByIndex(e2, i))) and armorType then continue; | |
if StartsStr('ArmorClothing', GetEditValue(ElementByIndex(e2, i))) and armorType then continue; | |
keywords.Add(GetEditValue(ElementByIndex(e2, i))); | |
end; | |
// Clear destination | |
RemoveElement(eP, 'KWDA'); | |
dest := Add(eP, 'KWDA', true); | |
for i := 0 to Pred(keywords.Count) do begin | |
k := ElementAssign(dest, HighInteger, nil, false); | |
SetEditValue(k, keywords[i]); | |
end; | |
end; | |
procedure HandleRecord(e: IInterface; eR: IInterface; eP: IInterface); | |
var | |
i: integer; | |
element: IwbElement; | |
begin | |
// Iterate over elements in record and copy the Requiem ones as needed | |
for i := 0 to Pred(ElementCount(eR)) do begin | |
element := ElementByIndex(eR, i); | |
// Always use Requiem editor ID | |
if ShortName(element) = 'EDID - Editor ID' then wbCopyElementToRecord(element, eP, false, true); | |
if Signature(e) = 'ALCH' then begin | |
if ShortName(element) = 'FULL - Name' then begin | |
// Use Requiem names for potions | |
if StartsStr('Potion', GetEditValue(element)) then wbCopyElementToRecord(element, eP, false, true); | |
end; | |
if ShortName(element) = 'Effects' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'ENIT - Effect Data' then wbCopyElementToRecord(element, eP, false, true); | |
end; | |
if Signature(e) = 'AMMO' then begin | |
if ShortName(element) = 'DESC - Description' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'KWDA - Keywords' then MergeKeywords(ElementBySignature(e, 'KWDA'), element, eP); | |
if ShortName(element) = 'DATA - Data' then begin | |
if IsAlreadyReqtified(eR) then begin | |
wbCopyElementToRecord(element, eP, false, true); | |
end else begin | |
CopyIfChanged('DATA - Data\Value', e, eR, eP); | |
CopyIfChanged('DATA - Data\Weight', e, eR, eP); | |
CopyIfChanged('DATA - Data\Damage', e, eR, eP); | |
end; | |
end; | |
end; | |
if Signature(e) = 'ARMO' then begin | |
if ShortName(element) = 'Record Header' then begin | |
CopyIfChanged('Record Header\Record Flags', e, eR, eP); | |
end; | |
if ShortName(element) = 'KWDA - Keywords' then MergeKeywords(ElementBySignature(e, 'KWDA'), element, eP); | |
if ShortName(element) = 'EITM - Object Effect' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'VMAD - Virtual Machine Adapter' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'DESC - Description' then wbCopyElementToRecord(element, eP, false, true); | |
// We use Requiem armor ratong when unchanged by WACCF but changed by requiem, Hide Armor is an example | |
if ShortName(element) = 'DNAM - Armor Rating' then CopyIfChanged('DNAM - Armor Rating', e, eR, eP); | |
if ShortName(element) = 'DATA - Data' then begin | |
CopyIfChanged('DATA - Data\Value', e, eR, eP); | |
end; | |
end; | |
if Signature(e) = 'ARMA' then begin | |
if ShortName(element) = 'Male world model' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'Female world model' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'Male 1st Person' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'Female 1st Person' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'Additional Races' then wbCopyElementToRecord(element, eP, false, true); | |
end; | |
if Signature(e) = 'COBJ' then begin | |
if ShortName(element) = 'Conditions' then wbCopyElementToRecord(element, eP, false, true); | |
end; | |
if Signature(e) = 'INGR' then begin | |
if ShortName(element) = 'ENIT - Effect Data' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'Effects' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'KWDA - Keywords' then MergeKeywords(ElementBySignature(e, 'KWDA'), element, eP); | |
end; | |
if Signature(e) = 'LIGH' then begin | |
if ShortName(element) = 'DATA - DATA' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'FNAM - Fade value' then wbCopyElementToRecord(element, eP, false, true); | |
end; | |
if Signature(e) = 'MISC' then begin | |
if ShortName(element) = 'DATA - Data' then begin | |
// Use Requiem changes like gold weight and Aretinos heirloom value | |
CopyIfChanged('DATA - Data\Value', e, eR, eP); | |
end; | |
if ShortName(element) = 'KWDA - Keywords' then MergeKeywords(ElementBySignature(e, 'KWDA'), element, eP); | |
end; | |
if Signature(e) = 'WEAP' then begin | |
if ShortName(element) = 'Record Header' then begin | |
CopyIfChanged('Record Header\Record Flags', e, eR, eP); | |
end; | |
if ShortName(element) = 'KWDA - Keywords' then MergeKeywords(ElementBySignature(e, 'KWDA'), element, eP); | |
if ShortName(element) = 'VMAD - Virtual Machine Adapter' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'DESC - Description' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'EAMT - Enchantment Amount' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'EITM - Object Effect' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'CNAM - Template' then begin | |
CopyIfChanged('CNAM - Template', e, eR, eP); | |
end; | |
// Copy all data on retified items, on non reqtified only copy part of the data | |
if IsAlreadyReqtified(eR) then begin | |
if ShortName(element) = 'DATA - Game Data' then wbCopyElementToRecord(element, eP, false, true); | |
if ShortName(element) = 'DNAM - Data' then wbCopyElementToRecord(element, eP, false, true); | |
end else begin | |
if ShortName(element) = 'DATA - Game Data' then begin | |
// If Daedric weapons use Requiem values | |
if StartsStr('Daedric', Name(e)) then begin | |
wbCopyElementToRecord(element, eP, false, true); | |
end else begin | |
CopyIfChanged('DATA - Game Data\Value', e, eR, eP); | |
CopyIfChanged('DATA - Game Data\Weight', e, eR, eP); | |
CopyIfChanged('DATA - Game Data\Damage', e, eR, eP); | |
end; | |
end; | |
// Check if Requiem element has Flags2, then copy them | |
// Mostly the "NPC's use ammo flag on bows" | |
if ShortName(element) = 'DNAM - Data' then begin | |
if ElementExists(element, 'Flags2') then wbCopyElementToRecord(ElementByPath(element, 'Flags2'), eP, false, true); | |
end; | |
end; | |
end; | |
end; | |
end; | |
function Initialize: integer; | |
var | |
i: integer; | |
e: IInterface; // WACCF element | |
eR: IInterface; // Requiem element | |
eP: IInterface; // Patch element | |
o: IInterface; // Override | |
begin | |
// create a new patch plugin if needed | |
if not Assigned(patch) then | |
patch := AddNewFile; | |
if not Assigned(patch) then | |
exit; | |
AddMasterIfMissing(patch, 'Skyrim.esm'); | |
AddMasterIfMissing(patch, 'Update.esm'); | |
AddMasterIfMissing(patch, 'Dawnguard.esm'); | |
AddMasterIfMissing(patch, 'Dragonborn.esm'); | |
AddMasterIfMissing(patch, 'HearthFires.esm'); | |
AddMasterIfMissing(patch, waccfFile); | |
AddMasterIfMissing(patch, requiemFile); | |
waccf := MasterByIndex(patch, 5); | |
requiem := MasterByIndex(patch, 6); | |
// iterate over all records in waccf | |
for i := 0 to Pred(RecordCount(waccf)) do begin | |
e := RecordByIndex(waccf, i); | |
if IsWinningOverride(e) then continue; | |
// Skip certain types of records entirely | |
if Signature(e) = 'LSCR' then continue; | |
if Signature(e) = 'LVLI' then continue; | |
if Signature(e) = 'NPC_' then continue; | |
if Signature(e) = 'OTFT' then continue; | |
if Signature(e) = 'PERK' then continue; | |
if Signature(e) = 'PROJ' then continue; | |
if Signature(e) = 'TXST' then continue; | |
if Signature(e) = 'CELL' then continue; | |
if Signature(e) = 'CONT' then continue; | |
if Signature(e) = 'ENCH' then continue; | |
eR := RecordByFormID(requiem, FixedFormID(e), false); | |
if not Assigned(eR) then continue; | |
if GetFileName(eR) <> requiemFile then continue; | |
//AddMessage('Processing: ' + FullPath(eR)); | |
AddRequiredElementMasters(e, patch, true); | |
AddRequiredElementMasters(eR, patch, true); | |
eP := wbCopyElementToFile(e, patch, false, true); | |
HandleRecord(e, eR, eP); | |
end; | |
end; | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment