Summon Object

From the Oblivion ConstructionSet Wiki
Jump to navigation Jump to search

It's now known that the PlaceAtMe command is likely to cause bloating. Used a few times, no problem. Used a lot of times, problem. However, you can often avoid it by defining a permanent ref object and moving it to the player. But suppose that you don't want it right at the player's feet, but instead in front of them?

Here are some considerations:

  • MoveTo does not allow relative positioning. You'll need to move to the player and then reposition relative to player.
  • You'll need sine/cosine functions to get x,y offset relative to player heading, but oblivion doesn't have those functions. You can approximate them with polynomials. (The one used here comes from Handbook of Mathematical Functions.
  • Moving a displayed object fails to update its collision data (notably required for getting activation cursors to work correctly). You can fix this by disabling the object and the re-enabling it.
  • However, if you move, disable and then re-enable, the object will appear briefly at the player and then reappear at new position. Instead, you should disable, move and then re-enable. And there should be a time gap of a frame or two before enabling and disabling.
  • The original ref has to be placed somewhere and set to persistent. If you want the original to be unfindable, a simple thing to do is place it in the Aleswell dummy cell.

So here's a chunk of code that does this for Wrye's Summon Bed mod. (Some irrelevant code trimmed for the purpose of clarity.)

; This is a bare bones version of my summon bed script effect. It moves
; a permanent ref bed to in front of the PC.
scn wrSleepBedME
float pos
float ang ;(the measure of the angle, first deg, then radians)
float ang2 ;(ang^2) Just for convenience. Could use ang*ang directly.
float ang4 ;(ang^4) Just for convenience. Could use ang*ang*ang*ang directly.
float sign ;sign correction
float sin ;(sine of the angle)
float cos ;(cosine of the angle)

begin scriptEffectStart
    ;Because world activation and collision data don't update correctly,
    ;need to first disable and then reenabled. So here, item is disabled.
    ;Spell lasts one second, so re-enabling in next block happens with 
    ;decent time gap.
    wrSleepBedRef.disable
end

begin scriptEffectFinish
    ; Now, want to summon bed and place it in front of player at 150 units.
    ; So, need to get player heading (north == 0, increasing clockwise, 
    ; (east == 90), going up to 360.) Then use fourth/fifth order polynomial
    ; approximations for sin and cos.
    ;--sin, cos
    set sign to 1
    set ang to player.getAngle z
    if ang > 270
        set ang to ang - 360
    elseif ang > 90
        set ang to ang - 180
        set sign to -1
    endif
    set ang to 0.017453*ang 
    set ang2 to ang * ang
    set ang4 to ang2 * ang2
    set sin to sign*ang*(1 - 0.16605*ang2 + 0.00761*ang4)
    set cos to sign*(1 - 0.4967*ang2 + 0.03705*ang4)

    ;--Move it
    wrSleepBedRef.moveTo player 

    set pos to player.getPos x + 150*sin
    wrSleepBedRef.setPos x pos
    
    set pos to player.getPos y + 150*cos
    wrSleepBedRef.setPos y pos

    set pos to player.getPos z + 55 ;--Tweak offset to put object on ground.
    wrSleepBedRef.setPos z pos

    ;--Set angle of bed relative to PC heading
    set ang to player.getAngle z + 90
    wrSleepBedRef.setAngle x 0
    wrSleepBedRef.setAngle y 0
    wrSleepBedRef.setAngle z ang

    ;--Done
    wrSleepBedRef.enable
end