• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

[General] Why are some custom spells based off of Channel?

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi,

I'd like to make a custom spell that say uses the hero's level to determine how much damage it does.

What is wrong with doing the following?

1. Base it off stormbolt
2. Write a trigger to detect when that spell is used
3. Do damage to the targeted unit based on the trigger unit's hero level
4. done?!
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Blizzard made Channel for triggered spells, I think.

What is wrong with doing the following?

1. Base it off stormbolt
2. Write a trigger to detect when that spell is used
3. Do damage to the targeted unit based on the trigger unit's hero level
4. done?!

If there's no conflict with the spell your triggering, then I guess there's none. (Unless you don't want your spell to have a 0.01 second stun)
 
1) You cannot have a unit having two of those skills coz they will collide with each other... You cannot even mix that custom skill with the default storm bolt coz it will collide too

+ as Chobibo said, you cannot remove Stun of storm bolt... set it to 0 and it will be forever...

Plus Channel has options that aren't available on other spells (like disabling commands while using it, follow thru time etc)
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
I see. So if I do set up an ability based off channel, I can just use it like above?

Detect which unit used it, detect the target, detect the hero level of the caster, and then apply that much damage?
 
Channel doesn't have an icon? => It has an icon, plus you can always set it's icon like any other ability


@Seth - yes, because Channel is still an ability...

Another thing is that Channel's target can be set to unit, point, unit and point and AOE... plus it can also be set to be able to select magic immune units which some default spells doesn't allow...

Basically, Channel does seem to have been made by Blizz as THE DUMMY SPELL...
 
Only on channel... setting it to false is nice for using Channel as some kind of hidden ability or for use as a check when selecting units... like if you need to pick several unit-types, you can give all of them a channel-based dummy ability so that you can just filter out units that have it...

your problem wasn't actually that Channel doesn't have an icon, it was that Channel is invisible by default... XD

btw, I think there is a tutorial for Channel here...
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
I tried adding a missile art to the channel ability I made, but nothing shows up. Are you sure it works? I should be able to make then a channel based stormbolt?
 
Missiles for Channel-based spells needs a custom projectile system...

Most custom missiles need custom missile systems...

Ofc you can opt to use stormbolt, but since you want to detect when stormbolt hits a unit (because you wanna customize it's damage), using the normal stormbolt has problems... mainly IDK if there's an efficient and sure way to catch the stormbolt's hit instance... you can try DDS + check the stormbolt buff assuming you made 1 specifically for stormbolt, I haven't tried that... but it might work...
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Missiles for Channel-based spells needs a custom projectile system...

Most custom missiles need custom missile systems...

Ofc you can opt to use stormbolt, but since you want to detect when stormbolt hits a unit (because you wanna customize it's damage), using the normal stormbolt has problems...

Why is that? So channel itself cannot actually launch a missile unlike stormbolt? So why does it have equivalent fields for doing so?
 
yeah... btw, ALL SPELLS HAVE MISSILE FIELDS... but only a few use it... ALL SPELLS HAVE BUFF FIELDS, again only a few use it... and so on...

probably because the effort it takes to remove those unused fields for some spells are greater and have performance issues maybe... so they have decided to just have them all...

anyway, IMHO, the way they did the spells weren't really good... Spells could have used 1 general Class which support all of those fields, then just subclasses for the different types (missile,instant etc)... so that you can still use the fields that aren't normally used by spells of a specific type...
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Ah this is where I prefer SC2 mapmaking, the data editor is so much more powerful. I didn't realize how limited they made the object editor in WC3...
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Channel has fields other abilities simply don't have, like follow through time, casting time ....

Base every custom spell of yours on channel, except those with a similiar concept like the spell in the object editor. An example from a custom wc3 map would be Dota - Pudge - Dismember is based on aerial shackles, while Meat Hook is 99,9% based on channel since wc3 has no familiar spell.

Note that a unit cannot have two spells with the same order id.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
'Channel' was introduced with TFT as a dummy ability for triggered spells. It has a range of options but by far does not cover everything. Next to the "multiple abilities of same order"-problem, it's essential that the base ability has no unwanted side effects. 'Stormbolt' for example stuns the targeted unit. Even if you immediately remove the buff, it will still cancel the channeling of abilities or restore the order. It also triggers a delayed damage event you should catch. Hostile-targeted spells trigger the damage event in general, 'Channel' too, but here, to be exact, you practically have to watch for the stun buff, which you cannot remove without aborting current real stuns.
 
Level 11
Joined
Nov 15, 2007
Messages
781
Another thing is that Channel's target can be set to unit, point, unit and point and AOE... plus it can also be set to be able to select magic immune units which some default spells doesn't allow...

Any spell can be made to target magic immune units by changing its required level (even non-hero spells "remember" the required level field). What Channel does allow, though, is setting your spell type to physical, which will allow it to target immune units but not ethereal units, which can be really useful for making dummy abilities on physically oriented spells (like charge attacks or spells that deal physical damage). As far as I know the only base physical spell is Ensnare.

Anyway. The main reason Channel is used so often is that in a complex and polished project, just about everything will be scripted. If you wanted to make an ability that shot a projectile and then stunned, you could just use storm bolt, but let's look at the problems associated with that:

1. You can only have one ability based on Storm Bolt on any given unit because of order id conflicts.
2. There are fields you won't be able to work around, such as the ability always applying a stun.
3. You won't be able to detect the impact easily, so if your ability deals triggered damage it'll basically deal its damage immediately and then the storm bolt will hit it some time later - it looks sloppy and unpolished. It gets even more complicated when you want to do something like knock the target back.

It's precisely because Channel does "nothing" that it's so useful. You're going to have to create a dummy projectile, deal scripted damage, and apply scripted effects if you want your spell to seem polished, so you may as well use Channel.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
In a few cases do I use alternative base abilities. Gyrocopter Bombs/Moon Glaives for passives, Windwalk/Berserk for immediate abilities without interrupting current order, autocast abilities if I need such, Trade Gold and Lumber (immediate, resource costs), Reveal (resource costs, point-targeted, does not interrupt order), anything that cannot really or well be made via triggers/for the ui.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Any spell can be made to target magic immune units by changing its required level (even non-hero spells "remember" the required level field). What Channel does allow, though, is setting your spell type to physical, which will allow it to target immune units but not ethereal units, which can be really useful for making dummy abilities on physically oriented spells (like charge attacks or spells that deal physical damage). As far as I know the only base physical spell is Ensnare.

Anyway. The main reason Channel is used so often is that in a complex and polished project, just about everything will be scripted. If you wanted to make an ability that shot a projectile and then stunned, you could just use storm bolt, but let's look at the problems associated with that:

1. You can only have one ability based on Storm Bolt on any given unit because of order id conflicts.
2. There are fields you won't be able to work around, such as the ability always applying a stun.
3. You won't be able to detect the impact easily, so if your ability deals triggered damage it'll basically deal its damage immediately and then the storm bolt will hit it some time later - it looks sloppy and unpolished. It gets even more complicated when you want to do something like knock the target back.

It's precisely because Channel does "nothing" that it's so useful. You're going to have to create a dummy projectile, deal scripted damage, and apply scripted effects if you want your spell to seem polished, so you may as well use Channel.

Is there a standard out of the box system for doing this dummy projectile system? What if it's a melee ranged spell? Do we need to worry about the timing of the dummy projectile then?
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,543
Is there a standard out of the box system for doing this dummy projectile system?

No, but for most cases, there is a "correct" way (in jass)

What if it's a melee ranged spell? Do we need to worry about the timing of the dummy projectile then?

Well, I can think of some ways a melee ranged spell might want to have a projectile (projectile goes up and comes down?) but if you mean for the case of "instant" abilities, then you need not use such a projectile system.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
No, but for most cases, there is a "correct" way (in jass)



Well, I can think of some ways a melee ranged spell might want to have a projectile (projectile goes up and comes down?) but if you mean for the case of "instant" abilities, then you need not use such a projectile system.

I'm just hoping for a pointer to jass code where all I do is insert the rawcode of the ability and the projectile model to use. Then it works like magic. There is no such available resource? Obviously it would have an empty spot where it would have comments saying

//do your damage/special effects here
//they are guaranteed to be synchronous with the missile impact
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,543
If all you want is:

A projectile for homing in on a unit which does damage when it lands

Then you should use, from http://www.wc3c.net/showthread.php?t=81742 :

ANab, AEsh (Acid Bomb, Shadow Strike): Acid bomb is very versatile and probably the best base for a direct damage ability. It has many nice features like slowing movement and attack speed, armor reduction, instant damage, damage over time, buffs, projectile art – yet all those effects can easily be removed if required. Also it doesn't have stun or any limitations regarding the allowed targets. Shadow strike offers similar fields except armor reduction. Additionally it displays the initial damage with a green fading text above the target. To get rid of the little dude and the sound, simply replace the default buff with a custom empty buff.

Acid Bomb.

If you want a homing projectile that applies arbitrary effects, you should learn to write it yourself because there many optimizations that can occur at an ad-hoc basis (and those optimizations matter)

Maybe this will get you started:

When a unit dies near a lieutenant, an orb is launched upwards from the dying unit which homes in on the lieutenant, healing him for a fraction of the dying unit's max health:

JASS:
scope Bloodthirst initializer i
    struct healingOrb
        unit orb
        unit target
        real size
        real dX
        real dY
        real dZ
        effect model
    endstruct
    
    globals
        private constant real ENUM_RAD = 600.
        private constant real MULTIPLIER = .1
        private constant real OVERFLOW_DURATION = 3.
        private constant real CLOCK_PERIOD = 1./30.
        private constant real INITIAL_ORB_VEL = 1000.
        private constant real ACCELERATION = 50.
        private constant real CHEST_HEIGHT = 50.
        private constant real TOUCH_DISTANCE_SQUARED = 80.*80.
        private constant real CHEAP_AIR_FRICTION = .6 //What fraction of the velocity is lost per second
        private constant real HEAL_BUFFER_MULTIPLIER = 1.
        private constant real HEAL_BUFFER_DURATION = 3.
        private constant string BLOOD_MODEL = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
        private constant string HEAL_EFFECT  = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"
        private constant string BUFFER_FX   = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
        private constant string BUFFER_FX_POINT="overhead"
        private group g = CreateGroup()
        private timer time = CreateTimer()
        private healingOrb array db
        private integer dbIndex = -1
    endglobals
    
    private function p takes nothing returns nothing
        local integer index = 0
        local healingOrb tempOrb
        local real xyDist2
        local real xyzDist2
        local real tX
        local real tY
        local real oX
        local real oY
        local real oZ
        local real ang
        local real zAng
        loop
            exitwhen index>dbIndex
            set tempOrb=db[index]
            // Simulate Orb Movement
            set tX=GetUnitX(tempOrb.target)
            set tY=GetUnitY(tempOrb.target)
            set oX=GetUnitX(tempOrb.orb)
            set oY=GetUnitY(tempOrb.orb)
            set oZ=GetUnitFlyHeight(tempOrb.orb)
            call SetUnitX(tempOrb.orb,oX+tempOrb.dX)
            call SetUnitY(tempOrb.orb,oY+tempOrb.dY)
            call SetUnitFlyHeight(tempOrb.orb,oZ+tempOrb.dZ,0.)
            
            // Calculate new vector acceleration
            set ang = Atan2(tY-oY,tX-oX)
            set xyDist2 = (tX-oX)*(tX-oX) + (tY-oY)*(tY-oY)
            set zAng= Atan2(CHEST_HEIGHT-oZ,SquareRoot(xyDist2))
            set xyzDist2 = (tX-oX)*(tX-oX) + (tY-oY)*(tY-oY) + (CHEST_HEIGHT-oZ)*(CHEST_HEIGHT-oZ)
            set tempOrb.dX=tempOrb.dX + ACCELERATION*Cos(ang)*Cos(zAng)*CLOCK_PERIOD
            set tempOrb.dY=tempOrb.dY + ACCELERATION*Sin(ang)*Cos(zAng)*CLOCK_PERIOD
            set tempOrb.dZ=tempOrb.dZ + ACCELERATION*Sin(zAng)*CLOCK_PERIOD
            
            // Calculate cheap air friction
            set tempOrb.dX = tempOrb.dX * (1.-CHEAP_AIR_FRICTION*CLOCK_PERIOD)
            set tempOrb.dY = tempOrb.dY * (1.-CHEAP_AIR_FRICTION*CLOCK_PERIOD)
            set tempOrb.dZ = tempOrb.dZ * (1.-CHEAP_AIR_FRICTION*CLOCK_PERIOD)
            
            // Check for no target
            if GetUnitTypeId(tempOrb.target)==0 or not UnitAlive(tempOrb.target) then
                call DestroyEffect(tempOrb.model)
                call DummyUnitStack.release(tempOrb.orb)
                call tempOrb.destroy()
                set db[index]=db[dbIndex]
                set dbIndex=dbIndex-1
                set index=index-1
                if dbIndex==-1 then
                    call PauseTimer(time)
                endif
            //Check for touch
            elseif xyzDist2<TOUCH_DISTANCE_SQUARED then
                call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT,tempOrb.target,"overhead"))
                set oX=GetUnitState(tempOrb.target,UNIT_STATE_LIFE)
                set oY=GetUnitState(tempOrb.target,UNIT_STATE_MAX_LIFE)
                set tX=oY-oX
                set tY=tX-tempOrb.size
                if tY>0. then
                    call SetUnitState(tempOrb.target,UNIT_STATE_LIFE,oX+tempOrb.size)
                else
                    call SetUnitState(tempOrb.target,UNIT_STATE_LIFE,oY)
                    call Shield.add(tempOrb.target,-1.*HEAL_BUFFER_MULTIPLIER*tY,HEAL_BUFFER_DURATION,BUFFER_FX,BUFFER_FX_POINT)
                endif
                call DestroyEffect(tempOrb.model)
                call DummyUnitStack.release(tempOrb.orb)
                call tempOrb.destroy()
                set db[index]=db[dbIndex]
                set dbIndex=dbIndex-1
                set index=index-1
                if dbIndex==-1 then
                    call PauseTimer(time)
                endif
            endif
            set index=index+1
        endloop
    endfunction
    
    private function c takes nothing returns boolean
        local unit tU=GetTriggerUnit()
        local unit FoG
        local unit bestMatch=null
        local real val = 1000000.
        local real life
        local real scale
        local real tX=GetUnitX(tU)
        local real tY=GetUnitY(tU)
        local healingOrb orb
        if GetUnitAbilityLevel(tU,'Aloc')<1 then
            call GroupEnumUnitsInRange(g,tX,tY,ENUM_RAD,null)
            loop
                set FoG=FirstOfGroup(g)
                exitwhen FoG==null
                if GetUnitTypeId(FoG)==LIEUT_ID and UnitAlive(FoG) then
                    set life = GetUnitState(FoG,UNIT_STATE_LIFE)
                    if life<val then
                        set bestMatch = FoG
                        set val = life
                    endif
                endif
                call GroupRemoveUnit(g,FoG)
            endloop
            if bestMatch!=null then
                set orb = healingOrb.create()
                set orb.target=bestMatch
                set orb.size=GetUnitState(tU,UNIT_STATE_MAX_LIFE)*MULTIPLIER
                set orb.orb=DummyUnitStack.get()
                set scale = .75 + Math.ln(orb.size)*.1
                call SetUnitScale(orb.orb,scale,scale,scale)
                set orb.model=AddSpecialEffectTarget(BLOOD_MODEL,orb.orb,"origin")
                call SetUnitX(orb.orb,tX)
                call SetUnitY(orb.orb,tY)
                call SetUnitFlyHeight(orb.orb,75.,0.)
                set orb.dX=0.
                set orb.dY=0.
                set orb.dZ=INITIAL_ORB_VEL*CLOCK_PERIOD
                set dbIndex=dbIndex+1
                set db[dbIndex]=orb
                if dbIndex==0 then
                    call TimerStart(time,CLOCK_PERIOD,true,function p)
                endif
            endif
        endif
        set tU=null
        return false
    endfunction
    
    private function i takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddCondition(t,Condition(function c))
        set t=null
    endfunction
endscope
 
Level 5
Joined
Jun 14, 2009
Messages
106
Personally I decide whether I base a spell of channel or not by considering the effects of ai.

If you need to make complex spells which you do not wish the hero to cast on his own, but be completely controlled by ai, I suggest you use channel since spells based off channel are never cast by the cpu spontaneously. However if you wish to avoid complex ai triggering, I suggest you use other spells so that the ai casts spells without the need of a prompt.

If you're not planning on using ai, then it doesn't matter whether you choose channel or not to base your spell, just do whatever makes you feel comfortable.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
But how do you figure out all the art values, like acceleration, the size of the projectile, angle of impact, etc.?

I'd have to eyeball those over and over again before I decided on a set of values...That sounds more like an art than a science.

Also I'm not a geometry guy, and it looks like you're actually calculating the maths behind the projectile. I figure that's not really a job for the programmer, but more like an engineer or artist?

Is there a library of generic custom spells?

But if I use acid bomb, won't it conflict if the unit also has another ability based on acid bomb's rawcode?

Also, what is the order id of the channel spell? This has been the most confusing so far.
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,543
But how do you figure out all the art values, like acceleration, the size of the projectile, angle of impact, etc.?

With reference to my script? All the values are in units per second. Consider that 100 units is melee range, that should help.

If you want to make acceleration independent on the timer, do this:

acceleration = 100

timer period = 1/30

then later in the periodic function:

set delVelocity = delVeloity + acceleration * timer period

make sense?

Size of the projectile: 1.0 is standard size. If you're using a projectile model as your projectile just start with 1 :)

Is there a library of generic custom spells?

There are spells submitted in the spells section, but your mileage will vary hugely. There are simply too many ways to make custom spells.

Does the projectile have constant velocity? Does it have a fixed angle of movement? Can it collide with terrain? Does it have to respect variations in terrain height? Does it effect targets along its path? Does it have a maximum range? All of these questions need to be answered so one can make the system efficient --> many individual spells.

But if I use acid bomb, won't it conflict if the unit also has another ability based on acid bomb's rawcode?

Maybe, you can try changing the order ID of the second instance - I've never tested this myself.

Also, what is the order id of the channel spell? This has been the most confusing so far.

The base order id is "channel" but you can change it to whatever you want and it responds correctly - that's the beauty of channel.
 
Maybe, you can try changing the order ID of the second instance - I've never tested this myself.

for spells not using Channel as a base, you can only change OrderString, but even if you do that, it will still collide... The orderstring seems to only affect the command needed to initiate or whatever the spell, but the actual internal casting is still using the BaseOrderID... so since non-channel based spells cannot change BaseOrderIDs, they still collide when you have more than 1 from the same base spell on 1 unit...

Also, what is the order id of the channel spell? This has been the most confusing so far.

You specify that using the BaseOrderID field...
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
If you want to use Acid Bomb just to create the missile visual effect you dont have to make your spell based on acid bomb, you can make your spell based on channel and cast acidbomb via dummycaster. This way you can have an arbitrary amount of missile spells coexisting on a single unit.

Acid bomb is a very nice and simple solution for homing missile visual effects (and it provides a significantly higher performance than triggering the missile). The only problem is that its hard to detect the impact so you might just wanna use a timer to estimate the flight time. This can be inaccurate when the target moves, however with fast missiles the error is small (Especially for Maps like RPGs this shouldnt be a problem).
As an example: most of the missile spells in Gaias Retaliation are based on Acid Bomb.

€: WaterKnight just reminded me that Acid Bomb can be easily detected using the damage event. In particular when its casted via dummycaster its also easy to assign the damage events to their respective missiles.
 
Status
Not open for further replies.
Top