Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New script to generate large references info in SSE (by Sheson)
- Loading branch information
1 parent
2617324
commit 125cae1
Showing
4 changed files
with
388 additions
and
11 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,377 @@ | ||
{ | ||
Add large reference RNAM data to a worldspace | ||
1. Select ESM plugin that adds new references to a worldspace | ||
* only new ESMs are listed - see sDefaultPlugins below | ||
2. Select worldspace for which to add large references | ||
* only worldspaces for selected ESM without existing RNAM data are listed | ||
* remove pre-existing RNAM data manually to update | ||
* any worldspace supports large references regardless of static object LOD | ||
3. Click OK to add RNAM data for selected worldspace | ||
* CK default 'size' is 512 - see fLargeRefMinSize below | ||
* CK adds large references for STAT and MSTT - see sLargeRefBaseObjects below | ||
* afterwards generate static LOD for worldspaces with LOD | ||
} | ||
unit UserScript; | ||
|
||
const | ||
sDefaultPlugins = 'skyrim.esm,update.esm,dawnguard.esm,hearthfires.esm,dragonborn.esm'; | ||
sLargeRefBaseObjects = 'STAT MSTT'; | ||
fLargeRefMinSize = 512; | ||
|
||
var | ||
cbPlugin, cbWorld: TComboBox; | ||
slLargeReferences: TwbFastStringList; | ||
|
||
//============================================================================ | ||
// name of LOD settings file for worldspace | ||
function LODSettingsFileName(wrld: IInterface): string; | ||
begin | ||
Result := 'lodsettings\' + EditorID(wrld) + '.lod'; | ||
end; | ||
|
||
//============================================================================ | ||
// does worldspace have a lod? | ||
function HasLOD(wrld: IInterface): boolean; | ||
begin | ||
// a presence of lod settings file | ||
Result := ResourceExists(LODSettingsFileName(wrld)); | ||
end; | ||
|
||
//============================================================================ | ||
procedure ProcessReference(e: IInterface); | ||
var | ||
stat: IInterface; | ||
Dimensions: TwbVector; | ||
fScale: float; | ||
begin | ||
stat := BaseRecord(e); | ||
|
||
// skip markers | ||
if GetElementNativeValues(stat, 'Record Header\Record Flags') and $800000 = $800000 then | ||
Exit; | ||
|
||
// skip no model | ||
if not ElementExists(stat, 'Model') then | ||
Exit; | ||
|
||
// skip no OBND | ||
if not ElementExists(stat, 'OBND') then | ||
Exit; | ||
|
||
if ElementExists(e, 'XSCL') then | ||
fScale := GetElementEditValues(e, 'XSCL') | ||
else | ||
fScale := 1.0; | ||
|
||
// get base object size | ||
Dimensions.x := (GetElementNativeValues(stat, 'OBND\X2') - GetElementNativeValues(stat, 'OBND\X1')) * fScale; | ||
Dimensions.y := (GetElementNativeValues(stat, 'OBND\Y2') - GetElementNativeValues(stat, 'OBND\Y1')) * fScale; | ||
Dimensions.z := (GetElementNativeValues(stat, 'OBND\Z2') - GetElementNativeValues(stat, 'OBND\Z1')) * fScale; | ||
|
||
// skipping wierd stuff or data | ||
if (Dimensions.z = 0) or (Dimensions.y = 0) or (Dimensions.x = 0) then | ||
Exit; | ||
|
||
// rules based on emperical evidence - could be incomplete | ||
// technically anything can be added, but we leave that fancy stuff for DynDOLOD | ||
if ((Dimensions.x >= fLargeRefMinSize) and (Dimensions.y >= fLargeRefMinSize) and (Dimensions.z >= fLargeRefMinSize)) or | ||
(Dimensions.x >= fLargeRefMinSize * 2) or (Dimensions.y >= fLargeRefMinSize * 2) or (Dimensions.z >= fLargeRefMinSize * 2) | ||
then | ||
slLargeReferences.AddObject(IntToHex(GetLoadOrderFormID(e), 8), e); | ||
|
||
end; | ||
|
||
//============================================================================ | ||
procedure IterateWorldspace(e: IInterface); | ||
var | ||
lst: TList; | ||
i: integer; | ||
begin | ||
lst := TList.Create; | ||
AddMessage('Gathering large references added by ' + GetFileName(GetFile(e))); | ||
// find all new references added by this plugin | ||
// no need to include modified references from master plugins again, since all RNAM data is merged anyways | ||
wbFindREFRsByBase(e, sLargeRefBaseObjects, 1, lst); | ||
for i := 0 to lst.Count - 1 do | ||
ProcessReference(ObjectToElement(lst[i])); | ||
lst.Free; | ||
end; | ||
|
||
//============================================================================ | ||
// add reference to existing RNAM | ||
procedure AddRNAMItem(rnam, e: IInterface); | ||
var | ||
Cell: TwbGridCell; | ||
begin | ||
Cell := wbPositionToGridCell(GetPosition(e)); | ||
rnam := ElementAssign(ElementByPath(rnam, 'References'), HighInteger, nil, False); | ||
SetElementNativeValues(rnam, 'X', Cell.x); | ||
SetElementNativeValues(rnam, 'Y', Cell.y); | ||
SetElementNativeValues(rnam, 'Ref', GetLoadOrderFormID(e)); | ||
end; | ||
|
||
//============================================================================ | ||
// add new RNAM | ||
function AddRNAM(wrld, e: IInterface): IInterface; | ||
var | ||
Cell: TwbGridCell; | ||
begin | ||
Result := nil; | ||
Cell := wbPositionToGridCell(GetPosition(e)); | ||
|
||
if not ElementExists(wrld, 'RNAM') then begin | ||
Result := ElementByPath(Add(wrld, 'RNAM', True), 'RNAM'); | ||
end | ||
else begin | ||
Result := ElementAssign(ElementByPath(wrld, 'RNAM'), HighInteger, nil, False); | ||
end | ||
|
||
SetElementNativeValues(Result, 'X', Cell.x); | ||
SetElementNativeValues(Result, 'Y', Cell.y); | ||
|
||
AddRNAMItem(Result, e); | ||
end; | ||
|
||
//============================================================================ | ||
procedure UpdateWorldspace(wrld: IInterface); | ||
var | ||
i, j: integer; | ||
e, rnam: IInterface; | ||
CurrentCell, Cell: TwbGridCell; | ||
begin | ||
|
||
if slLargeReferences.Count = 0 then | ||
Exit; | ||
|
||
AddMessage('Adding ' + IntToStr(slLargeReferences.Count) + ' large references to ' + EditorID(wrld)); | ||
|
||
i := Pred(slLargeReferences.Count); | ||
while i > 0 do begin | ||
e := ObjectToElement(slLargeReferences.Objects[i]); | ||
CurrentCell := wbPositionToGridCell(GetPosition(e)); | ||
|
||
// add new RNAM | ||
rnam := AddRNAM(wrld, e); | ||
// remove reference from list | ||
slLargeReferences.Delete(i); | ||
|
||
// add all other references for same cell | ||
for j := Pred(i) downto 0 do begin | ||
e := ObjectToElement(slLargeReferences.Objects[j]); | ||
Cell := wbPositionToGridCell(GetPosition(e)); | ||
if (CurrentCell.x = Cell.x) and (CurrentCell.y = Cell.y) then begin | ||
// add reference to existing RNAM | ||
AddRNAMItem(rnam, e); | ||
// remove reference from list | ||
slLargeReferences.Delete(j); | ||
end; | ||
end; | ||
|
||
i := Pred(slLargeReferences.Count); | ||
end; | ||
end; | ||
|
||
//============================================================================ | ||
procedure GenerateWorldspace(wrld: IInterface); | ||
begin | ||
IterateWorldspace(wrld); | ||
UpdateWorldspace(wrld); | ||
end; | ||
|
||
//============================================================================ | ||
// skip vanilla plugins | ||
function IsDefaultPlugin(s: string): Boolean; | ||
var | ||
i: integer; | ||
sl: TStringList; | ||
begin | ||
Result := False; | ||
sl := TStringList.Create; | ||
sl.StrictDelimiter := True; | ||
sl.CommaText := sDefaultPlugins; | ||
try | ||
for i := 0 to Pred(sl.Count) do | ||
if Lowercase(s) = sl[i] then begin | ||
Result := True; | ||
exit; | ||
end; | ||
finally | ||
sl.free; | ||
end; | ||
end; | ||
|
||
//============================================================================ | ||
// fill plugin drop-down | ||
procedure FillPlugins(cmbWorld: TComboBox); | ||
var | ||
i: integer; | ||
f: IInterface; | ||
sl: TStringList; | ||
begin | ||
// filling list of worldspaces | ||
sl := TStringList.Create; | ||
try | ||
sl.Duplicates := dupIgnore; | ||
sl.Sorted := True; | ||
sl.AddObject(' -- Select a plugin --', nil); | ||
for i := Pred(FileCount) downto 0 do begin | ||
f := FileByIndex(i); | ||
|
||
// skip vanilla plugins | ||
if IsDefaultPlugin(GetFileName(f)) then | ||
Continue; | ||
|
||
// only ESM for now | ||
if not GetIsESM(f) then | ||
Continue; | ||
|
||
sl.AddObject(GetFileName(f), f); | ||
end; | ||
cmbWorld.Items.Assign(sl); | ||
finally | ||
sl.Free; | ||
end; | ||
end; | ||
|
||
//============================================================================ | ||
// fill with worldspaces | ||
procedure FillWorldspaces(cmbWorld: TComboBox; f: IInterface); | ||
var | ||
i, j: integer; | ||
wrlds, wrld: IInterface; | ||
sl: TStringList; | ||
s: string; | ||
begin | ||
// filling list of worldspaces | ||
sl := TStringList.Create; | ||
try | ||
sl.Duplicates := dupIgnore; | ||
sl.Sorted := True; | ||
sl.AddObject(' -- Select a worldspace --', nil); | ||
wrlds := GroupBySignature(f, 'WRLD'); | ||
for j := 0 to Pred(ElementCount(wrlds)) do begin | ||
wrld := ElementByIndex(wrlds, j); | ||
if ElementType(wrld) <> etMainRecord then | ||
Continue; | ||
// skip if RNAM data already exists | ||
if ElementExists(wrld, 'RNAM') then | ||
Continue; | ||
sl.AddObject(EditorID(wrld), wrld); | ||
end; | ||
cmbWorld.Items.Assign(sl); | ||
finally | ||
sl.Free; | ||
end; | ||
end; | ||
|
||
//============================================================================ | ||
procedure UpdatecbWorld(Sender: TObject); | ||
begin | ||
FillWorldspaces(cbWorld, ObjectToElement(cbPlugin.Items.Objects[cbPlugin.ItemIndex])); | ||
if cbWorld.Items.Count > 0 then cbWorld.ItemIndex := 0; | ||
end; | ||
|
||
//============================================================================ | ||
function CreateLabel(aParent: TControl; x, y: Integer; aCaption: string): TLabel; | ||
begin | ||
Result := TLabel.Create(aParent); | ||
Result.Parent := aParent; | ||
Result.Left := x; | ||
Result.Top := y; | ||
Result.Caption := aCaption; | ||
end; | ||
|
||
//============================================================================ | ||
function OptionsForm: IInterface; | ||
var | ||
frm: TForm; | ||
btnOk, btnCancel: TButton; | ||
begin | ||
Result := nil; | ||
|
||
frm := TForm.Create(nil); | ||
try | ||
frm.Caption := 'Generate Large References'; | ||
frm.Width := 400; | ||
frm.Height := 200; | ||
frm.Position := poMainFormCenter; | ||
frm.BorderStyle := bsDialog; | ||
frm.KeyPreview := True; | ||
|
||
cbPlugin := TComboBox.Create(frm); cbPlugin.Parent := frm; | ||
cbPlugin.Left := 54; | ||
cbPlugin.Top := 12; | ||
cbPlugin.Width := 260; | ||
cbPlugin.Style := csDropDownList; | ||
cbPlugin.DropDownCount := 20; | ||
cbPlugin.OnSelect := UpdatecbWorld; | ||
CreateLabel(frm, 16, cbPlugin.Top + 4, 'Plugin'); | ||
|
||
FillPlugins(cbPlugin); | ||
if cbPlugin.Items.Count > 0 then cbPlugin.ItemIndex := 0; | ||
if cbPlugin.Items.Count = 2 then cbPlugin.ItemIndex := 1; | ||
|
||
cbWorld := TComboBox.Create(frm); cbWorld.Parent := frm; | ||
cbWorld.Left := 54; | ||
cbWorld.Top := cbPlugin.Top + cbPlugin.Height + 4; | ||
cbWorld.Width := 260; | ||
cbWorld.Style := csDropDownList; | ||
cbWorld.DropDownCount := 20; | ||
CreateLabel(frm, 16, cbWorld.Top + 4, 'World'); | ||
|
||
FillWorldspaces(cbWorld, ObjectToElement(cbPlugin.Items.Objects[cbPlugin.ItemIndex])); | ||
if cbWorld.Items.Count > 0 then cbWorld.ItemIndex := 0; | ||
|
||
btnCancel := TButton.Create(frm); btnCancel.Parent := frm; | ||
btnCancel.Caption := 'Cancel'; | ||
btnCancel.ModalResult := mrCancel; | ||
btnCancel.Left := cbWorld.Left + cbWorld.Width - btnCancel.Width; | ||
btnCancel.Top := cbWorld.Top + cbWorld.Height + 8; | ||
|
||
btnOk := TButton.Create(frm); btnOk.Parent := frm; | ||
btnOk.Caption := 'OK'; | ||
btnOk.ModalResult := mrOk; | ||
btnOk.Left := btnCancel.Left - btnOk.Width - 8; | ||
btnOk.Top := btnCancel.Top; | ||
|
||
frm.Width := cbWorld.Left + cbWorld.Width + 32; | ||
frm.Height := btnOk.Top + btnOk.Height + 32; | ||
|
||
if frm.ShowModal <> mrOk then | ||
Exit; | ||
|
||
Result := ObjectToElement(cbWorld.Items.Objects[cbWorld.ItemIndex]); | ||
|
||
finally | ||
frm.Free; | ||
end; | ||
end; | ||
|
||
|
||
//============================================================================ | ||
function Initialize: integer; | ||
var | ||
wrld: IInterface; | ||
begin | ||
Result := 0; | ||
|
||
slLargeReferences := TwbFastStringList.Create; | ||
|
||
if (wbGameMode <> gmSSE) then begin | ||
AddMessage('Game not supported'); | ||
Exit; | ||
end; | ||
|
||
wrld := OptionsForm; | ||
if Assigned(wrld) then | ||
GenerateWorldspace(wrld); | ||
|
||
slLargeReferences.Free; | ||
|
||
end; | ||
|
||
end. |
Oops, something went wrong.