• 🏆 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!

[vJASS] I can't learn vJASS

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564
Hello, vJASSers

I have asked many vJASSers about learning vJASS but every time I code a spell I fail.

Once before I asked for making a missile spell then after that I asked for making a Nova Spell. I tried to combine between the 2 spells. It worked but on a separate 2 trigger and linked them through some functions.

Now, I was attempting to make the spell in one trigger but it doesn't work. I know I asked too much about vJASS but please tell me what's wrong with my second script.

First Script (2 Separate Triggers)


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.03
        private constant real DIST = 32 // value of the distance cut every timer interval high value means high speed
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location missilePoint
        local location stepPoint
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set missilePoint = GetUnitLoc(d.missile)
                if DistanceBetweenPoints(missilePoint, d.CastPoint) < 25 then
                    call d.destroy()
                    call CreateNova(d.caster,GetLocationX(d.CastPoint),GetLocationY(d.CastPoint))
                else
                    set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(missilePoint, d.CastPoint))
                    call SetUnitPositionLoc(d.missile, stepPoint)
                    call RemoveLocation(stepPoint)
                endif
            call RemoveLocation(missilePoint)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit missile
        integer ID
        location CastPoint
 
        static method create takes unit caster, location target returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint = target
            set d.caster = caster
            set d.missile = CreateUnit(GetOwningPlayer(d.caster), UNIT_ID, GetUnitX(d.caster), GetUnitY(d.caster), 0)
            call SetUnitPosition(d.missile, GetUnitX(d.caster), GetUnitY(d.caster))
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
            call KillUnit(this.missile)
        endmethod
 
    endstruct
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit(), GetSpellTargetLoc()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope

JASS:
library AoETemplate initializer Init
    private keyword Data
 
    globals
        // Constants
        private constant real TIMER_INTERVAL = 0.015
        private constant real DIST = 16.00
        private constant real NOVA_RADIUS = 512.00 // radius of the Nova
        private constant integer NOVA_SIZE = 36 // number of the Nova Units
        private constant real dA = 360/NOVA_SIZE
        private constant integer UNIT_ID = 'e001' // rawcode of the Nova Unit
        private constant integer ABIL_ID = 'A000' // rawcode of the Dummy Spell
        private constant attacktype ATKTYPE = ATTACK_TYPE_MAGIC
        private constant damagetype DMGTYPE = DAMAGE_TYPE_FIRE
        private constant real AB_CONST_FACTOR = 50.00
        private constant real AB_LEVEL_FACTOR = 50.00
        private constant real AB_PREV_LVL_FACTOR = 0
        // Variables
        private timer tm = CreateTimer()
        private Data array temp_dat
        private integer index = 0
        private Data iFilter // Filter Data
        private boolexpr fFilter // Filter function
        private group TempGroup = CreateGroup()
    endglobals
    function Damage takes integer al returns real
        local real prevDmg
        if al == 1 then
            set prevDmg = 0
        else
            set prevDmg = Damage.evaluate(al -1)
        endif
        return AB_CONST_FACTOR + AB_LEVEL_FACTOR * al + AB_PREV_LVL_FACTOR * prevDmg
    endfunction
    private struct Data
        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 >= index
                set dat = temp_dat[i]
                // Nova Expanding
                set j = 0
                set a = 0
                set dat.expand = dat.expand + 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 index = index - 1
                    set temp_dat[i] = temp_dat[index]
                    set i = i - 1
                    call dat.destroy()
                endif
                set i = i + 1
            endloop
            set u = null
        endmethod
        static method create takes unit caster , real posX, real posY returns Data
            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(GetUnitAbilityLevel(dat.Caster,ABIL_ID))
            // the following function displays a msg of the amount of damage that the spell deals
            // make sure when you use it to remove the following function or add "//" before it
            //call DisplayTimedTextToPlayer(dat.Owner,0,0,10,"|cffff0000" + R2S(dat.dmg) + "|r")
            loop
                exitwhen i >= NOVA_SIZE
                set dat.Nova[i] = CreateUnit( dat.Owner , UNIT_ID , dat.Cx , dat.Cy , A )
                set A = A + dA
                set i = i + 1
            endloop
            if index == 0 then
                call TimerStart(tm,TIMER_INTERVAL,true,function Data.NovaExpand)
            endif
            set temp_dat[index] = dat
            set index = index + 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 index == 0 then
                call PauseTimer(tm)
            endif
        endmethod
    endstruct
        function CreateNova takes unit caster , real x , real y returns nothing
            call Data.create(caster,x,y)
        endfunction
        private function Init takes nothing returns nothing
            set fFilter = Filter(function Data.FilterUnit)
        endfunction
endlibrary



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.015
        private constant real N_TIMER_INTERVAL = 0.015
        private constant real M_DIST = 16.00
        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 player p , real damage , real posX, real posY returns NovaData
            local thistype dat = thistype.allocate()
            local integer i = 0
            local real A = 0.00
            set dat.Owner = p
            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 Missile
        player Owner
        location CastPoint
        real Damage
        real face
 
        static method MoveMissile takes nothing returns nothing
            local MislData dat
            local integer i = 0
            local location MLoc
            loop
                exitwhen i >= mindex
                set dat = temp_mdat[i]
                set MLoc = GetUnitLoc(dat.Missile)
call DisplayTimedTextToPlayer(Player(0),0,0,6,R2S(DistanceBetweenPoints(MLoc,dat.CastPoint)))
                if DistanceBetweenPoints(MLoc , dat.CastPoint) < 64 then
                    set mindex = mindex - 1
                    set temp_mdat[i] = temp_mdat[mindex]
                    set i = i - 1
                    call NovaData.create(dat.Owner , dat.Damage , GetLocationX(MLoc) , GetLocationY(MLoc))
                    call dat.destroy()
                endif
                call SetUnitX(dat.Missile , GetLocationX(MLoc) + M_DIST * Cos(bj_DEGTORAD * dat.face))
                call SetUnitY(dat.Missile , GetLocationY(MLoc) + M_DIST * Sin(bj_DEGTORAD * dat.face))
 
                call RemoveLocation(MLoc)
                set i = i + 1
            endloop
        endmethod
        static method create takes unit u , location target returns MislData
            local thistype dat = thistype.allocate()
            local real X
            local real Y
            set dat.CastPoint = target
            set dat.Owner = GetOwningPlayer(u)
            set dat.face = AngleBetweenPoints(GetUnitLoc(u) , dat.CastPoint)
            set dat.Missile = CreateUnit( dat.Owner , M_UNIT_ID , GetUnitX(u) , GetUnitY(u) , 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 temp_mdat[mindex] = dat
            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


I know that I used this script more than once in my threads but I do that to learn completely how to make AoE spells (Target and self Target) using vJASS. So please vJASSers don't lose your patience or temper.

And thanks for any help in advance.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Maybe tell us what is bugged?
The 2 separate triggers don't bug.

In the second the missile don't stop at the casting point so the Nova starts (it sometimes work and other time when casted on unit don't work) and the damage function don't work, I don't know the problem in the function or in the group picking function.

<< EDIT >>

I think the problem is with the group picking function as I inserted a text function to display the amount of damage dealt by the spell and it displayes 100 but the unit didn't take damage though.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
The 2 functions that sets the missile unit new location don't move it to the exact point.

JASS:
call SetUnitX(dat.Missile , GetLocationX(MLoc) + M_DIST * Cos(bj_DEGTORAD * dat.face))
call SetUnitY(dat.Missile , GetLocationY(MLoc) + M_DIST * Sin(bj_DEGTORAD * dat.face))

There is a deviation in the path of the missile away from the cast point which cause the check distance condition to be FALSE. What's wrong with those 2 functions ?

Also the Group picking function doesn't work, why ?
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
I have solved the Group picking function and have known why the spell didn't damage the units; the function

JASS:
call UnitDamageTarget(dat.Caster,u,dat.dmg,false,false,ATKTYPE,DMGTYPE,null)

takes a unit in its arguments I forget to transfer the caster data from MislData Struct to NovaData Struct. So this problem is solved but still the other problem of moving the missile dummy unit; it goes far from the cast point causing the nova to begin at a shifted point from the cast point if I increased the check distance and if I decrease the check distance the missile don't stop and continue till the game crashes (the check distance is M_DIST * 3)

(Must I wait 48 hr to bump a thread or I will lose rep ?)
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
The template uses Locations, thats bad. Also I see what you are trying to do, but not what happens wrong? What exactly happens, and what not?

Look at the first post it has 2 scripts.

When I run the first script which is consisted of 2 triggers, the point at which the nova starts is exactly the same as the cast point.

But in the 2nd combined script the point at which the nova starts is shifted although the moving equation is right.

In the first script the check distance is 25 which is smaller than the step at which the nova unit moves every timer expiry while in the second script I have no choice but to make the check distance is M_DIST * 3 (M_DIST = 32) so that the missile stops and the nova starts.

Btw, thanks for looking into my post. I have been wait for ages to get a reply. I will also attach my vJASS learning map as well.
 

Attachments

  • vJASS Learning.w3x
    49.9 KB · Views: 72
Level 16
Joined
Mar 3, 2006
Messages
1,564
I have no idea what that means...

The check distance is the condition that if True the missile dummy unit will not move any further and the onDestroy is called then the nova starts. The PROBLEM is that the point is shifted from the cast point which means that the distance between this cast point and the missile location never statisfies the condition.

Check this part of the 2nd script; its the struct of the missile unit
JASS:
private struct MislData

        static method MoveMissile takes nothing returns nothing
            local MislData dat
            local integer i = 0
            local location MLoc
            loop
                exitwhen i >= mindex
                set dat = temp_mdat[i]
                set MLoc = GetUnitLoc(dat.Missile)
call DisplayTimedTextToPlayer(Player(0),0,0,0.5,R2S(DistanceBetweenPoints(MLoc,dat.CastPoint)))
                if DistanceBetweenPoints(MLoc , dat.CastPoint) < M_DIST * 3 then  // This is the check condition, see the distance must be M_DIST * 3
                    set mindex = mindex - 1
                    set temp_mdat[i] = temp_mdat[mindex]
                    set i = i - 1
                    call NovaData.create(dat.Caster , dat.Damage , GetLocationX(MLoc) , GetLocationY(MLoc))
                    call dat.destroy()
                else
                    call SetUnitX(dat.Missile , GetUnitX(dat.Missile) + M_DIST * Cos(dat.face * bj_DEGTORAD))
                    call SetUnitY(dat.Missile , GetUnitY(dat.Missile) + M_DIST * Sin(dat.face * bj_DEGTORAD))
                endif
                
                call RemoveLocation(MLoc)
                set i = i + 1
            endloop
        endmethod

Now in the 1st script
JASS:
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location missilePoint
        local location stepPoint
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set missilePoint = GetUnitLoc(d.missile)
                if DistanceBetweenPoints(missilePoint, d.CastPoint) < DIST * 0.5 then // see now the check distance is small
                    call d.destroy()
                    call CreateNova(d.caster,GetLocationX(d.CastPoint),GetLocationY(d.CastPoint))
                else
                    set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(missilePoint, d.CastPoint))
                    call SetUnitPositionLoc(d.missile, stepPoint)
                    call RemoveLocation(stepPoint)
                endif
            call RemoveLocation(missilePoint)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction

Also try the attached map to understand the problem more - of course if you are willing to help me :wink: -
 
Level 11
Joined
Feb 22, 2006
Messages
752
The PROBLEM is that the point is shifted from the cast point which means that the distance between this cast point and the missile location never statisfies the condition.

That's impossible. Let's say you are going to move the missile x distance the next timer loop. And let's say your target is y distance away from the missile. There are only TWO possible situations:

1. y > x, in which case you move the missile normally the full x distance toward the target.
2. y <= x, in which case you move the missile to the target point and then destroy it and do w/e you need to do on missile destruction.

It doesn't matter if your missile has variable speed or if your target is moving. You will always get one of the two situations above.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
It worked Anachron thanks but that shift in points still exists and is really ugly and not logic I don't know why such shift exist; doest it have something to do with the SetUnitX and SetUnitY functions because when I used SetUnitPositionLoc in the non-bugged script there in no such shift.

But I wast told that SetUnitPositionLoc takes the ground pathing into account while SetUnitX and SetUnitY don't. Well I don't know what's wrong and that problem is still confusing me but thanks for the advice.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Yes, SetUnitPositionLoc takes pathing in account, while the SetUnitX and SetUnitY just replace the units coordinates.

Its totally logic,... The SetUnitPositionLoc instantly moves a unit from X to Y and it checks pathing on the aim area... Its the same pathing check as CreateUnit() has, I guess.
So that shift exist because the function moves the unit 2 times; the first to a new X with Y constant and the second move to new Y with X constant ?
 
So that shift exist because the function moves the unit 2 times; the first to a new X with Y constant and the second move to new Y with X constant ?
No... That would be dumb.

Function SetUnitLocation()
  • call SetUnitX(new)
  • call SetUnitY(new)
  • set AIM = PathingCheck() (This does not exist)
  • call SetUnitX(GetLocationX(AIM))
  • call SetUnitY(GetLocationY(AIM))

Nobody knows what is happening, but I think its something like that.
 
Status
Not open for further replies.
Top