u/cyberpsyche_mods

Starfield Creation Kit Tutorial - Quick edit Layered Material Swaps by scrolling
▲ 23 r/StarfieldCreationKit+1 crossposts

Starfield Creation Kit Tutorial - Quick edit Layered Material Swaps by scrolling

Hey all! Trying my hand at video tutorials. This is my first one, just got setup with my tooling so I'll have to refine as I go along.

This tutorial shows you how to quickly edit layered material swaps on statics and packins when editing in the render window, using some handy hotkeys!

Basically, change the color and texture of objects quickly when they have prebuilt options to do so.

Most of my tutorials will be on CK basics, and then some specifics around POIs and dungeon setup.

youtube.com
u/cyberpsyche_mods — 11 hours ago
▲ 110 r/StarfieldCreationKit+2 crossposts

Free Lanes update brought randomized loot placement to many locations

Good news! Hadn't seen anyone mentioned this yet, and I'm not sure it was mentioned in any of the official change logs.

It looks like how it works is the loot on a map (or any item really) is assigned into three groups. One of which will be randomly enabled when the map loads, and the rest will be disabled (not visible for players)

So if you noticed the placement and variety of loot differing on many of the new POIs you were not crazy!

Observations:

  • I didn't see this added to any old locations, so it only appears to be the new POIs.
  • Some key loot like boss chests are not included in the random placement, which makes sense to me. If you wanted to do something like that and make it make sense you would need to design locations around that which adds more complexity. That is because you want the boss near the boss chest and levels often are designed to flow a particular way around a boss location/boss fight if they are combat focused.
  • In some maps it seems like the three groups of loot are sort of a low, med, high value loot type arrangement. I haven't looked at a ton of the maps though so could just be the way I'm interpreting the loot selection.
  • The three groups of items to enable/disable include lootable things such as credsticks, containers and displays of all kinds, corpses, and some associated things such as blood decals associated with the corpses/scenery and stuff.

Technical, for mod authors, if you want to inspect:

  • Look for the SFBGS00D_RandomizeDummy activator in a cell on a map, this has a script which performs the random roll OnCellLoad, and chooses a set (number is set by property) number of enable markers to enable. The markers are in a linkref chain. I recommend looking at the script, although I don't think it de-compiles fully via Champollion.
  • Right click SFBGS00D_RandomizeDummy in the object window and look at Use Info to see every location using it currently.
  • To see all the loot in a given group, right click an enable marker, and choose Select Enable State Children. Set your cell view to Selected, (keep in mind cell view only shows you whats in the current cell, not the whole map in an exterior worldspace)

If you want to add it or use it to your POI:

  • There is a packin that has the "kit" of the activator and enable markers, already linked up. "SFBGS00D_RandomizerPackin" to make it easier to place.
  • Looks like you would drop this in, then select groups of loot and items, use batch reference actions to set enable parents on those items to one of the three enable markers, rinse and repeat. At a glance I think that is it?
  • I haven't tested any of this out on a custom POI yet, but I will soon!
u/cyberpsyche_mods — 4 days ago

Hey all!

I was making a fix for the ship builder crashes from the Terran Armada couch and I learned a bit. Uploaded a fix as well!

After lots of testing to verify, it looks like having more than one OutpostGroupPackinDummy REFRs in the Instanced Packin for your constructible object results in a ship builder crash after trying to edit a ship with one of those packins in it.

Multiple other authors acknowledged this is a known source of crashes, and since I also confirmed it with testing I wanted to share. I try to never spread misinformation so if I get any details wrong here, please chime in.

Below is an xedit script written by twenty rounds with ChatGPT Codex and myself that will scan through packin cells in an ESM and look for any with more than one dummy. It provides a bit of other additional information as well.

Log output looks like in the screenshot, which is the run on SFBGSS050.esm. You'll notice it found multiple dummies in a couple of unused packins, as well as another buildable, but that other buildable is Instanced Static and in my testing doesn't cause a crash there.

Once you identify the packin cells you need to go find the associated packins, edit them and remove the dummies. How to do all that is far outside the scope of this post.

tl;dr don't leave multiple dummies in instanced packins that will be used as buildables in ships. (probably don't want them in hab packins there but I couldn't reproduce a crash with it in the hab packins themselves)

unit userscript;

{
  Starfield xEdit scanner.

  Run:
    Right-click an ESM/ESP in the left pane
    Apply Script
    Select this script

  Purpose:
    Find PackIn* CELLs in the selected plugin that contain more than one
    placed REFR whose base object is OutpostGroupPackinDummy.

  This version processes REFR records directly.
}

const
  TargetCellPrefixLower = 'packin';
  TargetBaseEditorID = 'OutpostGroupPackinDummy';
  TargetBaseFixedFormID = $00015804;

  // Set False if you only want active/non-deleted refs.
  CountDeletedRefs = True;

  // Debug limits.
  MaxTargetRefDebugLogs = 100;

var
  TotalRecordsProcessed: Integer;
  TotalRefsProcessed: Integer;
  TotalTargetRefsFound: Integer;
  TotalPackinTargetRefsFound: Integer;
  TotalDeletedTargetRefsFound: Integer;
  DebugTargetRefLogs: Integer;

  CellKeys: TStringList;
  CellCounts: TStringList;
  CellFirstRef: TStringList;

function BoolString(b: Boolean): string;
begin
  if b then
    Result := 'True'
  else
    Result := 'False';
end;

function ContainsInsensitive(s, part: string): Boolean;
begin
  Result := Pos(LowerCase(part), LowerCase(s)) > 0;
end;

function StartsWithInsensitive(s, prefixLower: string): Boolean;
var
  sLower: string;
begin
  Result := False;

  if Length(s) < Length(prefixLower) then
    Exit;

  sLower := LowerCase(Copy(s, 1, Length(prefixLower)));

  if sLower = prefixLower then
    Result := True;
end;

function BaseMatchesTarget(baseObj: IInterface): Boolean;
var
  baseEdid: string;
  baseName: string;
  baseFixedID: Cardinal;
begin
  Result := False;

  if not Assigned(baseObj) then
    Exit;

  baseEdid := EditorID(baseObj);
  baseName := Name(baseObj);
  baseFixedID := FixedFormID(baseObj);

  if baseEdid = TargetBaseEditorID then begin
    Result := True;
    Exit;
  end;

  if baseFixedID = TargetBaseFixedFormID then begin
    Result := True;
    Exit;
  end;

  if ContainsInsensitive(baseName, TargetBaseEditorID) then begin
    Result := True;
    Exit;
  end;
end;

function IsTargetDummyRef(refRec: IInterface): Boolean;
var
  baseObj: IInterface;
begin
  Result := False;

  if Signature(refRec) <> 'REFR' then
    Exit;

  if GetIsDeleted(refRec) and not CountDeletedRefs then
    Exit;

  baseObj := LinksTo(ElementByPath(refRec, 'NAME'));
  if not Assigned(baseObj) then
    Exit;

  if BaseMatchesTarget(baseObj) then
    Result := True;
end;

function ExtractCellFromRefName(refRec: IInterface): string;
var
  s: string;
  pCell: Integer;
  pStart: Integer;
begin
  Result := '';

  {
    Known Starfield xEdit format from your test:

      [REFR:020838B9] (in PackInSFBGS037ElevatedCabinDesignerCouchStorageCell [CELL:02083804])

    We want:

      PackInSFBGS037ElevatedCabinDesignerCouchStorageCell
  }

  s := Name(refRec);

  pCell := Pos(' [CELL:', s);
  if pCell = 0 then
    Exit;

  // Most common observed format: "(in CellName [CELL:...])"
  pStart := Pos('(in ', s);
  if pStart > 0 then begin
    Result := Copy(s, pStart + 4, pCell - (pStart + 4));
    Exit;
  end;

  // Fallback for alternate format: " in CellName [CELL:...]"
  pStart := Pos(' in ', s);
  if pStart > 0 then begin
    Result := Copy(s, pStart + 4, pCell - (pStart + 4));
    Exit;
  end;
end;

procedure AddCellHit(cellKey: string; refName: string);
var
  idx: Integer;
  count: Integer;
begin
  idx := CellKeys.IndexOf(cellKey);

  if idx = -1 then begin
    CellKeys.Add(cellKey);
    CellCounts.Add('1');
    CellFirstRef.Add(refName);
  end
  else begin
    count := StrToInt(CellCounts[idx]);
    Inc(count);
    CellCounts[idx] := IntToStr(count);
  end;
end;

function Initialize: Integer;
begin
  TotalRecordsProcessed := 0;
  TotalRefsProcessed := 0;
  TotalTargetRefsFound := 0;
  TotalPackinTargetRefsFound := 0;
  TotalDeletedTargetRefsFound := 0;
  DebugTargetRefLogs := 0;

  CellKeys := TStringList.Create;
  CellCounts := TStringList.Create;
  CellFirstRef := TStringList.Create;

  AddMessage('Starting REFR-based scan...');
  AddMessage('Looking for REFRs with base object: ' + TargetBaseEditorID);
  AddMessage('Target fixed FormID fallback: ' + IntToHex(TargetBaseFixedFormID, 8));
  AddMessage('Only counting refs whose parent cell name starts with PackIn / Packin / packin.');
  AddMessage('Count deleted refs: ' + BoolString(CountDeletedRefs));
  AddMessage('This version processes REFR records directly.');

  Result := 0;
end;

function Process(e: IInterface): Integer;
var
  cellName: string;
begin
  Result := 0;

  Inc(TotalRecordsProcessed);

  if Signature(e) <> 'REFR' then
    Exit;

  Inc(TotalRefsProcessed);

  if not IsTargetDummyRef(e) then
    Exit;

  Inc(TotalTargetRefsFound);

  if GetIsDeleted(e) then
    Inc(TotalDeletedTargetRefsFound);

  cellName := ExtractCellFromRefName(e);

  if DebugTargetRefLogs < MaxTargetRefDebugLogs then begin
    Inc(DebugTargetRefLogs);
    AddMessage(
      'Target dummy REFR found #' + IntToStr(DebugTargetRefLogs) +
      ' | Deleted: ' + BoolString(GetIsDeleted(e)) +
      ' | Cell: "' + cellName + '"' +
      ' | Ref: ' + Name(e)
    );
  end;

  if cellName = '' then
    Exit;

  if not StartsWithInsensitive(cellName, TargetCellPrefixLower) then
    Exit;

  Inc(TotalPackinTargetRefsFound);

  AddCellHit(cellName, Name(e));
end;

function Finalize: Integer;
var
  i: Integer;
  count: Integer;
  matchedCells: Integer;
begin
  matchedCells := 0;

  AddMessage('Scan complete.');
  AddMessage('Total records processed by script: ' + IntToStr(TotalRecordsProcessed));
  AddMessage('Total REFR records processed: ' + IntToStr(TotalRefsProcessed));
  AddMessage('Total OutpostGroupPackinDummy REFRs found anywhere: ' + IntToStr(TotalTargetRefsFound));
  AddMessage('Total deleted OutpostGroupPackinDummy REFRs found anywhere: ' + IntToStr(TotalDeletedTargetRefsFound));
  AddMessage('Total OutpostGroupPackinDummy REFRs found in PackIn* cells: ' + IntToStr(TotalPackinTargetRefsFound));
  AddMessage('Unique PackIn* cells with at least one dummy: ' + IntToStr(CellKeys.Count));

  AddMessage('--- PackIn* cells with more than one OutpostGroupPackinDummy ---');

  for i := 0 to CellKeys.Count - 1 do begin
    count := StrToInt(CellCounts[i]);

    if count > 1 then begin
      Inc(matchedCells);
      AddMessage(
        'MATCH: ' + CellKeys[i] +
        ' | OutpostGroupPackinDummy refs: ' + IntToStr(count)
      );
      AddMessage('  First matching ref seen: ' + CellFirstRef[i]);
    end;
  end;

  AddMessage('Matching PackIn* cells with more than one dummy: ' + IntToStr(matchedCells));

  CellKeys.Free;
  CellCounts.Free;
  CellFirstRef.Free;

  Result := 0;
end;

end.
u/cyberpsyche_mods — 7 days ago
▲ 191 r/NoSodiumStarfield+1 crossposts

https://creations.bethesda.net/en/starfield/details/bfa80cd3-bdad-491f-b2f7-0384b4cb32de/Terran_Armada_Ship_Builder_Couch_Crash_Fix

If you are getting a ship builder crash due to the couch added in Terran Armada, this should fix it until BGS is able to get to it.

I'll report to them today when I get a minute.

For any mod authors, there was an extra OutpostGroupPackinDummy in the Instanced Packin, something for us all to watch out for too.

Edit: Oh and for mod authors I made a small post in the StarfieldCreationKit sub that includes an xedit script to scan for this scenario in any mod. That way you can check your own mods or others mods. https://www.reddit.com/r/StarfieldCreationKit/comments/1t5rn8v/one_ship_builder_crash_source_multiple/

u/cyberpsyche_mods — 7 days ago

Hey all! As always I'm working on simultaneous projects. Making tons of progress on Crimson Escalation, but wanted to show a workbench I made for an unnamed mod I'm working on which will be a very small mod to support gun running and arms dealing roleplay.

  • With weapon engineering ranks you can unlock benches to process/break down weapons into parts (melee, pistols, rifles, etc)
  • Parts can be used at a packing station to pack them into bulk contraband crates for resell.
  • Crafting ingredients, such as lubricant, solvent and so on can be used to enhance the value of the finished product.
  • Parts can also be used to build a variety of gun runner/arms dealer style decorations.
  • Its contraband, sell where you would sell contraband.
  • Potential: Considering some modifications to limit where you can sell contraband, so it feels a bit more challenging.
  • Potential: Simple quests to produce certain types of crates, and drop them off in suspicious locations for additional profit.

I have a working proof of concept up and running, so this is not theory anymore. Just working on knocking out objects and some finer details of the mechanics, playtesting the scripts, but its a simple mod, so I should have it out far before CE.

This mod will be free, but if I get accepted into VC I'll see about achievement friendly.

Possible names: Blackmarket Bundles, Crates for Criminals, Packin Heat

u/cyberpsyche_mods — 12 days ago
▲ 103 r/StarfieldCreationKit+1 crossposts

Hey all! I was poking through Free Lanes additions in the CK and found SFBGS00DWorldArtAssets under Interior cells. Check it out! It has a ton of art assets placed for reference. VERY useful. It isn't everything, but it is a ton.

If you aren't already aware of the pre-existing "warehouse" and "test" cells. Search Interior cells by those terms and go through some of those. WarehouseTraps is pretty interesting and useful!

u/cyberpsyche_mods — 13 days ago