dota-2-lua-abilities 地址

invoker_chaos_meteor.ts

import {BaseAbility, registerAbility} from "../lib/dota_ts_adapter";
import {modifier_invoker_chaos_meteor_thinker} from "../modifiers/modifier_invoker_chaos_meteor_thinker";

@registerAbility()
export class invoker_chaos_meteor extends BaseAbility
{
    OnSpellStart()
    {
        const caster = this.GetCaster();
        const point = this.GetCursorPosition();
        
        CreateModifierThinker(caster, this,
            modifier_invoker_chaos_meteor_thinker.name, {},
            point, caster.GetTeamNumber(), false);
    }
}

modifier_invoker_chaos_meteor_burn.ts

import {BaseModifier, registerModifier} from "../lib/dota_ts_adapter";

@registerModifier()
export class modifier_invoker_chaos_meteor_burn extends BaseModifier
{
    IsHidden(): boolean
    {
        return false;
    }
    
    IsDebuff(): boolean
    {
        return true;
    }
    
    IsStunDebuff(): boolean
    {
        return false;
    }
    
    IsPurgable(): boolean
    {
        return false;
    }
    
    private _damageTable:ApplyDamageOptions;
    
    OnCreated(params: object)
    {
        if (!IsServer())
            return
        
        const hero = this.GetCaster() as CDOTA_BaseNPC_Hero;
        
        this._damageTable = {
            victim: this.GetParent(),
            attacker: this.GetCaster(),
            damage: hero.GetIntellect() * this.GetAbility().GetSpecialValueFor("burn_damage"),
            damage_type: DAMAGE_TYPES.DAMAGE_TYPE_MAGICAL,
            ability: this.GetAbility()
        }
        
        this.StartIntervalThink(1);
    }
    
    OnIntervalThink()
    {
        ApplyDamage(this._damageTable)

        const sound_tick = "Hero_Invoker.ChaosMeteor.Damage"
        EmitSoundOn( sound_tick, this.GetParent())
    }
    
    GetEffectName(): string
    {
        return "particles/units/heroes/hero_invoker/invoker_chaos_meteor_burn_debuff.vpcf";
    }
    
    GetEffectAttachType(): ParticleAttachment_t
    {
        return ParticleAttachment_t.PATTACH_ABSORIGIN_FOLLOW;
    }
}

modifier_invoker_chaos_meteor_thinker.ts

import {BaseModifier, registerModifier} from "../lib/dota_ts_adapter";
import {modifier_invoker_chaos_meteor_burn} from "./modifier_invoker_chaos_meteor_burn";

@registerModifier()
export class modifier_invoker_chaos_meteor_thinker extends BaseModifier
{
    private _burn_duration: number;
    private _intellectual_multiplier: number;
    private _travel_speed: number;
    private _damageTable: ApplyDamageOptions;
    private _delay: number = 1.26;
    private _caster_origin: Vector;
    private _parent_origin: Vector;
    private _direction: Vector;
    private _damageInterval: number = 0.5;
    private _damageRadius: number = 275;
    private _distance: number = 1500;

    OnCreated(params: object)
    {
        if (!IsServer())
            return;

        let ability = this.GetAbility();
        let caster = this.GetCaster() as CDOTA_BaseNPC_Hero;

        this._caster_origin = caster.GetOrigin();
        this._parent_origin = this.GetParent().GetOrigin();

        this._direction = this._parent_origin.__sub(this._caster_origin);
        this._direction.z = 0;
        this._direction = this._direction.Normalized();

        this._burn_duration = ability.GetSpecialValueFor("burn_duration");
        this._intellectual_multiplier = ability.GetSpecialValueFor("intellectual_multiplier");
        this._travel_speed = ability.GetSpecialValueFor("travel_speed");

        this._damageTable = {
            damage: caster.GetIntellect() * this._intellectual_multiplier,
            ability: ability,
            victim: undefined,
            attacker: caster,
            damage_type: ability.GetAbilityDamageType()
        };

        //落地时间延迟
        this.StartIntervalThink(this._delay);
        this._playEffects1();
    }

    private _fallen: boolean = false;

    OnIntervalThink()
    {
        if (!this._fallen)
        {
            this._fallen = true;
            this.StartIntervalThink(this._damageInterval);
            this._burn();

            this._playEffects2();
        }
        else
        {
            this._move_Burn();
        }
    }

    private _burn()
    {
        const enemies = FindUnitsInRadius(
            this.GetCaster().GetTeamNumber(),
            this.GetParent().GetOrigin(),
            undefined,
            this._damageRadius,
            this.GetAbility().GetAbilityTargetTeam(),
            this.GetAbility().GetAbilityTargetType(),
            this.GetAbility().GetAbilityTargetFlags(),
            FindOrder.FIND_ANY_ORDER,
            false
        );

        for (let enemy of enemies)
        {
            this._damageTable.victim = enemy;
            ApplyDamage(this._damageTable);

            enemy.AddNewModifier(
                this.GetCaster(),
                this.GetAbility(),
                modifier_invoker_chaos_meteor_burn.name,
                {"duration": this._burn_duration});
        }
    }

    private _move_Burn()
    {
        const parent = this.GetParent();

        const parent_origin = this._parent_origin;

        const target = this._direction.__mul(this._travel_speed).__mul(0.5);

        parent.SetOrigin(parent.GetOrigin().__add(target));

        this._burn();

        if ((parent.GetOrigin().__sub(parent_origin).__add(target).Length2D() > this._distance))
            this.Destroy();
    }

    OnDestroy()
    {
        if (!IsServer())
            return;

        const sound_loop = "Hero_Invoker.ChaosMeteor.Loop";
        const sound_stop = "Hero_Invoker.ChaosMeteor.Destroy";
        StopSoundOn(sound_loop, this.GetParent());
        EmitSoundOnLocationWithCaster(this.GetParent().GetOrigin(), sound_stop, this.GetCaster());
    }

    private _playEffects1()
    {
        const particle_cast = "particles/units/heroes/hero_invoker/invoker_chaos_meteor_fly.vpcf";
        const sound_impact = "Hero_Invoker.ChaosMeteor.Cast";

        const height = 1000;
        const height_target = -0;

        const effect_cast = modifier_invoker_chaos_meteor_thinker._createEffect(this, particle_cast, ParticleAttachment_t.PATTACH_WORLDORIGIN, undefined);
        ParticleManager.SetParticleControl(effect_cast, 0, this._caster_origin.__add(Vector(0, 0, height)));
        ParticleManager.SetParticleControl(effect_cast, 1, this._parent_origin.__add(Vector(0, 0, height_target)));
        ParticleManager.SetParticleControl(effect_cast, 2, Vector(this._delay, 0, 0));
        ParticleManager.ReleaseParticleIndex(effect_cast);

        EmitSoundOnLocationWithCaster(this._caster_origin, sound_impact, this.GetCaster());
    }

    private _playEffects2()
    {
        const particle_cast = "particles/units/heroes/hero_invoker/invoker_chaos_meteor.vpcf";
        const sound_impact = "Hero_Invoker.ChaosMeteor.Impact";
        const sound_loop = "Hero_Invoker.ChaosMeteor.Loop";

        const effect_cast = modifier_invoker_chaos_meteor_thinker._createEffect(this, particle_cast, ParticleAttachment_t.PATTACH_WORLDORIGIN, undefined);
        ParticleManager.SetParticleControl(effect_cast, 0, this._parent_origin);
        ParticleManager.SetParticleControlForward(effect_cast, 0, this._direction);
        ParticleManager.SetParticleControl(effect_cast, 1, this._direction.__mul(this._travel_speed));

        this.AddParticle(
            effect_cast,
            false,
            false,
            -1,
            false,  
            false
        )

        EmitSoundOnLocationWithCaster( this._parent_origin, sound_impact, this.GetCaster())
        EmitSoundOn( sound_loop, this.GetParent())
    }

    private static _referenceEffects(): { [key: string]: string }
    {
        return {
            ["particles/units/heroes/hero_antimage/antimage_manavoid.vpcf"]: "particles/rubick_manavoid.vpcf",

            ["particles/units/heroes/hero_crystalmaiden/maiden_freezing_field_explosion.vpcf"]: "particles/rubick_freezing_field_explosion.vpcf",
            ["particles/units/heroes/hero_crystalmaiden/maiden_freezing_field_snow.vpcf"]: "particles/rubick_freezing_field_snow.vpcf",

            ["particles/units/heroes/hero_dark_willow/dark_willow_wisp_aoe.vpcf"]: "particles/rubick_wisp_aoe_proj.vpcf",
            ["particles/units/heroes/hero_dark_willow/dark_willow_wisp_aoe_cast.vpcf"]: "particles/rubick_wisp_aoe_cast.vpcf",
            ["particles/units/heroes/hero_dark_willow/dark_willow_wisp_spell_channel.vpcf"]: "particles/rubick_wisp_spell_channel.vpcf",
            ["particles/units/heroes/hero_dark_willow/dark_willow_willowisp_base_attack.vpcf"]: "particles/rubick_willowisp_base_attack.vpcf",
            ["particles/units/heroes/hero_dark_willow/dark_willow_willowisp_ambient.vpcf"]: "particles/rubick_willowisp_ambient.vpcf",

            ["particles/units/heroes/hero_earthshaker/earthshaker_fissure.vpcf"]: "particles/rubick_fissure.vpcf",

            ["particles/units/heroes/hero_enigma/enigma_blackhole.vpcf"]: "particles/rubick_blackhole.vpcf",

            ["particles/units/heroes/hero_faceless_void/faceless_void_chronosphere.vpcf"]: "particles/rubick_chronosphere.vpcf",

            ["particles/units/heroes/hero_invoker/invoker_chaos_meteor.vpcf"]: "particles/rubick_chaos_meteor.vpcf",
            ["particles/units/heroes/hero_invoker/invoker_chaos_meteor_fly.vpcf"]: "particles/rubick_chaos_meteor_fly.vpcf",

            ["particles/econ/items/lich/lich_ti8_immortal_arms/lich_ti8_chain_frost.vpcf"]: "particles/rubick_chain_frost.vpcf",

            ["particles/units/heroes/hero_shredder/shredder_chakram.vpcf"]: "particles/rubick_chakram.vpcf",
            ["particles/units/heroes/hero_shredder/shredder_chakram_stay.vpcf"]: "particles/rubick_chakram_stay.vpcf",
            ["particles/units/heroes/hero_shredder/shredder_chakram_return.vpcf"]: "particles/rubick_chakram_return.vpcf",

            ["particles/units/heroes/hero_omniknight/omniknight_guardian_angel_ally.vpcf"]: "particles/rubick_guardian_angel_ally.vpcf",
            ["particles/units/heroes/hero_omniknight/omniknight_guardian_angel_omni.vpcf"]: "particles/rubick_guardian_angel_rubick.vpcf"
        };
    }

    private static _createEffect(self: BaseModifier, particle_name: string, iAttach: ParticleAttachment_t, hUnit?: CDOTA_BaseNPC, iTeam?: DOTATeam_t)
    {
        const ability = self.GetAbility();
        const spell_steal = ability.GetCaster().FindAbilityByName("rubick_spell_steal_lua");
        const stolen = (ability.IsStolen() && spell_steal);

        const particle_alternate = this._referenceEffects()[particle_name] || particle_name;

        //实际这里返回就行,这里多半做了下拉比克偷技能的处理
        if (!stolen)
            return ParticleManager.CreateParticle(particle_name, iAttach, hUnit);

        let effect_cast;
        
        if (iTeam)
            effect_cast = ParticleManager.CreateParticleForTeam(particle_alternate, iAttach, hUnit, iTeam);
        else
            effect_cast = ParticleManager.CreateParticle(particle_alternate, iAttach, hUnit);

        let color = Vector(0, 0, 0);
        let level = spell_steal.GetLevel();
        if (level == 1)
            color.x = 255;
        else if (level == 2)
            color.y = 255;
        else if (level == 3)
            color.z = 255;

        ParticleManager.SetParticleControl(effect_cast, 60, color);
        ParticleManager.SetParticleControl(effect_cast, 61, Vector(1, 0, 0));

        return effect_cast;
    }
}

kv

"invoker_chaos_meteor" {
		"BaseClass" "ability_lua"
		"ScriptFile" "abilities/invoker_chaos_meteor"
		"AbilityTextureName" "throw_coal"
		"AbilityType" "DOTA_ABILITY_TYPE_BASIC"
		"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING"
		"AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY"
		"AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC"
		"AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL"
		"AbilityUnitTargetFlags" "DOTA_UNIT_TARGET_FLAG_NO_INVIS"
		"MaxLevel" "1"
		"AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_3"
		"AbilityCastPoint" "0.3"
		"AbilityCooldown" "1"
		"AbilityDuration" "5"
		"AbilityCastRange" "1200"
		"AbilitySpecial" 
		{
			"01" 
			{
				"var_type" "FIELD_INTEGER"
				"burn_duration" "5"
			}
			"02" 
			{
				"var_type" "FIELD_FLOAT"
				"burn_damage" "0.15"
			}
			"03" 
			{
				"var_type" "FIELD_FLOAT"
				"intellectual_multiplier" "0.8"
			}
			"04" 
			{
				"var_type" "FIELD_INTEGER"
				"travel_speed" "300"
			}
		}
	}

效果

我还写了一个ProjectileManager.CreateLinearProjectile的版,本,Projectile实现

ProjectileManager.CreateLinearProjectile 对特效的控制权少了很多