need help with my mod

Post » Wed Jun 20, 2012 8:27 pm

I released a decorator assistant mod (my only released mod) and recently been informed that there's a bug with it.

Some objects (potions, and some types of food) do not stay locked in place. Every time that the cell these objects are in gets reloaded, the affected objects drop a couple of units in the Z axis before locking in place again. I don't know what causes this, but I think it's something to do with the game engine, not with my mod.

The only way I can think of to fix this would be to store the information for each object's position and rotation, and moving the object to its stored location on cell load. Now obviously, I cannot place scripts on the items themselves. I also can't use reference aliases because some people go around placing hundreds, possibly thousands of objects and I don't really want to try making so many aliases.

So I have to store this information in arrays on some other object in the cell. The problem with this approach is that I have no way to tell when one of the placed objects is removed from the game world (put into inventory) and so no longer needs its information stored in arrays.

I'd thought to use http://www.creationkit.com/Is3DLoaded_-_ObjectReference in an OnUpdate loop to check whether or not the object has been removed. But the problem with this is room markers. Things in different rooms that aren't in LOS don't have their models loaded. Even though they don't have their models loaded, they are still in the game world, so that means using Is3DLoaded is unreliable.

So does anyone have some idea on how I can work around this?

EDIT: I've also tried using IsDisabled, IsInInterior, and GetPositionX with no results (they give the same results whether or not I add them to my inventory).
User avatar
Sam Parker
 
Posts: 3358
Joined: Sat May 12, 2007 3:10 am

Post » Thu Jun 21, 2012 9:36 am

OK, I've been messing around with my mod all day, and I've narrowed down the bug. The bug is that certain items will keep dropping a couple units in the Z axis every time its cell is loaded. These conditions are what causes the bug:
  • The item in question is a food item, potion, or certain types of miscellaneous objects. (Those are the ones I've discovered so far.)
  • The item is dropped manually from inventory. (http://www.creationkit.com/DropObject_-_ObjectReference scripting function doesn't count.)
  • The http://www.creationkit.com/MoveTo_-_ObjectReference or http://www.creationkit.com/SetPosition_-_ObjectReference scripting commands are used on the object. (The console commands don't count.)
  • The cell containing the item is loaded into memory. (So only after the cell has been purged from memory and then re-entered.)
So right now, I have two ways to get around this:
  • Create a duplicate and destroy the original.
  • Create tons of dummy objects to track the locations of the placed objects.
In regards to #1, I would use http://www.creationkit.com/PlaceAtMe_-_ObjectReference to create a new instance of the object, which means that it is not dropped from inventory. Then I disable and delete the original item. But there are some problems with this method:
  • Only 1 duplicate is made even if the original happens to be a stack of items.
  • I have no way to determine if the original object is persistent (or a quest item).
  • If the item is scripted, I could be breaking other peoples' mods by deleting the original and creating a new one.
In regards to #2, I encounter the problems I stated in the first post if I try to use a single object to try and store the locations of all the placed objects. Instead, each time I place a food/potion/misc object I will also need to spawn a dummy object at its location with this script:

Spoiler
Scriptname fg109TestObjScript extends ObjectReferenceObjectReference MyObjectBool Looping = TrueFunction SetUp(ObjectReference SomeObject)    SetScale(0.01)    MyObject = SomeObject    GoToState("Active")EndFunctionFunction CleanUp()    MyObject = None    Looping = False    Disable()    Delete()EndFunctionBool Function QueryObject(ObjectReference SomeObject)    if (SomeObject == MyObject)        Return True    else        Return False    endifEndFunctionState Active    Event OnLoad()        int count        while (!MyObject.Is3DLoaded() && Looping)            count += 1            if (count >= 10)                Debug.Trace("Placed object does not render, probably removed.")                CleanUp()                Return            endif        endwhile        if (GetDistance(MyObject) > 16.0)            Debug.Trace("Placed object is more than 16 units away, probably moved by player.")            CleanUp()            Return        endif        fg109TestObjScript TempRef = Game.FindClosestReferenceOfTypeFromRef(GetBaseObject(), MyObject, 16.0) as fg109TestObjScript        if !(TempRef == Self)            if (TempRef.QueryObject(MyObject))                Debug.Trace("I am defunct, there is a closer dummy object for MyObject.")                CleanUp()                Return            else                count = 0                while (count < 10 && Looping)                    TempRef = Game.FindRandomReferenceOfTypeFromRef(GetBaseObject(), MyObject, 16.0) as fg109TestObjScript                    if !(TempRef == Self) && (TempRef.QueryObject(MyObject))                        if (TempRef.GetDistance(MyObject) < GetDistance(MyObject))                            Debug.Trace("I am defunct, there is a closer dummy object for MyObject.")                            CleanUp()                            Return                        else                            Debug.Trace("TempRef is defunct, I am a closer dummy object for MyObject.")                            TempRef.CleanUp()                        endif                    endif                    count += 1                endwhile            endif        endif        MyObject.MoveTo(Self)    EndEventEndState

It's pretty rough right now, but the general idea is that since the dummy object is spawned at the same spot as the placed object, the OnLoad event should only fire whenever the placed object gets loaded. I couldn't use a single object with an OnCellAttach to check on all the placed objects in the cell because they might be in different room bounds, so they aren't loaded. The problems with this is approach:
  • Creates a lot of persistent objects.
  • It's messy.
  • Potential for lots of save game bloat.
So does anyone have a better idea of what I could try? If not, which of these two approaches would be better? Or should I just leave the mod as is and give a warning about the what causes the bug?
User avatar
cosmo valerga
 
Posts: 3477
Joined: Sat Oct 13, 2007 10:21 am

Post » Wed Jun 20, 2012 6:13 pm

Good work isolating the conditions :twirl:

Just brainstorming...

With #1
umm... will it ever be a quest item?
If it was persistent, instead of destroying could you move it into a temp placeholder container and redrop it via script?
(again there might be problems with modded items as this will cause events to happen)


With #2 You might be able to get around the persistence issue
Have the placeholder store the baseID, so it knows what type of object it is looking after.
Convert the RefID to an int (and thrown away the mod index part)
In theory, it will be able to get the ref back by using findclosest and then checking the ref->int again (?)
(Not perfect, and it still feels messy.)

Edit:
Umm, as you can detect the ref and baseID as an int, then it should be possible to detect which items are vanilla.
You could then take a leap of faith that there isn't a mod that uses RedefineWorld() on those base potions/food and rewpawn them using #1 (?)
User avatar
David Chambers
 
Posts: 3333
Joined: Fri May 18, 2007 4:30 am

Post » Wed Jun 20, 2012 7:01 pm

Adding the object to a container and then calling DropObject on it was one of the first things I tried. Unfortunately it doesn't work. I think the reason is because when I add the object to a container, I need to have the reference stored in a variable or property in order to manipulate it.

Since it's stored in a script, and the script is running, the item is persistent. Since it's persistent, the item that gets added into the inventory is the same object that was dropped earlier (I believe that if items are not persistent, then this is not necessarily true). And I think DropObject and RemoveItem will attempt to remove the most recently added item in the inventory, so I end up with the same item I started with.

The item displays the same bug as though I had manually dropped it. So the whole process of adding and removing items from a container does nothing. It also has the same problem in that I can only manipulate one out of a stack of items.

Your suggestion about storing the ref ID and base ID as ints is nice though. That would make the second approach much better so that it wouldn't cause so much bloat. I think I'm going to try it out.

How high can an Int variable go? FFFFFFFF would be 4294967295 in decimal.

Thanks for your help! :biggrin:
User avatar
SWagg KId
 
Posts: 3488
Joined: Sat Nov 17, 2007 8:26 am

Post » Thu Jun 21, 2012 7:01 am

Re the container idea, yeah that makes sense. The persistent ref probably doesn't get re-made, and just shifted to some 4th dimension while it's "in the container".

The int's are 32bit signed (-2,147,483,648 to 2,147,483,647). The most significant bit is the sign bit. So all items with a mod index above 0x7f will be negative, including the items that are spawned inworld - 0xff is reserved for spawned items.

Without 32bit binary operators, throwing the mod-index part away when it's negative is not straight forward. Sorry I wasn't thinking about that when I posted last.
I posted a fake 31bit binary ops function a while ago on here, but avoided the 32 bit problem as my brain can't think upside down. I'd wait for true binary ops with SKSE.
User avatar
djimi
 
Posts: 3519
Joined: Mon Oct 23, 2006 6:44 am

Post » Thu Jun 21, 2012 2:18 am

You could have the house attach a script to every item in it when the player leaves that stores it's exact position and rotation, and moves it back there on the OnCellLoad() event after a short wait.

OR, you could have it set every object inside to have Keyframed movement when the player leaves until, say, 5 seconds after the player returns. That way they will ONLY move if something deliberately moves them with a translate or move command.
User avatar
josie treuberg
 
Posts: 3572
Joined: Wed Feb 07, 2007 7:56 am

Post » Thu Jun 21, 2012 7:46 am

I uploaded a new version of my mod yesterday and let the player choose which approach they felt was better (between creating a duplicate and destroying the original, or saving the position with dummy objects).

Without 32bit binary operators, throwing the mod-index part away when it's negative is not straight forward. Sorry I wasn't thinking about that when I posted last.

That suggestion helped me a lot. I realized after I asked about it that it doesn't matter if the stored integer is negative or not, as long as it's unique. The integer variables can store values up to 2,147,483,647 but 2,147,483,648 becomes -2,147,483,648 and 2,147,483,649 becomes -2,147,483,647 and so on.

So yes, the ref ID will be some negative number almost all the time, but it's still going to be unique to each item, so I can still use it anyway.

You could have the house attach a script to every item in it when the player leaves that stores it's exact position and rotation, and moves it back there on the OnCellLoad() event after a short wait.

OR, you could have it set every object inside to have Keyframed movement when the player leaves until, say, 5 seconds after the player returns. That way they will ONLY move if something deliberately moves them with a translate or move command.

Yes, I'd considered the first method but as I said, I was worried about http://www.creationkit.com/Bethesda_Tutorial_Optimization#Room_Markers_and_Portals. If an object is in a different room and therefore not rendered, is it still loaded and can be manipulated with MoveTo? Sorry, I hadn't done any testing and just assumed that it wasn't possible.

The keyframed movement actually doesn't do anything to help. The placed objects are usually locked into place with keyframed movement but they still drop a couple units in the Z axis every time the cell is loaded.
User avatar
Eilidh Brian
 
Posts: 3504
Joined: Mon Jun 19, 2006 10:45 am

Post » Thu Jun 21, 2012 1:04 am

Roombounds don't affect MoveTos or other object-related effects...the objects are still there, they are just ignored by the render engine.
User avatar
butterfly
 
Posts: 3467
Joined: Wed Aug 16, 2006 8:20 pm

Post » Thu Jun 21, 2012 2:06 am

Good to know, I'll keep it in mind if I need to do another update to my mod. :)
User avatar
Nancy RIP
 
Posts: 3519
Joined: Mon Jan 29, 2007 5:42 am

Post » Thu Jun 21, 2012 2:23 am

Could it be some kind of rounding error? If it's affecting keyframed objects, I'm not sure what could make them move.
User avatar
TWITTER.COM
 
Posts: 3355
Joined: Tue Nov 27, 2007 3:15 pm

Post » Wed Jun 20, 2012 6:12 pm

I doubt it's some kind of rounding error, since the item moves anywhere from 2 to 40 units from its original position. Also, like I said in my second post, it only affects certain types of objects. This is one of the scripts I used to narrow down the problem:

Scriptname TestMEScript extends ActiveMagicEffectEvent OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)    if (akItemReference && !akDestContainer)        Actor Player = GetTargetActor()        Game.DisablePlayerControls()        Game.EnablePlayerControls()        while !akItemReference.Is3DLoaded()            ;wait for the item to be loaded        endwhile        if !Player.IsSneaking()            akItemReference.SetMotionType(akItemReference.Motion_Keyframed, False)        endif        akItemReference.MoveTo(Player, 0, 0, 100)    endifEndEvent

You can try it and find the same results. Just drop a potion from your inventory, go to a new cell, use the console command "PCB", and return to find the items have moved.
User avatar
Julie Ann
 
Posts: 3383
Joined: Thu Aug 23, 2007 5:17 am


Return to V - Skyrim