Jump to content

Understanding dynamic array correctly


SMB92

Recommended Posts

 

Wow, this looks complex. I haven't started learning coding for FO4 yet. But this looks like a variation of C.

 

In most programming languages I know, if you declare a private variable, it will vanish along with the function once function finishes its operation? So instead of creating a global variable that lingers, why not creating a custom variable type and pass this array around like parameters between functions. Too many global variables can increase memory load, and if not cleaning properly can cause problems.

Yeah I generally dislike using Globals if possible. But in Papyrus GlobalVariable is something actually global to the game not the script. So it's a little different context. Then you have "local script variables" which are global to any functions in the script. Then there's local function variables. Which are exactly as it sounds.

 

I was bored so I took the last piece of code and did some refactoring to make it a bit smaller. This is the refactored code. Also grouped the properties. If we wanted to get more advanced you could define a struct then pass an array of the struct then have the script loop the array of struct to decide what to spawn rather than having to hard code each type individually. The changes to the script aren't all that complex but having to re-input everything in the plugin would be tedious. The benefit to do this would be that adding additional spawn types later would be as easy as just setting the variables in CK. No script changes needed since it pulls it all from the vars.

 

I'd also like to point out that the code originally (and still) uses the same lvl list to spawn both regular and boss actors. For future use you'd probably want to add additional boss lists.

 

I also converted all the properties to const. This way if the plugin was ever updated the values would pass to the script. Otherwise when the script first fires and starts running it saves all those values inside the save file. If it's attached to a quest then it would never terminate and restart unless the quest was stopped and then restarted.

 

 

 

Scriptname ASC_SP_Random_R1 extends ObjectReference

GlobalVariable Property ASC_Main_ModEnabled Auto Const
GlobalVariable Property ASC_Main_RandomEnabled_R1 Auto Const
GlobalVariable Property ASC_Main_Random_Chance_R1 Auto Const
GlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto Const
FormList Property ASC_ResetList_R1 Auto Const
Actor Property PlayerRef Auto Const
ObjectReference Property PatrolMarker Auto Const
GlobalVariable Property ASC_Main_Difficulty_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_Chance_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_R1 Auto Const
GlobalVariable Property ASC_DeleteTimer_R1 Auto Const
Group RaiderParams
    ActorBase property LvlRaider Auto Const
    GlobalVariable Property ASC_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Chance_RaiderBoss_R1 Auto Const
EndGroup
Group GunnerParams
    ActorBase Property LvlGunner Auto Const
    GlobalVariable Property ASC_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Chance_GunnerBoss_R1 Auto Const
EndGroup
Group ForgedParams
    ActorBase Property LvlForged Auto Const
    GlobalVariable Property ASC_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Chance_ForgedBoss_R1 Auto Const
EndGroup
Group TriggermenParams
    ActorBase Property LvlTriggerman Auto Const
    GlobalVariable Property ASC_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Chance_TmenBoss_R1 Auto Const
EndGroup

Bool Rerolled = false
Actor[] GroupList
Int RespawnTimerTest = 10
Bool SpawnPointLoaded = false

Event OnCellAttach()
    SpawnPointLoaded = true
    if (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_RandomEnabled_R1.GetValueInt() == 1)
        BeginActivation()
    endif
EndEvent

Event OnTimerGameTime(int aiTimerID)
    if aiTimerID == RespawnTimerTest
        Self.Enable()
        Debug.Notification("Point Rearmed")
    endif
EndEvent

Event OnCellDetach()
    SpawnPointLoaded = false
    Utility.Wait(10)
    if (SpawnPointLoaded == false) && (PlayerRef.IsInCombat() == false)
        int iCounter = 0
        while iCounter < GroupList.Length
            GroupList[iCounter].SetLinkedRef(None)
            GroupList[iCounter].DeleteWhenAble()
            iCounter += 1
        endwhile
        GroupList.Clear()
        Debug.Notification("All members cleared from array")
    endif
EndEvent
        
Function BeginActivation()
    int ChanceToSpawn = Utility.RandomInt(1,100)
    if (ChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt())
        Spawn()
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
        Debug.Notification("Controller Disabled and added to formlist")
        StartTimerGameTime(1, RespawnTimerTest)
        Debug.Notification("Respawn Timer On")
    elseif (ChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1)
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
    else
        ;Nothing
    endif
EndFunction

Function RerollOnSuccess(bool bSuccess)
    if (bSuccess == true) && (Rerolled == false) && (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
        Spawn()
        Debug.Notification("Spawning another group!")
    else
        Rerolled = false
        Debug.Notification("Failed to Reroll another group!")
    endif    
EndFunction

Function Spawn()
    int WhoToSpawn = Utility.RandomInt(1,4)
        if (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
        elseif (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Raider_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Raider_R1.GetValueInt())
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Gunner_R1.GetValueInt(), ASC_Chance_Gunner_R1.GetValueInt(), LvlGunner, ASC_Allowed_GunnerBoss_R1.GetValue() as Bool, ASC_Max_Allowed_GunnerBoss_R1.GetValueInt(), ASC_Chance_GunnerBoss_R1.GetValueInt(), LvlGunner)
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Gunner_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Gunner_R1.GetValueInt())
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Forged_R1.GetValueInt(), ASC_Chance_Forged_R1.GetValueInt(), LvlForged, ASC_Allowed_ForgedBoss_R1.GetValue() as Bool, ASC_Max_Allowed_ForgedBoss_R1.GetValueInt(), ASC_Chance_ForgedBoss_R1.GetValueInt(), LvlForged)
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Forged_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Forged_R1.GetValueInt())
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Tmen_R1.GetValueInt(), ASC_Chance_Tmen_R1.GetValueInt(), LvlTriggerman, ASC_Allowed_TmenBoss_R1.GetValue() as Bool, ASC_Max_Allowed_TmenBoss_R1.GetValueInt(), ASC_Chance_TmenBoss_R1.GetValueInt(), LvlTriggerman)
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Tmen_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Tmen_R1.GetValueInt())
        endif
EndFunction

; call this with something like SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
; iMaxSpawnCount = how many spawned actors you want
; iChance = chance for a spawn to happen
; varBaseActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
; bBossAllowed = whether to allow boss
; iMaxBossCount = max number of bosses to roll spawns for
; iBossChance = chance per roll for a boss
; varBossActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)
    int Difficulty = ASC_Main_Difficulty_R1.GetValueInt()
    int iSpawnCounter = 0
    int iPosition = 0
    bool bSpawnSuccess = false
    GroupList = new Actor[0] ; since we don't have 100% chance of spawning actors, we need this to be an accumulating array
    Debug.Notification("Spawning enemies") ; unless we add a function parameter for text this had to be changed to be something generic
        while (iSpawnCounter < iMaxSpawnCount)
            if (Utility.RandomInt(1,100) <= iChance)
                GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,Difficulty), None))
                iPosition = GroupList.Length - 1
                GroupList[iPosition].SetLinkedRef(PatrolMarker)
                Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                bSpawnSuccess = True
            endif
            iSpawnCounter += 1
        endwhile
        if bBossAllowed == 1
            iSpawnCounter = 0 ; reuse var. memory efficient
            while (iSpawnCounter < iMaxBossCount)
                if (Utility.RandomInt(1,100) <= iBossChance)
                    GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,Difficulty), None))
                    iPosition = GroupList.Length - 1
                    GroupList[iPosition].SetLinkedRef(PatrolMarker)
                    Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                    bSpawnSuccess = True
                endif
                iSpawnCounter += 1
            endwhile
        endif
        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
            RerollOnSuccess(bSpawnSuccess)
        endif
EndFunction

Function RerollCheck(int iRerollChance)
    if (Rerolled == false) && (Utility.RandomInt(1,100) <= iRerollChance)
        Spawn()
        Rerolled = true
        Debug.Notification("Rerolling after block")
    else
        Debug.Notification("Reroll on block denied")
    endif
EndFunction

 

 

This is as much of the original code in tact with the refactor and the redundant stuff commented out for reference. I did group the properties though.

 

 

Scriptname ASC_SP_Random_R1 extends ObjectReference

GlobalVariable Property ASC_Main_ModEnabled Auto Const
GlobalVariable Property ASC_Main_RandomEnabled_R1 Auto Const
GlobalVariable Property ASC_Main_Random_Chance_R1 Auto Const
GlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto Const
FormList Property ASC_ResetList_R1 Auto Const
Actor Property PlayerRef Auto Const
ObjectReference Property PatrolMarker Auto Const
GlobalVariable Property ASC_Main_Difficulty_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_Chance_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_R1 Auto Const
GlobalVariable Property ASC_DeleteTimer_R1 Auto Const
Group RaiderParams
    ActorBase property LvlRaider Auto Const
    GlobalVariable Property ASC_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Chance_RaiderBoss_R1 Auto Const
EndGroup
Group GunnerParams
    ActorBase Property LvlGunner Auto Const
    GlobalVariable Property ASC_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Chance_GunnerBoss_R1 Auto Const
EndGroup
Group ForgedParams
    ActorBase Property LvlForged Auto Const
    GlobalVariable Property ASC_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Chance_ForgedBoss_R1 Auto Const
EndGroup
Group TriggermenParams
    ActorBase Property LvlTriggerman Auto Const
    GlobalVariable Property ASC_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Chance_TmenBoss_R1 Auto Const
EndGroup

;Int ChanceToSpawn = 0 ; moved to local var
;Int Difficulty = 0 ; you don't need this global. moved to local inside spawnactor function
;Int WhoToSpawn = 0 ; moved to local
; Int SpawnCounter = 0 ; you don't need this global
;Int SpawnCounterBoss = 0 ; you don't need this global
; Bool SpawnSuccess = false ; moved local and passed as parameter
Bool Rerolled = false
; Actor Spawn = None ; you don't need this global
Actor[] GroupList
Int RespawnTimerTest = 10
;Int GroupMember = 0 ; don't need this
Bool SpawnPointLoaded = false

Event OnCellAttach()
    SpawnPointLoaded = true
    if (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_RandomEnabled_R1.GetValueInt() == 1)
        BeginActivation()
    endif
EndEvent

Event OnTimerGameTime(int aiTimerID)
    if aiTimerID == RespawnTimerTest
        Self.Enable()
        Debug.Notification("Point Rearmed")
    endif
EndEvent

Event OnCellDetach()
    SpawnPointLoaded = false
    Utility.Wait(10)
    if (SpawnPointLoaded == false) && (PlayerRef.IsInCombat() == false)
        int iCounter = 0
        ;while GroupMember > -1
        ;    GroupList[GroupMember].SetLinkedRef(None)
        ;    GroupList[GroupMember].DeleteWhenAble()
        ;    GroupList[GroupMember] = None
        ;    GroupMember = GroupMember - 1
        ;    Debug.Notification("Member Removed From List")
        ;endwhile
        while iCounter < GroupList.Length
            GroupList[iCounter].SetLinkedRef(None)
            GroupList[iCounter].DeleteWhenAble()
            iCounter += 1
        endwhile
        GroupList.Clear()
        Debug.Notification("All members cleared from array")
    endif
EndEvent
        

Function BeginActivation()
    int ChanceToSpawn = Utility.RandomInt(1,100)
    if (ChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt())
        Spawn()
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
        Debug.Notification("Controller Disabled and added to formlist")
        StartTimerGameTime(1, RespawnTimerTest)
        Debug.Notification("Respawn Timer On")
    elseif (ChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1)
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
    else
        ;Nothing
    endif
EndFunction

Function RerollOnSuccess(bool bSuccess)
    if (bSuccess == true) && (Rerolled == false) && (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
        Spawn()
        Debug.Notification("Spawning another group!")
    else
        ;Difficulty = 0
        ;WhoToSpawn = 0
        ;SpawnSuccess = false
        Rerolled = false
        ;Spawn = none
        ;SpawnCounter = 0
        ;SpawnCounterBoss = 0
        Debug.Notification("Failed to Reroll another group!")
    endif    
EndFunction

Function Spawn()
    int WhoToSpawn = Utility.RandomInt(1,4)
        if (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 1)
            ;SpawnRaider()
            SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
        elseif (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Raider_R1.GetValueInt() == 1)
            ;RerollRaiderR1()
            RerollCheck(ASC_Reroll_Chance_Raider_R1.GetValueInt())
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 1)
            ;SpawnGunner()
            SpawnEnemyActors(ASC_Max_Allowed_Gunner_R1.GetValueInt(), ASC_Chance_Gunner_R1.GetValueInt(), LvlGunner, ASC_Allowed_GunnerBoss_R1.GetValue() as Bool, ASC_Max_Allowed_GunnerBoss_R1.GetValueInt(), ASC_Chance_GunnerBoss_R1.GetValueInt(), LvlGunner)
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Gunner_R1.GetValueInt() == 1)
            ;RerollGunnerR1()
            RerollCheck(ASC_Reroll_Chance_Gunner_R1.GetValueInt())
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 1)
            ;SpawnForged()
            SpawnEnemyActors(ASC_Max_Allowed_Forged_R1.GetValueInt(), ASC_Chance_Forged_R1.GetValueInt(), LvlForged, ASC_Allowed_ForgedBoss_R1.GetValue() as Bool, ASC_Max_Allowed_ForgedBoss_R1.GetValueInt(), ASC_Chance_ForgedBoss_R1.GetValueInt(), LvlForged)
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Forged_R1.GetValueInt() == 1)
            ;RerollForgedR1()
            RerollCheck(ASC_Reroll_Chance_Forged_R1.GetValueInt())
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 1)
            ;SpawnTmen()
            SpawnEnemyActors(ASC_Max_Allowed_Tmen_R1.GetValueInt(), ASC_Chance_Tmen_R1.GetValueInt(), LvlTriggerman, ASC_Allowed_TmenBoss_R1.GetValue() as Bool, ASC_Max_Allowed_TmenBoss_R1.GetValueInt(), ASC_Chance_TmenBoss_R1.GetValueInt(), LvlTriggerman)
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Tmen_R1.GetValueInt() == 1)
            ;RerollTmenR1()
            RerollCheck(ASC_Reroll_Chance_Tmen_R1.GetValueInt())
        endif
EndFunction

; call this with something like SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
; iMaxSpawnCount = how many spawned actors you want
; iChance = chance for a spawn to happen
; varBaseActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
; bBossAllowed = whether to allow boss
; iMaxBossCount = max number of bosses to roll spawns for
; iBossChance = chance per roll for a boss
; varBossActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)
    int Difficulty = ASC_Main_Difficulty_R1.GetValueInt()
    int iSpawnCounter = 0
    int iPosition = 0
    bool bSpawnSuccess = false
    GroupList = new Actor[0] ; since we don't have 100% chance of spawning actors, we need this to be an accumulating array
    Debug.Notification("Spawning enemies") ; unless we add a function parameter for text this had to be changed to be something generic
        while (iSpawnCounter < iMaxSpawnCount)
            if (Utility.RandomInt(1,100) <= iChance)
                GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,Difficulty), None))
                iPosition = GroupList.Length - 1
                GroupList[iPosition].SetLinkedRef(PatrolMarker)
                Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                ; Spawn = None ; you don't need to do this
                bSpawnSuccess = True
            endif
            iSpawnCounter += 1
        endwhile
        if bBossAllowed == 1
            iSpawnCounter = 0 ; reuse var. memory efficient
            while (iSpawnCounter < iMaxBossCount)
                if (Utility.RandomInt(1,100) <= iBossChance)
                    GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,Difficulty), None))
                    iPosition = GroupList.Length - 1
                    GroupList[iPosition].SetLinkedRef(PatrolMarker)
                    Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                    ;Spawn = None
                    bSpawnSuccess = True
                endif
                iSpawnCounter += 1
            endwhile
        endif
        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
            RerollOnSuccess(bSpawnSuccess)
        endif
EndFunction

Function RerollCheck(int iRerollChance)
    if (Rerolled == false) && (Utility.RandomInt(1,100) <= iRerollChance)
        Spawn()
        Rerolled = true
        Debug.Notification("Rerolling after block")
    else
        Debug.Notification("Reroll on block denied")
    endif
EndFunction


;Function SpawnRaider()
;    SpawnCounter = ASC_Max_Allowed_Raider_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Raider Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Raider_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlRaider, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_RaiderBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_RaiderBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_RaiderBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlRaider, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollRaiderR1()
;    if (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Raider_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Raiders Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Raiders denied")
;    endif
;EndFunction

;Function SpawnGunner()
;    SpawnCounter = ASC_Max_Allowed_Gunner_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Gunner Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Gunner_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlGunner, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_GunnerBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_GunnerBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_GunnerBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlGunner, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollGunnerR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Gunner_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Gunners Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Gunners denied")
;    endif
;EndFunction

;Function SpawnForged()
;    SpawnCounter = ASC_Max_Allowed_Forged_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Forged Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Forged_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlForged, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_ForgedBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_ForgedBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_ForgedBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlForged, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollForgedR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Forged_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Forgeds Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Forgeds denied")
;    endif
;EndFunction

;Function SpawnTmen()
;    SpawnCounter = ASC_Max_Allowed_Tmen_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Tmen Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Tmen_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlTriggerman, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_TmenBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_TmenBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_TmenBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlTriggerman, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollTmenR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Tmen_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Tmens Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Tmens denied")
;    endif
;EndFunction

 

Very informative. I didn't mean to sound like I criticize because ... honestly this is only like a partial code of something much larger, looking at this part of the code tells me nothing. Very soon I will get around to scripting.

 

Are all the scripters here with computer science background? I can't imagine anyone with zero programming skill to start writing scripts like this, overwhelming would be an understatement. What program do you use to write these scripts? Or just simple notepad? Or there are dedicated setup with proper compilers? Does the CK help? To this day I still don't know what CK looks like because that thing always crash on me no matter how many times I installed.

Link to comment
Share on other sites

  • Replies 111
  • Created
  • Last Reply

Top Posters In This Topic

Thanks for replies folks Im just reading through it all now. Big day at work, the stars must be lined up wrong because PCs are failing at all the different companies today...
Link to comment
Share on other sites

 

Add and remove do work for arrays. May be  it does not work for a reference, I did not try that, but it works for a form for sure.Â

Compiler carried on it wasn't a known function lol. So I done it like how you see above.

Â

I'm glad you mentioned the SetLinkedRef thing, because this Array is not working properly. It's not setting the LinkedRef to None. It's saying the Index is out of range in papyrus logs as well. Perhaps I should be declaring it as an array property, maybe it's not saving at the end of the function.Â

 

Edit - maybe because I have declared it at a size of 0. Maybe i should declare it at the max spawn size first, and use the groupmember int to get where its actually filled to. That kind seems like its not a dynamic array though to me. Name of topic lol.

 

It`s likely that you either made a syntax error or missed something. I have not checked every line in the script you posted..It`s quite long, but you can always goodle any function and check the example of usage in the CK wiki. You`ve got a good advice about optimisation here. The spawing functions you have are the same, just with different actors.

Link to comment
Share on other sites

Just to note the code is well and truly beta, lot of test variables there (like the timer and lvl lists etc, thats to be cleaned up the final solution is decided). Still reading through comments
Link to comment
Share on other sites

 

 

Wow, this looks complex. I haven't started learning coding for FO4 yet. But this looks like a variation of C.

 

In most programming languages I know, if you declare a private variable, it will vanish along with the function once function finishes its operation? So instead of creating a global variable that lingers, why not creating a custom variable type and pass this array around like parameters between functions. Too many global variables can increase memory load, and if not cleaning properly can cause problems.

Yeah I generally dislike using Globals if possible. But in Papyrus GlobalVariable is something actually global to the game not the script. So it's a little different context. Then you have "local script variables" which are global to any functions in the script. Then there's local function variables. Which are exactly as it sounds.

 

I was bored so I took the last piece of code and did some refactoring to make it a bit smaller. This is the refactored code. Also grouped the properties. If we wanted to get more advanced you could define a struct then pass an array of the struct then have the script loop the array of struct to decide what to spawn rather than having to hard code each type individually. The changes to the script aren't all that complex but having to re-input everything in the plugin would be tedious. The benefit to do this would be that adding additional spawn types later would be as easy as just setting the variables in CK. No script changes needed since it pulls it all from the vars.

 

I'd also like to point out that the code originally (and still) uses the same lvl list to spawn both regular and boss actors. For future use you'd probably want to add additional boss lists.

 

I also converted all the properties to const. This way if the plugin was ever updated the values would pass to the script. Otherwise when the script first fires and starts running it saves all those values inside the save file. If it's attached to a quest then it would never terminate and restart unless the quest was stopped and then restarted.

 

 

 

Scriptname ASC_SP_Random_R1 extends ObjectReference

GlobalVariable Property ASC_Main_ModEnabled Auto Const
GlobalVariable Property ASC_Main_RandomEnabled_R1 Auto Const
GlobalVariable Property ASC_Main_Random_Chance_R1 Auto Const
GlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto Const
FormList Property ASC_ResetList_R1 Auto Const
Actor Property PlayerRef Auto Const
ObjectReference Property PatrolMarker Auto Const
GlobalVariable Property ASC_Main_Difficulty_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_Chance_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_R1 Auto Const
GlobalVariable Property ASC_DeleteTimer_R1 Auto Const
Group RaiderParams
    ActorBase property LvlRaider Auto Const
    GlobalVariable Property ASC_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Chance_RaiderBoss_R1 Auto Const
EndGroup
Group GunnerParams
    ActorBase Property LvlGunner Auto Const
    GlobalVariable Property ASC_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Chance_GunnerBoss_R1 Auto Const
EndGroup
Group ForgedParams
    ActorBase Property LvlForged Auto Const
    GlobalVariable Property ASC_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Chance_ForgedBoss_R1 Auto Const
EndGroup
Group TriggermenParams
    ActorBase Property LvlTriggerman Auto Const
    GlobalVariable Property ASC_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Chance_TmenBoss_R1 Auto Const
EndGroup

Bool Rerolled = false
Actor[] GroupList
Int RespawnTimerTest = 10
Bool SpawnPointLoaded = false

Event OnCellAttach()
    SpawnPointLoaded = true
    if (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_RandomEnabled_R1.GetValueInt() == 1)
        BeginActivation()
    endif
EndEvent

Event OnTimerGameTime(int aiTimerID)
    if aiTimerID == RespawnTimerTest
        Self.Enable()
        Debug.Notification("Point Rearmed")
    endif
EndEvent

Event OnCellDetach()
    SpawnPointLoaded = false
    Utility.Wait(10)
    if (SpawnPointLoaded == false) && (PlayerRef.IsInCombat() == false)
        int iCounter = 0
        while iCounter < GroupList.Length
            GroupList[iCounter].SetLinkedRef(None)
            GroupList[iCounter].DeleteWhenAble()
            iCounter += 1
        endwhile
        GroupList.Clear()
        Debug.Notification("All members cleared from array")
    endif
EndEvent
        
Function BeginActivation()
    int ChanceToSpawn = Utility.RandomInt(1,100)
    if (ChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt())
        Spawn()
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
        Debug.Notification("Controller Disabled and added to formlist")
        StartTimerGameTime(1, RespawnTimerTest)
        Debug.Notification("Respawn Timer On")
    elseif (ChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1)
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
    else
        ;Nothing
    endif
EndFunction

Function RerollOnSuccess(bool bSuccess)
    if (bSuccess == true) && (Rerolled == false) && (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
        Spawn()
        Debug.Notification("Spawning another group!")
    else
        Rerolled = false
        Debug.Notification("Failed to Reroll another group!")
    endif    
EndFunction

Function Spawn()
    int WhoToSpawn = Utility.RandomInt(1,4)
        if (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
        elseif (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Raider_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Raider_R1.GetValueInt())
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Gunner_R1.GetValueInt(), ASC_Chance_Gunner_R1.GetValueInt(), LvlGunner, ASC_Allowed_GunnerBoss_R1.GetValue() as Bool, ASC_Max_Allowed_GunnerBoss_R1.GetValueInt(), ASC_Chance_GunnerBoss_R1.GetValueInt(), LvlGunner)
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Gunner_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Gunner_R1.GetValueInt())
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Forged_R1.GetValueInt(), ASC_Chance_Forged_R1.GetValueInt(), LvlForged, ASC_Allowed_ForgedBoss_R1.GetValue() as Bool, ASC_Max_Allowed_ForgedBoss_R1.GetValueInt(), ASC_Chance_ForgedBoss_R1.GetValueInt(), LvlForged)
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Forged_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Forged_R1.GetValueInt())
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Tmen_R1.GetValueInt(), ASC_Chance_Tmen_R1.GetValueInt(), LvlTriggerman, ASC_Allowed_TmenBoss_R1.GetValue() as Bool, ASC_Max_Allowed_TmenBoss_R1.GetValueInt(), ASC_Chance_TmenBoss_R1.GetValueInt(), LvlTriggerman)
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Tmen_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Tmen_R1.GetValueInt())
        endif
EndFunction

; call this with something like SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
; iMaxSpawnCount = how many spawned actors you want
; iChance = chance for a spawn to happen
; varBaseActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
; bBossAllowed = whether to allow boss
; iMaxBossCount = max number of bosses to roll spawns for
; iBossChance = chance per roll for a boss
; varBossActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)
    int Difficulty = ASC_Main_Difficulty_R1.GetValueInt()
    int iSpawnCounter = 0
    int iPosition = 0
    bool bSpawnSuccess = false
    GroupList = new Actor[0] ; since we don't have 100% chance of spawning actors, we need this to be an accumulating array
    Debug.Notification("Spawning enemies") ; unless we add a function parameter for text this had to be changed to be something generic
        while (iSpawnCounter < iMaxSpawnCount)
            if (Utility.RandomInt(1,100) <= iChance)
                GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,Difficulty), None))
                iPosition = GroupList.Length - 1
                GroupList[iPosition].SetLinkedRef(PatrolMarker)
                Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                bSpawnSuccess = True
            endif
            iSpawnCounter += 1
        endwhile
        if bBossAllowed == 1
            iSpawnCounter = 0 ; reuse var. memory efficient
            while (iSpawnCounter < iMaxBossCount)
                if (Utility.RandomInt(1,100) <= iBossChance)
                    GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,Difficulty), None))
                    iPosition = GroupList.Length - 1
                    GroupList[iPosition].SetLinkedRef(PatrolMarker)
                    Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                    bSpawnSuccess = True
                endif
                iSpawnCounter += 1
            endwhile
        endif
        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
            RerollOnSuccess(bSpawnSuccess)
        endif
EndFunction

Function RerollCheck(int iRerollChance)
    if (Rerolled == false) && (Utility.RandomInt(1,100) <= iRerollChance)
        Spawn()
        Rerolled = true
        Debug.Notification("Rerolling after block")
    else
        Debug.Notification("Reroll on block denied")
    endif
EndFunction

 

 

This is as much of the original code in tact with the refactor and the redundant stuff commented out for reference. I did group the properties though.

 

 

Scriptname ASC_SP_Random_R1 extends ObjectReference

GlobalVariable Property ASC_Main_ModEnabled Auto Const
GlobalVariable Property ASC_Main_RandomEnabled_R1 Auto Const
GlobalVariable Property ASC_Main_Random_Chance_R1 Auto Const
GlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto Const
FormList Property ASC_ResetList_R1 Auto Const
Actor Property PlayerRef Auto Const
ObjectReference Property PatrolMarker Auto Const
GlobalVariable Property ASC_Main_Difficulty_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_Chance_R1 Auto Const
GlobalVariable Property ASC_Reroll_Main_R1 Auto Const
GlobalVariable Property ASC_DeleteTimer_R1 Auto Const
Group RaiderParams
    ActorBase property LvlRaider Auto Const
    GlobalVariable Property ASC_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_RaiderBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Raider_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Raider_R1 Auto Const
    GlobalVariable Property ASC_Chance_RaiderBoss_R1 Auto Const
EndGroup
Group GunnerParams
    ActorBase Property LvlGunner Auto Const
    GlobalVariable Property ASC_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_GunnerBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Gunner_R1 Auto Const
    GlobalVariable Property ASC_Chance_GunnerBoss_R1 Auto Const
EndGroup
Group ForgedParams
    ActorBase Property LvlForged Auto Const
    GlobalVariable Property ASC_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_ForgedBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Forged_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Forged_R1 Auto Const
    GlobalVariable Property ASC_Chance_ForgedBoss_R1 Auto Const
EndGroup
Group TriggermenParams
    ActorBase Property LvlTriggerman Auto Const
    GlobalVariable Property ASC_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Max_Allowed_TmenBoss_R1 Auto Const
    GlobalVariable Property ASC_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Allowed_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Reroll_Chance_Tmen_R1 Auto Const
    GlobalVariable Property ASC_Chance_TmenBoss_R1 Auto Const
EndGroup

;Int ChanceToSpawn = 0 ; moved to local var
;Int Difficulty = 0 ; you don't need this global. moved to local inside spawnactor function
;Int WhoToSpawn = 0 ; moved to local
; Int SpawnCounter = 0 ; you don't need this global
;Int SpawnCounterBoss = 0 ; you don't need this global
; Bool SpawnSuccess = false ; moved local and passed as parameter
Bool Rerolled = false
; Actor Spawn = None ; you don't need this global
Actor[] GroupList
Int RespawnTimerTest = 10
;Int GroupMember = 0 ; don't need this
Bool SpawnPointLoaded = false

Event OnCellAttach()
    SpawnPointLoaded = true
    if (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_RandomEnabled_R1.GetValueInt() == 1)
        BeginActivation()
    endif
EndEvent

Event OnTimerGameTime(int aiTimerID)
    if aiTimerID == RespawnTimerTest
        Self.Enable()
        Debug.Notification("Point Rearmed")
    endif
EndEvent

Event OnCellDetach()
    SpawnPointLoaded = false
    Utility.Wait(10)
    if (SpawnPointLoaded == false) && (PlayerRef.IsInCombat() == false)
        int iCounter = 0
        ;while GroupMember > -1
        ;    GroupList[GroupMember].SetLinkedRef(None)
        ;    GroupList[GroupMember].DeleteWhenAble()
        ;    GroupList[GroupMember] = None
        ;    GroupMember = GroupMember - 1
        ;    Debug.Notification("Member Removed From List")
        ;endwhile
        while iCounter < GroupList.Length
            GroupList[iCounter].SetLinkedRef(None)
            GroupList[iCounter].DeleteWhenAble()
            iCounter += 1
        endwhile
        GroupList.Clear()
        Debug.Notification("All members cleared from array")
    endif
EndEvent
        

Function BeginActivation()
    int ChanceToSpawn = Utility.RandomInt(1,100)
    if (ChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt())
        Spawn()
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
        Debug.Notification("Controller Disabled and added to formlist")
        StartTimerGameTime(1, RespawnTimerTest)
        Debug.Notification("Respawn Timer On")
    elseif (ChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1)
        Self.Disable()
        ASC_ResetList_R1.AddForm(Self)
    else
        ;Nothing
    endif
EndFunction

Function RerollOnSuccess(bool bSuccess)
    if (bSuccess == true) && (Rerolled == false) && (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
        Spawn()
        Debug.Notification("Spawning another group!")
    else
        ;Difficulty = 0
        ;WhoToSpawn = 0
        ;SpawnSuccess = false
        Rerolled = false
        ;Spawn = none
        ;SpawnCounter = 0
        ;SpawnCounterBoss = 0
        Debug.Notification("Failed to Reroll another group!")
    endif    
EndFunction

Function Spawn()
    int WhoToSpawn = Utility.RandomInt(1,4)
        if (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 1)
            ;SpawnRaider()
            SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
        elseif (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Raider_R1.GetValueInt() == 1)
            ;RerollRaiderR1()
            RerollCheck(ASC_Reroll_Chance_Raider_R1.GetValueInt())
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 1)
            ;SpawnGunner()
            SpawnEnemyActors(ASC_Max_Allowed_Gunner_R1.GetValueInt(), ASC_Chance_Gunner_R1.GetValueInt(), LvlGunner, ASC_Allowed_GunnerBoss_R1.GetValue() as Bool, ASC_Max_Allowed_GunnerBoss_R1.GetValueInt(), ASC_Chance_GunnerBoss_R1.GetValueInt(), LvlGunner)
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Gunner_R1.GetValueInt() == 1)
            ;RerollGunnerR1()
            RerollCheck(ASC_Reroll_Chance_Gunner_R1.GetValueInt())
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 1)
            ;SpawnForged()
            SpawnEnemyActors(ASC_Max_Allowed_Forged_R1.GetValueInt(), ASC_Chance_Forged_R1.GetValueInt(), LvlForged, ASC_Allowed_ForgedBoss_R1.GetValue() as Bool, ASC_Max_Allowed_ForgedBoss_R1.GetValueInt(), ASC_Chance_ForgedBoss_R1.GetValueInt(), LvlForged)
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Forged_R1.GetValueInt() == 1)
            ;RerollForgedR1()
            RerollCheck(ASC_Reroll_Chance_Forged_R1.GetValueInt())
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 1)
            ;SpawnTmen()
            SpawnEnemyActors(ASC_Max_Allowed_Tmen_R1.GetValueInt(), ASC_Chance_Tmen_R1.GetValueInt(), LvlTriggerman, ASC_Allowed_TmenBoss_R1.GetValue() as Bool, ASC_Max_Allowed_TmenBoss_R1.GetValueInt(), ASC_Chance_TmenBoss_R1.GetValueInt(), LvlTriggerman)
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Tmen_R1.GetValueInt() == 1)
            ;RerollTmenR1()
            RerollCheck(ASC_Reroll_Chance_Tmen_R1.GetValueInt())
        endif
EndFunction

; call this with something like SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
; iMaxSpawnCount = how many spawned actors you want
; iChance = chance for a spawn to happen
; varBaseActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
; bBossAllowed = whether to allow boss
; iMaxBossCount = max number of bosses to roll spawns for
; iBossChance = chance per roll for a boss
; varBossActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)
    int Difficulty = ASC_Main_Difficulty_R1.GetValueInt()
    int iSpawnCounter = 0
    int iPosition = 0
    bool bSpawnSuccess = false
    GroupList = new Actor[0] ; since we don't have 100% chance of spawning actors, we need this to be an accumulating array
    Debug.Notification("Spawning enemies") ; unless we add a function parameter for text this had to be changed to be something generic
        while (iSpawnCounter < iMaxSpawnCount)
            if (Utility.RandomInt(1,100) <= iChance)
                GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,Difficulty), None))
                iPosition = GroupList.Length - 1
                GroupList[iPosition].SetLinkedRef(PatrolMarker)
                Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                ; Spawn = None ; you don't need to do this
                bSpawnSuccess = True
            endif
            iSpawnCounter += 1
        endwhile
        if bBossAllowed == 1
            iSpawnCounter = 0 ; reuse var. memory efficient
            while (iSpawnCounter < iMaxBossCount)
                if (Utility.RandomInt(1,100) <= iBossChance)
                    GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,Difficulty), None))
                    iPosition = GroupList.Length - 1
                    GroupList[iPosition].SetLinkedRef(PatrolMarker)
                    Debug.Notification("Array Add Success. Actor count now: " + iPosition)
                    ;Spawn = None
                    bSpawnSuccess = True
                endif
                iSpawnCounter += 1
            endwhile
        endif
        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
            RerollOnSuccess(bSpawnSuccess)
        endif
EndFunction

Function RerollCheck(int iRerollChance)
    if (Rerolled == false) && (Utility.RandomInt(1,100) <= iRerollChance)
        Spawn()
        Rerolled = true
        Debug.Notification("Rerolling after block")
    else
        Debug.Notification("Reroll on block denied")
    endif
EndFunction


;Function SpawnRaider()
;    SpawnCounter = ASC_Max_Allowed_Raider_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Raider Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Raider_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlRaider, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_RaiderBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_RaiderBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_RaiderBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlRaider, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollRaiderR1()
;    if (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Raider_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Raiders Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Raiders denied")
;    endif
;EndFunction

;Function SpawnGunner()
;    SpawnCounter = ASC_Max_Allowed_Gunner_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Gunner Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Gunner_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlGunner, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_GunnerBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_GunnerBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_GunnerBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlGunner, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollGunnerR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Gunner_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Gunners Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Gunners denied")
;    endif
;EndFunction

;Function SpawnForged()
;    SpawnCounter = ASC_Max_Allowed_Forged_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Forged Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Forged_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlForged, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_ForgedBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_ForgedBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_ForgedBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlForged, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollForgedR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Forged_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Forgeds Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Forgeds denied")
;    endif
;EndFunction

;Function SpawnTmen()
;    SpawnCounter = ASC_Max_Allowed_Tmen_R1.GetValueInt()
;    GroupList = new Actor[GroupMember]
;    Debug.Notification("Tmen Spawning")
;        while (SpawnCounter > 0)
;            if (Utility.RandomInt(1,100) <= ASC_Chance_Tmen_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlTriggerman, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounter = SpawnCounter - 1    
;        endwhile
;        if ASC_Allowed_TmenBoss_R1.GetValueInt() == 1
;            SpawnCounterBoss = ASC_Max_Allowed_TmenBoss_R1.GetValueInt()
;            while (SpawnCounterBoss) > 0
;                if (Utility.RandomInt(1,100) <= ASC_Chance_TmenBoss_R1.GetValueInt())
;                Spawn = Self.PlaceActorAtMe(LvlTriggerman, Utility.RandomInt(1,Difficulty), None)
;                GroupList[GroupMember] = Spawn as Actor
;                GroupList[GroupMember].SetLinkedRef(PatrolMarker)
;                Debug.Notification("Array Success")
;                Spawn = None
;                SpawnSuccess = True
;            endif
;            GroupMember = GroupMember + 1
;            SpawnCounterBoss = SpawnCounterBoss - 1
;            endwhile
;        endif
;        if (ASC_Reroll_Main_R1.GetValueInt() == 1) && (Utility.RandomInt(1,100) <= ASC_Reroll_Main_Chance_R1.GetValueInt())
;            RerollOnSuccess()
;        endif
;EndFunction

;Function RerollTmenR1()
;    if  (Rerolled == false) && (Utility.RandomInt(1,100) <= ASC_Reroll_Chance_Tmen_R1.GetValueInt())
;        Spawn()
;        Rerolled = true
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Rerolling after Tmens Blocked")
;    else
;        ;Difficulty = 0
;        ;WhoToSpawn = 0
;        Debug.Notification("Reroll on blocked Tmens denied")
;    endif
;EndFunction

 

Very informative. I didn't mean to sound like I criticize because ... honestly this is only like a partial code of something much larger, looking at this part of the code tells me nothing. Very soon I will get around to scripting.

 

Are all the scripters here with computer science background? I can't imagine anyone with zero programming skill to start writing scripts like this, overwhelming would be an understatement. What program do you use to write these scripts? Or just simple notepad? Or there are dedicated setup with proper compilers? Does the CK help? To this day I still don't know what CK looks like because that thing always crash on me no matter how many times I installed.

 

I have very little programming experience apart from some HTML and PHP/Wordpress crap that I have to deal with at work, along with the SQL databases a lot our server applications rely on for various departments, like HR and Accounting. Otherwise my talents lie in hardware and mechanics.

 

I started learning to write Papyrus code 3 weeks ago, when I decided that the only way to find out what was wrong the original War of the Commonwealth mod was to learn how to write it myself.

 

I am trying to write a system that is non reliant on threading, something that just "works". I could read most code and understand what it is doing, just never wrote anything complex before. But I like to think I'm not a complete idiot ;).

 

My use of many Global Variables is because users can define all the settings in this mod, absolutely all of them. I figure having many globals is overall safer than having a looping array/script that keeps these variables alive, but that does come at the cost of having them in the save game always (which for them is not so bad compared to other things like objects of sorts).

 

At some point tonight I'll review the codes posted at see what I can learn from them. Thanks a bunch for this. I will credit every member in the final product at the top of the page who ever helped me.

Link to comment
Share on other sites

Very informative. I didn't mean to sound like I criticize because ... honestly this is only like a partial code of something much larger, looking at this part of the code tells me nothing. Very soon I will get around to scripting.

 

Are all the scripters here with computer science background? I can't imagine anyone with zero programming skill to start writing scripts like this, overwhelming would be an understatement. What program do you use to write these scripts? Or just simple notepad? Or there are dedicated setup with proper compilers? Does the CK help? To this day I still don't know what CK looks like because that thing always crash on me no matter how many times I installed.

 

I think it looks overhelming because it`s not your own script. When you create a script, you usually don`t make it that complex straight away. You create a simple base and then add different functions/events/states to improve it. Papyrus often allows you to achieve the same goal using different ways. So each scripter creates his own sctructure depending on his knowledge, experience and prefences. You don`t have to be an IT specialist for this, but any scripting experince can be useful. On my personal view any scripting language is 100 times easier than any real language.

And I also think that getting more experince often allows you to achieve the same things using simplified and concise methods. My old scripts were twice longer than now.

About the CK. If you can make it work, you don`t need any other programs, you can create and compile scripts in the CK.

Sorry for going a bit off topic :)

Link to comment
Share on other sites

I use the CK and Notepad++. I've looked at some suggested programs but i like to keep it simple. I'm glad the compiler tells you, or at least mistly hints where you went wrong or any mistakes you made, otherwise I think this would suck :D.

 

No problems for off topic, doesn't phase me at all.

Link to comment
Share on other sites

@BigandFlabby

 

Thanks going out of your way to rewrite that. Just a few questions if you or anyone wants to answer

 

1. Regarding this:

 

 

 

Function Spawn()
    int WhoToSpawn = Utility.RandomInt(1,4)
        if (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
        elseif (WhoToSpawn == 1)  && (ASC_Allowed_Raider_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Raider_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Raider_R1.GetValueInt())
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Gunner_R1.GetValueInt(), ASC_Chance_Gunner_R1.GetValueInt(), LvlGunner, ASC_Allowed_GunnerBoss_R1.GetValue() as Bool, ASC_Max_Allowed_GunnerBoss_R1.GetValueInt(), ASC_Chance_GunnerBoss_R1.GetValueInt(), LvlGunner)
        elseif (WhoToSpawn == 2)  && (ASC_Allowed_Gunner_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Gunner_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Gunner_R1.GetValueInt())
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Forged_R1.GetValueInt(), ASC_Chance_Forged_R1.GetValueInt(), LvlForged, ASC_Allowed_ForgedBoss_R1.GetValue() as Bool, ASC_Max_Allowed_ForgedBoss_R1.GetValueInt(), ASC_Chance_ForgedBoss_R1.GetValueInt(), LvlForged)
        elseif (WhoToSpawn == 3)  && (ASC_Allowed_Forged_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Forged_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Forged_R1.GetValueInt())
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 1)
            SpawnEnemyActors(ASC_Max_Allowed_Tmen_R1.GetValueInt(), ASC_Chance_Tmen_R1.GetValueInt(), LvlTriggerman, ASC_Allowed_TmenBoss_R1.GetValue() as Bool, ASC_Max_Allowed_TmenBoss_R1.GetValueInt(), ASC_Chance_TmenBoss_R1.GetValueInt(), LvlTriggerman)
        elseif (WhoToSpawn == 4)  && (ASC_Allowed_Tmen_R1.GetValueInt() == 0) && (ASC_Reroll_Allowed_Tmen_R1.GetValueInt() == 1)
            RerollCheck(ASC_Reroll_Chance_Tmen_R1.GetValueInt())
        endif
EndFunction

; call this with something like SpawnEnemyActors(ASC_Max_Allowed_Raider_R1.GetValueInt(), ASC_Chance_Raider_R1.GetValueInt(), LvlRaider, ASC_Allowed_RaiderBoss_R1.GetValue() as Bool, ASC_Max_Allowed_RaiderBoss_R1.GetValueInt(), ASC_Chance_RaiderBoss_R1.GetValueInt(), LvlRaider)
; iMaxSpawnCount = how many spawned actors you want
; iChance = chance for a spawn to happen
; varBaseActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
; bBossAllowed = whether to allow boss
; iMaxBossCount = max number of bosses to roll spawns for
; iBossChance = chance per roll for a boss
; varBossActor = any value that PlaceActorAtMe will accept. ActorBase, LvlList, etc.
Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)

 

 

 

How does this get/list all the variables? Does it just get them in order and when it hits the function fill them automatically? never seen anything like that before, but that's cool if I can do that. That's a shizload of code/functions I don't have to write. There are 12 regions though (so R1 - R12).

 

2. I've made the Globals themselves not Const - so they are saved in the save game. Doesn't that mean I can't refer to them as Const in the script? Or does it just mean I don't have to force an update script to reset the values (in the case that this was attached to a quest, however this particular script is not, it's meant to be attached to a marker). I recall seeing an error with this at some point and avoided it.

 

3. Regarding this bit (not really a question):

 

 

 

Event OnCellDetach()
    SpawnPointLoaded = false
    Utility.Wait(10)
    if (SpawnPointLoaded == false) && (PlayerRef.IsInCombat() == false)
        int iCounter = 0
        while iCounter < GroupList.Length
            GroupList[iCounter].SetLinkedRef(None)
            GroupList[iCounter].DeleteWhenAble()
            iCounter += 1
        endwhile
        GroupList.Clear()
        Debug.Notification("All members cleared from array")
    endif
EndEvent

 

 

 

I got the feeling I could do something like this, I didn't understand it well enough so I just went for the "safest" way and iterate through until nothing was left in the way I understood how. I seen a lot of similar things in WOTC.

 

Apart from that, I never really thought to use prefixes like i, b and f for values, that is something I will take up habit of. I haven't studied into structs yet, just some basics that I haven't fully absorbed ( I understand quests are structs etc). If there is an economical way available to use this to store all the main values(settings) that could be faster than using Globals and Get functions, than that would be of interest. Most of the scripts like this one (random spawn code) are designed to be attached to markers though, I wanted to avoid having anything running all the time (well there are a few things that will require this in future features, but as less as possible I guess).

 

I'm positive I've read somewhere that Add() function is fairlylatent, but don't quote me on that.

Link to comment
Share on other sites

How does this get/list all the variables? Does it just get them in order and when it hits the function fill them automatically? never seen anything like that before, but that's cool if I can do that. That's a shizload of code/functions I don't have to write.

For your Q1 I'm not sure what you're referring to.

 

I replaced all your unique functions that you call to spawn stuff with a single one that you pass all the relevant parameters to. The function definition:

Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor)
;...
EndFunction
Is where the "magic" happens. The variables are the properties you are using. You call it specifying all the parameters that the function will use. This way you have 1 function that does the work based on what you tell it to use. This way you don't have to make a whole lot of unique functions that all do the same thing. If you mean The calls are made from your Spawn() function. I replaced all your calls to the real spawning logic functions with calls to the singular function I made.

 

2. I've made the Globals themselves not Const - so they are saved in the save game. Doesn't that mean I can't refer to them as Const in the script? Or does it just mean I don't have to force an update script to reset the values (in the case that this was attached to a quest, however this particular script is not, it's meant to be attached to a marker). I recall seeing an error with this at some point and avoided it.

For Q2. Using const on the property declaration in the script has a beneficial effect. You mentioned something about allowing for configuration. If you do not declare them const in the script, once the object the script is attached to spawns, then the values are locked in the scripts local memory space. If you had say, a holo tape to config parameters which changed something like the spawn chance. The script won't update it's value because the property is already in script memory and it's not re-polling the game memory for the change. Using const tells the script to use game memory only. This also makes it so things like an primitive (int, bool, etc) cannot be changed except in the ck. Things like a GlobalValue, FormList, or some other things that have their own set/get value functions can be. Anything where you change the value with a = directive won't let you. The only way to change non const values in this case would be to delete and respawn the object the script is running on, or get the reference to the object, cast it as the script type, and directly modify the property. If it's a quest then that's a viable option, otherwise probably not. An example of how that's done is in the Drinking Buddy update scripts from Nuka-World and Far Harbor. Take a look at DLC04DrinkingBuddyScript.psc and you can see how modifying properties of a different object works.

 

Another way of looking at it might be like this: Let's say you got a const property called BOOK. If you wanted to modify BOOK you'd do something like BOOK.ModifyPage(5, "some modification"). This is allowed because BOOK still points to the same object. You're modifying the contents of the object. Doing something like BOOK = A_New_Book. Is not allowed because you're changing the reference of the object. You've specified that BOOK is constantly pointing to the object passed from the CK entry. If this is a GlobalVariable then so be it. The GlobalVariable can be modified and is saved in the save game. But the script is pointing to the reference and not a copy of the reference. There's some qwerks to note here though. If the property is an array, and you modify the array, those changes are lost upon a save reload. The const value of the property tells it to revert to the array specified in the CK. However if the array is a list of globals, and the value of the globals in the array is modified. The values of the globals remains because the array definition hasn't changed.

 

On another note a useful case to declaring a GlobalValue as const in CK makes it essentially read only period. Also making it so that if you change it in the CK it always gets propagated down to the user in game on an existing save. This is useful if you want to write a version upgrade check. You declare 2 globals. One of "current version" and one of "last checked version". Your current version global is tagged const in ck and updated each time you update your mod. You make the other var 0 and leave it alone. The update code you write will handle changing the last checked version global after it deals with whatever you want it to do when upgrades are performed. That value gets saved in your game save. Then in your version check logic you get the value of both and compare. Do whatever you need to do for the version upgrade, then copy the current version to the last checked version and write it. Now the next time it checks if it hasn't been updated it knows. If you release an update you modify the current version one only.

 

Understanding how const affects values to your script takes a bit of experimenting sometimes to wrap your head around it and see what happens. Then it'll hit you and be like "oh yeah, duh!" I typically use const unless there's a very specific need/reason not to. The way you describe how your mod code is going to work it won't matter either way I don't think. However if you changed over to using a struct (which I'll explain in a bit, which you might want to) then you'll absolutely want to use a const for that property. Otherwise all sorts of painful realizations will happen if you try to change things later.

 

Using array dimension increasing functions have typically always had a huge overhead. They usually work by creating a new array of the new needed size, then copying every entry from the old one to the new one and setting the last new value you asked for. The copy process can be cycle time consuming. If you know how large it needs to be ahead of time that's best. The way you're doing things you won't since you can have a chance that a spawn doesn't happen. Now that can be handled using the Add() function or you can define a max size array, then keep track of how many you put in it. Depending on the size there will be different performance impacts. If it's less than 20 then it's unlikely there will be noticeable impact either way. If you were doing 100, then that might be a different story. You'd need to do some profiling tests to see what the impact would be.

 

Another point of performance concern is going to be the PlaceActorAtMe function. It's a blocking function. It waits until the actor is fully spawned before returning. There's some alternatives that involve making a threading system to spawn multiple actors simultaneously. There's a page on the wiki detailing a similar need in Skyrim Creating_Multithreaded_Skyrim_Mods most of it still applies. Some of the functions would have to be changed because they rely on the SKSE which has functions F4SE does not (yet). But there are some FO4 functions that could pull off the same thing. Specifically one that comes to mind is the RegisterForModEvent and the accompanying event triggering function. Those are built into SKSE. FO4 has CustomEvents which can be used to accomplish the same thing. And those are built into the FO4 system so no F4SE is required. That gets into some pretty advanced scripting tactics though so maybe not something to jump into right now.

 

Ok, now for the struct stuff. The reason I think this might be better to do is you can modify the spawning script in a way that you never have to touch it when you want to support more and more types of actors or different settings for different lists. So it would go something like this:

 

 

Struct ActorTypeStruct
    ActorBase LvlActorBase
    ActorBase LvlActorBossBase
    GlobalVariable ASC_Allowed
    GlobalVariable ASC_Allowed_Boss
    GlobalVariable ASC_Max_Allowed
    GlobalVariable ASC_Max_Allowed_Boss
    GlobalVariable ASC_Chance
    GlobalVariable ASC_Chance_Boss
    GlobalVariable ASC_Reroll_Allowed
    GlobalVariable ASC_Reroll_Chance
EndStruct

ActorTypeStruct[] Property ActorTypes Auto Const ; this is const so that any additional entries we put in the array are passed to the script whenever it checks the contents of the array. the only time this will be inaccrurate is if a save was created in the middle of the spawn function running and then the mod updated to have changes to the array.

Function Spawn()
    int iNumSpawnTypes = ActorTypes.Length ; how many types of spawning actors can we support
    int iWhoToSpawn = Utility.RandomInt(1,iNumSpawnTypes) ; changed to use the size of our array of actor types
    ActorTypeStruct spawnDetails = ActorTypes[iWhoToSpawn]
    if (spawnDetails.ASC_Allowed.GetValueInt() == 1)
        SpawnEnemyActors(spawnDetails.ASC_Max_Allowed.GetValueInt(), spawnDetails.ASC_Chance.GetValueInt(), spawnDetails.LvlActorBase, spawnDetails.ASC_Allowed_Boss.GetValue() as Bool, spawnDetails.ASC_Max_Allowed_Boss.GetValueInt(), spawnDetails.ASC_Chance_Boss.GetValueInt(), spawnDetails.LvlActorBossBase)
    elseif (spawnDetails.ASC_Allowed.GetValueInt() == 0) && (spawnDetails.ASC_Reroll_Allowed.GetValueInt() == 1)
        RerollCheck(spawnDetails.ASC_Reroll_Chance.GetValueInt())
    ;else
        ; should there be something to handle a case where we're not allowed and have no reroll allowance? the way this is right now, if both were 0 then nothing would get spawned and the script stalls until the cell unloads and the spawn is called again on the new cell load.
    endif
EndFunction

 

The way this would be used now is you would attach this to a marker. Setup the parameters for that marker to be for wherever it is. You mentioned multiple regions. So I assume this is how it would go. Let's say you put this marker in concord. Maybe you'd want just gunners and raiders there. Then when you're setting up the properties for the script on that marker you just enter the details for the gunners and raiders. You won't actually be able to auto fill them since it's now a struct array you'll have to enter each of the global names and leveled list names in the ck manually. One option might be to make a default object and put all the settings on it. Then when you create the marker reference by dragging it into the render window, edit the properties removing any that you don't want in the array. That way you just enter them once, then remove them which is easily done just clicking remove on each array entry.

 

The complication comes up when you want to add another type to a bunch of references at once. I'm not aware of any way to do that easily. Though even if you used regular properties it can't be done that way either. You'd have to update each reference individually.

 

You can see an example of how the struct array is defined in the CK by looking at the Drinking Buddy dialogue quest. Something I'd like to note here though is the explicit use of a *non-const* array. In this circumstance it is required in order for the DLC's to inject new recipes into the array and retain those changes. If the array was declared const, the DLC's could still do an injection and it would remain until the current running game was terminated. As soon as a new game is started, or a save reloaded the fact it's a const takes effect and the values are revered to the entries input in the CK. Any added values are dumped. In order to work around this, anything that was injecting values would have to re-run on every game load to put them back in. I personally just ran into this such issue with a script I had to write interfacing to the WorkshopParentScript. I was giving it additional food types to produce on the daily food production routine. My script has to re-run on every game load and put it back in because the value is defined in the script as a const. So it keeps reverting the changes.

 

Since the way your script is used will be on different markers in different locations then programmatically adding additional values to the array probably isn't desired. In which case making them const would be suggested.

 

Regarding variable names. I forget the proper term that's used to explain how to call them. But I personally find it easy (and a reminder) of putting a type identifier in the front. That way I won't forget the bIsAllowed is a boolean. Though sometimes the name is obvious. Something like "theone" is pretty ambiguous. However "iTheOne" would tell me it should be an integer, "bTheOne" a boolean, "sTheOne" a string etc. You'll see in a lot of professional code it's done that way. Makes it a whole lot easier for multiple programmers to work on each other code because they know what variables are supposed to have what content. If it's your own code then you can do whatever you want. But I've caught myself in a jam using mis typed vars on more than one occassion when I was a super newbie at writing code. Papyrus is fairly forgiving, but C/C++/Java not so much.

 

I hope I didn't make this too confusing or frustrating. If you have any more questions go right ahead please ask.

Edited by BigAndFlabby
Link to comment
Share on other sites

Ill read over and post any thoughts

 

That is about the best thing anyone ever done for me on the internet, amazing stuff. I think I'm with the program now, I can keep referring back to this when I need.

 

I'm surr many a Nexus user will be happy to read this too, its practically a small wiki!

 

Thank you very much for sharing your knowledge!

 

I'll post some updates on this thread as well at a later stage when I start implementing other systems to the mod as well

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...