- Joined
- Oct 9, 2019
- Messages
- 295
In my map I've isolated a freeze to a hero using a modified version of MoveSpeedX for GUI v1.1.0.0 and attacking another unit. By freeze I mean "this program has stopped responding" not bliz's crash handler. It freezes just as the hero lands an attack but not every time. I've had 2 freezes with a hero moving fast this way and 0 otherwise. Possibly only fatal hits trigger the freeze but that's not confirmed. If I were to guess it has something to do with the ticks and MAX_TICKS variables, since I added those to make fast movement smoother. Upon analyzing this I can probably optimize the GetClosestPathablePointX function to re-use a location but unless its leaking memory I don't think that's the cause. I thought a 2nd set of eyes might help me find something I missed.
P.S. Ignore the comments, they are not up to date
edit: Code has been revised see a few posts down
P.S. Ignore the comments, they are not up to date
edit: Code has been revised see a few posts down
JASS:
//TESH.scrollpos=175
//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.00125
// 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.
private constant real MAX_TICKS = 40.
private integer itemtypeid = 'moon'
private item itm=null
endglobals
private function GetClosestPathablePointX takes real x, real y returns location
local real returnx
local real returny
if GetItemTypeId(itm)==0 then
set itm = CreateItem( itemtypeid, 32256.,32256. )
call SetItemVisible(itm,false)
endif
call SetItemPosition(itm,x,y)
set returnx = GetItemX(itm)
set returny = GetItemY(itm)
call SetItemPosition(itm,32256.,32256.)
return Location(returnx,returny)
endfunction
/*
************************************************************************************
*
* 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 issuedpoint = CreateTrigger()
private static trigger issuedtarget = CreateTrigger()
thistype next
thistype prev
boolean enabled
unit curr
real speed
real x
real y
real ox
real oy
real ticks
integer oi
widget ot
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
local location loc=null
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 and GetUnitCurrentOrder(u)== this.oi 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 this.ot != null then
set this.ox = GetWidgetX(ot)
set this.oy = GetWidgetY(ot)
endif
if (this.oi == 851986 or this.oi == 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
elseif ticks >=MAX_TICKS then
set .ticks = 0
set loc=GetClosestPathablePointX(nx+dx,ny+dy)
set .x = GetLocationX(loc)
set .y = GetLocationY(loc)
if this.ot != null then
call IssueTargetOrderById(u,oi,ot)
else
call IssuePointOrderById(u,oi,ox,oy)
endif
else
set .ticks = .ticks + 1
//call BJDebugMsg(R2S(.ticks))
//call BJDebugMsg(R2S(MAX_TICKS))
set loc=GetClosestPathablePointX(nx+dx,ny+dy)
set .x = GetLocationX(loc)
set .y = GetLocationY(loc)
call SetUnitX(u, .x)
call SetUnitY(u, .y)
endif
endif
endif
call RemoveLocation(loc)
set loc=null
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()
set this.ot = null
set this.oi = GetUnitCurrentOrder(GetTriggerUnit())
return false
endmethod
private static method storeOrderTarget takes nothing returns boolean
local thistype this = GetUnitUserData(GetTriggerUnit())
set this.ot = GetOrderTarget()
set this.oi = GetUnitCurrentOrder(GetTriggerUnit())
return false
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), PERIOD, true, function thistype.periodic)
call TriggerRegisterAnyUnitEventBJ(issuedpoint, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(issuedpoint, Condition(function thistype.storeOrderPoint))
call TriggerRegisterAnyUnitEventBJ(issuedtarget, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(issuedtarget, Condition(function thistype.storeOrderTarget))
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 SetUnitMoveSpeed(whichUnit,newSpeed)
call MoveSpeedStruct.update(whichUnit, newSpeed)
set udg_UnitSpeedX[GetUnitUserData(whichUnit)] = newSpeed
endfunction
endlibrary
Last edited: