Name | Type | is_array | initial_value |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CheckDeathInList | boolean | Yes | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
DeathEvent | real | No | |
DetectRemoveAbility | abilcode | No | A004 |
DetectTransformAbility | abilcode | No | A005 |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
KillerOfUnit | unit | Yes | |
SummonerOfUnit | unit | Yes | |
tempGroup | group | No | |
tempInt | integer | No | |
tempReal | real | No | |
tempUnit | unit | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitSpeedX | real | Yes | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
WorldMaxX | real | No | |
WorldMaxY | real | No |
//===========================================================================
function UnitEventDestroyGroup takes integer i returns nothing
if udg_CargoTransportGroup[i] != null then
call DestroyGroup(udg_CargoTransportGroup[i])
set udg_CargoTransportGroup[i] = null
endif
endfunction
function UnitEventCheckAfter takes nothing returns nothing
local integer i = 0
loop
set i = udg_CheckDeathList[i]
exitwhen i == 0
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
elseif udg_IsUnitTransforming[i] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UDex = i
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[i] = GetUnitTypeId(udg_UDexUnits[i]) //Set this afterward to give the user extra reference
set udg_IsUnitTransforming[i] = false
call UnitAddAbility(udg_UDexUnits[i], udg_DetectTransformAbility)
elseif udg_IsUnitAlive[i] then
//The unit has started reincarnating.
set udg_IsUnitReincarnating[i] = true
set udg_IsUnitAlive[i] = false
set udg_UDex = i
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
endif
set udg_CheckDeathInList[i] = false
endloop
//Empty the list
set udg_CheckDeathList[0] = 0
endfunction
function UnitEventCheckAfterProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckAfter)
endif
if not udg_CheckDeathInList[i] then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
set udg_CheckDeathInList[i] = true
endif
endfunction
function UnitEventOnUnload takes nothing returns nothing
local integer i = udg_UDex
call GroupRemoveUnit(udg_CargoTransportGroup[GetUnitUserData(udg_CargoTransportUnit[i])], udg_UDexUnits[i])
set udg_IsUnitBeingUnloaded[i] = true
set udg_CargoEvent = 0.00
set udg_CargoEvent = 2.00
set udg_CargoEvent = 0.00
set udg_IsUnitBeingUnloaded[i] = false
if not IsUnitLoaded(udg_UDexUnits[i]) or IsUnitType(udg_CargoTransportUnit[i], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CargoTransportUnit[i]) == 0 then
set udg_CargoTransportUnit[i] = null
endif
endfunction
function UnitEventOnDeath takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_UDex != 0 then
set udg_KillerOfUnit[udg_UDex] = GetKillingUnit() //Added 29 May 2017 for GIMLI_2
set udg_IsUnitAlive[udg_UDex] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
set udg_KillerOfUnit[udg_UDex] = null
if udg_CargoTransportUnit[udg_UDex] != null then
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnOrder takes nothing returns boolean
local integer pdex = udg_UDex
local unit u = GetFilterUnit()
local integer i = GetUnitUserData(u)
if i > 0 then
set udg_UDex = i
if GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
if not udg_IsUnitRemoved[i] then
set udg_IsUnitRemoved[i] = true
set udg_IsUnitAlive[i] = false
set udg_SummonerOfUnit[i] = null
//For backwards-compatibility:
set udg_DeathEvent = 0.00
set udg_DeathEvent = 3.00
set udg_DeathEvent = 0.00
//Fire deindex event for UDex:
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 2.00
set udg_UnitIndexEvent = 0.00
set udg_UDexNext[udg_UDexPrev[i]] = udg_UDexNext[i]
set udg_UDexPrev[udg_UDexNext[i]] = udg_UDexPrev[i]
// Recycle the index for later use
set udg_UDexUnits[i] = null
set udg_UDexPrev[i] = udg_UDexLastRecycled
set udg_UDexLastRecycled = i
call UnitEventDestroyGroup(i)
endif
elseif not udg_IsUnitAlive[i] then
if not IsUnitType(u, UNIT_TYPE_DEAD) then
set udg_IsUnitAlive[i] = true
set udg_DeathEvent = 0.00
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
set udg_IsUnitReincarnating[i] = false
endif
elseif IsUnitType(u, UNIT_TYPE_DEAD) then
if udg_IsUnitNew[i] then
//This unit was created as a corpse.
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
elseif udg_CargoTransportUnit[i] == null or not IsUnitType(u, UNIT_TYPE_HERO) then
//The unit may have just started reincarnating.
call UnitEventCheckAfterProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 and not udg_IsUnitTransforming[i] then
set udg_IsUnitTransforming[i] = true
call UnitEventCheckAfterProxy(i) //This block has been updated on 21 July 2017
endif
if udg_CargoTransportUnit[i] != null and not udg_IsUnitBeingUnloaded[i] and not IsUnitLoaded(u) or IsUnitType(u, UNIT_TYPE_DEAD) then
call UnitEventOnUnload()
endif
set udg_UDex = pdex
endif
set u = null
return false
endfunction
function UnitEventOnSummon takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_IsUnitNew[udg_UDex] then
set udg_SummonerOfUnit[udg_UDex] = GetSummoningUnit()
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 0.50
set udg_UnitIndexEvent = 0.00
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnLoad takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = GetUnitUserData(GetTriggerUnit())
local integer index
if i != 0 then
set udg_UDex = i
if udg_CargoTransportUnit[i] != null then
call UnitEventOnUnload()
endif
//Loaded corpses do not issue an order when unloaded, therefore must
//use the enter-region event method taken from Jesus4Lyf's Transport.
if not udg_IsUnitAlive[i] then
call SetUnitX(udg_UDexUnits[i], udg_WorldMaxX)
call SetUnitY(udg_UDexUnits[i], udg_WorldMaxY)
endif
set udg_CargoTransportUnit[i] = GetTransportUnit()
set index = GetUnitUserData(udg_CargoTransportUnit[i])
if udg_CargoTransportGroup[index] == null then
set udg_CargoTransportGroup[index] = CreateGroup()
endif
call GroupAddUnit(udg_CargoTransportGroup[index], udg_UDexUnits[i])
set udg_CargoEvent = 0.00
set udg_CargoEvent = 1.00
set udg_CargoEvent = 0.00
set udg_UDex = pdex
endif
return false
endfunction
function UnitEventEnter takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = udg_UDexLastRecycled
local unit u = GetFilterUnit()
if udg_UnitIndexerEnabled and GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
//Generate a unique integer index for this unit
if i == 0 then
set i = udg_UDexMax + 1
set udg_UDexMax = i
else
set udg_UDexLastRecycled = udg_UDexPrev[i]
endif
//Link index to unit, unit to index
set udg_UDexUnits[i] = u
call SetUnitUserData(u, i)
//For backwards-compatibility, add the unit to a linked list
set udg_UDexNext[i] = udg_UDexNext[0]
set udg_UDexPrev[udg_UDexNext[0]] = i
set udg_UDexNext[0] = i
set udg_UDexPrev[i] = 0
set udg_CheckDeathInList[i] = false
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeOf[i] = GetUnitTypeId(u)
set udg_IsUnitNew[i] = true
set udg_IsUnitAlive[i] = true
set udg_IsUnitRemoved[i] = false
set udg_IsUnitReincarnating[i] = false
set udg_IsUnitPreplaced[i] = udg_IsUnitPreplaced[0] //Added 29 May 2017 for Spellbound
call UnitEventCheckAfterProxy(i)
//Fire index event for UDex
set udg_UDex = i
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 1.00
set udg_UnitIndexEvent = 0.00
else
set udg_UDex = GetUnitUserData(u)
if udg_CargoTransportUnit[udg_UDex] != null and not IsUnitLoaded(u) then
//The unit was dead, but has re-entered the map.
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
set u = null
return false
endfunction
//===========================================================================
function UnitEventInit takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS //update to make it work with 1.29
local player p
local trigger t = CreateTrigger()
local trigger load = CreateTrigger()
local trigger death = CreateTrigger()
local trigger summon = CreateTrigger()
local rect r = GetWorldBounds()
local region re = CreateRegion()
local boolexpr enterB = Filter(function UnitEventEnter)
local boolexpr orderB = Filter(function UnitEventOnOrder)
set udg_WorldMaxX = GetRectMaxX(r)
set udg_WorldMaxY = GetRectMaxY(r)
call RegionAddRect(re, r)
call RemoveRect(r)
call UnitEventDestroyGroup(0)
call UnitEventDestroyGroup(1)
set udg_CheckDeathList[0] = 0
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, enterB)
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set enterB = null
set orderB = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction
Unit Event for GUI gives you access to all kinds of events which normal GUI events can't do.
Now fully integrating Unit Indexer, I ve listed all substantial events and variables.
In each event, the custom value of the "triggering unit" is equal to UDex
and the way to get the unit with that custom value is via UDexUnits[(Custom value of Unit) --- In this case "UDex"].
Detect the instant when a unit starts reincarnating
Event: Game - DeathEvent Becomes Equal to 0.50
Detect when a unit is brought back to life via Reincarnation, Resurrect or Reanimate
Event: Game - DeathEvent Becomes Equal to 2.00
You can differentiate between the three of these as well. If it is summoned, that means it was reanimated. If IsUnitReincarnating[UDex] is true, that means it finished reincarnating. Otherwise, it was simply resurrected.
Detect when a unit is loaded into a transport
Event: Game - CargoEvent Becomes Equal to 1.00
Unit: CargoTransportUnit[UDex] --- The unit who loaded this unit
Detect when a unit is unloaded from a transport
Event: Game - CargoEvent Becomes Equal to 2.00
Unit: CargoTransportUnit[UDex] --- The unit who unloaded this unit
Detect when a unit transforms into a new unit type
Event: Game - UnitTypeEvent Becomes Equal to 1.00
Unit Type: UnitTypeOf[UDex] --- The previous unit type
Detect the instant before a unit is completely removed from the game
Event: Game - UnitIndexEvent Becomes Equal to 2.00
To detect when any unit is created (including pre-placed units)
Event: Game - UnitIndexEvent Becomes Equal to 1.00
To detect when a new unit is summoned (fires after the Create event)
Event: Game - UnitIndexEvent Becomes Equal to 0.50
Unit: SummonerOfUnit[UDex]
To detect when any unit dies (including units created dead)
Event: Game - DeathEvent Becomes Equal to 1.00
Unit: KillerOfUnit[UDex]
Unit Event also gives you access to some other useful variables, such as:
Boolean: IsUnitAlive[(Custom value of Unit)] - never bugs (checking life is not always accurate)
Unit Group: CargoTransportGroup[(Custom value of Transporter)] -
A unit group which holds all of the cargo of a transporting unit.
There are also some additional obscure things you can do with the system as well,
such as looping through all the units in a map or reading data/events based on when the system initialized.
library MovementModifier initializer Init //ver 1.07 by ZibiTheWand3r3r, 18-08-2018
// Allows to change unit's movement speed for given duration (or permanent)
// All Object Editor abilities [that changing move speed] will work with this system - they stack.
// System's bonuses also stack.
// ********************************
// ==> Requires Unit Event
// https://www.hiveworkshop.com/threads/gui-unit-event-v2-5-2-0.201641/
// ********************************
// ==> Applying move speed bonus:
// UnitAddMoveSpeedBonus(unit u, real percentBonus, real flatBonus, real duration)
// u - unit whos move speed will be changed
// percentBonus - positive to increase speed, negative to slow a target (use 0.00 to not apply percent bonus)
// bonus will be added as percent (like Endurance Aura for example)
// flatBonus - positive to increase speed, negative to slow a target (use 0.00 to not apply flat bonus)
// bonus will be added like item's Boots of Speed for example
// duration - how long bonus will be on unit, [use duration = 0.00 to make bonus permanent]
// both percentBonus and flatBonus can be used in one function call -- or use one chosen bonus only
// ==> Removing timed movespeed bonuses:
// UnitRemoveMoveSpeedBonuses(unit u, boolean removePositiveBonuses, boolean removeNegativeBonuses)
// u - unit whos timed bonuses will be removed
// removePositiveBonuses - if "true" _all_ positive timed bonuses (bonus>0.00) will be removed
// removeNegativeBonuses - if "true" _all_ negative timed bonuses (bonus<0.00) will be removed
// ==> Usage examples:
// Speed up unit u: +45% move speed for 12sec: call UnitAddMoveSpeedBonus(u, 0.45, 0.00, 12.00)
// Speed up unit u: +120 flat move speed for ever: call UnitAddMoveSpeedBonus(u, 0.00, 120, 0.00)
// Slow unit u: -15%(percent) and -20(flat) for 7sec: call UnitAddMoveSpeedBonus(u, -0.15, -20.00, 7.00)
// Remove *all negative* move speed bonuses: call UnitRemoveMoveSpeedBonuses(u, false, true)
// Remove *all positive* move speed bonuses: call UnitRemoveMoveSpeedBonuses(u, true, false)
// Remove _all_ (negative and positive) move speed bonuses: call UnitRemoveMoveSpeedBonuses(u, true, true)
// how bonuses are calculated:
// moveSpeed = (GetUnitDefaultMoveSpeed(u) + flatBonus) * (1 + percentageBonus1 + percentageBonus2 + etc)
globals
// configuration:
private constant real INTERVAL = 0.50 //How precise is timed applied movespeed change, for greater accuracy use lower value like 0.20 or 0.10
private constant boolean PERSISTS_THROUGH_DEATH = true // if "true" then unit/hero's death will not end up applied bonus
// Set to "false" to end bonus-instance upon unit's death. Above boolean will affect only timed bonuses.
// end of configuration ----------------------------------------------------------------------------
private real array totalFlat // total flat bonus [targetId] => binded to target unit
private real array totalPercent // total percent bonus [targetId]
private boolean array instanceIsBonusPositive // [id] => connected to spell instance
private real array instanceBonus // bonus [id]
private real array instanceDuration // duration [id]
private unit array instanceTarget // target [id]
private boolean array instanceIsBonusFlat // [id]
private integer instanceId = 0
private timer t = CreateTimer()
endglobals
//--------------------------------------------------------------------
native UnitAlive takes unit id returns boolean
//--------------------------------------------------------------------
private function Loop takes nothing returns nothing
local integer id=1
local integer targetId
loop
exitwhen id>instanceId
set instanceDuration[id] = instanceDuration[id] - INTERVAL //when spell expires:
if instanceDuration[id] <= 0.00 or ((not PERSISTS_THROUGH_DEATH) and (not UnitAlive(instanceTarget[id]))) then
//revert:
set targetId = GetUnitUserData(instanceTarget[id])
if instanceIsBonusFlat[id] then
set totalFlat[targetId] = totalFlat[targetId] - instanceBonus[id] //minus specific flat bonus
else
set totalPercent[targetId] = totalPercent[targetId] - instanceBonus[id] //minus specific % bonus
endif
call SetUnitMoveSpeed(instanceTarget[id], (GetUnitDefaultMoveSpeed(instanceTarget[id]) + totalFlat[targetId])*(1+totalPercent[targetId]))
//dealloc: /move data from max to current/
set instanceIsBonusPositive[id] = instanceIsBonusPositive[instanceId]
set instanceBonus[id] = instanceBonus[instanceId]
set instanceDuration[id] = instanceDuration[instanceId]
set instanceTarget[id] = instanceTarget[instanceId]
set instanceIsBonusFlat[id] = instanceIsBonusFlat[instanceId]
set instanceTarget[instanceId] = null
set instanceId=instanceId-1
set id=id-1
if instanceId==0 then
call PauseTimer(t)
endif
endif
set id=id+1
endloop
endfunction
//--------------------------------------------------------------------
private function UnitAddMoveSpeedBonus_Ex takes unit u, real bonus, boolean isFlatBonus, real duration returns nothing
local integer targetId = GetUnitUserData(u)
//common part:
if isFlatBonus then
set totalFlat[targetId] = totalFlat[targetId] + bonus //add flat bonus
else
set totalPercent[targetId] = totalPercent[targetId] + bonus //add % bonus
endif
call SetUnitMoveSpeed(u, (GetUnitDefaultMoveSpeed(u) + totalFlat[targetId])*(1+totalPercent[targetId]))
//permanent:
if duration==0.00 then
return
endif
//timed:
set instanceId = instanceId + 1
set instanceTarget[instanceId] = u
set instanceDuration[instanceId] = duration
set instanceIsBonusFlat[instanceId] = isFlatBonus
set instanceIsBonusPositive[instanceId] = bonus>0.00
set instanceBonus[instanceId] = bonus
if instanceId==1 then
call TimerStart(t, INTERVAL, true, function Loop)
endif
endfunction
//--------------------------------------------------------------------
//--------------------------------------------------------------------
function UnitAddMoveSpeedBonus takes unit u, real percentBonus, real flatBonus, real duration returns nothing
if not (u==null or (not UnitAlive(u)) or duration<0.00 or (percentBonus==0.00 and flatBonus==0.00)) then //<=protection
if percentBonus != 0.00 then
call UnitAddMoveSpeedBonus_Ex(u, percentBonus, false, duration)
endif
if flatBonus != 0.00 then
call UnitAddMoveSpeedBonus_Ex(u, flatBonus, true, duration)
endif
endif
endfunction
//-----------------------------------------------------------------------------------
// ==> Removing timed movespeed bonuses
function UnitRemoveMoveSpeedBonuses takes unit u, boolean removePositiveBonuses, boolean removeNegativeBonuses returns nothing
local integer id=1
loop
exitwhen id>instanceId
if u == instanceTarget[id] then
if removePositiveBonuses and instanceIsBonusPositive[id] then
set instanceDuration[id] = 0.00
endif
if removeNegativeBonuses and (not instanceIsBonusPositive[id]) then
set instanceDuration[id] = 0.00
endif
endif
set id=id+1
endloop
call Loop()
endfunction
//-----------------------------------------------------------------------------------
// Units that changing their form (metamorphosis, chamical rage, destroyer form, bear form etc)
// will lose their move speed bonuses comes from SetUnitMoveSpeed.
// To prevent that: after unit transforms to new unit-type this function must be called:
function Trig_UnitUpdateMoveSpeedBonusAfterMorph takes nothing returns boolean
local integer targetId = GetUnitUserData(udg_UDexUnits[udg_UDex])
call SetUnitMoveSpeed(udg_UDexUnits[udg_UDex], (GetUnitDefaultMoveSpeed(udg_UDexUnits[udg_UDex]) + totalFlat[targetId])*(1+totalPercent[targetId]))
return false
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitTypeEvent", EQUAL, 1.00)
call TriggerAddCondition(t, function Trig_UnitUpdateMoveSpeedBonusAfterMorph)
set t=null
endfunction
endlibrary
function Trig_selection_Actions takes nothing returns nothing
//call DisplayTextToForce( GetPlayersAll(), R2S(GetUnitMoveSpeed(GetTriggerUnit())) )
call DisplayTextToForce( GetPlayersAll(), R2S(GetUnitMoveSpeedX(GetTriggerUnit())) )
//use GetUnitMoveSpeedX if you are using optional library MoveSpeedXGUI /* v1.1.0.0
endfunction
//===========================================================================
function InitTrig_selection takes nothing returns nothing
set gg_trg_selection = CreateTrigger( )
call TriggerRegisterPlayerSelectionEventBJ( gg_trg_selection, Player(0), true )
call TriggerAddAction( gg_trg_selection, function Trig_selection_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MoveSpeedXGUI /* v1.1.0.0
*************************************************************************************
*
* This library allows you to set unit movement speeds beyond 522 without bugs.
* This is an extension of the library MoveSpeedX, but is formatted for GUI use.
* Credits to Jesus4Lyf for the original system.
*
************************************************************************************
*
* SETTINGS
*/
globals
private constant real PERIOD = 0.03125
// This is the period on which all units will be run.
// If you lower this value, movement bonuses will be smoother,
// but will require more processing power (lag more).
// Also, the lower this is, the higher the move speed can be
// before it starts bugging on waypoints. The lowest valid
// period is 0.00125. A period of 0.00625 is very robust.
private constant real MARGIN = 0.01
// This is the margin of approximation when comparing reals.
// You will most likely not need to change this.
endglobals
/*
************************************************************************************
*
* Functions
*
* function GetUnitMoveSpeedX takes unit whichUnit returns real
* - Returns a unit movement speed. The GUI function will
* - not return the correct value. This function will always
* - return the correct value regardless of whether the unit
* - has a movement speed beyond 522.
*
************************************************************************************
*
* REQUIREMENTS
*
* 1. JassNewGen Pack v5d
* 2. JassHelper 0.A.2.B
* 3. Any unit indexer
*
* HOW TO IMPLEMENT
*
* 1. Copy the 'folder' MoveSpeedX.
* 2. Paste it into your map.
* 3. Open "Advanced -> Gameplay Constants".
* 4. Checkmark "Use Custom Gameplay Constants".
* 5. Find the field, "Movement - Unit Speed - Maximum", change
* that to 522.
* 6. Find the field, "Movement - Unit Speed - Minimum", hold
* shift and click, and change it to 0.
* 7. Read HOW TO USE.
*
************************************************************************************
*
* HOW TO USE
*
* This system will automatically work by itself. You can use the
* normal GUI function for modifying unit movement speeds. Simply
* use "Unit - Set Movement Speed", input whatever value you want,
* and you are good to go! It will handle values beyond 522 by itself.
*
* HOWEVER, the GUI function will not return correct values if a unit
* has a movement speed greater than 522. To fix this, use the function
* GetUnitMoveSpeedX to return the correct value. A sample is given in
* the trigger "Speed Change" in the test map.
*
************************************************************************************
*
* NOTES
*
* Units that were issued orders as groups might not *always* end up in the proper
* "order". (they might not end up in an organized formation) They do sometimes though.
* This is only for units with speeds above 522.
*
* This also will not factor in bonuses and probably not slows either.
*
* Units may waddle around the point for a little bit. Reduce PERIOD to fix
* it a little bit. I recommend about 0.02 if you have really high speeds.
*
************************************************************************************/
private function ApproxEqual takes real A, real B returns boolean
return (A >= (B - MARGIN)) and (A <= (B + MARGIN))
endfunction
private module M
private static trigger issued = CreateTrigger()
thistype next
thistype prev
boolean enabled
unit curr
real speed
real x
real y
real ox
real oy
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
set this.enabled = false
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next // first instance in list
local real nx // the x-coordinate after tick
local real ny // the y-coordinate after tick
local real dx // distance between new-x and old-x
local real dy // distance between new-y and old-y
local real d // distance between new point and old point
local integer order // the unit's current order
local unit u // unit being affected
loop
exitwhen this == 0
set u = .curr
set nx = GetUnitX(u)
set ny = GetUnitY(u)
if IsUnitType(u, UNIT_TYPE_DEAD) then
call this.destroy()
elseif not ApproxEqual(nx, .x) or not ApproxEqual(ny, .y) then
if (not IsUnitPaused(u)) and GetUnitAbilityLevel(u, 'BSTN') == 0 and GetUnitAbilityLevel(u, 'BPSE') == 0 then
set order = GetUnitCurrentOrder(u)
set dx = nx - .x
set dy = ny - .y
set d = SquareRoot(dx * dx + dy * dy)
set dx = dx / d * .speed // move the unit offset-x by this
set dy = dy / d * .speed // move the unit offset-y by this
if (order == 851986 or order == 851971) and /*
*/ (this.ox - nx)*(this.ox - nx) < (dx*dx) and /*
*/ (this.oy - ny)*(this.oy - ny) < (dy*dy) then
// if the unit is issued a move or smart order and they are near their destination
// then move them there instantly (removes a bit of glitchyness towards the end)
call SetUnitX(u, .ox)
call SetUnitY(u, .oy)
set .x = .ox
set .y = .oy
call IssueImmediateOrderById(u, 851972) // order them to stop
else
set .x = nx + dx
set .y = ny + dy
call SetUnitX(u, .x)
call SetUnitY(u, .y)
endif
endif
endif
set this = this.next
endloop
set u = null
endmethod
static method create takes unit whichUnit, real newSpeed returns thistype
local thistype this = GetUnitUserData(whichUnit)
set this.next = thistype(0).next
set thistype(0).next.prev = this
set thistype(0).next = this
set this.prev = 0
set this.curr = whichUnit
set this.speed = (newSpeed - 522) * PERIOD
set this.x = GetUnitX(whichUnit)
set this.y = GetUnitY(whichUnit)
set this.enabled = true
return this
endmethod
static method update takes unit whichUnit, real newSpeed returns nothing
local thistype this = GetUnitUserData(whichUnit)
if this.enabled then
if newSpeed > 522 then
set this.speed = (newSpeed - 522) * PERIOD
else
call this.destroy()
endif
elseif newSpeed > 522 then
call thistype.create(whichUnit, newSpeed)
endif
endmethod
private static method storeOrderPoint takes nothing returns boolean
local thistype this = GetUnitUserData(GetTriggerUnit())
set this.ox = GetOrderPointX()
set this.oy = GetOrderPointY()
return false
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), PERIOD, true, function thistype.periodic)
call TriggerRegisterAnyUnitEventBJ(issued, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(issued, Condition(function thistype.storeOrderPoint))
endmethod
endmodule
private struct MoveSpeedStruct extends array
implement M
endstruct
function GetUnitMoveSpeedX takes unit whichUnit returns real
if MoveSpeedStruct(GetUnitUserData(whichUnit)).enabled then
return udg_UnitSpeedX[GetUnitUserData(whichUnit)]
endif
return GetUnitMoveSpeed(whichUnit)
endfunction
function SetUnitMoveSpeedX takes unit whichUnit, real newSpeed returns nothing
call MoveSpeedStruct.update(whichUnit, newSpeed)
set udg_UnitSpeedX[GetUnitUserData(whichUnit)] = newSpeed
endfunction
hook SetUnitMoveSpeed SetUnitMoveSpeedX
endlibrary