RomanR Posted December 31, 2022 Share Posted December 31, 2022 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 More sharing options...
razorblade457 Posted January 1, 2023 Author Share Posted January 1, 2023 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 More sharing options...
RomanR Posted January 1, 2023 Share Posted January 1, 2023 (edited) 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 January 1, 2023 by RomanR Link to comment Share on other sites More sharing options...
RomanR Posted January 1, 2023 Share Posted January 1, 2023 (edited) 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 January 3, 2023 by RomanR Link to comment Share on other sites More sharing options...
razorblade457 Posted January 3, 2023 Author Share Posted January 3, 2023 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 More sharing options...
RomanR Posted January 3, 2023 Share Posted January 3, 2023 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 More sharing options...
Ortorin Posted January 12, 2023 Share Posted January 12, 2023 (edited) 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 January 12, 2023 by Ortorin Link to comment Share on other sites More sharing options...
RomanR Posted January 13, 2023 Share Posted January 13, 2023 @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 More sharing options...
Ortorin Posted January 14, 2023 Share Posted January 14, 2023 @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 More sharing options...
Recommended Posts