KS beta - by emjlr3
Used as a global knockback system for all applications in a map. Using a users specified physical values for the environment and units, knocks back targeted units using simple controllable values.
JASS Script
JASS:
library KS initializer Init, needs PUI
//******************************************************************************************
//*
//* Knockback System beta - By emjlr3
//*
//* Used as a global knockback system for all applications in a map. Using a users
//* specified physical values for the environment and units, knocks back targeted
//* units using simple controllable values.
//*
//* Requires:
//* - "PUI" trigger copied to your map, if not already done so
//* - A vJASS preprocessor
//*
//******************************************************************************************
//******************************************************************************************
//*
//* Documentation:
//*
//* Function List:
//* KS_KnockbackUnit(unit to knock, real speed to knock at, real angle to knock at, real mass of knocker)
//* * returns a struct of type KS_Dat if the unit had previously been moving
//* * contains the real velocity/s (vel) and real angle (ang) in radians of the updated knockback
//* * these can be used to update the movement of missiles, other units, etc.
//* * this struct is cleaned after a ~1s wait, so it must be utilized within that time frame
//*
//* KS_GetUnitMass(unit)
//* * returns the real mass of the specified unit
//* * units without a specified mass are given a default value of 100.
//* * when units die, their mass value of their index is returned to the default value
//*
//* KS_SetUnitMass(unit, real mass)
//* * sets the real mass of the specified unit to the input value
//* * masses of units should be set when they enter the map, or before they are used in this system
//*
//* Usage:
//* Call KS_KnockbackUnit for the unit you wish to knockback. The speed and mass input determine
//* the speed at which the unit will slide, and the mass of the unit sliding along with your
//* gravity and friction values determine the acceleration rate. The inputed values for speed and
//* mass should be greater then 0, and your angle should be in radians. This allows for limitless, completely
//* realistic movement, however, given the nature of the inputs, you may have to play around with
//* the values a bit to find the desired movement and acceleration speeds. If the unit you wish to knockback
//* is already doing so, an inelastic collision will occur between the knocker and knockie. When this
//* happens, KS_KnockbackUnit returns a struct containing all the data needed to update the movement
//* of the knocker unit/missile/etc. Remember, you must use the struct within a ~1s timer frame.
//*
//* Call KS_SetUnitMass (KS_SUM) to set the mass of a unit. This determines how it interacts with others when they
//* collide. This also determines the speed at which it accelerates. Units who have not been given a
//* mass by the user will default to a value of 100. All masses should be greater then 0.
//*
//* Call KS_GetUnitMass (KS_GUM) to get the mass of a unit. This can allow you to alter masses given what they currently are,
//* input an accurate mass when knocking back a unit, etc.
//*
//* Pros:
//* * Removes the need to write such a system yourself, or trigger all knockbacks each time one is needed
//* * Allows for dynamic, realistic movement and collision, as well as effects from environment options
//* * Completely MUI, lagless, and realistic
//* * Few inputs allow for easy usage
//* * Includes warning messages when inputs are invalid
//*
//* Cons:
//* * Can become tedious to create the ideal knockback speed or acceleration
//* * Does not yet contain collision detection for sliding units
//* * Does not yet contain terrain change height detection
//* * You did not write it yourself, so unless you are adept in JASS and basic physics and geometry, you
//* cannot understand/edit the inner workings yourself
//*
//* Thanks to:
//* * Acehart - for his geometry help which finally got my collisions working properly
//* * Cohadar - for his knockback spell I wrote which originally got me interested in creating this
//* as well as his PUI system
//*
//******************************************************************************************
// Configuration Options:
globals
private constant real Friction = .7 // Friction between sliding units and the ground (>0.)
private constant real Gravity = 9.8 // Gravity in your WC3 world (distance/s, >0.)
private constant real Interval = .03 // Interval length for periodic timer callback
private constant real Stop_Velocity = .1 // Speed/interval at which units stop sliding
private constant boolean Fight = false // Whether units can fight against slides
private constant boolean Kill_Trees = true // Whether units kill trees as they slide
private constant string Sfx = "SlideDust.mdx" // Effect created on sliding units
endglobals
// NO TOUCHING PAST THIS POINT, UNLESS YOU PAIN FOR DEATH !!!
// Global struct to pass updated data to users
public struct Dat
real vel = 0.
real ang = 0.
endstruct
// Needed struct
private struct data
unit u = null
real vel = 0.
real accel = 0.
real ang = 0.
real cos = 0.
real sin = 0.
effect sfx = null
endstruct
globals
// Stores units mass and whether they are sliding
public real array Mass
public group Sliding = CreateGroup()
// Timer callbacks ran every second
private real Runs = 1./Interval
// Rect for the killing of trees
private rect R
// Data for looping through active sliding units
private timer Timer = CreateTimer()
private integer Total = 0
private data array Data
endglobals
// Recycle the mass of units
private function Die takes nothing returns nothing
local integer pui = GetUnitIndex(GetTriggerUnit())
set Mass[pui] = 0.
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(trig,function Die)
// Create tree killing rect
set R = Rect(0.,0.,250.,250.)
endfunction
// Set the mass of a unit, affects collision effects
public function SUM takes unit u, real mass returns nothing
local integer pui = GetUnitIndex(u)
set Mass[pui] = mass
endfunction
public function SetUnitMass takes unit u, real mass returns nothing // BJ, easier to remember for users
if u==null or mass<=0. then
call BJDebugMsg("KS Warning: Invalid input in SetUnitMass")
return
endif
call SUM(u,mass)
endfunction
// Get the mass of a unit, default mass = 100.
public function GUM takes unit u returns real
local integer pui = GetUnitIndex(u)
if Mass[pui]==0. then
return 100.
endif
return Mass[pui]
endfunction
public function GetUnitMass takes unit u returns real // BJ, easier to remember for users
if u==null then
call BJDebugMsg("KS Warning: Invalid input in GetUnitMass.")
return 0.
endif
return GUM(u)
endfunction
// Kill trees in an area 100. around the unit
private function KillTrees_Child takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
private function KillTrees takes real x, real y returns nothing
// Ever thought to do it like this?? No more creating and destroying rects each time
call MoveRectTo(R,x,y)
call EnumDestructablesInRect(R,null,function KillTrees_Child)
endfunction
// Timer callback, which loops through active sliding units
private function KnockbackUnit_Child takes nothing returns nothing
local data d
local integer i = 1
local real x
local real y
loop
exitwhen i > Total
set d = Data<i>
if d.vel>Stop_Velocity then // Still moving fast enough to continue sliding
// Slide to new x and y spots
set x = GetUnitX(d.u)+(d.vel/Runs)*d.cos
set y = GetUnitY(d.u)+(d.vel/Runs)*d.sin
if Fight then
call SetUnitX(d.u,x)
call SetUnitY(d.u,y)
else
call SetUnitPosition(d.u,x,y)
endif
if Kill_Trees then
call KillTrees(GetUnitX(d.u),GetUnitY(d.u))
endif
// Vo - a = Vnew
set d.vel = d.vel - d.accel
else // Its all over for this unit
call DestroyEffect(d.sfx)
call GroupRemoveUnit(Sliding,d.u)
call d.destroy()
set Data<i> = Data[Total]
set Total = Total - 1
if Total==0 then
call PauseTimer(Timer)
endif
set i = i - 1
endif
set i = i + 1
endloop
endfunction
private function RemoveStruct takes Dat dd returns nothing
call TriggerSleepAction(1.)
call dd.destroy()
endfunction
// Start a knockback for a unit, angle is in radians, mass/speed>0.
public function KnockbackUnit takes unit u, real speed, real angle, real mass returns Dat
local data d
local Dat dd
local integer pui = GetUnitIndex(u)
local boolean b = IsUnitInGroup(u,Sliding)
local real m = GUM(u)
local integer i = 1
local real theta
local real velx1
local real vely1
local real velx2
local real vely2
local real new_ang
local real new_vel
if u==null or mass<=0. or speed<=0. then
call BJDebugMsg("KS Warning: Invalid input in KnockbackUnit")
return 0
endif
if b==false then
set d = data.create()
set d.u = u
// Conservation of momentum, m1*v1 = m2*v2
set d.vel = (mass*speed) / m
// Fnet/m = a, the net force is the force of friction on the unit
set d.accel = (m*9.8*Friction) / m
set d.ang = angle
set d.cos = Cos(angle)
set d.sin = Sin(angle)
set d.sfx = AddSpecialEffectTarget(Sfx,u,"origin")
if Total==0 then
call TimerStart(Timer, Interval, true, function KnockbackUnit_Child)
endif
set Total = Total + 1
set Data[Total] = d
call GroupAddUnit(Sliding,u)
return 0
else
loop
set d = Data<i>
exitwhen d.u==u or i>Total
endloop
if i>Total then
call BJDebugMsg("KS Warning: This unit should be sliding but it is not, please report this error to emjlr3, with the replay and map.")
return 0
endif
// X and Y components of velocities
set theta = ModuloReal(d.ang*bj_RADTODEG,90.)
set vely1 = Sin(theta)*speed
set velx1 = Cos(theta)*speed
set theta = ModuloReal(angle*bj_RADTODEG,90.)
set vely2 = Sin(theta)*d.vel
set velx2 = Cos(theta)*d.vel
// Conservation of momentum
set velx1 = ((mass*velx1)+(m*velx2))/(mass+m)
set vely1 = ((mass*vely1)+(m*vely2))/(mass+m)
// Determine new velocity and angle from X and Y components
set new_vel = SquareRoot(velx1*velx1 + vely1*vely1)
set new_ang = Atan(vely1/velx1)
// Store new values to the sliding units struct
set d.ang = new_ang
set d.cos = Cos(d.ang)
set d.sin = Sin(d.ang)
set d.vel = new_vel
// Return said values to the user so he can use it at his end
set dd = Dat.create()
set dd.ang = new_ang
set dd.vel = d.vel
call RemoveStruct.execute(dd)
return dd
endif
endfunction
endlibrary</i></i></i>
Release Notes:
beta
- No longer needs ABC or ABCT
- Fixed inelastic collision bugs
- Fixed currently sliding bugs
- KS_Dat now contains the angle (ang) in radians, as opposd to the cos and sin of this
- Updated error messages to contain more relevant information
- Fixed tree destruction bugs
alpha
- initial release