Full Version : [WIP] MWE Telekinesis
aerelorn >>Other Projects >>[WIP] MWE Telekinesis


<< Prev | Next >>

tonto_101- 03-28-2005
*Current Beta Download Here*

About a month ago I began work on a project to improve telekinesis, making it a more interactive process. This idea, though inspired by many folks, was brought back to my attention by The_Silent_Pyro on the ES forums back in late January. After reconsidering some things, and getting a few new functions from Aerelorn, I began work on the plugin. Here's the general idea:

Telekinesis will require that the player actually invenst time and skill in taking objects. Rather than casting the spell and beginning an activate click-fest, the player will cast the spell, activate and object, and then focus on it until they can lift it through the air and into their inventory. During this time, the actions of the player can be checked by NPCs in the current cell using MWE. If the player's actions seem suspicious enough, that is they're trying to steal an object but they can't quite lift it yet, the player can be reported to the law. Also, while the object flies through the air, NPCs will be "alarmed" to its presence, fixing one of the major stealing exploits in Morrowind.

Thus far I've managed to complete the framework for a control script and a movment script. The control script is pretty much complete, and other than extending the "rules and requirements" of telekinesis, it will stay as it is. The movement script, however, has become something of an albatross.

Since High School and College I've managed to forget a goodly amount of math, especially trigonometry. That's why I'm asking for a bit of help. If someone, be it in psuedoscript or some other form, could give me an idea on how to find the angle (in 3d space) between the object the player is stealing and the player themselves, I would be very appreciative. For this task, Aerelorn has created a function to find the Sine, Cosine, and Tangent of a number. Once that info is acquired, it shouldn't be too hard to implement a script to take care of moving the object towards the player. Here's how I'd like it to work:

1. Upon activation, the object will raise about a foot or two above it's original position at a slight angle. A sine function with a domain of 0 to about Pi/2 (90 degrees) should resemble this growth. Doing so should allow the object to appear to raise above anything that would be blocking its path.

2. When it reaches the apex described in step 1, the object will begin moving towards about the player's eye level. This will vary between races, so a happy medium needs to be found. Each frame, or at intervals of frames afterwards, the object must adjust its movment for any changes in the player's position.

Okay. That's a load of info, but hopefully someone will be able to lend a hand. Here's the work I've managed to do on the scripts thus far, for reference purposes. These are by no means my final scripts, nor have they been -*test*-('")ed yet (the movement concept is -*test*-('")ed though).

CODE
Begin MWE_TelekinesisMain

;Globals for this mod will have the prefix TELE_
;G Float DestructorCode;gamehour - used in terminating outstanding scripts
;G Short TimeComplete;allows the object to fly towards the player

Short TelekinesisActive;this tells if the spell is active
Short TelekinesisUsed;this tells if the spell is in use
Short TypeValidator;used to validate if the object can be picked up
Short TypeApproved;object type can be picked up
Short PreventNewActivation;prevents you from using telekinesis on multiple items
Short ObjectFlying  ;Flag for MWE_TelekinesisObject
Float ActivateDistance;the distance at which an object is activated
Float TimeNeeded  ;minimum 1 - time needed to obtain the object
Float TimePassed  ;amount of time passed when using telekinesis

if ( TELE_DestructorCode != -1 )

Set TELE_DestructorCode to -1

endif

if ( TELE_PreventNewActivation == 1 )

return

endif

if ( player -> GetEffect, 59 );If the player is using telekinesis

Set TelekinesisActive to 1

Set MWE_Vars02 to 0
Set MWE_Vars01 to 50000
ToggleLoadFade;Target Function - Append Infix

if ( OnActivate == 1 )

 Set TELE_DestructorCode to GameHour

 Set MWE_Vars02 to 0
 Set MWE_Vars01 to 50000
 ToggleLoadFade;Target Function - Append Infix

 Set ActivateDistance to ( GetDistance, "Player" )

 Set TypeValidator to MWE_Vars03

 if ( ActivateDistance <= 192 );If the object is in pick up range

  Set TimeNeeded to 1
  Set TimePassed to 0
  Set TelekinesisUsed to 0

  Set MWE_Vars02 to 0
  Set MWE_Vars01 to 50000
  ToggleLoadFade;Target Function - Append Infix

  Activate  ;this is essentially 'ObjectID -> Activate'

 else;if outside pickup range

  if ( TypeValidator == 4 );LIGH

   Set TypeApproved to 0;LIGH entries cannot all be carried

  elseif ( TypeValidator == 8 );INGR

   Set TypeApproved to 1

  elseif ( TypeValidator == 9 );WEAP

   Set TypeApproved to 1

  elseif ( TypeValidator == 10 );ARMO

   Set TypeApproved to 1

  elseif ( TypeValidator == 11 );AMMO

   Set TypeApproved to 1

  elseif ( TypeValidator == 12 );CLOT

   Set TypeApproved to 1

  elseif ( TypeValidator == 13 );APPA

   Set TypeApproved to 1

  elseif ( TypeValidator == 14 );BOOK

   Set TypeApproved to 1

  elseif ( TypeValidator == 15 );LOCK

   Set TypeApproved to 1

  elseif ( TypeValidator == 16 );PROB

   Set TypeApproved to 1

  elseif ( TypeValidator == 17 );REPA

   Set TypeApproved to 1

  elseif ( TypeValidator == 18 );MISC

   Set TypeApproved to 1

  elseif ( TypeValidator == 19 );ALCH

   Set TypeApproved to 1

  else

   Set TypeApproved to 0

  endif

  if ( TypeApproved == 1 )

 ;if there are any sorts of base time cost calculations, they
 ;should be done in the line below

   Set TimeNeeded to ( 1 + GetSecondsPassed )
   Set TimePassed to 0
   Set TelekinesisUsed to 1

   Set MWE_Vars02 to 0
   Set MWE_Vars01 to 50000
   ToggleLoadFade;Target Function - Append Infix

   Startscript "MWE_TelekinesisObject";start the object's script

  else

   Set TimeNeeded to 0
   Set TimePassed to 0
   Set TelekinesisUsed to 0

  endif

 endif

endif

else

Set TelekinesisActive to 0

endif

if ( TelekinesisUsed == 1 )

;Modifiers to time costs should be added below

Set TimeNeeded to ( TimeNeeded - GetSecondsPassed )

if ( TimeNeeded <= 0 )

 Set TelekinesisUsed to 0
 Set TELE_PreventNewActivation to 1
 Set TELE_ObjectFlying to 1

endif  

endif

if ( TelekinesisActive != 1 )

Set TELE_DestructorCode to GameHour

endif

End


CODE
Begin MWE_TelekinesisObject

;Globals for this mod will have the prefix TELE_
;G Float DestructorCode;gamehour - used in terminating outstanding scripts
;G Short TimeComplete;allows the object to fly towards the player

Float DestructorCode
Short DestructorCodeInit

Float ObjX
Float ObjY
Float ObjZ
Float DistToPlayer
Float ZeroPoint
Float Sin
Float Cos
Float Tan
Float PlayerRelativeX
Float PlayerRelativeY
Float PlayerRelativeZ

Short ObjectPhase;phase 1 for lift, phase 2 for flying
Float FlightTime
Float FlightSpeed

if ( DestructorCodeInit == 0 );Initializes the destructor code and base data

Set DestructorCodeInit to 1
Set DestructorCode to TELE_DestructorCode
Set ObjX to GetPos x
Set ObjY to GetPos y
Set ObjZ to GetPos z
Set ZeroPoint to ObjZ

Set ObjectPhase to 1
Set FlightTime to 0
Set FlightSpeed to 500

endif

if ( TELE_DestructorCode != -1 );if the destructor isn't the default

if ( DestructorCode != TELE_DestructorCode )

 stopscript MWE_TelekinesisObject;destory the object's script
 return

endif

endif

;OBJECT MOVEMENT SCRIPTING BELOW *INCOMPLETE*

if ( ObjectPhase == 1 )

Set FlightTime to ( FlightTime + GetSecondsPassed )

Set ObjX to GetPos x
Set ObjY to GetPos y
Set ObjZ to GetPos z
Set PlayerRelativeX to ( Player -> GetPos, X )
Set PlayerRelativeY to ( Player -> GetPos, Y )
Set PlayerRelativeZ to ( Player -> GetPos, Z )

if ( ObjX > PlayerRelativeX )

 Set PlayerRelativeX to 1

else

 Set PlayerRelativeX to -1

endif

if ( ObjY > PlayerRelativeY )

 Set PlayerRelativeY to 1

else

 Set PlayerRelativeY to -1

endif

if ( ObjZ > PlayerRelativeZ )

 Set PlayerRelativeZ to 1

else

 Set PlayerRelativeZ to -1

endif

endif

End


halo112358- 03-30-2005
In game where exactly on the player model are the x,y,z coordinates from the getx, gety & getz functions? are they at the center of the player's bounding box? that's going to cause the functions to be a bit weird, things will fly to the center of the player rather than eye level. I guess this is more realistic as well, you don't want your new treasure flying at your eyes. Guiding your loot to your hands sounds better.

I'm not sure how object->getdistance, player works, does it return the true point to point distance in 3-space between the player and an object? Or does it return a straight-line distance from the xy coordinates of each object (ie: straight-line distance between two points on a map)? The trig calculations are different depending on how that function works.

I'll try to check this out in the morning...

tonto_101- 03-30-2005
QUOTE
We're lucky, the player is always facing the object enough to have it targetted - so we can think of the trig. in 2d terms smile.gif

We can, yes - I'd like to do a small lift in the initial stage though, preferably towards the player. That'll give it the illusion of passing over any objects in between, as I can't actually check for collisions between most objets. You might consider that the player may be stealing an object from far above or below it. I'll settle for anything that works though.

QUOTE
note: in game where exactly on the player model are the x,y,z coordinates from the getx, gety & getz functions? are they at the center of the player's bounding box? that's going to cause the functions to be a bit weird, things will fly to the center of the player rather than eye level. I guess this is more realistic as well, you don't want your new treasure flying at your eyes. Guiding your loot to your hands sounds better.

I believe the coordinates are for the center of the bounding box, yes. If they aren't it's at least very close by. They tend to oscilate from frame to frame as the player is animated, so I can't entirely tell. Flying towards the eyes isn't all that important, so using the player's position alone should be fine.

QUOTE
I'm not sure how object->getdistance, player works, does it return the true point to point distance in 3-space between the player and an object? Or does it return a straight-line distance from the xy coordinates of each object (ie: straight-line distance between two points on a map)? The trig calculations are different depending on how that function works.

It does distance in 3D space. I've heard that Morrowind does the GetDistance function slowly, so I'd like to try doing this calculation "manually" to see if I can get any extra speed out of it. I'm probably going to need to be checking the distance A LOT, so this one is important. Actually, this was the main reason I asked for some help - I have no idea (any longer) how to get line distance between two points in 3D space.

Anyway, thanks for the interest. Hopefully you or someone else can come up with a solution soon. Once I can move the object I'll probably put out a beta.

Cid88- 03-30-2005
If you want, you could add collision detection by putting an invis creature or npc around the object. This is how fishing mods have collision detection.

tonto_101- 03-30-2005
QUOTE (Cid88 @ Mar 30 2005, 04:26 PM)
If you want, you could add collision detection by putting an invis creature or npc around the object. This is how fishing mods have collision detection.

Nah, I don't think it'd be a good idea - not in this case. That would mean dropping an invisible creature on every meaningful object in a cell. That, and I would need to change the object into an activator...so on, so on. Sounds slow, hard-to-do, and I doubt that I'd have the scripting ability to make such a feature 'improve' the look and feel of the mod.

halo112358- 03-30-2005
You might want to ask aerelorn for an efficient getDistance function in MWE. Otherwise you're making a slew of calculations every frame and I'm not sure how efficient MW is at math.

A getdistance rewrite would be useful for stock game scripts as well, you could probably save a few frames in balmora by replacing the stock game calls on all the sign scripts, etc.

Now if I had to guess, I'd say the sqrt function they use probably sucks and that's what's making getDistance so slow - the rest of this is just multiplication (which should be fast). So maybe an efficient sqrt call is another prime request.

otherwise ....
CODE

;;pseudocode follows:

set xdiff to object->getxpos - player->getxpos
set ydiff to object->getypos - player->getypos
set zdiff to object->getzpos - player->getzpos

;; topographical distance to player
set dist2D to sqrt ( ( xdiff * xdiff ) + ( ydiff * ydiff ) )

;; distance in 3-space
set dist3D to sqrt ( ( zdiff * zdiff ) + ( dist2D * dist2D ) )


Cid88- 03-30-2005
QUOTE (tonto_101 @ Mar 30 2005, 05:04 PM)
Nah, I don't think it'd be a good idea - not in this case. That would mean dropping an invisible creature on every meaningful object in a cell. That, and I would need to change the object into an activator...so on, so on. Sounds slow, hard-to-do, and I doubt that I'd have the scripting ability to make such a feature 'improve' the look and feel of the mod.

Good point. Just was throwing it out there in case you really felt the need for such a thing.

tonto_101- 03-30-2005
QUOTE (halo112358 @ Mar 30 2005, 05:31 PM)
You might want to ask aerelorn for an efficient getDistance function in MWE. Otherwise you're making a slew of calculations every frame and I'm not sure how efficient MW is at math.

A getdistance rewrite would be useful for stock game scripts as well, you could probably save a few frames in balmora by replacing the stock game calls on all the sign scripts, etc.

Now if I had to guess, I'd say the sqrt function they use probably sucks and that's what's making getDistance so slow - the rest of this is just multiplication (which should be fast). So maybe an efficient sqrt call is another prime request.

Let's hope it's not the sqrt function. I was actually thinking it was the whole involved process of finding the references that made GetDistance so slow. Since MWE skips most of that mess I'm hoping it will pass off the savings to the game. I'll try the stock sqrt function to begin with. If it turns out to be *really* slow, then I'll bug Aerelorn for a function like that.

Oh, and thanks for the psuedoscript. That gives me the distance at least. Do you know a way to get the angles between the objects, or would you need Arcsin, Arccos, and Arctan for that?

QUOTE (Cid88 @ Mar 30 2005, 08:11 PM)
Good point. Just was throwing it out there in case you really felt the need for such a thing.

Actually, I do feel like it's *needed* if you want that truly immersive feel. I just don't see any great ways to do it. We'll just have to wait until Oblivion comes out I guess.

halo112358- 04-01-2005
...

tonto_101- 04-01-2005
Well, I think I'm just gonna ask Aerelorn to write some Arcsin, Arccos, and Arctan functions. That should solve the problem nicely. I can use the distance function you gave me, and the rest I'll work out as it comes.

halo112358- 04-02-2005
Sounds good, you'll need those if you want to find angles between known distances.

edit:

I'm not having any luck on the scanner front so I'll give you a little rundown on how you could model the initial motion of the object-

if you connect the player's location & the objects initial location by a line you can use this to define a plane in 3-space containing the new line & a line parallel to the z-axis which passes through the player's location.

It's easiest to model the initial curve motion if we setup new axes in our constructed plane - set one axis along the connecting line between the player & object, and another axis perpendicular to it.

Think of a the two locations (player & target object) as points in 3-space, but with a really big sheet of paper that passes through both of them. The paperis oriented so that it extends straight up & down as well as passing through the two points.

Now we set some coordinates on our plane to make the motion simple. If you call the line connecting the player & the object the n-axis, with the object at the origin & the player along the n-axis at some distance in the positive direction, you can model the sine-curve movement in this plane as a function of n (aka the distance the object has moved directly towards the player from it's original position).

A simple function would look like this:

f(n) = h * sin( (pi*n)/(2*m) )

where h is the height of the object in the constructed plane & m is the total distance you'd like the object to move towards the player as it completes it's initial arc.

As the object traces out a sine function in the new plane it will have components of it's motion in the direction of the player (along the new n-axis) and components of it's motion along the second un-named axis above n. Now it's just a matter of translating this motion in our new plane back to motion in XYZ coordinates.

I wish I had software that I could use to draw examples, but I don't know of any good/simple/free 3-space math illustration software. I hope this is reasonably useful.

tonto_101- 04-22-2005
Demo. Aerelorn wrote up a few simple inverse trig functions, so I've gone ahead and done some more work. A demo of object movement can be downloaded here or on my site (try the link in my signature). This demo (badly) moves the object towards the player when you activate it while using telekinesis. Any attempt to break it would be appreciated, but make sure you remember your sequence of events if you give me a report. Check out the readme for further details.

*Edit* The file is dirty, use a GMST cleaner on it. I'm feeling lazy right now, but any new demos/releases should be clean.

*Edit #2* Actually, don't worry about any bug reports. I've found a few, so I'm going to fundamentally rework my scripts.

tonto_101- 04-24-2005
I believe I'm up to a beta version with the mod. Need some feedback - has anybody tried this thing?

Download here.

halo112358- 04-25-2005
A couple of things to report:

the object script doesn't terminate when telekinesis terminates - perhaps terminate the effect and call ->fall on the object when the spell wears off? I had a bit of fun triggering objects and trying to outrun them.

I noticed strange behavior if I activated a corpse while telekinesis was in effect. Once the spell had worn off I could no longer activate that corpse until I cast telekinesis again. As soon as the new spell took effect the corpse's inventory window would appear. I didn't have multiple corpses around when I noticed this, I'll investigate this a bit more later wink.gif

Slick so far, this looks like it has the potential to be a lot of fun. If you don't get to it first maybe I'll write a little patch to give objects the curved initial motion you originally asked for.

tonto_101- 04-25-2005
QUOTE
the object script doesn't terminate when telekinesis terminates - perhaps terminate the effect and call ->fall on the object when the spell wears off? I had a bit of fun triggering objects and trying to outrun them.

That's done on purpose. I have to make sure that the player actually gets the object - if they don't then you stand the risk of having an item floating in the cell if you ever leave and come back. I'll probably do a couple of things to prevent this - like disable the player's ability to teleport and use doors until the object lands in their inventory. Seems a bit drastic - but there's only one object script in the mod - I have to make sure it finishes the job before it starts a new one.

QUOTE
I noticed strange behavior if I activated a corpse while telekinesis was in effect. Once the spell had worn off I could no longer activate that corpse until I cast telekinesis again. As soon as the new spell took effect the corpse's inventory window would appear. I didn't have multiple corpses around when I noticed this, I'll investigate this a bit more later

Were you using the first version I posted, or the one further down in the thread? The mod should completely ignore NPCs and containers - whichever a corpse is. I'll have a look-see. I probably disabled the player's ability to activate ALL objects at some point.

QUOTE
Slick so far, this looks like it has the potential to be a lot of fun. If you don't get to it first maybe I'll write a little patch to give objects the curved initial motion you originally asked for.

I will be doing it myself, but I'd love to see what others can do with it. The framework for something like that is already in place - just set change the script to have two ObjectPhases (the original being the second) and use FlightTime to vary its trajectory for the first second of flight. I only wish Rotate would take variables - then I could make it a bit more dramatic.

Free Forum Hosting by Forumer.comTM!