• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

CR - Attacks Engine

JASS:
library AttacksEngine initializer Init requires MainEngine, SkillsDatabase, Movement

// globals
    CastData CastStack[]
    private trigger Attack = CreateTrigger(), Cast = CreateTrigger()
    private int TempSource, Stack = 0
    int last_CastData = 0
    unit last_CastTargetUnit
    real last_Target_X, last_Target_Y
    real SpellX, SpellY
    group CastGroup = CG
    trigger CastBreak = CreateTrigger()

    
    define { 
        CAST_BAR_UNIT = 'h001' 
        CAST_CHANNEL_SKILL = 'A01C'
    }
    
    
    struct CastData
        unit caster, bar, target
        real cast_time, targ_x, targ_y
        int skill, level
    endstruct
    
    
    int GetUnitCastData(unit A){
        int index = 0 
            while(index++ < Stack){
                if A == CastStack[index].caster { last_CastData = index ;return index }
            }
        return 0
    }
    
//===========================================================================
    void BreakCast(unit A){
        int data = GetUnitCastData(A)
        UD unit_data = GetData(A)
            RemoveUnit(CastStack[data].bar)
            GroupRemoveUnit(CastGroup, A)
            IssueImmediateOrder(A, "stop")
            UnitAddAbility(A, unit_data.last_casted)
            unit_data.last_casted = 0
            SetMp(A, GetMp(A) + UseMpAmount[CastStack[data].skill][CastStack[data].level])
            CastStack[data].destroy()
            CastStack[data] = CastStack[Stack]
            Stack--
            //msg("removed - break")
            //msg(I2S(Stack))
    }
    
    bool CastBreak_Conditions(){
        if IsUnitInGroup(GetTriggerUnit(), CastGroup) {
            BreakCast(GetTriggerUnit())
        }
    return false
    }

    private void EndCast(CastData cast_data){
        UD source = GetData(cast_data.caster)
        int index = GetUnitCastData(cast_data.caster)
        GroupRemoveUnit(CastGroup, cast_data.caster)
        UnitRemoveAbility(cast_data.caster, CAST_CHANNEL_SKILL)
        UnitAddAbility(cast_data.caster, source.last_casted)
        IssueImmediateOrder(CastStack[index].caster, "stop")
            if cast_data.target != null
            { IssueTargetOrderById(cast_data.caster, Order[cast_data.skill], cast_data.target) }
            elseif (cast_data.targ_x != 0. and cast_data.targ_y != 0.)
            { IssuePointOrderById(cast_data.caster, Order[cast_data.skill], cast_data.targ_x, cast_data.targ_y) }
            else
            { IssueImmediateOrderById(cast_data.caster, Order[cast_data.skill]) }
        RemoveUnit(CastStack[index].bar)
        CastStack[index].destroy()
        CastStack[index] = CastStack[Stack]
        Stack--
        //msg("removed - end")
    }
    
    private void CastUpdate(){
        int index = 0
            while(index++ < Stack){
                if CastStack[index].cast_time <= 0. {
                    EndCast(CastStack[index])
                }
                elseif CastStack[index].target != null {
                    if DBU(CastStack[index].target, CastStack[index].caster) > Range[CastStack[index].skill][CastStack[index].level] + 75. {
                        //msg("out of range")
                        BreakCast(CastStack[index].caster)
                    }
                    else {
                        SetUnitFacing(CastStack[index].caster, ABU(CastStack[index].caster, CastStack[index].target))
                        CastStack[index].cast_time -= 0.1
                    }
                }
                else {
                    CastStack[index].cast_time -= 0.1
                }
            }
    }
    
    void CastInitialize(unit caster, unit victim, int abilityid, real tx, real ty){
        UD src = GetData(caster)
        int c = GetSkillCell(abilityid), alvl = GetUnitAbilityLevel(caster, abilityid)
        real RealSpeed
            if c == 0 { return }
            // проверка немоты
            if src.silence[1] and SkillType[c] { 
                SimError("Нельзя использовавать физические умения.") 
                IssueImmediateOrder(caster, "stop")
                return
            }
            elseif src.silence[2] and not SkillType[c] { 
                SimError("Нельзя использовавать магические умения.") 
                IssueImmediateOrder(caster, "stop")
                return
            }
            // проверка необходимости бафа 
            if (BuffCondition[c][alvl] > 0 and GetUnitAbilityLevel(caster, BuffCondition[c][alvl]) > 0) {
                    SimError("Отсутствует эффект для активации умения.")
                    IssueImmediateOrder(caster,"stop")
                    return
            }
            // проверка на оружие
            if (WeaponRequire[c][1] != ANY) {
                if (WeaponRequire[c][1] != src.current_weapon.weapon_type and WeaponRequire[c][2] != src.current_weapon.weapon_type and WeaponRequire[c][3] != src.current_weapon.weapon_type) and WeaponRequire[c][1] != SHIELD {
                    SimError("Неподходящее оружие для активации умения.")
                    IssueImmediateOrder(caster,"stop")
                    return
                }
                elseif (WeaponRequire[c][1] == SHIELD and src.shield == 0){
                    SimError("Для этого умения необходим щит.")
                    IssueImmediateOrder(caster,"stop")
                    return
                }
            }    
            // проверка на здоровье
            if HpCondition[c][alvl] > 0. {
                if HpConditionPolarity[c][alvl] {  
                    if GetMaxHp(caster) * HpCondition[c][alvl] < HpCondition[c][alvl] {
                        SimError("Недостаточный запас здоровья для активации умения.")
                        IssueImmediateOrder(caster, "stop")
                        return
                    }
                }
                else {
                    if GetMaxHp(caster) * HpCondition[c][alvl] > HpCondition[c][alvl] {
                        SimError("Недостаточный запас здоровья для активации умения.")
                        IssueImmediateOrder(caster, "stop")
                        return
                    }
                }
            }
            // проверка на положение
            if (UseCondition[c][alvl] == BACK_CONDITION and not IsUnitBack(caster,victim)){
                SimError("Нужно находиться за спиной для активации этого умения.")
                IssueImmediateOrder(caster, "stop")
                return
            }
            
            src.full_time = CastTime[c][alvl] * src.casting_speed
            src.cast_break_time = src.full_time - RMinBJ(0.5 / src.casting_speed, 0.5)
            Stack++
            CastStack[Stack].create()
            CastStack[Stack].bar = CreateUnit(GetOwningPlayer(caster), CAST_BAR_UNIT, Gx(caster) + 45., Gy(caster), 270.)
            SetUnitFlyHeight(CastStack[Stack].bar, GetUnitFlyHeight(caster) + 100., 0.)
            SetUnitVertexColor(CastStack[Stack].bar, 100, 100, 255, 255)
            SetUnitTimeScale(CastStack[Stack].bar, 1. / src.full_time)
            UnitApplyTimedLife(CastStack[Stack].bar, 'BTLF', src.full_time)
            CastStack[Stack].caster = caster
            CastStack[Stack].target = victim
            CastStack[Stack].level = alvl
            CastStack[Stack].skill = c
            CastStack[Stack].targ_x = tx
            CastStack[Stack].targ_y = ty
            CastStack[Stack].cast_time = src.full_time
            // instant spells
                if CastTime[c][alvl] == 0. {
                    EndCast(CastStack[Stack])
                    return
                }  
                if src.full_time < 0.1 { src.full_time = 0.1 }
            src.last_casted = abilityid
            UnitRemoveAbility(caster, abilityid)// <======= this
            UnitAddAbility(caster, CAST_CHANNEL_SKILL)
            IssueImmediateOrderById(caster, order_web)
            GroupAddUnit(CastGroup, caster)
    }

//============================================================================
    private void ReversCooldown(){
        UD caster = GetTimerAttach(GetExpiredTimer())
            if caster.current_weapon.relation == 1 { UnitAddAbility(caster.Owner, ATTACK_ID) }
            else { UnitAddAbility(caster.Owner, RANGE_ATTACK_ID) }
    }

    private void DamageAll(){
        UD source = TempSource
        UD victim = GetData(GetEnumUnit())
        int status = PhysicalDamage(source.Owner, victim.Owner, 0., true, true, false, true, 0)
        BroadcastDamage(status, Last_Damage, victim.Owner)
    }

    private void Damage(){
        UD source = GetTimerAttach(GetExpiredTimer())
        group g = CG
            if LoadInteger(hash, H2I(source.damage_delay_timer), 0) == 1 { 
                ForFilter1 = source.Owner
                GroupEnumUnitsInRange(g, Gx(source.Owner) + Rx(source.current_weapon.range, Ga(source.Owner)), Gy(source.Owner) + Ry(source.current_weapon.range, Ga(source.Owner)), source.current_weapon.range, Filter(function EnemiesFilter))
                TempSource = source
                ForGroup(g, function DamageAll)
            }
            else {
                FireWeaponShell(source.Owner, LoadReal(hash, H2I(source.damage_delay_timer), 0), LoadReal(hash, H2I(source.damage_delay_timer), 1))
            }
        GC(g)
        DG(g)
        g = null
    }

    private void Back(){
        UD source = GetTimerAttach(GetExpiredTimer())
            if source.alternate_effect == 0 {
                PauseUnit(source.Owner, false)
                SetUnitAnimation(source.Owner, "stand ready")
                SetUnitTimeScale(source.Owner, 1.)
            }
    }

    private void Attack_Act(){
        UD caster = GetData(GetSpellAbilityUnit())
        int h = H2I(caster.damage_delay_timer)
        real x = SpellX, y = SpellY
            TimerStartEx(caster.cooldown, caster.attack_speed, function ReversCooldown, caster)
            TimerStartEx(caster.back_delay_timer, caster.back_delay, function Back, caster)
            TimerStartEx(caster.damage_delay_timer, caster.damage_delay, function Damage, caster)
            SaveInteger(hash, h, 0, caster.current_weapon.relation)
            SaveReal(hash, h, 0, SpellX)
            SaveReal(hash, h, 1, SpellY)
                if GetUnitAbilityLevel(caster.Owner, ATTACK_ID) > 0 { 
                    UnitRemoveAbility(caster.Owner, ATTACK_ID) 
                }
                else {
                    UnitRemoveAbility(caster.Owner, RANGE_ATTACK_ID)
                }
            SetUnitAnimation(caster.Owner, "attack")
            SetUnitTimeScale(caster.Owner, caster.animation_speed)
            PauseUnit(caster.Owner, true)
    }

    private bool Attack_Cond() {
        if (GetSpellAbilityId() == ATTACK_ID or GetSpellAbilityId() == RANGE_ATTACK_ID) {
            SpellX = GetSpellTargetX(); SpellY = GetSpellTargetY()
            IssueImmediateOrderById(GetTriggerUnit(), order_stop)
            return true
        }
    return false
    }

    private bool Cast_Act(){
        UD caster = GetData(GetSpellAbilityUnit())
            if GetSkillCell(GetSpellAbilityId()) > 0 {
                if caster.last_casted != GetSpellAbilityId() {
                    CastInitialize(caster.Owner, GetSpellTargetUnit(), GetSpellAbilityId(), GetSpellTargetX(), GetSpellTargetY())
                }
                else {
                    caster.last_casted = 0
                    SetMp(caster.Owner, GetMp(caster.Owner) + UseMpAmount[last_FindedSkill][GetUnitAbilityLevel(caster.Owner, GetSpellAbilityId())])
                }
            }
        return false
    }
    
//============================================================================
    private void Init(){
        TriggerRegisterAnyUnitEventBJ(Attack, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        TriggerAddCondition(Attack, Condition(function Attack_Cond))
        TriggerAddAction(Attack, function Attack_Act)
        
        TriggerRegisterAnyUnitEventBJ(Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        TriggerAddCondition(Cast, Condition(function Cast_Act))
        
        TriggerRegisterAnyUnitEventBJ( CastBreak, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
        TriggerRegisterAnyUnitEventBJ( CastBreak, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
        TriggerRegisterAnyUnitEventBJ( CastBreak, EVENT_PLAYER_UNIT_ISSUED_ORDER )
        TriggerAddCondition( CastBreak, Condition( function CastBreak_Conditions ) )
        
        TimerStart(CT, 0.1, true, function CastUpdate)
    }


endlibrary
Top