HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Weird problem with GetLastCreatedUnit()

03-14-2007, 06:23 PM#1
Trashbite
Well, I'm kind of new to this website, so lets start by saying hi.

Secondly, I'm also pretty new to using JASS (I've used GUI for quite a while though), and I ran into a problem today. I'm trying to make a pretty simple spell, where you basicly just target a unit, and a dummy unit will cast a shockwave-like spell infront of the target unit, and in addition the targeted unit will just die instantly.

So I just made this script with JassCraft:

Collapse JASS:
function Trig_Blind_MageClaw_of_Vengeance_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A072'
endfunction

function Vengeance takes unit u, unit Cast, player p returns nothing
    local real face = GetUnitFacing(u) 
    local location dumTar = PolarProjectionBJ(GetUnitLoc(u), 200.0, face)
    local location dumLoc = PolarProjectionBJ(GetUnitLoc(u), -100.0, face)
    call CreateUnitAtLoc(p, 'h002', dumLoc, face)//h002 is the ID of my dummy caster.
    call Message(GetUnitName(GetLastCreatedUnit())+" is the name of the last created unit.", p) //Is in my main map script, because I use it more often to check stuff.
    call UnitAddAbility(GetLastCreatedUnit(), 'A001')//A001 is the ID of my shockwave like ability.
    call SetUnitAbilityLevel(GetLastCreatedUnit(), 'A001', GetUnitAbilityLevelSwapped('A072', Cast))
    call IssuePointOrderLoc(GetLastCreatedUnit(), "shockwave", dumTar)
    call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 2.00)
    call UnitDamageTargetBJ(Cast, u, 99999.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
    call RemoveLocation(dumTar)
    call RemoveLocation(dumLoc)
endfunction       

function Trig_Blind_MageClaw_of_Vengeance_Actions takes nothing returns nothing
    local unit Targ = GetSpellTargetUnit()
    local unit Cast = GetSpellAbilityUnit()
    local player ownCast = GetOwningPlayer(Cast)
    call Vengeance(Targ, Cast, ownCast) 
endfunction

//===========================================================================
function InitTrig_Blind_MageClaw_of_Vengeance takes nothing returns nothing
    set gg_trg_Blind_MageClaw_of_Vengeance = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Blind_MageClaw_of_Vengeance, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Blind_MageClaw_of_Vengeance, Condition( function Trig_Blind_MageClaw_of_Vengeance_Conditions ) )
    call TriggerAddAction( gg_trg_Blind_MageClaw_of_Vengeance, function Trig_Blind_MageClaw_of_Vengeance_Actions )
endfunction

The only error JassCraft gives is that "gg_trg_Blind_MageClaw_of_Vengeance" is an undeclared variable.

So my own assumption is that the problem is with GetLastCreatedUnit(), because the dummy unit does get created, but doesn't get an expiration timer, doesn't get the spell, and doesn't cast the spell. And the message I used to check what the name of the last created unit was, it just said " is the name of the last created unit.". So yeah.. I have no idea whats wrong with it, and I refuse to use GUI because that won't make me learn JASS, so please someone help me.

PS. Yes I know I could do it in 1 function too, but this just makes it look a lot easier to me, which is why I make it in 2 =\.
03-14-2007, 06:37 PM#2
moyack
I've checked you trigger, and I think there won't be any syntax issues when you pass the code to WE. gg_trg_Blind_MageClaw_of_Vengeance is the name of the global variable created internally by the World editor to make reference to this trigger in particular. If you plan to call this trigger in GUI using actions like:
Trigger:
Trigger
Collapse Actions
Trigger - Run <My trigger> ingnoring conditions
You will need this global variable and you can't change the InitTrig_Blind_MageClaw_of_Vengeance.

If you only need the function to be activated by the event, you can modify the InitTrig_Blind_MageClaw_of_Vengeance function in this way:

Collapse JASS:
function InitTrig_Blind_MageClaw_of_Vengeance takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_Blind_MageClaw_of_Vengeance_Conditions ) )
    call TriggerAddAction( t, function Trig_Blind_MageClaw_of_Vengeance_Actions )
    set t = null
endfunction

With this change, JASSCRAFT only will show the message error.
03-14-2007, 06:49 PM#3
Trashbite
Well, that actually only gets rid of the error message in JassCraft =p, in game it still doesn't add a timer to my dummy, nor add the spell, and it doesn't make it cast either :S .
03-14-2007, 07:02 PM#4
moyack
Got it!!! CreateUnitAtLoc doesn't set the lastcreatedunit() function. I did some changes in your spell. Test them and tell me if they work (I'm at the office right now :P).

Collapse JASS:
constant function Trig_Blind_MageClaw_of_Vengeance_SpellID takes nothing returns integer
    return 'A072'
endfunction

function Trig_Blind_MageClaw_of_Vengeance_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Trig_Blind_MageClaw_of_Vengeance_SpellID()
endfunction

function Vengeance takes unit u, unit Cast, player p returns nothing
    local real face = GetUnitFacing(u) 
    local location dumTar = PolarProjectionBJ(GetUnitLoc(u), 200.0, face)
    local location dumLoc = PolarProjectionBJ(GetUnitLoc(u), -100.0, face)
    local unit d = CreateUnitAtLoc(p, 'h002', dumLoc, face)//h002 is the ID of my dummy caster.
    call Message(GetUnitName(d)+" is the name of the last created unit.", p) //Is in my main map script, because I use it more often to check stuff.
    call UnitAddAbility(d, 'A001')//A001 is the ID of my shockwave like ability.
    call SetUnitAbilityLevel(d, 'A001', GetUnitAbilityLevel(Trig_Blind_MageClaw_of_Vengeance_SpellID, Cast))
    call IssuePointOrderLoc(d, "shockwave", dumTar)
    call UnitApplyTimedLife(d, 'BTLF', 2.00)
    call UnitDamageTargetBJ(Cast, u, 99999.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
    call RemoveLocation(dumTar)
    call RemoveLocation(dumLoc)
    set dumTar = null
    set dumLoc = null
    set d = null
endfunction       

function Trig_Blind_MageClaw_of_Vengeance_Actions takes nothing returns nothing
    local unit Targ = GetSpellTargetUnit()
    local unit Cast = GetSpellAbilityUnit()
    local player ownCast = GetOwningPlayer(Cast)
    call Vengeance(Targ, Cast, ownCast) 
endfunction

//===========================================================================
function InitTrig_Blind_MageClaw_of_Vengeance takes nothing returns nothing
    set gg_trg_Blind_MageClaw_of_Vengeance = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Blind_MageClaw_of_Vengeance, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Blind_MageClaw_of_Vengeance, Condition( function Trig_Blind_MageClaw_of_Vengeance_Conditions ) )
    call TriggerAddAction( gg_trg_Blind_MageClaw_of_Vengeance, function Trig_Blind_MageClaw_of_Vengeance_Actions )
endfunction
03-14-2007, 08:28 PM#5
Trashbite
Ah thanks! I didn't knew you could create a unit and set it to a local immediatly ^^.

Just 1 flaw, this should be the other way around =p:

Collapse JASS:
GetUnitAbilityLevel(Trig_Blind_MageClaw_of_Vengeance_SpellID, Cast))

//===========================================================================
native          GetUnitAbilityLevel takes unit whichUnit, integer abilcode returns integer

Oh, and I'm still pretty much of a newbie, so I'm wondering what the use of this is:

Collapse JASS:
constant function Trig_Blind_MageClaw_of_Vengeance_SpellID takes nothing returns integer
    return 'A072'
endfunction

function Trig_Blind_MageClaw_of_Vengeance_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Trig_Blind_MageClaw_of_Vengeance_SpellID()
endfunction

Was there something wrong with the one I used previously? (see code below)

Collapse JASS:
function Trig_Blind_MageClaw_of_Vengeance_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A072'
endfunction
03-14-2007, 10:24 PM#6
Pyrogasm
The way Moyack has set it up allows for easy spell implementation. Take, for instanc, Spell X, in which you need to reference the Id of the spell being cast 3 times. Someone who needed to edit the spell (to change the ability codes correctly) would now have to look through the whole spell and find every refernce of 'A072' without knowing how many there were.

There is a very large possibility that the would screw something up, cause an error, or miss a reference, which would cause problems.

By adding functions such as the one Moyack has made as "configuration" sections of your spells and replacing the appropriate areas with YourFunction(), someone planning on implementing it (or you yourself) can be confident they've done it correctly.

Constant functions are useful for other things besides just spell Ids. Want a configurable radius for your spell? Do something like this:
Collapse JASS:
constant function Your_Spell_Radius takes nothing returns real
    return 547.75
endfunction

function Your_Spell takes nothing returns nothing
    local group G = CreateGroup
    local real X = GetUnitX(GetTriggerUnit())
    local real Y = GetUnitY(GetTriggerUnit())
    //...
    call GroupEnumUnitsInRange(G, X, Y, Your_Spell_Radius(), null)
    //...
endfunction
03-14-2007, 10:32 PM#7
Trashbite
Oh ok, thanks a lot, I'll add such a configuration section next time I create a spell ^^.
03-14-2007, 11:53 PM#8
moyack
Yes because it returns the created unit:
Collapse JASS:
native          CreateUnitAtLoc         takes player id, integer unitid, location whichLocation, real face returns unit

As a suggestion, try to avoid locations, you can use these functions instead:
Collapse JASS:
local unit u
local real ux
local real uy
//****************************************************************
set u = CreateUnit( playerVar, unit_ID, real_X, real_Y, real_Face) // creates a unit
set ux = GetUnitX(YourUnit) // gets the X coordinate of the unit location
set uy = GetUnitY(YourUnit) // gets the Y coordinate of the unit location
call IssuePointOrder(YourUnit, "Your Order", TargetX, TargetY) // Makes the unit to apply the order to the X, Y coordinates
03-15-2007, 01:01 AM#9
Earth-Fury
I would like it to be noted that functions that move units to locations instead of X, Y coordinates force the unit to go to somewhere where it can fit and is pathable.
03-15-2007, 04:32 PM#10
The)TideHunter(
Yep, setting a units location will take pathing into mind, if you try to move a unit onto another unit it will get moved out of collision range.
If you want to aviod this, you can set a units coordinate, using SetUnitX(SomeUnit,X) and SetUnitY(SomeUnit,Y).
Setting a units position also stops casting and resets animation, using the SetUnitX/Y natives does not.
Sometimes its useful to set position, sometimes its not.
03-15-2007, 04:39 PM#11
Earth-Fury
Aren't SetUnit_() also significantly faster then the location based movement functions?
03-15-2007, 04:57 PM#12
blu_da_noob
Quote:
Originally Posted by Earth-Fury
I would like it to be noted that functions that move units to locations instead of X, Y coordinates force the unit to go to somewhere where it can fit and is pathable.

No. There are two functions that ignore pathing etc (and are thus faster), which are SetUnitX/Y. You can still use X/Y coords with SetUnitPosition and it will move to a pathable position etc.