490 lines
No EOL
15 KiB
C#
490 lines
No EOL
15 KiB
C#
// WingsEmu
|
|
//
|
|
// Developed by NosWings Team
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using PhoenixLib.Events;
|
|
using WingsEmu.Core.Generics;
|
|
using WingsEmu.DTOs.BCards;
|
|
using WingsEmu.DTOs.Skills;
|
|
using WingsEmu.Game._enum;
|
|
using WingsEmu.Game.Algorithm;
|
|
using WingsEmu.Game.Battle;
|
|
using WingsEmu.Game.Battle.Managers;
|
|
using WingsEmu.Game.Buffs;
|
|
using WingsEmu.Game.Characters;
|
|
using WingsEmu.Game.Entities;
|
|
using WingsEmu.Game.EntityStatistics;
|
|
using WingsEmu.Game.Extensions;
|
|
using WingsEmu.Game.Helpers.Damages;
|
|
using WingsEmu.Game.Items;
|
|
using WingsEmu.Game.Managers.StaticData;
|
|
using WingsEmu.Game.Maps;
|
|
using WingsEmu.Game.Npcs;
|
|
using WingsEmu.Game.Revival;
|
|
using WingsEmu.Game.Skills;
|
|
using WingsEmu.Game.Triggers;
|
|
using WingsEmu.Packets.Enums;
|
|
|
|
namespace WingsEmu.Game.Mates;
|
|
|
|
public partial class MateEntity : IMateEntity
|
|
{
|
|
private readonly IBattleEntityAlgorithmService _algorithm;
|
|
private readonly ICastingComponent _castingComponent;
|
|
private readonly IEndBuffDamageComponent _endBuffDamageComponent;
|
|
private readonly IAsyncEventPipeline _eventPipeline;
|
|
private readonly IEventTriggerContainer _eventTriggerContainer;
|
|
private readonly IReadOnlyList<NpcMonsterSkillDTO> _npcMonsterSkills;
|
|
|
|
public MateEntity(IPlayerEntity owner, MonsterData npcMonster, byte level, MateType mateType,
|
|
IMateTransportFactory transportFactory, IAsyncEventPipeline eventPipeline, IBattleEntityAlgorithmService algorithm,
|
|
IRandomGenerator randomGenerator)
|
|
{
|
|
Owner = owner;
|
|
NpcMonsterVNum = npcMonster.Id;
|
|
Level = level;
|
|
MateName = npcMonster.Name;
|
|
MateType = mateType;
|
|
Loyalty = 1000;
|
|
CharacterId = owner.Id;
|
|
Id = transportFactory.GenerateTransportId();
|
|
|
|
#region NpcMonsterData
|
|
|
|
ArmorLevel = npcMonster.ArmorLevel;
|
|
AttackType = npcMonster.AttackType;
|
|
AttackUpgrade = npcMonster.AttackUpgrade;
|
|
AttackEffect = npcMonster.AttackEffect;
|
|
BasicCastTime = npcMonster.BasicCastTime;
|
|
BasicCooldown = npcMonster.BasicCooldown;
|
|
BasicRange = npcMonster.BasicRange;
|
|
BCards = npcMonster.BCards;
|
|
BasicHitChance = npcMonster.BasicHitChance;
|
|
CellSize = npcMonster.CellSize;
|
|
CleanDamageMin = npcMonster.CleanDamageMin;
|
|
CleanDamageMax = npcMonster.CleanDamageMax;
|
|
CleanHitRate = npcMonster.CleanHitRate;
|
|
CleanMeleeDefence = npcMonster.CleanMeleeDefence;
|
|
CleanRangeDefence = npcMonster.CleanRangeDefence;
|
|
CleanMagicDefence = npcMonster.CleanMagicDefence;
|
|
CleanDodge = npcMonster.CleanDodge;
|
|
CleanHp = npcMonster.CleanHp;
|
|
CleanMp = npcMonster.CleanMp;
|
|
BaseCloseDefence = npcMonster.BaseCloseDefence;
|
|
BaseConcentrate = npcMonster.BaseConcentrate;
|
|
BaseCriticalChance = npcMonster.BaseCriticalChance;
|
|
BaseCriticalRate = npcMonster.BaseCriticalRate;
|
|
BaseDamageMaximum = npcMonster.BaseDamageMaximum;
|
|
BaseDamageMinimum = npcMonster.BaseDamageMinimum;
|
|
BaseDarkResistance = npcMonster.BaseDarkResistance;
|
|
BaseMaxHp = npcMonster.BaseMaxHp;
|
|
BaseMaxMp = npcMonster.BaseMaxMp;
|
|
DefenceDodge = npcMonster.DefenceDodge;
|
|
DefenceUpgrade = npcMonster.DefenceUpgrade;
|
|
DistanceDefence = npcMonster.DistanceDefence;
|
|
DistanceDefenceDodge = npcMonster.DistanceDefenceDodge;
|
|
BaseElement = npcMonster.BaseElement;
|
|
BaseElementRate = npcMonster.BaseElementRate;
|
|
BaseFireResistance = npcMonster.BaseFireResistance;
|
|
BaseLevel = npcMonster.BaseLevel;
|
|
BaseLightResistance = npcMonster.BaseLightResistance;
|
|
MagicDefence = npcMonster.MagicDefence;
|
|
MagicMpFactor = npcMonster.MagicMpFactor;
|
|
MeleeHpFactor = npcMonster.MeleeHpFactor;
|
|
MinimumAttackRange = npcMonster.MinimumAttackRange;
|
|
MonsterVNum = npcMonster.MonsterVNum;
|
|
MateName = npcMonster.Name;
|
|
MonsterRaceType = npcMonster.MonsterRaceType;
|
|
MonsterRaceSubType = npcMonster.MonsterRaceSubType;
|
|
RangeDodgeFactor = npcMonster.RangeDodgeFactor;
|
|
_speed = npcMonster.BaseSpeed;
|
|
BaseWaterResistance = npcMonster.BaseWaterResistance;
|
|
WeaponLevel = npcMonster.WeaponLevel;
|
|
WinfoValue = npcMonster.WinfoValue;
|
|
Name = npcMonster.Name;
|
|
MonsterSkills = npcMonster.MonsterSkills;
|
|
BaseXp = npcMonster.BaseXp;
|
|
BaseJobXp = npcMonster.BaseJobXp;
|
|
_npcMonsterSkills = npcMonster.Skills;
|
|
|
|
#endregion
|
|
|
|
_eventPipeline = eventPipeline;
|
|
_algorithm = algorithm;
|
|
_revivalComponent = new MateRevivalComponent();
|
|
BCardComponent = new BCardComponent(randomGenerator);
|
|
BuffComponent = new BuffComponent();
|
|
_eventTriggerContainer = new EventTriggerContainer(_eventPipeline);
|
|
_castingComponent = new CastingComponent();
|
|
_endBuffDamageComponent = new EndBuffDamageComponent();
|
|
StatisticsComponent = new MateStatisticsComponent(this);
|
|
ChargeComponent = new ChargeComponent();
|
|
}
|
|
|
|
private IMateRevivalComponent _revivalComponent { get; }
|
|
|
|
public bool TeleportBackOnNoticeRangeExceed { get; }
|
|
|
|
public DateTime LastSpeedChange { get; set; }
|
|
|
|
public byte Size { get; set; } = 10;
|
|
|
|
public byte Element
|
|
{
|
|
get => IsUsingSp && Specialist != null ? Specialist.GameItem.Element : BaseElement;
|
|
set { }
|
|
}
|
|
|
|
public int ElementRate
|
|
{
|
|
get => IsUsingSp && Specialist != null ? Specialist.GameItem.ElementRate : BaseElementRate;
|
|
set { }
|
|
}
|
|
|
|
public int FireResistance
|
|
{
|
|
get => GetMoreStats(StatisticType.FIRE, BaseFireResistance);
|
|
set { }
|
|
}
|
|
|
|
public int WaterResistance
|
|
{
|
|
get => GetMoreStats(StatisticType.WATER, BaseWaterResistance);
|
|
set { }
|
|
}
|
|
|
|
public int LightResistance
|
|
{
|
|
get => GetMoreStats(StatisticType.LIGHT, BaseLightResistance);
|
|
set { }
|
|
}
|
|
|
|
public int DarkResistance
|
|
{
|
|
get => GetMoreStats(StatisticType.DARK, BaseDarkResistance);
|
|
set { }
|
|
}
|
|
|
|
public int DamagesMinimum
|
|
{
|
|
get => GetMateDamage(_damageMin, true);
|
|
set { }
|
|
}
|
|
|
|
public int DamagesMaximum
|
|
{
|
|
get => GetMateDamage(_damageMax, false);
|
|
set { }
|
|
}
|
|
|
|
public short CloseDefence
|
|
{
|
|
get => GetMoreStats(StatisticType.DEFENSE_MELEE, _meleeDefense);
|
|
set { }
|
|
}
|
|
|
|
public short DistanceDefence
|
|
{
|
|
get => GetMoreStats(StatisticType.DEFENSE_RANGED, _rangedDefense);
|
|
set { }
|
|
}
|
|
|
|
public short MagicDefence
|
|
{
|
|
get => GetMoreStats(StatisticType.DEFENSE_MAGIC, _magicDefense);
|
|
set { }
|
|
}
|
|
|
|
public int BaseXp { get; }
|
|
public int BaseJobXp { get; }
|
|
|
|
public short DefenceDodge
|
|
{
|
|
get => GetMoreStats(StatisticType.DODGE_MELEE, _meleeDodge);
|
|
set { }
|
|
}
|
|
|
|
public short DistanceDodge
|
|
{
|
|
get => GetMoreStats(StatisticType.DODGE_RANGED, _rangedDodge);
|
|
set { }
|
|
}
|
|
|
|
public short HitRate
|
|
{
|
|
get => (short)GetMateHitRate(_hitRate);
|
|
set { }
|
|
}
|
|
|
|
public int HitCriticalChance
|
|
{
|
|
get => GetMateCritical(_criticalChance, true);
|
|
set { }
|
|
}
|
|
|
|
public int HitCriticalDamage
|
|
{
|
|
get => GetMateCritical(_criticalDamage, false);
|
|
set { }
|
|
}
|
|
|
|
public int MaxHp
|
|
{
|
|
get => this.GetMaxHp(_maxHp);
|
|
|
|
set => _maxHp = value;
|
|
}
|
|
|
|
public int MaxMp
|
|
{
|
|
get => this.GetMaxMp(_maxMp);
|
|
|
|
set => _maxMp = value;
|
|
}
|
|
|
|
public byte Speed
|
|
{
|
|
get => this.GetSpeed(_speed);
|
|
|
|
set
|
|
{
|
|
LastSpeedChange = DateTime.UtcNow;
|
|
_speed = value > 59 ? (byte)59 : value;
|
|
}
|
|
}
|
|
|
|
public IBCardComponent BCardComponent { get; }
|
|
public IChargeComponent ChargeComponent { get; }
|
|
public ThreadSafeHashSet<Guid> AggroedEntities { get; } = new();
|
|
public IBuffComponent BuffComponent { get; }
|
|
|
|
public FactionType Faction => Owner?.Faction ?? FactionType.Neutral;
|
|
|
|
public bool IsSitting { get; set; }
|
|
|
|
public bool IsUsingSp { get; set; }
|
|
|
|
public short MinilandY { get; set; }
|
|
|
|
public DateTime LastHealth { get; set; }
|
|
|
|
public DateTime LastDeath { get; set; }
|
|
|
|
public DateTime LastDefence { get; set; }
|
|
|
|
public DateTime LastBasicSkill { get; set; }
|
|
|
|
public DateTime? SpawnMateByGuardian { get; set; }
|
|
|
|
public DateTime LastSkillUse { get; set; }
|
|
|
|
public IMapInstance MapInstance => IsTeamMember ? Owner?.MapInstance : Owner?.Miniland;
|
|
|
|
public IPlayerEntity Owner { get; set; }
|
|
|
|
public byte PetSlot { get; set; }
|
|
|
|
public List<IBattleEntitySkill> Skills { get; set; }
|
|
|
|
public Position Position
|
|
{
|
|
get => new(PositionX, PositionY);
|
|
set
|
|
{
|
|
PositionX = value.X;
|
|
PositionY = value.Y;
|
|
}
|
|
}
|
|
|
|
public short PositionX { get; set; }
|
|
|
|
public short PositionY { get; set; }
|
|
|
|
public IBattleEntitySkill LastUsedPartnerSkill { get; set; }
|
|
|
|
public IBattleEntity Killer { get; set; }
|
|
|
|
public bool IsLimited { get; init; }
|
|
public DateTime? SpCooldownEnd { get; set; }
|
|
public DateTime LastEffect { get; set; }
|
|
public DateTime LastPetUpgradeEffect { get; set; }
|
|
public GameItemInstance Weapon => Owner?.PartnerGetEquippedItem(EquipmentType.MainWeapon, PetSlot)?.ItemInstance;
|
|
public GameItemInstance Armor => Owner?.PartnerGetEquippedItem(EquipmentType.Armor, PetSlot)?.ItemInstance;
|
|
public GameItemInstance Gloves => Owner?.PartnerGetEquippedItem(EquipmentType.Gloves, PetSlot)?.ItemInstance;
|
|
public GameItemInstance Boots => Owner?.PartnerGetEquippedItem(EquipmentType.Boots, PetSlot)?.ItemInstance;
|
|
public GameItemInstance Specialist => Owner?.PartnerGetEquippedItem(EquipmentType.Sp, PetSlot)?.ItemInstance;
|
|
|
|
public void Initialize()
|
|
{
|
|
RefreshStatistics();
|
|
|
|
if (Hp == 0)
|
|
{
|
|
Hp = _maxHp;
|
|
Mp = _maxMp;
|
|
}
|
|
|
|
Skills = new List<IBattleEntitySkill>();
|
|
foreach (INpcMonsterSkill skill in MonsterSkills)
|
|
{
|
|
var tmp = new NpcMonsterSkill(skill.Skill, skill.Rate, skill.IsBasicAttack, skill.IsIgnoringHitChance);
|
|
Skills.Add(tmp);
|
|
}
|
|
|
|
foreach (NpcMonsterSkillDTO skill in _npcMonsterSkills)
|
|
{
|
|
var monsterSkill = new NpcMonsterSkill(StaticSkillsManager.Instance.GetSkill(skill.SkillVNum), skill.Rate, skill.IsBasicAttack, skill.IsIgnoringHitChance);
|
|
Skills.Add(monsterSkill);
|
|
}
|
|
|
|
|
|
this.InitializeBCards();
|
|
Killer = null;
|
|
LastBasicSkill = DateTime.MinValue;
|
|
|
|
BasicSkill = new SkillInfo
|
|
{
|
|
Vnum = 0,
|
|
AttackType = AttackType,
|
|
SkillType = SkillType.PartnerSkill,
|
|
CastTime = BasicCastTime,
|
|
Cooldown = BasicCooldown,
|
|
Element = Element,
|
|
HitAnimation = 11,
|
|
HitEffect = AttackEffect,
|
|
HitChance = BasicHitChance,
|
|
TargetType = TargetType.Target,
|
|
TargetAffectedEntities = TargetAffectedEntities.Enemies,
|
|
Range = BasicRange
|
|
};
|
|
|
|
var dictionary = new Dictionary<SkillCastType, HashSet<BCardDTO>>();
|
|
foreach (BCardDTO bCard in BCardComponent.GetTriggerBCards(BCardTriggerType.ATTACK))
|
|
{
|
|
var key = (SkillCastType)bCard.CastType;
|
|
if (!dictionary.TryGetValue(key, out HashSet<BCardDTO> hashSet))
|
|
{
|
|
hashSet = new HashSet<BCardDTO>();
|
|
dictionary[key] = hashSet;
|
|
}
|
|
|
|
hashSet.Add(bCard);
|
|
}
|
|
|
|
BasicSkill.BCardsType = dictionary;
|
|
}
|
|
|
|
public SkillInfo BasicSkill { get; private set; }
|
|
|
|
public IMateStatisticsComponent StatisticsComponent { get; }
|
|
|
|
public DateTime LastLowLoyaltyEffect { get; set; } = DateTime.MinValue;
|
|
|
|
public DateTime LastLoyaltyRecover { get; set; } = DateTime.MinValue;
|
|
|
|
public VisualType Type => VisualType.Npc;
|
|
|
|
public int Id { get; }
|
|
|
|
public void AddEvent(string key, IAsyncEvent notification, bool removedOnTrigger = false) => _eventTriggerContainer.AddEvent(key, notification, removedOnTrigger);
|
|
|
|
public async Task TriggerEvents(string key) => await _eventTriggerContainer.TriggerEvents(key);
|
|
|
|
public async Task EmitEventAsync<T>(T eventArgs) where T : IBattleEntityEvent
|
|
{
|
|
if (eventArgs.Entity != this)
|
|
{
|
|
throw new ArgumentException("An event should be emitted only from the event sender");
|
|
}
|
|
|
|
await _eventPipeline.ProcessEventAsync(eventArgs);
|
|
}
|
|
|
|
public void EmitEvent<T>(T eventArgs) where T : IBattleEntityEvent
|
|
{
|
|
EmitEventAsync(eventArgs).ConfigureAwait(false).GetAwaiter().GetResult();
|
|
}
|
|
|
|
public byte Attack { get; set; }
|
|
|
|
public bool CanPickUp { get; set; }
|
|
|
|
public long CharacterId { get; set; }
|
|
|
|
public byte Defence { get; set; }
|
|
|
|
public byte Direction { get; set; } = 2;
|
|
|
|
public long Experience { get; set; }
|
|
|
|
public int Hp { get; set; }
|
|
|
|
public bool IsSummonable { get; set; }
|
|
|
|
public bool IsTeamMember { get; set; }
|
|
|
|
public byte Level { get; set; }
|
|
|
|
public short Loyalty { get; set; }
|
|
|
|
public short MapX { get; set; }
|
|
|
|
public short MapY { get; set; }
|
|
|
|
public short MinilandX { get; set; }
|
|
|
|
public MateType MateType { get; set; }
|
|
|
|
public int Mp { get; set; }
|
|
|
|
public string MateName { get; set; }
|
|
|
|
public int NpcMonsterVNum { get; set; }
|
|
|
|
public short Skin { get; set; }
|
|
|
|
public DateTime RevivalDateTimeForExecution => _revivalComponent.RevivalDateTimeForExecution;
|
|
|
|
public bool IsRevivalDelayed => _revivalComponent.IsRevivalDelayed;
|
|
|
|
public void UpdateRevival(DateTime revivalDateTimeForExecution, bool isRevivalDelayed)
|
|
{
|
|
_revivalComponent.UpdateRevival(revivalDateTimeForExecution, isRevivalDelayed);
|
|
}
|
|
|
|
public void DisableRevival()
|
|
{
|
|
_revivalComponent.DisableRevival();
|
|
}
|
|
|
|
public SkillCast SkillCast => _castingComponent.SkillCast;
|
|
|
|
public bool IsCastingSkill => _castingComponent.IsCastingSkill;
|
|
|
|
public void SetCastingSkill(SkillInfo skill, DateTime time)
|
|
{
|
|
_castingComponent.SetCastingSkill(skill, time);
|
|
}
|
|
|
|
public void RemoveCastingSkill()
|
|
{
|
|
_castingComponent.RemoveCastingSkill();
|
|
}
|
|
|
|
public IReadOnlyDictionary<short, int> EndBuffDamages => _endBuffDamageComponent.EndBuffDamages;
|
|
|
|
public void AddEndBuff(short buffVnum, int damage)
|
|
{
|
|
_endBuffDamageComponent.AddEndBuff(buffVnum, damage);
|
|
}
|
|
|
|
public int DecreaseDamageEndBuff(short buffVnum, int damage) => _endBuffDamageComponent.DecreaseDamageEndBuff(buffVnum, damage);
|
|
|
|
public void RemoveEndBuffDamage(short buffVnum)
|
|
{
|
|
_endBuffDamageComponent.RemoveEndBuffDamage(buffVnum);
|
|
}
|
|
} |