HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Xe.Cast only works once

01-22-2010, 09:35 PM#1
Saishy
Collapse JASS:
scope Rikujyou initializer Init_Rikujyou

globals
    private constant integer RIKUJYOU = 'A04D'
    private constant string MESSAGE = "Bakudou 61, Rikujyou Kourou!"
    private xecast cast
endglobals

private function setRikujyou takes nothing returns nothing //: In this function,
        set cast = xecast.create()                         //  we do some basic setup
        set cast.abilityid = 'A048'  //The ability Id for our dummy ability
        set cast.orderid   = 852095  //The order id.
endfunction

function Rikujyou_Conditions takes nothing returns boolean
    return (GetSpellAbilityId() == RIKUJYOU)
endfunction

function Rikujyou_Actions takes nothing returns nothing
    //local xecast xc = xecast.createA()
    local unit caster = GetTriggerUnit()
    local timer t
    
    call DisplayText(caster, MESSAGE, 1)
    set cast.owningplayer = GetOwningPlayer(caster)
    set cast.level = GetUnitAbilityLevel(caster, RIKUJYOU)
    call cast.castOnTarget(GetSpellTargetUnit())
    
    call BJDebugMsg(GetUnitName(caster))
    call BJDebugMsg(GetUnitName(GetSpellTargetUnit()))
    call BJDebugMsg(I2S(GetUnitAbilityLevel(caster, RIKUJYOU)))
    call BJDebugMsg("-----")
    call BJDebugMsg(I2S(cast.level))
    call BJDebugMsg(I2S(GetPlayerId(cast.owningplayer)))

    set caster = null
endfunction

//===========================================================================
function Init_Rikujyou takes nothing returns nothing
    local trigger trg_Rikujyou = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trg_Rikujyou, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trg_Rikujyou, Condition(function Rikujyou_Conditions))
    call TriggerAddAction(trg_Rikujyou, function Rikujyou_Actions )
    call setRikujyou()
    set trg_Rikujyou = null
endfunction

endscope

The line [//local xecast xc = xecast.createA()] is commented because I couldn't find any difference on letting it run or not.

What happens, on the first cast, the target is stunned, it displays:

MyCasterName
(null)
1
-----
1
0

After that any cast will show:

MyCasterName
MyTargetName
1
-----
1
0

And will not work anymore.

Thanks in advance for any help!
01-22-2010, 11:38 PM#2
Themerion
When I had the same problem as you, I moved all the xecast-stuff into the action (from xecast.create to xecast.destroy). This sort of destroys a bit of the beauty of xe, but hell, it worked.

(I suppose you have set up the casting unit and the ability right. You know, the routine stuff. Cooldown, mana cost, range, etc)
01-22-2010, 11:42 PM#3
Saishy
Well, this is the example vex gave of xe.cast:

Collapse JASS:
//**********************************************
//* Rune of Illusions
//*
//* A quick sample for xecast
//*
//*
//I003 : rune of illusion
//A009 : Illusions (dummy)
//----------------------------------------------
scope IllusionRune initializer init

    private function itemIdMatch takes nothing returns boolean  //If the id of the pickup item
      return (  GetItemTypeId(GetManipulatedItem()) == 'I003' ) //matches I003 ...
    endfunction                                                 //

    //====================================================
    //: This time we'll use a single xecast instance for
    //: all the cases the rune is used. It is recommended
    //: to do this since it is more efficient.
    //
    globals                 // :We begin by actually declaring our cast global
        private xecast cast // variable
    endglobals              //
    private function setItUp takes nothing returns nothing //: In this function,
        set cast = xecast.create()                         //  we do some basic setup
        set cast.abilityid = 'A009'  //The ability Id for our dummy illusions ability
        set cast.orderid   = 852274  //The order id.
        //: The illusions ability is a special case since it does not have an orderstring
        //  so we are just going to use its order id, every active ability has one,
        // I got the id using a detector trigger, but most people would prefer to use
        // the order id guide: [url]http://www.wc3campaigns.net/tools/weu/ORDERIDS.xls[/url]
        //
        //
        //
    endfunction

    private function onItemPick takes nothing returns nothing
     local xecast   xc  = xecast.createA() //CreateA so we don't have to destroy the object after the cast.
     local unit     u   = GetTriggerUnit()

        set cast.owningplayer = GetOwningPlayer(u) //* The owning player, determines who will own the illusion.
        call cast.castOnTarget( u )  //Let's call the castOnTarget method on the Triggering unit.

     set u=null
    endfunction


    private function init takes nothing returns nothing
     local trigger t=CreateTrigger() //The usual Trigger registering
       call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM) //: Unit pickups item
       call TriggerAddCondition(t, Condition(function itemIdMatch))         //:
       call TriggerAddAction(t, function onItemPick)

       call setItUp() //call our xecast setup function...
    endfunction

endscope

And the cast documentation:

Collapse JASS:
xecast
------
  This one solves typical problems that require dummy casters. Things like the
 AOE sleep, targetted warstomp, etc. It is object oriented, this just means that
 you'll actually not just call functions but deal with xecast objects, change
 their attributes and then order them to cast. It deals with the dirty things
 like recycling and dealing with timing, etc.
 
implementation
--------------
  Just copy the xecast trigger to your map.



xecast object
-------------

__________________________________________________________________________________________________
 static method create      takes nothing returns nothing 
-                    ------
 This is the create method, it will make a new xecast object for you to use:

 set somevariable = xecast.create()
_________________________________________________________________________________________________
 static method createBasic takes integer abilityID, integer orderid, player owner returns xecast
-      -      -           -     -       -          -       -        -      -     -       -
 An abbreviated constructor, allows you to quickly set the basic attributes
  abilityID: the rawcode of the ability to cast.
                  Example: 'AHbz'
  orderid  : the orderid (integer) of the ability to cast.
                  Example: OrderId("blizzard")
  owner    : the owning player for this cast object (The one that gets credit for damage)
                  Example: ( GetOwningPlayer(GetTriggerUnit() ))
                  
_________________________________________________________________________________________________
 method destroy             takes nothing returns nothing
-
 Call destroy() on instances you are not going to use anymore, to prevent struct leaks that would
 break your map. A simple use for xecast is to just keep one instance per dummy spell to prevent
 having to care about destroying them. Another possibility is to use the A constructors.
 
 Example:   call somevariable.destroy()                  
                  
__________________________________________________________________________________________________
 static method createA      takes nothing returns nothing 
 static method createBasicA takes integer abilityID, integer orderid, player owner returns xecast 
-
 These do the same as create and createBasic , the only difference is that the
 object is destroyed automatically after every call to a cast method (See bellow).
 
_____________________________________________________________________________________________________
 method castOnTarget         takes widget target returns nothing 
-
 Tells the xecast object to cast its spell on the target. target may be unit, item or destructable.
_____________________________________________________________________________________________________
 method castOnPoint  takes real x, real y returns nothing
 method castOnLoc    takes location loc returns nothing
 method castInPoint  takes real x, real y returns nothing
 method castInLoc    takes location loc returns nothing
-                                                      -----------------------------------------------
 Instead of casting on a target unit/item/destructable these ones cast on a target point. OnPoint is
 used for point-targeteable spells, while InPoint is used for spells that have no target. The Loc
 versions allow you to use locations. Locations are useless most of the times, but if you want to use
 them you can use the Loc versions.
 
 Example:   call somevar.castOnPoint( spellx, spelly )

_____________________________________________________________________________________________________ 
 method castOnAOE    takes real x, real y, real radius returns nothing
 method castOnAOELoc takes location loc,real radius returns nothing
 method castOnGroup  takes group g returns nothing 
-                                                 ---------------------------------------------------
 Methods to cast the spell on multiple units, AOE takes a circle's center and radius, while Group
 takes a unit group, notice that the unit group will get cleaned after calling this function, which
 means it will be an empty unit group, no, it does not destroy the group automatically, just empties it
 
* List of attributes *
________________________________________________________________________________
    integer abilityid
----
    This one holds the ability to cast's ability Id.
        Example: set somevar.abilityid='AHbz'
________________________________________________________________________________
    integer level
----
    The level of the ability to cast.
        Example: set somevar.level = GetUnitAbilityLevel(u, spellid)
________________________________________________________________________________
    real    recycledelay
----
    The recycle delay is the time to wait before recycling the dummy caster, if
    it is 0.0 the ability will be considered instant.
    
    A proper recycle delay is important since when a dummy caster is recycled
    its owner becomes player passive. Every damage done by the casted spell will
    not credit the correct player.
    
    Some other spells need some time in order to cast correctly. Not to mention
    the channeling ones that require the caster to last during that situation.

        Example: set somevar.recycledelay=10.0
________________________________________________________________________________
    player  owningplayer
----
    The player that owns the spell (Who gets credited for it)
        Example: set somevar.owningplayer = Player(2)
        
________________________________________________________________________________
    integer orderid (write-only)
----
    The ability to cast's order id. eg 858029 or OrderId("blizzard")
________________________________________________________________________________
    string orderstring (write-only)
----
    The ability to cast's order string (eg "blizzard")
________________________________________________________________________________
    boolean customsource
----
    false by default, determines if you want the dummy caster to be placed at
    a specific point when casting, this allows you to exploit blizz spell's eye
    candy. Once customsource is true, you need to set sourcex,sourcey and 
    sourcez.
________________________________________________________________________________
    real sourcex, sourcey, sourcez
----
    The coordinates where you want to place the dummy caster, z is height and
    is 0.0 by default. These are ignored if customsource is set to false.
________________________________________________________________________________
    method setSourcePoint  takes real x, real y, real z returns nothing
    method setSourceLoc    takes  location loc, real z returns nothing        
----
    In case setting all that stuff manually takes too much lines for your taste
    you can use these methods to set those values, they will automatically set
    customsource to true.

Well, if I was to put the xe.cast things on the trigger, I would just do createunit and make it cast D:
01-22-2010, 11:51 PM#4
Themerion
Quote:
Well, if I was to put the xe.cast things on the trigger, I would just do createunit and make it cast

Yes, I know. You've done everything right it would seem. I think this is an oddity with xe.

I've been scanning the xe-thread. They mention recycledelay. What happens if you add the following to your setup?

Collapse JASS:
set cast.recycledelay   = 1
01-23-2010, 12:24 AM#5
Saishy
It worked! Thank you!
(Don't know why it worked on the first call anyway)

Sir, I would call you a genius if you could solve what this line does:

[local xecast xc = xecast.createA()]

I found it odd, since the dummy that casts it is never destroyed, as seen in the setItUp function.
01-23-2010, 12:54 AM#6
Themerion
Quote:
Sir, I would call you a genius if you could solve what this line does:

[local xecast xc = xecast.createA()]

I found it odd, since the dummy that casts it is never destroyed, as seen in the setItUp function.

Well, it appears to be a mistake made by copying the spell samples. I believe Rune of Illusions was copied from Sheep Staff (also from xe sample map), and that Vexorian simply has forgotten to remove createA from Rune of Illusions.

I think Sheep Staff shows the real usage of createA: (that is, the struct instance will auto-destroy after calling a cast-function)

sheep staff

Collapse Sheep staff:
//**********************************************
//* Sheep staff.
//*
//* A quick sample for xecast.AOEloc
//*
//*

//----------------------------------------------
scope SheepStaff initializer init

    private function spellIdMatch takes nothing returns boolean
      return (GetSpellAbilityId()=='A006')
    endfunction

    private function onSpellCast takes nothing returns nothing
     local xecast   xc  = xecast.createA() //CreateA so we don't have to destroy the object after the cast.
     local unit     u   = GetTriggerUnit()
     local location loc = GetSpellTargetLoc() //The target point

        call SetUnitAnimation(u, "attack") // Let's focus on the look of the item cast for a second...


        //==============================================
        // Here, we do assignments:
        //
        set xc.abilityid    = 'A005'                          //* Cast ability A005
        set xc.level        = GetUnitAbilityLevel(u, 'A006' ) //* Same level as trigger ability
        set xc.orderstring  = "polymorph"                     //* The order is polymorph
        set xc.owningplayer = GetOwningPlayer(u)              //* The owning player, determines targets and bounty credits
       
        call xc.castOnAOELoc( loc, 200.0 )                    //* Our castOnAOE call, using the location version
                                                              //* A radius of 200.0
                                                              // Since createA was used, no need to destroy the xecast object

        call RemoveLocation(loc) //We need to clean the point.

     set u=null
    endfunction


    private function init takes nothing returns nothing
     local trigger t=CreateTrigger()
        call TriggerAddCondition(t, Condition( function spellIdMatch) )
        call TriggerAddAction(t,    function onSpellCast)
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )

     set t=null
    endfunction

endscope

01-23-2010, 01:51 AM#7
Saishy
Thanks you again, all questions solved! ^^