vkz89q Posted May 29, 2017 Share Posted May 29, 2017 (edited) Quest alias packages only work while the actor is in the alias. When they are out of the alias, all "stuff" from quest alias is lost. That's why I love quests myself, I can do how many I want, I can turn them on/off when I want, I can have one quest to give actor some keyword/package/faction/etc etc and turn it off whenever I want. Updating is easy just turn off quests and turn them back on and even totally modified scripts are updated. Even uninstalling is easy, just turn off all the quests and disable/delete every alias. It's pretty easy to make good patrol/travel packages just using stuff ingame already, you can have already existing Xmarkers as destinations for example. What you could do with your system: You could have each spawnpoint have their own keyword that they add to the actors that they spawn, then you could start quest after they are spawned, finding matching reference and looking for actors that have your keyword. That way you could have own quest for each "spawn area" and then the quest alias(collection alias?) would have packages. Or, instead of starting/stopping the quest all time to fill aliases, there propably is function to add the actor to RefCollectionAlias in quest that is already running. You could also remove them from the alias in OnDying event in the alias, and mark them for delete etc.. But, honestly, I think just using quest for everything is super smooth. I'm not saying there's anything wrong in your system, actually I like it.. it's just a personal preference for quests, lol. I know in the other hand that some people absolutely hate quests and they do have their own problems too, like sometimes for no real reason, alias not filling or quest not starting etc. Just throwing out ideas. Waiting to see your new system in action :smile: Edited May 29, 2017 by vkz89q Link to comment Share on other sites More sharing options...
kitcat81 Posted May 29, 2017 Share Posted May 29, 2017 (edited) Another thing that crosses my mind here, seems the OnCellAttach/detach works within the ugrids range, I have not implemented anything in the case that you happen to "pass by" a cell very quickly. I think in WOTC, a lot of CTDs happen when the player moves across a lot of cells and the script isn't keeping up, and by the time it elects a place to MoveTo the spawned actors, they're out of the loaded area (probably why I see up to 5 "could not find navmesh with MoveToNearestNavmeshLocation function before most regular crashes). I think my script is a bit faster though and from what I can see (given I don't intend on having any more than 2 points per cell mostly) it will be finished before that could happen. Just thoughts based on what I have seen anyway On an entirely separate note, it's in the back of my head that if I set up a patrol marker network, this will be persistent. I wanted to set it up so that other modders could also use it, a bit of a convenience feature, as well as to address the problem that WOTC sees with it's patrol AI packages and the fact I don't want to modify/have too many custom ActorBases/LvlNPCs (for cross compatibility). But for shiz and giggles I was looking at how it could be possible to dynamically give each NPC a "patrol" package. Apparently the only way to do this is ForceRefTo() a reference alias and assign the package (maybe having to force with EvaluatePackage()). I wonder though, if I had a single Ref Alias on the quest, and I kept forcing NPCs in and out of it and assigning them the package, would that work for the way things are set up (possibly multiple "spawnpoints" sending NPCs to the RefAlias, will they keep package after they are out of it, how to remove the very last NPC fro mthe RefAlias when they've all been through etc etc) Just reading that back to myself, I'll stay with the patrol marker network lol. Just curious how far I can "rev the engine" lol. On that note, I kinda thought of a way to make them non-persistent using an uninstall script. If my whole network is linked from one end of the CW to another, I could do some code that specifically starts at one of them, and keeps getting the next linked ref and setting it to none. Until they are all unlinked in that savegame, so users can have peace of mind. Suppose that would take some very careful placement though, but hey at least I think this way :smile: To give npc a package you can also apply an alias to the npc. It`s not the same as "ForceRefTo". There is a function "ApplyToRef" and "RemoveFromRef". It allows to apply alias data ( keywords, packages, factions) to multiple npc without filling the alias( means you can use 1 Reference Alias for all your npc in the same time). To do this you need to check "can be applied to non aliased refs" in the alias file. The only thing about this is that applying the data does not make the npc persistent and the data can be auto removed on unload sometimes. Edited May 29, 2017 by kitcat81 Link to comment Share on other sites More sharing options...
TheCourier13 Posted May 29, 2017 Share Posted May 29, 2017 (edited) Another thing that crosses my mind here, seems the OnCellAttach/detach works within the ugrids range, I have not implemented anything in the case that you happen to "pass by" a cell very quickly. I think in WOTC, a lot of CTDs happen when the player moves across a lot of cells and the script isn't keeping up, and by the time it elects a place to MoveTo the spawned actors, they're out of the loaded area (probably why I see up to 5 "could not find navmesh with MoveToNearestNavmeshLocation function before most regular crashes). I think my script is a bit faster though and from what I can see (given I don't intend on having any more than 2 points per cell mostly) it will be finished before that could happen. Just thoughts based on what I have seen anyway I can think of a mod that might cause that, drive-able motorcycles. Especially if you have the throttle going and you're moving fast. I know I can move faster than the objects can load. For example, I can get to the Diamond City gate for the first time when meeting Piper and the gate and her haven't loaded yet and it takes a couple of seconds for them to appear. So when traveling fast enough via the mod, a player might be able to cross several grids much faster than would otherwise be possible. Just thought I'd mention that as that could cause an issue from what you're saying. Edited May 29, 2017 by TheCourier13 Link to comment Share on other sites More sharing options...
SMB92 Posted May 29, 2017 Author Share Posted May 29, 2017 Quest alias packages only work while the actor is in the alias. When they are out of the alias, all "stuff" from quest alias is lost. That's why I love quests myself, I can do how many I want, I can turn them on/off when I want, I can have one quest to give actor some keyword/package/faction/etc etc and turn it off whenever I want. Updating is easy just turn off quests and turn them back on and even totally modified scripts are updated. Even uninstalling is easy, just turn off all the quests and disable/delete every alias. It's pretty easy to make good patrol/travel packages just using stuff ingame already, you can have already existing Xmarkers as destinations for example. What you could do with your system: You could have each spawnpoint have their own keyword that they add to the actors that they spawn, then you could start quest after they are spawned, finding matching reference and looking for actors that have your keyword. That way you could have own quest for each "spawn area" and then the quest alias(collection alias?) would have packages. Or, instead of starting/stopping the quest all time to fill aliases, there propably is function to add the actor to RefCollectionAlias in quest that is already running. You could also remove them from the alias in OnDying event in the alias, and mark them for delete etc.. But, honestly, I think just using quest for everything is super smooth. I'm not saying there's anything wrong in your system, actually I like it.. it's just a personal preference for quests, lol. I know in the other hand that some people absolutely hate quests and they do have their own problems too, like sometimes for no real reason, alias not filling or quest not starting etc. Just throwing out ideas. Waiting to see your new system in action :smile:Thanks for following the thread :). Personally I quite like it the way it is. But I guess one reason for that is I haven't fully figured out how to run the function from the quest on the marker properly :D. But I still just like it how it is anyway :P. You could say I like a mix of the Quest + script. The quest now contains all the timers and Formlist events, first install stages and uninstall stages as well, and I am thinking to dynamically fill the array of structs now if that's possible (to achieve only having what's allowed to spawn to be present there). I also like being able to copy and replace in the marker script, for variations etc. To give npc a package you can also apply an alias to the npc. It`s not the same as "ForceRefTo". There is a function "ApplyToRef" and "RemoveFromRef". It allows to apply alias data ( keywords, packages, factions) to multiple npc without filling the alias( means you can use 1 Reference Alias for all your npc in the same time). To do this you need to check "can be applied to non aliased refs" in the alias file. The only thing about this is that applying the data does not make the npc persistent and the data can be auto removed on unload sometimes. That is perfect. I'll look into it asap. If this works I can just assign them on spawn like the way they get linked to a patrol marker and unlinked on unload. Link to comment Share on other sites More sharing options...
SMB92 Posted May 30, 2017 Author Share Posted May 30, 2017 Another thing that crosses my mind here, seems the OnCellAttach/detach works within the ugrids range, I have not implemented anything in the case that you happen to "pass by" a cell very quickly. I think in WOTC, a lot of CTDs happen when the player moves across a lot of cells and the script isn't keeping up, and by the time it elects a place to MoveTo the spawned actors, they're out of the loaded area (probably why I see up to 5 "could not find navmesh with MoveToNearestNavmeshLocation function before most regular crashes). I think my script is a bit faster though and from what I can see (given I don't intend on having any more than 2 points per cell mostly) it will be finished before that could happen. Just thoughts based on what I have seen anyway I can think of a mod that might cause that, drive-able motorcycles. Especially if you have the throttle going and you're moving fast. I know I can move faster than the objects can load. For example, I can get to the Diamond City gate for the first time when meeting Piper and the gate and her haven't loaded yet and it takes a couple of seconds for them to appear. So when traveling fast enough via the mod, a player might be able to cross several grids much faster than would otherwise be possible. Just thought I'd mention that as that could cause an issue from what you're saying. This is a good point in a sense, but I won't be accommodating for this kind of thing :D. I wouldn't use it myself, vanilla would probably crash with it (I have tried the mod though). I will look at failsafes for when players are in Vertibirds however, that is a big one from WOTC people had a problem with (and there are some vanilla random spawns, believe it or not) I'm excited about this ApplyToRef() function kitcat81 mentioned, I'd like to see what I can do that Link to comment Share on other sites More sharing options...
SMB92 Posted June 3, 2017 Author Share Posted June 3, 2017 So as it would be, the "accumating array" is not working at all. I cannot for the life of me see why though. The Debug Note always returns iPosition as -1 after every spawn, even though actors are spawning just fine. Therefore, cleanup function does nothing either. I've tried both the Add function originally and casting it in but results are identical. I'm not sure what to do right now, this is a fundamental flaw in the mod if these spawned actors cannot be applied to the arrays This would have been the final version of the script: Scriptname ASC_SP_Random_R1 extends ObjectReference{Region 1 Random Type SpawnPoint Marker Script} import ASC_MainStruct ASC_MasterRandomQuestScript Property MasterQuest Auto ConstGlobalVariable Property ASC_Main_ModEnabled Auto ConstGlobalVariable Property ASC_Main_Enabled_Random_R1 Auto ConstGlobalVariable Property ASC_Main_Random_Chance_R1 Auto ConstGlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto ConstGlobalVariable Property ASC_Main_Random_EnableOnSpawnBlock_R1 Auto ConstGlobalVariable Property ASC_Main_Difficulty_R1 Auto ConstGlobalVariable Property ASC_Main_Reroll_Limit_R1 Auto ConstFormList Property ASC_ResetList_R1 Auto ConstActor Property PlayerRef Auto Const bool bSpawnPointLoaded = false ;bool for cleanup eventbool Group2Loaded = falsebool Group3Loaded = falseint iRerollLimit = 0int iRerollCount = 0Actor[] GroupList ; Array to track and cleanup all aspawned actors later, as well as apply new features in futureActor[] GroupList2 ;if 1st reroll attempt successful, assign spawns hereActor[] Grouplist3 ;2nd reroll attempt actors here, limit of rerolls is 2, hardcapped. Event OnCellAttach()bSpawnPointLoaded = trueif (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_Enabled_Random_R1.GetValueInt() == 1)SPActivate()Debug.Notification("All Functions Done")elseDebug.Notification("SpawnPoint Failed")endifEndEvent Event OnCellDetach() ;idea is to stop cleanup from happening while potentially in combat with group while potentially leaving the cell their spawnmarker is in. Cleans up all data generated during spawn. bSpawnPointLoaded = false Utility.Wait(10)while (PlayerRef.IsInCombat()) ; also may not be necessary but reference for sanity check.; Do nothing until combat is over, then check if we are still in the loaded areaUtility.Wait(0.1)endwhileif bSpawnPointLoaded == false ;if we are not in the loaded area, start cleaning up. Event will be called again if we aren't so no worriesint iCounter = 0while iCounter < GroupList.Length;GroupList[iCounter].SetLinkedRef(None) ;old, prior to using AI package(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList[iCounter])GroupList[iCounter].DeleteWhenAble()iCounter += 1endwhileiCounter = 0endifif Group2Loaded == trueint iCounter = 0while iCounter < GroupList2.Length;GroupList2[iCounter].SetLinkedRef(None) ;old, prior to using AI package(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList2[iCounter])GroupList2[iCounter].DeleteWhenAble()iCounter += 1endwhileiCounter = 0endifif Group3Loaded == trueint iCounter = 0while iCounter < GroupList3.Length;GroupList3[iCounter].SetLinkedRef(None) ;old, prior to using AI package(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList3[iCounter])GroupList3[iCounter].DeleteWhenAble()iCounter += 1endwhileiCounter = 0endifDebug.Notification("SpawnPoint cleaned up")EndEvent Function SPActivate() ;This may not be necessary but it looks neat. Main roll chance and disabling happens hereiRerollLimit = ASC_Main_Reroll_Limit_R1.GetValueInt()int iChanceToSpawn = Utility.RandomInt(1,100) if (iChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt())Debug.Notification("Spawn Function called on script!") Self.Disable() ASC_ResetList_R1.AddForm(Self)Spawn()Debug.Notification("Spawn function has completed") elseif (iChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1) Self.Disable() ASC_ResetList_R1.AddForm(Self)Debug.Notification("SpawnPoint failed and disabled") else Debug.Notification("SpawnPoint failed but still armed") endifEndFunction Function Spawn()iRerollLimit = ASC_Main_Reroll_Limit_R1.GetValueInt() int iNumSpawnTypes = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1.Length ; how many types of spawning actors can we supportint iWhoToSpawn = Utility.RandomInt(0,iNumSpawnTypes) ; changed to use the size of our array of actor types ActorTypeStruct spawnDetails = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1[iWhoToSpawn]bool bAllowedToSpawn = spawnDetails.ASC_Allowed.GetValueInt() as Boolif bAllowedToSpawn ;Is the elected group allowed to spawn?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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt())elseif !bAllowedToSpawnBlockedOnFirstSpawn() ;For the first election, we allow the player to potentially rearm the spawnpoint if it fails here. But not in rerolls. spawnDetails = NoneendifEndFunction Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance)Debug.Notification("Spawning Enemy Actors") ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0)Actor Spawned = Noneint iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0int iPosition = 0 ;bool bSpawnSuccess = false ;obsolete, was for checking old reroll system was allowed 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)Actor akSpawned = Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]);Other way to do it, left for referenceGroupList[iPosition] = akSpawned ;GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList[iPosition]) ;new system for patrols NOTE change to travel aliases endif iSpawnCounter += 1 endwhileDebug.Notification("Array Add Success. Actor count now: " + iPosition)Debug.Notification("Non Boss Group Spawned") if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])GroupList[iPosition] = Spawned;GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker)(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList[iPosition]) endif iSpawnCounter += 1 endwhile endifDebug.Notification("Array Add Success. Actor count now: " + iPosition)Debug.Notification("Enemies should be spawned")if (iRerollAllowed == 1) && (iRerollCount < iRerollLimit) && (Utility.RandomInt(1,100) <= iRerollChance) ;iRerollLimit is a universal value, only 1 global per region, not per typeSpawnAnotherGroup()iRerollCount += 1Debug.Notification("Rerolling for another group!")elseDebug.Notification("Reroll failed")endifEndFunction Function SpawnAnotherGroup() ;this simulates spawning battles between two groups, or possibly a really large group of the same enemy. int iNumSpawnTypes = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1.Length ; how many types of spawning actors can we supportint iWhoToSpawn = Utility.RandomInt(0,iNumSpawnTypes) ; changed to use the size of our array of actor types ActorTypeStruct spawnDetails = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1[iWhoToSpawn]bool bAllowedToSpawn = spawnDetails.ASC_Allowed.GetValueInt() as Boolif bAllowedToSpawn ;Is the elected group allowed to spawn?SpawnMoreEnemyActors(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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt())elsespawnDetails = none ;just to give the else function something to doendifEndFunction Function SpawnMoreEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance) ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0)int iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0int iPosition = 0 GroupList2 = new Actor[iPosition] ; 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) GroupList2.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker) old sytem(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList2[iPosition]) ;new system for patrols endif iSpawnCounter += 1 endwhileGroup2Loaded = true if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) GroupList2.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList2[iPosition].SetLinkedRef(kPatrolMarker)(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList2[iPosition]) endif iSpawnCounter += 1 endwhile endifif (iRerollAllowed == 1) && (iRerollCount < iRerollLimit) && (Utility.RandomInt(1,100) <= iRerollChance)SpawnYetAnotherGroup()iRerollCount += 1else;Nothing, exit functionendifEndFunction Function SpawnYetAnotherGroup() ;this simulates spawning battles between two groups, or possibly a really large group of the same enemy. int iNumSpawnTypes = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1.Length ; how many types of spawning actors can we supportint iWhoToSpawn = Utility.RandomInt(0,iNumSpawnTypes) ; changed to use the size of our array of actor types ActorTypeStruct spawnDetails = (MasterQuest as ASC_MasterRandomQuestScript).ActorTypesRandomR1[iWhoToSpawn]bool bAllowedToSpawn = spawnDetails.ASC_Allowed.GetValueInt() as Boolif bAllowedToSpawnSpawnEvenMoreEnemyActors(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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt())elsespawnDetails = none ;just to give the else function something to doendifEndFunction Function SpawnEvenMoreEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance) ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0)int iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0int iPosition = 0 GroupList3 = new Actor[iPosition] ; 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) GroupList3.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList3[iPosition].SetLinkedRef(kPatrolMarker) old sytem(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList3[iPosition]) ;new system for patrols endif iSpawnCounter += 1 endwhileGroup3Loaded = true if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) GroupList3.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker)(MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList3[iPosition]) endif iSpawnCounter += 1 endwhile;Nothing, exit function endifEndFunction Function BlockedOnFirstSpawn() ;reenable the spawnpoint upon not being allowed to spawn 1st elected group, if user settings allow this to happenif (ASC_Main_Random_EnableOnSpawnBlock_R1.GetValueInt() == 1)Self.Enable()else;Nothing, exit functionendifEndFunction Link to comment Share on other sites More sharing options...
SMB92 Posted June 3, 2017 Author Share Posted June 3, 2017 Okay so thanks to BigandFlabby again, I've moved over to the dynamic struct/array of structs. However the same problem remains as my last post with the SpawnEnemyActors function. It seems to me that if the first actor fails to spawn, they all do, and if the first is a success, it spawns the maximum amount. Sometimes the debug note says that it's size (iPosition) is -1, sometimes 0. When it says -1, there is usually 4 base actors there or 1 boss. Or nothing. So far, there has never been anything when it reports the size as 0. Wtf. Here would be the "dynamic" versions of the scripts (above has the array of structs as a full property) Quest Scriptname ASC_MasterRandomQuestScript extends Quest ;Import ASC_MainStruct ;Import ASC_DynMainStruct Struct ActorTypeStruct ActorBase LvlActorBase ActorBase LvlActorBossBase 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 GlobalVariable ASC_EZ_Lvl EndStruct ;Quest Property ASC_MasterRandomQuest Auto ;Quest Property Min01 Auto ;Quest Property MinRecruit00 Auto ;Message property ASC_StartUpMenu Auto Const ;Message property ASC_StartConfirmOnDefer Auto Const GlobalVariable Property ASC_Main_ModEnabled Auto Const GlobalVariable Property ASC_Timer_Reset_R1 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R2 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R3 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R4 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R5 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R6 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R7 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R8 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R9 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R10 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R11 Auto Const ;GlobalVariable Property ASC_Timer_Reset_R12 Auto Const FormList Property ASC_ResetList_R1 Auto Const ;FormList Property ASC_ResetList_R2 Auto Const ;FormList Property ASC_ResetList_R3 Auto Const ;FormList Property ASC_ResetList_R4 Auto Const ;FormList Property ASC_ResetList_R5 Auto Const ;FormList Property ASC_ResetList_R6 Auto Const ;FormList Property ASC_ResetList_R7 Auto Const ;FormList Property ASC_ResetList_R8 Auto Const ;FormList Property ASC_ResetList_R9 Auto Const ;FormList Property ASC_ResetList_R10 Auto Const ;FormList Property ASC_ResetList_R11 Auto Const ;FormList Property ASC_ResetList_R12 Auto Const ReferenceAlias Property ASC_PatrolPackageAlias Auto ActorBase property LvlRaider Auto Const ActorBase property LvlRaiderBoss Auto Const GlobalVariable Property ASC_Allowed_Raider_R1 Auto Const GlobalVariable Property ASC_Allowed_Boss_Raider_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Raider_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Boss_Raider_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Raider_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Boss_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_EZ_Raider_R1 Auto Const ActorBase property LvlGunner Auto Const ActorBase property LvlGunnerBoss Auto Const GlobalVariable Property ASC_Allowed_Gunner_R1 Auto Const GlobalVariable Property ASC_Allowed_Boss_Gunner_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Gunner_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Boss_Gunner_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Gunner_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Boss_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_EZ_Gunner_R1 Auto Const ActorBase property LvlForged Auto Const ActorBase property LvlForged_Boss Auto Const GlobalVariable Property ASC_Allowed_Forged_R1 Auto Const GlobalVariable Property ASC_Allowed_Boss_Forged_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Forged_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Boss_Forged_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Forged_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Boss_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_EZ_Forged_R1 Auto Const ActorBase property LvlTriggerman Auto Const GlobalVariable Property ASC_Allowed_Triggerman_R1 Auto Const GlobalVariable Property ASC_Allowed_Boss_Triggerman_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Triggerman_R1 Auto Const GlobalVariable Property ASC_Max_Allowed_Boss_Triggerman_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Triggerman_R1 Auto Const GlobalVariable Property ASC_Chance_Spawn_Boss_Triggerman_R1 Auto Const GlobalVariable Property ASC_Reroll_Allowed_Triggerman_R1 Auto Const GlobalVariable Property ASC_Reroll_Chance_Triggerman_R1 Auto Const GlobalVariable Property ASC_EZ_Triggerman_R1 Auto Const ;ActorTypeStruct[] Property ActorTypesRandomR1 Auto Const ;ActorTypeStruct[] Property ActorTypesR2 Auto Const ;ActorTypeStruct[] Property ActorTypesR3 Auto Const ;ActorTypeStruct[] Property ActorTypesR4 Auto Const ;ActorTypeStruct[] Property ActorTypesR5 Auto Const ;ActorTypeStruct[] Property ActorTypesR6 Auto Const ;ActorTypeStruct[] Property ActorTypesR7 Auto Const ;ActorTypeStruct[] Property ActorTypesR8 Auto Const ;ActorTypeStruct[] Property ActorTypesR9 Auto Const ;ActorTypeStruct[] Property ActorTypesR10 Auto Const ;ActorTypeStruct[] Property ActorTypesR11 Auto Const ;ActorTypeStruct[] Property ActorTypesR12 Auto Const EncounterZone[] Property EzArray Auto Const ActorTypeStruct[] ActorTypesRandomR1 bool bTimersRunning = false Int iResetTimerR1 = 1 ;Int iResetTimerR2 = 2 ;Int iResetTimerR3 = 3 ;Int iResetTimerR4 = 4 ;Int iResetTimerR5 = 5 ;Int iResetTimerR6 = 6 ;Int iResetTimerR7 = 7 ;Int iResetTimerR8 = 8 ;Int iResetTimerR9 = 9 ;Int iResetTimerR10 = 10 ;Int iResetTimerR11 = 11 ;Int iResetTimerR12 = 12 ;Event OnInit() ;StartUpMenu() ;EndEvent Event OnStageSet(int auiStageID, int auiItemID) ;if (auiStageID == 5) ; Utility.Wait(5.0) ; if MinRecruit00.GetStage() < 500 ; while MinRecruit00.GetStage() < 500 ; Utility.Wait(30.0) ; endwhile ; endif ; if MinRecruit00.GetStage() == 500 ; Confirm() ; endif if (auiStageID == 10) && (bTimersRunning == false) StartTimerGameTime(ASC_Timer_Reset_R1.GetValueInt(), iResetTimerR1) Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R2.GetValueInt(), iResetTimerR2) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R3.GetValueInt(), iResetTimerR3) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R4.GetValueInt(), iResetTimerR4) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R5.GetValueInt(), iResetTimerR5) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R6.GetValueInt(), iResetTimerR6) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R7.GetValueInt(), iResetTimerR7) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R8.GetValueInt(), iResetTimerR8) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R9.GetValueInt(), iResetTimerR9) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R10.GetValueInt(), iResetTimerR10) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R11.GetValueInt(), iResetTimerR11) ;Utility.Wait(0.1) ;StartTimerGameTime(ASC_Timer_Reset_R12.GetValueInt(), iResetTimerR12) bTimersRunning = true FillArrays() ASC_Main_ModEnabled.SetValue(1.0) Debug.Notification("Mod is Enabled") elseif (auiStageID == 100) ASC_Main_ModEnabled.SetValue(0.0) CancelTimerGameTime(iResetTimerR1) ;CancelTimerGameTime(iResetTimerR2) ;CancelTimerGameTime(iResetTimerR3) ;CancelTimerGameTime(iResetTimerR4) ;CancelTimerGameTime(iResetTimerR5) ;CancelTimerGameTime(iResetTimerR6) ;CancelTimerGameTime(iResetTimerR7) ;CancelTimerGameTime(iResetTimerR8) ;CancelTimerGameTime(iResetTimerR9) ;CancelTimerGameTime(iResetTimerR10) ;CancelTimerGameTime(iResetTimerR11) ;CancelTimerGameTime(iResetTimerR12) bTimersRunning = false ClearAllArrays() Debug.Notification("Mod is uninstalled") endif EndEvent Event OnTimerGameTime(int aiTimerID) if aiTimerID == iResetTimerR1 Int iIndex = 0 While (iIndex < ASC_ResetList_R1.GetSize()) ObjectReference kMarker = ASC_ResetList_R1.GetAt(iIndex) as ObjectReference kMarker.EnableNoWait() iIndex += 1 EndWhile ASC_ResetList_R1.Revert() Debug.Notification("R1 Spawn Markers Reset") ;elseif aiTimerID == iResetTimerR2 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R2.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R2.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R2.Revert() ;elseif aiTimerID == iResetTimerR3 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R3.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R3.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R3.Revert() ;elseif aiTimerID == iResetTimerR4 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R4.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R4.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R4.Revert() ;elseif aiTimerID == iResetTimerR5 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R5.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R5.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R5.Revert() ;elseif aiTimerID == iResetTimerR6 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R6.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R6.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R6.Revert() ;elseif aiTimerID == iResetTimerR7 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R7.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R7.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R7.Revert() ;elseif aiTimerID == iResetTimerR8 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R8.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R8.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R8.Revert() ;elseif aiTimerID == iResetTimerR9 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R9.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R9.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R9.Revert() ;elseif aiTimerID == iResetTimerR10 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R10.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R10.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R10.Revert() ;elseif aiTimerID == iResetTimerR11 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R11.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R11.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R11.Revert() ;elseif aiTimerID == iResetTimerR12 ; Int iIndex = 0 ; While (iIndex < ASC_ResetList_R12.GetSize()) ; ObjectReference kMarker = ASC_ResetList_R12.GetAt(iIndex) as ObjectReference ; kMarker.EnableNoWait() ; iIndex += 1 ; EndWhile ; ASC_ResetList_R12.Revert() endif EndEvent ;Function StartUpMenu() ; int iMenu = -1 ; While (iMenu != 4) ; iMenu = -1 ; iMenu = ASC_StartUpMenu.Show() ; If (iMenu == 0) ; ASC_MasterRandomQuest.SetStage(10) ; iMenu = 4 ; elseIf (iMenu == 1) ; ASC_MasterRandomQuest.SetStage(1) ; iMenu = 4 ; elseIf (iMenu == 2) ; ASC_MasterRandomQuest.SetStage(5) ; iMenu = 4 ; elseif (iMenu == 4) ; EndIf ; EndWhile ;EndFunction ;Function Confirm() ; int iMenu = -1 ; While (iMenu != 2) ; iMenu = -1 ; iMenu = ASC_StartConfirmOnDefer.Show() ; If (iMenu == 0) ; ASC_MasterRandomQuest.SetStage(10) ; iMenu = 2 ; elseif (iMenu == 1) ; ASC_MasterRandomQuest.SetStage(1) ; iMenu = 2 ; endif ; EndWhile ;EndFunction Function FillArrays() ;Fills the struct arrays with only the types allowed. Called by update event and initial initialization of the mod. May need to be called every startup. Int iPosition = 0 ActorTypesRandomR1 = new ActorTypeStruct[iPosition] if ASC_Allowed_Raider_R1.GetValueInt() == 1 ActorTypesRandomR1[iPosition].LvlActorBase = LvlRaider ActorTypesRandomR1[iPosition].LvlActorBossBase = LvlRaiderBoss ActorTypesRandomR1[iPosition].ASC_Allowed_Boss = ASC_Allowed_Boss_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed = ASC_Max_Allowed_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed_Boss = ASC_Max_Allowed_Boss_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Chance = ASC_Chance_Spawn_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Chance_Boss = ASC_Chance_Spawn_Boss_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Allowed = ASC_Reroll_Allowed_Raider_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Chance = ASC_Reroll_Chance_Raider_R1 ActorTypesRandomR1[iPosition].ASC_EZ_Lvl = ASC_EZ_Raider_R1 iPosition += 1 else ;Nothing, ActorType does not enter the array endif if ASC_Allowed_Gunner_R1.GetValueInt() == 1 ActorTypesRandomR1[iPosition].LvlActorBase = LvlGunner ActorTypesRandomR1[iPosition].LvlActorBossBase = LvlGunnerBoss ActorTypesRandomR1[iPosition].ASC_Allowed_Boss = ASC_Allowed_Boss_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed = ASC_Max_Allowed_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed_Boss = ASC_Max_Allowed_Boss_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Chance = ASC_Chance_Spawn_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Chance_Boss = ASC_Chance_Spawn_Boss_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Allowed = ASC_Reroll_Allowed_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Chance = ASC_Reroll_Chance_Gunner_R1 ActorTypesRandomR1[iPosition].ASC_EZ_Lvl = ASC_EZ_Gunner_R1 iPosition += 1 else ;Nothing, ActorType does not enter the array endif if ASC_Allowed_Forged_R1.GetValueInt() == 1 ActorTypesRandomR1[iPosition].LvlActorBase = LvlForged ActorTypesRandomR1[iPosition].LvlActorBossBase = LvlForged_Boss ActorTypesRandomR1[iPosition].ASC_Allowed_Boss = ASC_Allowed_Boss_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed = ASC_Max_Allowed_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed_Boss = ASC_Max_Allowed_Boss_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Chance = ASC_Chance_Spawn_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Chance_Boss = ASC_Chance_Spawn_Boss_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Allowed = ASC_Reroll_Allowed_Forged_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Chance = ASC_Reroll_Chance_Forged_R1 ActorTypesRandomR1[iPosition].ASC_EZ_Lvl = ASC_EZ_Forged_R1 iPosition += 1 else ;Nothing, ActorType does not enter the array endif if ASC_Allowed_Triggerman_R1.GetValueInt() == 1 ActorTypesRandomR1[iPosition].LvlActorBase = LvlTriggerman ActorTypesRandomR1[iPosition].LvlActorBossBase = LvlTriggerman ActorTypesRandomR1[iPosition].ASC_Allowed_Boss = ASC_Allowed_Boss_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed = ASC_Max_Allowed_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Max_Allowed_Boss = ASC_Max_Allowed_Boss_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Chance = ASC_Chance_Spawn_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Chance_Boss = ASC_Chance_Spawn_Boss_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Allowed = ASC_Reroll_Allowed_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_Reroll_Chance = ASC_Reroll_Chance_Triggerman_R1 ActorTypesRandomR1[iPosition].ASC_EZ_Lvl = ASC_EZ_Triggerman_R1 iPosition += 1 else ;Nothing, ActorType does not enter the array endif Debug.Notification("Array of Structs is filled") EndFunction Function ClearAllArrays() ActorTypesRandomR1.Clear() EndFunction Function SpawnR1(ASC_SP_Random_R1 akCallingObject) int iNumSpawnTypes = ActorTypesRandomR1.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 = ActorTypesRandomR1[iWhoToSpawn] akCallingObject.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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt()) EndFunction Function SpawnAnotherGroupR1(ASC_SP_Random_R1 akCallingObject) ;this simulates spawning battles between two groups, or possibly a really large group of the same enemy. int iNumSpawnTypes = ActorTypesRandomR1.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 = ActorTypesRandomR1[iWhoToSpawn] akCallingObject.SpawnMoreEnemyActors(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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt()) EndFunction Function SpawnYetAnotherGroupR1(ASC_SP_Random_R1 akCallingObject) ;this simulates spawning battles between two groups, or possibly a really large group of the same enemy. int iNumSpawnTypes = ActorTypesRandomR1.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 = ActorTypesRandomR1[iWhoToSpawn] akCallingObject.SpawnEvenMoreEnemyActors(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, spawnDetails.ASC_EZ_Lvl.GetValueInt(), spawnDetails.ASC_Reroll_Allowed.GetValueInt(), spawnDetails.ASC_Reroll_Chance.GetValueInt()) EndFunction Spawnpoint object: Scriptname ASC_SP_Random_R1 extends ObjectReference {Region 1 Random Type SpawnPoint Marker Script} import ASC_MainStruct ASC_MasterRandomQuestScript Property MasterQuest Auto Const GlobalVariable Property ASC_Main_ModEnabled Auto Const GlobalVariable Property ASC_Main_Enabled_Random_R1 Auto Const GlobalVariable Property ASC_Main_Random_Chance_R1 Auto Const GlobalVariable Property ASC_Main_Random_DisableOnBlock_R1 Auto Const GlobalVariable Property ASC_Main_Difficulty_R1 Auto Const GlobalVariable Property ASC_Main_Reroll_Limit_R1 Auto Const FormList Property ASC_ResetList_R1 Auto Const Actor Property PlayerRef Auto Const bool bSpawnPointLoaded = false ;bool for cleanup event bool Group2Loaded = false bool Group3Loaded = false int iRerollLimit = 0 int iRerollCount = 0 Actor[] GroupList ; Array to track and cleanup all aspawned actors later, as well as apply new features in future Actor[] GroupList2 ;if 1st reroll attempt successful, assign spawns here Actor[] Grouplist3 ;2nd reroll attempt actors here, limit of rerolls is 2, hardcapped. Event OnCellAttach() bSpawnPointLoaded = true if (Self.IsDisabled() == false) && (ASC_Main_ModEnabled.GetValueInt() == 1) && (ASC_Main_Enabled_Random_R1.GetValueInt() == 1) SPActivate() Debug.Notification("All Functions Done") else Debug.Notification("SpawnPoint Failed") endif EndEvent Event OnCellDetach() ;idea is to stop cleanup from happening while potentially in combat with group while potentially leaving the cell their spawnmarker is in. Cleans up all data generated during spawn. bSpawnPointLoaded = false Utility.Wait(10) while (PlayerRef.IsInCombat()) ; also may not be necessary but reference for sanity check. ; Do nothing until combat is over, then check if we are still in the loaded area Utility.Wait(0.1) endwhile if bSpawnPointLoaded == false ;if we are not in the loaded area, start cleaning up. Event will be called again if we aren't so no worries int iCounter = 0 while iCounter < GroupList.Length ;GroupList[iCounter].SetLinkedRef(None) ;old, prior to using AI package (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList[iCounter]) GroupList[iCounter].DeleteWhenAble() iCounter += 1 endwhile iCounter = 0 endif if Group2Loaded == true int iCounter = 0 while iCounter < GroupList2.Length ;GroupList2[iCounter].SetLinkedRef(None) ;old, prior to using AI package (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList2[iCounter]) GroupList2[iCounter].DeleteWhenAble() iCounter += 1 endwhile iCounter = 0 endif if Group3Loaded == true int iCounter = 0 while iCounter < GroupList3.Length ;GroupList3[iCounter].SetLinkedRef(None) ;old, prior to using AI package (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.RemoveFromRef(GroupList3[iCounter]) GroupList3[iCounter].DeleteWhenAble() iCounter += 1 endwhile iCounter = 0 endif Debug.Notification("SpawnPoint cleaned up") EndEvent Function SPActivate() ;This may not be necessary but it looks neat. Main roll chance and disabling happens here iRerollLimit = ASC_Main_Reroll_Limit_R1.GetValueInt() int iChanceToSpawn = Utility.RandomInt(1,100) if (iChanceToSpawn <= ASC_Main_Random_Chance_R1.GetValueInt()) Debug.Notification("Spawn Function called on script!") Self.Disable() ASC_ResetList_R1.AddForm(Self) (MasterQuest as ASC_MasterRandomQuestScript).SpawnR1(Self) Debug.Notification("Spawn function has completed") elseif (iChanceToSpawn >= ASC_Main_Random_Chance_R1.GetValueInt()) && (ASC_Main_Random_DisableOnBlock_R1.GetValueInt() == 1) Self.Disable() ASC_ResetList_R1.AddForm(Self) Debug.Notification("SpawnPoint failed and disabled") else Debug.Notification("SpawnPoint failed but still armed") endif EndFunction Function SpawnEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance) Debug.Notification("Spawning Enemy Actors") ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network ;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0) Actor Spawned = None int iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0 int iPosition = 0 ;bool bSpawnSuccess = false ;obsolete, was for checking old reroll system was allowed 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) Actor akSpawned = Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]);Other way to do it, left for reference GroupList[iPosition] = akSpawned ;GroupList.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList[iPosition]) ;new system for patrols NOTE change to travel aliases endif iSpawnCounter += 1 endwhile Debug.Notification("Array Add Success. Actor count now: " + iPosition) Debug.Notification("Non Boss Group Spawned") if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]) GroupList[iPosition] = Spawned ;GroupList.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker) (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList[iPosition]) endif iSpawnCounter += 1 endwhile endif Debug.Notification("Array Add Success. Actor count now: " + iPosition) Debug.Notification("Enemies should be spawned") if (iRerollAllowed == 1) && (iRerollCount < iRerollLimit) && (Utility.RandomInt(1,100) <= iRerollChance) ;iRerollLimit is a universal value, only 1 global per region, not per type (MasterQuest as ASC_MasterRandomQuestScript).SpawnAnotherGroupR1(Self) iRerollCount += 1 Debug.Notification("Rerolling for another group!") else Debug.Notification("Reroll 1 failed") endif EndFunction Function SpawnMoreEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance) ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network ;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0) Actor Spawned = None int iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0 int iPosition = 0 GroupList2 = new Actor[iPosition] ; 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) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]) GroupList2[iPosition] = Spawned ;GroupList2.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker) old sytem (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList2[iPosition]) ;new system for patrols endif iSpawnCounter += 1 endwhile Group2Loaded = true if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]) GroupList2[iPosition] = Spawned ;GroupList2.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList2[iPosition].SetLinkedRef(kPatrolMarker) (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList2[iPosition]) endif iSpawnCounter += 1 endwhile endif if (iRerollCount < iRerollLimit) && (Utility.RandomInt(1,100) <= iRerollChance) (MasterQuest as ASC_MasterRandomQuestScript).SpawnYetAnotherGroupR1(Self) iRerollCount += 1 Debug.Notification("Rerolling for yet another group!") else Debug.Notification("Reroll 2 failed") endif EndFunction Function SpawnEvenMoreEnemyActors(int iMaxSpawnCount, int iChance, ActorBase varBaseActor, bool bBossAllowed, int iMaxBossCount, int iBossChance, ActorBase varBossActor, int iEzLevel, int iRerollAllowed, int iRerollChance) ;Form kMarker = Game.GetFormFromFile(0x00002ce2, "Fallout4.ESM") old system for patroling based on physical marker network ;ObjectReference kPatrolMarker = Game.FindClosestReferenceOfTypeFromRef(kMarker, Self as ObjectReference, 32.0) Actor Spawned = None int iDifficulty = ASC_Main_Difficulty_R1.GetValueInt() int iSpawnCounter = 0 int iPosition = 0 GroupList3 = new Actor[iPosition] ; 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) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]) GroupList3[iPosition] = Spawned ;GroupList3.Add(Self.PlaceActorAtMe(varBaseActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList3[iPosition].SetLinkedRef(kPatrolMarker) old sytem (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList3[iPosition]) ;new system for patrols endif iSpawnCounter += 1 endwhile Group3Loaded = true if bBossAllowed == 1 iSpawnCounter = 0 ; reuse var. memory efficient while (iSpawnCounter < iMaxBossCount) if (Utility.RandomInt(1,100) <= iBossChance) Spawned = Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel]) GroupList3[iPosition] = Spawned ;GroupList3.Add(Self.PlaceActorAtMe(varBossActor, Utility.RandomInt(1,iDifficulty), (MasterQuest as ASC_MasterRandomQuestScript).EzArray[iEzLevel])) iPosition = GroupList.Length - 1 ;GroupList[iPosition].SetLinkedRef(kPatrolMarker) (MasterQuest as ASC_MasterRandomQuestScript).ASC_PatrolPackageAlias.ApplyToRef(GroupList3[iPosition]) endif iSpawnCounter += 1 endwhile endif EndFunction Edit: I am also looking at another angle here, I'm thinking of ditching the arrays on the object altogether and the cleanup OnCellDetach event, in favor of applying a script to each actor via a referencealias that may be able to clean them up and track them better. One upside to that I'm considering is tracking their killers, so I can make a score system. So in theory -Marker script spawns the actor and we give it the refalias data via ApplyToRef() on it (scripts and AI)-Scripts in the refalias include events for OnCellDetach or similar that will clean up the actor etc + OnCellAttach event to keep him loaded-Script has OnKill or OnDeath event that each kill they get in a region up their "regional score"-(Potentially if user enables this system), different actor types chances to spawn goes up or down depending on the score and maybe we could have an event happen where when the score gets high a special boss = group spawns that you battle it out with, and whoever wins claims the area (so all scores are reset) Something along those lines anyway :wink: Link to comment Share on other sites More sharing options...
kitcat81 Posted June 3, 2017 Share Posted June 3, 2017 (edited) "ApplyToRef" won`t work for a script. It will add the script, but the script won`t work properly. The only way to add a working alias script is to make aliases filled on quest start. It won`t properly work with "ForceRefTo" either. Maybe you can highlight that part of the script that does not work. It takes to long to find the part :wink: I think you would get more help. You have so many different values there that it`s hard to find out where is the error. What the papyrus log says ?I`ll just give you a simple example of an array that stores object references (it`s just simplified functions from the vanilla workshopobjectscript ): Function CreateFurnitureMarkers() ; Function runs when we assign a new actor to the object If myFurnitureBase ; if we have filled MyFurnitureBase property in the CK ObjectReference newMarker = PlaceAtMe(myFurnitureBase) myFurnitureMarkerRefs.Add(newMarker) ; an array defined outside of any function or event (ObjectReference[] myFurnitureMarkerRefs) newMarker.MoveToNearestNavmeshLocation()endFunction Function DeleteFurnitureMarkers() If myFurnitureMarkerRefs.Length > 0 ; if we do have any markers int i = 0 While i < myFurnitureMarkerRefs.Length myFurnitureMarkerRefs.SetActorRefOwner(none) myFurnitureMarkerRefs.Delete() i += 1 EndWhile myFurnitureMarkerRefs.Clear() EndIfendFunction Function HideMarkers() int i = 0 int size = myFurnitureMarkerRefs.Length While i < size myFurnitureMarkerRefs.Disable() i += 1 EndWhileendFunction Edited June 3, 2017 by kitcat81 Link to comment Share on other sites More sharing options...
SMB92 Posted June 3, 2017 Author Share Posted June 3, 2017 Ah i kinda suspected that might be the case with the refsliases, what a shame. Its the SpawnEnemyActors function on the Object script. Its not filling the array at all. Log is empty, nothing about anything in the mod at all. Seems very strange to me. Link to comment Share on other sites More sharing options...
vkz89q Posted June 3, 2017 Share Posted June 3, 2017 (edited) The only way to add a working alias script is to make aliases filled on quest start. It won`t properly work with "ForceRefTo" either. What makes you think that? I use forcerefto a lot and never had any problems. I use it on actors, doors, terminals, weapons, etc etc and not even once they failed to fill or had any problems with scripts. When I need to store variables I store them in properties so no problem there either. Only issue I know with ForceRefTo is if you use it on actor on a scene that is running package, if you too it several times a row (more than once per second), it can mess up actors scene package. Edited June 3, 2017 by vkz89q Link to comment Share on other sites More sharing options...
Recommended Posts