1. Are you planning to upload your awesome map to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The reforging of the races is complete. Come see the 14th Techtree Contest Results.
    Dismiss Notice
  5. It's time to choose your horse in the race - the 32nd Modeling Contest Poll is up!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

FlightSimConcept.w3x
Variables
Libraries
Maths
Debug
Quaternions
Matrices
Vectors
Rotations
Handle Recyclers
Lists
Arrow Keys
Camera Map
Water 3d
Physics
Terrain
Clouds
Camera
Classes
Particle Classes
Objects
Particles
Collision Shapes
Collision Detection
Collision Response
Main
Properties
Controllers
Controllers
ESC
Initialisierung
Nahkampf-Initialisierung
Kartenspezifischen eigenen Skript-Code unten eingeben. Dieser Text wird in das Karten-Skript nach der Deklaration der Variablen und vor jeglichem Auslöser-Code eingefügt.

		
Name Type Is Array Initial Value
//TESH.scrollpos=0
//TESH.alwaysfold=0
library maths
    function Round takes real r returns integer
        if r>0. then
            return R2I(r+.5)
        endif
        return R2I(r-.499999999)
    endfunction
endlibrary
//TESH.scrollpos=69
//TESH.alwaysfold=0
library debugHelper
    function FreezeWarcraft takes nothing returns nothing
        loop
            call TriggerSyncReady()
            call ExecuteFunc("FreezeWarcraft")
        endloop
    endfunction

    function ExitWarcraft takes nothing returns nothing
        loop
            call ExecuteFunc("ExitWarcraft")
        endloop
    endfunction

    function BreakThread takes nothing returns nothing
        local integer i = 1/0
    endfunction

    function CriticalError takes nothing returns nothing
        call ExecuteFunc("0")
    endfunction

    function GotoMenu takes nothing returns nothing
        call EndGame(false)
    endfunction

    globals
        constant string clBlue = "0000ff"
        constant string clRed = "ff0000"
        constant string clYellow = "ffff00"
        integer debugN = 0
    endglobals
   
    function DebugDot takes real x, real y, real z, string color returns nothing
        local texttag tt = CreateTextTag()
        call SetTextTagColor(tt,255,255,255,0)
        call SetTextTagFadepoint(tt,01.2)
        call SetTextTagLifespan(tt,01.2)
        call SetTextTagPos(tt,x,y,z)
        call SetTextTagVisibility(tt,true)
        call SetTextTagText(tt,"|cff"+color+".|r",0.023)
        call SetTextTagPermanent(tt,false)
        call SetTextTagColor(tt,255,255,255,255)
    endfunction
   
    function DebugMsg takes string s returns nothing
        set debugN = debugN+1
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"[|cffff0000Debug #"+I2S(debugN)+"|r] "+s)
    endfunction

    // Thread Break Detector
   
    globals
        string array debug_threads_s
        integer debug_threads_max = 0
    endglobals
   
    function RegisterThread takes string ThreadName returns nothing
        local integer ThreadId = 1
        loop
            exitwhen ThreadId>debug_threads_max
            if debug_threads_s[ThreadId]==ThreadName then
                call DebugMsg("Attempting to register an existent Thread (\""+ThreadName+"\").")
                return
            endif
            set ThreadId = ThreadId + 1
        endloop
        set debug_threads_s[ThreadId] = ThreadName
        set debug_threads_max = ThreadId
    endfunction

    function UnregisterThread takes string ThreadName returns nothing
        local integer ThreadId = 1
        loop
            if ThreadId>debug_threads_max then
                call DebugMsg("Attempting to unregister a non-existent Thread (\""+ThreadName+"\").")
                return
            endif
            exitwhen debug_threads_s[ThreadId]==ThreadName
            set ThreadId = ThreadId + 1
        endloop
        set debug_threads_s[ThreadId] = debug_threads_s[debug_threads_max]
        set debug_threads_max = debug_threads_max-1
    endfunction
   
    // Infinite Loop Detector
   
    globals
        string array debug_loops_s
        integer array debug_loops_execs
        integer debug_loops_max = 0
    endglobals
   
    function RegisterLoopStart takes string LoopName returns nothing
        local integer LoopId = 1
        loop
            exitwhen LoopId>debug_loops_max
            if debug_loops_s[LoopId]==LoopName then
                set debug_loops_execs[LoopId] = 0
                return
            endif
            set LoopId = LoopId + 1
        endloop
        set debug_loops_s[LoopId] = LoopName
        set debug_loops_execs[LoopId] = 0
        set debug_loops_max = LoopId
    endfunction
   
    function WatchLoop takes string LoopName, integer Limit returns nothing
        local integer LoopId = 1
        loop
            if LoopId>debug_loops_max then
                call DebugMsg("Attempting to watch an unregistered Loop (\""+LoopName+"\").")
                return
            endif
            exitwhen debug_loops_s[LoopId]==LoopName
            set LoopId = LoopId + 1
        endloop
        set debug_loops_execs[LoopId] = debug_loops_execs[LoopId]+1
        if debug_loops_execs[LoopId]>=Limit then
            call DebugMsg("Reached loop execution #"+I2S(Limit)+" and broke thread (\""+LoopName+"\").")
            call BreakThread()
        endif
    endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Quaternions
//*
//***************************************************************************

library quaternions

    function Q2S takes quaternion q returns string
        return "( "+R2S(q.x)+" | "+R2S(q.y)+" | "+R2S(q.z)+" | "+R2S(q.w)+" )"
    endfunction
   
    struct quaternion
       
        // q = ( x*i + y*j + z*k + w )
       
        real x
        real y
        real z
        real w
        method conjugate takes nothing returns nothing
            set .x = -.x
            set .y = -.y
            set .z = -.z
        endmethod
       
        method scale takes real scalar returns nothing
            set .x = .x*scalar
            set .y = .y*scalar
            set .z = .z*scalar
            set .w = .w*scalar
        endmethod
       
        method normalise takes nothing returns nothing
            local real l = SquareRoot(.x*.x+.y*.y+.z*.z+.w*.w)
            if l!=0. then
                set .x = .x/l
                set .y = .y/l
                set .z = .z/l
                set .w = .w/l
            endif
        endmethod
       
        method setQuat takes quaternion q2 returns nothing
            set .x = q2.x
            set .y = q2.y
            set .z = q2.z
            set .w = q2.w
        endmethod
       
        method add takes quaternion q2 returns nothing
            set .x = .x + q2.x
            set .y = .y + q2.y
            set .z = .z + q2.z
            set .w = .w + q2.w
        endmethod
       
        method mult takes quaternion q2 returns nothing
            local real x =  .x * q2.w + .y * q2.z - .z * q2.y + .w * q2.x
            local real y = -.x * q2.z + .y * q2.w + .z * q2.x + .w * q2.y
            local real z =  .x * q2.y - .y * q2.x + .z * q2.w + .w * q2.z
            local real w = -.x * q2.x - .y * q2.y - .z * q2.z + .w * q2.w
            set .x = x
            set .y = y
            set .z = z
            set .w = w
        endmethod
       
        method setXYZW takes real x, real y, real z, real w returns nothing
            set .x = x
            set .y = y
            set .z = z
            set .w = w
        endmethod
       
        method copy takes nothing returns quaternion
            local quaternion q = quaternion.allocate()
            set q.x = .x
            set q.y = .y
            set q.z = .z
            set q.w = .w
            return q
        endmethod
       
        static method createEuler takes real bank, real attitude, real heading returns quaternion
            local real Sin0 = Sin(bank*.5)
            local real Cos0 = Cos(bank*.5)
            local real Sin1 = Sin(attitude*.5)
            local real Cos1 = Cos(attitude*.5)
            local real Sin2 = Sin(heading*.5)
            local real Cos2 = Cos(heading*.5)
            return quaternion.create(Sin0 * Cos1 * Cos2 - Cos0 * Sin1 * Sin2,Cos0 * Sin1 * Cos2 + Sin0 * Cos1 * Sin2,Cos0 * Cos1 * Sin2 - Sin0 * Sin1 * Cos2,Cos0 * Cos1 * Cos2 + Sin0 * Sin1 * Sin2)
        endmethod
       
        static method createMult takes quaternion q1, quaternion q2 returns quaternion
            local quaternion q = quaternion.allocate()
            set q.x =  q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x
            set q.y = -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y
            set q.z =  q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z
            set q.w = -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w
            return q
        endmethod
       
        static method create takes real x, real y, real z, real w returns quaternion
            local quaternion q = quaternion.allocate()
            set q.x = x
            set q.y = y
            set q.z = z
            set q.w = w
            return q
        endmethod
       
        static method createAxisAngle takes vector v returns quaternion
            local real a = v.length()
            return quaternion.create(v.x*Sin(a/2),v.y*Sin(a/2),v.z*Sin(a/2),Cos(a/2))
            //q = cos(a/2) + i ( x * sin(a/2)) + j (y * sin(a/2)) + k ( z * sin(a/2))
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=132
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Matrices
//*
//***************************************************************************

library matrices

    function M2S takes matrix m returns string
        return "( "+R2S(m.r00)+" | "+R2S(m.r01)+" | "+R2S(m.r02)+" )\n( "+R2S(m.r10)+" | "+R2S(m.r11)+" | "+R2S(m.r12)+" )\n( "+R2S(m.r20)+" | "+R2S(m.r21)+" | "+R2S(m.r22)+" )"
    endfunction

    struct matrix
       
        //     / r00  r01  r02 \
        // M = | r10  r11  r12 |
        //     \ r20  r21  r22 /
       
        real r00
        real r01
        real r02
        real r10
        real r11
        real r12
        real r20
        real r21
        real r22
       
        static method create takes real r00, real r01, real r02, real r10, real r11, real r12, real r20, real r21, real r22 returns matrix
            local matrix m2 = matrix.allocate()
            set m2.r00 = r00
            set m2.r01 = r01
            set m2.r02 = r02
            set m2.r10 = r10
            set m2.r11 = r11
            set m2.r12 = r12
            set m2.r20 = r20
            set m2.r21 = r21
            set m2.r22 = r22
            return m2
        endmethod
       
        method copy takes nothing returns matrix
            local matrix m2 = matrix.allocate()
            set m2.r00 = .r00
            set m2.r01 = .r01
            set m2.r02 = .r02
            set m2.r10 = .r10
            set m2.r11 = .r11
            set m2.r12 = .r12
            set m2.r20 = .r20
            set m2.r21 = .r21
            set m2.r22 = .r22
            return m2
        endmethod
       
        method determinant takes nothing returns real
            return .r00*.r11*.r22 + .r01*.r12*.r20 + .r02*.r10*.r21 - .r00*.r12*.r21 - .r01*.r10*.r22 - .r02*.r11*.r20
            return .r00*.r11*.r22 + .r01*.r12*.r20 + .r02*.r20*.r21 - .r00*.r12*.r21 - .r01*.r10*.r22 - .r02*.r11*.r20
        endmethod
       
        method invert takes nothing returns nothing
            local real invdet = 1./.determinant()
            local real r00 = (.r11*.r22 - .r12*.r21)*invdet
            local real r01 = (.r02*.r21 - .r01*.r22)*invdet
            local real r02 = (.r01*.r12 - .r02*.r11)*invdet
            local real r10 = (.r12*.r20 - .r10*.r22)*invdet
            local real r11 = (.r00*.r22 - .r02*.r20)*invdet
            local real r12 = (.r02*.r10 - .r00*.r12)*invdet
            local real r20 = (.r10*.r21 - .r11*.r20)*invdet
            local real r21 = (.r01*.r20 - .r00*.r21)*invdet
            local real r22 = (.r00*.r11 - .r01*.r10)*invdet
            set .r00 = r00
            set .r01 = r01
            set .r02 = r02
            set .r10 = r10
            set .r11 = r11
            set .r12 = r12
            set .r20 = r20
            set .r21 = r21
            set .r22 = r22
        endmethod
       
        method copyInverted takes nothing returns matrix
            local matrix m2 = matrix.allocate()
            local real invdet = 1./.determinant()
            set m2.r00 = (.r11*.r22 - .r12*.r21)*invdet
            set m2.r01 = (.r02*.r21 - .r01*.r22)*invdet
            set m2.r02 = (.r01*.r12 - .r02*.r11)*invdet
            set m2.r10 = (.r12*.r20 - .r10*.r22)*invdet
            set m2.r11 = (.r00*.r22 - .r02*.r20)*invdet
            set m2.r12 = (.r02*.r10 - .r00*.r12)*invdet
            set m2.r20 = (.r10*.r21 - .r11*.r20)*invdet
            set m2.r21 = (.r01*.r20 - .r00*.r21)*invdet
            set m2.r22 = (.r00*.r11 - .r01*.r10)*invdet
            return m2
        endmethod
       
        method mult takes matrix m2 returns nothing
            local real r00 = .r00*m2.r00 + .r01*m2.r10 + .r02*m2.r20
            local real r01 = .r00*m2.r01 + .r01*m2.r11 + .r02*m2.r21
            local real r02 = .r00*m2.r02 + .r01*m2.r12 + .r02*m2.r22
            local real r10 = .r10*m2.r00 + .r11*m2.r10 + .r12*m2.r20
            local real r11 = .r10*m2.r01 + .r11*m2.r11 + .r12*m2.r21
            local real r12 = .r10*m2.r02 + .r11*m2.r12 + .r12*m2.r22
            local real r20 = .r20*m2.r00 + .r21*m2.r10 + .r22*m2.r20
            local real r21 = .r20*m2.r01 + .r21*m2.r11 + .r22*m2.r21
            local real r22 = .r20*m2.r02 + .r21*m2.r12 + .r22*m2.r22
            set .r00 = r00
            set .r01 = r01
            set .r02 = r02
            set .r10 = r10
            set .r11 = r11
            set .r12 = r12
            set .r20 = r20
            set .r21 = r21
            set .r22 = r22
        endmethod
       
        method multSwapOrder takes matrix m2 returns nothing
            local real r00 = m2.r00*.r00 + m2.r01*.r10 + m2.r02*.r20
            local real r01 = m2.r00*.r01 + m2.r01*.r11 + m2.r02*.r21
            local real r02 = m2.r00*.r02 + m2.r01*.r12 + m2.r02*.r22
            local real r10 = m2.r10*.r00 + m2.r11*.r10 + m2.r12*.r20
            local real r11 = m2.r10*.r01 + m2.r11*.r11 + m2.r12*.r21
            local real r12 = m2.r10*.r02 + m2.r11*.r12 + m2.r12*.r22
            local real r20 = m2.r20*.r00 + m2.r21*.r10 + m2.r22*.r20
            local real r21 = m2.r20*.r01 + m2.r21*.r11 + m2.r22*.r21
            local real r22 = m2.r20*.r02 + m2.r21*.r12 + m2.r22*.r22
            set .r00 = r00
            set .r01 = r01
            set .r02 = r02
            set .r10 = r10
            set .r11 = r11
            set .r12 = r12
            set .r20 = r20
            set .r21 = r21
            set .r22 = r22
        endmethod
       
        static method createProduct takes matrix m1, matrix m2 returns matrix
            local matrix m = matrix.allocate()
            set m.r00 = m1.r00*m2.r00 + m1.r01*m2.r10 + m1.r02*m2.r20
            set m.r01 = m1.r00*m2.r01 + m1.r01*m2.r11 + m1.r02*m2.r21
            set m.r02 = m1.r00*m2.r02 + m1.r01*m2.r12 + m1.r02*m2.r22
            set m.r10 = m1.r10*m2.r00 + m1.r11*m2.r10 + m1.r12*m2.r20
            set m.r11 = m1.r10*m2.r01 + m1.r11*m2.r11 + m1.r12*m2.r21
            set m.r12 = m1.r10*m2.r02 + m1.r11*m2.r12 + m1.r12*m2.r22
            set m.r20 = m1.r20*m2.r00 + m1.r21*m2.r10 + m1.r22*m2.r20
            set m.r21 = m1.r20*m2.r01 + m1.r21*m2.r11 + m1.r22*m2.r21
            set m.r22 = m1.r20*m2.r02 + m1.r21*m2.r12 + m1.r22*m2.r22
            return m
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=182
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Vectors
//*
//***************************************************************************

library vectors uses quaternions, matrices
    function scalarProduct takes vector vec1, vector vec2 returns real
        return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z
    endfunction
   
    function distanceBetween takes vector vec1, vector vec2 returns real
        return SquareRoot( Pow(vec1.x - vec2.x,2)+Pow(vec1.y - vec2.y,2)+Pow(vec1.z - vec2.z,2))
    endfunction
   
    function V2S takes vector vec returns string
        return "( "+R2S(vec.x)+" | "+R2S(vec.y)+" | "+R2S(vec.z)+" )"
    endfunction
   
    struct vector
   
        //     / x \
        // v = | y |
        //     \ z /
       
        real x
        real y
        real z
       
        static method create takes real x, real y, real z returns vector
            local vector v = vector.allocate()
            set v.x = x
            set v.y = y
            set v.z = z
            return v
        endmethod
       
        static method createLoc takes location loc returns vector
            local vector v = vector.allocate()
            set v.x = GetLocationX(loc)
            set v.y = GetLocationY(loc)
            set v.z = GetLocationZ(loc)
            return v
        endmethod
       
        static method createProduct takes vector vec1, vector vec2 returns vector
            local vector v = vector.allocate()
            set v.x = vec1.y*vec2.z - vec1.z*vec2.y
            set v.y = vec1.z*vec2.x - vec1.x*vec2.z
            set v.z = vec1.x*vec2.y - vec1.y*vec2.x
            return v
        endmethod
       
        method addProduct takes vector vec1, vector vec2 returns nothing
            set .x = .x + vec1.y*vec2.z - vec1.z*vec2.y
            set .y = .y + vec1.z*vec2.x - vec1.x*vec2.z
            set .z = .z + vec1.x*vec2.y - vec1.y*vec2.x
        endmethod
       
        method copy takes nothing returns vector
            return vector.create(.x,.y,.z)
        endmethod
       
        method copyScaled takes real scalar returns vector
            return vector.create(.x*scalar,.y*scalar,.z*scalar)
        endmethod

        static method createProjected takes vector projected, vector direction returns vector
            local real r = scalarProduct(projected,direction)
            local real l = scalarProduct(direction,direction)
            if l == 0. then
                return vector.create(0,0,1)
            endif
            return direction.copyScaled(r/l)
        endmethod
       
        method setSum takes vector vec1, vector vec2 returns nothing
            set .x = vec1.x + vec2.x
            set .y = vec1.y + vec2.y
            set .z = vec1.z + vec2.z
        endmethod
       
        static method createSum takes vector vec1, vector vec2 returns vector
            local vector v = vector.allocate()
            set v.x = vec1.x + vec2.x
            set v.y = vec1.y + vec2.y
            set v.z = vec1.z + vec2.z
            return v
        endmethod
       
        method setDifference takes vector minuend, vector subtrahend returns nothing
            set .x = minuend.x - subtrahend.x
            set .y = minuend.y - subtrahend.y
            set .z = minuend.z - subtrahend.z
        endmethod
       
        static method createDifference takes vector minuend, vector subtrahend returns vector
            local vector v = vector.allocate()
            set v.x = minuend.x - subtrahend.x
            set v.y = minuend.y - subtrahend.y
            set v.z = minuend.z - subtrahend.z
            return v
        endmethod
       
        static method createProjectedPlane takes vector projected, vector planeNormal returns vector
            local vector v = vector.allocate()
            local vector v1 = vector.createProjected(projected,planeNormal)
            local vector v2 = vector.createDifference(projected,v1)
            call v1.destroy()
            return v2
        endmethod
       
        method length takes nothing returns real
            return SquareRoot(.x*.x+.y*.y+.z*.z)
        endmethod
       
        method angleToVector takes vector vec2 returns real
            local real r = .length() * vec2.length()
            if r==0. then
                return 0.
            endif
            return Acos(scalarProduct(this,vec2)/r)
        endmethod
       
        method lengthVectored takes vector dir returns real
            local real r = dir.length()
            if r==0. then
                return 0.
            endif
            return scalarProduct(this,dir)/r
        endmethod
       
        method add takes vector addit returns nothing
            set .x = .x + addit.x
            set .y = .y + addit.y
            set .z = .z + addit.z
        endmethod
       
        method addScaled takes vector addit, real scale returns nothing
            set .x = .x + addit.x*scale
            set .y = .y + addit.y*scale
            set .z = .z + addit.z*scale
        endmethod
       
        method subtract takes vector subit returns nothing
            set .x = .x - subit.x
            set .y = .y - subit.y
            set .z = .z - subit.z
        endmethod
       
        method subtractScaled takes vector subit, real scale returns nothing
            set .x = .x - subit.x*scale
            set .y = .y - subit.y*scale
            set .z = .z - subit.z*scale
        endmethod
       
        method distTo takes vector tovec returns real
            return SquareRoot( Pow(.x - tovec.x,2)+Pow(.y - tovec.y,2)+Pow(.z - tovec.z,2))
        endmethod
       
        method addXYZ takes real x, real y, real z returns nothing
            set .x = .x + x
            set .y = .y + y
            set .z = .z + z
        endmethod
       
        method subtractXYZ takes real x, real y, real z returns nothing
            set .x = .x - x
            set .y = .y - y
            set .z = .z - z
        endmethod
       
        method scale takes real scalar returns nothing
            set .x = .x * scalar
            set .y = .y * scalar
            set .z = .z * scalar
        endmethod
       
        method setLength takes real tol returns nothing
            local real l = .length()
            if l==0. then
                set .z=tol
            else
                set .x = .x*tol/l
                set .y = .y*tol/l
                set .z = .z*tol/l
            endif
        endmethod
       
        method normalise takes nothing returns nothing
            local real l = .length()
            if l==0. then
                set .z=1.
            else
                set .x = .x/l
                set .y = .y/l
                set .z = .z/l
            endif
        endmethod
       
        method setXYZ takes real x, real y, real z returns nothing
            set .x = x
            set .y = y
            set .z = z
        endmethod
       
        method setLoc takes location loc returns nothing
            set .x = GetLocationX(loc)
            set .y = GetLocationY(loc)
            set .z = GetLocationZ(loc)
        endmethod
       
        method setVec takes vector vec2 returns nothing
            set .x = vec2.x
            set .y = vec2.y
            set .z = vec2.z
        endmethod
       
        method transformByMatrix takes matrix m returns nothing
            local real x = .x*m.r00 + .y*m.r01 + .z*m.r02
            local real y = .x*m.r10 + .y*m.r11 + .z*m.r12
            local real z = .x*m.r20 + .y*m.r21 + .z*m.r22
            set .x = x
            set .y = y
            set .z = z
        endmethod
       
        method setTransformedByMatrix takes vector v, matrix m returns nothing
            local real x = v.x*m.r00 + v.y*m.r01 + v.z*m.r02
            local real y = v.x*m.r10 + v.y*m.r11 + v.z*m.r12
            local real z = v.x*m.r20 + v.y*m.r21 + v.z*m.r22
            set .x = x
            set .y = y
            set .z = z
        endmethod
       
        method addTransformedByMatrix takes vector v, matrix m returns nothing
            local real x = v.x*m.r00 + v.y*m.r01 + v.z*m.r02
            local real y = v.x*m.r10 + v.y*m.r11 + v.z*m.r12
            local real z = v.x*m.r20 + v.y*m.r21 + v.z*m.r22
            set .x = .x+x
            set .y = .y+y
            set .z = .z+z
        endmethod
       
        method copyTransformedByMatrix takes vector v, matrix m returns vector
            return vector.create(v.x*m.r00 + v.y*m.r01 + v.z*m.r02,v.x*m.r10 + v.y*m.r11 + v.z*m.r12,v.x*m.r20 + v.y*m.r21 + v.z*m.r22)
        endmethod
   
        method rotateByQuaternion takes quaternion q returns nothing
            // r = q*v
            local real x =  q.y * .z - q.z * .y + q.w * .x
            local real y = -q.x * .z + q.z * .x + q.w * .y
            local real z =  q.x * .y - q.y * .x + q.w * .z
            local real w = -q.x * .x - q.y * .y - q.z * .z
           
            // v = r*q'
            set .x =  x * q.w - y * q.z + z * q.y - w * q.x
            set .y =  x * q.z + y * q.w - z * q.x - w * q.y
            set .z = -x * q.y + y * q.x + z * q.w - w * q.z
        endmethod
   
        method rotateByQuaternionInv takes quaternion q returns nothing
            // r = q*v
            local real x = -q.y * .z + q.z * .y + q.w * .x
            local real y =  q.x * .z - q.z * .x + q.w * .y
            local real z = -q.x * .y + q.y * .x + q.w * .z
            local real w =  q.x * .x + q.y * .y + q.z * .z
           
            // v = r*q'
            set .x =  x * q.w + y * q.z - z * q.y + w * q.x
            set .y = -x * q.z + y * q.w + z * q.x + w * q.y
            set .z =  x * q.y - y * q.x + z * q.w + w * q.z
        endmethod
   
        method addRotatedByQuaternion takes vector v, quaternion q returns nothing
            // r = q*v
            local real x =  q.y * v.z - q.z * v.y + q.w * v.x
            local real y = -q.x * v.z + q.z * v.x + q.w * v.y
            local real z =  q.x * v.y - q.y * v.x + q.w * v.z
            local real w = -q.x * v.x - q.y * v.y - q.z * v.z
           
            // v = r*q'
            set .x =  .x + x * q.w - y * q.z + z * q.y - w * q.x
            set .y =  .y + x * q.z + y * q.w - z * q.x - w * q.y
            set .z =  .z - x * q.y + y * q.x + z * q.w - w * q.z
        endmethod

        static method copyRotated takes vector rotated, vector axis, real angle returns vector
            local vector locX
            local vector locY
            local vector locZ
            local vector result
            set locZ = vector.createProjected(rotated, axis)
            if locZ == 0. then
                return 0.
            endif
            set locX = vector.createDifference(rotated,locZ)
            set locY = vector.createProduct(axis, locX)
            call locY.setLength( locX.length() )
            call locX.scale( Cos(angle) )
            call locY.scale( Sin(angle) )
            set result = vector.createSum(locX, locY)
            call result.add(locZ)
            call locX.destroy()
            call locY.destroy()
            call locZ.destroy()
            return result
        endmethod
       
        method rotateAroundXAxis takes real angle returns nothing
            local real ty = .y
            set .y = .y*Cos(angle)-.z*Sin(angle)
            set .z = .z*Cos(angle)-ty*Sin(angle)
        endmethod
       
        method rotateAroundYAxis takes real angle returns nothing
            local real tx = .x
            set .x = .x*Cos(angle)-.z*Sin(angle)
            set .z = .z*Cos(angle)-tx*Sin(angle)
        endmethod
       
        method rotateAroundZAxis takes real angle returns nothing
            local real tx = .x
            set .x = .x*Cos(angle)-.y*Sin(angle)
            set .y = .y*Cos(angle)-tx*Sin(angle)
        endmethod
       
        method inCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
            local boolean b = false
            local real l = cylinderHeight.length()
            local vector vec1
            local vector vec2
            local vector vec3
            if l > 0. then
                set vec1=vector.createDifference(this, cylinderOrigin)
                set vec2=vector.createDifference(vec1, cylinderHeight)
                set vec3=vector.createProjectedPlane(vec1, cylinderHeight)
                if scalarProduct(vec1, cylinderHeight)>=0. and scalarProduct(vec2, cylinderHeight)<=0. and scalarProduct(vec3, vec3)<=cylinderRadius*cylinderRadius then
                    set b = true
                endif
                call vec1.destroy()
                call vec2.destroy()
                call vec3.destroy()
            endif
            return b
        endmethod

        method inCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
            local vector vec1
            local vector vec2
            local vector vec3
            local vector vec4
            local boolean b = false
            local real r
            local real l = coneHeight.length()
            if l>0. then
                set vec1=vector.createDifference(this, coneOrigin)
                set vec2=vector.createDifference(vec1, coneHeight)
                set vec3=vector.createProjectedPlane(vec1, coneHeight)
                set vec4=vector.createDifference(vec1, vec3)
                set r = 1.0-( vec3.length()/l )
                if scalarProduct(vec1, coneHeight)>=0. and (vec4.length())<=(coneRadius*r) then
                    set b = true
                endif
                call vec1.destroy()
                call vec2.destroy()
                call vec3.destroy()
                call vec4.destroy()
            endif
            return b
        endmethod

        method inSphere takes vector sphereOrigin, real sphereRadius returns boolean
            return .distTo(sphereOrigin)<=sphereRadius
        endmethod
       
    endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Rotations
//*
//***************************************************************************

library rotations uses quaternions, matrices, vectors
    function RotationQuat2Mat takes quaternion q returns matrix
        return matrix.create(1 - 2*q.y*q.y - 2*q.z*q.z,2*q.x*q.y - 2*q.z*q.w,2*q.x*q.z + 2*q.y*q.w,2*q.x*q.y + 2*q.z*q.w,1 - 2*q.x*q.x - 2*q.z*q.z,2*q.y*q.z - 2*q.x*q.w,2*q.x*q.z - 2*q.y*q.w,2*q.y*q.z + 2*q.x*q.w,1 - 2*q.x*q.x - 2*q.y*q.y)
    endfunction
   
    function RotationMat2Quat takes matrix m returns quaternion
        local quaternion q = quaternion.create(0,0,0,1)
        local real trace = m.r00+m.r11+m.r22 + 1.
        local real s
        if trace > 0. then
            set s = .5 / SquareRoot(trace)
            set q.w = .25 / s
            set q.x = ( m.r21 - m.r12 ) * s
            set q.y = ( m.r02 - m.r20 ) * s
            set q.z = ( m.r10 - m.r01 ) * s
        elseif m.r00 > m.r11 and m.r00 > m.r22 then
            set s = .5 / SquareRoot( 1. + m.r00 - m.r11 - m.r22)
            set q.w = ( m.r21 - m.r12 ) * s
            set q.x = .25 / s
            set q.y = ( m.r01 + m.r10 ) * s
            set q.z = ( m.r02 + m.r20 ) * s
        elseif m.r11 > m.r22 then
            set s = .5 / SquareRoot( 1. + m.r11 - m.r00 - m.r22)
            set q.w = ( m.r02 - m.r20 ) * s
            set q.x = ( m.r01 + m.r10 ) * s
            set q.y = .25 / s
            set q.z = ( m.r12 + m.r21 ) * s
        else
            set s = .5 / SquareRoot( 1. + m.r22 - m.r00 - m.r11 )
            set q.w = ( m.r10 - m.r01 ) * s
            set q.x = ( m.r02 + m.r20 ) * s
            set q.y = ( m.r12 + m.r21 ) * s
            set q.z = .25 / s
        endif
        return q
    endfunction
       
    function RotationAxisAngle2QuatScaled takes vector v, real scale returns quaternion
        local real l = v.length()
        local real sin = Sin(l*scale*.5)
        if l==0. then
            set l=1.
        endif
        return quaternion.create(v.x/l*sin,v.y/l*sin,v.z/l*sin,Cos(l*scale*.5))
    endfunction
   
    function rotateQuaternionByVectorScaled takes quaternion q, vector v, real scale returns nothing
        local quaternion w = RotationAxisAngle2QuatScaled(v,scale)
        call w.mult(q)
        call q.setXYZW(w.x,w.y,w.z,w.w)
        //call q.mult(w)
        call w.destroy()
    endfunction
   
    function rotateMatrixByMatrix takes matrix m, matrix mRot returns nothing
        local matrix m2 = mRot.copy()
        call m.multSwapOrder(m2)
        call m2.invert()
        call m.mult(m2)
        call m2.destroy()
    endfunction
   
    function rotateMatrixByQuaternion takes matrix m, quaternion q returns nothing
        local matrix m2 = RotationQuat2Mat(q)
        call m.multSwapOrder(m2)
        call m2.invert()
        call m.mult(m2)
        call m2.destroy()
    endfunction
   
    // Matrix x Matrix will be the rotation around both that matrices after each other
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Handle Recyclers
//*
//***************************************************************************

library handleRecyclers
    function H2I takes handle h returns integer
        return h
        return 0
    endfunction

    globals
        integer gs_N = 0
        group array gs_g
    endglobals

    function NewGroup takes nothing returns group
        if gs_N==0 then
            return CreateGroup()
        endif
        set gs_N=gs_N-1
        return gs_g[gs_N]
    endfunction

    function ReleaseGroup takes group g returns nothing
        if gs_N>8191 then
            call DestroyGroup(g)
        else
            call GroupClear(g)
            set gs_g[gs_N]=g
            set gs_N=gs_N+1
        endif
    endfunction

    globals
        timer array t_t
        integer array t_a
        integer t_N
        integer t_b
    endglobals

    public function initTimers takes integer Count returns nothing
        local timer array tr
        set t_t[Count] = null
        set t_t[0] = CreateTimer()
        set tr[0] = t_t[0]
        set t_b = H2I(t_t[0])
        set t_N = 1
        loop
            exitwhen t_N >= Count
            set t_t[t_N] = CreateTimer()
            set tr[t_N] = t_t[t_N]
            set t_N = t_N + 1
        endloop
    endfunction

    function GetTimerId takes timer t returns integer
        return H2I(t)-t_b
    endfunction

    function NewTimer takes nothing returns timer
        set t_N = t_N - 1
        return t_t[t_N]
    endfunction
   
    function GetTimerStruct takes timer t returns integer
        return t_a[H2I(t)-t_b]
    endfunction
   
    function SetTimerStruct takes timer t, integer i returns nothing
        set t_a[H2I(t)-t_b]=i
    endfunction

    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        set t_t[t_N] = t
        set t_N = t_N + 1
    endfunction
endlibrary
//TESH.scrollpos=54
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  List
//*
//***************************************************************************

library lists
    struct listnode
        listnode next
        listnode previous
        integer content
       
        static method create takes integer content returns listnode
            local listnode ln = listnode.allocate()
            set ln.next = 0
            set ln.previous = 0
            set ln.content = content
            return ln
        endmethod
    endstruct
   
    struct list
        listnode first = 0
        listnode pointer = 0
        listnode last = 0
       
        method toNext takes nothing returns nothing
            set .pointer = .pointer.next
        endmethod
       
        method toPrevious takes nothing returns nothing
            set .pointer = .pointer.previous
        endmethod
       
        method toFirst takes nothing returns nothing
            set .pointer = .first
        endmethod
       
        method toLast takes nothing returns nothing
            set .pointer = .last
        endmethod
       
        method isFirst takes nothing returns boolean
            return (.pointer == .first)
        endmethod
       
        method isLast takes nothing returns boolean
            return (.pointer == .last)
        endmethod
       
        method getContent takes nothing returns integer
            return .pointer.content
        endmethod
       
        method setContent takes integer i returns nothing
            set .pointer.content = i
        endmethod
       
        method insertBehind takes integer content returns listnode
            local listnode ln = listnode.create(content)
            if .pointer==0 then
                if .last!=0 then
                    set ln.previous = .last
                    set .last.next = ln
                endif
                set .last = ln
            else
                set ln.next = .pointer.next
                if ln.next==0 then
                    set .last = ln
                else
                    set ln.next.previous = ln
                endif
                set ln.previous = .pointer
                set .pointer.next = ln
            endif
            if .first==0 then
                set .first = ln
            endif
            return ln
        endmethod
       
        method insertBefore takes integer content returns listnode
            local listnode ln = listnode.create(content)
            if .pointer==0 then
                if .first!=0 then
                    set ln.next = .first
                    set .first.previous = ln
                endif
                set .first = ln
            else
                set ln.previous = .pointer.previous
                if ln.previous==0 then
                    set .first = ln
                else
                    set ln.previous.next = ln
                endif
                set ln.next = .pointer
                set .pointer.previous = ln
            endif
            if .last==0 then
                set .last = ln
            endif
            return ln
        endmethod
       
        method pop takes nothing returns nothing
            local listnode new = .pointer.next
            if .pointer.next!=0 then
                set .pointer.next.previous = .pointer.previous
            endif
            if .pointer.previous!=0 then
                set .pointer.previous.next = .pointer.next
            endif
            if .pointer==.last then
                set .last = .pointer.previous
            endif
            if .pointer==.first then
                set .first = .pointer.next
            endif
            call .pointer.destroy()
            set .pointer = new
        endmethod
       
        method search takes integer content returns boolean
            local listnode p = .first
            loop
                exitwhen p==0
                if p.content==content then
                    set .pointer=p
                    return true
                endif
                set p = p.next
            endloop
            return false
        endmethod
       
        method searchAndPop takes integer content returns boolean
            local listnode p = .pointer
            if .search(content) then
                call .pop()
                set .pointer = p
                return true
            endif
            return false
        endmethod
       
        method outOfList takes nothing returns boolean
            return .pointer==0
        endmethod
       
        method isEmpty takes nothing returns boolean
            return .first==0
        endmethod
       
        method clear takes nothing returns nothing
            local listnode cur
            loop
                exitwhen .first==0
                set cur = .first
                set .first = .first.next
                call cur.destroy()
            endloop
            set .last = 0
            set .pointer = 0
        endmethod
       
        method onDestroy takes nothing returns nothing
            call .clear()
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=4
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Arrow Keys
//*
//***************************************************************************

library arrowkeys initializer arrowkeysInit
    globals
        boolean array arrowKeyPressed
    endglobals
   
    function arrow_LEFT_DOWN takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4] = true
    endfunction

    function arrow_LEFT_UP takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4] = false
    endfunction

    function arrow_RIGHT_DOWN takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+1] = true
    endfunction

    function arrow_RIGHT_UP takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+1] = false
    endfunction

    function arrow_DOWN_DOWN takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+2] = true
    endfunction

    function arrow_DOWN_UP takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+2] = false
    endfunction

    function arrow_UP_DOWN takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+3] = true
    endfunction

    function arrow_UP_UP takes nothing returns nothing
        set arrowKeyPressed[GetPlayerId(GetTriggerPlayer())*4+3] = false
    endfunction
   
    function createTrig takes code actionFunc, playerevent eventId returns nothing
        local trigger t = CreateTrigger()
        local integer PlayerId = 0
        call TriggerAddAction(t, actionFunc)
        loop
            exitwhen PlayerId > 9
            call TriggerRegisterPlayerEvent(t, Player(PlayerId), eventId)
            set PlayerId = PlayerId + 1
        endloop
        set t = null
    endfunction
   
    function arrowLeftPressed takes integer pid returns boolean
        return arrowKeyPressed[pid*4]
    endfunction
   
    function arrowRightPressed takes integer pid returns boolean
        return arrowKeyPressed[pid*4+1]
    endfunction
   
    function arrowDownPressed takes integer pid returns boolean
        return arrowKeyPressed[pid*4+2]
    endfunction
   
    function arrowUpPressed takes integer pid returns boolean
        return arrowKeyPressed[pid*4+3]
    endfunction

    function arrowkeysInit takes nothing returns nothing
        call createTrig(function arrow_LEFT_DOWN, EVENT_PLAYER_ARROW_LEFT_DOWN)
        call createTrig(function arrow_LEFT_UP, EVENT_PLAYER_ARROW_LEFT_UP)
        call createTrig(function arrow_RIGHT_DOWN, EVENT_PLAYER_ARROW_RIGHT_DOWN)
        call createTrig(function arrow_RIGHT_UP, EVENT_PLAYER_ARROW_RIGHT_UP)
        call createTrig(function arrow_DOWN_DOWN, EVENT_PLAYER_ARROW_DOWN_DOWN)
        call createTrig(function arrow_DOWN_UP, EVENT_PLAYER_ARROW_DOWN_UP)
        call createTrig(function arrow_UP_DOWN, EVENT_PLAYER_ARROW_UP_DOWN)
        call createTrig(function arrow_UP_UP, EVENT_PLAYER_ARROW_UP_UP)
    endfunction
endlibrary
//TESH.scrollpos=205
//TESH.alwaysfold=0
library cameramap initializer cameramapInit
    globals
        location cameramapLoc
        real array camOffset[256][256]
        real mapMinX
        real mapMinY
        real mapMaxX
        real mapMaxY
    endglobals
   
    function GetGridCamOffset takes integer ix, integer iy, integer offx, integer offy returns real
        local real r
        local integer ixl = ix-1
        local integer ixr = ix+1
        local integer iyd = iy-1
        local integer iyu = iy+1
        if ixl<0 then
            set ixl = 0
        endif
        if iyd<0 then
            set iyd = 0
        endif
        if ixr>256 then
            set ixr=256
        endif
        if iyu>256 then
            set iyu=256
        endif
        if offx>0 then
            if offy>0 then
                set r =   .089696*camOffset[ixl][iyu]+ .139657*camOffset[ix][iyu]+ .097349*camOffset[ixr][iyu]
                set r = r+.130989*camOffset[ixl][iy] + .099380*camOffset[ix][iy] + .139657*camOffset[ixr][iy]
                set r = r+.082587*camOffset[ixl][iyd]+ .130989*camOffset[ix][iyd]+ .089696*camOffset[ixr][iyd]
            elseif offy<0 then
                set r =   .082587*camOffset[ixl][iyu]+ .130989*camOffset[ix][iyu]+ .089696*camOffset[ixr][iyu]
                set r = r+.130989*camOffset[ixl][iy] + .099380*camOffset[ix][iy] + .139657*camOffset[ixr][iy]
                set r = r+.089696*camOffset[ixl][iyd]+ .139657*camOffset[ix][iyd]+ .097349*camOffset[ixr][iyd]
            else
                set r =   .084604*camOffset[ixl][iyu]+ .134226*camOffset[ix][iyu]+ .091913*camOffset[ixr][iyu]
                set r = r+.134017*camOffset[ixl][iy] + .101594*camOffset[ix][iy] + .142877*camOffset[ixr][iy]
                set r = r+.084604*camOffset[ixl][iyd]+ .134226*camOffset[ix][iyd]+ .091913*camOffset[ixr][iyd]
            endif
        elseif offx<0 then
            if offy>0 then
                set r =   .097349*camOffset[ixl][iyu]+ .139657*camOffset[ix][iyu]+ .089696*camOffset[ixr][iyu]
                set r = r+.139657*camOffset[ixl][iy] + .099380*camOffset[ix][iy] + .130989*camOffset[ixr][iy]
                set r = r+.089696*camOffset[ixl][iyd]+ .130989*camOffset[ix][iyd]+ .082587*camOffset[ixr][iyd]
            elseif offy<0 then
                set r =   .089696*camOffset[ixl][iyu]+ .130989*camOffset[ix][iyu]+ .082587*camOffset[ixr][iyu]
                set r = r+.139657*camOffset[ixl][iy] + .099380*camOffset[ix][iy] + .130989*camOffset[ixr][iy]
                set r = r+.097349*camOffset[ixl][iyd]+ .139657*camOffset[ix][iyd]+ .089696*camOffset[ixr][iyd]
            else
                set r =   .091913*camOffset[ixl][iyu]+ .134226*camOffset[ix][iyu]+ .084604*camOffset[ixr][iyu]
                set r = r+.142877*camOffset[ixl][iy] + .101594*camOffset[ix][iy] + .134017*camOffset[ixr][iy]
                set r = r+.091913*camOffset[ixl][iyd]+ .134226*camOffset[ix][iyd]+ .084604*camOffset[ixr][iyd]
            endif
        else
            if offy>0 then
                set r =   .091913*camOffset[ixl][iyu]+ .142877*camOffset[ix][iyu]+ .091913*camOffset[ixr][iyu]
                set r = r+.134226*camOffset[ixl][iy] + .101594*camOffset[ix][iy] + .134226*camOffset[ixr][iy]
                set r = r+.084604*camOffset[ixl][iyd]+ .134017*camOffset[ix][iyd]+ .084604*camOffset[ixr][iyd]
            elseif offy<0 then
                set r =   .084604*camOffset[ixl][iyu]+ .134017*camOffset[ix][iyu]+ .084604*camOffset[ixr][iyu]
                set r = r+.134226*camOffset[ixl][iy] + .101594*camOffset[ix][iy] + .134226*camOffset[ixr][iy]                
                set r = r+.091913*camOffset[ixl][iyd]+ .142877*camOffset[ix][iyd]+ .091913*camOffset[ixr][iyd]
            else
                set r =   .086125*camOffset[ixl][iyu]+ .136429*camOffset[ix][iyu]+ .086125*camOffset[ixr][iyu]
                set r = r+.136429*camOffset[ixl][iy] + .109784*camOffset[ix][iy] + .136429*camOffset[ixr][iy]
                set r = r+.086125*camOffset[ixl][iyd]+ .136429*camOffset[ix][iyd]+ .086125*camOffset[ixr][iyd]
            endif
        endif
        return r
    endfunction
   
    function GetCamOffset takes real x, real y returns real
        local integer iXLo = R2I((x-mapMinX)/512.+.5)
        local integer iYLo = R2I((y-mapMinY)/512.+.5)
        local integer iXHi = iXLo+1
        local integer iYHi = iYLo+1
        local integer iChkXLo
        local integer iChkXLoOff
        local integer iChkXHi
        local integer iChkXHiOff
        local integer iChkYLo
        local integer iChkYLoOff
        local integer iChkYHi
        local integer iChkYHiOff
        local real XLo
        local real YLo
        local real XHi
        local real YHi
        local real rX
        local real rY
        local real r
        local real LoDX = (x-mapMinX)-(iXLo*512.-256.)
        local real LoDY = (y-mapMinY)-(iYLo*512.-256.)
        if LoDX<=12 then
            set iChkXLo = iXLo
            set iChkXLoOff = 0
            set iChkXHi = iXLo
            set iChkXHiOff = 1
        elseif LoDX<500 then
            set iChkXLo = iXLo
            set iChkXLoOff = 1
            set iChkXHi = iXHi
            set iChkXHiOff =-1
        else
            set iChkXLo = iXHi
            set iChkXLoOff =-1
            set iChkXHi = iXHi
            set iChkXHiOff = 0
        endif
        if LoDY<=12 then
            set iChkYLo = iYLo
            set iChkYLoOff = 0
            set iChkYHi = iYLo
            set iChkYHiOff = 1
        elseif LoDY<500 then
            set iChkYLo = iYLo
            set iChkYLoOff = 1
            set iChkYHi = iYHi
            set iChkYHiOff =-1
        else
            set iChkYLo = iYHi
            set iChkYLoOff =-1
            set iChkYHi = iYHi
            set iChkYHiOff = 0
        endif
        set XLo = iChkXLo*512.+iChkXLoOff*12.-256.
        set XHi = iChkXHi*512.+iChkXHiOff*12.-256.
        set YLo = iChkYLo*512.+iChkYLoOff*12.-256.
        set YHi = iChkYHi*512.+iChkYHiOff*12.-256.
        set rX = ((x-mapMinX)-XLo)/(XHi-XLo)
        set rY = ((y-mapMinY)-YLo)/(YHi-YLo)
        set r =   GetGridCamOffset(iChkXHi,iChkYHi,iChkXHiOff,iChkYHiOff)*rX*rY
        set r = r+GetGridCamOffset(iChkXLo,iChkYHi,iChkXLoOff,iChkYHiOff)*(1-rX)*rY
        set r = r+GetGridCamOffset(iChkXHi,iChkYLo,iChkXHiOff,iChkYLoOff)*rX*(1-rY)
        set r = r+GetGridCamOffset(iChkXLo,iChkYLo,iChkXLoOff,iChkYLoOff)*(1-rX)*(1-rY)
        return r
    endfunction
   
    function cameramapInit_GridSub takes nothing returns nothing
        local integer index
        local integer iX = -6
        local integer iY
        local real z
        local real r
        local real x = bj_cineFadeContinueRed
        local real y = bj_cineFadeContinueBlue
        local real i = 64 //9*4+12*2+4
        call MoveLocation(cameramapLoc,x,y)
        set z = GetLocationZ(cameramapLoc)
        set r = 0.
        call MoveLocation(cameramapLoc,x-128.,y)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x,y)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x+128.,y)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x-128.,y+128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x,y+128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x+128.,y+128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x-128.,y-128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x,y-128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
        call MoveLocation(cameramapLoc,x+128.,y-128.)
        set r = r+GetLocationZ(cameramapLoc)*4./i
       
        call MoveLocation(cameramapLoc,x-256.,y-128.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x-256.,y)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x-256.,y+128.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
       
        call MoveLocation(cameramapLoc,x+256.,y-128.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x+256.,y)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x+256.,y+128.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
       
        call MoveLocation(cameramapLoc,x-128.,y-256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x,y-256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x+128.,y-256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
       
        call MoveLocation(cameramapLoc,x-128.,y+256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x,y+256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
        call MoveLocation(cameramapLoc,x+128.,y+256.)
        set r = r+GetLocationZ(cameramapLoc)*2./i
       
        call MoveLocation(cameramapLoc,x+256.,y+256.)
        set r = r+GetLocationZ(cameramapLoc)*1./i
        call MoveLocation(cameramapLoc,x+256.,y-256.)
        set r = r+GetLocationZ(cameramapLoc)*1./i
        call MoveLocation(cameramapLoc,x-256.,y+256.)
        set r = r+GetLocationZ(cameramapLoc)*1./i
        call MoveLocation(cameramapLoc,x-256.,y-256.)
        set r = r+GetLocationZ(cameramapLoc)*1./i
        set bj_cineFadeContinueGreen = r
    endfunction

    function cameramapInit takes nothing returns nothing
        local real x
        local real y
        local integer iX = 0
        local integer iY
        local rect map = GetWorldBounds()
        set mapMinX = GetRectMinX(map)
        set mapMinY = GetRectMinY(map)
        set mapMaxX = GetRectMaxX(map)
        set mapMaxY = GetRectMaxY(map)
        call RemoveRect(map)
        set map = null
        set x = mapMinX+256.
        set y = mapMinY+256.
        set cameramapLoc = Location(0,0)
        loop
            exitwhen x+iX*512.>mapMaxX
            set iY = 0
            loop
                exitwhen y+iY*512.>mapMaxY
                set bj_cineFadeContinueRed = x+iX*512.
                set bj_cineFadeContinueBlue = y+iY*512.
                call ExecuteFunc("cameramapInit_GridSub")
                set camOffset[(iX+1)][iY+1] = bj_cineFadeContinueGreen
                set iY = iY + 1
            endloop
            set iX = iX + 1
        endloop
        call RemoveLocation(cameramapLoc)
        set cameramapLoc = null
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library water3d initializer water3dInit
    globals
        constant real WATER_HEIGHT = 256.
    endglobals
   
    function water3dInit takes nothing returns nothing
        local rect map = GetWorldBounds()
        local real minX = GetRectMinX(map)
        local real minY = GetRectMinY(map)
        local real maxX = GetRectMaxX(map)
        local real maxY = GetRectMaxY(map)
        local real x
        local real y
        local integer iX = 0
        local integer iY
        call RemoveRect(map)
        set map = null
        set x = mapMinX+256.
        set y = mapMinY+256.
        loop
            exitwhen x+iX*512.>mapMaxX
            set iY = 0
            loop
                exitwhen y+iY*512.>mapMaxY
                call CreateDestructableZ('B000',x+iX*512.,y+iY*512.,WATER_HEIGHT,0,1,0)
                set iY = iY + 1
            endloop
            set iX = iX + 1
        endloop
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physTerrain uses maths, vectors
    function GetTerrainZ takes real x, real y returns real
        call MoveLocation(physTempLoc,x,y)
        return GetLocationZ(physTempLoc)
    endfunction
   
    function GetTerrainNormal takes real x, real y returns vector
        local real z
        local real radius = 8.
        local vector v = vector.create(0,0,0)
        set z = GetTerrainZ(x-radius,y)
        set v.x = z-GetTerrainZ(x+radius,y)
        set z = GetTerrainZ(x,y-radius)
        set v.y = z-GetTerrainZ(x,y+radius)
        set v.z = radius*2
        call v.normalise()
        return v
    endfunction
   
    function GetSurfaceZ takes real x, real y returns real
        local real xLo = Round(x/128.-.5)*128.
        local real yLo = Round(y/128.-.5)*128.
        local real xHi = xLo+128.
        local real yHi = yLo+128.
        local real baseZ
        if x-xLo+y-yLo>128. then
            set baseZ = GetTerrainZ(xHi,yHi)
            return baseZ+((xHi-x)*(GetTerrainZ(xLo,yHi)-baseZ)+(yHi-y)*(GetTerrainZ(xHi,yLo)-baseZ))/128.
        endif
        set baseZ = GetTerrainZ(xLo,yLo)
        return baseZ+((x-xLo)*(GetTerrainZ(xHi,yLo)-baseZ)+(y-yLo)*(GetTerrainZ(xLo,yHi)-baseZ))/128.
    endfunction
   
    function GetSurfaceNormal takes real x, real y returns vector
        local real xLo = Round(x/128.-.5)*128
        local real yLo = Round(y/128.-.5)*128
        local real xHi = xLo+128.
        local real yHi = yLo+128.
        local real baseZ
        local vector v
        if x-xLo+y-yLo>128. then
            set baseZ = GetTerrainZ(xHi,yHi)
            set v = vector.create(GetTerrainZ(xLo,yHi)-baseZ,GetTerrainZ(xHi,yLo)-baseZ,128.)
            call v.normalise()
            return v
        endif
        set baseZ = GetTerrainZ(xLo,yLo)
        set v = vector.create(baseZ-GetTerrainZ(xHi,yLo),baseZ-GetTerrainZ(xLo,yHi),128.)
        call v.normalise()
        return v
    endfunction
   
    function markPosition takes real x, real y, real z returns nothing
        local texttag tt = CreateTextTag()
        call SetTextTagColor(tt,255,255,255,0)
        call SetTextTagPos(tt,x,y,z-GetTerrainZ(x,y))
        call SetTextTagVisibility(tt,true)
        call SetTextTagText(tt,"|cffffffff.|r",0.023)
        call SetTextTagPermanent(tt,true)
        call SetTextTagColor(tt,255,255,255,255)
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library clouds uses physTerrain
    globals
        group cloudUnits
        constant real CLOUD_MIN_Z = 1100
        constant real CLOUD_MAX_Z = 1400
    endglobals
   
    function SetCloudCount takes integer i returns nothing
        local integer j = 1
        local unit cur
        local real x
        local real y
        local real z
        local real tz
        local integer c
        local location loc = Location(0,0)
        loop
            exitwhen j>i
            set x = GetRandomReal(mapMinX,mapMaxX)
            set y = GetRandomReal(mapMinY,mapMaxY)
            set z = GetRandomReal(CLOUD_MIN_Z,CLOUD_MAX_Z)
            call MoveLocation(loc,x,y)
            set tz = GetLocationZ(loc)
            if z>tz then
                set cur = CreateUnit(Player(0),'h000',GetRandomReal(mapMinX,mapMaxX),GetRandomReal(mapMinY,mapMaxY),270.)
                call AddSpecialEffectTarget("war3mapImported\\Cloud.mdx",cur,"origin")
                call UnitAddAbility(cur,'Amrf')
                call SetUnitAnimationByIndex(cur, 1)
                call SetUnitFlyHeight(cur,z-tz,0)
                call SetUnitPosition(cur,x,y)
                call UnitRemoveAbility(cur,'Amrf')
                call SetUnitScale(cur,2,2,2)
                set c = GetRandomInt(127,255)
                call SetUnitVertexColor(cur,c,c,c,255)
                set j = j + 1
            endif
        endloop
        call RemoveLocation(loc)
        set loc = null
    endfunction
endlibrary
//TESH.scrollpos=20
//TESH.alwaysfold=0
//***************************************************************************
//*
//*  Camera
//*
//***************************************************************************

library camera uses physTerrain
    globals
        real eyeX = 0
        real eyeY = 0
        real eyeZ = 0
    endglobals
   
    function CameraFollow takes real x, real y, real z, real dist returns nothing
        local real dX = x-eyeX
        local real dY = y-eyeY
        local real dZ = z-eyeZ
        local real d
        local real lim = GetTerrainZ(x,y)+64
        if eyeZ<lim then
            set dZ = z-lim
        endif
        set d = SquareRoot(dX*dX+dY*dY+dZ*dZ)+.001
        call PanCameraToTimed(x,y,0)
        call SetCameraField(CAMERA_FIELD_ZOFFSET,z-GetCamOffset(x,y),0)
        call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, dist, 0 )
        set eyeX = x-dX*dist/d
        set eyeY = y-dY*dist/d
        set eyeZ = z-dZ*dist/d
        call SetCameraField( CAMERA_FIELD_ROTATION, Atan2(dY,dX)*bj_RADTODEG, 0 )
        call SetCameraField( CAMERA_FIELD_FARZ, 7500, 0 )
        call SetCameraField( CAMERA_FIELD_ANGLE_OF_ATTACK, Atan2(dZ,SquareRoot(dX*dX+dY*dY))*bj_RADTODEG, 0 )
    endfunction
   
    function CameraLookAt takes real x, real y, real z returns nothing
        local real dX = x-eyeX
        local real dY = y-eyeY
        local real dZ = z-eyeZ
        call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 0, 0 )
        call SetCameraField( CAMERA_FIELD_ROTATION, Atan2(dY,dX)*bj_RADTODEG, 0 )
        call SetCameraField( CAMERA_FIELD_FARZ, 7500, 0 )
        call SetCameraField( CAMERA_FIELD_ANGLE_OF_ATTACK, Atan2(dZ,SquareRoot(dX*dX+dY*dY))*bj_RADTODEG, 0 )
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physClasses uses collisionshapes
    struct listpoint
        vector pos
        listpoint next
    endstruct
   
    struct class
        real mass
        matrix inertia
        matrix inertiaInv
        string modelName
        real modelScale
        integer unitType
        real radius
        shape collshape
        listpoint firstP
       
        static method create takes nothing returns class
            local class c = class.allocate()
            set c.firstP = 0
            set c.modelScale = 1.
            return c
        endmethod
       
        method addCollisionPoint takes real x, real y, real z returns nothing
            local listpoint p = listpoint.create()
            set p.pos = vector.create(x,y,z)
            set p.next = .firstP
            set .firstP = p
        endmethod
       
        method scaleVisual takes real r returns nothing
            local listpoint cur = .firstP
            set .modelScale = .modelScale*r
            call .collshape.scale(r)
            loop
                exitwhen cur==0
                call cur.pos.scale(r)
                set cur = cur.next
            endloop
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physParticleClasses
    struct pclass
        real mass
        string modelName
        real scale
    endstruct
endlibrary
//TESH.scrollpos=76
//TESH.alwaysfold=0
library physObjects uses physClasses, vectors, physTerrain
    globals
        objectCtrl NO_CONTROL
    endglobals
   
    struct objectCtrl
        real enginePower
        real turnPower
        integer weaponShots1
        integer weaponShots2
    endstruct
   
    struct object
        unit u
        vector pos          // world-coordinates
        vector posTmp
        vector vel          // world-coordinates
        vector velTmp
        vector accel
        quaternion orient
        quaternion orientTmp
        vector angVel       // world-coordinates
        vector angVelTmp
        vector angAccel
        matrix inertiaWorld
        matrix inertiaWorldInv
        objectCtrl ctrl
        list cellList
        real firstCollTime
        effect e
        image shadow
        class c
       
        method updateShadow takes real flyHeight returns nothing
            local real visibility = (500-flyHeight)/500.
            local integer alpha = R2I(visibility*255.)
            if alpha<0 then
                set alpha = 0
            elseif alpha>255 then
                set alpha = 255
            endif
            call SetImageColor(.shadow,255,255,255,alpha)
            call SetImagePosition(.shadow,.pos.x-100*.5, .pos.y-100*.5, 0)
        endmethod
       
        method addShadow takes nothing returns image
            local real size = 100//.c.radius
            if size<1. then
               return null
            endif
            set .shadow = CreateImage("ReplaceableTextures\\Shadows\\ShadowFlyer.blp", size, size, size, .pos.x-size*.5, .pos.y-size*.5, 0, 0, 0, 0, 2)
            call SetImageColor(.shadow,255,255,255,255)
            call SetImageRenderAlways( .shadow, true )
            call ShowImage(.shadow, true)
            return .shadow
        endmethod

        method updateAnimation takes nothing returns nothing
            local vector nose = vector.create(1,0,0)
            local vector head = vector.create(0,0,1)
            local vector normal
            local vector headNoRoll
            local vector zaxis = vector.create(0,0,1)
            local real r
            local real terrZ
            call nose.rotateByQuaternion(.orient)
            call head.rotateByQuaternion(.orient)
            set normal = vector.createProduct(nose,zaxis)
            set headNoRoll = vector.createProduct(nose,normal)
            call zaxis.destroy()
            call headNoRoll.normalise()
            set r = scalarProduct(head,headNoRoll)
            // Prevent rounding errors
            if r<=-1 then
                set r = bj_PI
            elseif r>=1 then
                set r = 0
            else
                set r = Acos(r)
            endif
            if scalarProduct(head,normal)<0 then
                set r = -r
            endif
            call headNoRoll.destroy()
            call normal.destroy()
            set r = bj_PI-r
            set terrZ = GetTerrainZ(.pos.x,.pos.y)
            call SetUnitAnimationByIndex(.u, R2I(ModuloReal(r*bj_RADTODEG*0.7+.5,252)))
            call SetUnitLookAt(.u,"bone_Head",.u,nose.x,nose.y,nose.z)
            call SetUnitFlyHeight(.u,.pos.z-terrZ,0)
            call SetUnitPosition(.u,.pos.x,.pos.y)
            if GetOwningPlayer(.u)==GetLocalPlayer() then
                if true then
                    call CameraFollow(.pos.x,.pos.y,.pos.z,300)
                    call SetCameraTargetController(.u,0,0,false)
                elseif true then
                    call SetCameraTargetController(.u,head.x*0.,head.y*0.,false)
                    call SetCameraField( CAMERA_FIELD_FARZ, 7500, 0 )
                    call SetCameraField( CAMERA_FIELD_TARGET_DISTANCE, 150, 0 )
                    call SetCameraField( CAMERA_FIELD_ZOFFSET, .pos.z+head.z*0.-GetCamOffset(.pos.x+head.x*0.,.pos.y+head.y*0.), 0 )
                    call SetCameraField( CAMERA_FIELD_ROTATION, Atan2(nose.y,nose.x)*bj_RADTODEG, 0 )
                    call SetCameraField( CAMERA_FIELD_ROLL,r*bj_RADTODEG,0)
                    call SetCameraField( CAMERA_FIELD_ANGLE_OF_ATTACK, Atan2(nose.z,SquareRoot(nose.x*nose.x+nose.y*nose.y))*bj_RADTODEG, 0 )
                elseif true then
                    call SetCameraTargetController(.u,0.,0.,false)
                    call SetCameraField( CAMERA_FIELD_FARZ, 7500, 0 )
                    call SetCameraField( CAMERA_FIELD_TARGET_DISTANCE, 1000, 0 )
                    call SetCameraField( CAMERA_FIELD_ZOFFSET, .pos.z-GetCamOffset(.pos.x,.pos.y), 0 )
                    call SetCameraField( CAMERA_FIELD_ROTATION, 270, 0 )
                    call SetCameraField( CAMERA_FIELD_ROLL,0,0)
                    call SetCameraField( CAMERA_FIELD_ANGLE_OF_ATTACK, 305, 0 )
                else
                    set eyeX = 0
                    set eyeY = 0
                    set eyeZ = 1000
                    call SetCameraTargetController(.u,eyeX-.pos.x,eyeY-.pos.y,false)
                    call SetCameraField( CAMERA_FIELD_ZOFFSET, eyeZ-GetCamOffset(0,0), 0 )
                    call CameraLookAt(.pos.x,.pos.y,.pos.z)
                endif
            endif
            call head.destroy()
            call nose.destroy()
            call SetPlayerState(GetOwningPlayer(.u),PLAYER_STATE_RESOURCE_GOLD,R2I(.vel.length()*.2*3.6))
            if .shadow!=null then
                call .updateShadow(.pos.z-terrZ)
            endif
        endmethod
       
        method addForce takes vector relPos, vector f returns nothing
            local vector angVelChange = vector.createProduct(relPos,f)
            call angVelChange.transformByMatrix(.inertiaWorldInv)
            call .accel.addScaled(f,1./.c.mass)
            call .angAccel.add(angVelChange)
            call angVelChange.destroy()
        endmethod
       
        method addRudderForce takes vector relPos, vector normal, real p returns nothing
            // Takes global coordinates
            local real velIntoFrict
            local real r
            local vector v = vector.createProduct(.angVel,relPos) // relPos für angular!
            local vector f
            call v.add(.vel)
            set r = scalarProduct(normal,v)
            call v.destroy()
            set velIntoFrict = r*r*p
            if r>0 then
                set velIntoFrict=-velIntoFrict
            endif
            set f = normal.copyScaled(velIntoFrict)
            call .addForce(relPos,f)
            call f.destroy()
        endmethod
       
        method addWing takes vector relPos, vector normal, real p returns nothing
            // Takes local coordinates
            local vector normalRotated = normal.copy()
            local vector relPosRotated = relPos.copy()
            call normalRotated.rotateByQuaternion(.orient)
            call relPosRotated.rotateByQuaternion(.orient)
            call .addRudderForce(relPosRotated,normalRotated,p)
            call relPosRotated.destroy()
            call normalRotated.destroy()
        endmethod
       
        method addImpulse takes vector relPos, vector f returns nothing
            local vector angVelChange = vector.createProduct(relPos,f)
            call angVelChange.transformByMatrix(.inertiaWorldInv)
            call .vel.addScaled(f,1./.c.mass)
            call .angVel.add(angVelChange)
            call angVelChange.destroy()
        endmethod
       
        static method create takes player pid, class c, real x, real y, real z, real pitch, real roll, real yaw returns object
            local object o = object.allocate()
            set o.u = CreateUnit(pid,c.unitType,x,y,yaw*bj_RADTODEG)
            set o.e = AddSpecialEffectTarget(c.modelName,o.u,"origin")
            call SetUnitAnimation(o.u,"stand")
            call SetUnitPathing(o.u,false)
            call SetUnitTimeScale(o.u,1000.)
            call SetUnitScale(o.u,c.modelScale,c.modelScale,c.modelScale)
            set o.pos = vector.create(x,y,z)
            set o.posTmp = vector.create(x,y,z)
            set o.vel = vector.create(0,0,0)
            set o.velTmp = vector.create(0,0,0)
            set o.accel = vector.create(0,0,0)
            set o.angVel = vector.create(0,0,0)
            set o.angVelTmp = vector.create(0,0,0)
            set o.angAccel = vector.create(0,0,0)
            set o.orient = quaternion.create(0,0,0,1)
            set o.orientTmp = quaternion.create(0,0,0,1)
            set o.inertiaWorld = c.inertia.copy()
            set o.inertiaWorldInv = c.inertiaInv.copy()
            set o.firstCollTime = 1000.
            set o.c = c
            set o.cellList = 0
            set o.ctrl = objectCtrl.create()
            //call o.addShadow()
            call UnitAddAbility(o.u,'Amrf')
            call o.updateAnimation()
            call UnitRemoveAbility(o.u,'Amrf')
            call physObjectList.insertBehind(o)
            return o
        endmethod
       
        method onDestroy takes nothing returns nothing
            call .pos.destroy()
            call .vel.destroy()
            call .orient.destroy()
            call .angVel.destroy()
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physParticles uses physParticleClasses, vectors, physTerrain
    struct particle
        unit u
        vector pos          // world-coordinates
        vector vel          // world-coordinates
        object sourceObj
        object targetObj
        real firstCollTime
        effect e
        pclass c
        real age
   
        method updateAnimation takes nothing returns nothing
            call SetUnitAnimationByIndex(.u, 1)
            call SetUnitLookAt(.u,"bone_Head",.u,.vel.x,.vel.y,.vel.z)
            // Billboarded: call SetUnitLookAt(.u,"bone_Head",.u,GetCameraEyePositionX()-.pos.x,GetCameraEyePositionY()-.pos.y,GetCameraEyePositionZ()-.pos.z)
            call SetUnitFlyHeight(.u,.pos.z-GetTerrainZ(.pos.x,.pos.y),0)
            call SetUnitPosition(.u,.pos.x,.pos.y)
        endmethod
   
        static method create takes player pid, pclass c, real x, real y, real z, real vX, real vY, real vZ returns particle
            local particle p = particle.allocate()
            set p.u = CreateUnit(pid,'h000',x,y,0.)
            call SetUnitScale(p.u,c.scale,c.scale,c.scale)
            set p.e = AddSpecialEffectTarget(c.modelName,p.u,"origin")
            call SetUnitAnimation(p.u,"stand")
            call SetUnitPathing(p.u,false)
            call SetUnitTimeScale(p.u,1000.)
            set p.pos = vector.create(x,y,z)
            set p.vel = vector.create(vX,vY,vZ)
            set p.firstCollTime = 1000.
            set p.c = c
            set p.age = 0
            call UnitAddAbility(p.u,'Amrf')
            call p.updateAnimation()
            call UnitRemoveAbility(p.u,'Amrf')
            call physParticleList.insertBehind(p)
            return p
        endmethod
       
        method onDestroy takes nothing returns nothing
            if .e!=null then
                call DestroyEffect(.e)
            endif
            call RemoveUnit(.u)
            call .pos.destroy()
            call .vel.destroy()
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=88
//TESH.alwaysfold=0
library collisionshapes
    globals
        constant integer SHAPE_SPHERE       = 0
        constant integer SHAPE_POLYGON      = 1
        constant integer SHAPE_BOX          = 2
        constant integer SHAPE_CYLINDER     = 3
    endglobals
   
    struct shapePolygon
        list nodes
    endstruct
   
    struct shapeSphere
        vector relPos
        real radius
    endstruct
   
    struct shapeBox
        vector relPos
        vector size
        quaternion orient
    endstruct
   
    struct shapeCylinder
        vector relPos
        real radius
        real height
        quaternion orient
    endstruct
   
    struct shapeListNode
        integer form
        integer sub
        shapeListNode next
        shapeListNode prev
       
        method scale takes real r returns nothing
            local shapeSphere s1
            if .form==SHAPE_SPHERE then
                set s1 = .sub
                set s1.radius = s1.radius*r
                call s1.relPos.scale(r)
            endif
        endmethod
    endstruct
   
    struct shape
        shapeListNode first
        shapeListNode last
       
        static method create takes nothing returns shape
            local shape s = shape.allocate()
            set s.first = 0
            set s.last = 0
            return s
        endmethod
       
        method addSubShapeToList takes integer form, integer subshape returns nothing
            local shapeListNode ln = shapeListNode.create()
            set ln.form = form
            set ln.sub = subshape
            if .last!=0 then
                set ln.prev = .last
                set .last.next = ln
            endif
            set .last = ln
            if .first==0 then
                set .first = ln
            endif
        endmethod
       
        method addNewSphere takes vector relPos, real radius returns shapeSphere
            local shapeSphere s = shapeSphere.create()
            set s.relPos = relPos
            set s.radius = radius
            call .addSubShapeToList(SHAPE_SPHERE,s)
            return s
        endmethod
       
        method addNewPolygon takes vector relPos, vector size, quaternion orient returns shapePolygon
            local shapePolygon s = shapePolygon.create()
            return s
        endmethod
       
        method addNewBox takes vector relPos, vector size, quaternion orient returns shapeBox
            local shapeBox s = shapeBox.create()
            set s.relPos = relPos
            set s.size = size
            set s.orient = orient
            call .addSubShapeToList(SHAPE_BOX,s)
            return s
        endmethod
       
        method addNewCylinder takes vector relPos, real radius, real height, quaternion orient returns shapeCylinder
            local shapeCylinder s = shapeCylinder.create()
            set s.relPos = relPos
            set s.radius = radius
            set s.orient = orient
            call .addSubShapeToList(SHAPE_CYLINDER,s)
            return s
        endmethod
       
        method scale takes real r returns nothing
            local shapeListNode cur = .first
            loop
                exitwhen cur==0
                call cur.scale(r)
                set cur = cur.next
            endloop
        endmethod
    endstruct
endlibrary
//TESH.scrollpos=502
//TESH.alwaysfold=0
library physCollisionDetection uses physObjects, physParticles, rotations, quaternions, matrices, vectors
    globals
        collision curColl = 0
    endglobals
   
    function findCollisions_SphereSphere takes object o1, shapeSphere s1, object o2, shapeSphere s2 returns nothing
        local vector relVel = vector.createDifference(o2.velTmp,o1.velTmp)
        local vector relPos = o1.pos.copy()
        local real t = 1000.
        local real a
        local real b
        local real c
        local real r
        call relPos.addRotatedByQuaternion(s1.relPos,o1.orientTmp)
        call relPos.scale(-1)
        call relPos.add(o2.pos)
        call relPos.addRotatedByQuaternion(s2.relPos,o2.orientTmp)
        set a = scalarProduct(relVel,relVel)
        set b = scalarProduct(relVel,relPos)
        set c = scalarProduct(relPos,relPos)-(s1.radius+s1.radius)*(s2.radius+s2.radius)
        if (a>0.) then
            set b = b/a
            set c = c/a
            if ((b*b - 4*c)>=0.) then
                set r = SquareRoot(b*b - 4.*c)
                if (r<=-b) or (r>=b) then
                    set t = (- b - r) *.5
                endif
            endif
        elseif c<0 then
            set t = 0.
        endif
        if t<=PHYS_TIME_STEP then
            if t<o1.firstCollTime then
                set o1.firstCollTime = t
            endif
            if t<o2.firstCollTime then
                set o2.firstCollTime = t
            endif
            call relPos.addScaled(relVel,t)
            call relPos.normalise()
            set curColl = collision.create(o1,o2)
            call curColl.addCollisionPoint(relPos.copyScaled(s1.radius),relPos.copyScaled(-s2.radius),relPos)
        else
            call relPos.destroy()
        endif
        call relVel.destroy()
    endfunction
   
    function findCollisions_SphereSphereNoSwept takes object o1, shapeSphere s1, object o2, shapeSphere s2 returns nothing
        local vector relPos = o1.pos.copy()
        local real d
        call relPos.addRotatedByQuaternion(s1.relPos,o1.orientTmp)
        call relPos.scale(-1)
        call relPos.add(o2.pos)
        call relPos.addRotatedByQuaternion(s2.relPos,o2.orientTmp)
        set d = relPos.length()
        if d<s1.radius+s2.radius then
            set o1.firstCollTime = 0
            set o2.firstCollTime = 0
            if d==0. then
                set relPos.z=1
            else
                call relPos.scale(1/d)
            endif
            set curColl = collision.create(o1,o2)
            call curColl.addCollisionPoint(relPos.copyScaled(s1.radius),relPos.copyScaled(-s2.radius),relPos)
        else
            call relPos.destroy()
        endif
    endfunction
   
    function findCollisions_BoxBox takes object o1, shapeBox s1, object o2, shapeBox s2 returns nothing
        local vector center1 = o1.pos.copy()
        local vector center2 = o2.pos.copy()
        local quaternion rotator1 = o1.orient.copy()
        local quaternion rotator2 = o2.orient.copy()
        local vector axis
        local real p1c
        local real p1x
        local real p1y
        local real p1z
        local real p2c
        local real p2x
        local real p2y
        local real p2z
        call center1.addRotatedByQuaternion(s1.relPos,o1.orient)
        call center2.addRotatedByQuaternion(s2.relPos,o2.orient)
       
        call rotator1.mult(s1.orient)
        call rotator2.mult(s2.orient)
       
        // box 1 z axis
        set axis = vector.create(0,0,1)
        call center1.addRotatedByQuaternion(axis,rotator1)
       
        set p2c = scalarProduct(center2,axis)
       
        call axis.destroy()
       
        // box 2 z axis
        set axis = vector.create(0,0,1)
        call center2.addRotatedByQuaternion(axis,rotator2)
       
        set p1c = scalarProduct(center2,axis)
       
        call axis.destroy()
       
        call rotator1.destroy()
        call rotator2.destroy()
        call center1.destroy()
        call center2.destroy()
    endfunction
   
    function findCollisions_CylinderCylinder takes object o1, shapeCylinder s1, object o2, shapeCylinder s2 returns nothing
       
    endfunction
   
    function findCollisions_ObjectObject takes object o1, object o2 returns nothing
        local shapeListNode ln1
        local shapeListNode ln2
        if o1.c.collshape==0 or o2.c.collshape==0 then
            return
        endif
        set ln1 = o1.c.collshape.first
        loop
            exitwhen ln1==0
            set ln2 = o2.c.collshape.first
            loop
                exitwhen ln2==0
                if ln1.form==SHAPE_SPHERE and ln2.form==SHAPE_SPHERE then
                    call findCollisions_SphereSphere(o1,ln1,o2,ln2)
                elseif ln1.form==SHAPE_BOX and ln2.form==SHAPE_BOX then
                    call findCollisions_BoxBox(o1,ln1,o2,ln2)
                elseif ln1.form==SHAPE_CYLINDER and ln2.form==SHAPE_CYLINDER then
                    call findCollisions_CylinderCylinder(o1,ln1,o2,ln2)
                else
                    call DebugMsg("Unknown collision shape pair")
                endif
                set ln2 = ln2.next
            endloop
            set ln1 = ln1.next
        endloop
    endfunction
   
    function findCollisions_ParticleSphere takes particle p1, object o2, shapeSphere s2 returns nothing
        local vector relVel = vector.createDifference(o2.velTmp,p1.vel)
        local vector relPos = o2.pos.copy()
        local real t = 1000.
        local real a
        local real b
        local real c
        local real r
        call relPos.addRotatedByQuaternion(s2.relPos,o2.orient)
        call relPos.subtract(p1.pos)
        set a = scalarProduct(relVel,relVel)
        set b = scalarProduct(relVel,relPos)
        set c = scalarProduct(relPos,relPos)-s2.radius*s2.radius
        if (a>0.) then
            set b = b/a
            set c = c/a
            if ((b*b - 4*c)>=0.) then
                set r = SquareRoot(b*b - 4.*c)
                if (r<=-b) or (r>=b) then
                    set t = (- b - r) *.5
                endif
            endif
        elseif c<0 then
            set t = 0.
        endif
        if t<=PHYS_TIME_STEP then
            call relPos.addScaled(relVel,t)
            call relPos.normalise()
            set curColl = collision.pcreate(p1,o2)
            call curColl.addCollisionPoint(0,relPos.copyScaled(-s2.radius),relPos)
            if t<p1.firstCollTime then
                set p1.firstCollTime = t
            endif
            if t<o2.firstCollTime then
                set o2.firstCollTime = t
            endif
        else
            call relPos.destroy()
        endif
        call relVel.destroy()
    endfunction
   
    function findCollisions_ParticleBox takes particle p1, object o2, shapeBox s2 returns nothing
        //if udg_bulletTargetUnit[i]!=null then
        //  set r = GetUnitFacing(udg_bulletTargetUnit[i])*bj_DEGTORAD
        //  set a = Sin(r)
        //  set b = Cos(r)
        //else
        //  set a = -1
        //  set b = 0
        //endif
        //set r = dX
        //set dX = r*b+dY*a
        //set dY = r*a-dY*b
        //set r = dSX
        //set dSX = r*b+dSY*a
        //set dSY = r*a-dSY*b
        //set t = -10.
        //if dSX<-0.0001 or dSX>0.0001 then
        //  if dSX>0 then
        //      set r = udg_classCollisionDim1[j]*.5/dSX
        //  else
        //      set r = -udg_classCollisionDim1[j]*.5/dSX
        //  endif
        //  if -dX/dSX+r<0 or (-dX/dSX-r)*.01>udg_bulletRefreshCalc then
        //      return
        //  elseif -dX/dSX-r>t then
        //      set t = -dX/dSX-r
        //  endif
        //elseif dX>udg_classCollisionDim1[j]*.5 or dX<-udg_classCollisionDim1[j]*.5 then
        //  return
        //endif
        //if dSY<-0.0001 or dSY>0.0001 then
        //  if dSY>0 then
        //      set r = udg_classCollisionDim2[j]*.5/dSY
        //  else
        //      set r = -udg_classCollisionDim2[j]*.5/dSY
        //  endif
        //  if -dY/dSY+r<0 or (-dY/dSY-r)*.01>udg_bulletRefreshCalc then
        //      return
        //  elseif -dY/dSY-r>t then
        //      set t = -dY/dSY-r
        //  endif
        //elseif dY>udg_classCollisionDim2[j]*.5 or dY<-udg_classCollisionDim2[j]*.5 then
        //  return
        //endif
        //if dSZ<-0.0001 or dSZ>0.0001 then
        //  if dSZ>0 then
        //      set r = udg_classCollisionDim3[j]*.5/dSZ
        //  else
        //      set r = -udg_classCollisionDim3[j]*.5/dSZ
        //  endif
        //  if -dZ/dSZ+r<0 or (-dZ/dSZ-r)*.01>udg_bulletRefreshCalc then
        //      return
        //  elseif -dZ/dSZ-r>t then
        //      set t = -dZ/dSZ-r
        //  endif
        //elseif dZ>udg_classCollisionDim3[j]*.5 or dZ<-udg_classCollisionDim3[j]*.5 then
        //  return
        //endif
        //if t<=-1. then
        //  return
        //endif
    endfunction
   
    function findCollisions_ParticleCylinder takes particle p1, object o2, shapeCylinder s2 returns nothing
        //set t = -10.
        //if dSZ<-0.0001 or dSZ>0.0001 then
        //  if dSZ>0 then
        //      set r = udg_classCollisionDim3[j]*.5/dSZ
        //  else
        //      set r = -udg_classCollisionDim3[j]*.5/dSZ
        //  endif
        //  if -dZ/dSZ+r<0 or (-dZ/dSZ-r)*.01>udg_bulletRefreshCalc then
        //      return
        //  else
        //      set t = -dZ/dSZ-r
        //  endif
        //elseif dZ>udg_classCollisionDim3[j]*.5 or dZ<-udg_classCollisionDim3[j]*.5 then
        //  return
        //endif
        //set a = dSX*dSX+dSY*dSY
        //if (a>0.) then
        //  set b = (dSX*dX+dSY*dY)*2./a
        //  set c = (dX*dX+dY*dY-udg_classCollisionDim1[j]*udg_classCollisionDim1[j])/a
        //  if ((b*b - 4*c)>=0.) then
        //      set r = SquareRoot(b*b - 4.*c)
        //      if ((r<=-b) or (r>=b)) and (- b - r) *.5> t then
        //          set t = (- b - r) *.5
        //      endif
        //  endif
        //endif
        //if t<-1. then
        //  return
        //endif
    endfunction
   
    function findCollisions_ParticleObject takes particle p1, object o2 returns nothing
        local shapeListNode ln2
        if o2.c.collshape==0 then
            return
        endif
        set ln2 = o2.c.collshape.first
        loop
            exitwhen ln2==0
            if ln2.form==SHAPE_SPHERE then
                call findCollisions_ParticleSphere(p1,o2,ln2)
            elseif ln2.form==SHAPE_BOX then
                call findCollisions_ParticleBox(p1,o2,ln2)
            elseif ln2.form==SHAPE_CYLINDER then
                call findCollisions_ParticleCylinder(p1,o2,ln2)
            else
                call DebugMsg("Unknown collision shape pair")
            endif
            set ln2 = ln2.next
        endloop
    endfunction
   
    function findCollisions_ParticleTerrain takes particle p returns nothing
        local real t = 1000.
        local real terrZ = GetTerrainZ(p.pos.x,p.pos.y)
        local vector normal
        if p.vel.z<0 then
            set t = (terrZ-p.pos.z)/p.vel.z
        elseif p.pos.z<terrZ then
            set t = 0
        endif
        if t<=PHYS_TIME_STEP then
            set curColl = collision.pcreate(p,0)
            set normal = GetTerrainNormal(p.pos.x,p.pos.y)
            call normal.scale(-1)
            call curColl.addCollisionPoint(0,0,normal)
            if t<p.firstCollTime then
                set p.firstCollTime = t
            endif
        endif
    endfunction
   
    function collisionDetection_ParticleList takes particle p, list l returns nothing
        local listnode n2 = l.first
        loop
            exitwhen n2==0
            call findCollisions_ParticleObject(p,n2.content)
            set n2 = n2.next
        endloop
    endfunction
   
    function findCollisions_ObjectTerrain takes object o returns nothing
        local listpoint p = o.c.firstP
        local vector pWorld
        local vector normal
        local collision coll
        local real t
        local real terrZ
       
        loop
            exitwhen p==0
            set pWorld = p.pos.copy()
            call pWorld.rotateByQuaternion(o.orientTmp)
            call pWorld.add(o.pos)
            set terrZ = GetSurfaceZ(pWorld.x,pWorld.y)
            if o.velTmp.z<0. then
                set t = (terrZ-pWorld.z)/o.velTmp.z
            elseif pWorld.z<terrZ then
                set t = 0.
            else
                set t = 1000.
            endif
            if t<=PHYS_TIME_STEP then
                set normal = GetSurfaceNormal(pWorld.x,pWorld.y)
                call normal.scale(-1.)
                call pWorld.subtract(o.pos)
                set curColl = collision.create(o,0)
                call curColl.addCollisionPoint(pWorld,0,normal)
                if t<o.firstCollTime then
                    set o.firstCollTime = t
                endif
            else
                call pWorld.destroy()
            endif
            set p = p.next
        endloop
    endfunction
   
    function collisionDetection_ObjectList takes object o, list l returns nothing
        local listnode n2 = l.first
        loop
            exitwhen n2==0
            call findCollisions_ObjectObject(o,n2.content)
            set n2 = n2.next
        endloop
    endfunction
   
    function collisionDetection_Object takes object o returns nothing
        local integer gridX1 = R2I((o.pos.x-PHYS_BOUND_MIN_X)/physCellSizeX)
        local integer gridY1 = R2I((o.pos.y-PHYS_BOUND_MIN_Y)/physCellSizeY)
        local integer gridZ1 = R2I((o.pos.z-PHYS_BOUND_MIN_Z)/physCellSizeZ)
        local integer gridX2
        local integer gridY2
        local integer gridZ2
        local integer ix
        local integer iy
        local integer iz
        if o.cellList!=0 then
            call o.cellList.searchAndPop(o)
            set o.cellList = 0
        endif
        set gridX1 = gridX1-1
        set gridX2 = gridX1+1
        set gridY1 = gridY1-1
        set gridY2 = gridY1+1
        set gridZ1 = gridZ1-1
        set gridZ2 = gridZ1+1
        if gridX1<0 then
            set gridX1 = 0
        endif
        if gridY1<0 then
            set gridY1 = 0
        endif
        if gridZ1<0 then
            set gridZ1 = 0
        endif
        if gridX2>=PHYS_COLLISION_CELLS_X then
            set gridX2 = PHYS_COLLISION_CELLS_X-1
        endif
        if gridY2>=PHYS_COLLISION_CELLS_Y then
            set gridY2 = PHYS_COLLISION_CELLS_Y-1
        endif
        if gridZ2>=PHYS_COLLISION_CELLS_Z then
            set gridZ2 = PHYS_COLLISION_CELLS_Z-1
        endif
       
        set ix = gridX1
        loop
            exitwhen ix>gridX2
            set iy = gridY1
            loop
                exitwhen iy>gridY2
                set iz = gridZ1
                loop
                    exitwhen iz>gridZ2
                    call collisionDetection_ObjectList(o,physCellList[ix*PHYS_COLLISION_CELLS_Y*PHYS_COLLISION_CELLS_Z+iy*PHYS_COLLISION_CELLS_Z+iz])
                    set iz = iz + 1
                endloop
                   
                set iy = iy + 1
            endloop
               
            set ix = ix + 1
        endloop
        call findCollisions_ObjectTerrain(o)
    endfunction
   
    function collisionDetection_Particle takes particle p returns nothing
        local integer gridX1 = R2I((p.pos.x-PHYS_BOUND_MIN_X)/physCellSizeX)
        local integer gridY1 = R2I((p.pos.y-PHYS_BOUND_MIN_Y)/physCellSizeY)
        local integer gridZ1 = R2I((p.pos.z-PHYS_BOUND_MIN_Z)/physCellSizeZ)
        local integer gridX2 = R2I((p.pos.x+p.vel.x*PHYS_TIME_STEP-PHYS_BOUND_MIN_X)/physCellSizeX)
        local integer gridY2 = R2I((p.pos.y+p.vel.y*PHYS_TIME_STEP-PHYS_BOUND_MIN_Y)/physCellSizeY)
        local integer gridZ2 = R2I((p.pos.z+p.vel.z*PHYS_TIME_STEP-PHYS_BOUND_MIN_Z)/physCellSizeZ)
        local integer ix
        local integer iy
        local integer iz
        if gridX1<gridX2 then
            set gridX1 = gridX1-1
            set gridX2 = gridX2+1
        else
            set gridX1 = gridX2-1
            set gridX2 = gridX1+1
        endif
        if gridY1<gridY2 then
            set gridY1 = gridY1-1
            set gridY2 = gridY2+1
        else
            set gridY1 = gridY2-1
            set gridY2 = gridY1+1
        endif
        if gridZ1<gridZ2 then
            set gridZ1 = gridZ1-1
            set gridZ2 = gridZ2+1
        else
            set gridZ1 = gridZ2-1
            set gridZ2 = gridZ1+1
        endif
        if gridX1<0 then
            set gridX1 = 0
        endif
        if gridY1<0 then
            set gridY1 = 0
        endif
        if gridZ1<0 then
            set gridZ1 = 0
        endif
        if gridX2>=PHYS_COLLISION_CELLS_X then
            set gridX2 = PHYS_COLLISION_CELLS_X-1
        endif
        if gridY2>=PHYS_COLLISION_CELLS_Y then
            set gridY2 = PHYS_COLLISION_CELLS_Y-1
        endif
        if gridZ2>=PHYS_COLLISION_CELLS_Z then
            set gridZ2 = PHYS_COLLISION_CELLS_Z-1
        endif
       
        set ix = gridX1
        loop
            exitwhen ix>gridX2
            set iy = gridY1
            loop
                exitwhen iy>gridY2
                set iz = gridZ1
                loop
                    exitwhen iz>gridZ2
                    call collisionDetection_ParticleList(p,physCellList[ix*PHYS_COLLISION_CELLS_Y*PHYS_COLLISION_CELLS_Z+iy*PHYS_COLLISION_CELLS_Z+iz])
                    set iz = iz + 1
                endloop
                   
                set iy = iy + 1
            endloop
               
            set ix = ix + 1
        endloop
        call findCollisions_ParticleTerrain(p)
    endfunction
   
    function collisionDetection takes nothing returns nothing
        local listnode n1 = physParticleList.first
        loop
            exitwhen n1==0
            //call collisionDetection_Particle(n1.content)
            call findCollisions_ParticleTerrain(n1.content)
            set n1 = n1.next
        endloop
        set n1 = physObjectList.first
        loop
            exitwhen n1==0
            call collisionDetection_Object(n1.content)
            set n1 = n1.next
        endloop
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physCollisionResponse uses physObjects, physParticles, rotations, quaternions, matrices, vectors
    globals
        list collisionList
    endglobals
   
    struct collisionpoint
        vector rel1
        vector rel2
        vector normal
        vector angVelChange1
        vector angVelChange2
        vector angMomChange1
        vector angMomChange2
        real denominator
        collisionpoint next
       
        static method create takes vector rel1, vector rel2, vector normal returns collisionpoint
            // All Coordinates in World-Coordinates
            local collisionpoint p = collisionpoint.allocate()
            set p.rel1 = rel1
            set p.rel2 = rel2
            set p.normal = normal
            set p.next = 0
            set p.angMomChange1 = 0
            set p.angVelChange1 = 0
            set p.angMomChange2 = 0
            set p.angVelChange2 = 0
            return p
        endmethod
       
        method onDestroy takes nothing returns nothing
            if .rel1!=0 then
                call .rel1.destroy()
            endif
            if .rel2!=0 then
                call .rel2.destroy()
            endif
            if .angMomChange1!=0 then
                call .angMomChange1.destroy()
                call .angVelChange1.destroy()
            endif
            if .angMomChange2!=0 then
                call .angMomChange2.destroy()
                call .angVelChange2.destroy()
            endif
            call .normal.destroy()
        endmethod
    endstruct

    struct collision
        real e
        particle p1 // Either particle or object
        object o1   // Only one variable written
        object o2
        collisionpoint cp
       
        static method create takes object o1, object o2 returns collision
            local collision coll = collision.allocate()
            set coll.p1 = 0
            set coll.o1 = o1
            set coll.o2 = o2
            set coll.e = 0.
            set coll.cp = 0
           
            call collisionList.insertBehind(coll)
           
            return coll
        endmethod
       
        static method pcreate takes particle p1, object o2 returns collision
            local collision coll = collision.allocate()
            set coll.p1 = p1
            set coll.o1 = 0
            set coll.o2 = o2
            set coll.e = 1.
            set coll.cp = 0
           
            call collisionList.insertBehind(coll)
           
            return coll
        endmethod
       
        method addCollisionPoint takes vector rel1, vector rel2, vector normal returns collisionpoint
            local collisionpoint p = collisionpoint.create(rel1,rel2,normal)
            if .cp==0 then
                set .cp = p
            else
                set p.next = .cp
                set .cp = p
            endif
            return p
        endmethod
       
        method prepare takes nothing returns nothing
            local collisionpoint p = .cp
            loop
                exitwhen p==0
                set p.denominator = 0.
               
                if .o1!=0 then
                    set p.angMomChange1 = vector.createProduct(p.rel1,p.normal)
                    set p.angVelChange1 = p.angMomChange1.copy()
                    call p.angVelChange1.transformByMatrix(.o1.inertiaWorldInv)
                   
                    set p.denominator = p.denominator + 1./.o1.c.mass + scalarProduct(p.angMomChange1,p.angVelChange1)
                elseif .p1!=0 then
                    set p.denominator = p.denominator + 1./.p1.c.mass
                endif
               
                if .o2!=0 then
                    set p.angMomChange2 = vector.createProduct(p.rel2,p.normal)
                    set p.angVelChange2 = p.angMomChange2.copy()
                    call p.angVelChange2.transformByMatrix(.o2.inertiaWorldInv)
                   
                    set p.denominator = p.denominator + 1./.o2.c.mass + scalarProduct(p.angMomChange2,p.angVelChange2)
                endif
       
                set p = p.next
            endloop
        endmethod
       
        method respond takes boolean elastic returns nothing
            local vector vdiff = vector.create(0,0,0)
            local real Jmod
            local collisionpoint p = .cp
            local real hitSpeed
            local real slideSpeed
            local real elasticityFactor = 0.
            if elastic then
                set elasticityFactor = 1.
            endif
           
            loop
                exitwhen p==0
                call vdiff.setXYZ(0,0,0)
               
               
                if .o1!=0 then
                    call vdiff.add(.o1.vel)
                    call vdiff.addProduct(.o1.angVel,p.rel1)
                elseif .p1!=0 then
                    call vdiff.add(.p1.vel)
                endif
               
                if .o2!=0 then
                    call vdiff.subtract(.o2.vel)
                    call vdiff.addProduct(p.rel2,.o2.angVel)
                endif
               
                // Calculate perpendicular component
                set hitSpeed = scalarProduct(vdiff,p.normal)
               
                set Jmod = (.e*elasticityFactor+1.)*hitSpeed/p.denominator
               
                if Jmod>0 then
                    // Calculate slide vector
                    call vdiff.subtractScaled(p.normal,hitSpeed)
                    set slideSpeed = vdiff.length()+.001
                    call vdiff.scale(-Jmod/slideSpeed)
               
                    if .o1!=0 then
                        call .o1.vel.addScaled(p.normal,-Jmod/.o1.c.mass)
                        call .o1.angVel.addScaled(p.angVelChange1,-Jmod)
                        call .o1.addImpulse(p.rel1,vdiff)
                    elseif .p1!=0 then
                        call .p1.vel.addScaled(p.normal,-Jmod/.p1.c.mass)
                        call .p1.vel.add(vdiff)
                    endif
                   
                    call vdiff.scale(-1)
                   
                    if .o2!=0 then
                        call .o2.vel.addScaled(p.normal,Jmod/.o2.c.mass)
                        call .o2.angVel.addScaled(p.angVelChange2,Jmod)
                        call .o2.addImpulse(p.rel1,vdiff)
                    endif
                endif
               
                set p = p.next
            endloop
            call vdiff.destroy()
        endmethod
       
        method onDestroy takes nothing returns nothing
            local collisionpoint p = .cp
            local collisionpoint pdest
           
            loop
                exitwhen p==0
                set pdest = p
                set p = p.next
                call pdest.destroy()
            endloop
        endmethod
    endstruct
   
    function collisionResponsePrepare takes nothing returns nothing
        local collision coll
        call collisionList.toFirst()
       
        loop
            exitwhen collisionList.outOfList()
            set coll = collisionList.getContent()
            call coll.prepare()
            call collisionList.toNext()
        endloop
    endfunction
   
    function collisionResponse takes integer enumerations, boolean elastic returns nothing
        local collision coll
        local integer i = 1
        loop
            exitwhen i > enumerations
            call collisionList.toFirst()
           
            loop
                exitwhen collisionList.outOfList()
                set coll = collisionList.getContent()
                call coll.respond(elastic)
                call collisionList.toNext()
            endloop
            set i = i + 1
        endloop
    endfunction
   
    function collisionResponseDestroy takes nothing returns nothing
        local collision coll
        call collisionList.toFirst()
       
        loop
            exitwhen collisionList.outOfList()
            set coll = collisionList.getContent()
            call coll.destroy()
            call collisionList.pop()
        endloop
    endfunction
endlibrary
//TESH.scrollpos=221
//TESH.alwaysfold=0
library physMain initializer physInit uses physCollisionDetection, lists, rotations
    globals
        list physObjectList
        list physParticleList
        real physCellSizeX
        real physCellSizeY
        real physCellSizeZ
        location physTempLoc
        list array physCellList
    endglobals
   
    function getCellList takes vector pos returns list
        local real x = pos.x-PHYS_BOUND_MIN_X
        local real y = pos.y-PHYS_BOUND_MIN_Y
        local real z = pos.z-PHYS_BOUND_MIN_Z
        local integer gridX = R2I(x/physCellSizeX)
        local integer gridY = R2I(y/physCellSizeY)
        local integer gridZ = R2I(z/physCellSizeZ)
        if gridX<0 then
            set gridX=0
        elseif gridX>=PHYS_COLLISION_CELLS_X then
            set gridX=PHYS_COLLISION_CELLS_X-1
        endif
        if gridY<0 then
            set gridY=0
        elseif gridY>=PHYS_COLLISION_CELLS_Y then
            set gridY=PHYS_COLLISION_CELLS_Y-1
        endif
        if gridZ<0 then
            set gridZ=0
        elseif gridZ>=PHYS_COLLISION_CELLS_Z then
            set gridZ=PHYS_COLLISION_CELLS_Z-1
        endif
        return physCellList[gridX*PHYS_COLLISION_CELLS_Y*PHYS_COLLISION_CELLS_Z+gridY*PHYS_COLLISION_CELLS_Z+gridZ]
    endfunction
   
    function physLoop takes nothing returns nothing
        local object o
        local particle p
        local quaternion add
        local vector frictvec
        local vector accelvec
        local vector angMom = vector.create(0,0,0)
        local vector relPos
        local vector angVelRel
        local real velIntoFrict
        local real r
        local real rotAimAroundX
        local real rotAimAroundY
        local real rotAimAroundZ
        local integer pid
        local collision coll
       
        // Calculate forces and predicted positions/velocities
        call physObjectList.toFirst()
        loop
            exitwhen physObjectList.outOfList()
            set o = physObjectList.getContent()
            set pid = GetPlayerId(GetOwningPlayer(o.u))
           
            // Forces
            set o.accel.z = o.accel.z-PHYS_GRAVITY
            set angVelRel = o.angVel.copy()
            call angVelRel.rotateByQuaternionInv(o.orient)
           
           
           
            if o.c==F22 then
                // Front Wing
                set frictvec = vector.create(0,0,1)
                set relPos = vector.create(14*o.c.modelScale,0,0)
                call o.addWing(relPos,frictvec,.02)
               
                // Main Wings
                if arrowRightPressed(pid) then
                    call frictvec.rotateAroundYAxis(-15*bj_DEGTORAD)
                endif
                if arrowLeftPressed(pid) then
                    call frictvec.rotateAroundYAxis(15*bj_DEGTORAD)
                endif
                call relPos.setXYZ(-16*o.c.modelScale,-22*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.02)
                set frictvec.z=-frictvec.z
                call relPos.setXYZ(-16*o.c.modelScale,22*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.02)
               
                // Height Rudders
                call frictvec.setXYZ(0,0,1)
                if arrowUpPressed(pid) then
                    call frictvec.rotateAroundYAxis(30*bj_DEGTORAD)
                endif
                if arrowDownPressed(pid) then
                    call frictvec.rotateAroundYAxis(-30*bj_DEGTORAD)
                endif
                call relPos.setXYZ(-39*o.c.modelScale,-14*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.006)
                call relPos.setXYZ(-39*o.c.modelScale,14*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.006)
               
                // Side Rudders
                call frictvec.setXYZ(0,1,0)
                call relPos.setXYZ(-26*o.c.modelScale,11*o.c.modelScale,7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.008)
                call relPos.setXYZ(-26*o.c.modelScale,-11*o.c.modelScale,7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.008)
               
                // Frontal Friction
                call frictvec.setXYZ(1,0,0)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.001)
               
                call relPos.destroy()
                call frictvec.destroy()
            elseif o.c==ME109 then
                // Front Wing
                set frictvec = vector.create(0,0,1)
                set relPos = vector.create(0,0,0)
                call o.addWing(relPos,frictvec,.005)
               
                // Main Wings
                if arrowRightPressed(pid) then
                    call frictvec.rotateAroundYAxis(-15*bj_DEGTORAD)
                endif
                if arrowLeftPressed(pid) then
                    call frictvec.rotateAroundYAxis(15*bj_DEGTORAD)
                endif
                call relPos.setXYZ(6*o.c.modelScale,-62*o.c.modelScale,-6*o.c.modelScale)
                call o.addWing(relPos,frictvec,.005)
                set frictvec.z=-frictvec.z
                call relPos.setXYZ(6*o.c.modelScale,62*o.c.modelScale,-6*o.c.modelScale)
                call o.addWing(relPos,frictvec,.005)
               
                // Height Rudders
                call frictvec.setXYZ(0,0,1)
                if arrowUpPressed(pid) then
                    call frictvec.rotateAroundYAxis(30*bj_DEGTORAD)
                endif
                if arrowDownPressed(pid) then
                    call frictvec.rotateAroundYAxis(-30*bj_DEGTORAD)
                endif
                call relPos.setXYZ(-104*o.c.modelScale,-20*o.c.modelScale,20*o.c.modelScale)
                call o.addWing(relPos,frictvec,.002)
                call relPos.setXYZ(-104*o.c.modelScale,20*o.c.modelScale,20*o.c.modelScale)
                call o.addWing(relPos,frictvec,.002)
               
                // Side Rudders
                call frictvec.setXYZ(0,1,0)
                call relPos.setXYZ(-104*o.c.modelScale,0,20*o.c.modelScale)
                call o.addWing(relPos,frictvec,.002)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.006)
               
                // Frontal Friction
                call frictvec.setXYZ(1,0,0)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.0005)
               
                call relPos.destroy()
                call frictvec.destroy()
            elseif o.c==BOMBER then
                // Front Wing
                set frictvec = vector.create(0,0,1)
                set relPos = vector.create(60,0,0)
                call o.addWing(relPos,frictvec,.01)
                //set relPos = vector.create(-60,0,0)
                //call o.addWing(relPos,frictvec,.05)
               
                // Main Wings
                call frictvec.setXYZ(0,0,1)
                call frictvec.rotateAroundYAxis(15*bj_DEGTORAD)
                if arrowRightPressed(pid) then
                    call frictvec.rotateAroundYAxis(-5*bj_DEGTORAD)
                endif
                if arrowLeftPressed(pid) then
                    call frictvec.rotateAroundYAxis(5*bj_DEGTORAD)
                endif
                call relPos.setXYZ(0,-51*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.03)
                call frictvec.setXYZ(0,0,1)
                call frictvec.rotateAroundYAxis(15*bj_DEGTORAD)
                if arrowRightPressed(pid) then
                    call frictvec.rotateAroundYAxis(5*bj_DEGTORAD)
                endif
                if arrowLeftPressed(pid) then
                    call frictvec.rotateAroundYAxis(-5*bj_DEGTORAD)
                endif
                call relPos.setXYZ(0,51*o.c.modelScale,0)
                call o.addWing(relPos,frictvec,.03)
               
                call frictvec.setXYZ(0,0,1)
                // Height Rudders
                if arrowUpPressed(pid) then
                    call frictvec.rotateAroundYAxis(15*bj_DEGTORAD)
                endif
                if arrowDownPressed(pid) then
                    call frictvec.rotateAroundYAxis(-15*bj_DEGTORAD)
                endif
                call relPos.setXYZ(-72*o.c.modelScale,-23*o.c.modelScale,10*o.c.modelScale)
                call o.addWing(relPos,frictvec,.005)
                call relPos.setXYZ(-72*o.c.modelScale,23*o.c.modelScale,10*o.c.modelScale)
                call o.addWing(relPos,frictvec,.005)
               
                // Side Rudders
                call frictvec.setXYZ(0,1,0)
                call relPos.setXYZ(-74*o.c.modelScale,0,28*o.c.modelScale)
                call o.addWing(relPos,frictvec,.01)
                call relPos.setXYZ(60,0,0)
                call o.addWing(relPos,frictvec,.01)
                call relPos.setXYZ(-60,0,0)
                call o.addWing(relPos,frictvec,.01)
               
                // Frontal Friction
                call frictvec.setXYZ(1,0,0)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.005)
               
                call relPos.destroy()
                call frictvec.destroy()
            else
                // Front Wing
                set frictvec = vector.create(0,0,1)
                set relPos = vector.create(0,0,0)
                call o.addWing(relPos,frictvec,.005)
               
                // Main Wings
                call relPos.setXYZ(-11*o.c.modelScale,-22*o.c.modelScale,-1.7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.001)
                set frictvec.z=-frictvec.z
                call relPos.setXYZ(-11*o.c.modelScale,22*o.c.modelScale,-1.7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.001)
               
                // Height Rudders
                call relPos.setXYZ(-135*o.c.modelScale,-17*o.c.modelScale,-1.7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.001)
                set frictvec.z=-frictvec.z
                call relPos.setXYZ(-135*o.c.modelScale,17*o.c.modelScale,-1.7*o.c.modelScale)
                call o.addWing(relPos,frictvec,.001)
               
                // Side Rudders
                call frictvec.setXYZ(0,1,0)
                call relPos.setXYZ(-195*o.c.modelScale,22*o.c.modelScale,19*o.c.modelScale)
                call o.addWing(relPos,frictvec,.002)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.006)
               
                // Frontal Friction
                call frictvec.setXYZ(1,0,0)
                call relPos.setXYZ(0,0,0)
                call o.addWing(relPos,frictvec,.001)
               
                call relPos.destroy()
                call frictvec.destroy()
            endif
           
           
           
           
            set relPos = vector.create(0,0,0)
            if o.c==COBRA then
                set accelvec = vector.create(0,0,1400+o.ctrl.enginePower)
                call relPos.setXYZ(0,0,25)
                if arrowRightPressed(pid) then
                    call accelvec.rotateAroundXAxis(20*bj_DEGTORAD)
                endif
                if arrowLeftPressed(pid) then
                    call accelvec.rotateAroundXAxis(-20*bj_DEGTORAD)
                endif
                if arrowUpPressed(pid) then
                    call accelvec.rotateAroundYAxis(-20*bj_DEGTORAD)
                endif
                if arrowDownPressed(pid) then
                    call accelvec.rotateAroundYAxis(20*bj_DEGTORAD)
                endif
            elseif o.c==ME109 then
                set accelvec = vector.create(100+o.ctrl.enginePower,0,0)
            elseif o.c==BOMBER then
                set accelvec = vector.create(2200+o.ctrl.enginePower,0,0)
            else
                call relPos.setXYZ(-33*o.c.modelScale,0,-2*o.c.modelScale)
                set accelvec = vector.create(2200+o.ctrl.enginePower,0,0)
                if arrowUpPressed(pid) then
                    call accelvec.rotateAroundYAxis(-20*bj_DEGTORAD)
                endif
                if arrowDownPressed(pid) then
                    call accelvec.rotateAroundYAxis(20*bj_DEGTORAD)
                endif
            endif
            call accelvec.rotateByQuaternion(o.orient)
            call relPos.rotateByQuaternion(o.orient)
            call o.addForce(relPos,accelvec)
            call relPos.destroy()
            call accelvec.destroy()
           
            if o.c==COBRA then
                set accelvec = vector.create(0,0,0)
                call accelvec.addXYZ(0,0,o.ctrl.turnPower)
                call accelvec.rotateByQuaternion(o.orient)
                call o.angAccel.add(accelvec)
                call accelvec.destroy()
            endif
           
           
           
           
           
            // Temp
            call o.velTmp.setVec(o.vel)
            call o.velTmp.addScaled(o.accel,PHYS_TIME_STEP)
           
            call o.angVelTmp.setVec(o.angVel)
            call o.angVelTmp.addScaled(o.angAccel,PHYS_TIME_STEP)
           
            call o.posTmp.setVec(o.pos)
            call o.posTmp.addScaled(o.velTmp,PHYS_TIME_STEP)
           
            call o.orientTmp.setQuat(o.orient)
            call rotateQuaternionByVectorScaled(o.orientTmp,o.angVelTmp,PHYS_TIME_STEP)
           
            set o.firstCollTime = 1000.
            call angVelRel.destroy()
            call physObjectList.toNext()
        endloop
       
        call collisionDetection() // Also resets cell lists
        call collisionResponsePrepare()
        call collisionResponse(2,true)
       
        // Add acceleration to speed, reset temporary acceleration variables
        call physObjectList.toFirst()
        loop
            exitwhen physObjectList.outOfList()
            set o = physObjectList.getContent()
           
            call o.vel.addScaled(o.accel,PHYS_TIME_STEP)
            call o.angVel.addScaled(o.angAccel,PHYS_TIME_STEP)
           
            call o.accel.setXYZ(0,0,0)
            call o.angAccel.setXYZ(0,0,0)
            call physObjectList.toNext()
        endloop
       
        call collisionResponse(3,false)
        call collisionResponseDestroy()
       
        // Calculate new positions
        call physObjectList.toFirst()
        loop
            exitwhen physObjectList.outOfList()
            set o = physObjectList.getContent()
           
            // Translate
            call angMom.setTransformedByMatrix(o.angVel,o.inertiaWorldInv)
            if o.firstCollTime<1. and o.firstCollTime>0. then
                call o.pos.addScaled(o.velTmp,o.firstCollTime)
            else
                set o.firstCollTime = 0.
            endif
            call o.pos.addScaled(o.vel,PHYS_TIME_STEP-o.firstCollTime)
            call rotateQuaternionByVectorScaled(o.orient,o.angVel,PHYS_TIME_STEP-o.firstCollTime)
            call o.orient.normalise()
           
            call o.inertiaWorld.destroy()
            call o.inertiaWorldInv.destroy()
           
           
            set o.inertiaWorld = o.c.inertia.copy()
            call rotateMatrixByQuaternion(o.inertiaWorld,o.orient)
            set o.inertiaWorldInv = o.inertiaWorld.copyInverted()
           
            call o.angVel.setTransformedByMatrix(angMom,o.inertiaWorld)
           
            if o.pos.x>PHYS_BOUND_MAX_X then
                set o.pos.x = PHYS_BOUND_MIN_X+1.
            endif
            if o.pos.x<PHYS_BOUND_MIN_X then
                set o.pos.x = PHYS_BOUND_MAX_X-1.
            endif
            if o.pos.y>PHYS_BOUND_MAX_Y then
                set o.pos.y = PHYS_BOUND_MIN_Y+1.
            endif
            if o.pos.y<PHYS_BOUND_MIN_Y then
                set o.pos.y = PHYS_BOUND_MAX_Y-1.
            endif
            if o.pos.z>PHYS_BOUND_MAX_Z then
                set o.pos.z = PHYS_BOUND_MAX_Z
            endif
           
            set o.cellList = getCellList(o.pos)
            call o.cellList.insertBehind(o)
           
            call o.updateAnimation()
           
            // Fire
            if o.ctrl.weaponShots2>0 then
                set o.ctrl.weaponShots2 = o.ctrl.weaponShots2 - 1
                set p = particle.create(Player(0),BULLET,o.pos.x,o.pos.y,o.pos.z,o.vel.x,o.vel.y,o.vel.z)
                set p.sourceObj = o
                set accelvec = vector.create(0,0,0)
                call accelvec.rotateByQuaternion(o.orient)
                call p.vel.add(accelvec)
                call accelvec.destroy()
            endif
           
            call physObjectList.toNext()
        endloop
        call angMom.destroy()
       
        call physParticleList.toFirst()
        loop
            exitwhen physParticleList.outOfList()
            set p = physParticleList.getContent()
            set p.vel.z = p.vel.z - PHYS_GRAVITY*PHYS_TIME_STEP
            call p.pos.addScaled(p.vel,PHYS_TIME_STEP)
            set p.age = p.age + PHYS_TIME_STEP
           
            if p.targetObj!=0 then
                set relPos = vector.createDifference(p.targetObj.pos,p.pos)
                call relPos.normalise()
                call p.vel.addScaled(relPos,1500*PHYS_TIME_STEP)
                call relPos.destroy()
            endif
            if p.pos.x>PHYS_BOUND_MAX_X then
                set p.pos.x = PHYS_BOUND_MIN_X
            endif
            if p.pos.x<PHYS_BOUND_MIN_X then
                set p.pos.x = PHYS_BOUND_MAX_X
            endif
            if p.pos.y>PHYS_BOUND_MAX_Y then
                set p.pos.y = PHYS_BOUND_MIN_Y
            endif
            if p.pos.y<PHYS_BOUND_MIN_Y then
                set p.pos.y = PHYS_BOUND_MAX_Y
            endif
            if p.age>20 then
                call p.destroy()
                call physParticleList.pop()
            else
                call p.updateAnimation()
                call physParticleList.toNext()
            endif
        endloop
        call SelectUnit(playerController[GetPlayerId(GetLocalPlayer())],true)
    endfunction

    function physInit takes nothing returns nothing
        local integer i = 0
        local integer imax = PHYS_COLLISION_CELLS_Y*PHYS_COLLISION_CELLS_Y*PHYS_COLLISION_CELLS_Z
        set physObjectList = list.create()
        set physParticleList = list.create()
        set physTempLoc = Location(0,0)
        set collisionList = list.create()
        loop
            exitwhen i>=imax
            set physCellList[i] = list.create()
            set i = i + 1
        endloop
        set physCellSizeX = (PHYS_BOUND_MAX_X-PHYS_BOUND_MIN_X)/PHYS_COLLISION_CELLS_X
        set physCellSizeY = (PHYS_BOUND_MAX_Y-PHYS_BOUND_MIN_Y)/PHYS_COLLISION_CELLS_Y
        set physCellSizeZ = (PHYS_BOUND_MAX_Z-PHYS_BOUND_MIN_Z)/PHYS_COLLISION_CELLS_Z
       
        call TimerStart(CreateTimer(),PHYS_TIME_STEP,true,function physLoop )
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library physProperties
    globals
        constant real    PHYS_TIME_STEP              =     .04
        constant real    PHYS_GRAVITY                =     50.
       
        constant integer PHYS_COLLISION_CELLS_X      =     32
        constant integer PHYS_COLLISION_CELLS_Y      =     32
        constant integer PHYS_COLLISION_CELLS_Z      =      1
       
        constant real    PHYS_BOUND_MIN_X            = -10000.
        constant real    PHYS_BOUND_MAX_X            =  10000.
        constant real    PHYS_BOUND_MIN_Y            = -10000.
        constant real    PHYS_BOUND_MAX_Y            =  10000.
        constant real    PHYS_BOUND_MIN_Z            =   -100.
        constant real    PHYS_BOUND_MAX_Z            =   4000.
       
        constant boolean PHYS_BOUND_REPEAT           =   true
    endglobals
endlibrary
//TESH.scrollpos=11
//TESH.alwaysfold=0
library physControllers initializer physControllers_Init
    globals
        unit array playerController
        object array playerControlledObj
    endglobals

    function ControlReaction takes nothing returns nothing
        local integer abil = GetSpellAbilityId()
        local object obj = playerControlledObj[GetPlayerId(GetOwningPlayer(GetTriggerUnit()))]
        if abil=='A000' then
            // Accelerate Main Engine
            set obj.ctrl.enginePower = obj.ctrl.enginePower + 400
        elseif abil=='A001' then
            // Decelerate Main Engine
            set obj.ctrl.enginePower = obj.ctrl.enginePower - 400
        elseif abil=='A002' then
            // Turn Right
            set obj.ctrl.turnPower = obj.ctrl.turnPower + .5
        elseif abil=='A003' then
            // Turn Left
            set obj.ctrl.turnPower = obj.ctrl.turnPower - .5
        elseif abil=='A004' then
            set obj.ctrl.weaponShots1 = 0
            set obj.ctrl.weaponShots2 = 5
        endif
    endfunction
   
    function physControllers_Init takes nothing returns nothing
        local integer i = 0
        local trigger t = CreateTrigger()
        loop
            exitwhen i>11
            set playerController[i] = CreateUnit(Player(i),'h002',mapMaxX,mapMaxY,0)
            set i = i + 1
        endloop    
        call SelectUnit(playerController[GetPlayerId(GetLocalPlayer())],true)
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddAction( t, function ControlReaction )
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_ESC_Actions takes nothing returns nothing
    local object o = playerControlledObj[GetPlayerId(GetTriggerPlayer())]
    if o!=0 then
        call DestroyEffect(o.e)
        call RemoveUnit(o.u)
        if o.c==F22 then
            set o.c=ME109
        elseif o.c==ME109 then
            set o.c=BOMBER
        elseif o.c==BOMBER then
            set o.c=COBRA
        else
            set o.c=F22
        endif
       
        set o.u = CreateUnit(GetTriggerPlayer(),o.c.unitType,o.pos.x,o.pos.y,0.)
        set o.e = AddSpecialEffectTarget(o.c.modelName,o.u,"origin")
        call SetUnitAnimation(o.u,"stand")
        call SetUnitPathing(o.u,false)
        call SetUnitTimeScale(o.u,1000.)
        call SetUnitScale(o.u,o.c.modelScale,o.c.modelScale,o.c.modelScale)
        call UnitAddAbility(o.u,'Amrf')
        call o.updateAnimation()
        call UnitRemoveAbility(o.u,'Amrf')
           
        call o.inertiaWorld.destroy()
        call o.inertiaWorldInv.destroy()
           
           
        set o.inertiaWorld = o.c.inertia.copy()
        call rotateMatrixByQuaternion(o.inertiaWorld,o.orient)
        set o.inertiaWorldInv = o.inertiaWorld.copyInverted()
    endif
endfunction

//===========================================================================
function InitTrig_ESC takes nothing returns nothing
    local integer i = 0
    set gg_trg_ESC = CreateTrigger(  )
    loop
        exitwhen i>11
        call TriggerRegisterPlayerEvent(gg_trg_ESC, Player(i), EVENT_PLAYER_END_CINEMATIC)
        set i = i + 1
    endloop
    call TriggerAddAction( gg_trg_ESC, function Trig_ESC_Actions )
endfunction

 
Vorgegebene Nahkampf-Spielinitialisierung für alle Spieler
//TESH.scrollpos=35
//TESH.alwaysfold=0
globals
    class F22
    class ME109
    class BOMBER
    class COBRA
    pclass BULLET
    pclass MISSILE
endglobals


function Trig_Nahkampf_Initialisierung_Actions takes nothing returns nothing
    local integer i = 0
    set F22 = class.create()
    set F22.mass = 20.
    set F22.inertia = matrix.create(20000,0,0,  0,40000,0,  0,0,40000)
    set F22.inertiaInv = F22.inertia.copyInverted()
    set F22.modelName = "war3mapImported\\FA-22 Raptor.mdx"
    set F22.unitType = 'h000'
    call F22.addCollisionPoint(-18,33,0)
    call F22.addCollisionPoint(-18,-33,0)
    call F22.addCollisionPoint(-43,21,0)
    call F22.addCollisionPoint(-43,-21,0)
    call F22.addCollisionPoint(50,0,-1)
    call F22.addCollisionPoint(-27,14,14)
    call F22.addCollisionPoint(-27,-14,14)
    call F22.addCollisionPoint(-27,6.5,-5)
    call F22.addCollisionPoint(-27,-6.5,-5)
    call F22.addCollisionPoint(18,9,-5)
    call F22.addCollisionPoint(18,-9,-5)
    call F22.addCollisionPoint(27,0,6.3)
   
    set F22.collshape = shape.create()
    call F22.collshape.addNewSphere(vector.create(0,0,0),30)
   
    set BOMBER = class.create()
    set BOMBER.mass = 50.
    set BOMBER.inertia = matrix.create(160000,0,0,  0,320000,0,  0,0,320000)
    set BOMBER.inertiaInv = BOMBER.inertia.copyInverted()
    set BOMBER.modelName = "war3mapImported\\BadgeBomber.mdx"
    set BOMBER.unitType = 'h000'
    call BOMBER.addCollisionPoint(80,0,0)
    call BOMBER.addCollisionPoint(-81,0,11)
    call BOMBER.addCollisionPoint(-84,0,38)
    call BOMBER.addCollisionPoint(-77,-37,11)
    call BOMBER.addCollisionPoint(-77,37,11)
    call BOMBER.addCollisionPoint(-13,-90,-1)
    call BOMBER.addCollisionPoint(-13,90,-1)
    call BOMBER.addCollisionPoint(64,-6.5,-6.5)
    call BOMBER.addCollisionPoint(64,6.5,-6.5)
    call BOMBER.addCollisionPoint(-6,-6.5,-6.5)
    call BOMBER.addCollisionPoint(-6,6.5,-6.5)
    call BOMBER.addCollisionPoint(-60,0,0)
    call BOMBER.addCollisionPoint(55,0,13)
   
    set BOMBER.collshape = shape.create()
    call BOMBER.collshape.addNewSphere(vector.create(0,0,0),300)
    call BOMBER.scaleVisual(1.2)
   
    set ME109 = class.create()
    set ME109.mass = 2.
    set ME109.inertia = matrix.create(500,0,0,  0,1000,0,  0,0,1000)
    set ME109.inertiaInv = ME109.inertia.copyInverted()
    set ME109.modelName = "war3mapImported\\ME109.mdx"
    set ME109.unitType = 'h000'
    call ME109.addCollisionPoint(-17,125,-2)
    call ME109.addCollisionPoint(-17,-125,-2)
    call ME109.addCollisionPoint(35,54,-6)
    call ME109.addCollisionPoint(35,-54,-6)
    call ME109.addCollisionPoint(79,0,0)
    call ME109.addCollisionPoint(33,-8,-14)
    call ME109.addCollisionPoint(33,8,-14)
    call ME109.addCollisionPoint(-23,-8,-14)
    call ME109.addCollisionPoint(-23,8,-14)
    call ME109.addCollisionPoint(-122,0,10)
    call ME109.addCollisionPoint(-103,0,37)
    call ME109.addCollisionPoint(-103,-38,20)
    call ME109.addCollisionPoint(-103,38,20)
    call ME109.addCollisionPoint(13,0,19)
   
    set ME109.collshape = shape.create()
    call ME109.collshape.addNewSphere(vector.create(0,0,0),100)
    call ME109.scaleVisual(5*9.87/250)
   
    //set ME109 = BOMBER
   
    set COBRA = class.create()
    set COBRA.mass = 20.
    set COBRA.inertia = matrix.create(10000,0,0,  0,20000,0,  0,0,20000)
    set COBRA.inertiaInv = COBRA.inertia.copyInverted()
    set COBRA.modelName = "war3mapImported\\Cobra.mdx"
    set COBRA.unitType = 'h000'
    call COBRA.addCollisionPoint(-200,0,-7)
    call COBRA.addCollisionPoint(-207,0,43)
    call COBRA.addCollisionPoint(-75,-9,-21)
    call COBRA.addCollisionPoint(-75,9,-21)
    call COBRA.addCollisionPoint(57,-9,-21)
    call COBRA.addCollisionPoint(57,9,-21)
    call COBRA.addCollisionPoint(84,-4,4)
    call COBRA.addCollisionPoint(84,4,4)
    call COBRA.addCollisionPoint(-4,-4,31)
    call COBRA.addCollisionPoint(-4,4,31)
    call COBRA.addCollisionPoint(38,-22,-33)
    call COBRA.addCollisionPoint(38,22,-33)
    call COBRA.addCollisionPoint(-45,-22,-33)
    call COBRA.addCollisionPoint(-45,22,-33)
   
    set COBRA.collshape = shape.create()
    call COBRA.collshape.addNewSphere(vector.create(0,0,0),100)
    call COBRA.scaleVisual(5*14./284.)
   
    set BULLET = pclass.create()
    set BULLET.modelName = "war3mapImported\\Bullet.mdx"
    set BULLET.mass = 0.01
    set BULLET.scale = 0.1
   
    set MISSILE = pclass.create()
    set MISSILE.modelName = "Abilities\\Weapons\\RocketMissile\\RocketMissile.mdl"
    set MISSILE.mass = 0.01
    set MISSILE.scale = 0.1
   
    call SetSkyModel( "Environment\\Sky\\FoggedSky\\FoggedSky.mdl" )
    call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12)
    call SetTimeOfDayScale( 0 )
   
    call EnableDragSelect(false, false)
    call EnablePreSelect(false, false)
    call EnableSelect(false, false)
    call EnableOcclusion(false)
   
    loop
        exitwhen i>11
        if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
            set playerControlledObj[i] = object.create(Player(i),F22,0,i*512,512,0,0,0)
            call CreateFogModifierRectBJ( true, Player(i), FOG_OF_WAR_VISIBLE, GetWorldBounds() )
        endif
        set i = i + 1
    endloop
   
    call SetCloudCount(20)
endfunction

//===========================================================================
function InitTrig_Nahkampf_Initialisierung takes nothing returns nothing
    set gg_trg_Nahkampf_Initialisierung = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Nahkampf_Initialisierung, function Trig_Nahkampf_Initialisierung_Actions )
endfunction