eferencing Properties and Functions Across Scripts

I can not seem to reference Properties and functions in the main script from a subsidiary script any reason why this should be? The statement Printscreen_MainQuest_Script Property MainQuest auto fails to define MainQuest. What is going on

Edit

This looks like a Papyrus scripting issue (from Skyrim/Fallout modding). The error you're seeing with the Property declaration suggests a few possible causes:

Most likely issues:

  1. Script compilation order - The main script needs to be compiled before the subsidiary script can reference it. Make sure your main script compiles successfully first.
  2. Incorrect script name - The property type Printscreen_MainQuest_Script must exactly match the script name. Check that:
    • The main script is actually named Printscreen_MainQuest_Script.psc
    • There are no typos in the script name
    • Case sensitivity matches exactly
  3. Script not extending the right base class - Make sure your main script extends an appropriate base class:

    papyrus

    Scriptname Printscreen_MainQuest_Script extends Quest

  4. Property not being filled in the editor - Even if the property compiles, you need to fill it in the Creation Kit:
    • Select your subsidiary script's quest/object
    • Go to the Properties section
    • Set the MainQuest property to point to your actual quest object

Quick troubleshooting steps:

  1. Verify the main script compiles without errors
  2. Check exact spelling and case of script names
  3. Ensure the main script extends an appropriate base class
  4. Recompile both scripts in the correct order
  5. Fill the property in the Creation Kit editor

What type of object is your main script attached to (Quest, Actor, etc.)? This will help determine the correct approach.

Retry

WL

How can I controle the order of compilation

Edit

In Papyrus scripting, you can control compilation order through several methods:

Manual Compilation Order:

  1. Compile scripts individually - In the Creation Kit's Papyrus Script Manager:
    • Compile your main script (Printscreen_MainQuest_Script) first
    • Then compile any scripts that reference it
    • Use "Compile" button instead of "Compile All"
  2. Dependencies matter - The compiler needs to see the referenced script's compiled .pex file before it can resolve the property type.

Best Practices for Compilation:

  1. Start with base/parent scripts:

    papyrus

    ; Compile this first Scriptname Printscreen_MainQuest_Script extends Quest

  2. Then compile dependent scripts:

    papyrus

    ; Compile this after the main script Scriptname YourSubsidiaryScript extends ObjectReference Printscreen_MainQuest_Script Property MainQuest auto

Alternative approaches:

  1. Use "Compile All" after clearing compiled scripts:
    • Delete .pex files from your Data/Scripts folder
    • Use "Compile All" - this usually handles dependencies correctly
  2. Use batch compilation:
    • Create a text file listing scripts in dependency order
    • Some mod tools can process this list sequentially
  3. Check for circular dependencies:
    • Make sure scripts don't reference each other in a loop
    • This can cause compilation failures

If you're still having issues:

  • Try deleting the .pex file for both scripts and recompiling the main one first
  • Check that the main script actually compiled successfully (look for the .pex file)
  • Verify there are no syntax errors in the main script preventing compilation

Which Creation Kit version are you using? The exact steps can vary slightly between Skyrim and Fallout 4.

Retry

WL

So if mainQuest referenced formula and formuka references MainQuest a circular eference would occur and neither would compile?

Edit

Exactly! You've identified a classic circular dependency problem. If MainQuest references Formula and Formula references MainQuest, neither script can compile because:

  1. MainQuest tries to compile first → Compiler looks for Formula's .pex file → Formula isn't compiled yet → Compilation fails
  2. Formula tries to compile first → Compiler looks for MainQuest's .pex file → MainQuest isn't compiled yet → Compilation fails

Common solutions for circular dependencies:

  1. Break the circle with a third script:

    papyrus

    ; SharedData.psc - compile this first Scriptname SharedData extends Quest ; Put shared properties/functions here ; MainQuest.psc SharedData Property Data auto ; Formula.psc SharedData Property Data auto

  2. Use dynamic casting instead of properties:

    papyrus

    ; Instead of: MainQuest Property MQ auto ; Use dynamic lookup: MainQuest Function GetMainQuest() return Game.GetFormFromFile(0x12345, "YourMod.esp") as MainQuest EndFunction

  3. Redesign the relationship:
    • Often one script should be the "parent" that manages the other
    • Child scripts call up to parent, parent calls down to children
    • Avoid peer-to-peer references
  4. Use events/custom events:

    papyrus

    ; Formula sends event instead of direct reference RegisterForCustomEvent(MainQuest, "SomeEvent")

Best practice: Design your script hierarchy so dependencies flow in one direction (like a tree), not in circles. The main quest script is usually at the top, with other scripts referencing it but not vice versa.

Retry

WL

so if mainQuest references a formula in formula_script do you just use formula_script.formula() to reference it with out a property onvolved

Edit

No, you still need to establish a reference to the other script object somehow. You can't just call formula_script.formula() directly without telling Papyrus which specific instance of that script you want to call.

Here are the correct approaches:

1. Using a Property (most common):

papyrus

; In MainQuest script Formula_Script Property MyFormula auto Function SomeFunction() MyFormula.formula() ; Calls the function on the property reference EndFunction

2. Dynamic casting/lookup:

papyrus

; In MainQuest script - no property needed Function SomeFunction() Formula_Script myRef = Game.GetFormFromFile(0x12345, "YourMod.esp") as Formula_Script myRef.formula() EndFunction

3. Finding by reference ID:

papyrus

Function SomeFunction() Formula_Script myRef = Game.GetForm(0x12345) as Formula_Script myRef.formula() EndFunction

4. If it's attached to a quest alias:

papyrus

Function SomeFunction() Formula_Script myRef = MyQuest.GetAlias(0) as Formula_Script myRef.formula() EndFunction

The key point is that Papyrus needs to know which specific object instance has the Formula_Script attached to it. Script names are just types - you need an actual reference to call functions on.

Why you can't just use formula_script.formula():

  • formula_script is a type name, not an object instance
  • Multiple objects could have the same script attached
  • Papyrus needs to know which specific one you mean

The property approach is usually cleanest because you set it up once in the Creation Kit editor.

Retry

Printscreen_Formula_Script.psc

38 lines

text

Printscreen_MainQuest_Script.psc

329 lines

text

WL

So the real problem in that the formula script and mainQuest script reference each other. What would the third script look like given the attached files. Present results in an artifact

Edit

Looking at your scripts, you have a circular dependency where:

  • Printscreen_MainQuest_Script references Printscreen_Formula_Script
  • Printscreen_Formula_Script references Printscreen_MainQuest_Script

Here's a solution using a third intermediary script to break the circular dependency:

Printscreen_SharedData_Script.psc

Code 

Scriptname Printscreen_SharedData_Script extends Quest ; This script acts as a shared data hub to break circular dependencies ; Both MainQuest and Formula scripts will reference this, but not each other ; Shared state properties Bool Property IsLat

Now you'll need to modify your existing scripts to use this shared script instead of referencing each other directly. Here are the key changes needed:

For Printscreen_MainQuest_Script:

  1. Remove the PrintScreen_Formula_Script Property FormulaScript Auto property
  2. Add Printscreen_SharedData_Script Property SharedData Auto
  3. In TakeLatentScreenshot(), call SharedData.StartScreenshot() instead
  4. Remove the event registration from OnInit() (SharedData handles it now)
  5. Remove the OnScreenshotCompleted and OnModEvent functions

For Printscreen_Formula_Script:

  1. Remove the Printscreen_MainQuest_Script property MainQuest auto property
  2. Add Printscreen_SharedData_Script Property SharedData Auto
  3. In the Printscreen() function, call SharedData.OnScreenshotFailed() instead of accessing MainQuest directly

Compilation order:

  1. Compile Printscreen_SharedData_Script first
  2. Then compile Printscreen_MainQuest_Script
  3. Finally compile Printscreen_Formula_Script

In Creation Kit:

  • Create a new quest for the SharedData script
  • Fill the MainQuestRef and FormulaQuestRef properties to point to your actual quest objects
  • Fill the SharedData properties in both other scripts to point to the SharedData quest

This breaks the circular dependency by having both scripts reference the shared data script, but not each other.

Retry

WL

so variables I have set up as properties in mainQuest should be noved to the shared data script

Edit

Not necessarily! You should only move properties to the shared data script if they need to be accessed by both scripts. Most of your MainQuest properties can stay where they are.

Properties that should STAY in MainQuest:

  • Configuration properties (TakePhoto, KeyMapCode, Path, ImageType, Compression, etc.)
  • MCM/UI related properties (bConfigOpen, JsonFileName, KeyName_* properties)
  • Utility properties (Version, Read_Write_Config, etc.)
  • Properties only MainQuest uses

Properties that should MOVE to SharedData:

  • IsLatentScreenshotActive - Both scripts need this
  • Menu - Formula script needs this to know about HUD control
  • Result - Both scripts update/read this
  • Potentially shots - if Formula script needs to access the counter

Hybrid approach (recommended): Keep most properties in MainQuest, but have SharedData provide controlled access:

papyrus

; In SharedData script Bool Property IsLatentScreenshotActive Auto Bool Property Menu Auto ; Synced from MainQuest ; Getter functions for MainQuest properties Bool Function GetMenuSetting() Printscreen_MainQuest_Script mainScript = MainQuestRef as Printscreen_MainQuest_Script if (mainScript) return mainScript.Menu endif return true EndFunction Function UpdateMenuSetting() Printscreen_MainQuest_Script mainScript = MainQuestRef as Printscreen_MainQuest_Script if (mainScript) Menu = mainScript.Menu ; Sync the value endif EndFunction

The key principle: Only move properties that create the circular dependency. Keep the rest organized where they logically belong (configuration stays with MainQuest, shared state goes to SharedData).

This way MainQuest remains the "configuration manager" and SharedData is just the "communication hub" between the two scripts.

Retry

WL

OK I am having trouble wrapping my mind around this. If shareddata is referenced by both MainQuest and Formula quest I presume It would need to be compiled before both of these files. But I see a "Ouest PropertyFormulaQuestRef Auto" does this create a reference to the formulaquest which is a circular reference? Or is it ok to reference it in this way since it does not mention the script in its definintion

Edit

Great question! You're absolutely right to think carefully about this. Let me clarify the key distinction:

It's NOT a circular reference because:

  1. Quest vs Script reference: Quest Property FormulaQuestRef Auto references the Quest object, not the script type. This is very different from Printscreen_Formula_Script Property FormulaScript Auto.
  2. Compilation order is safe:
    • Compile Printscreen_SharedData_Script first ✓
    • Then compile Printscreen_MainQuest_Script ✓
    • Then compile Printscreen_Formula_Script ✓

Why this works:

  • Quest is a base Papyrus type that's always available
  • The compiler doesn't need to know about the Formula script to compile SharedData
  • You're not trying to call script-specific functions through the Quest property

However, there's an even cleaner approach - remove those Quest properties entirely:

papyrus

Scriptname Printscreen_SharedData_Script extends Quest ; Shared state only - no quest references needed Bool Property IsLatentScreenshotActive Auto Bool Property Menu Auto String Property CurrentResult Auto ; Instead of Quest properties, use dynamic lookup when needed: Function NotifyMainQuest(String result) ; Find MainQuest dynamically if needed Quest mainQ = Game.GetFormFromFile(0x123, "YourMod.esp") as Quest Printscreen_MainQuest_Script mainScript = mainQ as Printscreen_MainQuest_Script if (mainScript) mainScript.Result = result ; etc... endif EndFunction

The cleanest solution: SharedData just holds shared variables and simple functions. Let MainQuest and Formula scripts call SharedData functions, but SharedData doesn't need to call back to them directly.

So you're right to be cautious, but Quest properties are safe - it's the script type properties that create circular dependencies.

Retry

WL

ok so in sharedData i Aee definitions for: menu , and islatentScreenshotActive and currentresult. These are currently defined in MainQuest but they can be removed and referenced with a Property such as Printscreen_SharedData_Script Property Data auto where the references in mainQuest wouland Data. d now be: Data.IsLatentScreenshotActive and Data.menu.. And in Printscreen_Formula_Script I would meed Printscreen_Shareddata Property Data auto so the the references could likewise be data.islatentScreenshotActive and Data.Menu

Edit

Exactly! You've got it perfectly. That's the correct approach to break the circular dependency.

Yes, you would:

  1. Remove from MainQuest:
    • Bool Property Menu auto
    • bool Property IsLatentScreenshotActive Auto
    • String Property Result auto (becomes CurrentResult in SharedData)
  2. Add to both MainQuest and Formula scripts:

    papyrus

    Printscreen_SharedData_Script Property SharedData Auto

  3. Update references in MainQuest:
    • Menu becomes SharedData.Menu
    • IsLatentScreenshotActive becomes SharedData.IsLatentScreenshotActive
    • Result becomes SharedData.CurrentResult
  4. Update references in Formula script:
    • MainQuest.IsLatentScreenshotActive = false becomes SharedData.IsLatentScreenshotActive = false
    • if (MainQuest.Menu) becomes if (SharedData.Menu)
    • MainQuest.ShowHud() becomes... well, this is trickier since ShowHud() is a MainQuest function

For functions like ShowHud(): You have a few options:

  • Move ShowHud()/HideHud() functions to SharedData
  • Have SharedData send an event that MainQuest listens for
  • Use a simple flag in SharedData that MainQuest checks

Example update in Formula script:

papyrus

; OLD: MainQuest.IsLatentScreenshotActive = false if (MainQuest.Menu) MainQuest.ShowHud() endif ; NEW: SharedData.IsLatentScreenshotActive = false if (SharedData.Menu) ; Option 1: Move function to SharedData SharedData.ShowHud() ; OR Option 2: Set a flag for MainQuest to check SharedData.NeedsHudRestore = true endif

This approach keeps the data centralized and accessible to both scripts without circular references.

Retry

WL

simplest to move the funcxtion definitions if all the hud/menu operation can be done in a single script. so I woulds have to mode the hideHud function call from Mainquest to Print_screeb_Formula_Script. Function printssreen seem logical to initiate the HUD cobtrol functin call

Edit

Exactly! That's the most logical approach. Since the Printscreen() function in the Formula script is already the one that starts the screenshot process, it makes perfect sense to have it handle the HUD control as well.

Move these functions from MainQuest to SharedData:

papyrus

; In Printscreen_SharedData_Script function hideHud() ConsoleUtil.ExecuteCommand("tm") utility.wait(0.1) EndFunction Function ShowHud() ConsoleUtil.ExecuteCommand("tm") EndFunction

Update the Formula script's Printscreen() function:

papyrus

Function Printscreen(String path, String imageType, Float compression, String ddsCompressionType) global Debug.Trace("Starting latent screenshot: " + path + ", " + imageType) ; Hide HUD if menu control is enabled if (SharedData.Menu) SharedData.hideHud() endif ; Start the latent function using the new plugin interface Bool started = TakeScreenshot(path, imageType, compression, ddsCompressionType) if !started Debug.Notification("Failed to start screenshot capture") ; Reset latent state and restore HUD if needed SharedData.IsLatentScreenshotActive = false if (SharedData.Menu) SharedData.ShowHud() endif endif EndFunction

Update MainQuest OnKeyUP event:

papyrus

Event OnKeyUP(int theKey, float holdtime) If( (theKey != TakePhoto) || (bConfigOpen) || (ui.IsTextInputEnabled()) ) return Endif ; If a previous latent screenshot is still processing, don't start another if(SharedData.IsLatentScreenshotActive) Debug.Notification("Screenshot already in progress") return endif ; Set the flag to indicate screenshot is in progress SharedData.IsLatentScreenshotActive = true Debug.Notification("Taking screenshot...") ; No need to call hideHud() here - Printscreen() will handle it Printscreen_Formula_Script.Printscreen(Path, ImageType, Compression, DDS_Compression) EndEvent

This centralizes all HUD control in one place and makes the flow much cleaner: Printscreen() handles hiding the HUD, and the completion event handles showing it again.

Retry

Claude can make mistakes. Please double-check responses.

 

Sonnet 4

Copy

Publish

Scriptname Printscreen_SharedData_Script extends Quest ; This script acts as a shared data hub to break circular dependencies ; Both MainQuest and Formula scripts will reference this, but not each other ; Shared state properties Bool Property IsLatentScreenshotActive Auto Bool Property Menu Auto String Property CurrentResult Auto ; Quest references - these will be filled in Creation Kit Quest Property MainQuestRef Auto ; Points to your main quest Quest Property FormulaQuestRef Auto ; Points to your formula quest ; Event handling - this script will manage the screenshot completion event Event OnInit() ; Register for the screenshot completion event here instead of in MainQuest RegisterForModEvent("PrintScreenComplete", "OnScreenshotCompleted") ; Initialize shared state IsLatentScreenshotActive = false Menu = true CurrentResult = "" EndEvent ; Central event handler for screenshot completion Event OnScreenshotCompleted(String eventName, String strArg, Float numArg, Form akSender) Debug.Trace("SharedData: Screenshot completed with result: " + strArg) ; Update shared result CurrentResult = strArg ; Get the main quest script to update its properties Printscreen_MainQuest_Script mainScript = MainQuestRef as Printscreen_MainQuest_Script if (mainScript) ; Update main quest's result and shot counter mainScript.Result = strArg if (strArg == "Success") mainScript.Shots = mainScript.Shots + 1 Debug.Notification("Screenshot #" + mainScript.Shots + " saved successfully!") else Debug.Notification("Screenshot failed: " + strArg) endif ; Restore HUD if needed if (IsLatentScreenshotActive && Menu) mainScript.ShowHud() endif endif ; Reset the tracking flag IsLatentScreenshotActive = false EndEvent ; Function to start screenshot process - called by MainQuest Function StartScreenshot(String path, String imageType, Float compression, String ddsCompressionType) if (IsLatentScreenshotActive) Debug.Notification("Screenshot already in progress") return endif ; Set flag before starting IsLatentScreenshotActive = true ; Call the formula script's function directly (no circular reference) Bool started = Printscreen_Formula_Script.TakeScreenshot(path, imageType, compression, ddsCompressionType) if (!started) Debug.Notification("Failed to start screenshot capture") IsLatentScreenshotActive = false ; Get main quest to restore HUD if needed Printscreen_MainQuest_Script mainScript = MainQuestRef as Printscreen_MainQuest_Script if (mainScript && Menu) mainScript.ShowHud() endif endif EndFunction ; Function to handle screenshot failure - called by Formula script Function OnScreenshotFailed() IsLatentScreenshotActive = false ; Get main quest to restore HUD if needed Printscreen_MainQuest_Script mainScript = MainQuestRef as Printscreen_MainQuest_Script if (mainScript && Menu) mainScript.ShowHud() endif EndFunction