server-master/srcs/WingsAPI.Game/Mates/MateEntity.cs
2026-02-10 18:21:30 +01:00

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);
}
}