Optimization Help

cleeezzz

The Undead Ranger.
Reaction score
268
JASS:
library ProSys initializer Init requires KT, GroupUtils

function interface OnRun takes unit u returns nothing

function interface UnitImpact takes unit d, unit u returns nothing

function interface OnDeath takes unit u returns nothing

globals
    group projectiles
    private constant integer FLYID = 'amrf'        
    private real Game_maxX
    private real Game_maxY
    private real Game_minX
    private real Game_minY
    private unit TP
endglobals

private function SafeX takes real x returns real
    if x<Game_minX then
        return Game_minX
    endif
    if x>Game_maxX then
        return Game_maxX
    endif
    return x
endfunction

private function IsXSafe takes real x returns boolean
    if x>Game_minX and x<Game_maxX then
        return true
    endif
    return false
endfunction

private function SafeY takes real y returns real
    if y<Game_minY then
        return Game_minY
    endif
    if y>Game_maxY then
        return Game_maxY
    endif
    return y
endfunction

private function IsYSafe takes real y returns boolean
    if y>Game_minY and y<Game_maxY then
        return true
    endif
    return false
endfunction

private function SetUnitZ takes unit u, real height returns nothing
    call UnitAddAbility(u, 'Arav')
    call UnitRemoveAbility(u, 'Arav')
    call SetUnitFlyHeight(u, height, 0.)
endfunction
               
//=======================================================================
struct P
    //! runtextmacro PUI()
    player play
    unit p
    real x
    real y
    real endcd // yes i know i dont use this  variable, ill remove later
    real maxd
    real r
    real cx
    OnRun OR
    UnitImpact UI
    OnDeath OD
    group g
    method onDestroy takes nothing returns nothing
        call GroupRemoveUnit(projectiles,.p)
        set .play = null
        call KillUnit(.p)
        set .p = null
        set .UI = 0
        set .OD = 0
        call ReleaseGroup(.g)
        set .g = null
    endmethod
endstruct

private function GlobalFilter takes unit u returns boolean  // global filter for range check
    return GetWidgetLife(u) > .405 and (IsUnitVisible(u, GetOwningPlayer(TP)) or GetUnitTypeId(u) == 'oeye')
endfunction

private function ExecuteCPS takes nothing returns boolean
    local P d = KT_GetData()
    local AP s   // struct AP (not in this trigger) works together with ProSys (AP stands for Arrow Properties)
    local real x  // AP stores damage, movement speed, and etc.
    local real y
    local real f
    local group g
    local unit fog
    local real dist
    call GroupRefresh(projectiles)
    if GetWidgetLife(d.p) < .405 then
        if d.p != null then
            if d.OD != 0 then
                call d.OD.evaluate(d.p)
            endif
            call d.release()
            return true
        else
            debug call BJDebugMsg("|cffffcc00CPS ERROR: DO NOT REMOVE PROJECTILES USING THE OnRun/GroundImpact/UnitImpact/OnDeath FUNCTIONS!|r")
            call d.release()
            return true
        endif
        call GroupRemoveUnit(projectiles,d.p)
    endif
    set TP = d.p
    if d.OR != 0 then
        call d.OR.evaluate(d.p)
    endif
    set s = AP[d.p]
    set x = GetUnitX(d.p)
    set y = GetUnitY(d.p)
    set dist = SquareRoot((d.x-x)*(d.x-x) + (d.y-y)*(d.y-y))
    set d.cx = d.cx + dist
    set s.totaldistance = d.cx
    if d.cx > d.maxd then
        if d.OD != 0 then
            call d.OD.evaluate(d.p)
        endif
        call d.release()
        return true
    endif
    set d.x = x
    set d.y = y
    set f = GetUnitFacing(d.p) * bj_DEGTORAD
    set x = x+s.currentspeed*Cos(f)
    set y = y+s.currentspeed*Sin(f)
    if s.currentspeed < s.basespeed then
        set s.currentspeed = s.currentspeed + s.acceleration
    endif
    if s.anglechange != 0. and GetUnitTypeId(d.p) != 'h00K' then    
        call SetUnitFacing(d.p, ModuloReal((GetUnitFacing(d.p)*bj_DEGTORAD) + s.anglechange,bj_PI*2)*bj_RADTODEG)
        set s.anglechange = 0.
    endif
    if IsXSafe(x) == true and IsYSafe(y) == true then
        call SetUnitX(d.p,x)
        call SetUnitY(d.p,y)
    else
        if d.OD != 0 then
            call d.OD.evaluate(d.p)
        endif
        call d.release()
        return true
    endif
    if s.radius > 0 then  // range check, if there are enemies, run UnitImpact function
        set g = NewGroup()
        call GroupEnumUnitsInRange(g,x,y,s.radius,null)
        if FirstOfGroup(g) == null then
            call GroupClear(d.g)
        endif
        loop
            set fog = FirstOfGroup(g)
            exitwhen fog == null
            if IsUnitInGroup(fog,d.g) == false then
                call GroupAddUnit(d.g,fog)
                if d.UI != 0 and GlobalFilter(fog) then
                    call d.UI.evaluate(d.p,fog)
                endif
            endif
            call GroupRemoveUnit(g,fog)
        endloop
        call ReleaseGroup(g)
        set g = null
    endif
    return false
endfunction

function ProjectileAddEventOnRun takes unit proj, OnRun OR returns nothing  //adds event to projectile, every time it runs
    local P d = P[proj]
    if d == 0 then
        return
    endif
    set d.OR = OR
endfunction

function ProjectileAddEventUnitImpact takes unit proj, UnitImpact UI returns nothing  //adds event to projectile, every time it hits a unit
    local P d = P[proj]
    if d == 0 then
        return
    endif
    set d.UI = UI
endfunction

function ProjectileAddEventOnDeath takes unit proj, OnDeath OD returns nothing  //adds event to projectile, when projectile dies
    local P d = P[proj]
    if d == 0 then
        return
    endif
    set d.OD = OD
endfunction

function CPS takes integer ut, player play, real angle, real startX, real startY, real maxd returns unit
    local P d
    local AP z
    if maxd == 0 then
        debug call BJDebugMsg("|cffffcc00ProSys-CPS ERROR: No max distance|r")
        return null
    endif
    set d = P.create()
    set d.play = play
    set d.p = CreateUnit(play, ut, startX, startY, bj_RADTODEG*(angle))
    call GroupAddUnit(projectiles,d.p)  //global projectile group
    set z = AP.create(d.p)
    set P[d.p] = d
    set AP[d.p] = z
    set d.maxd = maxd
    set d.x = startX
    set d.y = startY
    set d.cx = 0
    set d.g = NewGroup()
    set d.UI = 0
    set d.OD = 0
    set d.OR = 0
    call SetUnitPathing(d.p,false)
    call UnitAddAbility(d.p,FLYID)
    call SetUnitFlyHeight(d.p,50.,0.00)
    call KT_Add(function ExecuteCPS, d, 0.03)
    return d.p
endfunction

private function Init takes nothing returns nothing
    set projectiles = NewGroup()
    set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea)-50.00
    set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea)-50.00
    set Game_minX = GetRectMinX(bj_mapInitialPlayableArea)+50.00
    set Game_minY = GetRectMinY(bj_mapInitialPlayableArea)+50.00
endfunction

endlibrary


This is a projectile system that I created and it needs some serious improvement. I thought this version would be better than my previous system (its better in flexibility i guess), but its HUGELY lacking in power.

my old system wouldn't lag with about 100 projectiles but this one starts lagging around there.
 
Reaction score
341
Here ya go.
  • Use Azlier's autofly to add crow form to units once they enter your map.
    This will save speed when using SetUnitZ (which can be replaced with SetUnitFlyHeight)
  • Please don't use PUI, it's fairly outdated. I would suggest AutoIndex.
  • GetWidgetLife(u) > .405 could be replaced with the UnitAlive native.
  • I know you are recycling groups, but it would be even better to use a global one.
  • AFAIK, FirstOfGroup loops are depreciated, simply use ForGroup or the filter.
  • ProjectileAddEventOnRun and all the others like it could be used like this;

    JASS:
    function ProjectileAddEventOnRun takes unit proj, OnRun OR returns nothing  //adds event to projectile, every time it runs
        debug if d == 0 then
        debug    return
        debug  endif
        set P(P[proj]).OR = OR
    endfunction

    This way it will inline when not in debug mode.
 

cleeezzz

The Undead Ranger.
Reaction score
268
ill change what you suggested but a few questions,

about the FirstOfGroup,

its hard to use ForGroup or Filter because the variables dont carry over and its hard to work with, what do you suggest?

and

How accurate is UnitIsAlive native? i thought there was a reason people used the unitstate version of the check.

also about the inlining, so if P[proj] does not exist for some reason (bug, projectile has no struct), what happens? does it just stop? (so theres no harm in setting it even though it doesnt exist?)
 

Kenny

Back for now.
Reaction score
202
>its hard to use ForGroup or Filter because the variables dont carry over and its hard to work with, what do you suggest?

Use a global struct instance to transfer your struct to the filter function. Don't bother using [ljass]ForGroup()[/ljass], from tests I've seen it has no read benefit over FirstOfGroup loops when a small number of units is involved. The filter function is just better all round.

>How accurate is UnitIsAlive native? i thought there was a reason people used the unitstate version of the check.

It is the most accurate may of determining if a unit is dead or not. It doesn't succumb to the same flaws the other checks do. Also, it is [ljass]UnitAlive()[/ljass] not UnitIsAlive.

You should be using T32 for this, it will give you the efficiency boost your looking for.

Using velocities could also help with this, but if you want projectiles to follow units, you will have to constantly reset them, which will be a loss of efficiency.
 

Viikuna

No Marlo no game.
Reaction score
265
Yea, group usage needs to be improved.

Also get rid of that SafeX&Y stuff and use unit leaves region events instead.


Recycling dummies, instead of creating new projectile units alla time, would also be a good optimization, because create unit is one of the slowest natives there is.
 

cleeezzz

The Undead Ranger.
Reaction score
268
UnitAlive doesn't appear to be a native O-o, all i can find is the BJ

also, is it still bad if im using the PUI included with AIDS?, i heard its better

>Recycling dummies, instead of creating new projectile units alla time, would also be a good optimization, because create unit is one of the slowest natives there is.
would you suggest doing something like a unit stack (similar to how GroupUtils recycles its groups?), although i think this may require hashtables or something because i have multiple dummy units.

and question from last post -
also about the inlining, so if P[proj] does not exist for some reason (bug, projectile has no struct), what happens? does it just stop? (so theres no harm in setting it even though it doesnt exist?)

im still working on the others changes, will update
 

cleeezzz

The Undead Ranger.
Reaction score
268
JASS:
library ProSys initializer Init requires KT, GroupUtils

function interface OnRun takes unit u returns nothing

function interface UnitImpact takes unit d, unit u returns nothing

function interface OnDeath takes unit u returns nothing

globals
    group projectiles
    private group g
    private unit TP
endglobals

private function GlobalFilter takes unit u returns boolean
    return UnitAlive(u) and (IsUnitVisible(u, GetOwningPlayer(TP)) or GetUnitTypeId(u) == 'oeye')
endfunction
               
//=======================================================================
struct P
    //! runtextmacro PUI()
    player play
    unit p
    real x
    real y
    real maxd
    real r
    real cx
    group g
    OnRun OR
    UnitImpact UI
    OnDeath OD
    method periodic takes nothing returns nothing
        local P d = this
        local AP s
        local real x
        local real y
        local real f
        local unit fog
        local real dist
        call GroupRefresh(projectiles)
        if not UnitAlive(d.p) then
            if d.p != null then
                if d.OD != 0 then
                    call d.OD.evaluate(d.p)
                endif
                call d.release()
                return 
            else
                debug call BJDebugMsg("|cffffcc00CPS ERROR: DO NOT REMOVE PROJECTILES USING THE OnRun/GroundImpact/UnitImpact/OnDeath FUNCTIONS!|r")
                call d.release()
                return 
            endif
            call GroupRemoveUnit(projectiles,d.p)
        endif
        set TP = d.p
        if d.OR != 0 then
            call d.OR.evaluate(d.p)
        endif
        set s = AP[d.p]
        set x = GetUnitX(d.p)
        set y = GetUnitY(d.p)
        set dist = SquareRoot((d.x-x)*(d.x-x) + (d.y-y)*(d.y-y))
        set d.cx = d.cx + dist
        set s.totaldistance = d.cx
        if d.cx > d.maxd then
            if d.OD != 0 then
                call d.OD.evaluate(d.p)
            endif
            call d.release()
            return 
        endif
        set d.x = x
        set d.y = y
        set f = GetUnitFacing(d.p) * bj_DEGTORAD
        set x = x+s.currentspeed*Cos(f)
        set y = y+s.currentspeed*Sin(f)
        if s.currentspeed < s.basespeed then
            set s.currentspeed = s.currentspeed + s.acceleration
        endif
        if s.anglechange != 0. and GetUnitTypeId(d.p) != 'h00K' then    
            call SetUnitFacing(d.p, ModuloReal((GetUnitFacing(d.p)*bj_DEGTORAD) + s.anglechange,bj_PI*2)*bj_RADTODEG)
            set s.anglechange = 0.
        endif
        call SetUnitX(d.p,x)
        call SetUnitY(d.p,y)
        if s.radius > 0 then
            call GroupClear(g)
            call GroupEnumUnitsInRange(g,x,y,s.radius,null)
            if FirstOfGroup(g) == null then
                call GroupClear(d.g)
            endif
            loop
                set fog = FirstOfGroup(g)
                exitwhen fog == null
                if IsUnitInGroup(fog,d.g) == false then
                    call GroupAddUnit(d.g,fog)
                    if d.UI != 0 and GlobalFilter(fog) then
                        call d.UI.evaluate(d.p,fog)
                    endif
                endif
                call GroupRemoveUnit(g,fog)
            endloop
        endif
    endmethod
    
    static method create takes nothing returns P
        local P d = P.allocate()
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call GroupRemoveUnit(projectiles,.p)
        set .play = null
        call KillUnit(.p)
        set .p = null
        set .UI = 0
        set .OD = 0
        call .stopPeriodic()
    endmethod
    implement T32x
endstruct

function ProjectileAddEventOnRun takes unit proj, OnRun OR returns nothing
    set P(P[proj]).OR = OR
endfunction

function ProjectileAddEventUnitImpact takes unit proj, UnitImpact UI returns nothing
    set P(P[proj]).UI = UI
endfunction

function ProjectileAddEventOnDeath takes unit proj, OnDeath OD returns nothing
    set P(P[proj]).OD = OD
endfunction

function CPS takes integer ut, player play, real angle, real startX, real startY, real maxd returns unit
    local P d
    local AP z
    if maxd == 0 then
        debug call BJDebugMsg("|cffffcc00ProSys-CPS ERROR: No max distance|r")
        return null
    endif
    set d = P.create()
    set d.play = play
    set d.p = CreateUnit(play, ut, startX, startY, bj_RADTODEG*(angle))
    call GroupAddUnit(projectiles,d.p)
    set z = AP.create(d.p)
    set P[d.p] = d
    set AP[d.p] = z
    set d.maxd = maxd
    set d.x = startX
    set d.y = startY
    set d.cx = 0
    set d.g = NewGroup()
    set d.UI = 0
    set d.OD = 0
    set d.OR = 0
    call SetUnitPathing(d.p,false)
    call d.startPeriodic()
    return d.p
endfunction

private function Init takes nothing returns nothing
    set projectiles =CreateGroup()
    set g = CreateGroup()
endfunction

endlibrary

scope Bounded initializer Init

globals
    trigger BoundTrigger
endglobals

private function Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local P d = P<u>
    local AP z
    if d !=0 then
        set z = AP<u>
        if z != 0 then
            if z.bounded == true then
                call SetUnitX(u,SafeX(GetUnitX(u)))
                call SetUnitY(u,SafeY(GetUnitY(u)))
            elseif d.OD != 0 then
                call d.OD.evaluate(u)
                call d.release()
            endif
        endif
    else
        call KillUnit(u)
    endif
    set u = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger(  )
    set BoundTrigger = trig
    call TriggerAddAction( trig, function Actions )
endfunction

endscope
</u></u>


heres code now, it seems like its worse than when it was KT2 lol.

im still working on the groups, but other than that, anything wrong with it?
 

cleeezzz

The Undead Ranger.
Reaction score
268
if you want it.

JASS:
library AP requires AIDS

struct AP
    //! runtextmacro PUI()
    unit projectile
    unit archer
    player owner
    integer level
    real anglechange
    real damage
    boolean bounded
    unit follow
    real interval
    real totaldistance
    real basespeed
    real currentspeed
    real acceleration
    real radius
    static method create takes unit proj returns AP
        local AP d = AP.allocate()
        set d.projectile = proj
        set d.archer = null
        set d.owner = GetOwningPlayer(proj)
        set d.level = 0
        set d.currentspeed = 33.
        set d.basespeed = 33.
        set d.acceleration = 0.3
        set d.radius = 60.
        set d.damage = 0.
        set d.anglechange = 0.
        set d.bounded = false
        set d.follow = null
        set d.interval = 0.
        return d
    endmethod
    method onDestroy takes nothing returns nothing
        set .projectile = null
        set .owner = null
        set .follow = null
    endmethod
endstruct

endlibrary
 
Reaction score
341
JASS:
        set d.level = 0
        set d.currentspeed = 33.
        set d.basespeed = 33.
        set d.acceleration = 0.3
        set d.radius = 60.
        set d.damage = 0.
        set d.anglechange = 0.
        set d.bounded = false
        set d.follow = null
        set d.interval = 0.


Could all be initialized in the variable declarations.
 

cleeezzz

The Undead Ranger.
Reaction score
268
or wait i think you meant

JASS:
    unit projectile
    unit archer
    player owner
    integer level
    real anglechange
    real damage
    boolean bounded
    unit follow
    real interval
    real totaldistance
    real basespeed
    real currentspeed
    real acceleration
    real radius


there, i heard you cant do that because those values are only set when the struct is first created, however when its recycled, it wont have the same values
 

cleeezzz

The Undead Ranger.
Reaction score
268
k good to know, thanks, will update
UPDATED

JASS:
library ProSys initializer Init requires GroupUtils

function interface OnRun takes unit u returns nothing

function interface UnitImpact takes unit d, unit u returns nothing

function interface OnDeath takes unit u returns nothing

globals
    group projectiles
    private P a
    private AP b
    private group g
    private unit TP
endglobals

private function GlobalFilter takes nothing returns boolean
    local unit fu = GetFilterUnit()
    if IsUnitInGroup(fu,a.g) == false and UnitAlive(fu) then
        call GroupAddUnit(a.g,fu)
        if IsUnitType(fu,UNIT_TYPE_STRUCTURE) then
            call a.UI.evaluate(a.p,fu)
        elseif IsUnitInRange(fu,TP,b.radius) and (IsUnitVisible(fu, GetOwningPlayer(TP)) or GetUnitAbilityLevel(GetFilterUnit(),&#039;BOwk&#039;) == 0 or GetUnitTypeId(fu) == &#039;oeye&#039;) then
            call a.UI.evaluate(a.p,fu)  
        endif
    else
        set fu = null
        return false
    endif
    set fu = null
    return true
endfunction
               
//=======================================================================
struct P
    //! runtextmacro PUI()
    player play
    unit p
    real x
    real y
    real maxd
    real r
    real cx
    group g
    OnRun OR
    UnitImpact UI
    OnDeath OD
    method periodic takes nothing returns nothing
        local P d = this
        local AP s
        local real x
        local real y
        local real f
        local real dist
        call GroupRefresh(projectiles)
        if not UnitAlive(d.p) then
            if d.p != null then
                if d.OD != 0 then
                    call d.OD.evaluate(d.p)
                endif
                call d.release()
                return 
            else
                debug call BJDebugMsg(&quot;|cffffcc00CPS ERROR: DO NOT REMOVE PROJECTILES USING THE OnRun/GroundImpact/UnitImpact/OnDeath FUNCTIONS!|r&quot;)
                call d.release()
                return 
            endif
            call GroupRemoveUnit(projectiles,d.p)
        endif
        set TP = d.p
        if d.OR != 0 then
            call d.OR.evaluate(d.p)
        endif
        set s = AP[d.p]
        set x = GetUnitX(d.p)
        set y = GetUnitY(d.p)
        set dist = SquareRoot((d.x-x)*(d.x-x) + (d.y-y)*(d.y-y))
        set d.cx = d.cx + dist
        set s.totaldistance = d.cx
        if d.cx &gt; d.maxd then
            if d.OD != 0 then
                call d.OD.evaluate(d.p)
            endif
            call d.release()
            return 
        endif
        set d.x = x
        set d.y = y
        set f = GetUnitFacing(d.p) * bj_DEGTORAD
        set x = x+s.currentspeed*Cos(f)
        set y = y+s.currentspeed*Sin(f)
        if s.currentspeed &lt; s.basespeed then
            set s.currentspeed = s.currentspeed + s.acceleration
        endif
        if s.anglechange != 0. and GetUnitTypeId(d.p) != &#039;h00K&#039; then    
            call SetUnitFacing(d.p, ModuloReal((GetUnitFacing(d.p)*bj_DEGTORAD) + s.anglechange,bj_PI*2)*bj_RADTODEG)
            set s.anglechange = 0.
        endif
        call SetUnitX(d.p,x)
        call SetUnitY(d.p,y)
        if s.radius &gt; 0 and d.UI != 0 then
            call GroupClear(g)
            set a = d
            set b = s
            call GroupEnumUnitsInRange(g,x,y,s.radius+40.,Filter(function GlobalFilter))
            if FirstOfGroup(g) == null then
                call GroupClear(d.g)
            endif
        endif
    endmethod
    
    static method create takes nothing returns P
        local P d = P.allocate()
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call GroupRemoveUnit(projectiles,.p)
        set .play = null
        call KillUnit(.p)
        set .p = null
        set .UI = 0
        set .OD = 0
        call .stopPeriodic()
    endmethod
    implement T32x
endstruct

function ProjectileAddEventOnRun takes unit proj, OnRun OR returns nothing
    set P(P[proj]).OR = OR
endfunction

function ProjectileAddEventUnitImpact takes unit proj, UnitImpact UI returns nothing
    set P(P[proj]).UI = UI
endfunction

function ProjectileAddEventOnDeath takes unit proj, OnDeath OD returns nothing
    set P(P[proj]).OD = OD
endfunction

function CPS takes integer ut, player play, real angle, real startX, real startY, real maxd returns unit
    local P d
    local AP z
    if maxd == 0 then
        debug call BJDebugMsg(&quot;|cffffcc00ProSys-CPS ERROR: No max distance|r&quot;)
        return null
    endif
    set d = P.create()
    set d.play = play
    set d.p = CreateUnit(play, ut, startX, startY, bj_RADTODEG*(angle))
    call GroupAddUnit(projectiles,d.p)
    set z = AP.create(d.p)
    set P[d.p] = d
    set AP[d.p] = z
    set d.maxd = maxd
    set d.x = startX
    set d.y = startY
    set d.cx = 0
    set d.g = NewGroup()
    set d.UI = 0
    set d.OD = 0
    set d.OR = 0
    call SetUnitPathing(d.p,false)
    call d.startPeriodic()
    return d.p
endfunction

private function Init takes nothing returns nothing
    set projectiles =CreateGroup()
    set g = CreateGroup()
endfunction

endlibrary

scope Bounded initializer Init

globals
    trigger BoundTrigger
endglobals

private function Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local P d = P<u>
    local AP z
    if d !=0 then
        set z = AP<u>
        if z != 0 then
            if z.bounded == true then
                call SetUnitX(u,SafeX(GetUnitX(u)))
                call SetUnitY(u,SafeY(GetUnitY(u)))
            elseif d.OD != 0 then
                call d.OD.evaluate(u)
                call d.release()
            endif
        endif
    else
        call KillUnit(u)
    endif
    set u = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger(  )
    set BoundTrigger = trig
    call TriggerAddAction( trig, function Actions )
endfunction

endscope
</u></u>


JASS:
library AP

struct AP
    //! runtextmacro PUI()
    unit projectile
    unit archer = null 
    player owner
    integer level = 0
    real anglechange = 0.
    real damage = 0.
    boolean bounded = false
    unit follow = null
    real interval = 0.
    real totaldistance = 0.
    real basespeed = 33.
    real currentspeed = 33.
    real acceleration = 0.3
    real radius = 60.
    static method create takes unit proj returns AP
        local AP d = AP.allocate()
        set d.projectile = proj
        set d.owner = GetOwningPlayer(proj)
        return d
    endmethod
    method onDestroy takes nothing returns nothing
        set .projectile = null
        set .owner = null
        set .follow = null
    endmethod
endstruct

endlibrary


anything else? testing its speed right now.

edit: a quick speed test shows frame rate dropping at 70 arrows
 

Sickle

New Member
Reaction score
13
Yes. SafeX and SafeY are useless functions. The correct way to do this would to be to require BoundSentinel.

You are also calling GroupEnumUnitsInRange a little too often for my taste. Every 0.1 seconds is quite enough, but somewhat less accurate.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top