SetPos not working

Post » Thu Jul 07, 2016 2:21 pm

I tried to write a simple warp script that would let me pull a daedra towards me without using AiFollow (because I don't want them leaving the cell they're in), but the SetPos part of the script has been acting really flaky. Sometimes it works once or twice during my tests and then stops working forever. Other times I load the game (a fresh test save) and it doesn't work at all. SV shows me the co-ordinates are being set. The script executes everything, it's just the SetPos that doesn't happen. Here's the script:



Begin Uvi_DaeSc_Summon
; For daedra bound to crystal ball

Short OnPCHitMe
Short combatCheck
Short warp
Float timer
Float myX
Float myY
Float playerX
Float playerY
Float playerZ

If ( OnPCHitMe ) ;do nothing
Set OnPCHitMe to 0
Endif
If ( OnActivate ) ;do nothing
Endif
If ( OnDeath )
Set timer to 5
Endif
If ( MenuMode )
Return
Endif

If ( Player->GetSpellEffects uvi_daesc_call )
If ( GetDisabled )
Enable
Endif
If ( warp < 1 )
Set warp to 1
Endif
Elseif ( GetSpellEffects uvi_daesc_dismiss )
PlaySound3D "conjuration hit"
Disable
Elseif ( GetSpellEffects uvi_daesc_pull )
If ( warp < 1 )
Set warp to 1
Endif
Elseif ( Player->GetSpellEffects mel_tpp_XWait )
AiWander 0 0 0 0 50 50
Elseif ( Player->GetSpellEffects mel_tpp_XWander )
AiWander 1000 0 0 0 50 50
Elseif ( Player->GetSpellEffects mel_tpp_XStpCmbt )
StartCombat Player
StopCombat
Endif

If ( timer )
Set timer to ( timer - GetSecondsPassed )
If ( timer < 0 )
Set timer to 0
Endif
Return
Endif

If ( GetHealth < 1 )
PlaySound3D "conjuration cast"
Resurrect
Return
Endif

If ( combatCheck < 1 )
If ( GetSoundPlaying "Weapon Swish" )
Set combatCheck to 1
ElseIf ( GetSoundPlaying "crossbowShoot" )
Set combatCheck to 1
ElseIf ( GetSoundPlaying "bowShoot" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "Health Damage" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "destruction hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "frost_hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "shock hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "alteration hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "illusion hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "Hand To Hand Hit" )
Set combatCheck to 1
Elseif ( GetSoundPlaying "Hand To Hand Hit 2" )
Set combatCheck to 1
Elseif ( combatCheck == -1 )
AiWander 1000 0 0 0 50 50
Set warp to 0
Set combatCheck to 0
EndIf
Elseif ( combatCheck == 1 )
If ( warp == 0 )
Set warp to 1 ;warp to player
Else
Set timer to 5
Endif
Set combatCheck to -1
Endif

If ( warp < 1 )
Return
Elseif ( warp == 1 ) ; pull to player
Set myX to GetPos x
Set myY to GetPos y
Set playerX to ( Player->GetPos x )
Set playerY to ( Player->GetPos y )
Set playerZ to ( Player->GetPos z )
Set warp to 2
Elseif ( warp == 2 )
If ( playerX < myX )
Set myX to ( playerX + 128 )
Else
Set myX to ( playerX - 128 )
Endif
If ( playerY < myY )
Set myY to ( playerY + 128 )
Else
Set myY to ( playerY - 128 )
Endif
Set warp to 3
Elseif ( warp == 3 )
SetPos x myX ;doesn't work
SetPos y myY ;doesn't work
SetPos z playerZ ;doesn't work
Set warp to 4 ;does work
Elseif ( warp == 4 )
RemoveSpellEffects uvi_daesc_pull
If ( combatCheck )
AiEscort Player 0 0 0 0 0
Set warp to -1
Else
AiWander 0 0 0 0 50 50
Set warp to 0
Endif
Set timer to 5
Endif

End

I've been using the uvi_daesc_pull spell to test it, because all that does is the warp. I know the spell is triggering the script properly, because as I said, all the variables are being set properly. It just decides, "Nah, I'm not gonna actually do the SetPos part," and skips to warp 4. It isn't the timer interfering either, because I check that it's reset to 0 before casting again.

User avatar
Vickytoria Vasquez
 
Posts: 3456
Joined: Thu Aug 31, 2006 7:06 pm

Post » Thu Jul 07, 2016 5:58 am

I'm not sure how helpful it will be, but I would try to add some messageboxes like this:



set testvar1 to GetPos x
set testvar2 to GetPos y
set testvar3 to GetPos z
Messagebox "Old pos x:%.5f, y:%.5f, z:%.5f", testvar1, testvar2, testvar3
Messagebox "My pos x:%.5f, y:%.5f, z:%.5f", myX, myY, playerZ
SetPos x myX
SetPos y myY
SetPos z playerZ
Set warp to 4
set testvar1 to GetPos x
set testvar2 to GetPos y
set testvar3 to GetPos z
Messagebox "New pos x:%.5f, y:%.5f, z:%.5f", testvar1, testvar2, testvar3

Also I think that SetPos checks for collision and may act weird if there is any obstacle.



You can try to disable it, then setpos and then enable it again.

User avatar
Elisabete Gaspar
 
Posts: 3558
Joined: Thu Aug 31, 2006 1:15 pm

Post » Thu Jul 07, 2016 10:16 pm


Disabling/Enabling did the trick. Thanks! :)



Seeing as you've been playing around with Grumpy's warp scripts, can you think of the simplest way to improve my warp so the daedra always warps directly in front of the player? My math is so rusty, I don't even know where to start.

User avatar
GEo LIme
 
Posts: 3304
Joined: Wed Oct 03, 2007 7:18 pm

Post » Thu Jul 07, 2016 6:10 am


Directly in front of player? Not much related to the template but I think you will need cosine and sine functions.. or at least one of them since sin x = sqrt(1 - (cos x)^2), which can be calculated in tes script as


set a to myValue * myValue


set a to 1 - a


set a to GetSquareRoot a



First you need to move it to player and then find the correct vector to offset it in the way player is looking. I think something like this might work, although I'm not 100% sure



;first setpos directly to player
set playerX to player->GetPos x
set playerY to player->GetPos y
set playerAngle to player->GetAngle z;get player angle
set playerAngle to playerAngle + 180;transform it from -180,180 interval to 0,360
set x to cos(player->GetAngle z) ;cosine of player angle
set y to sin(player->GetAngle z)
set newX to playerX + x
set newY to playerY + y
SetPos x currentX
SetPos y currentY

There is sine and cosine function in some math library that is linked in MW scripting for dummies, so you can check it out.

User avatar
jennie xhx
 
Posts: 3429
Joined: Wed Jun 21, 2006 10:28 am

Post » Thu Jul 07, 2016 7:58 am

Or try this without the cosine:
;first setpos directly to playerset playerX to player->GetPos xset playerY to player->GetPos y;get player angleset playerAngle to player->GetAngle z;transform it from -180,180 interval to 0,360set playerAngle to playerAngle + 180;should be rough approximation of cosine using two functionsif(playerAngle < 180)set x to 1 -(playerAngle/90)elseset x to ((playerAngle-270)/90)endif;should be rough approximation of sine using three functionsif(playerAngle < 90)set y to (playerAngle/90)elseif (playerAngle < 270)set y to 1 -(playerAngle - 90/90)elseset y to (playerAngle - 270/90) - 1endifset newX to playerX + (x * 50)set newY to playerY + (y * 50)SetPos x currentXSetPos y currentY

it should aproximate sine and cosine functions. The approximation is very rough, but should be sufficient for this purpose. But I might make some stupid mistake, it is almost 2 AM here.


EDIT: Scrap all of that, there is much simplier solution. Use PlaceAtPc to create invisible unique activator in from the script in front of player and then read its coordinates by getpos and use those. Also place script with timer on the activator that will delete it after 5 seconds or so. This should be simple and reliable.
User avatar
Janeth Valenzuela Castelo
 
Posts: 3411
Joined: Wed Jun 21, 2006 3:03 am

Post » Thu Jul 07, 2016 1:52 pm


Unfortunately the placed activator solution doesn't work. Since it doesn't exist in the game until it's placed (otherwise it wouldn't be unique), the game gives annoying warnings about that when you load. Then, once in-game, the daedra doesn't do anything, as if its entire script has failed to compile because of it. Referencing the daedra to give it the activator's position is also impossible, because of what I wrote in the other thread about it not being a persistent enough reference to unbunch the game engine's panties either. If I do place the daedra in a storage cell, everything insists on reverencing that version instead of the one that's actually loaded in the same cell with me.



I tried your second script, but couldn't get it to work properly. It kinda works, sometimes, but mostly not--teleporting the daedra who-knows-where. Other times it just doesn't teleport at all. At one point the daedra even froze in place and stopped taking commands entirely. I guess I'm left trying your first script, after I figure out the cosine stuff.



Edit: Found one that works in one of my companion's warp scripts (one that calls them to the player which makes them show up in front of you).



Set warpZ to ( Player->GetPos z )
Set warpx to ( Player->GetPos x )
Set warpy to ( Player->GetPos y )
Set pa to ( Player->GetAngle z )
If ( pa < 60 )
If ( pa > -60 )
Set warpY to ( warpY + 50 )
Elseif ( pa < -120 )
Set warpY to ( warpY - 50 )
Endif
Elseif ( pa > 120 )
Set warpY to ( warpY - 50 )
Endif
If ( pa > 30 )
If ( pa < 150 )
Set warpX to ( warpX + 50 )
Endif
Elseif ( pa < -30 )
If ( pa > -150 )
Set warpX to ( warpX - 50 )
EndIf
EndIf

I still don't understand exactly how it works, but it gets it into the position I want. Now I just have to iron out all the other bugs around it, like for example it completely ignoring my attempts to set it AIWander 0 0 0 0 either by script or console, instead returning -1 while it walks away from where I wanted it to stay after calling it. This happens every single time it warps now, whereas it didn't with the script I first posted. It seems to happen every time I disable/enable it. It gets stubbornly stuck walking, even though its default package it set to a wander distance of 0.

User avatar
Fiori Pra
 
Posts: 3446
Joined: Thu Mar 15, 2007 12:30 pm

Post » Thu Jul 07, 2016 9:32 am

Yeah, the conversion of angle was wrong. This should do the trick



;first setpos directly to player
set playerX to player->GetPos x
set playerY to player->GetPos y

;get player angle
set playerAngle to player->GetAngle z

;transform it from -180,180 interval to 0,360
if (playerangle < 0)
set playerangle to (360 + playerangle)
endif

;should be rough approximation of cosine using two functions
if(playerAngle < 180)
set x to 1 -(playerAngle/90)
else
set x to ((playerAngle-270)/90)
endif

;should be rough approximation of sine using three functions
if(playerAngle < 90)
set y to (playerAngle/90)
elseif (playerAngle < 270)
set y to 1 -(playerAngle - 90/90)
else
set y to (playerAngle - 270/90) - 1
endif

set newX to playerX + (x * 50)
set newY to playerY + (y * 50)

SetPos x currentX
SetPos y currentY

The script you posted works on similar basis, but uses just couple of predevined values.



Basically, player's angle is between -180 and 180 degrees. If the degree is between -60 and +60(effectively player is looking the direction of y axis), it will move the teleported actor by +50 in y axis. If it is lower than -120 or more than 120 (player is looking against direction of y axis), it will move it by -50 in y axis. Because if your y position is 120 and you are looking in the direction of y axis, daedra's y position will be 170.



Similar logic is applied to x axis.



Unfortunatelly I do not know how to fix your other problem. It is possible that enabling/disabling is doing weird things with follower's AI packages, but I do not have much experience with AI packages.

User avatar
Amy Masters
 
Posts: 3277
Joined: Thu Jun 22, 2006 10:26 am

Post » Thu Jul 07, 2016 2:56 pm

I get it now. Thanks!



Now I have to figure out how to make it work with the MWSE stuff I want to do with it. Ugh... whole new bag of problems.

User avatar
Peetay
 
Posts: 3303
Joined: Sun Jul 22, 2007 10:33 am


Return to III - Morrowind

cron