Starting JASS

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Looks nice.
For preloading abilities, I suggest you to make a library which is specialized for preloading.
This can prevent too much of dummies being used to preload abilities.
 

Ayanami

칼리
Reaction score
288
Alright, now proceeding on using timers. So I decided to make a simple area knock back ability. Now the problem is, how do I actually add the units in a group and move them. I was told to use this method when handling groups, but this method only works when it isn't periodic, right? So, do I use ForGroup?
 

13lade619

is now a game developer :)
Reaction score
399
just make use of a timer system and a struct.

KeyTimers2 by Jesus4Lyf is good.

Make a struct with the caster and the group.
forward the struct to the callback function through the KT_Add() function.
retrieve the struct on the callback function.

EDIT: *easier.

grab a knockback system.
run through your unitgroup as usual,
use the knockback function on the individual units.
 

Ayanami

칼리
Reaction score
288
Hmm, although I roughly know what structs are, I didn't use them before, so It's quite confusing :/

You know in GUI action, there is a "Pick Every Unit UnitGroup and Do Actions" right? Is it inefficient to use that?
 

13lade619

is now a game developer :)
Reaction score
399
IIRC, there is no way to pass 'stuff' from one function to another.

*editing*

First, there were Global vars. But using them does not permit multi instanciability.

Then, Local Handle Vars were created. Primarily using the gamcache to 'store' stuff temporarily so that you can use them in another function.

What's used up to now is Timer-Struct attachment. Basically, you have a struct and pass it to a timer then you can retrieve the data you passed on the callback using the timer as reference.
 

Ayanami

칼리
Reaction score
288
Alright, finished the a simple area knockback using KeyTimers2. Didn't take deceleration into account yet, just need to check if the codes are okay. Here it is:

JASS:

library WarStomp initializer InitTrig

globals
    private constant integer ABIL_ID = 'ABMS'
    private constant integer DUMMY_ID = 'h001'
    private constant real AOE = 300.00
    private constant real DAMAGE = 200.00
    private constant real DISTANCE = 500.00
    private constant real TIME = 1.00
endglobals

globals
    private real x
    private real y
    private filterfunc ff
endglobals

private struct knockback
    real a
    real d
    real s
    unit u
endstruct

private function Periodic takes nothing returns boolean
    local knockback d = KT_GetData()
    if d.d <= 0 then
        call d.destroy()
        return true
    else
        set x = GetUnitX(d.u) + d.s * Cos(d.a * bj_DEGTORAD)
        set y = GetUnitY(d.u) + d.s * Sin(d.a * bj_DEGTORAD)
        call SetUnitPosition(d.u, x, y)
        set d.d = d.d - d.s
    endif
    return false
endfunction

private function Start takes unit caster, unit target, real dist, real time, real damage returns nothing
    local knockback d = knockback.create()
    local location cl = GetUnitLoc(caster)
    local location tl = GetUnitLoc(target)
    call UnitDamageTarget(caster, target, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
    set d.d = dist
    set d.s = dist/time * 0.03
    set d.a = AngleBetweenPoints(cl, tl)
    set d.u = target
    call KT_Add(function Periodic, d, 0.03)
    call RemoveLocation(cl)
    call RemoveLocation(tl)
    set cl = null
    set tl = null
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABIL_ID
endfunction

private function Execute takes nothing returns boolean
    local unit c = GetTriggerUnit()
    local unit u = GetFilterUnit()
    if not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_DEAD) and IsUnitEnemy(u, GetOwningPlayer(c)) then
        call Start(c, u, DISTANCE, TIME, DAMAGE)
    endif
    return false
endfunction

private function Actions takes nothing returns nothing
    call GroupEnumUnitsInRange(GROUP, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), AOE, ff)
endfunction

//===========================================================================
private function InitTrig 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 ff = Filter(function Execute)
endfunction

endlibrary
 

13lade619

is now a game developer :)
Reaction score
399
1 Scopes are better for spells generally

2

would be for simplicity
JASS:
 call GroupEnumUnitsInRange(GROUP, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), AOE, Condition(function Execute))


3 Does triggering unit get passed to Execute? i dont recall since i dont use this method... i use the loop construct for groups generally.

4 Use real coordinates directly, not locations to avoid leak removal.

5 SetUnitPosition() interrupts orders though.
SetUnitX() and SetUnitY() would ignore pathing at first so you have to have extra checks.
 

Ayanami

칼리
Reaction score
288
3 Does triggering unit get passed to Execute? i dont recall since i dont use this method... i use the loop construct for groups generally.

Yup, it does.

4 Use real coordinates directly, not locations to avoid leak removal.

How would I work around doing AngleBetweenPoints check without using locations?

5 SetUnitPosition() interrupts orders though.
SetUnitX() and SetUnitY() would ignore pathing at first so you have to have extra checks.

So basically, use SetUnitX() and SetUnitY()?
 

hgkjfhfdsj

Active Member
Reaction score
55
How would I work around doing AngleBetweenPoints check without using locations?
JASS:
    //..
    set x = IsPathable(GetUnitX(d.u) + d.x) //some condition
    set y = IsPathable(GetUnitY(d.u) + d.y)//some condition
   
    call SetUnitX(d.u, d.x)
    call SetUnitY(d.u, d.y)
    // -----
    local real angle = Atan2(GetUnitY(target) - GetUnitY(caster), GetUnitX(target) - GetUnitX(caster)) //radian
    //can probably store calculated values since d.s & angle doesnt change
    set d.x = dist/time * 0.03 + Cos(angle)
    set d.y = dist/time * 0.03 + Sin(angle)


EDIT
+Post 81
TESH also comes with a function list
 

Bribe

vJass errors are legion
Reaction score
67
Crack open the good ol' blizzard.j file (a copy of it comes with JassHelper or JNGP, or you can use an MPQ extractor to grab it from the .mpq file). Inside of this "magical" file, you will find [ljass]AngleBetweenPoints[/ljass] to be a load of crap as that converts your locations to coordinates before doing any calculations.

Same with [ljass]PolarProjectionBJ[/ljass], [ljass]DistanceBetweenPoints[/ljass] and all the others. There are a lot of BJ functions that serve a purpose, but the majority of them are more or less useless :p
 

tooltiperror

Super Moderator
Reaction score
231
Or, you can just click on them in the (L)JASS tags and get brought to the wiki page and read the actions they call.
 

Ayanami

칼리
Reaction score
288
Just asking, how do I retrieve the damage source and the damaged unit when using J4L's Damage System?
 

Ayanami

칼리
Reaction score
288
Need help again. I have this trigger so far.

JASS:

scope ChargingStrikes initializer InitTrig

globals
    private constant integer ABIL_ID1 = 'ABCS' // raw code of Ability "Charging Strikes"
    private constant integer ABIL_ID2 = 'ACS1' // raw code of Ability "Charging Strikes (Attack Speed)"
    private constant real MINDMG = 23.00 // minimum damage dealt
    private constant real MAXDMG = 25.00 // maximum damage dealt
    private constant real TIME = 0.25 // time taken for charge
    private constant real AOE = 200.00 // area of effect of the damage
    private constant boolean STR = false // true if the hero's primary attribute is strength
    private constant boolean AGI = true // true if the hero's primary attribute is agility
    private constant boolean INT = false // true if the hero's primary attribute is intelligence
    // if all is set to false, damage dealt will be random number between MINDMG and MAXDMG
endglobals

private struct cs
    real offsetx
    real offsety
    real dist
    real speed
    unit u
endstruct

private function Slide takes nothing returns boolean
    local cs d = KT_GetData()
    local real x = GetUnitX(d.u) + d.offsetx
    local real y = GetUnitY(d.u) + d.offsety
    if d.dist <= 0 then
        call SetUnitPathing(d.u, true)
        call d.destroy()
        return true
    elseif not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
        call SetUnitPathing(d.u, false)
        call SetUnitX(d.u, x)
        call SetUnitY(d.u, y)
    endif
    set d.dist = d.dist - d.speed
    return false
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABIL_ID1
endfunction

private function Actions takes nothing returns nothing
    local cs d = cs.create()
    local location t = GetSpellTargetLoc()
    local real dx = GetLocationX(t)
    local real dy = GetLocationY(t)
    local real angle
    local real x
    local real y
    set d.u = GetTriggerUnit()
    set x = GetUnitX(d.u)
    set y = GetUnitY(d.u)
    set angle = Atan2(dy - y, dx - x)
    set d.dist = SquareRoot(((x - dx) * (x - dx)) + ((y - dy) * (y - dy)))
    set d.speed = d.dist / TIME * 0.03
    set d.offsetx = d.speed * Cos(angle)
    set d.offsety = d.speed * Sin(angle)
    call KT_Add(function Slide, d, 0.03)
    call RemoveLocation(t)
    set t = null
endfunction

private function GetDamage takes unit caster returns real
    if STR then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroStr(caster, true)) 
    elseif AGI then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroAgi(caster, true)) 
    elseif INT then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroInt(caster, true)) 
    endif
    return GetRandomReal(MINDMG, MAXDMG)
endfunction

private function InitTrig 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)
endfunction

endscope


Now I'm having problems with the damaging part. This trigger is supposed to make the caster "slide" to the targeted point and dealing damage. However, I'm having trouble with dealing the damage as I need a method to check if the enemy has been already hit. Also, how would I deal damage without memory leaks?
 

13lade619

is now a game developer :)
Reaction score
399
I'm having trouble with dealing the damage as I need a method to check if the enemy has been already hit.
I find that using indexing systems (like AIDS, by J4L again) to perform simple integer or boolean checks for each unit works well.

like if [ljass]ChargingStrikesHit[target]==true[/ljass] or something.
 

Laiev

Hey Listen!!
Reaction score
188
instead [ljass]GetSpellTargetLoc()[/ljass] use [ljass]GetSpellTargetX()[/ljass] and [ljass]GetSpellTargetY()[/ljass] (i think is those natives)

EDIT:
JASS:
private function Actions takes nothing returns nothing
    local cs d = cs.create()
    local location t = GetSpellTargetLoc()
    local real dx = GetLocationX(t)
    local real dy = GetLocationY(t)
    local real angle
    local real x
    local real y
    set d.u = GetTriggerUnit()
    set x = GetUnitX(d.u)
    set y = GetUnitY(d.u)
    set angle = Atan2(dy - y, dx - x)
    set d.dist = SquareRoot(((x - dx) * (x - dx)) + ((y - dy) * (y - dy)))
    set d.speed = d.dist / TIME * 0.03
    set d.offsetx = d.speed * Cos(angle)
    set d.offsety = d.speed * Sin(angle)
    call KT_Add(function Slide, d, 0.03)
    call RemoveLocation(t)
    set t = null
endfunction

>>
JASS:
private function Actions takes nothing returns nothing
    local cs d = cs.create()
    local real dx = GetSpellTargetX()
    local real dy = GetSpellTargetY()
    local real x = GetUnitX(d.u)
    local real y = GetUnitY(d.u)
    local real = angle Atan2(dy - y, dx - x)

    set d.u = GetTriggerUnit()
    set d.dist = SquareRoot(((x - dx) * (x - dx)) + ((y - dy) * (y - dy)))
    set d.speed = d.dist / TIME * 0.03
    set d.offsetx = d.speed * Cos(angle)
    set d.offsety = d.speed * Sin(angle)

    call KT_Add(function Slide, d, 0.03)
endfunction
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Because it is static method, just like normal functions.
Can you put call BJDebugMsg(R2S(this)) in normal functions?
I think you know the answer..
Methods are actually
JASS:
static method my takes thistype this returns nothing
endmethod

That's why you can call the debug msg in methods.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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