• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Location and Coordinates

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564
I don't know if anyone (vJASSers) remember about a problem I have in a spell that I posted in an old thread; that problem was about a shift in points between the target location and the actual location that the dummy unit stops at.

I tried to make a separate trigger to test location functions and X,Y coordinate functions, the trigger looked like this:


JASS:
globals
    constant real T_INT = 0.01
    constant real DISP = 5.00
    unit u
    location l
    real xl
    real yl
    real Angle
    timer tm = CreateTimer()
endglobals
function Move takes nothing returns nothing
    local real X = GetUnitX(u)
    local real Y = GetUnitY(u)
    set X = X + DISP*Cos(Angle)
    set Y = Y + DISP*Sin(Angle)
    call SetUnitPosition(u,X,Y)
    if DistanceBetweenPoints(GetUnitLoc(u),l) <= 2.5 then
        call PauseTimer(tm)
        call RemoveLocation(l)
    endif
endfunction
function Create takes nothing returns nothing
    set u = CreateUnit(Player(0),'ewsp',0,0,0)
    set l = GetSpellTargetLoc()
    set xl = GetLocationX(l)
    set yl = GetLocationY(l)
    //call CreateItem('btst',xl,yl)
    set Angle = Atan2(yl - GetUnitY(u), xl - GetUnitX(u))
    call TimerStart(tm,T_INT,true,function Move)
endfunction
//===========================================================================
function InitTrig_XY_move takes nothing returns nothing
    set gg_trg_XY_move = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ(gg_trg_XY_move,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( gg_trg_XY_move, function Create )
endfunction


This worked fine and the unit goes exactly at the target w/o any shift. So whats wrong with my old script:

JASS:
scope FireNova initializer Init
    private keyword NovaData
    private keyword MislData
 
    globals
        // Constants
        private constant integer ABIL_ID = 'A000' // rawcode of the Dummy Spell
        private constant integer M_UNIT_ID = 'e000' // rawcode of the Missile Unit
        private constant integer N_UNIT_ID = 'e001' // rawcode of the Nova Unit
        private constant integer NOVA_SIZE = 36 // number of the Nova Units
        private constant real M_TIMER_INTERVAL = 0.03
        private constant real N_TIMER_INTERVAL = 0.015
        private constant real M_DIST = 32
        private constant real N_DIST = 16.00
        private constant real NOVA_RADIUS = 544.00 // radius of the Nova
        private constant real dA = 360/NOVA_SIZE
        private constant real AB_CONST_FACTOR = 50.00
        private constant real AB_LEVEL_FACTOR = 50.00
        private constant real AB_PREV_LVL_FACTOR = 1
        private constant attacktype ATKTYPE = ATTACK_TYPE_MAGIC
        private constant damagetype DMGTYPE = DAMAGE_TYPE_FIRE
        // Variables
        private timer mtm = CreateTimer()
        private timer ntm = CreateTimer()
        private MislData array temp_mdat
        private NovaData array temp_ndat
        private integer mindex = 0
        private integer nindex = 0
        private NovaData iFilter // Filter Data
        private boolexpr fFilter // Filter function
        private group TempGroup = CreateGroup()
    endglobals
    function NovaDamage takes integer al returns real
        local real prevDmg
        if al == 1 then
            set prevDmg = 0
        else
            set prevDmg = NovaDamage.evaluate(al -1)
        endif
        return AB_CONST_FACTOR + AB_LEVEL_FACTOR * al + AB_PREV_LVL_FACTOR * prevDmg
    endfunction
    private struct NovaData
        unit Caster
        unit array Nova[NOVA_SIZE]
        real expand
        real Cx
        real Cy
        player Owner
        group dGroup = CreateGroup() // Damaged Units Group
        real dmg
        static method NovaExpand takes nothing returns nothing
            local thistype dat
            local integer i = 0
            local integer j
            local real x
            local real y
            local real a
            local unit u
            loop // this loop cycles through all the casters which have cast the spell
                exitwhen i >= nindex
                set dat = temp_ndat[i]
                // Nova Expanding
                set j = 0
                set a = 0
                set dat.expand = dat.expand + N_DIST
                loop
                    exitwhen j >= NOVA_SIZE
                    set x = dat.Cx + dat.expand * Cos( a * bj_DEGTORAD )
                    set y = dat.Cy + dat.expand * Sin( a * bj_DEGTORAD )
                    call SetUnitX( dat.Nova[j] , x )
                    call SetUnitY( dat.Nova[j] , y )
                    set j = j + 1
                    set a = a + dA
                endloop
                // Damage Units that the nova has reached
                set iFilter = dat
                call GroupEnumUnitsInRange(TempGroup, dat.Cx, dat.Cy, dat.expand, fFilter)
                loop
                    set u = FirstOfGroup(TempGroup)
                    exitwhen u == null
                    call GroupAddUnit(dat.dGroup, u)
                    call UnitDamageTarget(dat.Caster, u, dat.dmg, false, false, ATKTYPE, DMGTYPE, null)
                    call GroupRemoveUnit(TempGroup, u)
                endloop
                if dat.expand >= NOVA_RADIUS then // check if the nova has reached its max AoE
                    set nindex = nindex - 1
                    set temp_ndat[i] = temp_ndat[nindex]
                    set i = i - 1
                    call dat.destroy()
                endif
                set i = i + 1
            endloop
            set u = null
        endmethod
        static method create takes unit caster , real damage , real posX, real posY returns NovaData
            local thistype dat = thistype.allocate()
            local integer i = 0
            local real A = 0.00
            set dat.Caster = caster
            set dat.Owner = GetOwningPlayer(dat.Caster)
            set dat.Cx = posX
            set dat.Cy = posY
            set dat.dmg = damage
            call DisplayTimedTextToPlayer(dat.Owner,0,0,10,"|cffff0000" + R2S(dat.dmg) + "|r")
            loop
                exitwhen i >= NOVA_SIZE
                set dat.Nova[i] = CreateUnit( dat.Owner , N_UNIT_ID , dat.Cx , dat.Cy , A )
                set A = A + dA
                set i = i + 1
            endloop
            if nindex == 0 then
                call TimerStart(ntm,N_TIMER_INTERVAL,true,function NovaData.NovaExpand)
            endif
            set temp_ndat[nindex] = dat
            set nindex = nindex + 1
            return dat
        endmethod
        static method FilterUnit takes nothing returns boolean
            return GetWidgetLife(GetFilterUnit()) >= .305 and IsUnitEnemy(GetFilterUnit(),iFilter.Owner) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitInGroup(GetFilterUnit(),iFilter.dGroup)
        endmethod
        method onDestroy takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i >= NOVA_SIZE
                call KillUnit(.Nova[i])
                set .Nova[i] = null
                set .expand = 0
                set i = i + 1
            endloop
            if nindex == 0 then
                call PauseTimer(ntm)
            endif
        endmethod
    endstruct
 
    private struct MislData
        unit Caster
        unit Missile
        location CastPoint
        real Distance
        real Damage
        real face
 
        static method MoveMissile takes nothing returns nothing
            local MislData dat
            local integer i = 0
            local real X = 0
            local real Y = 0
            loop
                exitwhen i >= mindex
                set dat = temp_mdat[i]
                set dat.Distance = dat.Distance - M_DIST
                set X = GetUnitX(dat.Missile) + M_DIST * Cos(dat.face * bj_DEGTORAD)
                set Y = GetUnitY(dat.Missile) + M_DIST * Sin(dat.face * bj_DEGTORAD)
                if dat.Distance < M_DIST * 0.75 then
                    set mindex = mindex - 1
                    set temp_mdat[i] = temp_mdat[mindex]
                    set i = i - 1
                    call NovaData.create(dat.Caster , dat.Damage , GetUnitX(dat.Missile) , GetUnitY(dat.Missile))
                    call dat.destroy()
                else
                    call SetUnitPosition(dat.Missile,X,Y)
                endif
                set i = i + 1
            endloop
        endmethod
        static method create takes unit u , location target returns MislData
            local MislData dat = MislData.allocate()
            set temp_mdat[mindex] = dat
            set dat.Caster = u
            set dat.CastPoint = target
            set dat.Distance = 0
            set dat.Distance = DistanceBetweenPoints(GetUnitLoc(dat.Caster),dat.CastPoint)
            set dat.face = AngleBetweenPoints(GetUnitLoc(dat.Caster) , dat.CastPoint)
            set dat.Missile = CreateUnit(GetOwningPlayer(dat.Caster),M_UNIT_ID,GetUnitX(dat.Caster),GetUnitY(dat.Caster),dat.face)
            set dat.Damage = NovaDamage(GetUnitAbilityLevel(u , ABIL_ID))
            if mindex == 0 then
                call TimerStart(mtm,M_TIMER_INTERVAL,true,function MislData.MoveMissile)
            endif
            set mindex = mindex + 1
            return dat
        endmethod
 
        method onDestroy takes nothing returns nothing
            call KillUnit(.Missile)
            set .Missile = null
            set .face = 0
            call RemoveLocation(.CastPoint)
            if mindex == 0 then
                call PauseTimer(mtm)
            endif
        endmethod
    endstruct
        private function Conditions takes nothing returns boolean
            return GetSpellAbilityId() == ABIL_ID
        endfunction
 
        private function Actions takes nothing returns nothing
            call MislData.create(GetTriggerUnit(), GetSpellTargetLoc())
        endfunction
 
        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t,Condition(function Conditions))
            call TriggerAddAction(t,function Actions)
            set fFilter = Filter(function NovaData.FilterUnit)
        endfunction
endscope

info
For more information about the old thread Click Here
Reward
Reputation for anyone who will help me
 
Level 11
Joined
Feb 22, 2006
Messages
752
JASS:
                if dat.Distance < M_DIST * 0.75 then
                    set mindex = mindex - 1
                    set temp_mdat[i] = temp_mdat[mindex]
                    set i = i - 1
                    call NovaData.create(dat.Caster , dat.Damage , GetUnitX(dat.Missile) , GetUnitY(dat.Missile))
                    call dat.destroy()
                else
                    call SetUnitPosition(dat.Missile,X,Y)
                endif

You never actually move the missile to the target point in the if block.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
After some testing, I made the triggers like this

JASS:
    private struct MislData
        unit Caster
        unit Missile
        location CastPoint
        real Distance
        real Damage
        real face
        
        static method MoveMissile takes nothing returns nothing
            local MislData dat
            local integer i = 0
            local real X = 0
            local real Y = 0
            loop
                exitwhen i >= mindex
                set dat = temp_mdat[i]
                set dat.Distance = dat.Distance - M_DIST
                set X = GetUnitX(dat.Missile)
                set Y = GetUnitY(dat.Missile)
                set X = X + M_DIST * Cos(dat.face)
                set Y = Y + M_DIST * Sin(dat.face)
                //if DistanceBetweenPoints(GetUnitLoc(dat.Missile),dat.CastPoint) < M_DIST then
                if dat.Distance < M_DIST * 0.75 then
                    set mindex = mindex - 1
                    set temp_mdat[i] = temp_mdat[mindex]
                    set i = i - 1
                    //call NovaData.create(dat.Caster , dat.Damage , GetUnitX(dat.Missile) , GetUnitY(dat.Missile))
                    call dat.destroy()
                else
                    call SetUnitPosition(dat.Missile,X,Y)
                endif
                set i = i + 1
            endloop
        endmethod
        static method create takes unit u , location target returns MislData
            local MislData dat = MislData.allocate()
            local real x1
            local real y1
            local real x2
            local real y2
            local real dx
            local real dy
            set temp_mdat[mindex] = dat
            set dat.Caster = u
            set x1 = GetUnitX(dat.Caster)
            set y1 = GetUnitY(dat.Caster)
            set dat.CastPoint = target
            set x2 = GetLocationX(dat.CastPoint)
            set y2 = GetLocationY(dat.CastPoint)
            set dat.Distance = 0
            set dx = x2 - x1
            set dy = y2 - y1
            set dat.Distance = SquareRoot(dx * dx + dy * dy)
            //call DisplayTimedTextToPlayer(Player(0),0,0,10,"|cffff0000" + R2S(dat.Distance) + "|r")
            set dat.face = Atan2(y2 - y1, x2 - x1)
            set dat.Missile = CreateUnit(GetOwningPlayer(dat.Caster),M_UNIT_ID,x1,y1,dat.face)
            call SetUnitPosition(dat.Missile,x1,y1)
            set dat.Damage = NovaDamage(GetUnitAbilityLevel(u , ABIL_ID))
            if mindex == 0 then
                call TimerStart(mtm,M_TIMER_INTERVAL,true,function MislData.MoveMissile)
            endif
            set mindex = mindex + 1
            return dat
        endmethod
        
        method onDestroy takes nothing returns nothing
            call KillUnit(.Missile)
            set .Missile = null
            set .face = 0
            call RemoveLocation(.CastPoint)
            if mindex == 0 then
                call PauseTimer(mtm)
            endif
        endmethod
    endstruct


I am not sure about that but I found that
JASS:
set dat.Caster = u
set x1 = GetUnitX(dat.Caster)
set y1 = GetUnitY(dat.Caster)
set dat.CastPoint = target
set x2 = GetLocationX(dat.CastPoint)
set y2 = GetLocationY(dat.CastPoint)
set dat.face = Atan2(y2 - y1, x2 - x1)

is better and more accurate than

JASS:
set dat.face = Atan2(GetLocationY(dat.CastPoint) - GetUnitY(dat.Caster), GetLocationY(dat.CastPoint) - GetUnitX(dat.Caster))

the same for setting the distance. Though I am not sure if it is right or not but I made it like that and it works fine.
 
Level 11
Joined
Feb 22, 2006
Messages
752
JASS:
set X = GetUnitX(dat.Missile) + M_DIST * Cos(dat.face * bj_DEGTORAD)
set Y = GetUnitY(dat.Missile) + M_DIST * Sin(dat.face * bj_DEGTORAD)

You only set the variables there. You need to make calls to SetUnitX/Y or SetUnitPosition to actually move the unit.

And the two scripts you post do the exact same thing. It makes no difference whether you store the data in variables or not; it's not like they suddenly become more accurate or have more decimal places once inside a variable.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Then what was wrong with the old code beside the mistake Ghost Wolf mentioned, its driving me crazy. I checked the code about the location functions and everything seems to be good and perfect and when the line that checks the distance between the position of the dummy unit and the target point is executed it always returns false because the distance never become equal to the true condition due to that shift.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Why would it do that?

I found that I was right; moving the unit at the caster location after creating it solved everything. The create function doesn't create the unit exactly at the caster location instead it creates it with a shift from his position which cause the problem.

I found that the line
JASS:
call SetUnitPosition(dat.Missile,GetUnitX(dat.Caster),GetUnitY(dat.Caster))
after
JASS:
set dat.Missile = CreateUnit(GetOwningPlayer(dat.Caster),M_UNIT_ID,GetUnitX(dat.Caster),GetUnitY(dat.Caster),dat.face)
solved this whole thing.

It will be like this
JASS:
set dat.Missile = CreateUnit(GetOwningPlayer(dat.Caster),M_UNIT_ID,GetUnitX(dat.Caster),GetUnitY(dat.Caster),dat.face)
call SetUnitPosition(dat.Missile,GetUnitX(dat.Caster),GetUnitY(dat.Caster))

And thanks to all your attempts to help me.
 
Status
Not open for further replies.
Top