• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Understanding Forces

Status
Not open for further replies.
Level 3
Joined
Jul 15, 2007
Messages
36
Hi, just working on a spell

At this stage, i am trying to filter in or out friendly units based on a constant function.

JASS:
//Constant Functions: used to customise the spell

//***  Ability Definitions
//***  Set the ability that triggers Frostburn and the buff that enables it's alternate form
constant function Fb_Ability takes nothing returns integer
    //This is the ability that triggers Frostburn
    return 'A000'
endfunction

constant function Fb_Switch takes nothing returns integer
    //This is the ability that determines whether or not the frost or fire variant is cast.
    //that means, if this buff ability is active, then the non-default spell is cast (usually fire)
    return 'B000'
endfunction

//*** Spell customisation
//*** Sets the way the spell interacts with the environment
constant function Fb_Default takes nothing returns boolean
    //If set to true, frost is the default spell, and fire is cast when the buff is present
    //If set to false, fire is the default, and frost is cast when the buff is present
    return true
endfunction

constant function Fb_FriendorFoe takes nothing returns boolean
    //If set to true, does not affect friendly units (own, allied)
    //If set to false, damages friends and foe alike
    return true
endfunction

constant function Fb_Attacktype takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC // Should ignore armour
    //return ATTACK_TYPE_CHAOS // Does not ignore armour, but equal to all armour types
    //return ATTACK_TYPE_NORMAL //etc....
endfunction

constant function Fb_Damagetype takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC // Should ignore armour
    //return DAMAGE_TYPE_NORMAL // Does not ignore armour, but equal to all armour types
    //return  //etc....
endfunction


//***  Spell Numbers
//***  Sets damage, AoE, etc.
constant function Fb_AOE takes nothing returns real
    //This is the AoE of the spell in real units
    //The spell effect is spread out over this area
    return 300.
endfunction

constant function Fb_AOEincrement takes nothing returns real
    //This is the increment added onto the AoE for each level above one of the cast spell
    //The default is 0.
    return 0.
endfunction

constant function Fb_DamageBase takes nothing returns real
    //This is the base damage delt by the spell to each affected unit. It is multiplied by the spell level
    return 70.
endfunction

constant function Fb_MaxLevel takes nothing returns integer
    //This is the max level and defines how "powerful" the effect is.
    //"Power" is divided into four
    //If the value is one, then all levels show the maximum sprites
    return 10
endfunction

// Sub-functions
function Fb_AffectedForce takes player trigplayer returns force
    local force affected
    if Fb_FriendorFoe() == true then
        call ForceEnumEnemies(affected, trigplayer, null)
    else
        call ForceEnumPlayers(affected, null)
    endif
    return affected
endfunction

//Main function (called by trigger)
function Fb_Actions takes nothing returns nothing
    local unit trigunit = GetTriggerUnit()
    local unit u = null
    local integer level = GetUnitAbilityLevel(trigunit, Fb_Ability())
    local player trigplayer = GetOwningPlayer(trigunit)
    local group g = null
    call GroupEnumUnitsInRange(g, GetUnitX(trigunit), GetUnitY(trigunit), ( Fb_AOE() + (1 - level ) ) , null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if u == trigunit then
            set u = null
        endif
        if IsUnitInForce(u, Fb_AffectedForce(trigplayer)) then
            call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), ConvertWeaponType(0))
        endif
        call GroupRemoveUnit(g, u)
    endloop
    set trigunit = null
    set trigplayer=null
    call DestroyGroup(g)
endfunction

  • Am I leaking a force in Fb_AffectedForce() ? (as I know there is a ForceClear native
  • Is there a simpler way so as to filter units weather they are in the affected force or not when they are added to the group of in-range units?
  • Is it ATTACK_TYPE or DAMAGE_TYPE that determines if it is "spell/ability" damage or "weapon / attack (i.e. affected by armour)"? (I think it's DAMAGE_TYPE_MAGIC vs DAMAGE_TYPE_NORMAL)

Ty Ty for help!
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Since it returns a force, you don't leak one. However, you need to

set affected = CreateForce()

also, ==true is redundant.

Weapon/Attack = Attack type (chaos, etc...) Targeting such as Ethereal and Magic Immune = Damage type (normal is physical, magic is magical, universal hits everyone)

NOTE: Damage types are all as far as I know.
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Yes, you are leaking a code, but it will be cleaned outside the Fb_AffectedForce. ForceClear native just clears the force of all players, but there is also a DestroyForce native, this is how you fix it:

JASS:
function Fb_Actions takes nothing returns nothing
    local unit trigunit = GetTriggerUnit()
    local unit u = null
    local integer level = GetUnitAbilityLevel(trigunit, Fb_Ability())
    local player trigplayer = GetOwningPlayer(trigunit)
    local group g = null
    local force f // *
    call GroupEnumUnitsInRange(g, GetUnitX(trigunit), GetUnitY(trigunit), ( Fb_AOE() + (1 - level ) ) , null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if u == trigunit then
            set u = null
        endif
        set f = Fb_AffectedForce(trigplayer) // *
        if IsUnitInForce(u, f) then // *
            call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), ConvertWeaponType(0))
        endif
        call DestroyForce(f) // *
        call GroupRemoveUnit(g, u)
    endloop
    set trigunit = null
    set trigplayer=null
    set f = null //*
    call DestroyGroup(g)
    set g = null // you forgot to null group here
endfunction


With * I marked the things I added/changed.

There is one tiny error in Fb_AffectedForce:

JASS:
function Fb_AffectedForce takes player trigplayer returns force
    local force affected = CreateForce() // *
    if Fb_FriendorFoe() == true then
        call ForceEnumEnemies(affected, trigplayer, null)
    else
        call ForceEnumPlayers(affected, null)
    endif
    return affected
endfunction


And one more thing, instead of "if Fb_FriendorFoe() == true" you can just put "if Fb_FriendorFoe()". (And if you're checking if something is false, instead of "if Fb_FriendorFoe() == false" you can put "if not Fb_FriendorFoe()")

Is there a simpler way? Yes, there is, you can remove Fb_AffectedForce function and just do this:

JASS:
function Fb_Actions takes nothing returns nothing
    local unit trigunit = GetTriggerUnit()
    local unit u = null
    local integer level = GetUnitAbilityLevel(trigunit, Fb_Ability())
    local player trigplayer = GetOwningPlayer(trigunit)
    local group g = null
    call GroupEnumUnitsInRange(g, GetUnitX(trigunit), GetUnitY(trigunit), ( Fb_AOE() + (1 - level ) ) , null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if u == trigunit then
            set u = null
        endif
        if IsUnitEnemy(u, trigplayer) then
            call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), ConvertWeaponType(0))
        endif
        call GroupRemoveUnit(g, u)
    endloop
    set trigunit = null
    set trigplayer=null
    call DestroyGroup(g)
    set g = null
endfunction


And about that third question, it's DAMAGE_TYPE_MAGIC that determines if the damage was magical (spellish).

Any questions?

EDIT: ARGGHHH!!! Stop doing that Pooty! :p
 
Level 3
Joined
Jul 15, 2007
Messages
36
Thanks for the help and explanation;

Ended up rearranging it slightly and doing this;

JASS:
//Main function (called by trigger)
function Fb_Actions takes nothing returns nothing
    local unit trigunit = GetTriggerUnit()
    local unit u = null
    local integer level = GetUnitAbilityLevel(trigunit, Fb_Ability())
    local player trigplayer = GetOwningPlayer(trigunit)
    local group g = CreateGroup()
    call BJDebugMsg("Frostburn level " + I2S(level) + " has been cast by " + GetUnitName(trigunit) + "AoE of " +R2S( Fb_AOE() + (level - 1 )*Fb_AOEincrement() ))
    call GroupEnumUnitsInRange(g, GetUnitX(trigunit), GetUnitY(trigunit), ( Fb_AOE() + (level - 1 )*Fb_AOEincrement() ) , null)
    if GetUnitAbilityLevel(trigunit, Fb_Switch()) > 0 then
        call Fb_FireActions()
    else
        call Fb_FrostActions()
    endif
    loop
        set u = FirstOfGroup(g)
        call BJDebugMsg(GetUnitName(u) +" has been selected in the group" )
        exitwhen u == null
        if u != trigunit then
            if Fb_UnitAffectedType(u) != false then
                if Fb_FriendorFoe() then
                    if IsUnitEnemy(u, GetOwningPlayer(trigunit)) then
                        call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), WEAPON_TYPE_WHOKNOWS)
                        call BJDebugMsg( GetUnitName(u) + " should be damaged by " + R2S(Fb_DamageBase() * level)  )
                    endif
                else
                    call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call GroupRemoveUnit(g, u)
        set u = null
    endloop
    set trigunit = null
    set trigplayer = null
    call DestroyGroup(g)
    set g = null
endfunction

Cheers! Stay tuned for my next question (new thread) about how to get effects working....
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Instead of those if/then/elses you can do something like this (to simplify):

JASS:
function Fb_TargetFilter takes nothing returns boolean
    return Fb_UnitAffectedType(GetFilterUnit()) and Fb_FriendorFoe() // GetFilterUnit() is like "matching unit" in GUI
    // you put Fb_UnitAffectedType() != false,
    // that is equal to Fb_UnitAffectedType() == true,
    // that is equal to FbUnitAffectedType()
endfunction

//Main function (called by trigger)
function Fb_Actions takes nothing returns nothing
    local unit trigunit = GetTriggerUnit()
    local unit u // = null - no need of nulling this
    local integer level = GetUnitAbilityLevel(trigunit, Fb_Ability())
    local player trigplayer = GetOwningPlayer(trigunit)
    local group g = CreateGroup()
    local boolexpr b = Condition(function Fb_TargetFilter) // this is something like "matching" in
    // Pick Every Unit In Group (in GUI)
    call BJDebugMsg("Frostburn level " + I2S(level) + " has been cast by " + GetUnitName(trigunit) + "AoE of " +R2S( Fb_AOE() + (level - 1 )*Fb_AOEincrement() ))
    call GroupEnumUnitsInRange(g, GetUnitX(trigunit), GetUnitY(trigunit), ( Fb_AOE() + (level - 1 )*Fb_AOEincrement() ) , b)
    if GetUnitAbilityLevel(trigunit, Fb_Switch()) > 0 then
        call Fb_FireActions()
    else
        call Fb_FrostActions()
    endif
    loop
        set u = FirstOfGroup(g)
        call BJDebugMsg(GetUnitName(u) +" has been selected in the group" )
        exitwhen u == null
        if IsUnitEnemy(u, trigplayer) then // you used GetOwningPlayer(trigunit),
        // but you already put that in trigplayer variable, so you can just use that variable
            call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), WEAPON_TYPE_WHOKNOWS)
            call BJDebugMsg( GetUnitName(u) + " should be damaged by " + R2S(Fb_DamageBase() * level) )
        else
            call UnitDamageTarget(trigunit, u, ( Fb_DamageBase() * level ), true, true, Fb_Attacktype(), Fb_Damagetype(), WEAPON_TYPE_WHOKNOWS)
        endif
        call GroupRemoveUnit(g, u)
        // set u = null - no need of nullifying this,
        // the loop will end when u == null so it will
        // be nullified
    endloop
    call DestroyGroup(g) // I like nullifying things at the end of the function,
    // so I just moved this function up
    set trigunit = null
    set trigplayer = null
    set g = null
    set b = null
endfunction


Understand?
 
Status
Not open for further replies.
Top