• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

[JASS] (vJass) CustomAttack & CustomMissile

Status
Not open for further replies.
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.


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.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Why don't you actually submit them, instead of posting them to the Triggers & Scripts forum - I hardly think that this is where they belong, not to mention you're kind of expressing through the whole procedure by making them public via a thread.

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.

Oh; I see why. Because you're too lazy to finish it but you want people to give you props for it. Okay then, continue.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well you can use WINE to operate Windows programs if you're running some Linux OS, otherwise it isn't very hard to get your hands on a copy of Windows 7 and either do a dual-boot with boot-camp in OS X or just install it fresh.
 
Status
Not open for further replies.
Top