- Joined
- Jun 26, 2019
- Messages
- 318
What's the best tutorial I can find that will help me to learn on how to adding configurations and conditions to this system? I have no one that can help me out with this system. I guess I will have to be on my own, but I need help finding the good tutorials that work with this system...
JASS:
//TESH.scrollpos=291
//TESH.alwaysfold=0
library AirplaneSystem requires ListModule, optional AutoIndex
/*
.~@ FINGOLFINS AIRPLANE MOVEMENT SYSTEM @~.
INSTRUCTIONS:
- Implement AutoIndex as specified in its description
- Create your airplane units as you would with any normal unit
- Set the turn speed of your airplanes to something below 0.1 (use shift-click)
- Register plane types by adding this line to the initPlaneTypes function:
call registerPlaneType('xxxx')
where 'xxxx' is the id of your airplane.
*/
//=======================//
//======= GLOBALS =======//
//=======================//
globals
private constant real UPDATE_INTERVAL = 0.03125 //I SUBMIT TO THIS FOLLY
private constant real ORDER_INTERVAL = 1 // HOW OFTEN ORDERS ARE REFRESHED
private constant real DEFAULT_FACING = 0
private constant integer MAX_PLAYERS = 24
//LOWER NUMBERS = LESS INERTIA. SET TO ZERO TO REMOVE INERTIA COMPLETELY. THIS VALUE MUST BE LOWER THAN 1!
private constant real AIR_FRICTION = 0.9
//A CONSTANT MULTIPILER TO THE UNIT SPEED, TO CIRCUMVENT THE HARDCODED SPEED LIMIT OF WC3.
private constant real SPEED_FACTOR = 4.0
//MAX DISTANCE TO MAP BORDER WHERE PLANES CAN TRAVEL
private constant real BORDER_WIDTH = 0
//MAP BOUNDS
private real MAP_MAX_X
private real MAP_MAX_Y
private real MAP_MIN_X
private real MAP_MIN_Y
private constant integer ORDER_SMART = OrderId("smart")
private constant integer ORDER_MOVE = OrderId("move")
private constant integer ORDER_ATTACK = OrderId("attack")
private constant integer TYPES_KEY = StringHash("Airplane Types")
private constant integer INSTANCES_KEY = StringHash("Airplane Instances")
private hashtable Hash = InitHashtable()
endglobals
native UnitAlive takes unit id returns boolean
//=======================//
//===== SYSTEM CODE =====//
//=======================//
function IsUnitPlane takes unit whichunit returns boolean
return HaveSavedInteger(Hash, TYPES_KEY, GetUnitTypeId(whichunit))
endfunction
private function RegisterPlaneType takes integer unitid returns nothing
call SaveInteger(Hash, TYPES_KEY, unitid, 1)
endfunction
private function InitPlaneTypes takes nothing returns nothing
//REGISTER YOUR PLANE TYPES LIKE THIS
call RegisterPlaneType('jetf')
call RegisterPlaneType('bomb')
call RegisterPlaneType('rdra')
endfunction
struct Plane
implement List
unit plane = null
unit target = null
real vx = 0
real vy = 0
real tx = 0
real ty = 0
real speed = 0
integer order = 0
private static timer t1
private static timer t2
private static method get takes unit whichunit returns thistype
return LoadInteger(Hash, INSTANCES_KEY, GetHandleId(whichunit))
endmethod
method onDestroy takes nothing returns nothing
//DON'T GIVE ME CRAP ABOUT USING onDestroy(), IT POSES NO PERFORMANCE ISSUE IN THIS CASE.
call .listRemove()
call RemoveSavedInteger(Hash, INSTANCES_KEY, GetHandleId(.plane))
if .count == 0 then
call PauseTimer(.t1)
call PauseTimer(.t2)
endif
endmethod
private static method updateOrder takes nothing returns nothing
local thistype this = .first
local thistype temp
local real x
local real y
if .count == 0 then
call PauseTimer(.t1)
call PauseTimer(.t2)
endif
loop
exitwhen this == 0
set temp = .next
if GetUnitTypeId(.plane) == 0 then
//DESTROY THE STRUCT WHEN THE PLANE NO LONGER EXISTS
call .destroy()
else
if .target != null then
if UnitAlive(.target) == false or GetUnitTypeId(.target) == 0 then
set .target = null
set .tx = GetUnitX(.plane)
set .ty = GetUnitY(.plane)
set .order = ORDER_ATTACK
else
call IssueTargetOrderById(.plane, .order, .target)
endif
else
if .order == ORDER_MOVE or .order == ORDER_SMART then
set x = GetUnitX(.plane)
set y = GetUnitY(.plane)
if (x - .tx)*(x - .tx) + (y - .ty)*(y - .ty) < 10000 then
//ALLOW THE UNIT TO ATTACK ONCE IT HAS REACHED ITS DESTINATION
set .order = ORDER_ATTACK
endif
endif
call IssuePointOrderById(.plane, .order, .tx, .ty)
endif
endif
set this = temp
endloop
endmethod
private static method updatePosition takes nothing returns nothing
local thistype this = .first
local real x = 0
local real y = 0
local real f = 0
loop
exitwhen this == 0
set f = GetUnitFacing(.plane)*bj_DEGTORAD
//ADD FRICTION FIRST INCASE IT IS ZEROED
set .vx = (.vx * AIR_FRICTION) + .speed * Cos(f)
set .vy = (.vy * AIR_FRICTION) + .speed * Sin(f)
set x = GetUnitX(.plane)+.vx
set y = GetUnitY(.plane)+.vy
//ENFORCE BOUNDS
if x > MAP_MAX_X then
set x = MAP_MAX_X
elseif x < MAP_MIN_X then
set x = MAP_MIN_X
endif
if y > MAP_MAX_Y then
set y = MAP_MAX_Y
elseif y < MAP_MIN_Y then
set y = MAP_MIN_Y
endif
call SetUnitX(.plane, x)
call SetUnitY(.plane, y)
set this = .next
endloop
endmethod
private static method onOrderTarget takes nothing returns boolean
local thistype this = thistype.get(GetTriggerUnit())
if this == 0 then
return false
endif
set .target = GetOrderTargetUnit()
set .tx = GetUnitX(.target)
set .ty = GetUnitY(.target)
set .order = GetIssuedOrderId()
return false
endmethod
private static method onOrderPoint takes nothing returns boolean
local real x = GetOrderPointX()
local real y = GetOrderPointY()
local thistype this = thistype.get(GetTriggerUnit())
if this == 0 then
return false
endif
set .tx = x
set .ty = y
set .target = null
set .order = GetIssuedOrderId()
return false
endmethod
static method create takes unit whichunit returns thistype
local thistype this = thistype.allocate()
call .listAdd()
call SaveInteger(Hash, INSTANCES_KEY, GetHandleId(whichunit), this)
//=========SOME MATH TRIVIA!=========//
//The acceleration is given by the following differential equation:
// v(t)' = a = v(t) - v(t)*(1-R), where R = AIR_FRICTION
//And it's solution:
//v(t) = (a/(1-R)*(1 + e^-(1-R)t)
//Calculating the limit when (t -> infinity) gives:
//vmax = a/(1-R)
//vmax*(1-R) = a (in this case 'vmax' is known and 'a' is unknown)
//===========END OF TRIVIA===========//
set .plane = whichunit
set .speed = GetUnitDefaultMoveSpeed(.plane)*(1-AIR_FRICTION)*UPDATE_INTERVAL*SPEED_FACTOR
set .tx = GetUnitX(.plane) + 500 * Cos(DEFAULT_FACING*bj_DEGTORAD)
set .ty = GetUnitY(.plane) + 500 * Sin(DEFAULT_FACING*bj_DEGTORAD)
set .order = ORDER_SMART
call SetUnitMoveSpeed(.plane, 0.01)
call SetUnitFacing(.plane, DEFAULT_FACING)
call SetUnitFlyHeight(.plane, 0, 0)
call SetUnitFlyHeight(.plane, GetUnitDefaultFlyHeight(.plane), GetUnitDefaultFlyHeight(.plane)/3)
if .count == 1 then
call TimerStart(.t1, UPDATE_INTERVAL, true, function thistype.updatePosition)
call TimerStart(.t2, ORDER_INTERVAL, true, function thistype.updateOrder)
endif
return this
endmethod
private static method onUnitEntersMap takes nothing returns boolean
call thistype.onIndex(GetFilterUnit())
return false
endmethod
private static method onIndex takes unit u returns nothing
if IsUnitPlane(u) then
call .create(u)
endif
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local rect r = GetWorldBounds()
local region map = CreateRegion()
local group g = CreateGroup()
set MAP_MAX_X = GetRectMaxX(r)-BORDER_WIDTH
set MAP_MAX_Y = GetRectMaxY(r)-BORDER_WIDTH
set MAP_MIN_X = GetRectMinX(r)+BORDER_WIDTH
set MAP_MIN_Y = GetRectMinY(r)+BORDER_WIDTH
set Plane.t1 = CreateTimer()
set Plane.t2 = CreateTimer()
call InitPlaneTypes()
loop
exitwhen i > MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
set i = i+1
endloop
call TriggerAddCondition(t, Condition(function Plane.onOrderTarget))
set t = CreateTrigger()
set i = 0
loop
exitwhen i > MAX_PLAYERS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
set i = i+1
endloop
call TriggerAddCondition(t, Condition(function Plane.onOrderPoint))
static if (LIBRARY_AutoIndex) then
call OnUnitIndexed(Plane.onIndex)
else
set t = CreateTrigger()
call RegionAddRect(map, r)
call TriggerRegisterEnterRegion(t, map, function Plane.onUnitEntersMap)
set i = 0
loop
exitwhen i > MAX_PLAYERS
call GroupEnumUnitsOfPlayer(g, Player(i), function Plane.onUnitEntersMap)
set i = i+1
endloop
endif
call DestroyGroup(g)
call RemoveRect(r)
set g = null
set r = null
set t = null
set map = null
endmethod
endstruct
endlibrary
JASS:
//TESH.scrollpos=8
//TESH.alwaysfold=0
library ListModule
//===========================================================================
// Information:
//==============
//
// This library provides the List module, which allows you to easily create
// a linked list of all of the allocated instances of a struct-type. Iterating
// through a linked list is about 12% faster than iteratating through an array
// in JASS. There is no faster method to iterate through a list of structs than
// the method used by this module. Aside from the marginal speed gain, the best
// use of this library is to hide some ugly low-level code from your structs.
// Rather than manually building and maintaining a list of struct instances,
// just implement the List module, and your code will become much prettier.
//
//===========================================================================
// How to use the List module:
//=============================
//
// Using the List module is pretty simple. First, implement it in your
// struct (preferably at the top to avoid unnecessary TriggerEvaluate calls).
// In the struct's create method, you must call listAdd(). In the onDestroy
// method, you must also call listRemove(). An example is shown below:
/*
struct Example
implement List
static method create takes nothing returns Example
local Example this = allocate()
call listAdd() //This method adds the instance to the list.
return this
endmethod
method onDestroy takes nothing returns nothing
call listRemove() //This method removes the instance from the list.
endmethod
endstruct
*/
// The requirement to call listAdd() and listRemove() will be done away
// with once JassHelper supports module onDestroy and module onCreate, but
// for now, it is not too much of a burden.
//
// Once this is done, your struct will gain all of the methods detailed
// in the API section. Below is an example of how to iterate through the list
// of allocated structs of the implementing struct-type:
/*
function IterationExample takes nothing returns nothing
local Example e = Example.first
loop
exitwhen e == 0
//Do something with e here.
set e = e.next
endloop
//Use .last and .prev instead to iterate backwards.
endmethod
*/
//
//===========================================================================
// List module API:
//==================
//
// (readonly)(static) first -> thistype
// This member contains the first instance of thistype in the list.
//
// (readonly)(static) last -> thistype
// This member contains the last instance of thistype in the list.
//
// (readonly)(static) count -> integer
// This member contains the number of allocated structs of thistype.
//
// (readonly) next -> thistype
// This member contains the next instance of thistype in the list.
//
// (readonly) prev -> thistype
// This member contains the previous instance of thistype in the list.
//
// listAdd()
// This method adds this instance to the list of structs of thistype.
// This should be called on each instance after it is allocated (within
// the create method).
//
// listRemove()
// This method removes this instance from the list of structs of thistype.
// This should be called on each instance before it is destroyed (within
// the onDestroy method).
//
// (static) listDestroy()
// This method destroys all the structs of thistype within the list.
//
//===========================================================================
module List
private static boolean destroying = false
private boolean inlist = false
readonly static integer count = 0
readonly thistype next = 0
readonly thistype prev = 0
static method operator first takes nothing returns thistype
return thistype(0).next
endmethod
static method operator last takes nothing returns thistype
return thistype(0).prev
endmethod
method listRemove takes nothing returns nothing
if not inlist then
return
endif
set inlist = false
set prev.next = next
set next.prev = prev
set count = count - 1
endmethod
method listAdd takes nothing returns nothing
if inlist or destroying then
return
endif
set inlist = true
set last.next = this
set prev = last
set thistype(0).prev = this
set count = count + 1
endmethod
static method listDestroy takes nothing returns nothing
local thistype this = last
set destroying = true
loop
exitwhen this == 0
call destroy()
set this = prev
endloop
set destroying = false
endmethod
endmodule
endlibrary