mirror of
https://github.com/ACEmulator/ACE
synced 2026-08-17 12:26:06 -04:00
* Update AttributeTransferDevice.cs * Update SkillAlterationDevice.cs * Update WorldObject_Properties.cs * Update WorldObject_Use.cs * Update AttributeTransferDevice.cs * Update SkillAlterationDevice.cs * Update AttributeTransferDevice.cs * Update SkillAlterationDevice.cs * Update WorldObject_Use.cs * Update WorldObject_Use.cs * Update SkillAlterationDevice.cs * Update AttributeTransferDevice.cs * Update AttributeTransferDevice.cs
337 lines
15 KiB
C#
337 lines
15 KiB
C#
using System;
|
|
using System.Linq;
|
|
|
|
using ACE.DatLoader;
|
|
using ACE.DatLoader.Entity;
|
|
using ACE.Entity;
|
|
using ACE.Entity.Enum;
|
|
using ACE.Entity.Enum.Properties;
|
|
using ACE.Entity.Models;
|
|
using ACE.Server.Entity;
|
|
using ACE.Server.Network.GameEvent.Events;
|
|
using ACE.Server.Network.GameMessages.Messages;
|
|
using ACE.Server.WorldObjects.Entity;
|
|
|
|
namespace ACE.Server.WorldObjects
|
|
{
|
|
public class SkillAlterationDevice : WorldObject
|
|
{
|
|
public enum SkillAlterationType
|
|
{
|
|
Undef = 0,
|
|
Specialize = 1,
|
|
Lower = 2,
|
|
}
|
|
|
|
public SkillAlterationType TypeOfAlteration
|
|
{
|
|
get => (SkillAlterationType)(GetProperty(PropertyInt.TypeOfAlteration) ?? 0);
|
|
set { if (value == 0) RemoveProperty(PropertyInt.TypeOfAlteration); else SetProperty(PropertyInt.TypeOfAlteration, (int)value); }
|
|
}
|
|
|
|
public Skill SkillToBeAltered
|
|
{
|
|
get => (Skill)(GetProperty(PropertyInt.SkillToBeAltered) ?? 0);
|
|
set { if (value == 0) RemoveProperty(PropertyInt.SkillToBeAltered); else SetProperty(PropertyInt.SkillToBeAltered, (int)value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// A new biota be created taking all of its values from weenie.
|
|
/// </summary>
|
|
public SkillAlterationDevice(Weenie weenie, ObjectGuid guid) : base(weenie, guid)
|
|
{
|
|
SetEphemeralValues();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore a WorldObject from the database.
|
|
/// </summary>
|
|
public SkillAlterationDevice(Biota biota) : base(biota)
|
|
{
|
|
SetEphemeralValues();
|
|
}
|
|
|
|
private void SetEphemeralValues()
|
|
{
|
|
}
|
|
|
|
public override void ActOnUse(WorldObject activator)
|
|
{
|
|
ActOnUse(activator, false);
|
|
}
|
|
|
|
public void ActOnUse(WorldObject activator, bool confirmed)
|
|
{
|
|
if (!(activator is Player player))
|
|
return;
|
|
|
|
// verify skill
|
|
var skill = player.GetCreatureSkill(SkillToBeAltered);
|
|
|
|
if (skill == null)
|
|
{
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouFailToAlterSkill));
|
|
return;
|
|
}
|
|
|
|
// get skill training / specialization costs
|
|
var skillBase = DatManager.PortalDat.SkillTable.SkillBaseHash[(uint)skill.Skill];
|
|
|
|
if (!VerifyRequirements(player, skill, skillBase))
|
|
return;
|
|
|
|
if (!confirmed)
|
|
{
|
|
var msg = "This action will ";
|
|
switch (TypeOfAlteration)
|
|
{
|
|
case SkillAlterationType.Specialize:
|
|
msg += $"specialize your {skill.Skill.ToSentence()} skill and cost {skillBase.UpgradeCostFromTrainedToSpecialized} credits.";
|
|
break;
|
|
case SkillAlterationType.Lower:
|
|
msg += $"lower your {skill.Skill.ToSentence()} skill from {(skill.AdvancementClass == SkillAdvancementClass.Specialized ? "specialized to trained" : "trained to untrained")} and refund the skill credits and experience invested in this skill.";
|
|
break;
|
|
}
|
|
|
|
if (!player.ConfirmationManager.EnqueueSend(new Confirmation_AlterSkill(player.Guid, Guid), msg))
|
|
player.SendWeenieError(WeenieError.ConfirmationInProgress);
|
|
|
|
return;
|
|
}
|
|
|
|
AlterSkill(player, skill, skillBase);
|
|
}
|
|
|
|
public bool VerifyRequirements(Player player, CreatureSkill skill, SkillBase skillBase)
|
|
{
|
|
switch (TypeOfAlteration)
|
|
{
|
|
// Gem of Enlightenment
|
|
case SkillAlterationType.Specialize:
|
|
|
|
// ensure skill is trained
|
|
if (skill.AdvancementClass != SkillAdvancementClass.Trained)
|
|
{
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.Your_SkillMustBeTrained, skill.Skill.ToSentence()));
|
|
return false;
|
|
}
|
|
|
|
// ensure player has enough available skill credits
|
|
if (player.AvailableSkillCredits < skillBase.UpgradeCostFromTrainedToSpecialized)
|
|
{
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.NotEnoughSkillCreditsToSpecialize, skill.Skill.ToSentence()));
|
|
return false;
|
|
}
|
|
|
|
// ensure player won't exceed limit of 70 specialized credits after operation
|
|
var specializedCost = skillBase.SpecializedCost;
|
|
|
|
if (DatManager.PortalDat.CharGen.HeritageGroups.TryGetValue((uint)player.Heritage, out var heritageGroup))
|
|
{
|
|
// check for adjusted costs of Specialization due to player's heritage (e.g. Arcane Lore)
|
|
var heritageAdjustedCost = heritageGroup.Skills.FirstOrDefault(i => i.SkillNum == (int)skill.Skill);
|
|
|
|
if (heritageAdjustedCost != null)
|
|
specializedCost = heritageAdjustedCost.PrimaryCost;
|
|
}
|
|
|
|
if (GetTotalSpecializedCredits(player) + specializedCost > 70)
|
|
{
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.TooManyCreditsInSpecializedSkills, skill.Skill.ToSentence()));
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// Gem of Forgetfulness
|
|
case SkillAlterationType.Lower:
|
|
|
|
// ensure skill is trained or specialized
|
|
if (skill.AdvancementClass < SkillAdvancementClass.Trained)
|
|
{
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.Your_SkillIsAlreadyUntrained, skill.Skill.ToSentence()));
|
|
return false;
|
|
}
|
|
|
|
// Check for equipped items that have requirements in the skill we're lowering
|
|
if (CheckWieldedItems(player))
|
|
{
|
|
// Items are wielded which might be affected by a lowering operation
|
|
player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.CannotLowerSkillWhileWieldingItem, skill.Skill.ToSentence()));
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void AlterSkill(Player player, CreatureSkill skill, SkillBase skillBase)
|
|
{
|
|
switch (TypeOfAlteration)
|
|
{
|
|
// Gem of Enlightenment
|
|
case SkillAlterationType.Specialize:
|
|
|
|
if (player.SpecializeSkill(skill.Skill, skillBase.UpgradeCostFromTrainedToSpecialized, false))
|
|
{
|
|
var updateSkill = new GameMessagePrivateUpdateSkill(player, skill);
|
|
var availableSkillCredits = new GameMessagePrivateUpdatePropertyInt(player, PropertyInt.AvailableSkillCredits, player.AvailableSkillCredits ?? 0);
|
|
var msg = new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.YouHaveSucceededSpecializing_Skill, skill.Skill.ToSentence());
|
|
|
|
player.Session.Network.EnqueueSend(updateSkill, availableSkillCredits, msg);
|
|
|
|
player.TryConsumeFromInventoryWithNetworking(this, 1);
|
|
}
|
|
break;
|
|
|
|
// Gem of Forgetfulness
|
|
case SkillAlterationType.Lower:
|
|
|
|
// specialized => trained
|
|
if (skill.AdvancementClass == SkillAdvancementClass.Specialized)
|
|
{
|
|
var specializedViaAugmentation = player.IsSkillSpecializedViaAugmentation(skill.Skill, out var playerHasAugmentation) && playerHasAugmentation;
|
|
|
|
if (player.UnspecializeSkill(skill.Skill, skillBase.UpgradeCostFromTrainedToSpecialized))
|
|
{
|
|
var updateSkill = new GameMessagePrivateUpdateSkill(player, skill);
|
|
var availableSkillCredits = new GameMessagePrivateUpdatePropertyInt(player, PropertyInt.AvailableSkillCredits, player.AvailableSkillCredits ?? 0);
|
|
var msg = specializedViaAugmentation ? WeenieErrorWithString.YouSucceededRecoveringXPFromSkill_AugmentationNotUntrainable : WeenieErrorWithString.YouHaveSucceededUnspecializing_Skill;
|
|
var message = new GameEventWeenieErrorWithString(player.Session, msg, skill.Skill.ToSentence());
|
|
|
|
player.Session.Network.EnqueueSend(updateSkill, availableSkillCredits, message);
|
|
|
|
player.TryConsumeFromInventoryWithNetworking(this, 1);
|
|
}
|
|
}
|
|
|
|
// trained => untrained
|
|
// in the case of skills which can't be untrained,
|
|
// keep trained, but recover the xp spent
|
|
else if (skill.AdvancementClass == SkillAdvancementClass.Trained)
|
|
{
|
|
var untrainable = Player.IsSkillUntrainable(skill.Skill);
|
|
|
|
if (player.UntrainSkill(skill.Skill, skillBase.TrainedCost))
|
|
{
|
|
var updateSkill = new GameMessagePrivateUpdateSkill(player, skill);
|
|
var availableSkillCredits = new GameMessagePrivateUpdatePropertyInt(player, PropertyInt.AvailableSkillCredits, player.AvailableSkillCredits ?? 0);
|
|
var msg = untrainable ? WeenieErrorWithString.YouHaveSucceededUntraining_Skill : WeenieErrorWithString.CannotUntrain_SkillButRecoveredXP;
|
|
var message = new GameEventWeenieErrorWithString(player.Session, msg, skill.Skill.ToSentence());
|
|
|
|
player.Session.Network.EnqueueSend(updateSkill, availableSkillCredits, message);
|
|
|
|
player.TryConsumeFromInventoryWithNetworking(this, 1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates and returns the current total number of specialized credits
|
|
/// </summary>
|
|
private int GetTotalSpecializedCredits(Player player)
|
|
{
|
|
var specializedCreditsTotal = 0;
|
|
|
|
foreach (var kvp in player.Skills)
|
|
{
|
|
if (kvp.Value.AdvancementClass == SkillAdvancementClass.Specialized)
|
|
{
|
|
switch (kvp.Key)
|
|
{
|
|
// exclude None/Undef skill
|
|
case Skill.None:
|
|
|
|
// exclude aug specs
|
|
case Skill.ArmorTinkering:
|
|
case Skill.ItemTinkering:
|
|
case Skill.MagicItemTinkering:
|
|
case Skill.WeaponTinkering:
|
|
case Skill.Salvaging:
|
|
continue;
|
|
}
|
|
|
|
var skill = DatManager.PortalDat.SkillTable.SkillBaseHash[(uint)kvp.Key];
|
|
|
|
var specializedCost = skill.SpecializedCost;
|
|
|
|
if (DatManager.PortalDat.CharGen.HeritageGroups.TryGetValue((uint)player.Heritage, out var heritageGroup))
|
|
{
|
|
// check for adjusted costs of Specialization due to player's heritage (e.g. Arcane Lore)
|
|
var heritageAdjustedCost = heritageGroup.Skills.FirstOrDefault(i => i.SkillNum == (int)kvp.Key);
|
|
|
|
if (heritageAdjustedCost != null)
|
|
specializedCost = heritageAdjustedCost.PrimaryCost;
|
|
}
|
|
specializedCreditsTotal += specializedCost;
|
|
}
|
|
}
|
|
|
|
return specializedCreditsTotal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks wielded items and their requirements to see if they'd be violated by an impending skill lowering 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.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;
|
|
}
|
|
}
|
|
}
|