using System; using log4net; using ACE.Entity; using ACE.Entity.Enum; using ACE.Entity.Enum.Properties; using ACE.Entity.Models; using ACE.Server.Entity; using ACE.Server.Network.GameMessages.Messages; using ACE.Server.Physics; namespace ACE.Server.WorldObjects { public class Food : Stackable { private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); /// /// A new biota be created taking all of its values from weenie. /// public Food(Weenie weenie, ObjectGuid guid) : base(weenie, guid) { SetEphemeralValues(); } /// /// Restore a WorldObject from the database. /// public Food(Biota biota) : base(biota) { SetEphemeralValues(); } private void SetEphemeralValues() { ObjectDescriptionFlags |= ObjectDescriptionFlag.Food; } /// /// This is raised by Player.HandleActionUseItem. /// The item should be in the players possession. /// public override void ActOnUse(WorldObject activator) { if (!(activator is Player player)) return; if (player.IsBusy || player.Teleporting || player.suicideInProgress) { player.SendWeenieError(WeenieError.YoureTooBusy); return; } if (player.IsJumping) { player.SendWeenieError(WeenieError.YouCantDoThatWhileInTheAir); return; } var motionCommand = GetUseSound() == Sound.Eat1 ? MotionCommand.Eat : MotionCommand.Drink; player.ApplyConsumable(motionCommand, () => ApplyConsumable(player)); } /// /// Applies the boost from the consumable, broadcasts the sound, /// sends message to player, and consumes from inventory /// public void ApplyConsumable(Player player) { if (player.IsDead) return; // verify item is still valid if (player.FindObject(Guid.Full, Player.SearchLocations.MyInventory) == null) { //player.SendWeenieError(WeenieError.ObjectGone); // results in 'Unable to move object!' transient error player.SendTransientError($"Cannot find the {Name}"); // custom message return; } // trying to use a dispel potion while pk timer is active // send error message and cancel - do not consume item if (SpellDID != null) { var spell = new Spell(SpellDID.Value); if (spell.MetaSpellType == SpellType.Dispel && !VerifyDispelPKStatus(this, player)) return; } if (BoosterEnum != PropertyAttribute2nd.Undef) { BoostVital(player); } if (SpellDID != null) { CastSpell(player); } var soundEvent = new GameMessageSound(player.Guid, GetUseSound(), 1.0f); player.EnqueueBroadcast(soundEvent); if (!UnlimitedUse) player.TryConsumeFromInventoryWithNetworking(this, 1); } public void BoostVital(Player player) { var vital = player.GetCreatureVital(BoosterEnum); if (vital == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} ({Guid}) contains invalid vital {BoosterEnum}", ChatMessageType.Broadcast)); return; } // only apply to restoration food? var ratingMod = BoostValue > 0 ? player.GetHealingRatingMod() : 1.0f; var boostValue = (int)Math.Round(BoostValue * ratingMod); var vitalChange = (uint)Math.Abs(player.UpdateVitalDelta(vital, boostValue)); if (BoosterEnum == PropertyAttribute2nd.Health) { if (BoostValue >= 0) player.DamageHistory.OnHeal(vitalChange); else player.DamageHistory.Add(this, DamageType.Health, vitalChange); } var verb = BoostValue >= 0 ? "restores" : "takes"; player.Session.Network.EnqueueSend(new GameMessageSystemChat($"The {Name} {verb} {vitalChange} points of your {BoosterEnum}.", ChatMessageType.Broadcast)); if (player.IsDead) { player.OnDeath(player.DamageHistory.LastDamager, DamageType.Health, false); player.Die(); } } public void CastSpell(Player player) { var spell = new Spell(SpellDID.Value); if (spell.NotFound) { if (spell._spellBase != null) player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); else player.Session.Network.EnqueueSend(new GameMessageSystemChat($"Invalid spell id {SpellDID ?? 0}", ChatMessageType.System)); return; } // should be 'You cast', instead of 'Item cast' // omitting the item caster here, so player is also used for enchantment registry caster, // which could prevent some scenarios with spamming enchantments from multiple food sources to protect against dispels player.TryCastSpell(spell, player, this, tryResist: false); } public Sound GetUseSound() { var useSound = UseSound; if (useSound == Sound.Invalid) useSound = Sound.Eat1; return useSound; } } }