HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Help using xefx

01-28-2010, 08:04 PM#1
Titanhex
Unfortunately I couldn't get much out of xefx's demo spell, seeing as how it incorporated collider and whatnot. So I was setback trying to use it for my purposes.

I have two spells. One where the unit throws a vial concoction at a target point which subsequently explodes upon landing and damages enemies in an area with smaller damage over time. So it's pretty much flamestrike with a missile.

The other is where a unit throws a glaive that hits a targeted enemy.

Now I'm just entering into vJASS, and am still researching all it can do. So far I've just been using it to replace hashtables.

Anyways any help using the xefx system would be appreciated. Here is the code I'm doing too.

Hidden information:
Collapse JASS:
scope AlcheBomb initializer InitTrig_AlcheBomb

globals
    private constant integer    SpellID = 'bomb'
    private constant damagetype dmg     = DAMAGE_TYPE_FIRE
    private constant weapontype wpn     = WEAPON_TYPE_WHOKNOWS
    private constant attacktype atk     = ATTACK_TYPE_MAGIC
    private constant string  MODEL_PATH_MISSILE       = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    private constant string  MODEL_PATH_FLASH         = null
endglobals

private function speed takes nothing returns integer
    return 700
endfunction

   private function castRecycleDelay takes real level, real bonuslevel returns real
       return 5.0 * level + 0.0*bonuslevel + 0.0
   endfunction
   
private struct Data
    unit c
    real X2
    real Y2
    real til
    real dur
    group g
    
    static method create takes unit caster, real targX, real targY, real until, real duration, group gro returns Data
     local Data D = Data.allocate()
        set D.c = caster
        set D.X2 = targX
        set D.Y2 = targY
        set D.til = until
        set D.dur = duration
        set D.g = gro
     return D
    endmethod
    
endstruct

private function AlcheBomb_Conditions takes nothing returns boolean    
    return GetSpellAbilityId() == SpellID
endfunction

private function AlcheBomb_Timer takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data D = Data(GetTimerData(tim))
    local unit t
    local real X1
    local real Y1
    if D.dur >= 5. then
        call ReleaseTimer(tim)
        call D.destroy()
    else
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 350 + 85. * (GetUnitAbilityLevel(D.c, SpellID) - 1), null)
    loop
        set t = FirstOfGroup(D.g)
    exitwhen t == null
        if IsUnitEnemy(t, GetOwningPlayer(D.c)) and GetWidgetLife(t) >= 1 then
            call UnitDamageTarget(D.c, t, 5 * GetUnitAbilityLevel(D.c, SpellID), true, false, atk, dmg, wpn)
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", t, "origin"))
        endif
        set X1 = GetUnitX(t)
        set Y1 = GetUnitY(t)
        call GroupRemoveUnit(D.g, t)
    endloop
    set D.dur = D.dur + 1.
    endif
 set t = null
endfunction

private function AlcheBomb_Actions takes nothing returns nothing
 local timer tim = NewTimer()
 local Data D = Data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), 0., 0., CreateGroup())
 local unit t
 local real X1 = GetUnitX(D.c)
 local real Y1 = GetUnitY(D.c)
    set D.til = SquareRoot(Pow(X1 - D.X2, 2) + Pow(Y1 - D.Y2, 2)) / 650.
    call TriggerSleepAction(D.til)
    call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", D.X2, D.Y2))
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 350 + 85. * (GetUnitAbilityLevel(D.c, SpellID) - 1), null)
    loop
        set t = FirstOfGroup(D.g)
    exitwhen t == null
        if IsUnitEnemy(t, GetOwningPlayer(D.c)) then
            call UnitDamageTarget(D.c, t, 125 + (50 * GetUnitAbilityLevel(D.c, SpellID)), true, false, atk, dmg, wpn)
        endif
        call GroupRemoveUnit(D.g, t)
    endloop
    call SetTimerData(tim, D)
    call TimerStart(tim, D.til, true, function AlcheBomb_Timer)
 set t = null
 set tim = null
endfunction

//===========================================================================
public function InitTrig_AlcheBomb takes nothing returns nothing  
 local trigger AlcheBomb = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ( AlcheBomb, EVENT_PLAYER_UNIT_SPELL_EFFECT )  
  call TriggerAddCondition( AlcheBomb, Condition( function AlcheBomb_Conditions ) )  
  call TriggerAddAction( AlcheBomb, function AlcheBomb_Actions )
endfunction

endscope[
01-28-2010, 08:37 PM#2
Themerion
Hello!

For a starter in vJASS, you're doing pretty well. Let me give you some pointers before we get into the real issue!

Collapse JASS:
 // Since you're using keywords, you don't need ugly prefixes in the names:

private function AlcheBomb_Conditions takes nothing returns boolean
private function AlcheBomb_Actions takes nothing returns nothing

// Scope initializers need not be public. Make it private.
public function InitTrig_AlcheBomb takes nothing returns nothing

Collapse JASS:
 // This is redundant code!
  // You have almost exactly the same thing in two places.

    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 350 + 85. * (GetUnitAbilityLevel(D.c, SpellID) - 1), null)
    loop
        set t = FirstOfGroup(D.g)
    exitwhen t == null
        if IsUnitEnemy(t, GetOwningPlayer(D.c)) and GetWidgetLife(t) >= 1 then
            call UnitDamageTarget(D.c, t, 5 * GetUnitAbilityLevel(D.c, SpellID), true, false, atk, dmg, wpn)
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", t, "origin"))
        endif
        set X1 = GetUnitX(t)
        set Y1 = GetUnitY(t)
        call GroupRemoveUnit(D.g, t)
    endloop

// =============================================
// Move the code to a separate function

function DamageUnitsInRange takes integer damage, Data d returns nothing
  // ...
endfunction

You could also do the looping in a more efficient way. The trick is to use the filterFunction as the loop, and let the groupEnum call do the loop for you. If you're interested, you can follow the link in my sig.

Concerning xefx

A very short tutorial.

Collapse JASS:
function Tutorial takes nothing returns nothing
    local unit caster = GetSpellAbilityUnit()
    local integer x = GetSpellTargetX()
    local integer y = GetSpellTargetY()
    local integer facing = 0

  // Create the effect at the point (x,y)
  // Effect will face right (0 radians)
    local xefx fx = xefx.create(x, y, facing)

  // Give the effect an appearance.
    set fx.fxpath = "Human\\DeathMissile.mdx"

    call TriggerSleepAction(3)

  // Spell is now above caster's head
    set fx.x = GetUnitX( caster )
    set fx.y = GetUnitY( caster )
    set fx.z = 200  // z means height

    call TriggerSleepAction(3)

    call fx.destroy()
    set caster = null
endfunction
01-30-2010, 04:31 AM#3
Titanhex
Quick update! Thanks much, this has been very helpful and informative and I'm reading your tutorial right now. It's very helpful in learning vJASS and some general things =)

I'm trying this out and will see how I do. If I have any problems I'll post here again.
01-31-2010, 12:37 AM#4
Titanhex
Alright I'm working with a mobile fx. I'm clueless on proper mathmatics to move it towards the destination. I looked at this tutorial:
http://www.hiveworkshop.com/forums/t...apes-gui-7337/
And was checking out the line portion, but I can't wrap my head around it. :( In part because it's in GUI and in part because I never got to Trig or Calculus in highschool.

How would I move the fx from it's current target to the target point at a speed of 700. I know I'd create a timer and tell it to move a few units on a line that runs from the target caster to the target point every 0.x seconds. I just don't know how to figure out those numbers.
01-31-2010, 01:44 AM#5
Anitarf
Well, you'd need to use trigonometry to convert your movement speed (700 multiplied by whatever your timer period is) and movement angle into usable x and y coordinate offsets.
01-31-2010, 06:22 PM#6
Titanhex
I may have to look at some example codes in order to understand this. It seems pretty simple as far as the equation would go. The destination doesn't move, and the missile moves along a straight line with a fixed course.

Alright I found a pretty worthwhile formula to use for a grenade spell. Unfortunately I don't know how to transfer the FX from the XE system from the acitons trigger into the timer trigger in order to allow it to move. Any help here?

Also, how can I optomize and make this trigger work properly?

I was thinking of running all the data inside the struct, as I've seen it done before and it doesn't seem to hard. Just make the create static method run the starting actions, then create a static method Loop and have that one run like the timer, but I'm not sure if it'll work properly, as it was done for activatables.

So any help? =) I'm looking forward to figure this out and doing this spell :) I can start my Beta map once it's done.


AlcheBomb

Collapse JASS:
scope AlcheBomb initializer Init

globals
    private constant integer    SpellID = 'bomb'
    private constant damagetype dmg     = DAMAGE_TYPE_FIRE
    private constant weapontype wpn     = WEAPON_TYPE_WHOKNOWS
    private constant attacktype atk     = ATTACK_TYPE_MAGIC
    private constant string  MODEL_PATH_MISSILE       = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    private constant string  MODEL_PATH_FLASH         = null
endglobals

private function JumpParabola takes real dist, real maxdist,real maxheight returns real
    local real t = (dist*2)/maxdist-1    
    return (-t*t+1)*maxheight
endfunction
   
private struct Data
    unit c
    real X2
    real Y2
    real til
    real dur
    group g
    real X3
    real Y3
    real maxDist
    
    static method create takes unit caster, real targX, real targY, group gro, real X3, real Y3 returns Data
     local Data D = Data.allocate()
        set D.c = caster
        set D.X2 = targX
        set D.Y2 = targY
        set D.dur = 0.
        set D.g = gro
        set D.maxDist = SquareRoot((D.X2 - D.X3)*(D.X2 - D.X3)+(D.Y2 - D.Y3)*(D.Y2 - D.Y3))
     return D
    endmethod
    
endstruct

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

private function MissileMove takes nothing returns nothing
 local timer tim = GetExpiredTimer()
 local Data D = Data(GetTimerData(tim))
 local real maxHeight = 500.
 local real cur_dist = SquareRoot((fx.x - D.X2)*(fx.x - D.X2)+(fx.y - D.Y2)*(fx.y-D.Y2))
 local real fly = JumpParabola(cur_dist, maxDist, maxHeight)//Shadow1500 jump parabola 
 local real angle = Atan2(D.X2 - GetUnitY(D.c), D.Y2 - GetUnitX(D.c))
 local real moveX              = fx.x + 15. * Cos(angle)
 local real moveY              = fx.y + 15. * Sin(angle)
 
    if curDist <= 15.0 then
        call fx.destroy
        call RemoveTimer(tim)
        //Do Damage Stuff Here
        
    else
        set fx.x = GetUnitX( moveX )
        set fx.y = GetUnitY( moveY )
        set fx.z = fly  // z means height
    endif
    
endfunction

private function Timer takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data D = Data(GetTimerData(tim))
    local unit t
    local real X1
    local real Y1
    if D.dur >= 5. then
        call ReleaseTimer(tim)
        call D.destroy()
    else
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 350 + 85. * (GetUnitAbilityLevel(D.c, SpellID) - 1), null)
    loop
        set t = FirstOfGroup(D.g)
    exitwhen t == null
        if IsUnitEnemy(t, GetOwningPlayer(D.c)) and GetWidgetLife(t) >= 1 then
            call UnitDamageTarget(D.c, t, 5 * GetUnitAbilityLevel(D.c, SpellID), true, false, atk, dmg, wpn)
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", t, "origin"))
        endif
        set X1 = GetUnitX(t)
        set Y1 = GetUnitY(t)
        call GroupRemoveUnit(D.g, t)
    endloop
    set D.dur = D.dur + 1.
    endif
 set t = null
endfunction

private function Actions takes nothing returns nothing
 local timer tim = NewTimer()
 local timer tim2 = NewTimer()
 local real X1 = GetUnitX(GetTriggerUnit())
 local real Y1 = GetUnitY(GetTriggerUnit())
 local unit t 
 local real a = Atan2(GetSpellTargetX() - GetUnitY(GetTriggerUnit()), GetSpellTargetY() - GetUnitX(GetTriggerUnit()))
 local xefx fx = xefx.create(GetUnitX(GetTriggerUnit())+20.0*Cos(a), GetUnitY(GetTriggerUnit())+20.0*Sin(a), a)
 local Data D = Data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), CreateGroup(), fx.x, fx.y)
 
 local real til = SquareRoot(Pow(X1 - D.X2, 2) + Pow(Y1 - D.Y2, 2)) / 700.
    
    set fx.fxpath = "Abilities\\Weapons\\BatTrollMissile\\BatTrollMissile.mdl"
    
    call SetTimerData(tim, D)
    call TimerStart(tim, 0.03, true, function MissileMove)
    
    call TriggerSleepAction(til)
    call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", D.X2, D.Y2))
    call GroupEnumUnitsInRange(D.g, D.X2, D.Y2, 150 + 65. * (GetUnitAbilityLevel(D.c, SpellID) - 1), null)
    loop
        set t = FirstOfGroup(D.g)
    exitwhen t == null
        if IsUnitEnemy(t, GetOwningPlayer(D.c)) then
            call UnitDamageTarget(D.c, t, 125 + (50 * GetUnitAbilityLevel(D.c, SpellID)), true, false, atk, dmg, wpn)
        endif
        call GroupRemoveUnit(D.g, t)
    endloop
    call SetTimerData(tim2, D)
    call TimerStart(tim2, til, true, function Timer)
 set t = null
 set tim = null
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 )
endfunction

endscope

01-31-2010, 11:29 PM#7
blanc_dummy
move xefx fx to your struct instead create it as local variable
02-01-2010, 10:18 AM#8
Themerion
For linear movement in the XY-plane:

Collapse JASS:
private struct Data
    xefx fx
    real x_speed
    real y_speed
endstruct

globals
  // Move 700 each second.
    private constant real SPEED=700

  // Update 25 times per second
    private constant real TIMER_INTERVAL = 0.04

  // How much speed in each loop?
    private constant real SPEED_PER_INTERVAL = SPEED * TIMER_INTERVAL
  // 700 * 0.04 = 28
  // It runs 25 times per second, so it will move:
  // 28*25 = 700
  // each second, like we want it too.
endglobals

// Now, we know speed per loop.
// Warcraft, however, works with X and Y coordinates.
// We need to know how much to change x and y each loop.

local Data d = Data.create()
set d.fx = xefx.create( ... )

// Here's the trigonometry:

set d.x_speed = Cos(angle) * SPEED_PER_INTERVAL
set d.y_speed = Sin(angle) * SPEED_PER_INTERVAL


// And in the loop:
d.fx.x = d.fx.x + d.x_speed
d.fx.y = d.fx.y + d.y_speed