From 718cf89351c3380b8fc04995177a0d38c11e7ed0 Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Mon, 10 Oct 2022 20:54:53 +0200 Subject: [PATCH] Support Near reference, proper same cell detection --- ESMify_Plugins.pas | 158 ++++++++++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 44 deletions(-) diff --git a/ESMify_Plugins.pas b/ESMify_Plugins.pas index 2d9e525..22d4ffd 100644 --- a/ESMify_Plugins.pas +++ b/ESMify_Plugins.pas @@ -16,7 +16,7 @@ unit ESMify_Plugins; var refsChecked, recordsCounted, flaggedCount, persLocSkipped: integer; - pluginAnnounced: boolean; + pluginShown: boolean; function IsReferencedByNonLocation(rec: IwbMainRecord): boolean; var @@ -50,9 +50,7 @@ begin // Does not cover long chains of linked refs. if (ReferencedByCount(referencingRecord) = 0) then begin if (sig = 'REFR') then begin - if Equals(LinksTo(ElementByPath(rec, 'Cell')), LinksTo(ElementByPath(referencingRecord, 'Cell'))) then begin - continue; - end; + if InSameCell(rec, referencingRecord) then continue; end; end; @@ -66,6 +64,61 @@ begin end; end; +function InSameCell(ref: IwbMainRecord; otherRef: IwbMainRecord): boolean; +var + cell, otherCell, worldspace, otherWorldspace: IwbMainRecord; + coords, otherCoords: TwbGridCell; +begin + cell := LinksTo(ElementByPath(ref, 'Cell')); + otherCell := LinksTo(ElementByPath(otherRef, 'Cell')); + + if not Assigned(otherCell) then exit; + + // Interior cell + if (GetElementNativeValues(cell, 'DATA') and 1) > 0 then begin + result := Equals(cell, otherCell); + exit; + end; + + worldspace := LinksTo(ElementByPath(cell, 'Worldspace')); + otherWorldspace := LinksTo(ElementByPath(otherCell, 'Worldspace')); + + if not Equals(worldspace, otherWorldspace) then begin + result := false; + exit; + end; + + // Consider small world a cell + if (GetElementNativeValues(worldspace, 'DATA') and 1) > 0 then begin + result := true; + exit; + end; + + // Persistent refs are located in 0,0 so we detect their original grid position via their position + coords := wbPositionToGridCell(GetPosition(ref)); + otherCoords := wbPositionToGridCell(GetPosition(otherRef)); + + result := (coords.x = otherCoords.x) and (coords.y = otherCoords.y); +end; + +function HasScripts(e: IwbMainRecord; targetScripts: string): boolean; +var + scripts, currentItem: IwbElement; + linkedCount, i: integer; +begin + if not ElementExists(e, 'VMAD') then exit; + + scripts := ElementByPath(e, 'VMAD\Scripts'); + for i := 0 to Pred(ElementCount(scripts)) do begin + currentItem := ElementByIndex(scripts, i); + + if Pos(GetElementEditValues(currentItem, 'ScriptName'), targetScripts) <> 0 then begin + result := true; + break; + end; + end; +end; + function MarkPersistent(e: IwbMainRecord): boolean; begin AddMessage(' + Marking as persistent: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); @@ -113,6 +166,33 @@ begin end; end; +function IsLinkedRefRemote(e: IwbMainRecord; linkedRefKeyword: IwbMainRecord): boolean; +var + linkedRef: IwbMainRecord; +begin + result := true; + if Assigned(linkedRefKeyword) then begin + linkedRef := GetLinkedRef(e, linkedRefKeyword); + end + else begin + linkedRef := GetLinkedRefNull(e); + end; + + if Assigned(linkedRef) then begin + if InSameCell(e, linkedRef) then begin + //AddMessage(' Skipping actor, staying in one cell with his linked ref: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); + result := false; + end + else if not GetIsPersistent(linkedRef) then begin + MarkPersistent(linkedRef); + end; + end + else begin + // Missing linked ref. Usually, package checks for its presence. Sometimes, it's just missing. The outcome is the same, this package is not doing anything. + result := false; + end; +end; + function Process(e: IInterface): integer; var currentPlugin: IwbFile; @@ -122,10 +202,10 @@ var sig, baseSig, packageLoc, packageType: string; isREFR, isACHR, skip: boolean; begin - if not pluginAnnounced then begin + if not pluginShown then begin currentPlugin := GetFile(e); - AddMessage(#13#10 + '* Processing ' + Name(currentPlugin)); - pluginAnnounced := true; + AddMessage(#13#10 + '# Processing ' + Name(currentPlugin)); + pluginShown := true; refsChecked := 0; recordsCounted := 0; flaggedCount := 0; @@ -139,7 +219,7 @@ begin AddMessage(' ' + IntToStr(refsChecked) + ' references checked.'); AddMessage(' ' + IntToStr(persLocSkipped) + ' actors with Persistent Location skipped.'); AddMessage(' ' + IntToStr(flaggedCount) + ' references have been marked as persistent.' + #13#10); - pluginAnnounced := false; + pluginShown := false; exit; end; @@ -156,7 +236,7 @@ begin if GetIsDeleted(e) then begin if GetIsPersistent(e) then begin - AddMessage(' Removing persistence flag from deleted record: ' + ShortName(e)); + AddMessage(' ! Removing persistence flag from deleted record: ' + ShortName(e)); SetIsPersistent(e, False); end; exit; @@ -224,62 +304,52 @@ begin if packageCount = 0 then exit; - skip := false; + skip := true; for i := 0 to Pred(packageCount) do begin package := LinksTo(ElementByIndex(packages, i)); if not Assigned(package) then begin - skip := true; AddMessage(' ! WARNING: Invalid package entry: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); continue; end; packageLoc := GetElementEditValues(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PLDT\Type'); - if (packageLoc = 'Near editor location') or (packageLoc = 'Near self') then begin - AddMessage(' Skipping editor location actor: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); - skip := true; - continue; - end; + if packageLoc <> '' then begin - if (packageLoc = 'In cell') then begin - refCell := LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PLDT\Cell')); - if Assigned(refCell) then begin - if Equals(refCell, LinksTo(ElementByPath(e, 'Cell'))) then begin - AddMessage(' Skipping actor, staying in one cell: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); - skip := true; - continue; - end; - end; - end; - - if (GetElementEditValues(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PTDA\Target Data\Type') = 'Linked Reference') then begin - - linkedRefKeyword := LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PTDA\Target Data\Reference')); - - if Assigned(linkedRefKeyword) then begin - linkedRef := GetLinkedRef(e, linkedRefKeyword); + if (packageLoc = 'Near editor location') or (packageLoc = 'Near self') then begin + //AddMessage(' Skipping editor location actor: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); + continue; end - else begin - linkedRef := GetLinkedRefNull(e); - end; - - if Assigned(linkedRef) then begin - if Equals(LinksTo(ElementByPath(linkedRef, 'Cell')), LinksTo(ElementByPath(e, 'Cell'))) then begin - AddMessage(' Skipping actor, staying in one cell with his linked ref: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); - skip := true; - continue; + else if (packageLoc = 'In cell') then begin + refCell := LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PLDT\Cell')); + if Assigned(refCell) then begin + if Equals(refCell, LinksTo(ElementByPath(e, 'Cell'))) then continue; end; + end + else if (packageLoc = 'Near linked reference') then begin + linkedRefKeyword := LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PLDT\Keyword')); + if not IsLinkedRefRemote(e, linkedRefKeyword) then continue; + end + else if (packageLoc = 'Near reference') then begin + if InSameCell(e, LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PLDT\Reference'))) then continue; end; - + + end + else if (GetElementEditValues(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PTDA\Target Data\Type') = 'Linked Reference') then begin + linkedRefKeyword := LinksTo(ElementByPath(ElementByIndex(ElementByPath(package, 'Package Data\Data Input Values'), 0), 'PTDA\Target Data\Reference')); + if not IsLinkedRefRemote(e, linkedRefKeyword) then continue; end; skip := false; end; - if skip then exit; + if skip then begin + //AddMessage(' Skipping actor, staying in one cell: ' + GetElementEditValues(e, 'NAME') + ' - (' + Name(e) + ')'); + exit; + end; MarkPersistent(e);