diff --git a/CoreBase/Network/BaseServer.cs b/CoreBase/Network/BaseServer.cs index a748c8d8d4..1641e25e12 100644 --- a/CoreBase/Network/BaseServer.cs +++ b/CoreBase/Network/BaseServer.cs @@ -169,7 +169,7 @@ void FreeBufferPosition() catch (SocketException e) { if (log.IsDebugEnabled) - log.Error($"Socket exception on UDP receive (Code: {e.SocketErrorCode})"); + log.Debug($"Socket exception on UDP receive (Code: {e.SocketErrorCode})"); } catch (Exception e) { diff --git a/CoreDatabase/Tables/DbCoreCharacter.cs b/CoreDatabase/Tables/DbCoreCharacter.cs index e612f657ad..11069a5e76 100644 --- a/CoreDatabase/Tables/DbCoreCharacter.cs +++ b/CoreDatabase/Tables/DbCoreCharacter.cs @@ -63,14 +63,14 @@ public class DbCoreCharacter : DataObject private int m_mithril; private int m_currentModel; - private int m_constitution = 0; - private int m_dexterity = 0; - private int m_strength = 0; - private int m_quickness = 0; - private int m_intelligence = 0; - private int m_piety = 0; - private int m_empathy = 0; - private int m_charisma = 0; + private int m_constitution; + private int m_dexterity; + private int m_strength; + private int m_quickness; + private int m_intelligence; + private int m_piety; + private int m_empathy; + private int m_charisma; //This needs to be uint and ushort! private int m_xpos; @@ -130,19 +130,19 @@ public class DbCoreCharacter : DataObject private bool m_isLevelRespecUsed; private int m_respecBought; // /respec buy private bool m_safetyFlag; - private int m_craftingPrimarySkill = 0; + private int m_craftingPrimarySkill; private bool m_cancelStyle; private bool m_isAnonymous; - private byte m_customisationStep = 1; + private byte m_customisationStep; - private byte m_eyesize = 0; - private byte m_lipsize = 0; - private byte m_eyecolor = 0; - private byte m_hairColor = 0; - private byte m_facetype = 0; - private byte m_hairstyle = 0; - private byte m_moodtype = 0; + private byte m_eyesize; + private byte m_lipsize; + private byte m_eyecolor; + private byte m_hairColor; + private byte m_facetype; + private byte m_hairstyle; + private byte m_moodtype; private bool m_gainXP; private bool m_gainRP; @@ -167,15 +167,15 @@ public class DbCoreCharacter : DataObject private bool m_mlGranted; // Should this player stats be ignored when tabulating statistics? - private bool m_ignoreStatistics = false; + private bool m_ignoreStatistics; // What should the Herald display of this character? - private byte m_notDisplayedInHerald = 0; + private byte m_notDisplayedInHerald; // Should we hide the detailed specialization of this player in the APIs? private bool m_hideSpecializationAPI; - private byte m_activeSaddleBags = 0; + private byte m_activeSaddleBags; private DateTime m_lastLevelUp; @@ -188,46 +188,28 @@ public class DbCoreCharacter : DataObject private bool m_receiveROG; // toggle receiving ROGs for the player private bool m_boosted; // set to true if player has used a free level/rr NPC + // Controls automation + private ushort _automaticBackupStyleId; + /// /// Create the character row in table /// public DbCoreCharacter() - { - m_creationDate = DateTime.Now; - m_concentration = 100; - m_exp = 0; - m_bntyPts = 0; - m_realmPts = 0; - - m_lastPlayed = DateTime.Now; // Prevent /played crash. - m_playedTime = 0; // /played startup - m_deathTime = long.MinValue; - m_respecAmountAllSkill = 0; - m_respecAmountSingleSkill = 0; - m_respecAmountRealmSkill = 0; - m_respecAmountDOL = 0; - m_respecBought = 0; - - m_isLevelRespecUsed = true; - m_safetyFlag = true; - m_craftingPrimarySkill = 0; - m_usedLevelCommand = false; - m_spellQueue = true; - m_gainXP = true; - m_gainRP = true; - m_autoloot = true; - m_showXFireInfo = false; - m_noHelp = false; - m_showGuildLogins = false; - m_roleplay = false; - m_ignoreStatistics = false; + { + m_creationDate = DateTime.Now; + m_concentration = 100; + m_lastPlayed = DateTime.Now; // Prevent /played crash. + m_deathTime = long.MinValue; + m_isLevelRespecUsed = true; + m_customisationStep = 1; + m_safetyFlag = true; + m_spellQueue = true; + m_gainXP = true; + m_gainRP = true; + m_autoloot = true; m_lastLevelUp = DateTime.Now; - m_playedTimeSinceLevel = 0; m_receiveROG = true; - m_hardcore = false; - m_hardcoreCompleted = false; - m_boosted = false; - } + } /// /// Gets/sets if this character has xp in a gravestone @@ -2249,6 +2231,17 @@ public bool isBoosted } } + [DataElement(AllowDbNull = false)] + public ushort AutomaticBackupStyleId + { + get => _automaticBackupStyleId; + set + { + Dirty = true; + _automaticBackupStyleId = value; + } + } + /// /// Do we ignore all statistics for this player? /// diff --git a/GameServer/ECS-Components/Actions/NpcAttackAction.cs b/GameServer/ECS-Components/Actions/NpcAttackAction.cs index 1fb3bf6d35..47e29f2cb9 100644 --- a/GameServer/ECS-Components/Actions/NpcAttackAction.cs +++ b/GameServer/ECS-Components/Actions/NpcAttackAction.cs @@ -91,7 +91,7 @@ protected override bool PrepareMeleeAttack() } } - _combatStyle = StyleComponent.NPCGetStyleToUse(); + _combatStyle = StyleComponent.GetStyleToUse(); if (!base.PrepareMeleeAttack()) return false; diff --git a/GameServer/ECS-Components/NpcStyleComponent.cs b/GameServer/ECS-Components/NpcStyleComponent.cs new file mode 100644 index 0000000000..9221af98c0 --- /dev/null +++ b/GameServer/ECS-Components/NpcStyleComponent.cs @@ -0,0 +1,86 @@ +using DOL.GS.ServerProperties; +using DOL.GS.Styles; + +namespace DOL.GS +{ + public class NpcStyleComponent : StyleComponent + { + private GameNPC _npcOwner; + + public NpcStyleComponent(GameNPC npcOwner) : base(npcOwner) + { + _npcOwner = npcOwner; + } + + public override Style GetStyleToUse() + { + if (_npcOwner.Styles == null || _npcOwner.Styles.Count < 1 || _npcOwner.TargetObject == null) + return null; + + AttackData lastAttackData = _npcOwner.attackComponent.attackAction.LastAttackData; + + // Chain and defensive styles are excluded from the chance roll because they would almost never happen otherwise. + // For example, an NPC blocks 10% of the time, so the default 20% style chance effectively means the defensive + // style would only actually occur during 2% of of a mob's attacks. In comparison, a style chain would only happen + // 0.4% of the time. + if (_npcOwner.StylesChain != null && _npcOwner.StylesChain.Count > 0) + { + foreach (Style style in _npcOwner.StylesChain) + { + if (StyleProcessor.CanUseStyle(lastAttackData, _npcOwner, style, _npcOwner.ActiveWeapon)) + return style; + } + } + + if (_npcOwner.StylesDefensive != null && _npcOwner.StylesDefensive.Count > 0) + { + foreach (Style style in _npcOwner.StylesDefensive) + { + if (StyleProcessor.CanUseStyle(lastAttackData, _npcOwner, style, _npcOwner.ActiveWeapon) && _npcOwner.CheckStyleStun(style)) // Make sure we don't spam stun styles like Brutalize + return style; + } + } + + if (Util.Chance(Properties.GAMENPC_CHANCES_TO_STYLE)) + { + // All of the remaining lists are randomly picked from, + // as this creates more variety with each combat result. + // For example, a mob with both Pincer and Ice Storm + // styles could potentially use one or the other with + // each attack roll that succeeds. + + // First, check positional styles (in order of back, side, front) + // in case the defender is facing another direction + if (_npcOwner.StylesBack != null && _npcOwner.StylesBack.Count > 0) + { + Style style = _npcOwner.StylesBack[Util.Random(0, _npcOwner.StylesBack.Count - 1)]; + + if (StyleProcessor.CanUseStyle(lastAttackData, _npcOwner, style, _npcOwner.ActiveWeapon)) + return style; + } + + if (_npcOwner.StylesSide != null && _npcOwner.StylesSide.Count > 0) + { + Style style = _npcOwner.StylesSide[Util.Random(0, _npcOwner.StylesSide.Count - 1)]; + + if (StyleProcessor.CanUseStyle(lastAttackData, _npcOwner, style, _npcOwner.ActiveWeapon)) + return style; + } + + if (_npcOwner.StylesFront != null && _npcOwner.StylesFront.Count > 0) + { + Style style = _npcOwner.StylesFront[Util.Random(0, _npcOwner.StylesFront.Count - 1)]; + + if (StyleProcessor.CanUseStyle(lastAttackData, _npcOwner, style, _npcOwner.ActiveWeapon)) + return style; + } + + // Pick a random anytime style + if (_npcOwner.StylesAnytime != null && _npcOwner.StylesAnytime.Count > 0) + return _npcOwner.StylesAnytime[Util.Random(0, _npcOwner.StylesAnytime.Count - 1)]; + } + + return null; + } + } +} diff --git a/GameServer/ECS-Components/PlayerStyleComponent.cs b/GameServer/ECS-Components/PlayerStyleComponent.cs new file mode 100644 index 0000000000..cda51306f3 --- /dev/null +++ b/GameServer/ECS-Components/PlayerStyleComponent.cs @@ -0,0 +1,208 @@ +using System.Collections.Generic; +using DOL.Database; +using DOL.GS.PacketHandler; +using DOL.GS.Styles; +using DOL.Language; + +namespace DOL.GS +{ + public class PlayerStyleComponent : StyleComponent + { + private GamePlayer _playerOwner; + private bool _awaitingBackupInput; + private Style _automaticBackupStyle; + + public override bool CancelStyle + { + get => _playerOwner.DBCharacter != null && _playerOwner.DBCharacter.CancelStyle; + set + { + if (_playerOwner.DBCharacter != null) + _playerOwner.DBCharacter.CancelStyle = value; + } + } + + public override bool AwaitingBackupInput + { + get => _awaitingBackupInput; + set => _awaitingBackupInput = value; + } + + public override Style AutomaticBackupStyle + { + get => _automaticBackupStyle; + set => _automaticBackupStyle = value; + } + + public PlayerStyleComponent(GamePlayer playerOwner) : base(playerOwner) + { + _playerOwner = playerOwner; + } + + public void OnPlayerLoadFromDatabase() + { + DbCoreCharacter dbCharacter = _playerOwner.DBCharacter; + + if (dbCharacter == null) + return; + + AutomaticBackupStyle = SkillBase.GetStyleByID(dbCharacter.AutomaticBackupStyleId, _playerOwner.CharacterClass.ID); + } + + public void OnPlayerSaveIntoDatabase() + { + DbCoreCharacter dbCharacter = _playerOwner.DBCharacter; + + if (dbCharacter == null || AutomaticBackupStyle.ID == dbCharacter.AutomaticBackupStyleId) + return; + + dbCharacter.AutomaticBackupStyleId = (ushort) AutomaticBackupStyle.ID; + } + + public override Style GetStyleToUse() + { + if (NextCombatStyle == null) + return null; + + // If the player no longer access to the style. + if (AutomaticBackupStyle != null && _playerOwner.GetBaseSpecLevel(AutomaticBackupStyle.Spec) < AutomaticBackupStyle.SpecLevelRequirement) + { + _playerOwner.Out.SendMessage($"{AutomaticBackupStyle.Name} is no longer a valid backup style for your spec level and has been cleared.", eChatType.CT_System, eChatLoc.CL_SystemWindow); + AutomaticBackupStyle = null; + } + + AttackData lastAttackData = Owner.attackComponent.attackAction.LastAttackData; + DbInventoryItem weapon = NextCombatStyle.WeaponTypeRequirement == (int) eObjectType.Shield ? Owner.Inventory.GetItem(eInventorySlot.LeftHandWeapon) : Owner.ActiveWeapon; + + //determine which style will actually be used + Style styleToUse; + + if (StyleProcessor.CanUseStyle(lastAttackData, Owner, NextCombatStyle, weapon)) + styleToUse = NextCombatStyle; + else if (NextCombatBackupStyle != null) + styleToUse = NextCombatBackupStyle; + else if (AutomaticBackupStyle != null) + { + StyleProcessor.TryToUseStyle(Owner, AutomaticBackupStyle); + styleToUse = NextCombatBackupStyle; // `NextCombatBackupStyle` became `AutomaticBackupStyle` if `TryToUse` succeeded. + } + else + styleToUse = NextCombatStyle; // Not sure why. + + return styleToUse; + } + + public override void DelveWeaponStyle(List delveInfo, Style style) + { + StyleProcessor.DelveWeaponStyle(delveInfo, style, _playerOwner); + } + + public override void AddStyle(Style style, bool notify) + { + lock (lockStyleList) + { + if (m_styles.TryGetValue(style.ID, out Style existingStyle)) + { + existingStyle.Level = style.Level; + return; + } + + m_styles.Add(style.ID, style); + + if (!notify) + return; + + _playerOwner.Out.SendMessage(LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.YouLearn", style.Name), eChatType.CT_System, eChatLoc.CL_SystemWindow); + string message = null; + + if (style.OpeningRequirementType is Style.eOpening.Offensive) + { + switch (style.AttackResultRequirement) + { + case Style.eAttackResultRequirement.Style: + case Style.eAttackResultRequirement.Hit: // TODO: make own message for hit after styles DB is updated + { + Style reqStyle = SkillBase.GetStyleByID(style.OpeningRequirementValue, _playerOwner.CharacterClass.ID); + + if (reqStyle == null) + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterStyle", "(style " + style.OpeningRequirementValue + " not found)"); + else + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterStyle", reqStyle.Name); + + break; + } + case Style.eAttackResultRequirement.Miss: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterMissed"); + break; + } + case Style.eAttackResultRequirement.Parry: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterParried"); + break; + } + case Style.eAttackResultRequirement.Block: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterBlocked"); + break; + } + case Style.eAttackResultRequirement.Evade: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterEvaded"); + break; + } + case Style.eAttackResultRequirement.Fumble: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.AfterFumbles"); + break; + } + } + } + else if (style.OpeningRequirementType is Style.eOpening.Defensive) + { + switch (style.AttackResultRequirement) + { + case Style.eAttackResultRequirement.Miss: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetMisses"); + break; + } + case Style.eAttackResultRequirement.Hit: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetHits"); + break; + } + case Style.eAttackResultRequirement.Parry: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetParried"); + break; + } + case Style.eAttackResultRequirement.Block: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetBlocked"); + break; + } + case Style.eAttackResultRequirement.Evade: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetEvaded"); + break; + } + case Style.eAttackResultRequirement.Fumble: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetFumbles"); + break; + } + case Style.eAttackResultRequirement.Style: + { + message = LanguageMgr.GetTranslation(_playerOwner.Client.Account.Language, "GamePlayer.RefreshSpec.TargetStyle"); + break; + } + } + } + + if (!string.IsNullOrEmpty(message)) + _playerOwner.Out.SendMessage(message, eChatType.CT_System, eChatLoc.CL_SystemWindow); + } + } + } +} diff --git a/GameServer/ECS-Components/StyleComponent.cs b/GameServer/ECS-Components/StyleComponent.cs index 39ce6ab480..4b11a01360 100644 --- a/GameServer/ECS-Components/StyleComponent.cs +++ b/GameServer/ECS-Components/StyleComponent.cs @@ -1,23 +1,46 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using DOL.Database; -using DOL.GS.PacketHandler; -using DOL.GS.ServerProperties; using DOL.GS.Styles; -using DOL.Language; namespace DOL.GS { public class StyleComponent { - private GameLiving _owner; + public GameLiving Owner { get; } - public bool AwaitingBackupInput = false; + public virtual bool CancelStyle + { + get => false; + set { } + } + + public virtual bool AwaitingBackupInput + { + get => false; + set { } + } + + public virtual Style AutomaticBackupStyle + { + get => null; + set { } + } + + protected StyleComponent(GameLiving owner) + { + Owner = owner; + } - public StyleComponent(GameLiving owner) + public static StyleComponent Create(GameLiving owner) { - _owner = owner; + if (owner is GameNPC npcOwner) + return new NpcStyleComponent(npcOwner); + else if (owner is GamePlayer playerOwner) + return new PlayerStyleComponent(playerOwner); + else + return new StyleComponent(owner); } /// @@ -44,45 +67,35 @@ public IList GetStyleList() return list; } - /// - /// Holds the style that this living should use next - /// - protected Style m_nextCombatStyle; - /// - /// Holds the backup style for the style that the living should use next - /// - protected Style m_nextCombatBackupStyle; - /// - /// Holds the time at which the style was set - /// - protected long m_nextCombatStyleTime; - - //if automatic backup styles are enabled, this is the one that will be used - public Style AutomaticBackupStyle { get; set; } + protected Style _nextCombatStyle; + protected Style _nextCombatBackupStyle; + protected long _nextCombatStyleTime; /// /// Gets or Sets the next combat style to use /// public Style NextCombatStyle { - get { return m_nextCombatStyle; } - set { m_nextCombatStyle = value; } + get { return _nextCombatStyle; } + set { _nextCombatStyle = value; } } + /// /// Gets or Sets the next combat backup style to use /// public Style NextCombatBackupStyle { - get { return m_nextCombatBackupStyle; } - set { m_nextCombatBackupStyle = value; } + get { return _nextCombatBackupStyle; } + set { _nextCombatBackupStyle = value; } } + /// /// Gets or Sets the time at which the style was set /// public long NextCombatStyleTime { - get { return m_nextCombatStyleTime; } - set { m_nextCombatStyleTime = value; } + get { return _nextCombatStyleTime; } + set { _nextCombatStyleTime = value; } } /// @@ -90,137 +103,23 @@ public long NextCombatStyleTime /// protected bool m_cancelStyle; - /// - /// Gets or Sets the cancel style flag - /// (delegate to PlayerCharacter) - /// - public bool CancelStyle - { - get => _owner is GamePlayer player && player.DBCharacter != null && player.DBCharacter.CancelStyle; - set - { - if (_owner is GamePlayer player && player.DBCharacter != null) - player.DBCharacter.CancelStyle = value; - } - } - public void ExecuteWeaponStyle(Style style) { - StyleProcessor.TryToUseStyle(_owner, style); + StyleProcessor.TryToUseStyle(Owner, style); } /// /// Decides which style living will use in this moment /// /// Style to use or null if none - public Style GetStyleToUse() + public virtual Style GetStyleToUse() { - if (NextCombatStyle == null) - return null; - - AttackData lastAttackData = _owner.attackComponent.attackAction.LastAttackData; - DbInventoryItem weapon = NextCombatStyle.WeaponTypeRequirement == (int) eObjectType.Shield ? _owner.Inventory.GetItem(eInventorySlot.LeftHandWeapon) : _owner.ActiveWeapon; - - //if they've cached a style and then respecced to no longer have access, remove it - if (AutomaticBackupStyle != null && _owner is GamePlayer player && player.GetBaseSpecLevel(AutomaticBackupStyle.Spec) < AutomaticBackupStyle.SpecLevelRequirement) - { - player.Out.SendMessage($"{AutomaticBackupStyle.Name} is no longer a valid backup style for your spec level and has been cleared.", eChatType.CT_System, eChatLoc.CL_SystemWindow); - AutomaticBackupStyle = null; - } - - //determine which style will actually be used - Style styleToUse; - - if (StyleProcessor.CanUseStyle(lastAttackData, _owner, NextCombatStyle, weapon)) - styleToUse = NextCombatStyle; - else if (NextCombatBackupStyle != null) - styleToUse = NextCombatBackupStyle; - else if (AutomaticBackupStyle != null) - { - StyleProcessor.TryToUseStyle(_owner, AutomaticBackupStyle); - styleToUse = NextCombatBackupStyle; // `NextCombatBackupStyle` became `AutomaticBackupStyle` if `TryToUse` succeeded. - } - else - styleToUse = NextCombatStyle; // Not sure why. - - return styleToUse; - } - - /// - /// Picks a style, prioritizing reactives and chains over positionals and anytimes - /// - /// Selected style - public Style NPCGetStyleToUse() - { - var p = _owner as GameNPC; - if (p.Styles == null || p.Styles.Count < 1 || p.TargetObject == null) - return null; - - AttackData lastAttackData = p.attackComponent.attackAction.LastAttackData; - - // Chain and defensive styles are excluded from the chance roll because they would almost never happen otherwise. - // For example, an NPC blocks 10% of the time, so the default 20% style chance effectively means the defensive - // style would only actually occur during 2% of of a mob's attacks. In comparison, a style chain would only happen - // 0.4% of the time. - if (p.StylesChain != null && p.StylesChain.Count > 0) - foreach (Style s in p.StylesChain) - if (StyleProcessor.CanUseStyle(lastAttackData, p, s, p.ActiveWeapon)) - return s; - - if (p.StylesDefensive != null && p.StylesDefensive.Count > 0) - foreach (Style s in p.StylesDefensive) - if (StyleProcessor.CanUseStyle(lastAttackData, p, s, p.ActiveWeapon) - && p.CheckStyleStun(s)) // Make sure we don't spam stun styles like Brutalize - return s; - - if (Util.Chance(Properties.GAMENPC_CHANCES_TO_STYLE)) - { - // All of the remaining lists are randomly picked from, - // as this creates more variety with each combat result. - // For example, a mob with both Pincer and Ice Storm - // styles could potentially use one or the other with - // each attack roll that succeeds. - - // First, check positional styles (in order of back, side, front) - // in case the defender is facing another direction - if (p.StylesBack != null && p.StylesBack.Count > 0) - { - Style s = p.StylesBack[Util.Random(0, p.StylesBack.Count - 1)]; - if (StyleProcessor.CanUseStyle(lastAttackData, p, s, p.ActiveWeapon)) - return s; - } - - if (p.StylesSide != null && p.StylesSide.Count > 0) - { - Style s = p.StylesSide[Util.Random(0, p.StylesSide.Count - 1)]; - if (StyleProcessor.CanUseStyle(lastAttackData, p, s, p.ActiveWeapon)) - return s; - } - - if (p.StylesFront != null && p.StylesFront.Count > 0) - { - Style s = p.StylesFront[Util.Random(0, p.StylesFront.Count - 1)]; - if (StyleProcessor.CanUseStyle(lastAttackData, p, s, p.ActiveWeapon)) - return s; - } - - // Pick a random anytime style - if (p.StylesAnytime != null && p.StylesAnytime.Count > 0) - return p.StylesAnytime[Util.Random(0, p.StylesAnytime.Count - 1)]; - } - - return null; + throw new NotImplementedException(); } - /// - /// Delve a weapon style for this player - /// - /// - /// - /// - public void DelveWeaponStyle(List delveInfo, Style style) + public virtual void DelveWeaponStyle(List delveInfo, Style style) { - StyleProcessor.DelveWeaponStyle(delveInfo, style, _owner as GamePlayer); + throw new NotImplementedException(); } public void RemoveAllStyles() @@ -231,92 +130,9 @@ public void RemoveAllStyles() } } - public void AddStyle(Style style, bool notify) + public virtual void AddStyle(Style style, bool notify) { - var p = _owner as GamePlayer; - - lock (lockStyleList) - { - if (m_styles.TryGetValue(style.ID, out Style existingStyle)) - { - existingStyle.Level = style.Level; - return; - } - - m_styles.Add(style.ID, style); - - if (!notify) - return; - - - p.Out.SendMessage(LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.YouLearn", style.Name), eChatType.CT_System, eChatLoc.CL_SystemWindow); - - string message = null; - - if (Style.eOpening.Offensive == style.OpeningRequirementType) - { - switch (style.AttackResultRequirement) - { - case Style.eAttackResultRequirement.Style: - case Style.eAttackResultRequirement.Hit: // TODO: make own message for hit after styles DB is updated - - Style reqStyle = SkillBase.GetStyleByID(style.OpeningRequirementValue, p.CharacterClass.ID); - - if (reqStyle == null) - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterStyle", "(style " + style.OpeningRequirementValue + " not found)"); - - else - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterStyle", reqStyle.Name); - - break; - case Style.eAttackResultRequirement.Miss: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterMissed"); - break; - case Style.eAttackResultRequirement.Parry: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterParried"); - break; - case Style.eAttackResultRequirement.Block: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterBlocked"); - break; - case Style.eAttackResultRequirement.Evade: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterEvaded"); - break; - case Style.eAttackResultRequirement.Fumble: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.AfterFumbles"); - break; - } - } - else if (Style.eOpening.Defensive == style.OpeningRequirementType) - { - switch (style.AttackResultRequirement) - { - case Style.eAttackResultRequirement.Miss: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetMisses"); - break; - case Style.eAttackResultRequirement.Hit: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetHits"); - break; - case Style.eAttackResultRequirement.Parry: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetParried"); - break; - case Style.eAttackResultRequirement.Block: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetBlocked"); - break; - case Style.eAttackResultRequirement.Evade: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetEvaded"); - break; - case Style.eAttackResultRequirement.Fumble: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetFumbles"); - break; - case Style.eAttackResultRequirement.Style: - message = LanguageMgr.GetTranslation(p.Client.Account.Language, "GamePlayer.RefreshSpec.TargetStyle"); - break; - } - } - - if (!string.IsNullOrEmpty(message)) - p.Out.SendMessage(message, eChatType.CT_System, eChatLoc.CL_SystemWindow); - } + throw new NotImplementedException(); } } } diff --git a/GameServer/gameobjects/GameLiving.cs b/GameServer/gameobjects/GameLiving.cs index faed6c089a..a2817736a8 100644 --- a/GameServer/gameobjects/GameLiving.cs +++ b/GameServer/gameobjects/GameLiving.cs @@ -4010,7 +4010,7 @@ public GameLiving() : base() { attackComponent = new AttackComponent(this); rangeAttackComponent = new RangeAttackComponent(this); - styleComponent = new StyleComponent(this); + styleComponent = StyleComponent.Create(this); castingComponent = CastingComponent.Create(this); effectListComponent = new EffectListComponent(this); movementComponent = MovementComponent.Create(this); diff --git a/GameServer/gameobjects/GameNPC.cs b/GameServer/gameobjects/GameNPC.cs index a95b5c5534..c46105d23b 100644 --- a/GameServer/gameobjects/GameNPC.cs +++ b/GameServer/gameobjects/GameNPC.cs @@ -4368,6 +4368,7 @@ protected virtual void BroadcastLoot(ArrayList droplist) public override eGender Gender { get; set; } public new NpcMovementComponent movementComponent; + public new NpcStyleComponent styleComponent; public GameNPC Copy() { @@ -4489,8 +4490,8 @@ public GameNPC Copy(GameNPC copyTarget) public GameNPC(ABrain defaultBrain) : base() { - if (movementComponent == null) - movementComponent = base.movementComponent as NpcMovementComponent; + movementComponent ??= base.movementComponent as NpcMovementComponent; + styleComponent ??= base.styleComponent as NpcStyleComponent; Level = 1; m_health = MaxHealth; diff --git a/GameServer/gameobjects/GamePlayer.cs b/GameServer/gameobjects/GamePlayer.cs index b6c4e9bd45..a07b3165a1 100644 --- a/GameServer/gameobjects/GamePlayer.cs +++ b/GameServer/gameobjects/GamePlayer.cs @@ -42,6 +42,7 @@ public class GamePlayer : GameLiving private readonly object m_LockObject = new(); public new PlayerMovementComponent movementComponent; + public new PlayerStyleComponent styleComponent; public override eGameObjectType GameObjectType => eGameObjectType.PLAYER; public ChainedActions ChainedActions { get; } @@ -11417,6 +11418,8 @@ public override void LoadFromDatabase(DataObject obj) // check the account for the Muted flag if (Client.Account.IsMuted) IsMuted = true; + + styleComponent.OnPlayerLoadFromDatabase(); } /// @@ -11510,6 +11513,7 @@ public override void SaveIntoDatabase() DBCharacter.Direction = loc.Heading; } } + styleComponent.OnPlayerSaveIntoDatabase(); GameServer.Database.SaveObject(DBCharacter); Inventory.SaveIntoDatabase(InternalID); @@ -14542,8 +14546,8 @@ private GamePlayer(ICharacterClass charClass) : base() /// The character for this player public GamePlayer(GameClient client, DbCoreCharacter dbChar) : base() { - if (movementComponent == null) - movementComponent = base.movementComponent as PlayerMovementComponent; + movementComponent ??= base.movementComponent as PlayerMovementComponent; + styleComponent ??= base.styleComponent as PlayerStyleComponent; IsJumping = false; m_steed = new WeakRef(null);