祈求者 - 混沌陨石(Projectile版本)
该写法不建议实际使用哈,我就是写了玩,看看就好
在移动到
invoker_chaos_meteor
中时,有一个坑点需要注意一下contextName
invoker_chaos_meteor.ts
import {BaseAbility, BaseModifier, registerAbility} from "../lib/dota_ts_adapter";
import {modifier_invoker_chaos_meteor_burn} from "../modifiers/modifier_invoker_chaos_meteor_burn";
@registerAbility()
export class invoker_chaos_meteor extends BaseAbility
{
private _checkIn = 0.5;
private _burn_duration: number;
private _intellectual_multiplier: number;
private _travel_speed: number;
private _damageTable: ApplyDamageOptions;
OnUpgrade()
{
this._burn_duration = this.GetSpecialValueFor("burn_duration");
this._intellectual_multiplier = this.GetSpecialValueFor("intellectual_multiplier");
this._travel_speed = this.GetSpecialValueFor("travel_speed");
this._damageTable = {
damage: 0,
ability: this,
victim: undefined,
attacker: (this.GetOwnerEntity() as CDOTA_BaseNPC_Hero),
damage_type: this.GetAbilityDamageType()
};
}
private _caster_origin: Vector;
private _parent_origin: Vector;
private _direction: Vector;
private _delay: number = 1.26;
OnSpellStart()
{
const caster = this.GetCaster();
const point = this.GetCursorPosition();
this._caster_origin = caster.GetOrigin();
this._parent_origin = point;
this._direction = this._parent_origin.__sub(this._caster_origin);
this._direction.z = 0;
this._direction = this._direction.Normalized();
this._playEffects1();
const projectile_direction = caster.GetCursorPosition().__sub(caster.GetAbsOrigin()).Normalized();
let _speed = 300;
this.SetThink(()=>{
const projectile_information:CreateLinearProjectileOptions =
{
EffectName : "particles/units/heroes/hero_invoker/invoker_chaos_meteor.vpcf",
Ability : this,
vSpawnOrigin : point,
fDistance : 2000,
Source : caster,
iVisionTeamNumber : caster.GetTeam(),
iUnitTargetTeam : DOTA_UNIT_TARGET_TEAM.DOTA_UNIT_TARGET_TEAM_NONE,
iUnitTargetFlags : DOTA_UNIT_TARGET_FLAGS.DOTA_UNIT_TARGET_FLAG_NONE,
iUnitTargetType : DOTA_UNIT_TARGET_TYPE.DOTA_UNIT_TARGET_NONE ,
fExpireTime : GameRules.GetGameTime() + 10,
vVelocity : Vector(projectile_direction.x * _speed, projectile_direction.y * _speed, 0),
}
const pid = ProjectileManager.CreateLinearProjectile(projectile_information);
const sound_impact = "Hero_Invoker.ChaosMeteor.Impact";
const sound_loop = "Hero_Invoker.ChaosMeteor.Loop";
EmitSoundOnLocationWithCaster( point, sound_impact, this.GetCaster())
EmitSoundOn( sound_loop, this)
this.SetThink(()=>{
this._burn(pid)
return this._checkIn;
}, this,
invoker_chaos_meteor._getCheckContextName(pid),
this._checkIn)
return undefined;
}, this, DoUniqueString("___ST___"), this._delay);
}
private static _getCheckContextName(id:ProjectileID)
{
//这里这样返回是因为需要避免context覆盖,当cd过短在上一个结束前放了下一个,之前的伤害就无法处理
//也需要在Projectile结束时使用这个规则来停止think
return "___check___" + id
}
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 = invoker_chaos_meteor._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 _damageRadius: number = 275;
private _burn(id:ProjectileID)
{
const enemies = FindUnitsInRadius(
this.GetCaster().GetTeamNumber(),
ProjectileManager.GetLinearProjectileLocation(id),
undefined,
this._damageRadius,
this.GetAbilityTargetTeam(),
this.GetAbilityTargetType(),
this.GetAbilityTargetFlags(),
FindOrder.FIND_ANY_ORDER,
false
);
for (let enemy of enemies)
{
this._damageTable.victim = enemy;
this._damageTable.damage = (this.GetOwnerEntity() as CDOTA_BaseNPC_Hero).GetIntellect() * this._intellectual_multiplier
ApplyDamage(this._damageTable);
enemy.AddNewModifier(
this.GetCaster(),
this,
modifier_invoker_chaos_meteor_burn.name,
{"duration": this._burn_duration});
}
}
OnProjectileHitHandle(target: CDOTA_BaseNPC | undefined, location: Vector, projectileHandle: ProjectileID): boolean | void
{
const sound_loop = "Hero_Invoker.ChaosMeteor.Loop";
const sound_stop = "Hero_Invoker.ChaosMeteor.Destroy";
StopSoundOn(sound_loop, this);
EmitSoundOnLocationWithCaster(this.GetOrigin(), sound_stop, this.GetCaster());
this.StopThink(invoker_chaos_meteor._getCheckContextName(projectileHandle));
}
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: BaseAbility, particle_name: string, iAttach: ParticleAttachment_t, hUnit?: CDOTA_BaseNPC, iTeam?: DOTATeam_t)
{
const ability = self;
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;
}
}
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;
}
}
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"
}
}
}
效果