Jump to content

Custom Bound Armor Spell


razorblade457

Recommended Posts

The only reason for crashing would be that the original equip is enchanted. Because OBSE seems using its own code for magic and its effects, the result is you often can't handle the enchanted items the same way as non magical ones. I must apologize for this, because I simply forgot how much pain is to handle them correctly, even for a advanced modder. The main problem is that magical item can't be for example unequipped and equipped back in the same frame. In fact I too learned of this and I worked on solution, but in the end it involved rewriting spell script, making a token which handles a queue for equipping, two service functions and top of this a quest which I using for sharing all needed variables. For you it would suffice to detect an enchanted item using GetEnchantment function and remove it from a field. It's not much, but at least it won't crash on recast.

You can modify the script this way:

if eval ( IsPlayable item == 0 && IsPlayable2 item == 0 ) ;non-playable item?
	set remove to 1
	let nonplay_slots := nonplay_slots | temp ; update "forbidden" slots 
else
	let temp := temp & cb_slots
	if temp == 0 ; no match
		set remove to 1
	endif
        let enchant := GetEnchantment item ;extra ref variable
        if enchant != 0
                set remove to 1
        endif
endif

And very good in CS? No, there are still parts in it where I'm practically a noob. It looks like it because I use it for years and in "real" life I'm working as a technician, so my work and education brings some advantage. And I got so far only because I worked on my first Oblivion project which lasted for years and that path was full of obstacles. It forced me to study CS Wiki, included OBSE command docs and experimenting until I simply got what I wanted.

Link to comment
Share on other sites

RomanR

It's alright and it's not your fault.

It's amazing how a simple inconvenience forced you to to do all that(script rewriting). And thank you for explaining the problem to me.

 

I've tried your fix(GetEnchantment) and still the game crashes.

I double checked and I have typed in the script as exactly as you have shown me, is there no other way to circumvent this?

Link to comment
Share on other sites

In your case could be something more at play, it worked for me. I can provide you with my latest versions of scripts which are working for me:

 

1. Script for quest named RomRAddBoundSpell:

scn RomRCustomBoundQuestScript
array_var actors
array_var requests
array_var bound
short token_ready
short spell_active
short allownonplay

begin GameMode
	if player.HasSpell RomRCustomBoundSpell == 0
		player.AddSpell RomRCustomBoundSpell
	endif
	StopQuest RomRAddBoundSpell
end

After start it add a bound spell to player and it's also a database of shared varables and fields between token and spell scripts. You can start it manually with command "startquest RomRAddBoundSpell".

 

2. Script for a token which editor ID is RomRCustomBoundToken:

 

 

scn RomRCustomBoundTokenScript
int requests_size
int requests_index
int actors_size
int actors_size_old
int actors_index
ref actor
ref item
int request
short remove
int temp
int count
array_var equipped
int equipped_size
int equipped_index
int bound_size
int bound_index

begin GameMode
	;init
	let requests_size := ar_Size RomRAddBoundSpell.requests
	if requests_size == -1
		let RomRAddBoundSpell.requests := ar_Construct array
		let requests_size := ar_Size RomRAddBoundSpell.requests
		if requests_size == 0
			;print "CustomBoundToken: Init complete."
			set RomRAddBoundSpell.token_ready to 1
		endif
	endif
	; checks after restart/load
	if GetGameRestarted || GetGameLoaded
		let actors_size := ar_Size RomRAddBoundSpell.actors
		if actors_size > 0
			set temp to 0
			while temp < actors_size
				let actor := RomRAddBoundSpell.actors[temp]
				if actor == 0
					ar_Erase RomRAddBoundSpell.actors temp
					let actors_size := ar_Size RomRAddBoundSpell.actors
				endif
				set temp to temp + 1
			loop
		endif		
	endif
	; reporting actors
	let actors_size := ar_Size RomRAddBoundSpell.actors
	if actors_size != actors_size_old
		;print "CustomBoundToken: The list of actors changed."
		;if actors_size > 0
		;	print "Actors registered now:"
		;	set temp to 0
		;	while temp < actors_size
		;		let actor := RomRAddBoundSpell.actors[temp]
		;		if actor != 0
		;			print $actor
		;		else
		;			print "<actor invalid>"
		;		endif
		;		set temp to temp + 1
		;	loop
		;else
		;	print "No actors right now."
		;endif
		set actors_size_old to actors_size
	endif
	; make bound items unequipable
	let bound_size := ar_Size RomRAddBoundSpell.bound
	if actors_size > 0 && bound_size > 0 && RomRAddBoundSpell.spell_active != 0
		let equipped_size := ar_Size equipped
		if equipped_size == -1
			let equipped := ar_Construct array
			let equipped_size := ar_Size Equipped
		endif
		set actors_index to 0
		while actors_index < actors_size
			if actor != 0
				ar_Resize equipped 0
				let equipped[0] := 0
				let equipped := actor.GetEquippedItems
				set bound_index to 0
				while bound_index < bound_size
					let item := RomRAddBoundSpell.bound[bound_index]
					if item != 0
						set count to actor.GetItemCount item
						if count > 0
							let temp := ar_Find item equipped
							if temp < 0
								actor.EquipItemNS item
							endif
						endif
					endif
					set bound_index to bound_index + 1
				loop
			endif
			set actors_index to actors_index + 1
		loop
	endif
	; searching for equip request
	let requests_size := ar_Size RomRaddBoundSpell.requests
	if requests_size > 0
		set requests_index to 0
		while requests_index < requests_size
			set remove to 0
			let actor := RomRAddBoundSpell.requests[requests_index]
			let item := RomRAddBoundSpell.requests[requests_index+1]
			let request := RomRAddBoundSpell.requests[requests_index+2]
			if actor == 0 || item == 0 || request == 0
				set remove to 1
			else
				actor.EquipItemNS item
				let RomRAddBoundSpell.requests[requests_index+2] := 0
			endif
			if remove != 0
				ar_Erase RomRAddBoundSpell.requests requests_index:(requests_index+2)
				let requests_size := ar_Size RomRAddBoundSpell.requests
			else
				set requests_index to requests_index + 3
			endif
		loop
	endif
	if actors_size == 0 && requests_size == 0
		;print "CustomBoundToken: No actors and requests - removing itself."
		let RomRAddBoundSpell.actors := ar_Null
		let RomRAddBoundspell.requests := ar_Null
		ar_Resize equipped 0
		let equipped := ar_Null
		set RomRAddBoundSpell.token_ready to 0
		RemoveMe
	endif
end

begin MenuMode 1002 ;inventory	
	; make bound items unequipable with message 
	let bound_size := ar_Size RomRAddBoundSpell.bound
	if actors_size > 0 && bound_size > 0 && RomRAddBoundSpell.spell_active != 0
		let equipped_size := ar_Size equipped
		if equipped_size == -1
			let equipped := ar_Construct array
			let equipped_size := ar_Size Equipped
		endif
		set actors_index to 0
		while actors_index < actors_size
			if actor != 0
				ar_Resize equipped 0
				let equipped[0] := 0
				let equipped := actor.GetEquippedItems
				set bound_index to 0
				while bound_index < bound_size
					let item := RomRAddBoundSpell.bound[bound_index]
					if item != 0
						set count to actor.GetItemCount item
						if count > 0
							let temp := ar_Find item equipped
							if temp < 0
								actor.EquipItemNS item
								Message "Bound spell forces you to equip bound item back."
							endif
						endif
					endif
					set bound_index to bound_index + 1
				loop
			endif
			set actors_index to actors_index + 1
		loop
	endif
end

 

 

It mostly handles requests for equip and makes bound items unequipable in game mode or in player's inventory.

 

3. Script for a spell itself (spell's editor ID is RomRCustomBoundSpell):

 

 

scn RomRCustomBoundSpellScript
ref item
ref actor
ref magictype
int items_size
int items_index
int actors_size
int bound_size
array_var items
int temp ; for various purposes
int cb_slots ; cb means custom bound 
int nonplay_slots 
short remove
short do_once

begin ScriptEffectStart
	set actor to GetSelf
	if actor != 0
		; token init
		let actors_size := ar_Size RomRAddBoundSpell.actors
		if actors_size == -1
			let RomRAddBoundSpell.actors := ar_Construct array
			let actors_size := ar_Size RomRAddBoundSpell.actors
		endif
		if actors_size > 0
			let temp := ar_Find actor RomRAddBoundSpell.actors 
			if temp < 0
				let RomRAddBoundSpell.actors[actors_size] := actor
			;	print "Bound Spell: Actor "+$actor+" added."
			;else
			;	print "Bound spell: "+$actor+" is already registered."
			endif
		endif
		if actors_size == 0
			let RomRAddBoundSpell.actors[0] := actor
			;print "Bound spell: Actor "+$actor+" added to empty field."
		endif
		let temp := player.GetItemCount RomRCustomBoundToken
			if temp == 0
			player.AddItemNS RomRCustomBoundToken 1
		endif
		set do_once to 0
		;print "Bound Spell: Init for a token completed."
		; bound items init
		let bound_size := ar_Size RomRAddBoundSpell.bound
		if bound_size == -1
			let RomRAddBoundSpell.bound := ar_Construct array
			let bound_size := ar_Size RomRAddBoundSpell.bound
		endif
		if bound_size == 0
			let RomRAddBoundSpell.bound[0] := DaedricHelmet
			let RomRAddBoundSpell.bound[1] := DaedricCuirass
			let RomRAddBoundSpell.bound[2] := DaedricGreaves
			let RomRAddBoundSpell.bound[3] := EnchDaedricGauntletsFortStrength
			let RomRAddBoundSpell.bound[4] := EnchDaedricBootsResistShock
			let bound_size := ar_Size RomRAddBoundSpell.bound
			;print "Bound Spell: Bound items added."
		endif
	endif
end

begin ScriptEffectUpdate
	; custom bound begins
	if actor != 0
		let temp := Call RomRCustomBoundFnFindRequest actor
	endif
	if actor != 0 && RomRAddBoundSpell.token_ready != 0 && do_once == 0 && temp == -1
		let items_size := ar_Size items
		if items_size == -1
			let items := ar_Construct array
			let items[0] := 0
		endif
		let items := actor.GetEquippedItems
		let items_size := ar_Size items
		if items_size > 0
			let item := items[0]
		endif
		if items_size > 0 && item != 0
			; actor has some equipped, we need checks for armor/clothing
			set cb_slots to 63 ;head+hair+upper+lower+hands+feet
			set items_index to 0 
			while items_index < items_size
				set remove to 0
				let item := items[items_index]
				if eval (IsArmor item != 0 || IsClothing item != 0) ;cloth or armor?
					let temp := GetBipedSlotMask item
					if temp != 0
						if eval ( IsPlayable item == 0 && IsPlayable2 item == 0 ) && RomRAddBoundSpell.allownonplay == 0 ;non-playable item?
							set remove to 1
							let nonplay_slots := nonplay_slots | temp ; update "forbidden" slots
						else
							let temp := temp & cb_slots
							if temp == 0 ; no match
								set remove to 1
							endif
						endif
					else
						set remove to 1
					endif				
				else
					set remove to 1
				endif
				if remove != 0
				ar_Erase items items_index
					let items_size := ar_Size items
				else
					set items_index to items_index + 1
				endif
			loop
		endif
		;print "Init results for actor "+$actor+"."
		;print "Items remembered: "+$items_size
		;if items_size > 0
		;	set items_index to 0
		;	while items_index < items_size
		;		let item := items[items_index]
		;		if item != 0
		;			print $item
		;		else
		;			print "<empty>"
		;		endif
		;		set items_index to items_index + 1
		;	loop
		;endif
		;print "Non-play slot mask result: "+$nonplay_slots
		; now the equip part
		set items_index to 0 ;we will reuse this
		while items_index < bound_size
			let item := RomRAddBoundSpell.bound[items_index]
			if nonplay_slots != 0
				let temp := GetBipedSlotMask item
				let temp := nonplay_slots & temp
				if temp == 0
					actor.AddItemNS item 1
					actor.EquipItemNS item
				endif
			else
				actor.AdditemNS item 1
				actor.EquipItemNS item
			endif
			set items_index to items_index + 1
		loop
		set do_once to 1
		set RomRAddBoundSpell.spell_active to 1
	endif
end

begin ScriptEffectFinish
	set RomRAddBoundSpell.spell_active to 0
	;remove the bound armor
	set items_index to 0
	while items_index < bound_size
		let item := RomRAddBoundSpell.bound[items_index]
		set temp to actor.GetItemCount item
		if temp > 0
			if temp > 1
				actor.UnequipItemNS item 
			endif
			actor.RemoveItemNS item 1
		endif
		set items_index to items_index + 1
	loop
	; and equip back original items
	let items_size := ar_Size items
	set item to 0
	if items_size > 0
		let item := items[0]
	endif
	if items_size > 0 && item != 0
		set items_index to 0
		while items_index < items_size
			set temp to 0
			let item := items[items_index]
			if item != 0
				set temp to actor.GetItemCount item
			endif
			if temp > 0
				Call RomRCustomBoundFnAddRequest actor item 1
			endif
			set items_index to items_index + 1
		loop
	endif
	; unregister actor for a token
	let temp := ar_Find actor RomRAddBoundSpell.actors
	if temp >= 0
		ar_Erase RomRAddBoundSpell.actors temp
	endif
	; field cleanup
	ar_Resize items 0
	let items := ar_Null
	ar_Resize RomRAddBoundSpell.bound 0
	let RomRAddBoundSpell.bound := ar_Null
end

 

 

This script uses two small functions too for searching and to issue a equip request to a token:

scn RomRCustomBoundFnFindRequest
ref actor
int result
int index

begin function {actor}
	set result to -1
	if actor != 0
		let index := ar_Find actor RomRAddBoundSpell.requests
		if index >= 0
			set result to index
		endif
	endif
	SetFunctionValue result
end
scn RomRCustomBoundFnAddRequest
ref actor
ref item
int type
int size

begin function {actor, item, type}
	if actor != 0 && item != 0 && type != 0
		let size := ar_Size RomRAddBoundSpell.requests
		if size >= 0
			let RomRAddBoundSpell.requests[size] := actor
			let RomRAddBoundSpell.requests[size+1] := item
			let RomRAddBoundSpell.requests[size+2] := type
		endif
	endif
end

Small advice: define needed objects first (quest, spell, token etc.) to avoid compile errors and script malfunctions.

Edited by RomanR
Link to comment
Share on other sites

I'm sorry but further testing revealed a bug in script for token, in the "make bound items unequipable" part section of commands in both GameMode and MenuMode 1028 parts. Please add this line:

...		
set actors_index to 0
while actors_index < actors_size
        let actor := RomRAddBoundSpell.actors[actors_index] ;add this line please
	if actor != 0
...

It didn't occured to me at first because for the first time this variable was filled from checks when Oblivion was restarted or loaded save (as I was testing a spell with different characters). So for the first time or recasts this part worked, but after spell normally finished, next cast this part (rightly) detected this variable as empty and so it didn't do anything.

 

But crashing ... no, Oblivion didn't crashed for me as I was testing the original script with added check for enchanted items again and the new ones too.

 

Edit : MenuMode 1002 of course.

Edited by RomanR
Link to comment
Share on other sites

I see.The fact that you have your own Custom Bound spell is very cool.I have seen your script for a bit and it is interesting but I can't reply with anything useful for now.At least for some time. I'm quite busy at the moment. I'll reply again when I have time.Is that all right with you?

 

And thank you so much for the help you have given me all this time once again.

Link to comment
Share on other sites

Thanks, however the exact culprit for your crashes is still uknown. If you have difficulties to implement these scripts, I can send you an esp file from which they originated. If it will work for you, you can study it all you like. If you're interested, send me an email in private message. I will send you it at tomorrow afternoon when I return form work (maybe even today, if you will be quick).

Link to comment
Share on other sites

  • 2 weeks later...

Unless I'm mistaken, this is all you really need:

GetEquippedObject - the Oblivion ConstructionSet Wiki (uesp.net)

 

scn BasicHardCodedBoundArmor


ref item


begin scripteffectstart
;the slot number needs to be set to the slot your bound armor takes
set item to player.getequippedobject 1
player.additemns (boundarmor) 1
player.equipitemns (boundarmor)
end


begin scripteffectfinish
player.equipitem item
player.removeitemns (boundarmor) 1
end
Edited by Ortorin
Link to comment
Share on other sites

@Ortorin:

 

In fact no, the main problem arised on recast as we experienced crashes. In my case it was magical items, because the way OBSE handles them, they can't be unequipped and equipped in same frame. So they must be equipped one frame later, but as spell is finished already, that must be done by something else. I choose a token, in which I implemented very simple queue to equip original items, including syncing with spell through shared variables in quest. Spell was also made to wait until the original items were equipped to avoid crashing problems.

 

Worked for me, so I offered to razorblade457 finished esp and sent it to him. But as he become silent I fear that Oblivion still crashes for him.

Link to comment
Share on other sites

@Ortorin:

 

In fact no, the main problem arised on recast as we experienced crashes. In my case it was magical items, because the way OBSE handles them, they can't be unequipped and equipped in same frame. So they must be equipped one frame later, but as spell is finished already, that must be done by something else. I choose a token, in which I implemented very simple queue to equip original items, including syncing with spell through shared variables in quest. Spell was also made to wait until the original items were equipped to avoid crashing problems.

 

Worked for me, so I offered to razorblade457 finished esp and sent it to him. But as he become silent I fear that Oblivion still crashes for him.

 

I found the problem. I used a global on EffectStart, and checked for the equipped spell on EffectFinish. This works.

 

scn BasicHardCodedBoundArmor

ref item

begin scripteffectstart

    if BoundChest ;globalVar
        return
    endif


    set item to player.getequippedobject 2
    player.additemns EnchGlassCuirassResistNormalWeapons 1
    player.equipitemns EnchGlassCuirassResistNormalWeapons
    set BoundChest to 1
end




begin scripteffectfinish

    if player.iscasting
        if getplayerspell == TestBoundHelm ;The bound spell
            return
        endif
    endif

    if item
        player.equipitem item
    else
        player.unequipitemns EnchGlassCuirassResistNormalWeapons
    endif


    player.removeitemns EnchGlassCuirassResistNormalWeapons 1
    set BoundChest to 0
end
Link to comment
Share on other sites

  • Recently Browsing   0 members

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