• 🏆 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!

Yet more questions about Channel Base Order IDs

Antares

Spell Reviewer
Level 22
Joined
Dec 13, 2009
Messages
521
Hello,

I've recently began to change all unit-targeted spells so that you just have to click in the vicinity of the unit and it will target the nearest unit in order to make it easier to target. As such, I've changed the Channel abilities to "Unit or Point target". What are base order IDs that I can use with this type of targeting? I assume shockwave and carrionswarm have that targeting type? Which else?

Also, I have the problem that the Blizzard AI sometimes casts spells for the bots. The base order ID affects the AI behavior when it comes to casting Channel. Are there base order IDs for which the AI will never cast it?

Cheers
 
Level 14
Joined
Jan 16, 2009
Messages
716
There is breath of fire and impale.
I am pretty sure you should be able to use strict unit target or point target orders without any issue though.

Here is a thread about the spells that the AI will use.
However I think you can disable the AI using PauseCompAI, though I heard that function might not work.
In that case using one of the neutral player slot should work as they don't have AI enabled by default.
 

Antares

Spell Reviewer
Level 22
Joined
Dec 13, 2009
Messages
521
Breath of Fire and Impale are good. So that's four different ones, should be more than enough. Thanks!

I've put PauseCompAI somewhere in the init for the bots, but yea, it doesn't seem to do anything. Changing the AI slots to a neutral player would remove the name, so that's not something I'm too happy about doing.

As I'm replacing the spells with ones based on Channel, I'm reminded that I still have this problem with the spell animation not playing through correctly with a Channel-based spell. I might have to move away from Channel-based spells again if I'm not able to fix that. Data - Art Duration doesn't seem to do anything either.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,570
It shouldn't be too difficult to come up with your own spell casting system that grants full control over the animations played, their animation speed, length, etc.. So I'd say that aspect of it isn't a deterrent if you're willing to throw together a somewhat simple system.

I've done something like this for a map I was making using C#. Assuming this is for Particle Party then it's a funny coincidence because I have sliding in my map as well which I accounted for in the code:
C#:
    public static class Spell {

        public static Ability Ability;
        private static bool SkipSpellStop;

        public static void Init() {
            PlayerUnitEvents.Register(PlayerUnitEvent.SpellChannel, SpellBegin);
            PlayerUnitEvents.Register(PlayerUnitEvent.UnitTypeReceivesOrder, SpellStop);
        }

        private static void SpellBegin() {
            var hero = GetHero(GetTriggerUnit());
            hero.abilityCast = GetSpellAbility();
            hero.abilityCastId = GetSpellAbilityId();

            hero.AbilityDict[hero.abilityCastId].CasterX = GetUnitX(hero.unit);
            hero.AbilityDict[hero.abilityCastId].CasterY = GetUnitY(hero.unit);

            if (hero.AbilityDict[hero.abilityCastId].Option == Options.Instant) {
                // Call the ability method
                BlzStartUnitAbilityCooldown(hero.unit, hero.abilityCastId, hero.AbilityDict[hero.abilityCastId].Cooldown);
                Ability = hero.AbilityDict[hero.abilityCastId];
                hero.AbilityDict[hero.abilityCastId].Method();

                // Cancel casting the ability but don't fire SpellStop()
                SkipSpellStop = true;
                IssueImmediateOrder(hero.unit, "stop");
                return;
            }

            hero.isCasting = true;
            //hero.AbilityDict[hero.abilityCastId].Caster = hero; (this is already set)
            if (GetSpellTargetUnit() != null) { hero.AbilityDict[hero.abilityCastId].Target = GetHero(GetSpellTargetUnit()); }
            hero.AbilityDict[hero.abilityCastId].PointX = GetSpellTargetX();
            hero.AbilityDict[hero.abilityCastId].PointY = GetSpellTargetY();
            hero.AbilityDict[hero.abilityCastId].Angle = Atan2(hero.AbilityDict[hero.abilityCastId].PointY - hero.AbilityDict[hero.abilityCastId].CasterY,
                hero.AbilityDict[hero.abilityCastId].PointX - hero.AbilityDict[hero.abilityCastId].CasterX);

            // This timer allows the animation to play
            TimerStart(CreateTimer(), 0.00f, false, () => {
                TimerRemove(GetExpiredTimer());
                if (hero.isCasting == false) { return; }
                SetUnitTimeScale(hero.unit, hero.AbilityDict[hero.abilityCastId].AnimTimescale);
                SetUnitAnimationByIndex(hero.unit, hero.AbilityDict[hero.abilityCastId].AnimIndex);
                SpellCastingTimer(hero, hero.AbilityDict[hero.abilityCastId].CastingTime);
            });
        }

        private static void SpellStop() {
            if (SkipSpellStop) { SkipSpellStop = false; return; }
            var hero = GetHero(GetTriggerUnit());
            if (hero.AbilityDict[hero.abilityCastId].Option == Options.Instant) { return; }
            if (hero.isCasting == false) { return; }
  
            // Remove the ability timer so it doesn't run as well
            TimerRemove(hero.abilityTimer);

            // Reset casting state
            SetUnitTimeScale(hero.unit, 1.0f);
            hero.isCasting = false;

            // If the hero is sliding they should return to their pre-cast facing angle
            if (hero.isSliding) {
                hero.slideCounter = 10; // 0.20 seconds of strafing time
                SetUnitFacing(hero.unit, hero.slideAngle * RAD2DEG);
                SetUnitAnimationByIndexDelay(hero.unit, hero.slideAnimation);
            }
        }

        private static void SpellCastingTimer(Hero hero, float delay) {
            TimerStart(hero.abilityTimer = CreateTimer(), delay, false, () => {
                TimerRemove(hero.abilityTimer);
                if (hero.isCasting == false) { return; }

                hero.AbilityDict[hero.abilityCastId].CasterX = GetUnitX(hero.unit);
                hero.AbilityDict[hero.abilityCastId].CasterY = GetUnitY(hero.unit);
                hero.AbilityDict[hero.abilityCastId].Angle = Atan2(hero.AbilityDict[hero.abilityCastId].PointY - hero.AbilityDict[hero.abilityCastId].CasterY,
                    hero.AbilityDict[hero.abilityCastId].PointX - hero.AbilityDict[hero.abilityCastId].CasterX);

                // Reset casting state
                SetUnitTimeScale(hero.unit, 1.0f);
                BlzStartUnitAbilityCooldown(hero.unit, hero.abilityCastId, hero.AbilityDict[hero.abilityCastId].Cooldown);
                hero.isCasting = false;
      
                // If the hero is sliding they should return to their pre-cast facing angle
                if (hero.isSliding) {
                    hero.slideCounter = 20; // 0.40 seconds of strafing time
                    SetUnitFacing(hero.unit, hero.slideAngle * RAD2DEG);
                    SetUnitAnimationByIndexDelay(hero.unit, hero.slideAnimation);
                }

                // Call the ability method
                Ability = hero.AbilityDict[hero.abilityCastId];
                hero.AbilityDict[hero.abilityCastId].Method();

                // Cancel casting the ability but don't fire SpellStop()
                SkipSpellStop = true;
                IssueImmediateOrder(hero.unit, "stop");
            });
        }
    }
The idea is to give your abilities a Casting Time (this acts like Cast - Point) and detect the usage of said abilities with the EVENT_PLAYER_UNIT_SPELL_CHANNEL event. Then manage the animations and everything yourself. I don't have a Cast - Backswing implemented here but it wouldn't be too difficult to add, I think you'd just add that amount of time to the Casting Time and adjust the code slightly. With this setup you can give every ability it's own unique Cast - Point and Cast - Backswing instead of having to rely on the constant values that exist for the Casting unit. I mean you could really do just about anything you want.

(I'm sure the code could be improved.)
 
Last edited:

Antares

Spell Reviewer
Level 22
Joined
Dec 13, 2009
Messages
521
That's really cool.

I don't know if it will help me with my problem. I have to be more specific about what's going on. The heroes are entangled, so they can't move and the right-click move commands get rerouted to the sliding system. When I cast a spell based on a non-Channel ability, the animation plays and still persists, even if I give a movement command directly afterwards. This is only because the hero is entangled; if it could move, obviously the animation has to be interrupted by the walking animation. However, if I use the Channel ability, the animation gets overwritten.

I tried to work on a solution with @Bawbz briefly, and he recommended SetUnitAnimationByIndex 0.01 seconds after the spellcast finishes, but that ended up also being overwritten when a movement command is issued. Since you're also using SetUnitAnimationByIndex, I'm doubtful that it works for this problem.

It seems like, no matter which method I use, there are some problems I have to fix. Either the AI problems or the animation problems - whichever is easier to fix.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,570
Ah, I'm using a different system in my map for sliding. I intercept the Orders to move and Pause/Stop/Unpause the unit in response. This prevents the unit from attempting to do the order and doesn't interrupt anything. So my units can move normally, switch to sliding mode (I Use this for mounts/vehicles), and cast spells freely without any issues (as far as I know). You can even make the unit turn and cast a spell in a targeted direction while still retaining it's previous sliding direction (like strafing).
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,570
So, it's basically equivalent to what I'm doing but you don't get the animation problem with Channel? I also heard that you can achieve this by setting the Propulsion Window to 0?
Yeah, Channel will still animation cancel but like I said before you can use a custom spell casting system to create your own spell animations. Combine that with a sliding system that is designed to work with said animation system and you shouldn't have any of the problems you described.

I think Propulsion Window only prevents turning, I remember trying it with mixed results.
 
Top