Jump to content

Equipping arrows with a script


Tefnacht

Recommended Posts

Hello. I have the following “problems” with a weapon script, maybe someone here knows a way around it.

 

My character is a sneak, using primarily bows. After some time I got really tired of all the mini-stacks of two to five arrows of different flavor piling up in my inventory. Its a real hassle to switch back to the inventory every few odd shots to equip new arrows. I finally got so sick of it, that I decided to automate this somehow.

 

I wrote a script attached to “my” bow, that would walk through my inventory, look for arrows, choose the “best” one (enchanted with highest damage if available or just highest damage) and equip them.

I have it working pretty well, but it doesn't really satisfy me yet.

 

My first problem: I did not find a CPU-friendly way to execute the script when the bow is fired. Currently I check the quiver slot each and every frame in a GameMode block while the bow is equipped. If the slot is empty, the script will search the best arrow and equip it. I did not notice any rendering slowdown but my computer is a power horse. There must be a better, less “expensive” way to do this? Something more elegant?

 

My second problem: The script will only equip one single arrow (using EquipItemNS), not the whole stack. If I have 20 silver arrows, one will get equipped (and shown as equipped in the inventory screen), the remaining 19 get separated into a second stack. No big deal, really, but this means the ammunition counter in the GUI always shows 1.

Of course, with the auto arrow equip, the ammo counter is now pretty useless anyway. But... I play mostly in third person. Since there is technically only one arrow equipped, the quiver on my characters back will disappear when the arrow is used. Of course the auto equip immediately kicks in and equips the next arrow, but for one render frame the quiver is gone. It “flickers” after every shot, if you will. This looks... a bit odd. Funny enough: The amount of arrows rendered in the quiver is always correct. If I have three silver arrows left, there will be three arrows visible in the quiver, although the ammo counter still reads 1. Odd, huh?

Is there any way around this? Some trick to equip the whole stack of arrows?

 

For reference, here are the scripts I wrote. OBSE 18 is required. This is the script attached to my bow:

ScriptName LYDIABowScript

;*******************************************************************************
; LYDIABowScript (Object script)
;*******************************************************************************
; This script is attatched to the bow and will automagically equip the
; "best" arrow from the player inventory.

Short worn		;set to 1 while the bow is equipped
Ref arrowref		;reference to the currently equipped arrow

Begin OnUnEquip Player
Set worn To 0						;the bow is no longer worn by the player
If (arrowref)						;if the player has an arrow equipped
	Player.UnEquipItemNS arrowref			;unequip the arrow
EndIf
Set arrowref To 0					;no arrow equipped anymore
End

Begin OnEquip Player
Set worn To 1						;the bow is now being worn by the player
Set arrowref To Call LYDIAChooseArrow			;choose and equip best arrow from player inventory
End

Begin GameMode
If (worn)						;if the player is wearing the bow
	If (Player.GetEquippedObject 17 == 0)		;if the player has no item in the quiver slot
		Set arrowref To Call LYDIAChooseArrow	;choose and equip best arrow from player inventory
	EndIf
EndIf
End

And this is the OBSE user function script called by the bow script:

ScriptName LYDIAChooseArrow

;*******************************************************************************
; LYDIAChooseArrow (User function)
;*******************************************************************************
; This function will choose the best arrow from the player inventory,
; equip it and return a reference to the arrow, or 0 if no arrow was found.
; "Best arrow" means the arrow with the highest damage. If magical
; arrows are available, those will be chosen over mundane arrows, even
; if their damage is lower.

Long i			;index counter variable
Long j			;total number of inventory objects
Long damage		;best found damage so far
Ref arrowref		;reference to best arrow so far
Ref itemref		;reference of inventory object currently being checked
Short ismagic		;set to 1 if a magic arrow was found
Long d			;variable with current arrows damage

Begin Function {}
Set i To 0							;start with first inventory object
Set j To Player.GetNumItems					;get total number of unique inventory objects
Set damage To 0							;best damage found so far is 0
Set arrowref To 0						;no "best arrow" reference found so far
Set ismagic To 0						;no magical arrows found
While (i < j)							;loop over all inventory objects
	Set itemref To Player.GetInventoryObject i		;get reference to the inventory object
	If (IsAmmo itemref)					;if this is an arrow
		Set d To GetAttackDamage itemref		;get that arrows damage
		If (GetEnchantment itemref)			;if the arrow has a enchantment
			If (ismagic == 0)			;if this is the first magical arrow we found
				Set damage To d			;it does best damage (override any mundane arrow)
				Set arrowref To itemref		;it is the best arrow
				Set ismagic To 1		;now we found a magical arrow
			Else					;otherwise we already found a magical arrow
				If (d > damage)			;if it does better damage than our current "best" arrow
					Set damage To d		;it is the new best damage
					Set arrowref To itemref	;and the new best arrow
				EndIf
			EndIf
		Else						;otherwise the arrow is not enchanted
			If (d > damage) && (ismagic == 0)	;if it does better damage and we have not found a magical arrow
				Set damage To d			;this becomes best damage
				Set arrowref To itemref		;and the arrow is now "best" arrow
			EndIf
		EndIf
	EndIf
	Set i To i + 1						;increment to next inventory object
Loop
If (arrowref)							;if we found a "best" arrow
	Player.EquipItemNS arrowref				;equip it
EndIf
SetFunctionValue arrowref					;return what we found
End

Any help or comment would be appreciated. Thank you in advance.

Link to comment
Share on other sites

^^ i've got the same darn question.

 

"The script will only equip one single arrow not the whole stack."

 

that's why i feel the urge to bump this up.

 

heh heh "automagically"...

 

by the way, i think if you told it to cycle through the inventory items one time when the bow is equipped and once more every time the equipped arrows have ran out, it would eat up a lot less CPU. but of course, you'll need to figure out the "equip only one arrow" problem, first.

Link to comment
Share on other sites

I think I found a solution for equipping all the arrows. I had to, or shelf the whole idea, because I noticed a serious issues with the two stacks of the same arrow type in the inventory.

 

Imagine this scenario: One arrow got equipped by the script, 19 more are in the second, not equipped stack. I shoot the arrow, but it bounces off a wall. Another arrow gets equipped, 18 are now in the second stack. If I now pick up the arrow I fired first, it is added to the already equipped arrow, so now I have a stack of two arrows equipped and 18 in the second stack. If I shoot now... the equipped stack of two is correctly reduced to one, but the second stack of 18 goes poof and vanishes.

 

After a short dungeon crawl I was wondering where all my hundred daedric arrows went... heh.

 

The trick seems to be to remove all but one arrow of the stack from the player inventory, then equip that single arrow and then add back all the previously removed arrows. Since we're not really adding or removing items, just adding and subtracting from a stack, there seems to be no problem with doing this in a single frame.

 

This is how I modified my LYDIAChooseArrow script. This stuff comes after the Loop:

If (arrowref)					;if we found a "best" arrow
Set i To Player.GetItemCount arrowref	;get amount of arrows in inventory
If (i > 1)				;if there is more than one
	Set i To i - 1			;calculate how many more than one
	Player.RemoveItemNS arrowref i	;and remove all but one
Else					;otherwise there is just one
	Set i To 0			;we did not have to remove any
EndIf
Player.EquipItemNS arrowref		;equip the one remaining arrow
If (i)					;if we removed arrows
	Player.AddItemNS arrowref i	;add them back to the player
EndIf
EndIf
SetFunctionValue arrowref			;return what we found

Of course, there is no free lunch. While the whole stack of arrows now gets equipped correctly (and the ammo counter works again) the amount of arrows rendered in the quiver is now wrong. There is always only one single arrow rendered in the quiver. Picking up another arrow corrects this though and it is really only a “cosmetic” problem. But it is still not a perfect solution.

Link to comment
Share on other sites

i think you might want to look into Duke Patrick's Archery mod's scripts. there is an instance where the script auto equips a batch of arrows once they run out if you are close enough to the arrow case. that might be of some use.
Link to comment
Share on other sites

Thanks a lot for the tip, VileTouch. I'll go take a look at that mod right away. This is really driving me crazy (laughs) and not the good kind of crazy.

 

Yesterday I found a “almost free lunch” solution after hours of trying, thinking and more trying. It seems to work well, the ammo counter is correct, there is just one stack, rendering of the quiver is right, but it requires dropping of one item into the gameworld every time the script has to equip more than one arrow... which, from my understanding, will lead to savegame bloating, very slow bloating, but bloating nonetheless. So... not quite a “free” lunch, but at least a cheap one.

 

Here is my newest solution. Its not fully tested yet, but besides the bloating problem (which might not even exist, no idea, need more testing) it looks promising. This time, instead of removing the arrows and then adding them back, all but one arrow get dropped as a stack into the gameworld, then the one remaining arrow is equipped, and then the script scans through the cell the player is in until it finds the stack we dropped, and picks it up again. All in one frame, so the player will not notice any of the jazz that is going on behind his back. It looks great from third person view.

 

Again, this comes after the Loop in the LYDIAChooseArrow script:

If (arrowref)							;if we found a "best" arrow
Set i To Player.GetItemCount arrowref			;get amount of arrows in the players inventory
If (i > 1)						;if there is more than one
	Set i To i - 1					;calculate how many more than one
	Player.Drop arrowref i				;drop all but one arrow as a stack into the gameworld
Else							;otherwise we have only one arrow
	Set i To 0					;and did not have to drop any
EndIf
Player.EquipItemNS arrowref				;equip the single arrow
If (i)							;if we dropped arrows into the gameworld
	Set itemref To GetFirstRef 34 1			;start scanning the current call for ammo (and 8 adjacent cells, in case we are standing on a cell border)
	While (itemref)					;as long as we find ammo references
		If (itemref.GetBaseObject == arrowref) && (itemref.GetRefCount == i)	;if the reference is a child of our arrow base and a stack of matching size
			itemref.Activate Player		;make the player pick up the stack
			Set itemref To 0		;set itemref to 0 to leave the loop, we have found what we were looking for
		Else					;otherwise it is some other arrow
			Set itemref To GetNextRef	;scan for next ammo reference
		EndIf
	Loop
EndIf
EndIf
SetFunctionValue arrowref					;return what we found

All that just to equip some damned arrows (laughs). Hopefully Duke Patrick found a better solution...

Link to comment
Share on other sites

I was unable to find the mod made by Duke Patrik that VileTouch referred me to, so instead I just concentrated on testing my solution.

 

I have now been using this script for a couple of days of intense marksmanship and it seems to work well. I couldn't find any problems with it.

 

There is another “wrong amount of arrows rendered in the quiver”-glitch when the game was restarted and a savegame is loaded that was made with the arrows equipped, but that is a vanilla Oblivion problem (that I never noticed before). In this case the quiver always looks completely full, even if there should be just one arrow in it. But that is Oblivions fault, it happens without my script too. Also, the quiver rendering is not updated if you loot arrows of the type you have equipped from a actors inventory or a container. Again, this is a vanilla bug. Both are only visual and so rare, I decided not to try scripting a workaround.

 

The bloating problem exists, however. But it is not “persistent” bloating, as far as I can tell. The savegame bloats the same way it does when a normal arrow is shot into the gameworld. After three ingame days, when a cell reset occurs, the garbage is cleared.

 

So I think, my last solution, dropping all but one arrow into the gameworld, equipping the remaining arrow, then searching and picking up the dropped stack again is the best working way to do this. The bloating is negligible and not permanent. There are many vanilla Oblivion actions that cause much worse savegame bloat. This method should be save to use.

 

With this conclusion, I consider my problem solved. Thanks again to those who posted comments. I do not claim credit for the method or the script itself, feel free to use it (or parts of it) if you need to equip arrows on the player within your own mods.

 

Have fun.

Link to comment
Share on other sites

you could have asked for the link... i thought i had put it here... well,sorry, my bad..this mod can be a little elusive

 

so here's the official thread. the Actual file is in TesAlliance though, but you'll get everything you need there. Including the chance that Duke Patrick himself give you permission to use his scripts.

 

http://forums.bethsoft.com/index.php?/topic/1093453-relz-duke-patricks-combat-archery-mod-thread-2/page__hl__duke%20patrick%20combat%20archery

Link to comment
Share on other sites

  • 1 year later...

I have a solution. Retrieve intentory reference of a stack and then call EquipMe on that reference.

 

let invRefAmmo := *(player.GetInvRefsForItem Arrow3Silver) ; unbox array to get first reference of silver arrows
invRefAmmo.EquipMe ; you get whole stack equipped and no message spam

 

Edit: Forget it, seems only to work first time after stack of arrows has been added.

Edited by mitchalek
Link to comment
Share on other sites

To cut down on the scripting and make sure it runs only when an arrow is fired, you could set an event handler.

 

It would be quite simple I think...

 

; LYDIABowScript (Object script)
;*******************************************************************************
; This script is attatched to the bow and will automagically equip the
; "best" arrow from the player inventory.

Short worn              ;set to 1 while the bow is equipped
Ref arrowref            ;reference to the currently equipped arrow

Begin OnUnEquip Player
       Set worn To 0                                           ;the bow is no longer worn by the player
       If (arrowref)                                           ;if the player has an arrow equipped
               Player.UnEquipMe arrowref                   ;unequip the arrow [using UnEquipMe like mitchalek said]
       EndIf
       Set arrowref To 0                                       ;no arrow equipped anymore
       RemoveEventHandler                                 ;To avoid problems when using other weapons
End

Begin OnEquip Player
       Set worn To 1                                           ;the bow is now being worn by the player
       Set arrowref To Call LYDIAChooseArrow                   ;choose and equip best arrow from player inventory
       SetEventHandler OnRelease LYDIAReleaseEventSCRIPT ref::Player          ;The event "OnRelease" occurs whenever the actor finishes an attack
End

 

Now what used to be the GameMode block is now only executed after each shot. You will need a way of extracting the arrowref from the event handler, because they have no function values, but I think this should be enough to get you started. You seem like you know your way around a script editor.

 

Hope this helped :)

Link to comment
Share on other sites

  • Recently Browsing   0 members

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