GamerRick Posted June 30, 2023 Share Posted June 30, 2023 (edited) Supposedly this command will give you an array of stacks of an item in a container. I want to count the empty soulgems in the player's inventory for soulgems that match the target's soul size. My Challenge: To display the number of empty soul gems that match the target's soul size every time I cast a soul trap spell on an Actor. Here is what I have: if rTarget.IsCreature == 0 set iSoulSize to 6 let arrSoulGems := PlayerRef.GetInvRefsForItem BlackSoulGem elseif rTarget.GetLevel >= 18 set iSoulSize to 5 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty5Grand elseif rTarget.GetLevel >= 13 set iSoulSize to 4 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty4Greater elseif rTarget.GetLevel >= 7 set iSoulSize to 3 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty3Common elseif rTarget.GetLevel >= 2 set iSoulSize to 2 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty2Lesser else set iSoulSize to 1 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty1Petty endif set iPetty to 0 set iLesser to 0 set iCommon to 0 set iGreater to 0 set iGrand to 0 set iBlack to 0 set bAzuraStar to 0 if arrSoulGems let iArrSize := ar_Size arrSoulGems set iArrSize to iArrSize - 1 PrintC "Array size is %g" iArrSize while iArrSize >= 0 let rItem := arrSoulGems[iArrSize] ;; FIXED!!! <<============ I DON'T THINK THIS WORKS. let rItemBase := rItem.GetBaseObject let iCurrSoulLevel := rItem.GetCurrentSoulLevel let iGemCount := rItem.GetRefCount PrintC "RFFnOnSoulTrap ------ Stack Count %g for %n is %g, Current Soul Level = %g", iArrSize, rItemBase, iGemCount, iCurrSoulLevel if iCurrSoulLevel == 0 && iGemCount > 0 ; Soulgem & empty if iSoulSize == 1 if rItemBase == SoulGemEmpty1Petty ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iPetty to iPetty + iGemCount endif elseif iSoulSize == 2 if rItemBase == SoulGemEmpty2Lesser ;rItem.GetSoulGemCapacity == 2 ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iLesser to iLesser + iGemCount endif elseif iSoulSize == 3 if rItemBase == SoulGemEmpty3Common ;rItem.GetSoulGemCapacity == 3 ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iCommon to iCommon + iGemCount endif elseif iSoulSize == 4 if rItemBase == SoulGemEmpty4Greater ;rItem.GetSoulGemCapacity == 4 ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iGreater to iGreater + iGemCount endif elseif iSoulSize == 5 if rItemBase == AzurasStar set bAzuraStar to 1 elseif rItemBase == SoulGemEmpty5Grand ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iGrand to iGrand + iGemCount endif elseif iSoulSize == 6 if rItemBase == BlackSoulGem ;PrintC "RFFnOnSoulTrap ------ Stack Count for %n is %g, Current Soul Level = %g", rItemBase, iGemCount, iCurrSoulLevel set iBlack to iBlack + iGemCount endif endif endif set iArrSize to iArrSize - 1 loop ; endifIf I dump that array I get, I see the inventory refs it gives stacks of that soulgem, but I my code to process that array does not work. Thanks! OOPS. I have a typo in the array index variable. Now that I fixed that I am seeing the same problem I have trying to loop through the player with the foreach command. The soulgem stacks I am getting are not what is in my inventory. I just tested it with 18 empty Common soulgems in my inventory. When cast on a Fire Atronauch (a Common soul), my code reports there are 33 empty Commons. In the console I print each item from the array on a single. There is an item in the stack that has a count of 33 and a current soul size of 0 (empty). I cannot get the number of gems that the code finds to match what is in my inventory. Edited June 30, 2023 by GamerRick Link to comment Share on other sites More sharing options...
RomanR Posted July 1, 2023 Share Posted July 1, 2023 (edited) I don't how Oblivion handles soul gems when used, so I would make some notices and questions too: 1. Maybe it's only a formating of your script, but I feel that is something amiss with iSoulSize selection. Personaly I would do it like this: ;just for sure init of iSoulSize and cleaning a field set iSoulSize to 0 let iArrSize := ar_Size arrSoulGems if iArrSize > 0 ar_Resize arrSoulGems 0 endif ;selecting soul level and filling a field if rTarget.IsCreature == 0 ;no creature? set iSoulSize to 6 let arrSoulGems := PlayerRef.GetInvRefsForItem BlackSoulGem else ;checking creature level if rTarget.GetLevel >= 18 set iSoulSize to 5 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty5Grand elseif rTarget.GetLevel >= 13 set iSoulSize to 4 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty4Greater elseif rTarget.GetLevel >= 7 set iSoulSize to 3 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty3Common elseif rTarget.GetLevel >= 2 set iSoulSize to 2 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty2Lesser else set iSoulSize to 1 let arrSoulGems := PlayerRef.GetInvRefsForItem SoulGemEmpty1Petty endif endif 2. That condition "if arrSoulGems" - can it be used like this? I would remove it and make a condition: ... let iArrSize := ar_Size arrSoulGems ;checking size after using OBSE function PrintC "Array size is %g" iArrSize if iSoulSize != 0 && iArrSize > 0 ;both variable and field are filled set iArrSize to iArrSize - 1 ... 3. So when you used a filled gem it will become one with "SoulGemEmpty..." as base object? Edited July 1, 2023 by RomanR Link to comment Share on other sites More sharing options...
GamerRick Posted July 2, 2023 Author Share Posted July 2, 2023 (edited) The "if arrSoulGems" is just making sure it actually is referencing something, since I don't know what the OBSE function will return if there are no soulgems of that type in my inventory. It either returns null or it returns an empty array. This way, I cover my ass either way. The soultrap spell works by finding an empty soulgem in your inventory and then doing a SetCurrentSoulLevel on it. So the item remains the same base object (like SoulGemEmpty3Common) but is now different from the other ones by info saved in that instance of the object. The only way to get an inventory ref on an object in your inventory (or any container for that matter) is by using a foreach loop or using the GetInveRefsForItem. For the latter I should have one array item that is a stack of the soulgems that are still empty. I am getting something else. The numbers are way off. I have been plugging away with this for over a week now. I am out of ideas and things to try. For now, I want to figure out how to wirte debug to a file using pluggy. The only mod I have that uses it successfully is RefScope. Wish me luck.... :ermm: Edited July 2, 2023 by GamerRick Link to comment Share on other sites More sharing options...
RomanR Posted July 3, 2023 Share Posted July 3, 2023 For me I experienced the second case - if I define a field and leave it empty, the calling functon which returns a field will not change it in case of empty results. So afterwards I update the size variable and if field is empty (size 0), I can be sure that function returned nothing. As it seems that your script you showed is a part of whole I'm assuming that before using GetInvRefsForItem the field must be empty, so the cleanup before this function or aftewards after processing is needed. Documentation of OBSE is in my opinion unclear what happens to a field when the script ends, so I'm always using ar_Resize command to zero and destroying a field using ar_Null at end of my scripts, constructing it again on start. Poking at OBSE docs I see that GetCurrentSoulLevel function is used on actor, not a Soul Gem. For gem I see GetSoulLevel - "returns the soul level currently in the soul gem". Link to comment Share on other sites More sharing options...
RomanR Posted July 3, 2023 Share Posted July 3, 2023 Scrap that part beginning "Poking at OBSE docs..." part. After I made testing function for it it seems that docs part is somewhat wrong. I managed to use GetCurrentSoulLevel on a field filled by soul gems and it's really returning their current soul level. But problem arised - I can't manage GetCurrentSoulLevel or GetSoulLevel to make it work on actors, be it reference or base object. They always return zero. Oh well, at least I can present my test function for you to play with. It has two inputs - base object of soul gem and its soul level. After calling it return a count of matching soul gems of requested soul level. scn RomRFnMatchGems ref gem ;input soul gem as base object!!! ref gem2 int size int index int count2 ;output count of matched gems int count int s_level ;input soul level int g_level array_var soulGems begin function {gem, s_level} set count2 to 0 let size := ar_Size soulGems if size == -1 let soulGems := ar_Construct array endif let size := ar_Size soulGems if gem != 0 && size == 0 ;gem reference is valid and field is defined if s_level >= -1 && s_level < 6 ; soul level is within range -1 = all levels, 0 - 5 from petty to grand let soulGems := player.GetInvRefsForItem gem let size := ar_Size soulGems if size > 0 set index to 0 set count to 0 while index < size let gem2 := soulGems[index] let g_level := gem2.GetCurrentSoulLevel if s_level == -1 let count := gem2.GetRefCount ;print "All match mode:" ;print $gem2+" count: "+$count+" level: "+$g_level else if g_level == s_level let count := gem2.GetRefCount ;print $gem2+" count: "+$count+" level: "+$g_level else set count to 0 endif endif set count2 to count2 + count set index to index + 1 loop endif endif endif ;cleanup let size := ar_Size soulGems if size > 0 ar_Resize soulGems 0 endif let soulGems := ar_Null SetFunctionValue count2 end Link to comment Share on other sites More sharing options...
GamerRick Posted July 3, 2023 Author Share Posted July 3, 2023 (edited) Yea I know, that is why I do the level check on creatures to get their soul level. Because NOTHING else I tried works on creatures that are not a hard coded level but a PC level offset instead. Same with the various Get commands that return a soul level. The GetSoulLevel takes a BaseID, not a reference. Though you could be right about the GetCurrentSoulLevel being made for actors. It seems to work for the soulgem inventory refs I have to identify the ones I filled already. And yes, I do have a command to get rid of the array below that (to enable the garbage collector to take it): let arrSoulGems := ar_null dfd Edited July 3, 2023 by GamerRick Link to comment Share on other sites More sharing options...
RomanR Posted July 4, 2023 Share Posted July 4, 2023 Well, if all formal needs were met, I'm at my wits end too. If you're in mood you can try my fuction to compare the result of yours and mine, but as I can't see some obvious error in your script, I don't know what to suggest else. Link to comment Share on other sites More sharing options...
GamerRick Posted July 4, 2023 Author Share Posted July 4, 2023 (edited) Thanks for trying. Your code is doing exactly the same thing mine is as far as looping through the array you get from the GetInvRefsForItem. Did you test it to see if it works?? If you do the ar_dump command on the array, what do you see in the console? If I have 8 empty common soulgems on me (8 SoulGemEmpty3Common that are empty and none that have a soul in them), I expect to see ONE stack in the array where the RefCount is 8 and the current soul level is 0. I get an array of 10+ items. Most have a refcount of -1. Then I see one stack with 16 empty soulgems. Other times I may see one stack with 1 and another with 15. The results I have been getting aren't totally repeatable. In fact it works fine with Grand and Black soulgems. Why not the others then? So it seems to be the GetRefCount instruction that is failing here. I have seen other weird things with the stacks I get from the foreach command. If there is nothing wrong with the script, and you don't see the weirdness I am seeing, it's possible my game is corrupted or something is broken with OBSE. I already asked the OBSE person about this, but (as you probably already know) they won't even consider that there's something wrong with their code unless I can prove it beyond any doubt. So, I am trying to get to the bottom of this. So, what I really need here is for someone else to try this out and see if it works for them or not. One wild guess I have now is that the latest xOBSE and/or some plugin (DLL file) I installed is not compatible with Win 7. But I reverted to OBSE 21 and started from an old save to test this, but I get the same wrong results. Thanks. Edited July 4, 2023 by GamerRick Link to comment Share on other sites More sharing options...
RomanR Posted July 4, 2023 Share Posted July 4, 2023 (edited) Yes, I tested it. I took to my inventory one black soul gem (empty), 5 empty grand souls gems and 4 grand soul gems filled with grand soul.I went to some cave and began testing my function with simple work flow - casting my test spell before combat and after a creature died, capturing its soul to one of empty grand soul gems. A function had a print commands uncomented and for test used -1 as soul level parameter to show all grand gems regardless os soul level filled. The printouts didn't showed any oddities, so I posted my function here. Test touch spell was used with this script: scn RomRSoulGemMatchSpellScript ref actor ref a_base ref gem short init int index int size int a_soul int g_soul int g_count int count array_var soulGems begin ScriptEffectStart set actor to GetSelf if actor != 0 print "Target hit" if actor.IsActor != 0 ;&& actor.GetDead == 0 if actor.GetIsCreature != 0 let a_base := actor.GetBaseObject let a_soul := actor.GetCurrentSoulLevel if a_soul == 0 set a_soul to 1 endif else set a_soul to 6 ;actor is a human endif endif endif print $actor+"'s soul level is: "+$a_soul if a_soul > 0 if a_soul == 6 set gem to BlackSoulGem set g_soul to -1 let count := Call RomRFnMatchGems gem g_soul set gem to BlackSoulGemGrand let count := Call RomRFnMatchGems gem g_soul else set g_count to 0 set g_soul to -1 set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGem5Grand5GrandSoul let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGem5Grand4GreaterSoul let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGem5Grand3CommonSoul let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGem5Grand2LesserSoul let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGem5Grand1PettySoul let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count print "This part found "+$g_count+" matching gems" endif endif end It's a very quick work, due to not working GetCurrentSoulLevel the soul level of creature reported will be always 1 and creature part check only grand soul gems now. Edit: Oh sorry, I missed part in your post saying about testing grand and black soul gems. But as I inserted ar_Dump command in my function and testing calls to it using petty and lesser soul gems (I "borrowed" some from a local Mage Guild), comparing results printed by function and ar_Dump command didn't show anything unusual again (certainly not negative stack values). So at least on my computer these two functions behave normal (GetRefCount and GetInvRefsForItem). Edited July 4, 2023 by RomanR Link to comment Share on other sites More sharing options...
RomanR Posted July 8, 2023 Share Posted July 8, 2023 (edited) OK, as further testing of my function doesn't reveal any problem, I made enchanced version of my script test spell which calls this function. I made also success on finding right OBSE function to use: it's "GetCreatureSoulLevel" or "GetActorSoulLevel". But for creatures with PCLevelOffset you still need a check based on their level, as both functions report what is edited in actor's edit window only. Outputs are to console only and script doesn't report Azura's Star. Here it is: scn RomRSoulGemMatchSpellScript ref actor ref gem short init int index int size int a_soul int a_soul2 int g_soul int g_count int count short match short higher string_var str_soul begin ScriptEffectStart set actor to GetSelf if actor != 0 print "Target hit" if actor.IsActor != 0 ;&& actor.GetDead == 0 if actor.GetIsCreature != 0 ; level check is based on a chart from UESP website set a_soul2 to actor.GetLevel print $actor+" is at level "+$a_soul2+"." if a_soul2 == 1 set a_soul to 1 ;petty elseif a_soul2 > 1 && a_soul2 < 7 set a_soul to 2 ;lesser elseif a_soul2 >= 7 && a_soul2 < 13 set a_soul to 3 ;common elseif a_soul2 >= 13 && a_soul2 < 18 set a_soul to 4 ;greater elseif a_soul2 >= 18 set a_soul to 5 ;grand endif let a_soul2 := actor.GetCreatureSoulLevel ; if these two checks doesn't match if a_soul != a_soul2 print "Soul checks for this creature doesn't match!" print "From level check: "+$a_soul+" OBSE function: "+$a_soul2 if actor.IsPCLevelOffset print "Creature has level offset - selected soul level from level check." else ;OBSE function seems reporting static level creatures right set a_soul to a_soul2 print "Creature has static level - selected soul level from OBSE function." endif endif else set a_soul to 6 ;actor is a human endif endif endif if a_soul > 0 print $actor+"'s soul level is: "+$a_soul set match to 0 set higher to 0 if a_soul == 6 let str_soul := "Black" set g_count to 0 set g_soul to 0 set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count endif else set g_count to 0 set g_soul to 0 ;finding matching or higher capacity soul gems if a_soul == 1 let str_soul := "Petty" set gem to SoulGemEmpty1Petty let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count else set gem to SoulGemEmpty2Lesser let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty3Common let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty4Greater let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set higher to g_count endif endif endif if a_soul == 2 let str_soul := "Lesser" set gem to SoulGemEmpty2Lesser let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count else set gem to SoulGemEmpty3Common let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty4Greater let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set higher to g_count endif endif endif if a_soul == 3 let str_soul := "Common" set gem to SoulGemEmpty3Common let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count else set gem to SoulGemEmpty4Greater let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set higher to g_count endif endif endif if a_soul == 4 let str_soul := "Greater" set gem to SoulGemEmpty4Greater let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count else set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set higher to g_count endif endif endif if a_soul == 5 let str_soul := "Grand" set gem to SoulGemEmpty5Grand let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count set gem to BlackSoulGem let count := Call RomRFnMatchGems gem g_soul set g_count to g_count + count if g_count > 0 set match to g_count endif endif endif if match > 0 print "You have "+$match+" empty "+$str_soul+ " Soul Gem(s) matching this soul." elseif higher > 0 print "You have "+$higher+" empty soul gem(s) with higher capacity, but this would be a waste." endif endif ;cleanup sv_Destruct str_soul end I used both soul checks because there are some exceptions where level check only will give wrong results (for example horses - they have level 1, but lesser soul edited). Edited July 8, 2023 by RomanR Link to comment Share on other sites More sharing options...
Recommended Posts