diff --git a/Source/ACE.Server/WorldObjects/AttributeTransferDevice.cs b/Source/ACE.Server/WorldObjects/AttributeTransferDevice.cs index f339f279dd..7ca378ca9f 100644 --- a/Source/ACE.Server/WorldObjects/AttributeTransferDevice.cs +++ b/Source/ACE.Server/WorldObjects/AttributeTransferDevice.cs @@ -6,6 +6,7 @@ using ACE.Entity.Models; using ACE.Server.Entity; using ACE.Server.Network.GameMessages.Messages; +using ACE.Server.WorldObjects.Entity; namespace ACE.Server.WorldObjects { @@ -54,25 +55,23 @@ public void ActOnUse(WorldObject activator, bool confirmed) if (player == null) return; if (TransferFromAttribute == PropertyAttribute.Undef || TransferToAttribute == PropertyAttribute.Undef) + { + player.SendWeenieError(WeenieError.YouHaveFailedToAlterAttributes); return; + } var device = player.FindObject(Guid.Full, Player.SearchLocations.MyInventory); - if (device == null) return; - - var fromAttr = player.Attributes[TransferFromAttribute]; - var toAttr = player.Attributes[TransferToAttribute]; - - if (fromAttr.StartingValue <= 10) + if (device == null) { - player.Session.Network.EnqueueSend(new GameMessageSystemChat($"Your innate {TransferFromAttribute} must be above 10 to use the {Name}.", ChatMessageType.Broadcast)); + player.SendWeenieError(WeenieError.YouHaveFailedToAlterAttributes); return; } - if (toAttr.StartingValue >= 100) - { - player.Session.Network.EnqueueSend(new GameMessageSystemChat($"Your innate {TransferToAttribute} must be below 100 to use the {Name}.", ChatMessageType.Broadcast)); + var fromAttr = player.Attributes[TransferFromAttribute]; + var toAttr = player.Attributes[TransferToAttribute]; + + if (!VerifyRequirements(player, fromAttr, toAttr)) return; - } if (!confirmed) { @@ -92,16 +91,105 @@ public void ActOnUse(WorldObject activator, bool confirmed) var updateFrom = new GameMessagePrivateUpdateAttribute(player, fromAttr); var updateTo = new GameMessagePrivateUpdateAttribute(player, toAttr); - var msgFrom = new GameMessageSystemChat($"Your base {TransferFromAttribute} is now {fromAttr.Base}!", ChatMessageType.Broadcast); - var msgTo = new GameMessageSystemChat($"Your base {TransferToAttribute} is now {toAttr.Base}!", ChatMessageType.Broadcast); + //// begin things not seen in pcaps? + //var msgFrom = new GameMessageSystemChat($"Your base {TransferFromAttribute} is now {fromAttr.Base}!", ChatMessageType.Broadcast); + //var msgTo = new GameMessageSystemChat($"Your base {TransferToAttribute} is now {toAttr.Base}!", ChatMessageType.Broadcast); + + //var sound = new GameMessageSound(player.Guid, Sound.RaiseTrait); + //// end things not seen in pcaps? + //// the above provides better feedback to player but it wasn't seen in pcaps... - var sound = new GameMessageSound(player.Guid, Sound.RaiseTrait); + //player.Session.Network.EnqueueSend(updateFrom, updateTo, msgFrom, msgTo, sound); + player.Session.Network.EnqueueSend(updateFrom, updateTo); - player.Session.Network.EnqueueSend(updateFrom, updateTo, msgFrom, msgTo, sound); + // this should be a UseDone(WeenieError.YouHaveSucceededTransferringAttributes) but Player.TryUseItem has built in UseDone(WeenieError.None) which would conflict. + player.SendWeenieError(WeenieError.YouHaveSucceededTransferringAttributes); player.SaveBiotaToDatabase(); player.TryConsumeFromInventoryWithNetworking(this, 1); } + + public bool VerifyRequirements(Player player, CreatureAttribute transferFromAttribute, CreatureAttribute transferToAttribute) + { + // Check for equipped items that have requirements in skill or attributes + if (CheckWieldedItems(player)) + { + // Items are wielded which might be affected by a transfer operation + //player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.CannotLowerSkillWhileWieldingItem, skill.Skill.ToSentence())); + player.SendWeenieError(WeenieError.CannotTransferAttributesWhileWieldingItem); + return false; + } + + if (transferFromAttribute.StartingValue <= 10) + { + //player.Session.Network.EnqueueSend(new GameMessageSystemChat($"Your innate {TransferFromAttribute} must be above 10 to use the {Name}.", ChatMessageType.Broadcast)); + player.SendWeenieErrorWithString(WeenieErrorWithString.AttributeTransferFromTooLow, $"Your innate level of {TransferFromAttribute} is already as low as it can be. You may not reduce it any further."); + return false; + } + + if (transferToAttribute.StartingValue >= 100) + { + //player.Session.Network.EnqueueSend(new GameMessageSystemChat($"Your innate {TransferToAttribute} must be below 100 to use the {Name}.", ChatMessageType.Broadcast)); + player.SendWeenieErrorWithString(WeenieErrorWithString.AttributeTransferToTooHigh, $"Your innate level of {TransferFromAttribute} is already as high as it can be. You may not increase it any further."); + return false; + } + + return true; + } + + /// + /// Checks wielded items and their requirements to see if they'd be violated by an impending attribute transfer operation + /// + private bool CheckWieldedItems(Player player) + { + foreach (var equippedItem in player.EquippedObjects.Values) + { + if (CheckWieldRequirement(player, equippedItem.WieldRequirements, equippedItem.WieldSkillType, equippedItem.WieldDifficulty) || + CheckWieldRequirement(player, equippedItem.WieldRequirements2, equippedItem.WieldSkillType2, equippedItem.WieldDifficulty2) || + CheckWieldRequirement(player, equippedItem.WieldRequirements3, equippedItem.WieldSkillType3, equippedItem.WieldDifficulty3) || + CheckWieldRequirement(player, equippedItem.WieldRequirements4, equippedItem.WieldSkillType4, equippedItem.WieldDifficulty4)) //|| + //CheckActivationRequirements(player, equippedItem)) + { + return true; + } + } + return false; + } + + private bool CheckWieldRequirement(Player player, WieldRequirement itemWieldReq, int? wieldSkillType, int? wieldSkillDifficulty) + { + //if (itemWieldReq == WieldRequirement.RawSkill || itemWieldReq == WieldRequirement.Skill + // || itemWieldReq == WieldRequirement.RawAttrib || itemWieldReq == WieldRequirement.Attrib + // || itemWieldReq == WieldRequirement.RawSecondaryAttrib || itemWieldReq == WieldRequirement.SecondaryAttrib + // || itemWieldReq == WieldRequirement.Training) + //if ((itemWieldReq >= WieldRequirement.Skill && itemWieldReq <= WieldRequirement.RawSecondaryAttrib) || itemWieldReq == WieldRequirement.Training) + if (itemWieldReq == WieldRequirement.RawAttrib || itemWieldReq == WieldRequirement.Attrib) + { + return true; + } + + return false; + } + + private bool CheckActivationRequirements(Player player, WorldObject equippedItem) + { + if (equippedItem.ItemDifficulty > 0) + return true; + + if (player.ConvertToMoASkill(equippedItem.ItemSkillLimit ?? 0) != Skill.None) + return true; + + if (player.ConvertToMoASkill(equippedItem.ItemSpecializedOnly ?? 0) != Skill.None) + return true; + + if (player.ConvertToMoASkill((Skill)(equippedItem.UseRequiresSkill ?? 0)) != Skill.None) + return true; + + if (player.ConvertToMoASkill((Skill)(equippedItem.UseRequiresSkillSpec ?? 0)) != Skill.None) + return true; + + return false; + } } } diff --git a/Source/ACE.Server/WorldObjects/SkillAlterationDevice.cs b/Source/ACE.Server/WorldObjects/SkillAlterationDevice.cs index 1bd4d8ac1c..84c8d46932 100644 --- a/Source/ACE.Server/WorldObjects/SkillAlterationDevice.cs +++ b/Source/ACE.Server/WorldObjects/SkillAlterationDevice.cs @@ -280,10 +280,11 @@ private bool CheckWieldedItems(Player player) { foreach (var equippedItem in player.EquippedObjects.Values) { - if (CheckWieldRequirement(player, equippedItem.WieldRequirements, equippedItem.WieldSkillType) || - CheckWieldRequirement(player, equippedItem.WieldRequirements2, equippedItem.WieldSkillType2) || - CheckWieldRequirement(player, equippedItem.WieldRequirements3, equippedItem.WieldSkillType3) || - CheckWieldRequirement(player, equippedItem.WieldRequirements4, equippedItem.WieldSkillType4)) + if (CheckWieldRequirement(player, equippedItem.WieldRequirements, equippedItem.WieldSkillType, equippedItem.WieldDifficulty) || + CheckWieldRequirement(player, equippedItem.WieldRequirements2, equippedItem.WieldSkillType2, equippedItem.WieldDifficulty2) || + CheckWieldRequirement(player, equippedItem.WieldRequirements3, equippedItem.WieldSkillType3, equippedItem.WieldDifficulty3) || + CheckWieldRequirement(player, equippedItem.WieldRequirements4, equippedItem.WieldSkillType4, equippedItem.WieldDifficulty4)) //|| + //CheckActivationRequirements(player, equippedItem)) { return true; } @@ -291,12 +292,46 @@ private bool CheckWieldedItems(Player player) return false; } - private bool CheckWieldRequirement(Player player, WieldRequirement itemWieldReq, int? wieldSkillType) + private bool CheckWieldRequirement(Player player, WieldRequirement itemWieldReq, int? wieldSkillType, int? wieldSkillDifficulty) { + if (itemWieldReq == WieldRequirement.Training) + { + var skill = player.ConvertToMoASkill((Skill)(wieldSkillType ?? 0)); + if (skill != SkillToBeAltered) + return false; + + var creatureSkill = player.GetCreatureSkill(skill, false); + + if (creatureSkill == null || wieldSkillDifficulty is null) + return false; + + return (SkillAdvancementClass)wieldSkillDifficulty >= creatureSkill.AdvancementClass; + } + if (itemWieldReq != WieldRequirement.RawSkill && itemWieldReq != WieldRequirement.Skill) return false; return player.ConvertToMoASkill((Skill)(wieldSkillType ?? 0)) == SkillToBeAltered; } + + private bool CheckActivationRequirements(Player player, WorldObject equippedItem) + { + if (equippedItem.ItemDifficulty > 0 && SkillToBeAltered == Skill.ArcaneLore) + return true; + + if (player.ConvertToMoASkill(equippedItem.ItemSkillLimit ?? 0) == SkillToBeAltered) + return true; + + if (player.ConvertToMoASkill(equippedItem.ItemSpecializedOnly ?? 0) == SkillToBeAltered) + return true; + + if (player.ConvertToMoASkill((Skill)(equippedItem.UseRequiresSkill ?? 0)) == SkillToBeAltered) + return true; + + if (player.ConvertToMoASkill((Skill)(equippedItem.UseRequiresSkillSpec ?? 0)) == SkillToBeAltered) + return true; + + return false; + } } } diff --git a/Source/ACE.Server/WorldObjects/WorldObject_Properties.cs b/Source/ACE.Server/WorldObjects/WorldObject_Properties.cs index d194687c63..a2270d5ff2 100644 --- a/Source/ACE.Server/WorldObjects/WorldObject_Properties.cs +++ b/Source/ACE.Server/WorldObjects/WorldObject_Properties.cs @@ -2202,6 +2202,12 @@ public Skill? ItemSkillLimit set { if (!value.HasValue) RemoveProperty(PropertyDataId.ItemSkillLimit); else SetProperty(PropertyDataId.ItemSkillLimit, (uint)value); } } + public Skill? ItemSpecializedOnly + { + get => (Skill?)GetProperty(PropertyDataId.ItemSpecializedOnly); + set { if (!value.HasValue) RemoveProperty(PropertyDataId.ItemSpecializedOnly); else SetProperty(PropertyDataId.ItemSpecializedOnly, (uint)value); } + } + public int? ItemSkillLevelLimit { get => GetProperty(PropertyInt.ItemSkillLevelLimit); diff --git a/Source/ACE.Server/WorldObjects/WorldObject_Use.cs b/Source/ACE.Server/WorldObjects/WorldObject_Use.cs index b9029dea56..170922ec5f 100644 --- a/Source/ACE.Server/WorldObjects/WorldObject_Use.cs +++ b/Source/ACE.Server/WorldObjects/WorldObject_Use.cs @@ -216,6 +216,14 @@ public virtual ActivationResult CheckUseRequirements(WorldObject activator) var skill = activator.ConvertToMoASkill((Skill)ItemSkillLimit.Value); var playerSkill = player.GetCreatureSkill(skill); + //if (playerSkill.AdvancementClass < SkillAdvancementClass.Trained) + //{ + // //return new ActivationResult(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.Your_SkillMustBeTrained, playerSkill.Skill.ToSentence())); + // player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"You must have {playerSkill.Skill.ToSentence()} trained to use that item's magic")); + // return new ActivationResult(false); + //} + + // verify skill level if (playerSkill.Current < ItemSkillLevelLimit.Value) return new ActivationResult(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.Your_IsTooLowToUseItemMagic, playerSkill.Skill.ToSentence())); } @@ -259,6 +267,24 @@ public virtual ActivationResult CheckUseRequirements(WorldObject activator) } } + // verify skill specialized + // only found on a few items, doesn't show up on ID panel so is effectively a hidden requirement unless noted in ShortDesc/LongDesc string text + if (ItemSpecializedOnly != null) + { + var skill = activator.ConvertToMoASkill((Skill)ItemSpecializedOnly.Value); + var playerSkill = player.GetCreatureSkill(skill); + + if (playerSkill.AdvancementClass < SkillAdvancementClass.Specialized) + return new ActivationResult(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.YouMustSpecialize_ToUseItemMagic, playerSkill.Skill.ToSentence())); + + // verify skill level (if this was included) + if (ItemSkillLevelLimit != null) + { + if (playerSkill.Current < ItemSkillLevelLimit.Value) + return new ActivationResult(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.Your_IsTooLowToUseItemMagic, playerSkill.Skill.ToSentence())); + } + } + // verify player level if (UseRequiresLevel != null) {