- Joined
- Sep 9, 2007
- Messages
- 6,759
Hey guys, I am giving you the two remaining scripts of my systems.
Maybe someone can do something with them!
Please note: This codes are not tested. They will most probably not work. But you will get the basic idea what I tried to do.
Please give credit to me, because this was pretty much work.
Maybe someone can do something with them!
Please note: This codes are not tested. They will most probably not work. But you will get the basic idea what I tried to do.
JASS:
//: ----------------------------------------------------------------------------------------------------------------------------
//: |
//: | Missile 0.0.1
//: | by Anachron
//: |
//: | ------------------------------------------------------------------------------
//: |
//: | Credits:
//: | Rising_Dusk for GroupUtils and his teaching
//: | Vexorian for vJass and Table(Timer_Utilities)
//: | MindWorX for JNGP
//: | Flame_Phoenix for teaching me vJass
//: | Bribe for ProjectileUtils (Gave me a lot of ideas)
//: | Berb for Projectile and his feedback
//: |
//: | Readme:
//: | You need JassNewGenPack (JNPG) in order to use this system.
//: | (Wheren can I get this? Here: )
//: | You need Table to use this! (Included)
//: | You can optionally have GroupUtils and TimerUtils
//: |
//: | A few words:
//: | Hey dude, thanks for downloading my system!
//: | To make sure you have set up my system correctly, please read the readme.
//: | If you need help with the API, please check the documentation below.
//: | If you want to check the credits, please read the text above.
//: | If you want to contact me, send me an Email to [email][email protected][/email] or leave
//: | me any kind of message on hiveworkshop.com/forum/user/Anachron.
//: |
//: | Features:
//: | - Highly customizeable (Modules as extensions)
//: | - Eventstack which supports multiple actions per event
//: | - You can define UserEvents
//: | - Totally fast
//: | - Well documentated
//: | - Examples included
//: | - Custom unit support (Use your units as missiles)
//: | - Pitch support
//: |
//: | ------------------------------------------------------------------------------
//: |
//: | Api:
//: | Missile: CoreStruct. Includes all required methods and members.
//: | should be extended.
//: | members:
//: | -------
//: | range: Use this to define the range that the missile can hit things.
//: | (Suggested for big missiles)
//: | Example: 128. //: Can hit units, missiles, destructables etc in range of 128.
//: |
//: | turn: This speed is the turnrate of your missile. This rate represents
//: | the time (in sec) that the missile needs to turn 360°.
//: | Example: 0.5 //: Can turn 720 degrees per second
//: |
//: | height: Realvalue which represents the actual height of the missile/model. Used in collisions
//: | that have targetOnZ on true.
//: | Example: 64. //: This missile can collide with anything that is between 32 lower&higher.
//: |
//: | scale: Percentual size of the actor. (1.00 = 100%)
//: | Example: 1.25 //: 25% bigger
//: |
//: | active: Boolean whether the missile is active (moving) or not.
//: | Example: false //: Unit is still alive, but does not fly to anywhere
//: |
//: | alive: Boolean whether the missile is alive. If set to false the missile will be destroyed.
//: | Example: true //: Unit is alive and acts properly
//: |
//: | targetOnZ: Boolean whether the missile should care about the z differences in the target collision.
//: | Example: true //: Only collide with target on the same z-level (Cares about height!)
//: |
//: | moveCheckF: Boolean whether to check the facing on unit movement or not.
//: | If yes, the missile will only move when its current facing is equal with the facing to the target.
//: |
//: | actor: Unit variable representing the current missile handle that is flying.
//: | Example: null //: Uninitialized
//: |
//: | target: Unit that is representing the current target handle for the missile
//: | Example: CreateUnit(Player(PLAYER_NEUTRAL_AGGRESIVE), 'test', 0., 0., 0.) //: Put any unit here to set a new target unit
//: | WARNING: THIS SHOULD NOT BE USED WHEN YOU HAVE THE DEFAULT LAUNCHERS! USE .launch() INSTEAD!
//: | USE THIS ONLY WITH YOUR OWN LAUNCHERS!
//: |
//: | period: Time interval for missile refreshes. The faster it is, the smoother they will be.
//: | However, it will also increase calculations drastically.
//: | Example: 0.03175 //: Found out that this value is perfect for most cases
//: |
//: | self: This variable represents the current instance that is checked.
//: | WARNING: DO NOT CHANGE THIS! THE SYSTEM MAY CRASH OR CAUSE BUGGY BEHAVIOR!
//: |
//: |
//: | //: (These are actually method operators)
//: | -----------------------------------------
//: | x: Realvalue representing the actual x-coordinate of the missile.
//: | Set it to anywhere to move the missiles x to the target point.
//: | Example: 2500. //: Missiles x is now on 2500.
//: |
//: | x: Realvalue representing the actual y-coordinate of the missile.
//: | Set it to anywhere to move the missiles y to the target point.
//: | Example: -120. //: Missiles y is now on -120
//: |
//: | z: Realvalue representing the actual z-coordinate of the missile.
//: | Set it to anywhere to move the missiles z to the target point.
//: | Example: 90. //: Missiles z is now on 90
//: |
//: | f: Realvalue representing xy rotation of the unit. Change it to change the units facing.
//: | Example: 180. //: Missiles is now looking to 180°.
//: |
//: | p: Realvalue representing z rotation (pitch) of the unit. Change it to change the units pitch rotation.
//: | Example: 90. //: Missiles is now looking forwards.
//: | INFO: DOES ONLY WORK TOGETHER WITH THE MISSILE DUMMY UNITTYPE AND THE DUMMY MODEL. OTHER MAY WORK TOO,
//: | BUT ARE NOT GUARANTEED TO WORK!
//: |
//: | s: The movement speed of your missile. Can be negative.
//: | Example: 250. //: Travels 250 unit degrees every second
//: |
//: | xyArc: A real value that significates the arch on the horizontal axis. (1.00 = 100%)
//: | Example: 0.45 // 45% arch
//: |
//: | zArc: A real value that significates the arch on the vertical axis. (1.00 = 100%)
//: | Example: 0.25 // 25% arch
//: |
//: | //: Vectors - SHOULD NOT BE TOUCHED!
//: | -----------------------------------------
//: | start: Vector of the X-Y-Z coordinations from missile launch
//: | WARNING: PLEASE DO NOT MODIFY THIS!
//: |
//: | end: Vector of the X-Y-Z coordinations from missile target
//: | WARNING: PLEASE DO NOT MODIFY THIS!
//: |
//: | pos: Vector of the X-Y-Z coordinations from current position
//: | WARNING: PLEASE DO NOT MODIFY THIS!
//: |
//: | last: Vector of the X-Y-Z coordinations from last position
//: | WARNING: PLEASE DO NOT MODIFY THIS!
//: |
//: | methods:
//: | --------
//: | create (unit theActor)
//: | Creates a missile which takes the parameter actor. Automatically applies all values from the unit.
//: | Example: call Missile.create(CreateUnit(Player(0), 'myut', 250., 250., 270.))
//: |
//: | destroy
//: | WARNING: DO NOT USE THIS METHOD TO DESTROY MISSILES! USE "set instance.alive = false" INSTEAD!
//: |
//: | bj_lastCreatedMissile: Use this variable anywhere to refer to the latest missile that has been created.
//: | bj_lastCreatedMissileGroup: Use this to refer to the latest created missilegroup.
//: |
//: | CreateMissile(unit theActor): Create any missile anywhere in your script.
//: | CreateMissileGroup(): Create any missilegroup anywhere.
//: |
//: | ------------------------------------------------------------------------------
//: |
//: | Addons:
//: | WARNING: EVERY ADDON SLOWS THE SYSTEM! ONLY USE THE ADDONS THAT YOU NEED!
//: | ALSO ADDONS WHICH USE TABLE WILL DECREASE THE AMOUNT OF MISSILES THAT CAN BE USED WITH THIS SYSTEM!
//: | IF YOU STILL NEED ALL ADDONS AND ITS TO LESS, INCREASE THE INSTANCE LIMIT IN TABLE!
//: |
//: | List:
//: | - Stack: Adds the instance control to the core [HIGHLY RECOMMENDED]
//: |
//: | - Launcher: Adds a few methods to launch (shoot) a missile [SUGGESTED]
//: |
//: | - Dummy: Adds a new create method which creates the MissileDummy [SUGGESTED]
//: |
//: | - Chain: Allows the missile to behave as an chaining effect
//: | new methods:
//: | chainHitFilter: Filter any unit that should be possible to be chained
//: |
//: | new members:
//: | useChain: Boolean whether or not to use the chain feature
//: | Example: .useChain = false //: Does not chain
//: |
//: | chainRange: A real with the range of enumeration for chain targets.
//: | Example: .chainRange = 500. //: Searches targets in 500 area
//: |
//: | - Any Collision:
//: | Possible collisions:
//: | - Missile
//: | - Unit
//: | - Destructable
//: |
//: | new members:
//: | hits$NAME$s: Boolean whether this missile is allowed to collide with the type of objects or not.
//: | Example: true
//: |
//: | hits$NAME$OnZ: Boolean whether these collisions check the target object for having the same height
//: |
//: | new methods:
//: | $NAME$HitFilter: Filter whether the object is valid for collision or not.
//: |
//: |
//: | ------------------------------------------------------------------------------
//: |
//: | Events:
//: | This system works with stub methods so you can actually hook whenever an event is fired.
//: |
//: | Default Eventlist:
//: | ------------------
//: | - onCreate: Everytime you create a new missile
//: | - onDestroy: Everytime a missile is removed
//: | - onTarget: Once the missile reached its target
//: | - onDecay: Everytime you launch a missile
//: | - onLoop: Every missile period seconds do for missile
//: |
//: | Advanced Eventlist:
//: | ------------------
//: | - onMissileHit: [Requires MissileMissileCollision] Everytime the missile hits another missile
//: | - onUnitHit: [Requires MissileUnitCollision] Everytime the missile hits an unit
//: | - onDestructableHit: [Requires MissileDestructableCollision] Everytime the missile hits a destructable
//: | =========
//: | - onGround: [Requires MissileGroundCollision] Everytime the unit hits ground
//: | - onFly: [Requires MissileGroundCollision] Everytime the unit begins to fly
//: |
//: | ------------------------------------------------------------------------------
//: |
//: |
//: ----------------------------------------------------------------------------------------------------------------------------
library Missile requires MissileUtils, Vector
//: Defaults
//:----------------------------------------
globals
//: For help read the manual on the top!
private constant real DEFAULT_SPEED = 600.
private constant real DEFAULT_RANGE = 75.
private constant real DEFAULT_TURN = 1.
private constant real DEFAULT_HEIGHT = 90.
private constant real DEFAULT_SCALE = 1.
private constant real DEFAULT_DECAY = 0. //: No decay
private constant real DEFAULT_XYARC = 0.
private constant real DEFAULT_ZARC = 0.
endglobals
//:----------------------------------------
private struct MissileEventHandler
stub method enumChain takes nothing returns nothing
endmethod
stub method checkGround takes nothing returns nothing
endmethod
stub method enumDestructables takes nothing returns nothing
endmethod
stub method enumUnits takes nothing returns nothing
endmethod
stub method enumMissiles takes nothing returns nothing
endmethod
stub method onCreate takes nothing returns nothing
endmethod
stub method onLoop takes nothing returns nothing
endmethod
stub method onTarget takes nothing returns nothing
endmethod
stub method onDecay takes nothing returns nothing
endmethod
stub method onDeath takes nothing returns nothing
endmethod
endstruct
struct Missile extends MissileEventHandler
public real range = DEFAULT_RANGE
public real turn = DEFAULT_TURN
public real height = DEFAULT_HEIGHT
public real scale = DEFAULT_SCALE
public real decay = DEFAULT_DECAY
public real xyArc = DEFAULT_XYARC
public real zArc = DEFAULT_ZARC
public real time = 0.
public real reach = 0.
public boolean active = false
public boolean alive = true
public boolean moveCheckF = false
public boolean targetOnZ = false
public boolean autoFace = true
public vector start = 0
public vector end = 0
public vector pos = 0
public vector last = 0
public unit target = null
public unit actor = null
public static real period = 0.03175
public static thistype self = 0
private static real MIN_X = 0.
private static real MIN_Y = 0.
private static real MAX_X = 0.
private static real MAX_Y = 0.
private real speed = DEFAULT_SPEED
private real facing = 0.
private real pitch = 0.
private boolean isCreated = false
implement MissileStack
method operator x takes nothing returns real
return .pos.x
endmethod
method operator x= takes real value returns nothing
if value < thistype.MIN_X or value > thistype.MAX_X then
set .alive = false
return
endif
set .pos.x = value
call SetUnitX(.actor, value)
endmethod
method operator y takes nothing returns real
return .pos.y
endmethod
method operator y= takes real value returns nothing
if value < thistype.MIN_Y or value > thistype.MAX_Y then
set .alive = false
return
endif
set .pos.y = value
call SetUnitY(.actor, value)
endmethod
method operator z takes nothing returns real
return .pos.z
endmethod
method operator z= takes real value returns nothing
set .pos.z = value
call SetUnitFlyHeight(.actor, value - GetLocZ(.pos.x, .pos.y), 0.)
endmethod
method operator f takes nothing returns real
return .facing
endmethod
method operator f= takes real value returns nothing
set .facing = value
call SetUnitFacing(.actor, value * bj_RADTODEG)
endmethod
method operator p takes nothing returns real
return .pitch
endmethod
method operator p= takes real value returns nothing
local integer i = 0
if GetUnitTypeId(.actor) == MISSILE_DUMMY then
return
endif
set i = R2I(value * bj_RADTODEG + 90.5)
if i >= 180 then
set i = 179
elseif i < 0 then
set i = 0
endif
call SetUnitAnimationByIndex(.actor, i)
set .pitch = value
endmethod
method operator s takes nothing returns real
return .speed
endmethod
method operator s= takes real value returns nothing
set .speed = value
set .reach = DistanceBetweenVectors(this.start, this.end) / .speed
endmethod
public stub method run takes nothing returns nothing
//: Movement locals
local real a = 0.
local real f = .f
local real t = 0.
local real once = .turn * thistype.period * 2 * bj_PI //: rotate "once"
local real move = .s * thistype.period
local real diff = 0.
local real diffOne = 0.
local real diffTwo = 0.
local real diffOneP = 0.
local real diffTwoP = 0.
local real x = 0.
local real y = 0.
local real negate = 1.
//: Arc locals
local real arc = 0.
local real maxD = 0.
local real curD = 0.
local real maxH = 0.
//: Target locals
local boolean finished = false
//: ============================================================
//: Running events
//: ============================================================
call .onLoop()
//: ============================================================
//: Movement
//: ============================================================
if .target != null then
//: Our target is moveable, update positions
set .end.x = GetUnitX(.target)
set .end.y = GetUnitY(.target)
set .end.z = GetLocZ(.end.x, .end.y) + GetUnitFlyHeight(.target)
endif
//: Gonna check rotation!
//: Make sure both values are positive
set a = AngleBetweenVectors(.pos, .end)
if a < 0. then
set a = a + FULL_RADIAN
endif
if f < 0. then
set f = f + FULL_RADIAN
endif
if f != a and .autoFace then
//: Get the difference and check whether we
//: have to negate it or not.
set diff = a - f
if diff < -HALF_RADIAN then
set diff = -FULL_RADIAN +diff
elseif diff > HALF_RADIAN then
set diff = -(FULL_RADIAN -diff)
endif
//: If we have to turn more then we can, we
//: keep it in the area we are allowed to
if diff < 0. then
if diff > -once then
set diff = -once
endif
else
if diff > once then
set diff = once
endif
endif
//: Set the new facing
set f = f + diff
set .f = f
endif
//: Successfully rotated (or rotation is ignored) so lets move!
if f == a or not .moveCheckF or not .autoFace then
set x = .x + move * Cos(.f)
set y = .y + move * Sin(.f)
//: Update coordinates
set .last.x = x
set .last.y = y
set .last.z = z
set .x = x
set .y = y
if not .alive then
return
endif
//: Set the new pitch
set .p = PitchBetweenVectors(.pos, .last, DistanceBetweenVectors(.pos, .last))
//: Update time
set .time = .time + thistype.period
if .decay != 0. and .time >= .decay then
set .alive = false
set .active = false
call .onDecay()
endif
if not .alive then
return
endif
endif
//: ============================================================
//: Arcs
//: ============================================================
if .xyArc != 0. or .zArc != 0. then
set maxD = DistanceBetweenVectors(.start, .end)
set curD = DistanceBetweenVectors(.start, .pos)
endif
if .xyArc != 0. then
set arc = ParabolaZ2(.start.z, .end.z, maxD * .xyArc, maxD, curD)
//set .x = .x + arc * Cos(.f) * NINTY_DEGREE
//set .y = .y + arc * Sin(.f) * NINTY_DEGREE
endif
if .zArc != 0. then
set arc = maxD * .zArc
set .z = ParabolaZ2(.start.z, .end.z, arc, maxD, curD)
endif
//: ============================================================
//: Target checking
//: ============================================================
set finished = AreTwoPointsInRange(.x, .y, .end.x, .end.y, .range)
if .targetOnZ then
set finished = finished and .end.z <= .z + .height / 2 and .end.z >= .z - .height / 2
endif
if finished then
set .alive = false
set .active = false
call .onTarget()
return
endif
//: ============================================================
//: Target checking
//: ============================================================
call .enumChain()
if not .alive then
return
endif
//: ============================================================
//: Enumeration
//: ============================================================
call .checkGround()
if not .alive then
return
endif
call .enumDestructables()
if not .alive then
return
endif
call .enumUnits()
if not .alive then
return
endif
call .enumMissiles()
if not .alive then
return
endif
endmethod
public static method create takes unit theActor returns thistype
local thistype this = thistype.allocate()
local real x = GetUnitX(theActor)
local real y = GetUnitY(theActor)
local real z = GetLocZ(x, y) + GetUnitFlyHeight(theActor)
local real f = GetUnitFacing(theActor) * bj_DEGTORAD
set .facing = f
set .actor = theActor
set .start = vector.create(x, y, z)
set .pos = vector.create(x, y, z)
set .last = vector.create(x, y, z)
set .end = vector.create(0., 0., 0.)
call UnitAddAbility(theActor, RAVEN_FORM)
call UnitRemoveAbility(theActor, RAVEN_FORM)
set bj_lastCreatedMissile = this
call .addToIndex(this)
call .onCreate()
return this
endmethod
private static method onInit takes nothing returns nothing
local rect r = GetWorldBounds()
set thistype.MIN_X = GetRectMinX(r)
set thistype.MIN_Y = GetRectMinY(r)
set thistype.MAX_X = GetRectMaxX(r)
set thistype.MAX_Y = GetRectMaxY(r)
set r = null //: Yes, this would leak
endmethod
private method onDestroy takes nothing returns nothing
call thistype.removeFromIndex(this)
call .onDeath()
endmethod
endstruct
globals
integer bj_lastCreatedMissile = 0
endglobals
function CreateMissile takes unit theMissile returns Missile
return Missile.create(theMissile)
endfunction
endlibrary
module MissileStack
public static Table stack = 0
public static Table cache = 0
public static Table actors = 0
public static integer index = 0
public static timer runner = null
private boolean inStack = false
public static method addToIndex takes thistype this returns nothing
if not .inStack then
set .inStack = true
//call BJDebugMsg("Starting missile " + I2S(thistype.index))
set thistype.stack[thistype.index] = integer(this)
set thistype.cache[integer(this)] = thistype.index
set thistype.actors[GetHandleId(this.actor)] = integer(this)
set thistype.index = thistype.index +1
if thistype.index == 1 then
call TimerStart(thistype.runner, thistype.period, true, function thistype.runall)
endif
endif
endmethod
public static method removeFromIndex takes thistype this returns nothing
local integer i = thistype.cache[integer(this)]
local thistype top = 0
if .inStack then
//call BJDebugMsg("Ending missile " + I2S(i))
//call BJDebugMsg("Move " + I2S(thistype.index -1) + ">" + I2S(i))
set top = thistype.stack[thistype.index -1]
set thistype.stack[i] = top
set thistype.cache[integer(top)] = i
set thistype.actors[GetHandleId(.actor)] = i
call thistype.actors.flush(GetHandleId(.actor))
call thistype.stack.flush(thistype.index -1)
set thistype.index = thistype.index -1
if thistype.index == 0 then
call PauseTimer(thistype.runner)
endif
endif
endmethod
private static method runall takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= thistype.index
set thistype.self = thistype(thistype.stack[i])
if not thistype.self.alive then
call thistype.self.destroy()
set i = i -1
else
call thistype.self.run()
endif
set i = i +1
endloop
endmethod
public static method getMissileByUnit takes unit theUnit returns Missile
return thistype(thistype.actors[GetHandleId(theUnit)])
endmethod
private static method onInit takes nothing returns nothing
set thistype.stack = Table.create()
set thistype.cache = Table.create()
set thistype.actors = Table.create()
static if LIBRARY_TimerUtils then
set thistype.runner = NewTimer()
else
set thistype.runner = CreateTimer()
endif
endmethod
endmodule
module MissileChain
public boolean useChain = true
public real chainRange = 0.
private static group chainGroup = null
private static unit nextChain = null
public stub method chainHitFilter takes unit theUnit returns boolean
return not IsUnitInGroup(theUnit, .chainGroup)
endmethod
private static method enumChainUnit takes nothing returns boolean
local thistype this = thistype.self
local unit u = GetFilterUnit()
if .chainHitFilter(u) and .nextChain == null and not IsUnitInGroup(u, .chainGroup) then
set .nextChain = u
endif
return false
endmethod
method enumChain takes nothing returns nothing
if not .useChain then
return
endif
if LIBRARY_GroupUtils then
set thistype.chainGroup = NewGroup()
else
set thistype.chainGroup = CreateGroup()
endif
set .nextChain = null
call GroupEnumUnitsInRange(thistype.chainGroup, .x, .y, .chainRange, Condition(function thistype.enumChainUnit))
if .nextChain != null then
call GroupAddUnit(.chainGroup, .nextChain)
set .alive = true
set .active = true
call .launchTarget(.nextChain)
endif
endmethod
endmodule
module MissileLauncher
public method launch takes nothing returns nothing
set .start.x = .x
set .start.y = .y
set .start.z = .z
set .target = null
set .active = true
static if this.onLaunch.exists then
call .onLaunch()
endif
endmethod
public method launchTargetLocZ takes real x, real y, real z returns nothing
set .start.x = .x
set .start.y = .y
set .start.z = .z
set .end.x = x
set .end.y = y
set .end.z = z
call .launch()
endmethod
public method launchTargetLoc takes real x, real y returns nothing
call .launchTargetLocZ(x, y, GetLocZ(x, y))
endmethod
public method launchTargetVector takes vector theVector returns nothing
call .launchTargetLocZ(theVector.x, theVector.y, theVector.z)
endmethod
public method launchTarget takes unit theTarget returns nothing
local real x = GetUnitX(theTarget)
local real y = GetUnitY(theTarget)
local real z = GetLocZ(x, y) + GetUnitFlyHeight(theTarget)
call .launchTargetLocZ(x, y, z)
set .target = theTarget
endmethod
endmodule
module MissileDummy
private static unit dummy = null
public static method createFromDummy takes player p, real x, real y, real z, real f returns thistype
set thistype.dummy = CreateUnit(p, MISSILE_DUMMY, x, y, f)
call SetUnitFlyHeight(thistype.dummy, z, 0)
return thistype.create(thistype.dummy)
endmethod
public static method createFromDummyWithVector takes player p, vector theVector, real facing returns thistype
return thistype.createFromDummy(p, theVector.x, theVector.y, theVector.z - GetLocZ(theVector.x, theVector.y), facing)
endmethod
private method onDestroy takes nothing returns nothing
if GetUnitTypeId(.actor) == MISSILE_DUMMY then
call RemoveUnit(.actor)
endif
endmethod
endmodule
library MissileUtils
globals
constant real NINTY_DEGREE = 1.57079633 //: in radians
constant integer MISSILE_DUMMY = 'mssl'
constant real FULL_RADIAN = 2 * bj_PI
constant real HALF_RADIAN = bj_PI
constant integer RAVEN_FORM = 'Arav'
private location loc = Location(0, 0)
endglobals
function GetLocZ takes real x, real y returns real
call MoveLocation(loc, x, y)
return GetLocationZ(loc)
endfunction
function GetUnitZ takes unit u returns real
return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
function DistFromTwoPoints takes real x1, real y1, real x2, real y2 returns real
local real dx = x2 - x1
local real dy = y2 - y1
if dx < 0. then
set dx = dx * -1
endif
if dy < 0. then
set dy = dy * -1
endif
return dx + dy
endfunction
function DistFromTwoPointsWithZ takes real x1, real y1, real z1, real x2, real y2, real z2 returns real
local real dz = z2 - z1
if dz < 0. then
set dz = dz * -1
endif
return DistFromTwoPoints(x1, y1, x2, y2) + dz
endfunction
function AreTwoPointsInRange takes real x1, real y1, real x2, real y2, real range returns boolean
return DistFromTwoPoints(x1, y1, x2, y2) <= range + range
endfunction
function AreTwoPointsWithZInRange takes real x1, real y1, real z1, real x2, real y2, real z2, real range returns boolean
return DistFromTwoPointsWithZ(x1, y1, z1, x2, y2, z2) <= range + range
endfunction
function AreVectorsInRange takes vector first, vector second, real range returns boolean
return AreTwoPointsInRange(first.x, first.y, second.x, second.y, range)
endfunction
//: Thanks to The_Reborn_Devil <3
function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
return ((2*(y0+y1)-4*h)/(d*d))*x*x + ((y1-y0-((2*(y0+y1)-4*h)/(d*d))*d*d)/d)*x + y0
endfunction
//: Thanks to The_Reborn_Devil
function DistanceBetweenVectors takes vector first, vector second returns real
return SquareRoot((second.x - first.x)*(second.x - first.x) + (second.y - first.y)*(second.y - first.y))
endfunction
function AngleBetweenVectors takes vector first, vector second returns real
return Atan2(second.y - first.y, second.x - first.x)
endfunction
function PitchBetweenVectors takes vector first, vector second, real dist returns real
return Atan2(second.z - first.z, dist)
endfunction
function VectorFromPos takes real x, real y returns vector
return vector.create(x, y, GetLocZ(x, y))
endfunction
function VectorFromUnit takes unit u returns vector
return vector.create(GetUnitX(u), GetUnitY(u), GetUnitZ(u))
endfunction
endlibrary
library MissileGroup requires Missile
function interface doForMissiles takes Missile theMissile returns nothing
struct MissileGroup
private Table stack = 0
private Table cache = 0
private integer index = 0
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .stack = Table.create()
set .cache = Table.create()
set bj_lastCreatedMissileGroup = this
return this
endmethod
public method hasMissile takes Missile theMissile returns boolean
return this.cache.exists(integer(theMissile))
endmethod
public method addMissile takes Missile theMissile returns boolean
local boolean in = .hasMissile(theMissile)
if not in then
set .stack[.index] = integer(theMissile)
set .cache[integer(theMissile)] = .index
set .index = .index +1
endif
return not in
endmethod
public method removeMissile takes Missile theMissile returns boolean
local boolean in = .hasMissile(theMissile)
local integer i = .cache[integer(theMissile)]
if in then
set .stack[i] = .stack[.index -1]
set .cache[.stack[.index -1]] = i
call .cache.flush(.stack[.index])
call .stack.flush(i)
set .index = .index -1
endif
return in
endmethod
public method runFunc takes doForMissiles theFunc returns nothing
local integer i = 0
loop
exitwhen i >= .index
call theFunc.evaluate(Missile(.stack[i]))
set i = i +1
endloop
endmethod
public method flush takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= .index
call .removeMissile(Missile(.stack[i]))
set i = i +1
endloop
endmethod
private method onDestroy takes nothing returns nothing
call .flush()
endmethod
endstruct
function ForMissileGroupDo takes MissileGroup theGroup, doForMissiles theFunc returns nothing
call theGroup.runFunc(theFunc)
endfunction
function HasMissileGroupMissile takes MissileGroup theGroup, Missile theMissile returns boolean
return theGroup.hasMissile(theMissile)
endfunction
function MissileGroupAddMissile takes MissileGroup theGroup, Missile theMissile returns boolean
return theGroup.addMissile(theMissile)
endfunction
function MissileGroupRemoveMissile takes MissileGroup theGroup, Missile theMissile returns boolean
return theGroup.removeMissile(theMissile)
endfunction
function MissileGroupClear takes MissileGroup theGroup returns nothing
call theGroup.flush()
endfunction
function CreateMissileGroup takes nothing returns MissileGroup
return MissileGroup.create()
endfunction
globals
integer bj_lastCreatedMissileGroup = 0
endglobals
endlibrary
function interface onSpelLCastFunc takes nothing returns nothing
module MissileSpellHelper
public static onSpelLCastFunc onSpellCast = 0
private static method spellCastCheck takes nothing returns boolean
return GetSpellAbilityId() == thistype.SPELL_ID
endmethod
private static method doSpellCast takes nothing returns nothing
call thistype.onSpellCast.execute()
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
call TriggerAddCondition(t, Condition(function thistype.spellCastCheck))
call TriggerAddAction(t, function thistype.doSpellCast)
loop
exitwhen i >= bj_MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
set i = i +1
endloop
endmethod
endmodule
module MissileOrderHelper
public static trigger onOrder = null
private static method orderIssueCheck takes nothing returns boolean
return GetIssuedOrderId() == thistype.SPELL_ORDER
endmethod
public static method addOnOrder takes code onOrderFunc returns nothing
call TriggerAddAction(thistype.onOrder, onOrderFunc)
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
set thistype.onOrder = CreateTrigger()
call TriggerAddCondition(thistype.onOrder, Condition(function thistype.orderIssueCheck))
loop
exitwhen i >= bj_MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(thistype.onOrder, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
set i = i +1
endloop
endmethod
endmodule
module MissileUnitCollision
public boolean hitsUnits = true
public boolean hitsUnitsOnZ = false
private group enumedUnits = null
public method isUnitHit takes unit u returns boolean
return IsUnitInGroup(u, .enumedUnits)
endmethod
public method hitUnit takes unit u returns nothing
call GroupAddUnit(.enumedUnits, u)
call .onUnitHit(u)
endmethod
public method unhitUnit takes unit u returns nothing
call GroupRemoveUnit(.enumedUnits, u)
endmethod
stub method unitHitFilter takes unit theUnit returns boolean
return IsUnitEnemy(.actor, GetOwningPlayer(theUnit))
endmethod
private static method enumHitUnit takes nothing returns boolean
local thistype this = thistype.self
local unit u = GetFilterUnit()
local real z = 0.
local boolean collide = true
if IsUnitInGroup(u, .enumedUnits) or u == .actor then
set u = null
return false
endif
if .hitsUnitsOnZ then
set z = GetUnitZ(u)
set collide = z >= .z - .height /2 and z <= .z + .height /2
endif
if collide then
if .unitHitFilter(u) then
call .hitUnit(u)
endif
endif
set u = null
return false
endmethod
method enumUnits takes nothing returns nothing
if not .hitsUnits then
return
endif
if .enumedUnits == null then
static if LIBRARY_GroupUtils then
set .enumedUnits = NewGroup()
else
set .enumedUnits = CreateGroup()
endif
endif
call GroupEnumUnitsInRange(.enumedUnits, .x, .y, .range, thistype.unitFilter)
endmethod
endmodule
library MissileMissileCollision requires MissileGroup
module MissileMissileCollision
public boolean hitsMissiles = true
public boolean hitsMissilesOnZ = false
private MissileGroup enumedMissiles = 0
public method isMissileHit takes thistype theMissile returns boolean
return HasMissileGroupMissile(.enumedMissiles, theMissile)
endmethod
public method hitMissile takes thistype theMissile returns nothing
call MissileGroupAddMissile(.enumedMissiles, theMissile)
call .onMissileHit(theMissile)
endmethod
public method unhitMissile takes thistype theMissile returns nothing
call MissileGroupRemoveMissile(.enumedMissiles, theMissile)
endmethod
public stub method onMissileHit takes thistype theMissile returns nothing
set .alive = false
set .active = false
set theMissile.alive = false
set theMissile.active = false
return
endmethod
public stub method missileHitFilter takes thistype theMissile returns boolean
return theMissile.alive and theMissile.active
endmethod
method enumMissiles takes nothing returns nothing
local thistype that = 0
local real x1 = 0.
local real y1 = 0.
local real z1 = 0.
local real x2 = 0.
local real y2 = 0.
local real z2 = 0.
local integer i = thistype.cache[integer(this)] +1
local boolean collide = false
if not .hitsMissiles then
return
endif
if .enumedMissiles == 0 then
set .enumedMissiles = MissileGroup.create()
endif
loop
exitwhen i >= thistype.index or collide or not .alive
set that = thistype.stack[i]
if this.hitsMissiles and that.hitsMissiles and not HasMissileGroupMissile(.enumedMissiles, that) then
if .missileHitFilter(this, that) then
set x1 = this.x
set y1 = this.y
set x2 = that.x
set y2 = that.y
set collide = AreTwoPointsInRange(x1, y1, x2, y2, .range)
if collide and this.hitsMissilesOnZ then
set z1 = this.z
set z2 = that.z
set collide = collide and z1 + that.height / 2 <= z + this.height / 2
set collide = collide and z1 - that.height / 2 >= z - this.height / 2
endif
if collide and .alive and .active then
call .hitMissile(that)
endif
endif
endif
set i = i +1
endloop
endmethod
endmodule
endlibrary
module MissileGroundCollision
private boolean is = false
method checkGround returns nothing
local boolean now = .z <= GetLocZ(.x, .y)
if now != .is then
set .is = now
call ShowUnit(.actor, now)
if is then
call .onGround()
else
call .onFly()
endif
endif
endmethod
endmodule
module MissileDestructableCollision
public boolean hitsDestructables = true
public boolean hitsDestructablesOnZ = false
private static rect destPickRect = Rect(0., 0., 0., 0.)
private static method pickDest takes nothing returns boolean
local thistype this = thistype.self
local destructable d = GetFilterDestructable()
local real z = 0.
local boolean collide = true
if collide and .hitsDestructablesOnZ then
set z = GetLocZ(GetDestructableX(d), GetDestructableY(d)) + GetDestructableOccluderHeight(d) / 2
set collide = collide and z <= .z + .height / 2 and z >= .z - .height / 2
endif
if collide then
call .onDestructableHit(d)
endif
set d = null
return false
endmethod
method enumDestructables takes nothing returns nothing
if not .hitsDestructables then
return
endif
call SetRect(thistype.destPickRect, .x - .range / 2, .y - .range / 2, .x + .range / 2, .y + .range / 2)
call EnumDestructablesInRect(thistype.destPickRect, Condition(function thistype.pickDest), null)
endmethod
endmodule
library MissileTest requires Missile
struct TestMissile extends Missile
private static constant integer SPELL_ID = 'A000'
private static constant integer SPELL_ORDER = 851971 //: smart
//: Add features!
//implement MissileChain //: Do a chain spell with this feature
//implement MissileGroundCollision
implement MissileDestructableCollision
//implement MissileUnitCollision
//implement MissileMissileCollision
implement MissileLauncher
//: Add helpers!
//implement MissileSpellHelper
implement MissileOrderHelper
stub method unitHitFilter takes unit theUnit returns boolean
return .actor != theUnit and IsUnitEnemy(theUnit, GetOwningPlayer(.actor)) and GetUnitState(theUnit, UNIT_STATE_LIFE) > 0.
endmethod
method onCreate takes nothing returns nothing
//call IssueImmediateOrder(.actor, "stop")
call SetUnitPathing(.actor, false)
//call PauseUnit(.actor, true)
endmethod
method onUnitHit takes unit theUnit returns nothing
call BJDebugMsg("Hits unit: " + GetUnitName(theUnit))
call KillUnit(theUnit)
endmethod
stub method destructableHitFilter takes destructable theDestructable returns boolean
return GetDestructableLife(theDestructable) <= 0.
endmethod
method onDestructableHit takes destructable theDestructable returns nothing
call BJDebugMsg("Hits destructable: " + GetDestructableName(theDestructable))
call KillDestructable(theDestructable)
endmethod
private static method createMissile takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
//local vector end = VectorFromUnit(target)
local real x = GetOrderPointX()
local real y = GetOrderPointY()
local vector end = vector.create(x, y, GetLocZ(x, y))
local thistype this = thistype.getMissileByUnit(caster) //: Check if there is any valid instance
if this != 0 then
call .destroy()
endif
set this = thistype.create(caster)
//: --- Set up your stuff ---
set .s = 500. //: Speed
set .zArc = 0.25
set .decay = 5.
set .height = 100.
set .range = 50.
set .moveCheckF = true
set .targetOnZ = true
set .autoFace = true
//set .chainRange = 500.
set .turn = 1.
//: --- Advanced Stuff ---
//set .hitsUnitsOnZ = true
//set .hitsDestructablesOnZ = true
//set .hitsMissilesOnZ = true
//: --- Fire somewhere ---
call .launchTargetVector(end) //: Method from Missile_Core
endmethod
private method onDestroy takes nothing returns nothing
call SetUnitFlyHeight(.actor, 0., 0)
call SetUnitPathing(.actor, true)
//call PauseUnit(.actor, false)
endmethod
private static method onInit takes nothing returns nothing
call thistype.addOnOrder(function thistype.createMissile) //: Available with MissileSpellHelper
endmethod
endstruct
endlibrary
JASS:
library ArmorType requires AttackType
struct ArmorType
endstruct
function GetUnitArmorType takes unit theUnit returns AttackType
return 0
endfunction
function LoadDamageReduction takes AttackType att, ArmorType arm returns real
return 0.
endfunction
function SetDamageReduction takes AttackType att, ArmorType arm, real value returns nothing
return
endfunction
function GetTargetDamage takes unit theAttacker, unit theTarget, real theDamage returns real
local AttackType atttype = GetUnitAttackType(theAttacker)
local ArmorType armtype = GetUnitArmorType(theTarget)
local real amount = LoadDamageReduction(attype, armtype)
return theDamage * amount
endfunction
endlibrary
JASS:
//: ----------------------------------------------------------------------------------------------------------------------------
//: |
//: | Attack 0.0.1
//: | by Anachron
//: |
//: | ------------------------------------------------------------------------------
//: |
//: |
//: ----------------------------------------------------------------------------------------------------------------------------
library Attack initializer init requires AttackHandler, AttackType, ArmorType
private interface AttackHandler
stub method onStart takes nothing returns nothing
endmethod
stub method onRelease takes nothing returns nothing
endmethod
stub method onEnd takes nothing returns nothing
endmethod
endinterface
keyword Attack
globals
Attack ATTACK = 0 //: Current attack object, updated on fire, release and damage
endglobals
struct Attack extends AttackHandler
private static Table attacks = 0
AttackHandler handler = 0
AttackType typ = 0
unit attacker = null
boolean missed = false
boolean forced = false
boolean silent = false
public static method create takes integer unitType, AttackHandler theHandler, AttackType theType returns thistype
local thistype this = thistype.allocate()
set .attacks[unitType] = this
set .handler = theHandler.clone()
set .handler.attack = this
set .typ = theType.clone()
return this
endmethod
public static method apply takes unit theAttacker returns thistype
local thistype that = .attacks[GetUnitTypeId(theAttacker)].clone()
set that.attacker = theAttacker
return that
endmethod
public method fire takes nothing returns nothing
call .handler.fire()
call .onStart()
if not .silent then
call SetUnitAnimation(.attacker, "Attack")
endif
set ATTACK = this
set ATTACKER = .attacker
set ATTACKED = .handler.target
set ATTACKDAMAGE = 0. // Unknown yet
set ATTACKMISSED = .missed
set ATTACKFORCED = .forced
set ATTACKSILENT = .silent
// call Attackevents.onAttackFire()
endmethod
public method release takes nothing returns nothing
local real dmg = 0.
call .onRelease()
if (.missed and .forced) or not .missed then
set dmg = .handler.getDamage()
set ATTACK = this
set ATTACKER = .attacker
set ATTACKED = .handler.target
set ATTACKDAMAGE = dmg
set ATTACKMISSED = .missed
set ATTACKFORCED = .forced
set ATTACKSILENT = .silent
// call Attackevents.onAttackRelease()
call .handler.damage(dmg)
endif
endmethod
public method clear takes nothing returns nothing
call .onEnd()
endmethod
public method clone takes nothing returns nothing
endmethod
endstruct
function CreateTargetUnitAttack takes unit theAttacker, unit theTarget returns Attack
local Attack att = Attack.apply(theAttacker)
set att.handler.target = theTarget
return att
endfunction
function CreateTargetPosAttack takes unit theAttacker, real x, real y returns Attack
local Attack att = Attack.apply(theAttacker)
set att.handler.targX = x
set att.handler.targY = y
return att
endfunction
function DestroyAttack takes Attack att returns nothing
set att.handler.missile.range = 0. //: Prevent hits
set att.handler.missile.target = null //: Prevent remaining targeting & evaluating
set att.handler.missile.alive = false //: Prevent remaining actions
set att.handler.missile.active = false //: Mark missile as inactive
endfunction
endlibrary
JASS:
library AttackController requires Attack
globals
private constant integer ATTACK_SPELL_FAKE = 'Atfs'
private constant integer ATTACK_SPELL_REAL = 'Atrs'
constant real AGILITY_ASBONUS = 0.03
endglobals
struct AttackController
real cd = 0.
integer range = 0.
integer spell = 0
integer typ = 0
real cool = 0.
unit source = null
public static real period = 0.03175 //: minimum attack interval
public static thistype self = 0
implement AttackStack
public static method create takes integer theType, integer theRange, integer theCooldown returns thistype
local thistype this = thistype.allocate()
set .typ = theType
set .range = theRange
set .cd = theCooldown
return this
endmethod
public static method apply takes unit theUnit returns thistype
local thistype that = thistype.units[GetHandleId(theUnit)]
local thistype this = that.clone()
set .source = theSource
call .refresh()
return this
endmethod
public method attackTarget takes unit theTarget returns nothing
call UnitRemoveAbility(.source, .spell)
call UnitAddAbility(.source, ATTACKS_SPELL_FAKE)
call CreateTargetUnitAttack(.source, theTarget).fire()
endmethod
public method attackPoint takes real x, real y returns nothing
call UnitRemoveAbility(.source, .spell)
call UnitAddAbility(.source, ATTACKS_SPELL_FAKE)
call CreateTargetPosAttack(.source, x, y).fire()
endmethod
public method refresh takes nothing returns nothing
set .cool = 0.
call UnitRemoveAbility(.source, ATTACK_SPELL_FAKE)
call UnitAddAbility(.source, .spell)
call SetUnitAbilityLevel(.source, .spell, .range)
endmethod
private method run takes nothing returns nothing
local real check = 0.
if IsUnitType(.source, UNIT_TYPE_HERO) then
set check = GetHeroLevel(.source) * AGILITY_ASBONUS
endif
if .cool > 0. then
set .cool = .cool - thistype.PERIOD
if .cool <= check then
call .refresh()
endif
endif
endmethod
private static method checkAbility takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer a = GetSpellAbilityId()
local thistype att = thistype.units[GetHandleId(u)]
local real x = 0.
local real y = 0.
if a == att.spell and att != 0 then
if u != null then
call att.attackTarget(u)
else
call att.attackPoint(GetSpellTargetX(), GetSpellTargetY())
endif
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
call TriggerAddAction(t, function thistype.checkAbility)
loop
exitwhen i >= BJ_MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
set i = i +1
endloop
endmethod
endstruct
function isAttackRangeSupported takes real range returns boolean
if range <= 100. then
return R2I(range / 10) == range / 10
endif
if range <= 250. then
return R2I(range / 15) == range / 15
endif
if range <= 450. then
return R2I(range / 20) == range / 20
endif
if range <= 700. then
return R2I(range / 25) == range / 25
endif
if range <= 1000. then
return R2I(range / 30) == range / 30
endif
if range <= 1350. then
return R2I(range / 35) == range / 35
endif
if range <= 1750. then
return R2I(range / 40) == range / 40
endif
if range <= 2200. then
return R2I(range / 45) == range / 45
endif
if range <= 2700. then
return R2I(range / 50) == range / 50
endif
if range <= 3250. then
return R2I(range / 55) == range / 55
endif
if range <= 3850. then
return R2I(range / 60) == range / 60
endif
if range <= 4500. then
return R2I(range / 65) == range / 65
endif
if range <= 5200. then
return R2I(range / 70) == range / 70
endif
debug call BJDebugMsg(SCOPE_PREFIX + "ERROR: Range of " + R2S(range) + " is not supported!")
return false
endfunction
function ResetAttackCooldown takes unit theUnit returns nothing
call AttackController(AttackController.stack[GetHandleId(theUnit)]).refresh()
endfunction
function onAttackFire takes nothing returns nothing
//: We can use all of this variables to do our stuff
//: ATTACKER (unit), ATTACKED (unit), ATTACKMISSED (boolean)
//: ATTACKFORCED (boolean), ATTACKSILENT (boolean) and ATTACK (Attack)
call BJDebugMsg("A " + GetUnitName(ATTACKER) + " has attacked a " + GetUnitName(ATTACKED))
call BJDebugMsg("Dmgrange: " + R2S(ATTACK.handler.getMinDamage()) + " - " + R2S(ATTACK.handler.getMaxDamage())) //: This is so cool!
call BJDebugMsg("Dealt: " + R2S(ATTACKDAMAGE)) //: How do you like this? :)
endfunction
private function init takes nothing returns nothing
local Attack att = 0
local unit mage = null
local unit enemy = null
local AttackController ac = 0
//: Footman
set att = Attack.create('hfoo', ATTACK_HANDLER, ATTACK_TYPE_HERO)
set att.handler.instant = true
set att.handler.dmgBase = 5
set att.handler.dmgDiceInc = 2
set att.handler.dmgDiceCount = 3
//: Archmage
set att = Attack.create('Hamg', ATTACK_HANDLER, ATTACK_TYPE_HERO)
set att.handler.primary = PRIMARY_ATTRIBUTE_INTELLIGENCE //: For heroes!
set att.handler.instant = false
set att.handler.dmgBase = 25
set att.handler.dmgDiceInc = 3
set att.handler.dmgDiceCount = 4
//: Missile Data
set att.handler.missile.s = 500.
set att.handler.missile.sfx = "Just//Some//Crazy//Effect.mdx"
set att.handler.missile.turn = 1.
//: Mortar
set att = Attack.create('hmtr', ATTACK_HANDLER, ATTACK_TYPE_SIEGE)
set att.handler.instant = false
set att.handler.dmgBase = 12
set att.handler.dmgDiceInc = 2
set att.handler.dmgDiceCount = 3
//: Splash Data
set att.handler.splashRanges[0] = 250. //: Up to 250 AOE deals 100% dmg
set att.handler.splashPercents[0] = 1.
set att.handler.splashRanges[1] = 500. //: Up to 500 AOE deals 50% dmg
set att.handler.splashPercents[1] = 0.5
//: Missile Data
set att.handler.missile.s = 600.
set att.handler.missile.sfx = "Just//Some//Crazy//Effect.mdx"
set att.handler.missile.turn = 1.
set att.handler.missile.zArc = 0.45
//: Lets auto trigger attacks and use attack ranges, as well as cooldowns
//: Every range has to be a level :/ [Sadly]
//: The range is the aquireration range of the attack targets
set ac = AttackController.create('hfoo', 120, 2.65)
set ac = AttackController.create('Hamg', 500, 2.00)
set mage = CreateUnit(Player(0), 'Hamg', 0., 0., 0.)
set enemy = CreateUnit(Player(PLAYER_NEUTRAL_AGRESSIVE), 'hfoo', 250., 250., 0.)
//: Let the magic happen!
set ac = AttackController.apply(mage)
set ac = AttackController.apply(enemy)
call TriggerRegisterUnitAttackReleaseEvent(onAttackFire)
endfunction
endlibrary
JASS:
library AttackEvents
private interface function AttackEventHandler takes nothing returns nothing
function TriggerRegisterUnitAttackFireEvent takes AttackEventHandler func returns nothing
endfunction
function TriggerRegisterUnitAttackReleaseEvent takes AttackEventHandler func returns nothing
endfunction
function TriggerRegisterUnitAttackDamageEvent takes AttackEventHandler func returns nothing
endfunction
endlibrary
JASS:
library AttackHandler initializer init requires AttackMissile, AttackEvents
globals
constant integer PRIMARY_ATTRIBUTE_AGILITY = 1
constant integer PRIMARY_ATTRIBUTE_INTELLIGENCE = 2
constant integer PRIMARY_ATTRIBUTE_STRENGTH = 3
constant real PRIMARY_ATTRIBUTE_DMGBONUS = 1.
endglobals
globals
unit ATTACKER = null //: The attacker unit. Should never be null
unit ATTACKED = null //: The target unit. Can be null if invalid attack or ground is target
real ATTACKDAMAGE = 0. //: Will be zero on fire, but on release and damage set
boolean ATTACKMISSED = false //: Flag (bool) whether attack has missed or not
boolean ATTACKFORCED = false //: Flag (bool) whether attack is forced to deal dmg (even with miss) or not
boolean ATTACKSILENT = false //: Flag (bool) whether attack is silent (does not show animation) or not
endglobals
struct AttackHandler
Attack attack = 0
AttackMissile missile = 0
integer primary = 0 //: Only if hero
boolean instant = false
unit target = null
real targX = 0.
real targY = 0.
real splash = 0.
real array splashRanges [5]
real array splashPercents [5]
real dmgBase = 0.
real dmgDiceInc = 0.
integer dmgDiceCount = 0
static group aoegroup = CreateGroup()
static real aoesplashdmg = 0.
static thistype cur = 0
public static method create takes AttackMissile theMissile returns thistype
local thistype this = thistype.allocate()
set .missile = theMissile.clone()
set .missile.handler = this
return this
endmethod
stub method splashFilter takes unit u returns boolean
return true
endmethod
private method damageSingle takes unit target, real dmg returns nothing
//set dmg = GetTargetDamage(.attacker, target, dmg)
call UnitDamageTarget(.attack.attacker, target, dmg, .attack.typ.dmgType, .attack.typ.wpnType, .attack.typ.ranged, .attack.typ.attack)
set ATTACK = this.attack
set ATTACKER = .attack.attacker
set ATTACKED = target
set ATTACKDAMAGE = dmg
set ATTACKMISSED = .attack.missed
set ATTACKFORCED = .attack.forced
set ATTACKSILENT = .attack.silent
// call Attackevents.onAttackDamage()
endmethod
private static method damageAOETarget takes nothing returns boolean
local unit u = GetFilterUnit()
if not IsUnitInGroup(u, thistype.aoegroup) then
if thistype.cur.splashFilter.evaluate(u) then
call .damageSingle(u, thistype.aoesplashdmg)
set u = null
return true
endif
endif
set u = null
return false
endmethod
private method damageAOE takes real dmg returns nothing
local integer i = 0
call GroupClear(thistype.aoegroup)
set thistype.cur = this
if .target != null then
set .targX = GetUnitX(.target)
set .targY = GetUnitY(.target)
endif
loop
exitwhen i >= 5
if .splashRanges[i] > 0. then
set thistype.aoesplashdmg = dmg * .splashPercents[i]
call GroupEnumUnitsInRange(thistype.aoegroup, .targX, .targY, .splashRanges[i], Condition(function thistype.damageAOETarget));
endif
set i = i +1
endloop
endmethod
public method damage takes real dmg returns nothing
if IsUnitType(.attack.attacker, UNIT_TYPE_HERO) then
if .primary == PRIMARY_ATTRIBUTE_AGILITY then
set dmg = dmg + GetHeroAgi(.attack.attacker, true)
elseif .primary == PRIMARY_ATTRIBUTE_INTELLIGENCE then
set dmg = dmg + GetHeroInt(.attack.attacker, true)
elseif .primary == PRIMARY_ATTRIBUTE_STRENGTH then
set dmg = dmg + GetHeroStr(.attack.attacker, true)
endif
endif
if .splashRanges[0] == 0 then
call .damageSingle(.attack.target, dmg)
else
call .damageAOE(dmg)
endif
endmethod
public method getMinDamage takes nothing returns real
local real dmg = .dmgBase
if IsUnitType(.attack.attacker, UNIT_TYPE_HERO) then
if .primary == PRIMARY_ATTRIBUTE_AGILITY then
set dmg = dmg + GetHeroAgi(.attack.attacker, true)
elseif .primary == PRIMARY_ATTRIBUTE_INTELLIGENCE then
set dmg = dmg + GetHeroInt(.attack.attacker, true)
elseif .primary == PRIMARY_ATTRIBUTE_STRENGTH then
set dmg = dmg + GetHeroStr(.attack.attacker, true)
endif
endif
return dmg
endmethod
public method getMaxDamage takes nothing returns real
return .getMinDamage() + .dmgDiceInc * .dmgDiceCount
endmethod
public method getDamage takes nothing returns real
return .getMinDamage() + .dmgDiceInc * GetRandomInt(0, .dmgDiceCount)
endmethod
public method clear takes nothing returns nothing
call .attack.clear()
call .onClear()
endmethod
public method fire takes nothing returns nothing
if .instant then
call .release()
else
if this.target != null then
call .missile.launchTarget(.attack.target)
else
call .missile.launchTargetLoc(.targX, .targY)
endif
endif
endmethod
public method release takes nothing returns nothing
call .attack.release()
endmethod
public method clone takes nothing returns nothing
local thistype that = thistype.allocate()
set that.missile = .missile.clone()
set that.instant = .instant
set that.target = .target
set that.targX = .targX
set that.targY = .targY
return that
endmethod
endstruct
globals
AttackHandler ATTACK_HANDLER
endglobals
private function init takes nothing returns nothing
set ATTACK_HANDLER = AttackHandler.create(ATTACK_MISSILE)
endfunction
endlibrary
JASS:
library AttackMissile requires Missile
struct AttackMissile extends Missile
AttackHandler handler = 0
method onDeath takes nothing returns nothing
call .handler.clear()
endmethod
method onTarget takes nothing returns nothing
call .handler.release()
endmethod
endstruct
globals
AttackMissile ATTACK_MISSILE = 0
endglobals
private function init takes nothing returns nothing
set ATTACK_MISSILE = AttackMissile.create()
endfunction
endlibrary
JASS:
module AttackStack
public static Table stack = 0
public static Table cache = 0
public static Table types = 0
public static integer index = 0
public static timer runner = null
private boolean inStack = false
public static method addToIndex takes thistype this returns nothing
if not .inStack then
set .inStack = true
set thistype.stack[thistype.index] = integer(this)
set thistype.cache[integer(this)] = thistype.index
set thistype.types[this.typ] = this
set thistype.index = thistype.index +1
if thistype.index == 1 then
call TimerStart(thistype.runner, thistype.period, true, function thistype.runall)
endif
endif
endmethod
public static method removeFromIndex takes thistype this returns nothing
local integer i = thistype.cache[integer(this)]
local thistype top = 0
if .inStack then
set top = thistype.stack[thistype.index -1]
set thistype.stack[i] = top
set thistype.cache[integer(top)] = i
call thistype.types.flush(GetHandleId(.source))
call thistype.stack.flush(thistype.index -1)
set thistype.index = thistype.index -1
if thistype.index == 0 then
call PauseTimer(thistype.runner)
endif
endif
endmethod
private static method runall takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= thistype.index
set thistype.self = thistype(thistype.stack[i])
call thistype.self.run()
set i = i +1
endloop
endmethod
private static method onInit takes nothing returns nothing
set thistype.stack = Table.create()
set thistype.cache = Table.create()
set thistype.units = Table.create()
static if LIBRARY_TimerUtils then
set thistype.runner = NewTimer()
else
set thistype.runner = CreateTimer()
endif
endmethod
endmodule
JASS:
library AttackType initializer init
struct AttackType
damagetype dmgType = null
weapontype wpnType = null
boolean ranged = true
boolean attack = true
endstruct
function GetUnitAttackType takes unit theUnit returns AttackType
return 0
endfunction
globals
AttackType ATTACK_TYPE_HERO = 0
AttackType ATTACK_TYPE_SIEGE = 0
endglobals
private function init takes nothing returns nothing
set ATTACK_TYPE_HERO = AttackType.create()
set ATTACK_TYPE_HERO.dmgType = DAMAGE_TYPE_HERO
set ATTACK_TYPE_HERO.wpnType = WEAPON_TYPE_WHOKNOWS
set ATTACK_TYPE_SIEGE = AttackType.create()
set ATTACK_TYPE_SIEGE.dmgType = DAMAGE_TYPE_SIEGE
set ATTACK_TYPE_SIEGE.wpnType = WEAPON_TYPE_WHOKNOWS
endfunction
endlibrary
Please give credit to me, because this was pretty much work.