Simulating new functions

Revision as of 11:31, 7 May 2006 by imported>JustTim (Moved the discussion to the "Discussion" page and put the actual solution into the "article".)

Writing complex scripts for oblivion can be very painful and frustrating. Not being able to write reusable functions that execute directly on call instead of the next frame is one of the major problems (at least for me).

But Kkuhlmann from Bethesda had a great idea to make a workaround: Quest Stages! When you call setStage in a script, the related stage result script will be executed immediately BEFORE the current script continues! This is therefore a great way to write immediately executing function calls!

I've spent MANY hours to advance this idea to a fully reusable function framework that can easily be used in your scripts. Here is what i came up with:


Create a Quest just called "f" (for "function"), ACTIVATE "ALLOW REPEATED STAGES" and attach the following quest script:

ScriptName FunctionQuestScript

; Internals
short doOnce

; Constants
float rad
float pi

; Function In- and Output
float fin1
float fin2
float fin3
float fout
float fout2

ref rin1
ref rin2
ref rin3
ref rout
ref rout2

;S5 FUNCTION sqrt
float sqr

;S10 FUNCTION Hypotenuse
;float sqr
float n

;S20 FUNCTION getAngle
;float sqr
float x
float y
float sin
float cos
float ang

; Set constants first
Begin Gamemode
  if doOnce == 0
    set rad to 57.2957792
    set pi to 3.1415927
    set doOnce to 1
  endif
End

This is a generalized function framework that can be re-used for as many functions as you like.

And now make stage 5 for this quest with the following code:

;FUNCTION float sqrt(float input)
if (f.fin1 <= 0)
  set f.fout to 0
else
  set f.sqr to f.fin1/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.sqr to (f.sqr+(f.fin1/f.sqr))/2
  set f.fout to f.sqr
endif

And stage 10:

;FUNCTION float Hypotenuse(float CathetusA, float CathetusB)
set f.n to ((f.fin1 * f.fin1) + (f.fin2 * f.fin2))
;CALL float sqrt(float input)
set f.fin1 to f.n
setStage f 5
;fout is already the result

And stage 20:

;FUNCTION float getAngle(float x, float y)
set f.x to f.fin1
set f.y to f.fin2
;CALL Hypotenuse using same fin
setStage f 10
set f.sqr to f.fout

set f.sin to (f.x/f.sqr)
set f.cos to (f.y/f.sqr)

if f.cos >= 0 && f.sin < 0 ;Q4
  set f.ang to (f.cos*f.rad)
  set f.ang to f.ang + 270
elseif f.cos < 0 && f.sin < 0; Q3
  set f.ang to ((0-f.sin)*f.rad)
  set f.ang to f.ang + 180
elseif f.cos < 0 && f.sin >= 0 ;Q2
  set f.ang to ((0-f.cos)*f.rad)
  set f.ang to f.ang + 90
else ;if f.cos >= 0 && f.sin >= 0 ;Q1
  set f.ang to (f.sin*f.rad)
endif
set f.fout to f.ang

Okay, the function setup is complete. Now you've already got 3 functions: One to calculate the squareroot, one to calculate the Hypotenuse in a triangle and one to calculate the angle of a vector. To get the angle between two objects in a script just type:

;CALL float getAngle(float x, float y)
set f.fin1 to ( Object2.getPos x - Object1.getPos x )
set f.fin2 to ( Object2.getPos y - Object1.getPos y )
setStage f 20
set ResultingAngleZ to f.fout

Et voilà, here is your Z-Angle between Object 1 and 2! You can reuse the function wherever you like, how often you wish to and you can even call functions from inside another stage of "f" (aka. another function)!

I is also very easy to share the functions with others since you only need to share the code of the stage result script for your function.

The only downsides are that you have to define all vars in the quest script (it compiles when you define them in the stage result script, but they'll always be 0) and the limited maximum length of each quest result script (which isn't a big problem since you are able to split a single function into multiple stages).

Many thanks go tu Kkuhlmann for his great idea and to Galerion for the Square Root function! The above function isn't very accurate, which may be due to my bad mathematics skills. If anyone has an idea on how to improve these scripts for accuracy and performance then please contribute!