Skip to content
117 changes: 102 additions & 15 deletions Source/ACE.Server/WorldObjects/AttributeTransferDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -92,16 +91,104 @@ 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 {TransferFromAttribute} must be above 10 to use the {Name}.");
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 {TransferToAttribute} must be below 100 to use the {Name}.");
return false;
}

return true;
}

/// <summary>
/// Checks wielded items and their requirements to see if they'd be violated by an impending attribute transfer operation
/// </summary>
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)
{
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;
}
}
}
45 changes: 40 additions & 5 deletions Source/ACE.Server/WorldObjects/SkillAlterationDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,23 +280,58 @@ 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;
}
}
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;
}
}
}
6 changes: 6 additions & 0 deletions Source/ACE.Server/WorldObjects/WorldObject_Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions Source/ACE.Server/WorldObjects/WorldObject_Use.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
Expand Down Expand Up @@ -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)
{
Expand Down