mirror of
https://github.com/ACEmulator/ACE
synced 2026-08-17 12:26:06 -04:00
* Update ShardDatabaseOfflineTools.cs * Update Corpse.cs * Update Creature_Death.cs * Update Player_Death.cs * Update Player_Inventory.cs * Update WorldObject_Decay.cs * Update HouseManager.cs * Update Player_House.cs * Update RecipeManager.cs * Update Player_Allegiance.cs * Update AuthenticationDatabase.cs * Update ShardDatabase.cs * Update WorldDatabase.cs * Update Player_Magic.cs * Update Player_Skills.cs * Update Player_Attributes.cs * Update Player_Combat.cs * Update PhysicsObj.cs * Update Player_Vitals.cs * Update Player_Missile.cs * Update Player_Melee.cs * Update Monster_Melee.cs * Update Session.cs
137 lines
5.3 KiB
C#
137 lines
5.3 KiB
C#
using ACE.DatLoader;
|
|
using ACE.Entity.Enum;
|
|
using ACE.Entity.Enum.Properties;
|
|
using ACE.Server.Managers;
|
|
using ACE.Server.Network;
|
|
using ACE.Server.Network.GameMessages.Messages;
|
|
using ACE.Server.WorldObjects.Entity;
|
|
|
|
namespace ACE.Server.WorldObjects
|
|
{
|
|
partial class Player
|
|
{
|
|
public bool HandleActionRaiseAttribute(PropertyAttribute attribute, uint amount)
|
|
{
|
|
if (!Attributes.TryGetValue(attribute, out var creatureAttribute))
|
|
{
|
|
log.Warn($"{Name}.HandleActionRaiseAttribute({attribute}, {amount}) - invalid attribute");
|
|
return false;
|
|
}
|
|
|
|
if (amount > AvailableExperience)
|
|
{
|
|
log.Warn($"{Name}.HandleActionRaiseAttribute({attribute}, {amount}) - amount > AvaiableExperience ({AvailableExperience})");
|
|
return false;
|
|
}
|
|
|
|
var prevRank = creatureAttribute.Ranks;
|
|
|
|
if (!SpendAttributeXp(creatureAttribute, amount))
|
|
{
|
|
ChatPacket.SendServerMessage(Session, $"Your attempt to raise {attribute} has failed.", ChatMessageType.Broadcast);
|
|
return false;
|
|
}
|
|
|
|
Session.Network.EnqueueSend(new GameMessagePrivateUpdateAttribute(this, creatureAttribute));
|
|
|
|
if (prevRank != creatureAttribute.Ranks)
|
|
{
|
|
// checks if max rank is achieved and plays fireworks w/ special text
|
|
var suffix = "";
|
|
if (creatureAttribute.IsMaxRank)
|
|
{
|
|
// fireworks
|
|
PlayParticleEffect(PlayScript.WeddingBliss, Guid);
|
|
suffix = " and has reached its upper limit";
|
|
}
|
|
|
|
var sound = new GameMessageSound(Guid, Sound.RaiseTrait);
|
|
var msg = new GameMessageSystemChat($"Your base {attribute} is now {creatureAttribute.Base}{suffix}!", ChatMessageType.Advancement);
|
|
|
|
Session.Network.EnqueueSend(sound, msg);
|
|
|
|
if (attribute == PropertyAttribute.Endurance)
|
|
{
|
|
// this packet appears to trigger client to update both health and stamina
|
|
var updateHealth = new GameMessagePrivateUpdateVital(this, Health);
|
|
|
|
Session.Network.EnqueueSend(updateHealth);
|
|
}
|
|
else if (attribute == PropertyAttribute.Self)
|
|
{
|
|
var updateMana = new GameMessagePrivateUpdateVital(this, Mana);
|
|
|
|
Session.Network.EnqueueSend(updateMana);
|
|
}
|
|
|
|
// retail was missing the 'raise attribute' runrate hook here
|
|
if ((attribute == PropertyAttribute.Strength || attribute == PropertyAttribute.Quickness) && PropertyManager.GetBool("runrate_add_hooks").Item)
|
|
HandleRunRateUpdate();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SpendAttributeXp(CreatureAttribute creatureAttribute, uint amount, bool sendNetworkUpdate = true)
|
|
{
|
|
// ensure attribute is not already max rank
|
|
if (creatureAttribute.IsMaxRank)
|
|
{
|
|
log.Warn($"{Name}.SpendAttributeXp({creatureAttribute.Attribute}, {amount}) - player tried to raise attribute beyond max rank");
|
|
return false;
|
|
}
|
|
|
|
// the client should already handle this naturally,
|
|
// but ensure player can't spend xp beyond the max rank
|
|
var amountToEnd = creatureAttribute.ExperienceLeft;
|
|
|
|
if (amount > amountToEnd)
|
|
{
|
|
log.Warn($"{Name}.SpendAttributeXp({creatureAttribute.Attribute}, {amount}) - player tried to raise attribute beyond {amountToEnd} experience");
|
|
return false; // returning error here, instead of setting amount to amountToEnd
|
|
}
|
|
|
|
// everything looks good at this point,
|
|
// spend xp on attribute
|
|
if (!SpendXP(amount, sendNetworkUpdate))
|
|
{
|
|
log.Warn($"{Name}.SpendAttributeXp({creatureAttribute.Attribute}, {amount}) - SpendXP failed");
|
|
return false;
|
|
}
|
|
|
|
creatureAttribute.ExperienceSpent += amount;
|
|
|
|
// calculate new rank
|
|
creatureAttribute.Ranks = (ushort)CalcAttributeRank(creatureAttribute.ExperienceSpent);
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SpendAllAvailableAttributeXp(CreatureAttribute creatureAttribute, bool sendNetworkUpdate = true)
|
|
{
|
|
var amountRemaining = creatureAttribute.ExperienceLeft;
|
|
|
|
if (amountRemaining > AvailableExperience)
|
|
amountRemaining = (uint)AvailableExperience;
|
|
|
|
SpendAttributeXp(creatureAttribute, amountRemaining, sendNetworkUpdate);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the maximum rank that can be purchased with an xp amount
|
|
/// </summary>
|
|
/// <param name="xpAmount">The amount of xp used to make the purchase</param>
|
|
public static int CalcAttributeRank(uint xpAmount)
|
|
{
|
|
var rankXpTable = DatManager.PortalDat.XpTable.AttributeXpList;
|
|
|
|
for (var i = rankXpTable.Count - 1; i >= 0; i--)
|
|
{
|
|
var rankAmount = rankXpTable[i];
|
|
if (xpAmount >= rankAmount)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
}
|