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