Jump to content

What makes certain scripts harmful?


Recommended Posts

Okay, I had a look at it, and it did not look too suspicious to me, at least. It does indeed take a while to get into Papyrus, I am still in the process of properly getting into it myself, too, I think. Little by little is the best way to go, and trying to make scripts run as best as they can, with as little code as they can, is the best way to learn new tricks and alternative ways to do the same thing. :smile:

 

There was, however, still the GetSize command that was used a lot, I think. Maybe not too "a lot", but it could be possible to, instead of looping from 0 to GetSize - 1, loop from GetSize - 1 to 0. That would work by getting the size, then adding a while loop with the stored size > 0 as condition, and immediately adding a command to decrease the stored size by 1 inside the loop. That way, when GetSize returns the last index + 1, it will be last index + 1 - 1 = last index on the first loop. And when stored size = 1 on the last round (when the condition is met), it will actually be 1 - 1 = 0 in the loop! Handy, one could say.

int i = SomeList.GetSize()

While (i > 0)
    i -= 1
    Debug.MessageBox("Index: " + i)
EndWhile

Also, I hope you do not mind, but I made a sort of alternative version, that uses four FormLists, as another sort of idea on how it could be made, perhaps, but not necessarily, if it would work at all:

  1. items to equip when not in power armour and not in combat
  2. items to equip when not in power armour and in combat
  3. items to equip when in power armour and not in combat
  4. items to equip when in power armour and in combat

When combat state changes, the script should equip the appropriate FormList, after unequipping every other list. It is just an idea, perhaps it could help you cut down on the amount of code, if it is possible to reuse the function to equip/unequip a whole FormList with reasonably good performance (needs to be tested), assuming it is possible to give a FormList as a variable parameter (I have never done it, it should be possible, but one can never know with Bethesda's stuff). You could then, in the item selection menu you are building, add items to the FormLists and remove them when necessary, and it would not be limited to headgear only. However, it would require you to make checks in the adding part, using Find, RemoveAddedForm and Add, to make sure the lists would not get too crowded or something in an unnecessary extent: http://www.creationkit.com/index.php?title=FormList_Script

 

The script is below in a spoiler, because I apparently cannot add .psc files as attachments to posts here. Do note that it is just an idea, I am basically in the university basement again today until evening, so I do not have a chance to even see if it compiles, much less to check if it does what it is supposed to after compiling. Just a thought, an idea, a rough sketch, a random suggestion and all that combined. Take it with at least a few absolutely massive grains of salt. :laugh:

 

 


Scriptname KE_DEHQuestScript Extends Quest


; KE_DEH - This is just a tiny example idea sort of testing thingy that might even fail to compile...


Actor Property PlayerRef Auto Const
GlobalVariable Property KE_DEHActive Auto Const

FormList Property KE_DEHListNormal Auto			; list for all non-PA out-of-combat stuff
FormList Property KE_DEHListCombat Auto			; list for all non-PA combat stuff
FormList Property KE_DEHListNormalPA Auto		; list for all PA out-of-combat stuff
FormList Property KE_DEHListCombatPA Auto		; list for all PA combat stuff

FormList Property KE_DEHFormList Auto Const
FormList Property KE_DEHPowerArmorFormList Auto Const


Event OnInit()

	Debug.Notification("Hello world!")

	Self.RegisterForRemoteEvent(PlayerRef, "OnPlayerLoadGame")
	Self.RegisterForRemoteEvent(PlayerRef, "OnCombatStateChanged")

	AddTestingGearToPlayer(1, 1) ; comment this out to disable testing gear adding

EndEvent


Event Actor.OnPlayerLoadGame(Actor akSender)
	Debug.Notification("You have loaded the game.")
EndEvent


Event Actor.OnCombatStateChanged(Actor akSender, Actor akTarget, int aeCombatState)

	If ((KE_DEHActive.GetValue() as int) == 0) ; when you get far enough, you could use States with a function to set the "system State" on/off
		;Debug.Notification("Combat state changed, mod inactive")
		Return
	Else
		;Debug.Notification("Combat state changed, new state " + aeCombatState)
		If (PlayerRef.IsInPowerArmor())							; if player is inside Power Armour...
			EquipUnequipFormList(PlayerRef, KE_DEHListNormal, False)		; ...unequip all non-PA out-of-combat
			EquipUnequipFormList(PlayerRef, KE_DEHListCombat, False)		; ...unequip all non-PA combat
			If (aeCombatState == 0)							; ...if player is not in combat...
				EquipUnequipFormList(PlayerRef, KE_DEHListCombatPA, False)	;    ...unequip all PA combat
				EquipUnequipFormList(PlayerRef, KE_DEHListNormalPA, True)	;    ...equip all PA out-of-combat
			Else									; ...else if player is in combat...
				EquipUnequipFormList(PlayerRef, KE_DEHListNormalPA, False)	;    ...unequip all PA out-of-combat
				EquipUnequipFormList(PlayerRef, KE_DEHListCombatPA, True)	;    ...equip all PA combat
			EndIf
		Else										; else if player is NOT in power armour...
			EquipUnequipFormList(PlayerRef, KE_DEHListCombatPA, False)		; ...unequip all PA combat
			EquipUnequipFormList(PlayerRef, KE_DEHListNormalPA, False)		; ...unequip all PA out-of-combat
			If (aeCombatState == 0)							; ...if player is not in combat...
				EquipUnequipFormList(PlayerRef, KE_DEHListCombat, False)	;    ...unequip all non-PA combat
				EquipUnequipFormList(PlayerRef, KE_DEHListNormal, True)		;    ...equip all non-PA out-of-combat
			Else									; ...else if player is in combat...
				EquipUnequipFormList(PlayerRef, KE_DEHListNormal, False)	;    ...unequip all non-PA out-of-combat
				EquipUnequipFormList(PlayerRef, KE_DEHListCombat, True)		;    ...equip all non-PA combat
			EndIf
		EndIf
	EndIf

EndEvent


Function AddTestingGearToPlayer(int iAddCount, int iAddCountPA)

	; iAddCount	= number of non-PA items to add
	; iAddCountPA	= number of PA items to add

	; get the size here, the loop it to 0, to prevent unnecessary GetSize function calls
	int i = KE_DEHFormList.GetSize()

	While (i > 0)
		i -= 1
		PlayerRef.AddItem(KE_DEHFormList.GetAt(i), iAddCount, True)
	EndWhile

	; again, get the size here to cutn down on function calls, reusing the same variable
	i = KE_DEHPowerArmorFormList.GetSize()

	While (i > 0)
		i -= 1
		PlayerRef.AddItem(KE_DEHPowerArmorFormList.GetAt(i), iAddCountPA, True)
	EndWhile

EndFunction


Function EquipUnequipFormList(Actor akActor, FormList akList, bool abEquip)

    ; akActor = the actor to equip/unequip items on
    ; akList = FormList to equip/unequip
    ; abEquip = whether the items should be equipped

    int i = akList.GetSize()
    Form f

    While (i > 0)
        i -= 1
        f = akList.GetAt(i)
        If (akActor.GetItemCount(f) > 0)
            If (abEquip)
                akActor.EquipItem(f, False, True)
            Else
                akActor.UnequipItem(f, False, True)
            EndIf
        EndIf
    EndWhile

EndFunction

 

 

And if cdcooley says something to correct me or anything else, listen to him, not me, because I know I will definitely listen to him myself if he has something to say. Observing the master and evaluating his feedback and suggestions is another way to learn a lot. :happy:

Edited by Contrathetix
Link to comment
Share on other sites

Bloody hell mate you've done a lot, thanks for all of the help :smile:

 

I honestly didn't think of the GetSize() function being called every time it looped, makes sense now that you mention it (second time's the charm I guess haha). The items were sometimes added to the player on a second or more delay, or at least that was the delay between some of the featured items popping up. I guess I assumed that it was calculated once, and then the value held constant and used as a condition in each of the loops. Thanks for pointing that out, that will obviously make it run a lot faster (as it was looping a minimum 116 times).

 

I honestly didn't think of adding the base objects to a form list and equipping the entire form list, I only thought to determine which entry in the form was to be equipped using a global variable. I like your idea better, as it should make it compatible with everything and remove the mods dependency on any .esm/.esp that adds headgear. It would also make it easier on me as well haha. Hopefully, I'll be able to determine the reference ID of a specific piece of armour using the OnItemEquipped event (or one of its variants) and then add that reference ID to a form list. That is, I'm assuming I can get the reference ID from the akReference parameter (I haven't had a chance to test it yet). This would allow me to equip specific armour pieces, and not just the base object. I plan on using the OnItemEquipped event to get the reference ID of the next object equipped. This would be something the user would have to initiate through a settings holotape, though it should still be better than selecting the base object from a huge list.

 

I did think of expanding this mod to all armour, but I'm not sure how efficient I could make the script. I could probably get it to a decent state with everything that you guys have told me, though. For now, I'll get it working with headgear and then see how I go expanding it to all armour. This whole mod was at least originally geared toward people who wanted to see their characters face, but were also wanting the benefits that come with headgear. One shortcoming I can't see myself overcoming is being able to determine which armour piece is in which slot, at least until F4SE is released anyway (I'm pretty sure you're able to with SKSE). All this really means though is that the user will have to keep track of what can be equipped with what.

 

Unfortunately, I won't have the time to work on this for the next couple of weeks, so I won't have a chance to test these ideas out for a little while. I do definitely appreciate the help in the meanwhile though of course, you guys have substantially improved the quality of the script, and had some great ideas. I'll definitely have a look at implementing your ideas once I get the chance, thanks again mate! Apologies if I missed anything, I'm on my way to bed.

Edited by KernalsEgg
Link to comment
Share on other sites

No problem (on my part, at least), the pleasure is/was mine, happy to help, I love scripting. I think I also learned a little new here, thanks to cdcooley. I did not know it is possible to leave the parameter names there, I know it works in Python, but I never tried it with Papyrus. :smile:

 

I had a look at the Wiki, but I could not find a function to get any currently equipped items. Odd, maybe it will either be added in a future update to the base game, or by F4SE, definitely a must have. As for the FormLists... I think in Skyrim, a FormList could only contain forms of a single type, and I think it might be the case in Fallout 4, too. However, if all equippable items are of the same type (Armor, I would assume, cannot remember), and if you could therefore add them to the same list (as in, if a hat and an outfit should be equipped when entering combat, they could both be added in the same "out of combat" list if they are Armor), the example I gave in the spoiler should work with all equippable items, with all quantities, out of the box. Assuming it works in the first place, of course. I do not have any ideas how to improve the performance of the example in the spoiler, though, at least not at the moment. As in, not a reasonable way. The Skyrim wiki said that using EquipItem will add the item to the target if it does not have one, the Fallout 4 wiki did not have a mention of it. Assuming the "add if missing" part has been removed in Fallout 4, you could remove the GetItemCount check, perhaps, to improve the performance a little when equipping/unequipping the lists.

 

So if all equippable items are of type Armor, something like the example should work to equip/unequip coats, sunglasses, armour, headgear and everything. I will check whether it compiles, at least, when I get home.

 

Edit: The wiki page you linked about OnItemEquipped says it only returns the reference if the item is persistent, and most items are not. Once inside the inventory, if the items are generic ones, I do not know how you could get the exact reference you want. If you can get it at all.

Edited by Contrathetix
Link to comment
Share on other sites

Fortunately, all armour pieces are considered armor, so there shouldn't be any issues there. I've been mainly working off of the Fallout 4 wiki to make sure I'm using the functions correctly, so I only realised that the EquipItem function added the item to the player's inventory after a bit of testing (if you didn't have the item to equip in the first place). Unfortunately, this means I require this check. One check that I'm not entirely sure on (just off the top of my head, I'm heading to bed as soon as I post this) is whether it matters if I equip an item that's already equipped. I currently only equip an item if it's not already equipped, and vice versa. I'm not sure if this matters at all, but I added it just in case. If it doesn't matter, then I'll remove it to improve performance. I'll probably just remove it and add it back if I notice anything odd going on as a result.

 

That sucks about most items not being persistent. Being so new to papyrus, I don't even know what that implies, but it's good to know that I have to look elsewhere. Either way, as it will improve compatibility, and potentially save me time in the long run, I'll still try to use the event to get the base object anyway using the akBaseObject parameter and add it to a form list. I'm assuming that this will work anyway, given that it doesn't appear to require the item to be persistent.

 

Unfortunately, the only "vanilla" papyrus function that allows you to determine what the player currently has equipped relates to weapons and spells (the GetEquippedWeapon function and its variants).

Link to comment
Share on other sites

Thing that really makes scripts harmful is improper cleanup, when your scripts or objects used by them are not unloaded properly and are running endlessly or are persistent. Sooner or later that will break the game stability to the point of endless unstoppable CTDs.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...