// namespace Supercell.Laser.Logic.Battle.Component // { // using Supercell.Laser.Logic.Battle.Objects; // using Supercell.Laser.Logic.Battle.Level; // using Supercell.Laser.Logic.Data; // using Supercell.Laser.Titan.DataStream; // using Supercell.Laser.Titan.Debug; // using Supercell.Laser.Titan.Math; // public class Skill // { // public readonly int SkillId; // public SkillData SkillData => DataTables.Get(DataType.Skill).GetDataByGlobalId(SkillId); // public readonly bool IsUltiSkill; // private readonly int MaxCharge; // private int Charge; // public int X { get; private set; } // public int Y { get; private set; } // public int ActiveTime { get; private set; } // private int TicksActive; // public bool IsRapidSpreadPattern // { // get // { // return SkillData.AttackPattern == 6; // } // } // public readonly int[] ATTACK_PATTERN_TABLE; // public Skill(int globalId, bool isUltiSkill) // { // SkillId = globalId; // int maxChargeCount = SkillData.MaxCharge; // MaxCharge = LogicMath.Max(1000, 1000 * maxChargeCount); // Charge = MaxCharge; // TicksActive = -1; // if (SkillData.AttackPattern == 6) // { // ATTACK_PATTERN_TABLE = new int[4]; // столик // int p = 0; // int sp = -(SkillData.Spread / 6) * 2; // for (int i = 0; i < 4; i++) // { // if (i >= 2) // { // if (i == 2) // { // ATTACK_PATTERN_TABLE[i] = sp; // } // else // { // ATTACK_PATTERN_TABLE[i] = -sp; // } // } // else // { // ATTACK_PATTERN_TABLE[i] = p; // p += SkillData.Spread / 6; // } // } // } // else // { // ATTACK_PATTERN_TABLE = new int[0]; // } // } // public bool IsActive // { // get // { // return TicksActive >= 0 && TicksActive < ActiveTime; // } // } // public bool ShouldEndThisTick // { // get // { // return TicksActive >= ActiveTime; // } // } // public bool HasEnoughCharge() // { // return Charge >= 1000; // } // public void Interrupt() // { // TicksActive = -1; // } // public void Tick() // { // if (Charge < MaxCharge && !IsActive && SkillData.MaxCharge != 0) // { // int rechargeTicks = SkillData.RechargeTime / 50; // if (rechargeTicks <= 0) // { // Charge = MaxCharge; // } // else // { // Charge += 1000 / rechargeTicks; // Charge = LogicMath.Min(MaxCharge, Charge); // } // } // if (IsActive) // { // TicksActive++; // } // } // public void Activate(Character character, int x, int y, TileMap tileMap) // { // if (Charge < 1000 && SkillData.MaxCharge != 0) return; // if (SkillData.MaxCharge != 0) // Charge -= 1000; // if (IsUltiSkill) // { // character.InterruptAllSkills(); // } // X = x; // Y = y; // TicksActive = 0; // if (SkillData.BehaviorType != "Charge") // { // ActiveTime = (SkillData.ActiveTime / 50) - 1; // } // else // { // // ChargeSpeed применяется как: dx = Cos(angle)/100 * ChargeSpeed/80 за тик // // итого скорость = ChargeSpeed / 80 единиц/тик // // тиков = CastingRange / (ChargeSpeed / 80) // int speedPerTick = LogicMath.Max(1, SkillData.ChargeSpeed / 80); // ActiveTime = SkillData.CastingRange / speedPerTick; // } // character.BlockHealthRegen(); // } // public bool ShouldAttackThisTick() // { // if (TicksActive == 0) // { // return SkillData.ExecuteFirstAttackImmediately; // } // int attackIntervalTicks = SkillData.MsBetweenAttacks / 50; // if (attackIntervalTicks <= 0) // { // return true; // } // return (TicksActive % attackIntervalTicks) == 0; // } // public void Encode(BitStream bitStream) // { // bitStream.WritePositiveVIntMax255OftenZero(TicksActive >= 0 && IsActive ? TicksActive+1 : 0); // 0x15a048 // bitStream.WriteBoolean(false); // 0x15a05c // bitStream.WritePositiveVIntMax255OftenZero(0); // 0x15a068 // if (SkillData.MaxCharge >= 1) // { // bitStream.WritePositiveIntMax4095(Charge); // 0x15a09c // } // if (SkillData.SkillCanChange) // { // int instanceId = SkillData.GetInstanceId(); // bitStream.WritePositiveIntMax255(instanceId); // } // } // public int GetSkillRangeAddFromHold(int ticks) // { // if (SkillData.AttackPattern != 13) // return 0; // return LogicMath.Min(ticks / 2, Character.MAX_SKILL_HOLD_TICKS / 2); // } // public int GetMaxCharge() // { // return MaxCharge; // } // public bool HasButton() // { // return true; // } // public bool IsOkToReduceCooldown() // { // return true; // } // public int MaxCooldown() // { // return SkillData.Cooldown; // } // } // } namespace Supercell.Laser.Logic.Battle.Component { using Supercell.Laser.Logic.Battle.Objects; using Supercell.Laser.Logic.Battle.Level; using Supercell.Laser.Logic.Data; using Supercell.Laser.Titan.DataStream; using Supercell.Laser.Titan.Debug; using Supercell.Laser.Titan.Math; using Supercell.Laser.Logic.Home; public class Skill { public readonly int SkillId; public SkillData SkillData => DataTables.Get(DataType.Skill).GetDataByGlobalId(SkillId); public readonly bool IsUltiSkill; private readonly int MaxCharge; public int Charge { get; private set; } public int X { get; private set; } public int Y { get; private set; } public int ActiveTime { get; private set; } public int TicksActive { get; private set; } public int CoolDown { get; set; } public bool IsRapidSpreadPattern { get { return SkillData.AttackPattern == 6; } } public readonly int[] ATTACK_PATTERN_TABLE; public Skill(int globalId, bool isUltiSkill) { SkillId = globalId; IsUltiSkill = isUltiSkill; int maxChargeCount = SkillData.MaxCharge; MaxCharge = LogicMath.Max(1000, 1000 * maxChargeCount); Charge = MaxCharge; TicksActive = -1; CoolDown = 0; if (SkillData.AttackPattern == 6) { ATTACK_PATTERN_TABLE = new int[4]; int p = 0; int sp = -(SkillData.Spread / 6) * 2; for (int i = 0; i < 4; i++) { if (i >= 2) { ATTACK_PATTERN_TABLE[i] = i == 2 ? sp : -sp; } else { ATTACK_PATTERN_TABLE[i] = p; p += SkillData.Spread / 6; } } } else { ATTACK_PATTERN_TABLE = new int[0]; } } public bool IsActive { get { return TicksActive >= 0 && TicksActive < ActiveTime; } } public bool ShouldEndThisTick { get { return TicksActive >= ActiveTime; } } public bool HasEnoughCharge() { return Charge >= 1000; } public void Interrupt() { TicksActive = -1; CoolDown = SkillData.Cooldown; } // Вызывается из Projectile.cs, когда кирка Карла ловится руками public void RestoreCharge() { Charge += 1000; if (Charge > MaxCharge) Charge = MaxCharge; CoolDown = 0; } public void Tick() { if (CoolDown > 0) { CoolDown -= 50; } if (Charge < MaxCharge && !IsActive && SkillData.MaxCharge != 0 && CoolDown <= 0) { int rechargeTicks = SkillData.RechargeTime / 50; if (rechargeTicks <= 0) { // [ИСПРАВЛЕНИЕ] У Карла (Whirlwind) RechargeTime = 0. // Ему патроны АВТОМАТИЧЕСКИ не выдаем! Они вернутся только через кирку. if (SkillData.Name != null && !SkillData.Name.StartsWith("Whirlwind")) { Charge = MaxCharge; } } else { Charge += 1000 / rechargeTicks; Charge = LogicMath.Min(MaxCharge, Charge); } } if (IsActive) { TicksActive++; } } public void Activate(Character character, int x, int y, TileMap tileMap) { if (Charge < 1000 && SkillData.MaxCharge != 0) return; if (CoolDown > 0) return; if (SkillData.MaxCharge != 0) Charge -= 1000; if (IsUltiSkill) { character.InterruptAllSkills(); } X = x; Y = y; TicksActive = 0; CoolDown = SkillData.Cooldown; var player = character.GetPlayer(); if (player != null && player.Home != null && player.Home.IsDev) { CoolDown = 0; } if (SkillData.BehaviorType == "Charge") { int speedPerTick = LogicMath.Max(1, SkillData.ChargeSpeed / 20); int chargeDistance = SkillData.CastingRange * 180; ActiveTime = LogicMath.Max(1, chargeDistance / speedPerTick); } else if (SkillData.BehaviorType == "Ww") { ActiveTime = SkillData.ActiveTime / 50; } else { int castTicks = SkillData.CastingTime / 50; ActiveTime = (SkillData.ActiveTime / 50) + castTicks; if (ActiveTime <= 0) ActiveTime = 1; } character.BlockHealthRegen(); } public bool ShouldAttackThisTick() { int castTicks = SkillData.CastingTime / 50; if (TicksActive < castTicks) { return false; } int adjustedTicks = TicksActive - castTicks; if (adjustedTicks == 0) { return SkillData.ExecuteFirstAttackImmediately; } int attackIntervalTicks = SkillData.MsBetweenAttacks / 50; if (attackIntervalTicks <= 0) { return false; } return (adjustedTicks % attackIntervalTicks) == 0; } public void Encode(BitStream bitStream) { bitStream.WritePositiveVIntMax255OftenZero(TicksActive >= 0 && IsActive ? TicksActive + 1 : 0); bitStream.WriteBoolean(false); bitStream.WritePositiveVIntMax255OftenZero(LogicMath.Max(0, CoolDown / 50)); if (SkillData.MaxCharge >= 1) { bitStream.WritePositiveIntMax4095(Charge); } if (SkillData.SkillCanChange) { int instanceId = SkillData.GetInstanceId(); bitStream.WritePositiveIntMax255(instanceId); } } public int GetSkillRangeAddFromHold(int ticks) { if (SkillData.AttackPattern != 13) return 0; return LogicMath.Min(ticks / 2, Character.MAX_SKILL_HOLD_TICKS / 2); } public int GetMaxCharge() { return MaxCharge; } public bool HasButton() { return true; } public bool IsOkToReduceCooldown() { return true; } public int MaxCooldown() { return SkillData.Cooldown; } } }