Deactivatable Spells without Engineering Upgrade?

Tamisrah

Active Member
Reaction score
16
I'm trying to create spells which you can activate/deactivate like immolation without using an immolation-like spell as a base. Why would I want to do that? I want spells which have the following attributes:
- targetable (unit or point)
- cost mana over time
- can be deactivated at will
- is not channeled

My current method:
remove the targetable spell on cast
add a spell which takes no target and does nothing on itself
trigger all the effects
if the other spell is cast, remove it and readd the targetable spell

The problem with this is, that if you try to spend another skill point on this spell while it's activated you learn the ability another time instead of leveling it up.

Now my actual question: Is there any way to avoid this glitch without using engineering upgrade?

Prove of Concept - Recreating Immolation
JASS:
//! zinc
library ActivatableSpell requires SpellEvent
{
    Table Ability2Order;
    
    function onEffect ()
    {
        integer id = GetSpellAbilityId();
        unit whichUnit = GetTriggerUnit();
        if (Ability2Order.exists(id))
        {
            UnitRemoveAbility(whichUnit,id);
            UnitAddAbility(whichUnit,Ability2Order[id]);
        }
    }
    
    public function RegisterActivatableSpell (integer BaseId, SpellEvent_Response onActivate, integer AuxId, SpellEvent_Response onDeactivate)
    {
        Ability2Order[BaseId] = AuxId;
        RegisterSpellEffectResponse(BaseId,onActivate);
        
        Ability2Order[AuxId]  = BaseId;
        RegisterSpellEffectResponse(AuxId,onDeactivate);
    }
    
    function onInit ()
    {
        trigger t = CreateTrigger();
        TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT);
        TriggerAddAction(t,function onEffect);
        
        Ability2Order = Table.create();
    }
}
//! endzinc

JASS:
//! zinc
library Immolation requires ActivatableSpell, ABuff
{
    constant integer SpellId = 'A000';
    constant integer AuxId   = 'A001';
    constant integer AuraId  = 'C000';
    constant integer BuffId  = 'BEim';
    aBuffType BuffType;
    
    constant string BurnFx   = "Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl";
    
    function getRange (integer level) -> real
    {
        return 160.;
    }
    
    function getBurnDamage (integer level) -> real
    {
        return (5.+5*level)*ABuff_PERIODIC_EVENT_PERIOD;
    }
    
    function getPeriodicCost (integer level) -> real
    {
        return 7.*ABuff_PERIODIC_EVENT_PERIOD;
    }
    
    function getManaThreshold (integer level) -> real
    {
        return 10.;
    }
    
    function targetFilter () -> boolean
    {
        unit u = GetFilterUnit();
        boolean b = IsUnitType(u,UNIT_TYPE_GROUND)==true && IsUnitEnemy(u,bj_groupEnumOwningPlayer)==true && IsUnitType(u,UNIT_TYPE_MECHANICAL)==false && IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==false;
        u = null;
        return b;
    }
    
    function onCreate (aBuff eventBuff)
    {
        BuffType.display.apply(eventBuff.target.u,eventBuff.level);
    }
    
    function onCleanup (aBuff eventBuff)
    {
        BuffType.display.remove(eventBuff.target.u);
    }
    
    function onPeriodic (aBuff eventBuff)
    {
        real damage = getBurnDamage(eventBuff.level);
        unit f;
        bj_groupEnumOwningPlayer = GetOwningPlayer(eventBuff.target.u);
        GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(eventBuff.target.u),GetUnitY(eventBuff.target.u),getRange(eventBuff.level),Filter(function targetFilter));
        f = FirstOfGroup(ENUM_GROUP);
        while (f!=null)
        {
            GroupRemoveUnit(ENUM_GROUP,f);
            DestroyEffect(AddSpecialEffectTarget(BurnFx,f,"head"));
            UnitDamageTargetEx(eventBuff.target.u, f, damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_SPELL, true);
            f = FirstOfGroup(ENUM_GROUP);
        }
        SetUnitState(eventBuff.target.u,UNIT_STATE_MANA,GetUnitState(eventBuff.target.u,UNIT_STATE_MANA)-getPeriodicCost(eventBuff.level));
        if (GetUnitState(eventBuff.target.u,UNIT_STATE_MANA)<getManaThreshold(eventBuff.level))
        {
            ABuffRemove(eventBuff);
        }
    }
    
    function onActivate ()
    {
        ABuffApply(BuffType,SpellEvent.TargetUnit,SpellEvent.CastingUnit,0,SpellEvent.SpellLevel,0);
    }
    
    function onDeactivate ()
    {
        ABuffRemove(GetABuffFromUnitByType(SpellEvent.CastingUnit,BuffType));
    }
    
    function onInit ()
    {
        RegisterActivatableSpell(SpellId,onActivate,AuxId,onDeactivate);
        BuffType = aBuffType.create();
        BuffType.eventCreate   = onCreate;
        BuffType.eventCleanup  = onCleanup;
        BuffType.eventPeriodic = onPeriodic;
        BuffType.display       = aBuffDisplay.create(AuraId,BuffId);
    }
}
//! endzinc
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
as the ability learned by your heroes you could use an invisible passive dummy ability. when this dummy ability is learned add the active unit-target ability.
 

Tamisrah

Active Member
Reaction score
16
Thanks a bunch, that works like a charm.
JASS:
//! zinc
library SpellToggleEvent requires SpellEvent, Table, BoolexprUtils
{
    Table Storage;
    
    struct Data
    {
        integer learnDummy;
        integer activator;
        integer deactivator;
    }
    
    function onLearn ()
    {
        unit whichUnit = GetLearningUnit();
        integer id = GetLearnedSkill();
        Data d;
        if (Storage.exists(id)==true)
        {
            d = Storage[id];
            if (GetUnitAbilityLevel(whichUnit,id)==1)
            {
                UnitAddAbility(whichUnit,d.activator);
            } else if (GetUnitAbilityLevel(whichUnit,d.activator)>0)
            {
                SetUnitAbilityLevel(whichUnit,d.activator,GetUnitAbilityLevel(whichUnit,id));
            }
        }
    }
    
    function onToggle ()
    {
        unit whichUnit = GetTriggerUnit();
        integer id = GetSpellAbilityId();
        Data d;
        if (Storage.exists(id)==true)
        {
            d = Storage[id];
            if (id==d.deactivator)
            {
                UnitRemoveAbility(whichUnit,d.deactivator);
                UnitAddAbility(whichUnit,d.activator);
                SetUnitAbilityLevel(whichUnit,d.activator,GetUnitAbilityLevel(whichUnit,d.learnDummy));
            } else {
                UnitRemoveAbility(whichUnit,d.activator);
                UnitAddAbility(whichUnit,d.deactivator);
            }
        }
    }
    
    public function RegisterSpellToggleResponse (integer LearnedSkill, integer Activator, integer Deactivator, SpellEvent_Response onActivation, SpellEvent_Response onDeactivation)
    {
        Data d;
        if (!Storage.exists(LearnedSkill) && !Storage.exists(Activator) && !Storage.exists(Deactivator))
        {
            d = Data.create();
            d.learnDummy  = LearnedSkill;
            d.activator   = Activator;
            d.deactivator = Deactivator;
            Storage[LearnedSkill] = d;
            Storage[Activator]    = d;
            Storage[Deactivator]  = d;
            RegisterSpellEffectResponse(Activator,onActivation);
            RegisterSpellEffectResponse(Deactivator,onDeactivation);
        } else {
            debug BJDebugMsg("SpellToggleEvent - Error: You may not register spells twice");
        }
    }

    function onInit ()
    {
        trigger t1 = CreateTrigger();
        trigger t2 = CreateTrigger();
        integer i;
        player p;
        for (i=0;i<bj_MAX_PLAYER_SLOTS;i+=1)
        {
            p = Player(i);
            TriggerRegisterPlayerUnitEvent(t1,p,EVENT_PLAYER_HERO_SKILL,BOOLEXPR_TRUE);
            TriggerRegisterPlayerUnitEvent(t2,p,EVENT_PLAYER_UNIT_SPELL_EFFECT,BOOLEXPR_TRUE);
        }
        TriggerAddAction(t1,function onLearn);
        TriggerAddAction(t2,function onToggle);
        
        Storage = Table.create();
    }
}
//! endzinc
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top