//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Initialization
globals
private constant real TEXT_X = -2400.0
private constant real TEXT_Y = 4300.0
private constant real TEXT_LETTER_OFFSET = 200.0
private constant real TEXT_SEGMENT_OFFSET = 150.0
endglobals
private function Actions takes nothing returns nothing
local unit u
local real theta = 0.0
local integer i = 1
local entity e
local real z = 0.0
// --
// The letter 'S'
loop
exitwhen i == 3
set e = entity.create(ball.create(), Player(0), TEXT_X - TEXT_SEGMENT_OFFSET*i, TEXT_Y - 2.0*TEXT_SEGMENT_OFFSET*bj_PI, 1200.0, 0.0)
call e.addGravity(1.0)
if z == 0.0 then
set z = e.zP
else
set e.zP = z
endif
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*i, TEXT_Y, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set i = i + 1
endloop
loop
exitwhen theta >= 2.0*bj_PI
set e = entity.create(ball.create(), Player(0), TEXT_X - TEXT_SEGMENT_OFFSET*2.25*Sin(theta), TEXT_Y - TEXT_SEGMENT_OFFSET*theta, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set theta = theta + bj_PI/6.0
endloop
// --
// --
// The first letter 'E'
set i = 0
loop
exitwhen i == 7
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET, TEXT_Y - TEXT_SEGMENT_OFFSET*i, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set i = i + 1
endloop
set i = 1
loop
exitwhen i == 4
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET + TEXT_SEGMENT_OFFSET*i, TEXT_Y, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET + TEXT_SEGMENT_OFFSET*i, TEXT_Y - 100.0*bj_PI, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET + TEXT_SEGMENT_OFFSET*i, TEXT_Y - 2.0*TEXT_SEGMENT_OFFSET*bj_PI, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set i = i + 1
endloop
// --
// --
// The second letter 'E'
set i = 0
loop
exitwhen i == 7
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET*2.0 + TEXT_SEGMENT_OFFSET*4.0, TEXT_Y - TEXT_SEGMENT_OFFSET*i, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set i = i + 1
endloop
set i = 1
loop
exitwhen i == 4
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET*2.0 + TEXT_SEGMENT_OFFSET*4.0 + TEXT_SEGMENT_OFFSET*i, TEXT_Y, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET*2.0 + TEXT_SEGMENT_OFFSET*4.0 + TEXT_SEGMENT_OFFSET*i, TEXT_Y - TEXT_SEGMENT_OFFSET*bj_PI, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set e = entity.create(ball.create(), Player(0), TEXT_X + TEXT_SEGMENT_OFFSET*2.25 + TEXT_LETTER_OFFSET*2.0 + TEXT_SEGMENT_OFFSET*4.0 + TEXT_SEGMENT_OFFSET*i, TEXT_Y - 2.0*TEXT_SEGMENT_OFFSET*bj_PI, 100.0, 0.0)
call e.addGravity(1.0)
set e.zP = z
set i = i + 1
endloop
// --
// --
// Below is some stuff that has nothing to do with SEE
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12.00)
call SetTimeOfDayScale(0.0)
call FogEnable(false)
call FogMaskEnable(false)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 290.0, 0.0)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 4000.0, 0.0)
// --
endfunction
public function InitTrig takes nothing returns nothing
set gg_trg_Initialization = CreateTrigger()
call TriggerAddAction( gg_trg_Initialization, function Actions )
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SEEcore requires SEEentityData
globals
private constant integer DUMMY_ID = 'e000'
public constant real FPS = 40.0
private constant real GRAVITY_ACCELERATION = -3000.0
private constant real AIR_DENSITY = 0.001
private constant integer REST_TALLY = 30
private constant real REST_STATE_SPD = 60.0
private constant real COLL_ALGO_SPD = 300.0
private constant real THRESHOLD_COLL_SPD = 70.0
private constant real THRESHOLD_SLIDE_SPD = 100.0
private constant real SAMPLE_RADIUS = 32.0
private constant real MAX_TERRAIN_HEIGHT = 1230.500
endglobals
globals
public real minX
public real maxX
public real minY
public real maxY
private entity enumEntity
private boolexpr filter
private real RADIUS = 0.0
private real array tempReal
private group G = CreateGroup()
private integer I
endglobals
private function enum takes nothing returns boolean
local entity e = entity(GetUnitUserData(GetFilterUnit()))
return e.data.collideable and not IsUnitInGroup(e.dummy, enumEntity.collGroup) and ( (enumEntity.zP + enumEntity.data.radius + enumEntity.zV >= e.zP - e.data.radius + e.zV) or (enumEntity.zP - enumEntity.data.radius + enumEntity.zV >= e.zP + e.data.radius + e.zV) )
endfunction
private function main takes nothing returns nothing
local integer i = 0 // Iterator
local entity e // Dummy entity used in the main loop.
local entity c // Dummy entity used in collisions.
local real r
local real r2
local real s = 0.0
local real px
local real py
local real pz = 0.0
local real vx = 0.0
local real vy = 0.0
local real vz = 0.0
loop
exitwhen i >= entity.COUNT
set e = entity.ENTITY[i]
// --
// The following only exectutes if timed life is activated
if e.timedLife > -1.0 then
set e.timedLife = e.timedLife - 1.0/FPS
if e.timedLife <= 0.0 then
call e.data.onDeath(e)
call e.remove(e.wantRemoveUnit)
endif
endif
// --
call e.data.onLoop(e)
if not e.data.affected then
set px = GetUnitX(e.dummy)
set py = GetUnitY(e.dummy)
call MoveLocation(entity.tloc, px, py)
set pz = GetUnitFlyHeight(e.dummy) + GetLocationZ(entity.tloc) - e.data.oZ
if px != e.xP or py != e.yP or pz != e.zP then
set e.restCount = 0
set e.xV = px - e.xP
set e.yV = py - e.yP
set e.zV = pz - e.zP
set e.xP = px
set e.yP = py
set e.zP = pz
endif
endif
if e.restCount < REST_TALLY then
set s = SquareRoot(e.xV*e.xV + e.yV*e.yV + e.zV*e.zV)
if s <= REST_STATE_SPD/FPS then
set e.restCount = e.restCount + 1
if e.restCount >= REST_TALLY then
set e.xV = 0.0
set e.yV = 0.0
set e.zV = 0.0
set s = 0.0
call e.data.onRest(e)
endif
else
set e.restCount = 0
endif
if e.data.affected then
// --
// The following involves calculations for spring constraints
if e.constrained != 0 then
set vx = e.xP - e.constrained.xP
set vy = e.yP - e.constrained.yP
set vz = e.zP - e.constrained.zP
set r = SquareRoot(vx*vx + vy*vy + vz*vz) + 0.01
set r = -e.constraintConstant*(1 - e.constraintLength/r)
set vx = (vx*r - (e.xV - e.constrained.xV)*e.constraintFriction)/(e.data.mass*FPS*FPS)
set vy = (vy*r - (e.yV - e.constrained.yV)*e.constraintFriction)/(e.data.mass*FPS*FPS)
set vz = (vz*r - (e.zV - e.constrained.zV)*e.constraintFriction)/(e.data.mass*FPS*FPS)
set e.restCount = 0
set e.xV = e.xV + 1000.0*vx
set e.yV = e.yV + 1000.0*vy
set e.zV = e.zV + 1000.0*vz
if e.constrained.data.affected then
set e.constrained.restCount = 0
set e.constrained.xV = e.constrained.xV - 1000.0*vx
set e.constrained.yV = e.constrained.yV - 1000.0*vy
set e.constrained.zV = e.constrained.zV - 1000.0*vz
endif
endif
// --
// --
// The following involves air drag calculations and position/velocity updating
set px = 1.0 - AIR_DENSITY*s*e.data.radius*e.data.radius*e.data.drag/(e.data.mass + 0.001)
set e.xV = e.xV*px + e.xA
set e.yV = e.yV*px + e.yA
set e.zV = e.zV*px + e.zA
set e.xP = e.xP + e.xV
set e.yP = e.yP + e.yV
set e.zP = e.zP + e.zV
// --
// --
// The following is a check for if the entity is within map bounds
if e.xP < minX or e.yP < minY or e.xP > maxX or e.yP > maxY then
call e.data.onExit(e)
endif
// --
if e.zP <= MAX_TERRAIN_HEIGHT then
// --
// The following gets the terrain normal <vx, vy, SAMPLE_RADIUS>
// and checks to see if the concerned entity sphere is colliding
// with the terrain plane.
call MoveLocation(entity.tloc, e.xP - SAMPLE_RADIUS, e.yP)
call MoveLocation(entity.tloc2, e.xP + SAMPLE_RADIUS, e.yP)
set vx = GetLocationZ(entity.tloc) - GetLocationZ(entity.tloc2)
call MoveLocation(entity.tloc, e.xP, e.yP - SAMPLE_RADIUS)
call MoveLocation(entity.tloc2, e.xP, e.yP + SAMPLE_RADIUS)
set vy = GetLocationZ(entity.tloc) - GetLocationZ(entity.tloc2)
set vz = vx*vx + vy*vy + SAMPLE_RADIUS*SAMPLE_RADIUS
call MoveLocation(entity.tloc, e.xP, e.yP)
set r = e.data.radius/SquareRoot(vz) - (e.zP - GetLocationZ(entity.tloc))*SAMPLE_RADIUS/vz
// --
if r >= 0.0 then
// --
// Moves the entity to the touching point between the
// entity and the terrain plane.
set e.xP = e.xP + r*vx
set e.yP = e.yP + r*vy
set e.zP = e.zP + r*SAMPLE_RADIUS
// --
// --
// Gets the projection of the entity's velocity onto the
// terrain normal: <px, py, pz>
set vz = (e.xV*vx + e.yV*vy + e.zV*SAMPLE_RADIUS)/vz
set px = vx*vz
set py = vy*vz
set pz = SAMPLE_RADIUS*vz
// --
if px*px + py*py + pz*pz <= THRESHOLD_SLIDE_SPD*THRESHOLD_SLIDE_SPD/(FPS*FPS) or not(e.data.bounceable) then
// --
// Calculates the entity's new velocity after impact. This script
// scales the velocity component parallel to the terrain plane by
// taking in account friction, and completely removes the
// perpendicular component. This gives a sliding effect.
set e.xV = (e.xV - px)*(1.0 - e.data.friction)
set e.yV = (e.yV - py)*(1.0 - e.data.friction)
set e.zV = (e.zV - pz)*(1.0 - e.data.friction)
// --
else
// --
// The parallel component functions as described above. However,
// the perpendicular component is added here, and scaled taking
// in account restitution. Bouncing is determined by resitution.
set e.xV = (e.xV - px)*(1.0 - e.data.friction) - px*e.data.restitution
set e.yV = (e.yV - py)*(1.0 - e.data.friction) - py*e.data.restitution
set e.zV = (e.zV - pz)*(1.0 - e.data.friction) - pz*e.data.restitution
// --
endif
set s = SquareRoot(e.xV*e.xV + e.yV*e.yV + e.zV*e.zV)
call e.data.onGround(e)
endif
endif
// --
// Rendering the entity based on position and offset
call MoveLocation(entity.tloc, e.xP, e.yP)
set r = GetLocationZ(entity.tloc)
call SetUnitX(e.dummy, e.xP + e.data.oX)
call SetUnitY(e.dummy, e.yP + e.data.oY)
call SetUnitFlyHeight(e.dummy, e.zP - r + e.data.oZ, 0.0)
// --
endif
// --
// Below is the collision detection and resolution
if e.data.collideable then
set enumEntity = e
// --
// Enum'ing through the entities in a cylindrical range first;
// speed*2.0 is an added term due to a logical constraint (thanks to grim001);
// RADIUS is the largest current radius of all entities;
// e.collGroup contains entities with which collisions have already been
// dealt. Thus, calculating a second collision would be redundant.
// Entities are properly sifted considering this.
call GroupEnumUnitsInRange(entity.COLL, e.xP, e.yP, e.data.radius + RADIUS + s*2.0, filter)
call GroupRemoveUnit(entity.COLL, e.dummy)
call GroupClear(e.collGroup)
// --
loop
set c = entity(GetUnitUserData(FirstOfGroup(entity.COLL)))
exitwhen c.dummy == null
// --
// Storing of various relative components and dot products
set vx = e.xV - c.xV
set vy = e.yV - c.yV
set vz = e.zV - c.zV
set r2 = vx*vx + vy*vy + vz*vz
// --
// The following condition evaluates the relative velocity with respect to the collision separator
if r2 >= COLL_ALGO_SPD*COLL_ALGO_SPD/(FPS*FPS) then
// If the relative velocity is larger than the collision separator, advanced collision detection is executed
// The following collision detection was established using the equation: |deltaP + t*deltaV| <= r1 + r2
// Note that at this point, variable names most likely have nothing to do with what they hold
set px = e.xP - c.xP
set py = e.yP - c.yP
set pz = e.zP - c.zP
set s = px*vx + py*vy + pz*vz
set r = px*px + py*py + pz*pz
set vx = e.data.radius + c.data.radius
set vy = s*s - r2*(r - vx*vx)
if vy >= 0.0 then
set vy = (-s - SquareRoot(vy))/r2
if vy > 0.0 and vy <= 1.0 then
// --
// The following prepares collision resolution while moving entities to their touching position
set c.xP = c.xP + c.xV*vy
set c.yP = c.yP + c.yV*vy
set c.zP = c.zP + c.zV*vy
set e.xP = e.xP + e.xV*vy
set e.yP = e.yP + e.yV*vy
set e.zP = e.zP + e.zV*vy
// --
// --
// The following is code for collision resolution
set s = s*(1.0 + c.data.restitution*e.data.restitution)/(r*(c.data.mass + e.data.mass) + 0.001)
if c.data.bounceable then
set vx = s*e.data.mass
set c.xV = c.xV + px*vx
set c.yV = c.yV + py*vx
set c.zV = c.zV + pz*vx
if c.xV*c.xV + c.yV*c.yV + c.zV*c.zV <= THRESHOLD_COLL_SPD*THRESHOLD_COLL_SPD/(FPS*FPS) then
// If the entity moves slower than the threshold velocity, set the velocity to 0
set c.xV = 0.0
set c.yV = 0.0
set c.zV = 0.0
else
// If the entity begins moving again, its rest has been disturbed
set c.restCount = 0
endif
endif
if e.data.bounceable then
set vx = s*c.data.mass
set e.xV = e.xV - px*vx
set e.yV = e.yV - py*vx
set e.zV = e.zV - pz*vx
if e.xV*e.xV + e.yV*e.yV + e.zV*e.zV <= THRESHOLD_COLL_SPD*THRESHOLD_COLL_SPD/(FPS*FPS) then
set e.xV = 0.0
set e.yV = 0.0
set e.zV = 0.0
else
set e.restCount = 0
endif
endif
// --
call e.data.onCollision(e, c)
call c.data.onCollision(c, e)
elseif vy < 0.0 and r < vx*vx then
set vz = (vx/(SquareRoot(r) + 0.001) - 1.0)/(c.data.mass + e.data.mass + 0.001)
set vy = e.data.mass*vz
if r*vy*vy > 0.01/(FPS*FPS) then
set c.xP = c.xP - vy*px
set c.yP = c.yP - vy*py
set c.zP = c.zP - vy*pz
set c.restCount = 0
endif
set vy = c.data.mass*vz
if r*vy*vy > 0.01/(FPS*FPS) then
set e.xP = e.xP + vy*px
set e.yP = e.yP + vy*py
set e.zP = e.zP + vy*pz
set e.restCount = 0
endif
endif
endif
else
set px = e.xP - c.xP
set py = e.yP - c.yP
set pz = e.zP - c.zP
set r = px*px + py*py + pz*pz
set r2 = e.data.radius + c.data.radius
if r < r2*r2 then
// If the relative velocity is smaller than the collision separator;
// and if two entities are currently colliding, simple collision script is executed
// --
// The following code moves entities apart from eachother based on their mass and the distance between them
set s = (px*vx + py*vy + pz*vz)*(1.0 + c.data.restitution*e.data.restitution)/(r*(c.data.mass + e.data.mass) + 0.001)
set vz = (r2/(SquareRoot(r) + 0.001) - 1.0)/(c.data.mass + e.data.mass + 0.001)
set vy = c.data.mass*vz
if r*vy*vy > 0.01/(FPS*FPS) then
set e.xP = e.xP + vy*px
set e.yP = e.yP + vy*py
set e.zP = e.zP + vy*pz
set e.restCount = 0
endif
set vy = e.data.mass*vz
if r*vy*vy > 0.01/(FPS*FPS) then
set c.xP = c.xP - vy*px
set c.yP = c.yP - vy*py
set c.zP = c.zP - vy*pz
set c.restCount = 0
endif
// --
if c.data.bounceable then
set vx = s*e.data.mass
set c.xV = c.xV + px*vx
set c.yV = c.yV + py*vx
set c.zV = c.zV + pz*vx
if c.xV*c.xV + c.yV*c.yV + c.zV*c.zV <= THRESHOLD_COLL_SPD*THRESHOLD_COLL_SPD/(FPS*FPS) then
set c.xV = 0.0
set c.yV = 0.0
set c.zV = 0.0
else
set c.restCount = 0
endif
endif
if e.data.bounceable then
set vx = s*c.data.mass
set e.xV = e.xV - px*vx
set e.yV = e.yV - py*vx
set e.zV = e.zV - pz*vx
if e.xV*e.xV + e.yV*e.yV + e.zV*e.zV <= THRESHOLD_COLL_SPD*THRESHOLD_COLL_SPD/(FPS*FPS) then
set e.xV = 0.0
set e.yV = 0.0
set e.zV = 0.0
else
set e.restCount = 0
endif
endif
call e.data.onCollision(e, c)
call c.data.onCollision(c, e)
endif
// --
endif
call GroupRemoveUnit(entity.COLL, c.dummy)
call GroupAddUnit(c.collGroup, e.dummy)
endloop
endif
// --
endif
set i = i + 1
endloop
// --
// The following destroys all entities added to the destroy stack
set i = entity.COUNT_DES
loop
set i = i - 1
exitwhen i < 0
call entity.ENTITY_DES[i].destroy()
endloop
set entity.COUNT_DES = 0
// --
endfunction
struct entity
entityData data
unit dummy
effect eff
real xP
real yP
real zP
real xV = 0.0
real yV = 0.0
real zV = 0.0
real xA = 0.0
real yA = 0.0
real zA = 0.0
trigger onEvent = CreateTrigger()
real timedLife = -1.0
integer restCount = 0
integer ID
boolean wantRemoveUnit
entity constrained = 0
real constraintConstant
real constraintLength
real constraintFriction
group collGroup = CreateGroup()
static timer T = CreateTimer()
static group COLL = CreateGroup()
static integer COUNT = 0
static entity array ENTITY
static integer COUNT_DES = 0
static entity array ENTITY_DES
static location tloc = Location(0.0, 0.0)
static location tloc2 = Location(0.0, 0.0)
////////////////////////////////////////////////////////////
///////////////// __FUNCTIONALITY METHODS__ ////////////////
method remove takes boolean wantRemoveUnit returns nothing
if this.timedLife != -2.0 then
set this.constrained = 0
set this.data.collideable = false
set this.wantRemoveUnit = wantRemoveUnit
set this.timedLife = -2.0
set entity.ENTITY_DES[entity.COUNT_DES] = this
set entity.COUNT_DES = entity.COUNT_DES + 1
endif
endmethod
method applyTimedLife takes real time, boolean removeUnit returns nothing
set this.timedLife = time
set this.wantRemoveUnit = removeUnit
endmethod
method addGravity takes real factor returns nothing
set this.zA = GRAVITY_ACCELERATION*factor/(FPS*FPS)
endmethod
method setVelocity takes real xVel, real yVel, real zVel returns nothing
set this.xV = xVel/FPS
set this.yV = yVel/FPS
set this.zV = zVel/FPS
endmethod
method addVelocity takes real x, real y, real z returns nothing
set this.xV = this.xV + x/FPS
set this.yV = this.yV + y/FPS
set this.zV = this.zV + z/FPS
endmethod
method setAcceleration takes real xAcc, real yAcc, real zAcc returns nothing
set this.xA = xAcc/(FPS*FPS)
set this.yA = yAcc/(FPS*FPS)
set this.zA = zAcc/(FPS*FPS)
endmethod
method addAcceleration takes real x, real y, real z returns nothing
set this.xA = this.xA + x/(FPS*FPS)
set this.yA = this.yA + y/(FPS*FPS)
set this.zA = this.zA + z/(FPS*FPS)
endmethod
method setEntityFlyHeight takes real z returns nothing
call MoveLocation(entity.tloc, this.xP, this.yP)
set this.zP = GetLocationZ(entity.tloc) + z
endmethod
method removeHealthBar takes nothing returns nothing
call UnitAddAbility(this.dummy, 'Aloc')
call ShowUnit(this.dummy, false)
call UnitRemoveAbility(this.dummy, 'Aloc')
call ShowUnit(this.dummy, true)
endmethod
////////////// __END OF FUNCTIONALITY METHODS__ ////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////// __CONSTRAINT METHODS__ /////////////////
method addSpringConstraint takes entity attached, real springConstant, real springLength, real friction returns nothing
set this.constrained = attached
set this.constraintConstant = springConstant
set this.constraintLength = springLength
set this.constraintFriction = friction
endmethod
method removeSpringConstraint takes nothing returns nothing
set this.constrained = 0
endmethod
//////////////// __END OF CONSTRAINT METHODS__ /////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////// __MOVEMENT METHODS__ ///////////////////
method projectWithAngle takes real xyAngle, real zAngle, real speed returns nothing
local real cos = Cos(zAngle)*speed/FPS
set this.restCount = 0
set this.xV = Cos(xyAngle)*cos
set this.yV = Sin(xyAngle)*cos
set this.zV = Sin(zAngle)*speed/FPS
endmethod
method projectToPointTimed takes real xPos, real yPos, real zPos, real time returns nothing
set this.restCount = 0
set this.xV = this.xV + (xPos - this.xP)/(time*FPS)
set this.yV = this.yV + (yPos - this.yP)/(time*FPS)
call MoveLocation(entity.tloc, xPos, yPos)
set this.zV = this.zV + (zPos + GetLocationZ(entity.tloc) - this.zP)/(time*FPS) - 0.5*this.zA*FPS*time
endmethod
method projectToPointTimedEx takes real xPos, real yPos, real zPos, real time, real maxDist returns nothing
local real d
call MoveLocation(entity.tloc, xPos, yPos)
set xPos = xPos - this.xP
set yPos = yPos - this.yP
set zPos = zPos + GetLocationZ(entity.tloc) - this.zP
set d = xPos*xPos + yPos*yPos + zPos*zPos
if d > maxDist*maxDist then
set d = maxDist/(SquareRoot(d) + 0.01)
set xPos = xPos*d
set yPos = yPos*d
set zPos = zPos*d
endif
call MoveLocation(entity.tloc, this.xP, this.yP)
call this.projectToPointTimed(this.xP + xPos, this.yP + yPos, this.zP + zPos - GetLocationZ(entity.tloc), time)
endmethod
method projectTowardsPointSpeed takes real xPos, real yPos, real zPos, real speed returns nothing
local real dx = xPos - this.xP
local real dy = yPos - this.yP
local real dz
local real d
call MoveLocation(entity.tloc, xPos, yPos)
set dz = zPos + GetLocationZ(entity.tloc) - this.zP
set this.restCount = 0
set d = speed/(FPS*SquareRoot(dx*dx + dy*dy + dz*dz) + 0.001)
set this.xV = dx*d
set this.yV = dy*d
set this.zV = dz*d
endmethod
method projectToPointSpeed takes real xPos, real yPos, real zPos, real speed, boolean lob returns boolean
local real dx = xPos - this.xP
local real dy = yPos - this.yP
local real d
local real des
call MoveLocation(entity.tloc, xPos, yPos)
set speed = speed/FPS
if this.zA != 0.0 then
set d = SquareRoot(dx*dx + dy*dy) + 0.001
set des = speed*speed*speed*speed - this.zA*this.zA*d*d + 2*this.zA*speed*speed*(GetLocationZ(entity.tloc) + zPos - this.zP)
if des >= 0.0 then
set this.restCount = 0
if lob then
set des = Atan((-speed*speed-SquareRoot(des))/(this.zA*d))
else
set des = Atan((-speed*speed+SquareRoot(des))/(this.zA*d))
endif
set this.zV = speed*Sin(des)
set des = speed*Cos(des)/d
set this.xV = des*dx
set this.yV = des*dy
return true
endif
return false
else
set this.restCount = 0
set d = GetLocationZ(entity.tloc) + zPos - this.zP
set des = speed/(SquareRoot(dx*dx + dy*dy + d*d) + 0.001)
set this.xV = dx*des
set this.yV = dy*des
set this.zV = d*des
return true
endif
endmethod
method projectToPointSpeedEx takes real xPos, real yPos, real zPos, real speed, boolean lob, real maxDist returns boolean
local real d
call MoveLocation(entity.tloc, xPos, yPos)
set xPos = xPos - this.xP
set yPos = yPos - this.yP
set zPos = zPos + GetLocationZ(entity.tloc) - this.zP
set d = xPos*xPos + yPos*yPos + zPos*zPos
if d > maxDist*maxDist then
set d = maxDist/(SquareRoot(d) + 0.01)
set xPos = xPos*d
set yPos = yPos*d
set zPos = zPos*d
endif
call MoveLocation(entity.tloc, this.xP, this.yP)
return this.projectToPointSpeed(this.xP + xPos, this.yP + yPos, this.zP + zPos - GetLocationZ(entity.tloc), speed, lob)
endmethod
method projectToEntityTimed takes entity e, real time returns nothing
local real xPos = e.xP + e.xV*time*FPS
local real yPos = e.yP + e.yV*time*FPS
local real zPos = e.zP + e.zV*time*FPS
set this.restCount = 0
set this.xV = (xPos - this.xP)/(time*FPS)
set this.yV = (yPos - this.yP)/(time*FPS)
set this.zV = (zPos - this.zP)/(time*FPS) - 0.5*this.zA*FPS*time
endmethod
//////////////// __END OF MOVEMENT METHODS__ ///////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//////////////////// __GROUP METHODS__ /////////////////////
static method isEntityInRangeFilter takes nothing returns boolean
local entity e = entity(GetUnitUserData(GetFilterUnit()))
local real dx
local real dy
local real dz
if integer(e) != 0 then
set dx = e.xP - tempReal[0]
set dy = e.yP - tempReal[1]
set dz = e.zP - tempReal[2]
return dx*dx + dy*dy + dz*dz <= tempReal[3]*tempReal[3]
endif
return false
endmethod
static method enumEntitiesInRange takes group g, real x, real y, real z, real range, boolexpr filter returns nothing
local boolexpr b = And(filter, Filter(function entity.isEntityInRangeFilter))
debug if filter == null then
debug call BJDebugMsg(SCOPE_PREFIX + ": null filter in enumEntitiesInRange")
debug endif
set tempReal[0] = x
set tempReal[1] = y
set tempReal[2] = z
set tempReal[3] = range
call GroupEnumUnitsInRange(g, x, y, range, b)
call DestroyBoolExpr(b)
set b = null
endmethod
static method isEntityFilter takes nothing returns boolean
return GetUnitUserData(GetFilterUnit()) != 0
endmethod
static method enumEntitiesInRect takes group g, rect whichRect, boolexpr filter returns nothing
local boolexpr b = And(filter, Filter(function entity.isEntityFilter))
debug if filter == null then
debug call BJDebugMsg(SCOPE_PREFIX + ": null filter in enumEntitiesInRect")
debug endif
call GroupEnumUnitsInRect(g, whichRect, b)
call DestroyBoolExpr(b)
set b = null
endmethod
static method isEntityOfTypeFilter takes nothing returns boolean
local entity e = GetUnitUserData(GetFilterUnit())
return integer(e) != 0 and e.data.getType() == I
endmethod
static method enumEntitiesOfType takes group g, integer typeID, boolexpr filter returns nothing
local boolexpr b = And(filter, Filter(function entity.isEntityOfTypeFilter))
debug if filter == null then
debug call BJDebugMsg(SCOPE_PREFIX + ": null filter in enumEntitiesOfType")
debug endif
set I = typeID
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, b)
call DestroyBoolExpr(b)
set b = null
endmethod
static method enumEntitiesOfPlayer takes group g, player whichPlayer, boolexpr filter returns nothing
local boolexpr b = And(filter, Filter(function entity.isEntityFilter))
debug if filter == null then
debug call BJDebugMsg(SCOPE_PREFIX + ": null filter in enumEntitiesOfPlayer")
debug endif
call GroupEnumUnitsOfPlayer(g, whichPlayer, b)
call DestroyBoolExpr(b)
set b = null
endmethod
////////////////// __END OF GROUP METHODS__ /////////////////
/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//////////////////// __CORE METHODS__ //////////////////////
method onDestroy takes nothing returns nothing
local integer i = 0
// If the unit is labeled to be removed, the kill it, hide it, and destroy the attached effect.
call SetUnitUserData(this.dummy, 0)
call DestroyTrigger(this.onEvent)
set this.onEvent = null
call DestroyGroup(this.collGroup)
set this.collGroup = null
if this.wantRemoveUnit then
call RemoveUnit(this.dummy)
call DestroyEffect(this.eff)
endif
set this.dummy = null
set this.eff = null
if this.data.radius == RADIUS then
loop
exitwhen i >= entity.COUNT
if entity.ENTITY[i].data.radius > RADIUS then
set RADIUS = entity.ENTITY[i].data.radius
endif
set i = i + 1
endloop
endif
// Updating the entity stack.
set entity.COUNT = entity.COUNT - 1
set entity.ENTITY[this.ID] = entity.ENTITY[entity.COUNT]
set entity.ENTITY[this.ID].ID = this.ID
if entity.COUNT == 0 then
call PauseTimer(entity.T)
endif
endmethod
static method onEvent_Action takes nothing returns boolean
local eventid id = GetTriggerEventId()
local entity e = GetUnitUserData(GetTriggerUnit())
// Checking all possible eventids to call the correct method.
if id == EVENT_UNIT_DEATH then
call e.data.onDeath(e)
elseif id == EVENT_UNIT_SELECTED then
call e.data.onSelect(e)
elseif id == EVENT_UNIT_DESELECTED then
call e.data.onDeselect(e)
elseif id == EVENT_UNIT_ISSUED_ORDER then
call e.data.onOrder(e)
elseif id == EVENT_UNIT_ISSUED_POINT_ORDER then
call e.data.onPointOrder(e)
elseif id == EVENT_UNIT_ISSUED_TARGET_ORDER then
call e.data.onTargetOrder(e)
endif
return false
endmethod
////////////////// __END OF CORE METHODS__ /////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////// __CREATION METHODS__ ///////////////////
static method create takes entityData data, player owner, real xPos, real yPos, real height, real facing returns entity
local entity e = entity.allocate()
set e.data = data
// Creating dummy unit.
set e.dummy = CreateUnit(owner, DUMMY_ID, xPos, yPos, facing)
call UnitAddAbility(e.dummy, 'Aloc')
call ShowUnit(e.dummy, false)
call UnitRemoveAbility(e.dummy, 'Aloc')
call ShowUnit(e.dummy, true)
if data.scale == -1.0 then
set data.scale = 1.0
endif
call SetUnitScale(e.dummy, data.scale, data.scale, data.scale)
call UnitAddAbility(e.dummy, 'Amrf')
call UnitRemoveAbility(e.dummy, 'Amrf')
set e.eff = AddSpecialEffectTarget(data.modelPath, e.dummy, "origin")
call SetUnitAnimationByIndex(e.dummy, 90)
// Initializing position.
set e.xP = xPos
set e.yP = yPos
call MoveLocation(entity.tloc, xPos, yPos)
set e.zP = height + GetLocationZ(entity.tloc)
call SetUnitFlyHeight(e.dummy, height, data.oZ)
// Initializing the onEvent trigger.
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DEATH)
if data.collideable then
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_SELECTED)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DESELECTED)
else
call UnitAddAbility(e.dummy, 'Aloc')
endif
call TriggerAddCondition(e.onEvent, Filter(function entity.onEvent_Action))
// Updating the entity stack.
set e.ID = entity.COUNT
set entity.ENTITY[entity.COUNT] = e
set entity.COUNT = entity.COUNT + 1
call SetUnitUserData(e.dummy, integer(e))
if entity.COUNT == 1 then
call TimerStart(entity.T, 1.0/FPS, true, function main)
endif
if data.radius > RADIUS then
set RADIUS = data.radius
endif
call data.onCreate(e)
return e
endmethod
static method createFromUnit takes entityData data, unit someUnit returns entity
local entity e = entity.allocate()
if GetUnitUserData(someUnit) != 0 then
debug call BJDebugMsg(SCOPE_PREFIX + ": createFromUnit argument is already an entity/UnitUserData was used")
call e.destroy()
return 0
endif
if data.scale != -1.0 then
call SetUnitScale(someUnit, data.scale, data.scale, data.scale)
endif
set e.data = data
// Initializing the entity's unit.
set e.dummy = someUnit
call UnitAddAbility(someUnit, 'Amrf')
call UnitRemoveAbility(someUnit, 'Amrf')
// Initializing the entity's position.
set e.xP = GetUnitX(someUnit)
set e.yP = GetUnitY(someUnit)
call MoveLocation(entity.tloc, e.xP, e.yP)
set e.zP = GetUnitFlyHeight(someUnit) + GetLocationZ(entity.tloc) + data.radius
// Initializing the onEvent trigger.
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DEATH)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_SELECTED)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DESELECTED)
call TriggerAddCondition(e.onEvent, Filter(function entity.onEvent_Action))
// Updating the entity stack.
set e.ID = entity.COUNT
set entity.ENTITY[entity.COUNT] = e
set entity.COUNT = entity.COUNT + 1
call SetUnitUserData(someUnit, integer(e))
if entity.COUNT == 1 then
call TimerStart(entity.T, 1.0/FPS, true, function main)
endif
if data.radius > RADIUS then
set RADIUS = data.radius
endif
call data.onCreate(e)
return e
endmethod
static method createAsUnit takes entityData data, player whichPlayer, integer unitID, real x, real y, real height, real facing returns entity
local entity e = entity.allocate()
set e.dummy = CreateUnit(whichPlayer, unitID, x, y, facing)
if data.scale != -1.0 then
call SetUnitScale(e.dummy, data.scale, data.scale, data.scale)
endif
set e.data = data
// Initializing the entity's unit.
call UnitAddAbility(e.dummy, 'Amrf')
call UnitRemoveAbility(e.dummy, 'Amrf')
call SetUnitFlyHeight(e.dummy, height, 0.0)
// Initializing the entity's position.
set e.xP = x
set e.yP = y
call MoveLocation(entity.tloc, e.xP, e.yP)
set e.zP = height + GetLocationZ(entity.tloc) + data.radius
// Initializing the onEvent trigger.
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DEATH)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_SELECTED)
call TriggerRegisterUnitEvent(e.onEvent, e.dummy, EVENT_UNIT_DESELECTED)
call TriggerAddCondition(e.onEvent, Filter(function entity.onEvent_Action))
// Updating the entity stack.
set e.ID = entity.COUNT
set entity.ENTITY[entity.COUNT] = e
set entity.COUNT = entity.COUNT + 1
call SetUnitUserData(e.dummy, integer(e))
if entity.COUNT == 1 then
call TimerStart(entity.T, 1.0/FPS, true, function main)
endif
if data.radius > RADIUS then
set RADIUS = data.radius
endif
call data.onCreate(e)
return e
endmethod
static method createFromGroupChild takes nothing returns nothing
call entity.createFromUnit(entityData.create(I), GetEnumUnit())
endmethod
static method createFromGroup takes integer entityType, group whichGroup returns nothing
set I = entityType
call ForGroup(whichGroup, function entity.createFromGroupChild)
endmethod
static method createFromUnitType takes integer entityType, integer unitID, boolexpr filter returns nothing
local boolexpr b = And(filterGetUnitsOfTypeIdAll, filter)
set bj_groupEnumTypeId = unitID
call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea, b)
set I = entityType
call ForGroup(G, function entity.createFromGroupChild)
call DestroyBoolExpr(b)
set b = null
endmethod
//////////////// __END OF CREATION METHODS__ ///////////////
////////////////////////////////////////////////////////////
private static method onInit takes nothing returns nothing
set minX = GetRectMinX(bj_mapInitialPlayableArea)
set minY = GetRectMinY(bj_mapInitialPlayableArea)
set maxX = GetRectMaxX(bj_mapInitialPlayableArea)
set maxY = GetRectMaxY(bj_mapInitialPlayableArea)
set filter = Filter(function enum)
endmethod
//! runtextmacro SEEplugin()
endstruct
//call proj.projectWithAngle(a*bj_DEGTORAD, Atan(-1500.0*1500.0/(SquareRoot(x*x + y*y)*SEEcore_FPS*SEEcore_FPS*proj.zA)), 1500.0)
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SEEentityData
interface entityData
real radius
real mass = 0.0
real drag = 0.0
real restitution = 1.0
real friction = 0.0
real scale = -1.0
boolean collideable = false
boolean bounceable = false
string modelPath = ""
real oX = 0.0
real oY = 0.0
real oZ = 0.0
boolean affected = true
method onCreate takes entity e returns nothing defaults nothing
method onGround takes entity e returns nothing defaults nothing
method onCollision takes entity e, entity e2 returns nothing defaults nothing
method onRest takes entity e returns nothing defaults nothing
method onSelect takes entity e returns nothing defaults nothing
method onDeselect takes entity e returns nothing defaults nothing
method onDeath takes entity e returns nothing defaults nothing
method onExit takes entity e returns nothing defaults nothing
method onPointOrder takes entity e returns nothing defaults nothing
method onTargetOrder takes entity e returns nothing defaults nothing
method onOrder takes entity e returns nothing defaults nothing
method onLoop takes entity e returns nothing defaults nothing
endinterface
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SEEheight initializer Init requires SEEcore
globals
private real X
private real Y
private real Z = 0.0
endglobals
private function Callback takes nothing returns nothing
local real z
call MoveLocation(entity.tloc, X, Y)
set z = GetLocationZ(entity.tloc)
if z > Z then
set Z = z
endif
set X = X + 64.0
if X >= SEEcore_maxX then
set X = SEEcore_minX
set Y = Y + 64.0
if Y >= SEEcore_maxY then
call BJDebugMsg(SCOPE_PREFIX + ": Maximum terrain height: |cffffcc00" + R2S(Z))
call BJDebugMsg(" Enter this value in the SEEcore trigger for the constant labeled\n MAX_TERRAIN_HEIGHT and disable the SEEheight trigger")
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endif
endif
endfunction
private function Init takes nothing returns nothing
set X = SEEcore_minX
set Y = SEEcore_minY
call BJDebugMsg(SCOPE_PREFIX + ": Calculating maximum terrain height...")
call TimerStart(CreateTimer(), 0.0, true, function Callback)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! textmacro_once SEEplugin
//! endtextmacro
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SEEdatalib requires SEEentityData
struct ball extends entityData
boolean bounceable = true
boolean collideable = true
real radius = 64.0
real scale = 7.8
real mass = 300.0
real friction = 0.0
real restitution = 1.0
real drag = 0.01
string modelPath = "war3mapImported\\Marble.mdx"
method onPointOrder takes entity e returns nothing
call e.projectTowardsPointSpeed(GetOrderPointX(), GetOrderPointY(), e.data.radius, 2000.0)
endmethod
method onExit takes entity e returns nothing
call e.remove(true)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
*******************************************
****** ~ Implementing instructions ~ ******
*******************************************
Follow these steps to succesfully implement this
system into your map.
1. Import the "dummy.mdx" model from this map to yours,
and be sure to keep the same model path for it.
2. Copy the dummy unit 'e000' from this map to yours.
You can change the rawcode if you want, you'll just
have to change another value later in the code.
3. Copy these triggers into your map:
"SEEentityData"
"SEEcore"
"SEEdatalib"
"SEEplugin"
"SEEheight"
4. Go to the top of the "SEEcore" trigger, and toggle the
constants of the trigger to fit your needs (if you
changed the rawcode of the dummy unit before,
you have to set it to the changed rawcode now. The constant
you have to change is called "DUMMY_ID".
5. Delete everything in between the library delimiters
in the "SEEdatalib" trigger.
6. Run your map once with SEEheight enabled. Follow the
on-screen instructions.
7. Have fun making your maps with this system! :D
8. And for GOD's sake, read the damned documentation!!!
//TESH.scrollpos=3
//TESH.alwaysfold=0
*******************************************
****** ~ Change Log ~ ******
*******************************************
Simple Entity Engine. Version 2.4
--- 2.4 ---
Rewrote ground collision detection script. It's now feckin great.
Replaced the debug messages to use SCOPE_PREFIX.
Added the SEEheight library. (neat idea, shamelessly taken from grim001)
Made SEEcore_FPS a constant again.
Fixed some stuff in the implementing instructions.
Removed the selection circle from the dummy unit.
--- 2.3 ----
Changed friction calculations once again. Now seems much better.
Now keeps default scale on entity.createFromUnit if no scale is specified.
Removed health bars on non-locusted entities from entity.create.
projectTowardsPointSpeed now works as desired.
Entity collisions are now prevented from being calculated twice.
Fixed some bugs in the demo.
onCollision is now executed after collision resolution.
Fixed a wrong entity collision calculation.
Added an extra term in terrain collision detection for better precision.
Added the SAMPLE_RADIUS constant for terrain collisions.
Entities can now slide on the ground. Added a THRESHOLD_VEL constant for this.
Added the setEntityFlyHeight method.
Added the entity.createAsUnit method.
Fixed an issue with resting non-affected entities.
Removed a useless parameter for the removeSpringConstraint method.
Added the removeHealthBar method.
Added a "Constant List" documentation page.
Moved the onLoop call in SEEcore's main function (fixed a bug in resting non-affected entities).
Added a new demo.
--- 2.2 ---
Added projectToPointSpeedEx and projectToPointTimedEx.
Added projectTowardsPointSpeed.
Added enumEntitiesOfPlayer/InRect/OfType.
Added createFromGroup and createFromUnitType.
Rewrote the applyTimedLife method into something much simpler.
Fixed an issue with rest on non-affected entities.
Improved collision detection (now there are two integrated methods)
Removed some useless operations.
Added COLLISION_VEL and THRESHOLD_VEL constants.
Removed some lame parts about the entity struct.
Added the appropriate scale for createFromUnit.
Added a new demo.
Fixed some parts about the documentation.
Most stuff that I've forgotten.
--- 2.1 ---
Fixed friction calculations. (1.0 friction is now no motion, 0.0 friction is no friction at all)
Added an enum method.
Added a new projectToPointSpeed which takes in account gravity.
Made a new demo. (Credits to WILLTHEALMIGHTY for the nuke effect)
Probably added more stuff but I don't remember.
--- 2.0 ---
Remade the whole thing from scratch.
Collision is now time-velocity based.
New collision resolution technique.
Added air drag.
Added spring constraints.
TONS of optimization.
Added a few onX methods to entityData.
COMPLETE makeover on the syntax.
Made a new demo.
Probably many more changes that I forgot about.
--- 2.0.1 ---
Fixed a bug in the demo.
--- 1.2 ---
Added a Bounceable property.
Inlined the GroundBounce function.
Inlined (and optimized) the CollisionBounce function.
Replaced Static entities with an Affected state.
Added a Rest tally for more precise resting states.
Collision optimization.
Added Offsetx/y/z coordinates.
Replaced the crappy Projectile plugin with a cool Vehicle plugin.
Added documentation.
Smaller fixes...
--- 1.1 ---
Patched up collision detection and prevention.
Optimized the GroundBounce function (just a little bit).
Fixed an Entity removal problem (double free).
Removed the use of TriggerAddAction. The system now uses TriggerAddCondition.
Fixed an onEvent method problem with the CreateEntity method.
Added a little debug function.
A couple of smaller bug fixes...
--- 1.0 ---
Public release of the system.
*******************************************
****** ~ Previous Systems ~ ******
*******************************************
- Simple Particle System (slow, buggy and bad)
- Simple Particle System Mark II (used procedural sloppy coding)
//TESH.scrollpos=0
//TESH.alwaysfold=0
*******************************************
***** ~ SEE Constant List ~ *****
*******************************************
The default values listed here are not static, and should be changed if
a user deems it necessary. They are not perfectly calibrated by any means.
private constant integer DUMMY_ID = 'e000'
-- This represents the unit rawcode of the dummy unit. You should transfer the dummy unit
included in this map to yours, or be sure that your dummy works in the same way as the one
in this map (z-axis orientation by animation index).
public real FPS = 40.0
-- The FPS constant represents the frequency of the SEEcore main function. A higher FPS
means smoother motion, but more processing stress on the system.
private real GRAVITY_ACCELERATION = -3000.0
-- GRAVITY_ACCELERATION is like the gravitational constant g in real life physics. It
represents the downwards acceleration of an object. This means -3000.0 Wc3 units per second
per second.
private constant real AIR_DENSITY = 0.001
-- AIR_DENSITY is the global air density constant used in drag calculations. It's abstract
in this case, as it includes many factors such as pi and 0.5 and others. Consider it an
arbitrary constant.
private constant integer REST_TALLY = 30
-- When an entity is reaching its resting state, this is the amount of iterations of it being
at a resting speed for it to be considered at rest. Higher REST_TALLY indicates more processing
stress on the system, but possibly more precision on resting states.
private constant real REST_STATE_SPD = 60.0
-- This constant represents the maximum speed that an entity can have to still be considered
at rest. If an entity's speed goes above REST_STATE_SPD, then an entity will stop being
considered for a resting state.
private constant real COLL_ALGO_SPD = 300.0
-- COLL_ALGO_SPD represents the collision detection algorithm threshold speed. There are two
different collision algorithms included in SEE. One of them is used when entities move at slow
speeds, and the other when entities move at high speeds. When an entity's speed is below
COLL_ALGO_SPD on collision, then the simple algorithm will run. 300.0 is a good value for this.
private constant real THRESHOLD_COLL_SPD = 70.0
-- THRESHOLD_COLL_SPEED is used in collision resoltion. If an entity bounces off another entity
and it's resulting speed is lower than THRESHOLD_COLL_SPD, then its speed will be set to 0.0.
private constant real THRESHOLD_SLIDE_SPD = 100.0
-- THRESHOLD_SLIDE_SPEED is used in collision resolution with the ground. When a bounceable
entity collides with the ground and its perpendicular component of velocity is smaller than
THRESHOLD_SLIDE_SPD, then its perpendicular component is set to 0.0. Therefore, the only
velocity remaining is parallel to the ground, i.e. the entity is sliding.
private constant real SAMPLE_RADIUS = 32.0
-- SAMPLE_RADIUS is used in terrain collision resolution. SAMPLE_RADIUS is the radius from which
a terrain normal is established. 32.0 is a good value. Try messing around with it if you don't
like the way entities bounce currently, but don't expect much of a difference.
private constant real MAX_TERRAIN_HEIGHT = 1230.500
-- MAX_TERRAIN_HEIGHT is the highest terrain point in your map. The SEEheight library automatically
calculates this value if its trigger is enabled. If you are using very tall walkable destrucables,
then you might have to enter the value manually. Usually not, though.
//TESH.scrollpos=0
//TESH.alwaysfold=0
*******************************************
****** ~ SEE Member List ~ ******
*******************************************
-----------------------------------------------------
*** 1. 'entityData' struct members:
-----------------------------------------------------
real radius
-- The radius is used in collision detection, both entity-entity and ground-entity.
Radius does NOT automatically detect the appropriate scale of a model.
real mass
-- Mass is used in elastic collisions. There is no unit for mass: all entered
mass properties are relative to eachother.
real drag
-- The drag coefficient is related solely to the decelleration caused by air
resistance. The higher the coefficient, the more air resistance.
real restitution
-- The coefficient of restitution is a coefficient of velocities after entity-entity
impact. It's an abstract value. A coeffiecient of 1.0 is perfectly elastic collision.
real friction
-- This is a coefficient to velocity on ground-entity collisions. If friction = 1.0,
then there is no motion. If friction = 0.0, then there is no friction.
real scale
-- This is a coefficient to the model's scale that will be applied upon entity creation.
boolean collideable
-- This boolean determines whether an entity is collideable or not. Useful if you just want particles.
boolean bounceable
-- This boolean determines if an entity should bounce using elastic collisions.
string modelPath
-- modelPath points to a string, which refers to the entity's model, in the case of
the entity.create method. If you are using entity.createFromUnit, this parameter is useless.
real oX
real oY
real oZ
-- The 'o' vector is an offset parameter, meaning that the model's position will be offset
by (oX, oY, oZ) during entity rendering. Useful for things like inanimate objects. This has
been created because entities must not sink into the ground, thus their height can be, at the
least, their radius. In certain cases, this results in a floating model. A negative oZ can fix
this issue.
boolean affected
-- This boolean determines if an entity is affected by gravity or terrain etc...
Basically means that if affected is true, then the entity will behave like an entity.
If affected is false, the entity will behave like a regular unit in Warcraft III: you can
order it and it will move like a regular unit. One important thing to remember is that
velocity is conserved, even if affected is false.
-----------------------------------------------------
*** 2. 'entity' struct members:
-----------------------------------------------------
entityData data
-- 'data' points to the assigned entityData of an entity.
unit dummy
-- This unit is the concerned entity unit. For entity.create method, this points
to the dummy unit, on which is attached an effect.
effect eff
-- This variable points to the attached effect for the entity.create method.
real xP
real yP
real zP
-- The 'P' vector is the current position vector. zP is the sum of the unit's fly
height and the terrain height at point (xP, yP).
real xV
real yV
real zV
-- The 'V' vector is the current velocity vector. Each parameter is divided by FPS,
the amount of frames per second, which is defined as a constant near the top of SEEcore.
'V' is the rate of change of 'P'.
real xA
real yA
real zA
-- The 'A' vector is the current acceleration vector. Usually, only zA is used, for
gravitational acceleration, but xA and yA can also be used for wind and such.
'A' is the rate of change of 'V'.
trigger onEvent
-- This trigger points to most of the methods defined in entityData.
integer restCount
-- 'restCount' is the current amount of tallied rests.
integer ID
-- 'ID' is the entity's position in the entity.ENTITY array. Useful in entity destruction.
boolean wantRemoveUnit
-- If wantRemoveUnit is true, then the entity will be removed after the end of the current timeframe.
real timedLife
-- The time remaining for timedLife. -1.0 if no timedLife is set. -2.0 if an entity has been removed.
entity constrained
-- This is the pointer to the constrained entity, if any. If an entity A is constrained by another entity B,
then B is not constrained by A.
real constraintConstant
-- The spring constant "k" in Hooke's law: F = -k*d. A high constant is like a very strong rope. A small
constant is like a rubber band.
real constraintLength
-- The distance between two constrained entities.
real constraintFriction
-- The friction opposing the force in Hooke's law, of two constrained entities.
static timer T
-- The main timer controlling the function main.
static group COLL
-- The group inside which collision detection is ran.
static integer COUNT
-- The current amount of entities, active or not.
static entity array ENTITY
-- An array containing all entities in a stack.
static integer COUNT_DES
-- The current amount of 'to-be-destroyed' entities.
static entity array ENTITY_DES
-- An array containing the entities to be destroyed in a stack.
static location tloc = Location(0.0, 0.0)
-- A temporary location used for terrain collision and height.
static location tloc2 = Location(0.0, 0.0)
-- A temporary location used for terrain collision and height.
//TESH.scrollpos=0
//TESH.alwaysfold=0
*******************************************
****** ~ SEE Method List ~ ******
*******************************************
-----------------------------------------------------
*** 1. 'entityData' struct methods:
-----------------------------------------------------
All of the methods below will work with natives corresponding
to the concerned event. For example, GetTriggerPlayer() will
return the selecting player, for the onSelect method.
method onGround takes entity e returns nothing defaults nothing
-- The method that will be run when an entity collides with the ground.
method onCollision takes entity e, entity e2 returns nothing defaults nothing
-- The method that will be run when two entities collide.
method onRest takes entity e returns nothing defaults nothing
-- This method will be run when an entity reaches a resting state.
method onSelect takes entity e returns nothing defaults nothing
-- The onSelect method will run when a player selects an entity.
NOTE : onSelect and onDeselect are delayed, due to Warcraft III's engine.
method onDeselect takes entity e returns nothing defaults nothing
-- The onDeselect method will run when a player deselects an entity.
NOTE : onSelect and onDeselect are delayed, due to Warcraft III's engine.
method onDeath takes entity e returns nothing defaults nothing
-- The onDeath method will run when an entity dies of any cause including
timed life by <entity>.applyTimedLife.
method onExit takes entity e returns nothing defaults nothing
-- onExit will run once an entity exits map bounds.
method onPointOrder takes entity e returns nothing defaults nothing
-- onPointOrder will run when an entity is issued an order to a point.
NOTE : A Warcraft III bug/stupidity makes it such that PauseUnit registers as a point order.
method onTargetOrder takes entity e returns nothing defaults nothing
-- onTargetOrder will be run when an entity is issued an order to a target.
method onOrder takes entity e returns nothing defaults nothing
-- onOrder will run when an entity is issued an immediate order.
method onCreate takes entity e returns nothing defaults nothing
-- onCreate will run when an entity is created.
method onLoop takes entity e returns nothing defaults nothing
-- onLoop will run every iteration of the main function.
-----------------------------------------------------
*** 2. 'entity' struct methods:
-----------------------------------------------------
static method create takes entityData data, player owner, real xPos, real yPos, real height, real facing returns entity
-- Creates an entity with all of the chosen parameters as data.
static method createFromUnit takes entityData data, unit someUnit returns entity
-- Turns someUnit into an entity.
static method createAsUnit takes entityData data, player owner, integer unitID, real xPos, real yPos, real height, real facing returns entity
-- Creates a unit of type unitID at the specified flying height and turns it into an entity.
static method createFromGroup takes integer entityType, group whichGroup returns nothing
-- Takes all units from the group whichGroup and turns them into entities of type entityType. entityType
is a struct's typeid. For example, if a certain entity type is called "Boulder", then this function could
be called in the following manner:
i.e. call entity.createFromGroup(Boulder.typeid, yourGroup)
static method createFromUnitType takes integer entityType, integer unitID, boolexpr filter returns nothing
-- Takes all units on the map of type unitID and obeying to filter and turns them into entities of type entityType.
entityType is a struct's typeid. For example, if a certain entity type is called "Boulder", and you would want to
make a Boulder entity for every human peasant on the map, then the following should be called:
i.e. call entity.createFromUnitType(Boulder.typeid, 'hpea', Filter(function True))
Where Filter(function True) is a filter that always returns true.
method remove takes boolean wantRemoveUnit returns nothing
-- Removes the concerned entity. If wantRemoveUnit is true, then the entity
dummy and effect will be removed too.
method applyTimedLife takes real time, boolean removeUnit returns nothing
-- Applies a timed life to the concerned entity. Is precise up to 1.0/FPS seconds.
method addGravity takes real factor returns nothing
-- Adds gravity's acceleration to the 'A' vector in the entity struct. 'factor'
is a factor to the gravitational acceleration. If it is 1.0, then nothing is changed.
If it is 2.0, then gravity's acceleration is doubled.
method setVelocity takes real xVel, real yVel, real zVel returns nothing
-- Sets the current velocity vector to the vector parameter.
method addVelocity takes real x, real y, real z returns nothing
-- Adds the vector parameter to the current velocity vector.
method setAcceleration takes real xAcc, real yAcc, real zAcc returns nothing
-- Sets the current acceleration vector to the vector parameter.
method addAcceleration takes real x, real y, real z returns nothing
-- Adds the vector parameter to the current acceleration vector.
method setEntityFlyHeight takes real height returns nothing
-- Sets the concerned entity's fly height to the given value. This acts in the same manner as SetUnitFlyHeight.
method removeHealthBar takes nothing returns nothing
-- This method removes the floating health bar above an entity. Useful sometimes for collideable entities created
with createFromUnit or createAsUnit.
method projectWithAngle takes real xyAngle, real zAngle, real speed returns nothing
-- Shoots the concerned entity towards xyAngle and zAngle, with the chosen speed.
method projectToPointTimed takes real xPos, real yPos, real zPos, real time returns nothing
-- Shoots the concerned entity to the target point in a parabolic trajectory,
taking in account the time until ground contact.
NOTE : This DOES take in account gravity's acceleration.
method projectToPointTimedEx takes real xPos, real yPos, real zPos, real time, real maxDist returns nothing
-- This method behaves in the same manner as projectToPointTimed. The difference is that
projectToPointTimedEx has a maxDist parameter which limits the distance to the target point.
If the target point is located further from the entity's current position than maxDist, then
the target point will be scaled to fit maxDist.
NOTE : This DOES take in account gravity's acceleration.
method projectToPointSpeed takes real xPos, real yPos, real zPos, real speed, boolean lob returns nothing
-- Shoots the concerned entity to the position parameter with the chosen speed.
If lob is true, then the entity will be projected to the target point with optimal angle of elevation
to make a lob. If it is false, then it will be projected to the point with optimal angle of elevation
to make a direct hit. If the entity has no gravity, then lob is a useless parameter and the entity will
be projected in a straight line (in such a case, it's possible to use projectTowardsPointSpeed instead)
NOTE : This DOES take in account gravity's acceleration.
method projectToPointSpeedEx takes real xPos, real yPos, real zPos, real speed, boolean lob, real maxDist returns nothing
-- This method behaves in the same manner as projectToPointSpeed. The difference is that
projectToPointSpeedEx has a maxDist parameter which limits the distance to the target point.
If the target point is located further from the entity's current position than maxDist, then
the target point will be scaled to fit maxDist.
NOTE : This DOES take in account gravity's acceleration.
method projectTowardsPointSpeed takes real xPos, real yPos, real zPos, real speed returns nothing
-- This method projects an entity towards the target point. If the entity has acceleration,
then it cannot his the target point, as its velocity will stoop while it is on trajectory. If the
entity does not have acceleration, then it will hit the target point successfully.
NOTE : This does NOT take in account gravity's acceleration.
method addSpringConstraint takes entity attached, real springConstant, real springLength, real friction returns nothing
-- Constrains two entities as a spring constraint, with Hooke's law : F = -k*d. springConstant is the
spring constant in Hooke's law. springLength is the maximum distance between the two entities. friction
is the force opposing the resulting force of Hooke's law.
method removeSpringConstraint takes nothing returns nothing
-- Removes the currently attached entity from the constraint.
method enumEntitiesInRange takes group g, real x, real y, real z, real range, boolexpr filter returns nothing
-- Adds all entities within a given (spherical) range from the chosen point to the group g.
method enumEntitiesInRect takes group g, rect whichRect, boolexpr filter returns nothing
-- Adds all entities within a given rect to the group g.
method enumEntitiesOfType takes group g, integer entityType, boolexpr filter returns nothing
-- Adds all entities of type entityType to the group g. entityType is found using the 'typeid' member.
For example, for a certain entityData struct called "A", the typeid of "A" would be "A.typeid".
Calling the function to get all entities of type "A" would look like the following:
i.e. call entity.enumEntitiesOfType(yourGroup, A.typeid, yourFilter)
method enumEntitiesOfPlayer takes group g, player whichPlayer, boolexpr filter returns nothing
-- Adds all entities owned by a certain player to the group g.