/// <summary> /// Applies the boost from the consumable, broadcasts the sound, /// sends message to player, and consumes from inventory /// </summary> public void ApplyConsumable(Player player) { if (player.IsDead) { 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); player.TryConsumeFromInventoryWithNetworking(this, 1); }
public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health Health.Current = (uint)Math.Max(0, (int)Health.Current - amount); if (Health.Current == 0) { HandleActionDie(); return; } // send network messages var msgHealth = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, Health.Current); var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var creature = source as Creature; var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + GetSplatterHeight() + creature.GetSplatterDir(this))); var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text, msgHealth, hitSound, splatter); if (percent >= 0.1f) { var wound = new GameMessageSound(Guid, Sound.Wound1, 1.0f); Session.Network.EnqueueSend(wound); } }
/// <summary> /// Simplified monster take damage over time function, only called for DoTs currently /// </summary> public virtual void TakeDamageOverTime(float amount, DamageType damageType) { if (IsDead) { return; } TakeDamage(null, damageType, amount); // splatter effects var hitSound = new GameMessageSound(Guid, Sound.HitFlesh1, 0.5f); //var splatter = (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + playerSource.GetSplatterHeight() + playerSource.GetSplatterDir(this)); var splatter = new GameMessageScript(Guid, damageType == DamageType.Nether ? PlayScript.HealthDownVoid : PlayScript.DirtyFightingDamageOverTime); EnqueueBroadcast(hitSound, splatter); if (Health.Current <= 0) { return; } if (amount >= Health.MaxValue * 0.25f) { var painSound = (Sound)Enum.Parse(typeof(Sound), "Wound" + ThreadSafeRandom.Next(1, 3), true); EnqueueBroadcast(new GameMessageSound(Guid, painSound, 1.0f)); } }
public void SpendXp(Enum.Ability ability, uint amount) { uint baseValue = character.Abilities[ability].Base; uint result = SpendAbilityXp(character.Abilities[ability], amount); bool isSecondary = (ability == Enum.Ability.Health || ability == Enum.Ability.Stamina || ability == Enum.Ability.Mana); if (result > 0u) { uint ranks = character.Abilities[ability].Ranks; uint newValue = character.Abilities[ability].UnbuffedValue; var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); GameMessage abilityUpdate; if (!isSecondary) { abilityUpdate = new GameMessagePrivateUpdateAbility(Session, ability, ranks, baseValue, result); } else { abilityUpdate = new GameMessagePrivateUpdateVital(Session, ability, ranks, baseValue, result, character.Abilities[ability].Current); } var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.AbilityIncrease, 1f); var message = new GameMessageSystemChat($"Your base {ability} is now {newValue}!", ChatMessageType.Broadcast); Session.Network.EnqueueSend(xpUpdate, abilityUpdate, soundEvent, message); } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {ability} has failed.", ChatMessageType.Broadcast); } }
public override void OnUse(Player player) { string serverMessage = null; // validate within use range, taking into account the radius of the model itself, as well SetupModel csetup = SetupModel.ReadFromDat(this.PhysicsData.CSetup); float radiusSquared = (this.GameData.UseRadius + csetup.Radius) * (this.GameData.UseRadius + csetup.Radius); var motionSanctuary = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player, player.Session, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(this.Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(this.Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.EnqueueMovementEvent(motionSanctuary, player.Guid); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); }
public static UnlockResults Unlock(WorldObject target, string keyCode) { string myLockCode = GetLockCode(target); if (myLockCode == null) { return(UnlockResults.IncorrectKey); } if (target.IsOpen ?? false) { return(UnlockResults.Open); } if (keyCode == myLockCode) { if (!target.IsLocked ?? false) { return(UnlockResults.AlreadyUnlocked); } target.IsLocked = false; var updateProperty = new GameMessagePublicUpdatePropertyBool(target, PropertyBool.Locked, target.IsLocked ?? false); var sound = new GameMessageSound(target.Guid, Sound.LockSuccess, 1.0f); target.EnqueueBroadcast(updateProperty, sound); return(UnlockResults.UnlockSuccess); } return(UnlockResults.IncorrectKey); }
/// <summary> /// Applies the boost from the consumable, broadcasts the sound, /// sends message to player, and consumes from inventory /// </summary> public void ApplyConsumable(Player player) { var buffType = (ConsumableBuffType)BoosterEnum; GameMessageSystemChat buffMessage = null; // spells if (buffType == ConsumableBuffType.Spell) { var spellID = SpellDID ?? 0; var result = player.CreateSingleSpell(spellID); if (result) { var spell = new Server.Entity.Spell(spellID); buffMessage = new GameMessageSystemChat($"{Name} casts {spell.Name} on you.", ChatMessageType.Magic); } else { buffMessage = new GameMessageSystemChat($"{Name} has invalid spell id {spellID}", ChatMessageType.Broadcast); } } // vitals else { var vital = player.GetCreatureVital(BoosterEnum); if (vital != null) { 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"; buffMessage = new GameMessageSystemChat($"The {Name} {verb} {vitalChange} points of your {BoosterEnum}.", ChatMessageType.Broadcast); } else { buffMessage = new GameMessageSystemChat($"{Name} ({Guid}) contains invalid vital {BoosterEnum}", ChatMessageType.Broadcast); } } var soundEvent = new GameMessageSound(player.Guid, GetUseSound(), 1.0f); player.EnqueueBroadcast(soundEvent); player.Session.Network.EnqueueSend(buffMessage); player.TryConsumeFromInventoryWithNetworking(this, 1); }
/// <summary> /// Applies damages to a player from a physical damage source /// </summary> public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { if (Invincible ?? false) { return; } // check lifestone protection if (UnderLifestoneProtection) { HandleLifestoneProtection(); return; } var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount); DamageHistory.Add(source, damageType, damageTaken); // update stamina UpdateVitalDelta(Stamina, -1); if (Fellowship != null) { Fellowship.OnVitalUpdate(this); } if (Health.Current == 0) { OnDeath(source, damageType, crit); Die(); return; } var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; // send network messages var creature = source as Creature; var hotspot = source as Hotspot; if (creature != null) { var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text); var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this))); EnqueueBroadcast(hitSound, splatter); } if (percent >= 0.1f) { EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f)); } }
/// <summary> /// Applies damages to a player from a physical damage source /// </summary> public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { if (Invincible ?? false) { return; } var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount); DamageHistory.Add(source, damageType, damageTaken); if (Health.Current == 0) { OnDeath(source, damageType, crit); Die(); return; } // update stamina UpdateVitalDelta(Stamina, -1); var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; // send network messages var creature = source as Creature; var hotspot = source as Hotspot; if (creature != null) { var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text); var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this))); EnqueueBroadcast(hitSound, splatter); } else if (hotspot != null) { if (!string.IsNullOrWhiteSpace(hotspot.ActivationTalkString)) { Session.Network.EnqueueSend(new GameMessageSystemChat(hotspot.ActivationTalkString.Replace("%i", amount.ToString()), ChatMessageType.Craft)); } if (!hotspot.Visibility) { hotspot.EnqueueBroadcast(new GameMessageSound(hotspot.Guid, Sound.TriggerActivated, 1.0f)); } } if (percent >= 0.1f) { EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f)); } }
public void RaiseAttributeGameAction(PropertyAttribute attribute, uint amount) { var creatureAttribute = new CreatureAttribute(this, attribute); uint result = SpendAttributeXp(creatureAttribute, amount); if (result > 0u) { GameMessage abilityUpdate = new GameMessagePrivateUpdateAttribute(this, attribute, creatureAttribute.Ranks, creatureAttribute.StartingValue, result); // checks if max rank is achieved and plays fireworks w/ special text string messageText; if (IsAttributeMaxRank(creatureAttribute.Ranks)) { // fireworks PlayParticleEffect(ACE.Entity.Enum.PlayScript.WeddingBliss, Guid); messageText = $"Your base {attribute} is now {creatureAttribute.Base} and has reached its upper limit!"; } else { messageText = $"Your base {attribute} is now {creatureAttribute.Base}!"; } var soundEvent = new GameMessageSound(Guid, Sound.RaiseTrait, 1f); var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); // This seems to be needed to keep health up to date properly. // Needed when increasing health and endurance. if (attribute == PropertyAttribute.Endurance) { var healthUpdate = new GameMessagePrivateUpdateVital(this, PropertyAttribute2nd.MaxHealth, Health.Ranks, Health.StartingValue, Health.ExperienceSpent, Health.Current); Session.Network.EnqueueSend(abilityUpdate, soundEvent, message, healthUpdate); } else if (attribute == PropertyAttribute.Self) { var manaUpdate = new GameMessagePrivateUpdateVital(this, PropertyAttribute2nd.MaxMana, Mana.Ranks, Mana.StartingValue, Mana.ExperienceSpent, Mana.Current); Session.Network.EnqueueSend(abilityUpdate, soundEvent, message, manaUpdate); } else { Session.Network.EnqueueSend(abilityUpdate, soundEvent, message); } // retail was missing the 'raise attribute' runrate hook here if ((attribute == PropertyAttribute.Strength || attribute == PropertyAttribute.Quickness) && PropertyManager.GetBool("runrate_add_hooks").Item) { HandleRunRateUpdate(); } } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {attribute} has failed.", ChatMessageType.Broadcast); } }
public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { if (Invincible ?? false) { return; } var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health Health.Current = (uint)Math.Max(0, (int)Health.Current - amount); if (Health.Current == 0) { HandleActionDie(); return; } var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; // send network messages Session.Network.EnqueueSend(new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, Health.Current)); var creature = source as Creature; var hotspot = source as Hotspot; if (creature != null) { var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + GetSplatterHeight() + creature.GetSplatterDir(this))); var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text, hitSound, splatter); } else if (hotspot != null) { if (!string.IsNullOrWhiteSpace(hotspot.ActivationTalkString)) { Session.Network.EnqueueSend(new GameMessageSystemChat(hotspot.ActivationTalkString.Replace("%i", amount.ToString()), ChatMessageType.Craft)); } if (!(hotspot.Visibility ?? false)) { CurrentLandblock.EnqueueBroadcastSound(hotspot, Sound.TriggerActivated); } } if (percent >= 0.1f) { var wound = new GameMessageSound(Guid, Sound.Wound1, 1.0f); Session.Network.EnqueueSend(wound); } }
/// <summary> /// Handles the GameAction 0x46 - RaiseSkill network message from client /// </summary> public bool HandleActionRaiseSkill(Skill skill, uint amount) { var creatureSkill = GetCreatureSkill(skill, false); if (creatureSkill == null || creatureSkill.AdvancementClass < SkillAdvancementClass.Trained) { log.Error($"{Name}.HandleActionRaiseSkill({skill}, {amount}) - trained or specialized skill not found"); return(false); } if (amount > AvailableExperience) { log.Error($"{Name}.HandleActionRaiseSkill({skill}, {amount}) - amount > AvailableExperience ({AvailableExperience})"); return(false); } var prevRank = creatureSkill.Ranks; if (!SpendSkillXp(creatureSkill, amount)) { return(false); } Session.Network.EnqueueSend(new GameMessagePrivateUpdateSkill(this, creatureSkill)); if (prevRank != creatureSkill.Ranks) { // if the skill ranks out at the top of our xp chart // then we will start fireworks effects and have special text! var suffix = ""; if (creatureSkill.IsMaxRank) { // fireworks on rank up is 0x8D PlayParticleEffect(PlayScript.WeddingBliss, Guid); suffix = $" and has reached its upper limit"; } var sound = new GameMessageSound(Guid, Sound.RaiseTrait); var msg = new GameMessageSystemChat($"Your base {skill.ToSentence()} skill is now {creatureSkill.Base}{suffix}!", ChatMessageType.Advancement); Session.Network.EnqueueSend(sound, msg); // retail was missing the 'raise skill' runrate hook here if (skill == Skill.Run && PropertyManager.GetBool("runrate_add_hooks").Item) { HandleRunRateUpdate(); } } return(true); }
public void HandleActionRaiseVital(PropertyAttribute2nd attribute, uint amount) { var creatureVital = new CreatureVital(this, attribute); uint result = SpendVitalXp(creatureVital, amount); if (result > 0u) { GameMessage abilityUpdate = new GameMessagePrivateUpdateVital(this, attribute, creatureVital.Ranks, creatureVital.StartingValue, result, creatureVital.Current); // checks if max rank is achieved and plays fireworks w/ special text string messageText; if (IsVitalMaxRank(creatureVital.Ranks)) { // fireworks PlayParticleEffect(ACE.Entity.Enum.PlayScript.WeddingBliss, Guid); messageText = $"Your base {attribute.ToSentence()} is now {creatureVital.Base} and has reached its upper limit!"; } else { messageText = $"Your base {attribute.ToSentence()} is now {creatureVital.Base}!"; } var soundEvent = new GameMessageSound(Guid, Sound.RaiseTrait, 1f); var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); // This seems to be needed to keep health up to date properly. // Needed when increasing health and endurance. //if (attribute == PropertyAttribute2nd.Endurance) //{ // var healthUpdate = new GameMessagePrivateUpdateVital(Session, Ability.Health, Health.Ranks, Health.StartingValue, Health.ExperienceSpent, Health.Current); // Session.Network.EnqueueSend(abilityUpdate, soundEvent, message, healthUpdate); //} //else if (attribute == PropertyAttribute2nd.Self) //{ // var manaUpdate = new GameMessagePrivateUpdateVital(Session, Ability.Mana, Mana.Ranks, Mana.StartingValue, Mana.ExperienceSpent, Mana.Current); // Session.Network.EnqueueSend(abilityUpdate, soundEvent, message, manaUpdate); //} //else //{ Session.Network.EnqueueSend(abilityUpdate, soundEvent, message); //} } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {attribute.ToSentence()} has failed.", ChatMessageType.Broadcast); } }
/// <summary> /// Handles the GameAction 0x44 - RaiseVital network message from client /// </summary> public bool HandleActionRaiseVital(PropertyAttribute2nd vital, uint amount) { if (!Vitals.TryGetValue(vital, out var creatureVital)) { log.Error($"{Name}.HandleActionRaiseVital({vital}, {amount}) - invalid vital"); return(false); } if (amount > AvailableExperience) { // there is a client bug for vitals only, // where the client will enable the button to raise a vital by 10 // if the player only has enough AvailableExperience to raise it by 1 ChatPacket.SendServerMessage(Session, $"Your attempt to raise {vital.ToSentence()} has failed.", ChatMessageType.Broadcast); log.Error($"{Name}.HandleActionRaiseVital({vital}, {amount}) - amount > AvailableExperience ({AvailableExperience})"); return(false); } var prevRank = creatureVital.Ranks; if (!SpendVitalXp(creatureVital, amount)) { return(false); } Session.Network.EnqueueSend(new GameMessagePrivateUpdateVital(this, creatureVital)); if (prevRank != creatureVital.Ranks) { // checks if max rank is achieved and plays fireworks w/ special text var suffix = ""; if (creatureVital.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 {vital.ToSentence()} is now {creatureVital.Base}{suffix}!", ChatMessageType.Advancement); Session.Network.EnqueueSend(sound, msg); } return(true); }
public override void HandleActionOnUse(ObjectGuid playerId) { // All data on a lifestone is constant -- therefore we just run in context of player ActionChain chain = new ActionChain(); CurrentLandblock.ChainOnObject(chain, playerId, (WorldObject wo) => { Player player = wo as Player; string serverMessage = null; // validate within use range, taking into account the radius of the model itself, as well SetupModel csetup = SetupModel.ReadFromDat(SetupTableId.Value); float radiusSquared = (UseRadius.Value + csetup.Radius) * (UseRadius.Value + csetup.Radius); // Run this animation... // Player Enqueue: var motionSanctuary = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player.Guid, player.Sequences.GetCurrentSequence(Network.Sequence.SequenceType.ObjectInstance), player.Sequences, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.HandleActionMotion(motionSanctuary); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); }); chain.EnqueueChain(); }
public static UnlockResults Unlock(WorldObject target, Key key, string keyCode = null) { if (keyCode == null) { keyCode = key?.KeyCode; } string myLockCode = GetLockCode(target); if (myLockCode == null) { return(UnlockResults.IncorrectKey); } if (target.IsOpen) { return(UnlockResults.Open); } // there is only 1 instance of an 'opens all' key in PY16 data, 'keysonicscrewdriver' // which uses keyCode '_bohemund's_magic_key_' // when LSD added the rare skeleton key (keyrarevolatileuniversal), // they used PropertyBool.OpensAnyLock, which appears to have been used for something else in retail on Writables: // https://github.com/ACEmulator/ACE-World-16PY/blob/master/Database/3-Core/9%20WeenieDefaults/SQL/Key/Key/09181%20Sonic%20Screwdriver.sql // https://github.com/ACEmulator/ACE-World-16PY/search?q=OpensAnyLock if (keyCode != null && (keyCode.Equals(myLockCode, StringComparison.OrdinalIgnoreCase) || keyCode.Equals("_bohemund's_magic_key_")) || key != null && key.OpensAnyLock) { if (!target.IsLocked) { return(UnlockResults.AlreadyUnlocked); } target.IsLocked = false; var updateProperty = new GameMessagePublicUpdatePropertyBool(target, PropertyBool.Locked, target.IsLocked); var sound = new GameMessageSound(target.Guid, Sound.LockSuccess, 1.0f); target.EnqueueBroadcast(updateProperty, sound); return(UnlockResults.UnlockSuccess); } return(UnlockResults.IncorrectKey); }
public void SpendXp(Enum.Ability ability, uint amount) { uint baseValue = character.Abilities[ability].Base; uint result = SpendAbilityXp(character.Abilities[ability], amount); bool isSecondary = (ability == Enum.Ability.Health || ability == Enum.Ability.Stamina || ability == Enum.Ability.Mana); uint ranks = character.Abilities[ability].Ranks; uint newValue = character.Abilities[ability].UnbuffedValue; string messageText = ""; if (result > 0u) { GameMessage abilityUpdate; if (!isSecondary) { abilityUpdate = new GameMessagePrivateUpdateAbility(Session, ability, ranks, baseValue, result); } else { abilityUpdate = new GameMessagePrivateUpdateVital(Session, ability, ranks, baseValue, result, character.Abilities[ability].Current); } // checks if max rank is achieved and plays fireworks w/ special text if (IsAbilityMaxRank(ranks, isSecondary)) { // fireworks PlayParticleEffect(Effect.WeddingBliss); messageText = $"Your base {ability} is now {newValue} and has reached its upper limit!"; } else { messageText = $"Your base {ability} is now {newValue}!"; } var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.RaiseTrait, 1f); var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(abilityUpdate, xpUpdate, soundEvent, message); } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {ability} has failed.", ChatMessageType.Broadcast); } }
public void SpendXp(Skill skill, uint amount) { uint baseValue = 0; uint result = SpendSkillXp(character.Skills[skill], amount); if (result > 0u) { uint ranks = character.Skills[skill].Ranks; uint newValue = character.Skills[skill].UnbuffedValue; var status = character.Skills[skill].Status; var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); var ablityUpdate = new GameMessagePrivateUpdateSkill(Session, skill, status, ranks, baseValue, result); var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.AbilityIncrease, 1f); var message = new GameMessageSystemChat($"Your base {skill} is now {newValue}!", ChatMessageType.Broadcast); NetworkManager.SendWorldMessages(Session, new GameMessage[] { xpUpdate, ablityUpdate, soundEvent, message }); } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {skill} has failed.", ChatMessageType.Broadcast); } }
public static void HandlePlaySound(Session session, params string[] parameters) { try { Network.Enum.Sound sound = Network.Enum.Sound.Invalid; string message = ""; float volume = 1f; var soundEvent = new GameMessageSound(session.Player.Guid, Network.Enum.Sound.Invalid, volume); if (parameters.Length > 1) { if (parameters[1] != "") { volume = float.Parse(parameters[1]); } } message = $"Unable to find a sound called {parameters[0]} to play."; if (Enum.TryParse(parameters[0], true, out sound)) { if (Enum.IsDefined(typeof(Network.Enum.Sound), sound)) { message = $"Playing sound {Enum.GetName(typeof(Network.Enum.Sound), sound)}"; // add the sound to the player queue for everyone to hear // player action queue items will execute on the landblock // player.playsound will play a sound on only the client session that called the function session.Player.ActionApplySoundEffect(sound, session.Player.Guid); } } var sysChatMessage = new GameMessageSystemChat(message, ChatMessageType.Broadcast); session.Network.EnqueueSend(sysChatMessage); } catch (Exception) { // Do Nothing } }
/// <summary> /// Applies the boost from the consumable, broadcasts the sound, /// sends message to player, and consumes from inventory /// </summary> public void ApplyConsumable(Player player) { if (player.IsDead) { return; } if (BoosterEnum != PropertyAttribute2nd.Undef) { BoostVital(player); } if (SpellDID != null) { CastSpell(player); } var soundEvent = new GameMessageSound(player.Guid, GetUseSound(), 1.0f); player.EnqueueBroadcast(soundEvent); player.TryConsumeFromInventoryWithNetworking(this, 1); }
/// <summary> /// Spend xp Skill ranks /// </summary> public void SpendXp(Skill skill, uint amount) { uint baseValue = 0; uint result = SpendSkillXp(character.Skills[skill], amount); uint ranks = character.Skills[skill].Ranks; uint newValue = character.Skills[skill].UnbuffedValue; var status = character.Skills[skill].Status; var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); var skillUpdate = new GameMessagePrivateUpdateSkill(Session, skill, status, ranks, baseValue, result); var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.AbilityIncrease, 1f); string messageText = ""; if (result > 0u) { //if the skill ranks out at the top of our xp chart //then we will start fireworks effects and have special text! if (IsSkillMaxRank(ranks, status)) { //fireworks on rank up is 0x8D PlayParticleEffect(0x8D); messageText = $"Your base {skill} is now {newValue} and has reached its upper limit!"; } else { messageText = $"Your base {skill} is now {newValue}!"; } } else { messageText = $"Your attempt to raise {skill} has failed!"; } var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(xpUpdate, skillUpdate, soundEvent, message); }
private void HandleManaDepleted(WorldObject item) { var msg = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic); var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted); Session.Network.EnqueueSend(msg, sound); // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { foreach (var spellId in item.Biota.GetKnownSpellsIds(item.BiotaDatabaseLock)) { RemoveItemSpell(item, (uint)spellId); } }); actionChain.EnqueueChain(); item.OnSpellsDeactivated(); }
public static void HandlePlaySound(Session session, params string[] parameters) { try { Network.Enum.Sound sound = Network.Enum.Sound.Invalid; string message = ""; float volume = 1f; var soundEvent = new GameMessageSound(session.Player.Guid, Network.Enum.Sound.Invalid, volume); if (parameters.Length > 1) { if (parameters[1] != "") { volume = float.Parse(parameters[1]); } } message = $"Unable to find a sound called {parameters[0]} to play."; if (Enum.TryParse(parameters[0], true, out sound)) { if (Enum.IsDefined(typeof(Network.Enum.Sound), sound)) { message = $"Playing sound {Enum.GetName(typeof(Network.Enum.Sound), sound)}"; soundEvent = new GameMessageSound(session.Player.Guid, sound, volume); } } var sysChatMessage = new GameMessageSystemChat(message, ChatMessageType.Broadcast); session.Network.EnqueueSend(soundEvent, sysChatMessage); } catch (Exception) { // Do Nothing } }
/// <summary> /// Spend xp Skill ranks /// </summary> public void RaiseSkillGameAction(Skill skill, uint amount) { uint baseValue = 0; var creatureSkill = GetCreatureSkill(skill); uint result = SpendSkillXp(creatureSkill, amount); string messageText; if (result > 0u) { // if the skill ranks out at the top of our xp chart // then we will start fireworks effects and have special text! if (IsSkillMaxRank(creatureSkill.Ranks, creatureSkill.Status)) { // fireworks on rank up is 0x8D PlayParticleEffect(ACE.Entity.Enum.PlayScript.WeddingBliss, Guid); messageText = $"Your base {skill} is now {creatureSkill.Base} and has reached its upper limit!"; } else { messageText = $"Your base {skill} is now {creatureSkill.Base}!"; } } else { messageText = $"Your attempt to raise {skill} has failed!"; } var skillUpdate = new GameMessagePrivateUpdateSkill(Session, skill, creatureSkill.Status, creatureSkill.Ranks, baseValue, result); var soundEvent = new GameMessageSound(Guid, Sound.RaiseTrait, 1f); var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(skillUpdate, soundEvent, message); }
/// <summary> /// Method used to perform the animation, sound, and vital update on consumption of food or potions /// </summary> /// <param name="consumableName">Name of the consumable</param> /// <param name="sound">Either Sound.Eat1 or Sound.Drink1</param> /// <param name="buffType">ConsumableBuffType.Spell,ConsumableBuffType.Health,ConsumableBuffType.Stamina,ConsumableBuffType.Mana</param> /// <param name="boostAmount">Amount the Vital is boosted by; can be null, if buffType = ConsumableBuffType.Spell</param> /// <param name="spellDID">Id of the spell cast by the consumable; can be null, if buffType != ConsumableBuffType.Spell</param> public void ApplyConsumable(string consumableName, Sound sound, ConsumableBuffType buffType, uint?boostAmount, uint?spellDID) { MotionCommand motionCommand; if (sound == Sound.Eat1) { motionCommand = MotionCommand.Eat; } else { motionCommand = MotionCommand.Drink; } // start the eat/drink motion var motion = new Motion(MotionStance.NonCombat, motionCommand); EnqueueBroadcastMotion(motion); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId); var animTime = motionTable.GetAnimationLength(CurrentMotionState.Stance, motionCommand, MotionCommand.Ready); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(animTime); actionChain.AddAction(this, () => { GameMessageSystemChat buffMessage; if (buffType == ConsumableBuffType.Spell) { bool result = false; uint spellId = spellDID ?? 0; if (spellId != 0) { result = CreateSingleSpell(spellId); } if (result) { var spell = new Server.Entity.Spell(spellId); buffMessage = new GameMessageSystemChat($"{consumableName} casts {spell.Name} on you.", ChatMessageType.Magic); } else { buffMessage = new GameMessageSystemChat($"Consuming {consumableName} attempted to apply a spell not yet fully implemented.", ChatMessageType.System); } } else { CreatureVital creatureVital; string vitalName; // Null check for safety if (boostAmount == null) { boostAmount = 0; } switch (buffType) { case ConsumableBuffType.Health: creatureVital = Health; vitalName = "Health"; break; case ConsumableBuffType.Mana: creatureVital = Mana; vitalName = "Mana"; break; default: creatureVital = Stamina; vitalName = "Stamina"; break; } var vitalChange = UpdateVitalDelta(creatureVital, (uint)boostAmount); if (vitalName == "Health") { DamageHistory.OnHeal((uint)vitalChange); if (Fellowship != null) { Fellowship.OnVitalUpdate(this); } } buffMessage = new GameMessageSystemChat($"You regain {vitalChange} {vitalName}.", ChatMessageType.Craft); } var soundEvent = new GameMessageSound(Guid, sound, 1.0f); EnqueueBroadcast(soundEvent); Session.Network.EnqueueSend(buffMessage); // return to original stance var returnStance = new Motion(CurrentMotionState.Stance); EnqueueBroadcastMotion(returnStance); }); actionChain.EnqueueChain(); }
public void ExecuteEmote(BiotaPropertiesEmote emote, BiotaPropertiesEmoteAction emoteAction, ActionChain actionChain, WorldObject sourceObject = null, WorldObject targetObject = null) { var player = targetObject as Player; var creature = sourceObject as Creature; var targetCreature = targetObject as Creature; var emoteType = (EmoteType)emoteAction.Type; //if (emoteType != EmoteType.Motion && emoteType != EmoteType.Turn && emoteType != EmoteType.Move) //Console.WriteLine($"{WorldObject.Name}.ExecuteEmote({emoteType})"); var text = emoteAction.Message; switch ((EmoteType)emoteAction.Type) { case EmoteType.Act: // short for 'acting' text var message = Replace(text, sourceObject, targetObject); sourceObject?.EnqueueBroadcast(new GameMessageSystemChat(message, ChatMessageType.Broadcast), 30.0f); break; case EmoteType.Activate: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (creature != null && (creature.ActivationTarget ?? 0) > 0) { var activationTarget = creature.CurrentLandblock?.GetObject(creature.ActivationTarget ?? 0); activationTarget?.ActOnUse(creature); } }); break; case EmoteType.AddCharacterTitle: // emoteAction.Stat == null for all EmoteType.AddCharacterTitle entries in current db? if (player != null) { player.AddTitle((CharacterTitle)emoteAction.Stat); } break; case EmoteType.AddContract: //if (player != null) //Contracts werent in emote table //player.AddContract(emoteAction.Stat); break; case EmoteType.AdminSpam: var players = WorldManager.GetAll(); foreach (var onlinePlayer in players) { onlinePlayer.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.AdminTell)); } break; case EmoteType.AwardLevelProportionalSkillXP: if (player != null) { player.GrantLevelProportionalSkillXP((Skill)emoteAction.Stat, emoteAction.Percent ?? 0, (ulong)emoteAction.Max); } break; case EmoteType.AwardLevelProportionalXP: if (player != null) { player.GrantLevelProportionalXp(emoteAction.Percent ?? 0, (ulong)emoteAction.Max); } break; case EmoteType.AwardLuminance: if (player != null) { player.GrantLuminance((long)emoteAction.Amount); } break; case EmoteType.AwardNoShareXP: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (player != null) { player.EarnXP((long)emoteAction.Amount64); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You've earned " + emoteAction.Amount64 + " experience.", ChatMessageType.Broadcast)); } }); break; case EmoteType.AwardSkillPoints: if (player != null) { player.AwardSkillPoints((Skill)emoteAction.Stat, (uint)emoteAction.Amount, true); } break; case EmoteType.AwardSkillXP: if (player != null) { player.RaiseSkillGameAction((Skill)emoteAction.Stat, (uint)emoteAction.Amount, true); } break; case EmoteType.AwardTrainingCredits: if (player != null) { player.AddSkillCredits((int)emoteAction.Amount, true); } break; case EmoteType.AwardXP: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (player != null) { player.EarnXP((long)emoteAction.Amount64); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You've earned " + emoteAction.Amount64 + " experience.", ChatMessageType.Broadcast)); } }); break; case EmoteType.BLog: // only one test drudge used this emoteAction. break; case EmoteType.CastSpell: if (WorldObject is Player) { (WorldObject as Player).CreatePlayerSpell((uint)emoteAction.SpellId); } else if (WorldObject is Creature) { (WorldObject as Creature).CreateCreatureSpell(player.Guid, (uint)emoteAction.SpellId); } break; case EmoteType.CastSpellInstant: var spellTable = DatManager.PortalDat.SpellTable; var spell = spellTable.Spells[(uint)emoteAction.SpellId]; actionChain.AddAction(sourceObject, () => { if (spell.TargetEffect > 0) { creature.CreateCreatureSpell(targetObject.Guid, (uint)emoteAction.SpellId); } else { creature.CreateCreatureSpell((uint)emoteAction.SpellId); } }); break; case EmoteType.CloseMe: targetObject.Close(WorldObject); break; case EmoteType.CreateTreasure: break; case EmoteType.DecrementIntStat: var id = (PropertyInt)emoteAction.Stat; var prop = player.GetProperty(id); if (prop != null) { player.SetProperty(id, prop.Value - 1); } break; case EmoteType.DecrementMyQuest: break; case EmoteType.DecrementQuest: // Used as part of the test drudge for events break; case EmoteType.DeleteSelf: sourceObject.CurrentLandblock?.RemoveWorldObject(sourceObject.Guid, false); break; case EmoteType.DirectBroadcast: text = Replace(emoteAction.Message, WorldObject, targetObject); if (player != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } break; case EmoteType.EraseMyQuest: break; case EmoteType.EraseQuest: if (player != null) { player.QuestManager.Erase(emoteAction.Message); } break; case EmoteType.FellowBroadcast: text = emoteAction.Message; if (player != null) { var fellowship = player.Fellowship; if (fellowship == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } else { foreach (var fellow in fellowship.FellowshipMembers) { fellow.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } } } break; case EmoteType.Generate: uint wcid = (uint)emoteAction.WeenieClassId; var item = WorldObjectFactory.CreateNewWorldObject((wcid)); break; case EmoteType.Give: bool success = false; if (player != null && emoteAction.WeenieClassId != null) { actionChain.AddAction(sourceObject, () => { item = WorldObjectFactory.CreateNewWorldObject((uint)emoteAction.WeenieClassId); var stackSize = emoteAction.StackSize ?? 1; var stackMsg = ""; if (stackSize > 1) { item.StackSize = (ushort)stackSize; stackMsg = stackSize + " "; // pluralize? } success = player.TryCreateInInventoryWithNetworking(item); // transaction / rollback on failure? if (success) { var msg = new GameMessageSystemChat($"{WorldObject.Name} gives you {stackMsg}{item.Name}.", ChatMessageType.Broadcast); var sound = new GameMessageSound(player.Guid, Sound.ReceiveItem, 1); player.Session.Network.EnqueueSend(msg, sound); } }); } break; case EmoteType.Goto: var rng = Physics.Common.Random.RollDice(0.0f, 1.0f); var firstEmote = sourceObject.Biota.BiotaPropertiesEmote.FirstOrDefault(e => e.Category == (uint)EmoteCategory.GotoSet && rng < e.Probability); foreach (var action in firstEmote.BiotaPropertiesEmoteAction) { actionChain.AddAction(player, () => { ExecuteEmote(firstEmote, action, actionChain, sourceObject, targetObject); }); } break; case EmoteType.IncrementIntStat: if (player == null || emoteAction.Stat == null) { break; } id = (PropertyInt)emoteAction.Stat; prop = player.GetProperty(id); if (prop != null) { player.SetProperty(id, prop.Value + 1); } break; case EmoteType.IncrementMyQuest: break; case EmoteType.IncrementQuest: if (player != null) { player.QuestManager.Increment(emoteAction.Message); } break; case EmoteType.InflictVitaePenalty: if (player != null) { player.VitaeCpPool++; // TODO: full path } break; case EmoteType.InqAttributeStat: if (targetCreature != null) { var attr = targetCreature.GetCreatureAttribute((PropertyAttribute)emoteAction.Stat); success = attr != null && attr.Ranks >= emoteAction.Min && attr.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqBoolStat: // This is only used with NPC's 24944 and 6386, which are dev tester npc's. Not worth the current effort. // Could also be post-ToD break; case EmoteType.InqContractsFull: // not part of the game at PY16? //if (player != null) //{ // var contracts = player.TrackedContracts; // InqCategory(contracts.Count != 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); //} break; case EmoteType.InqEvent: var started = EventManager.IsEventStarted(emoteAction.Message); InqCategory(started ? EmoteCategory.EventSuccess : EmoteCategory.EventFailure, emoteAction, sourceObject, targetObject, actionChain); break; case EmoteType.InqFellowNum: InqCategory(player != null && player.Fellowship != null ? EmoteCategory.TestSuccess : EmoteCategory.TestNoFellow, emoteAction, sourceObject, targetObject, actionChain); break; case EmoteType.InqFellowQuest: // focusing on 1 person quests to begin with break; case EmoteType.InqFloatStat: //InqProperty(target.GetProperty((PropertyFloat)emote.Stat), emote); break; case EmoteType.InqInt64Stat: //InqProperty(target.GetProperty((PropertyInt64)emote.Stat), emote); break; case EmoteType.InqIntStat: if (emoteAction.Stat != 25) { break; // ?? } success = player.Level >= emoteAction.Min && player.Level <= emoteAction.Max; // rng for failure case? var useRNG = !success; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain, useRNG); break; case EmoteType.InqMyQuest: break; case EmoteType.InqMyQuestBitsOff: break; case EmoteType.InqMyQuestBitsOn: break; case EmoteType.InqMyQuestSolves: break; case EmoteType.InqNumCharacterTitles: //if (player != null) //InqCategory(player.NumCharacterTitles != 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); break; case EmoteType.InqOwnsItems: //if (player != null) //InqCategory(player.Inventory.Count > 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); break; case EmoteType.InqPackSpace: //if (player != null) //{ // var freeSpace = player.ContainerCapacity > player.ItemCapacity; // InqCategory(freeSpace ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); //} break; case EmoteType.InqQuest: if (player != null) { var hasQuest = player.QuestManager.HasQuest(emoteAction.Message); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqQuestBitsOff: break; case EmoteType.InqQuestBitsOn: break; case EmoteType.InqQuestSolves: // should this be different from InqQuest? if (player != null) { var hasQuest = player.QuestManager.HasQuest(emoteAction.Message); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawAttributeStat: if (targetCreature != null) { var attr = targetCreature.GetCreatureAttribute((PropertyAttribute)emoteAction.Stat); success = attr != null && attr.Base >= emoteAction.Min && attr.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawSecondaryAttributeStat: if (targetCreature != null) { var vital = targetCreature.GetCreatureVital((PropertyAttribute2nd)emoteAction.Stat); success = vital != null && vital.Base >= emoteAction.Min && vital.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawSkillStat: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); success = skill != null && skill.Base >= emoteAction.Min && skill.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSecondaryAttributeStat: if (targetCreature != null) { var vital = targetCreature.GetCreatureVital((PropertyAttribute2nd)emoteAction.Stat); success = vital != null && vital.Ranks >= emoteAction.Min && vital.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSkillSpecialized: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); //InqProperty(skill.Status == SkillStatus.Specialized, emoteAction); } break; case EmoteType.InqSkillStat: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); success = skill != null && skill.Ranks >= emoteAction.Min && skill.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSkillTrained: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); // TestNoQuality? InqProperty(skill.AdvancementClass == SkillAdvancementClass.Trained || skill.AdvancementClass == SkillAdvancementClass.Specialized, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqStringStat: //InqProperty(targetCreature.GetProperty((PropertyString)emoteAction.Stat), emote); break; case EmoteType.InqYesNo: ConfirmationManager.ProcessConfirmation((uint)emoteAction.Stat, true); break; case EmoteType.Invalid: break; case EmoteType.KillSelf: if (targetCreature != null) { targetCreature.Smite(targetCreature); } break; case EmoteType.LocalBroadcast: if (actionChain != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject?.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Broadcast)); }); } else { sourceObject.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Broadcast)); } break; case EmoteType.LocalSignal: break; case EmoteType.LockFellow: if (player != null && player.Fellowship != null) { player.HandleActionFellowshipChangeOpenness(false); } break; case EmoteType.ForceMotion: // TODO: figure out the difference case EmoteType.Motion: if (sourceObject == null || sourceObject.CurrentMotionState == null) { break; } if (emote.Category != (uint)EmoteCategory.Vendor && emote.Style != null) { var startingMotion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emote.Substyle)); var motion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); if (sourceObject.CurrentMotionState.Stance != startingMotion.Stance) { if (sourceObject.CurrentMotionState.Stance == MotionStance.Invalid) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running starting motion {(MotionStance)emote.Style}, {(MotionCommand)emote.Substyle}"); sourceObject.ExecuteMotion(startingMotion); }); } } else { if (sourceObject.CurrentMotionState.Commands.Count > 0 && sourceObject.CurrentMotionState.Commands[0].Motion == startingMotion.Commands[0].Motion) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running motion {(MotionStance)emote.Style}, {(MotionCommand)emoteAction.Motion}"); float?maxRange = ClientMaxAnimRange; if (MotionQueue.Contains((MotionCommand)emoteAction.Motion)) { maxRange = null; } sourceObject.ExecuteMotion(motion, true, maxRange); }); actionChain.AddDelaySeconds(DatManager.PortalDat.ReadFromDat <DatLoader.FileTypes.MotionTable>(sourceObject.MotionTableId).GetAnimationLength((MotionCommand)emoteAction.Motion)); if (motion.Commands[0].Motion != MotionCommand.Sleeping && motion.Commands[0].Motion != MotionCommand.Sitting) // this feels like it can be handled better, somehow? { actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running starting motion again {(MotionStance)emote.Style}, {(MotionCommand)emote.Substyle}"); sourceObject.ExecuteMotion(startingMotion); }); } } } } else { var motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running motion (block 2) {(MotionStance)emote.Style}, {(MotionCommand)emoteAction.Motion}"); sourceObject.ExecuteMotion(motion); }); } break; case EmoteType.Move: // what is the difference between this and MoveToPos? // using MoveToPos logic for now... if (targetCreature != null) { var currentPos = targetCreature.Location; var newPos = new Position(); newPos.LandblockId = new LandblockId(currentPos.LandblockId.Raw); newPos.Pos = new Vector3(emoteAction.OriginX ?? currentPos.Pos.X, emoteAction.OriginY ?? currentPos.Pos.Y, emoteAction.OriginZ ?? currentPos.Pos.Z); if (emoteAction.AnglesX == null || emoteAction.AnglesY == null || emoteAction.AnglesZ == null || emoteAction.AnglesW == null) { newPos.Rotation = new Quaternion(currentPos.Rotation.X, currentPos.Rotation.Y, currentPos.Rotation.Z, currentPos.Rotation.W); } else { newPos.Rotation = new Quaternion(emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 1); } if (emoteAction.ObjCellId != null) { newPos.LandblockId = new LandblockId(emoteAction.ObjCellId.Value); } targetCreature.MoveTo(newPos, targetCreature.GetRunRate()); } break; case EmoteType.MoveHome: // TODO: call MoveToManager on server if (targetCreature != null) { targetCreature.MoveTo(targetCreature.Home, targetCreature.GetRunRate()); } break; case EmoteType.MoveToPos: if (targetCreature != null) { var currentPos = targetCreature.Location; var newPos = new Position(); newPos.LandblockId = new LandblockId(currentPos.LandblockId.Raw); newPos.Pos = new Vector3(emoteAction.OriginX ?? currentPos.Pos.X, emoteAction.OriginY ?? currentPos.Pos.Y, emoteAction.OriginZ ?? currentPos.Pos.Z); if (emoteAction.AnglesX == null || emoteAction.AnglesY == null || emoteAction.AnglesZ == null || emoteAction.AnglesW == null) { newPos.Rotation = new Quaternion(currentPos.Rotation.X, currentPos.Rotation.Y, currentPos.Rotation.Z, currentPos.Rotation.W); } else { newPos.Rotation = new Quaternion(emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 1); } if (emoteAction.ObjCellId != null) { newPos.LandblockId = new LandblockId(emoteAction.ObjCellId.Value); } targetCreature.MoveTo(newPos, targetCreature.GetRunRate()); } break; case EmoteType.OpenMe: sourceObject.Open(sourceObject); break; case EmoteType.PetCastSpellOnOwner: if (creature != null) { creature.CreateCreatureSpell(targetObject.Guid, (uint)emoteAction.SpellId); } break; case EmoteType.PhysScript: // TODO: landblock broadcast if (sourceObject != null) { sourceObject.PhysicsObj.play_script((PlayScript)emoteAction.PScript, 1.0f); } break; case EmoteType.PopUp: if (player != null) { if ((ConfirmationType)emoteAction.Stat == ConfirmationType.Undefined) { player.Session.Network.EnqueueSend(new GameEventPopupString(player.Session, emoteAction.Message)); } else { Confirmation confirm = new Confirmation((ConfirmationType)emoteAction.Stat, emoteAction.Message, sourceObject.Guid.Full, targetObject.Guid.Full); ConfirmationManager.AddConfirmation(confirm); player.Session.Network.EnqueueSend(new GameEventConfirmationRequest(player.Session, (ConfirmationType)emoteAction.Stat, confirm.ConfirmationID, confirm.Message)); } } break; case EmoteType.RemoveContract: if (player != null) { player.HandleActionAbandonContract((uint)emoteAction.Stat); } break; case EmoteType.RemoveVitaePenalty: if (player != null) { player.VitaeCpPool = 0; // TODO: call full path } break; case EmoteType.ResetHomePosition: //creature = sourceObject as Creature; //if (creature != null) // creature.Home = emoteAction.Position; break; case EmoteType.Say: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject?.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Emote)); }); break; case EmoteType.SetAltRacialSkills: break; case EmoteType.SetBoolStat: targetObject.SetProperty((PropertyBool)emoteAction.Stat, emoteAction.Amount == 0 ? false : true); break; case EmoteType.SetEyePalette: if (creature != null) { creature.EyesPaletteDID = (uint)emoteAction.Display; } break; case EmoteType.SetEyeTexture: if (creature != null) { creature.EyesTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetFloatStat: targetObject.SetProperty((PropertyFloat)emoteAction.Stat, (float)emoteAction.Amount); break; case EmoteType.SetHeadObject: if (creature != null) { creature.HeadObjectDID = (uint)emoteAction.Display; } break; case EmoteType.SetHeadPalette: break; case EmoteType.SetInt64Stat: player.SetProperty((PropertyInt)emoteAction.Stat, (int)emoteAction.Amount); break; case EmoteType.SetIntStat: player.SetProperty((PropertyInt)emoteAction.Stat, (int)emoteAction.Amount); break; case EmoteType.SetMouthPalette: break; case EmoteType.SetMouthTexture: creature = sourceObject as Creature; if (creature != null) { creature.MouthTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetMyQuestBitsOff: break; case EmoteType.SetMyQuestBitsOn: break; case EmoteType.SetMyQuestCompletions: break; case EmoteType.SetNosePalette: break; case EmoteType.SetNoseTexture: creature = sourceObject as Creature; if (creature != null) { creature.NoseTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetQuestBitsOff: break; case EmoteType.SetQuestBitsOn: break; case EmoteType.SetQuestCompletions: break; case EmoteType.SetSanctuaryPosition: //if (player != null) //player.Sanctuary = emote.Position; break; case EmoteType.Sound: targetObject.EnqueueBroadcast(new GameMessageSound(targetObject.Guid, (Sound)emoteAction.Sound, 1.0f)); break; case EmoteType.SpendLuminance: if (player != null) { player.SpendLuminance((long)emoteAction.Amount); } break; case EmoteType.StampFellowQuest: break; case EmoteType.StampMyQuest: break; case EmoteType.StampQuest: // work needs to be done here if (player != null) { player.QuestManager.Add(emoteAction.Message); } break; case EmoteType.StartBarber: break; case EmoteType.StartEvent: EventManager.StartEvent(emoteAction.Message); break; case EmoteType.StopEvent: EventManager.StopEvent(emoteAction.Message); break; case EmoteType.TakeItems: if (player != null && emoteAction.WeenieClassId != null) { item = WorldObjectFactory.CreateNewWorldObject((uint)emoteAction.WeenieClassId); if (item == null) { break; } success = player.TryRemoveItemFromInventoryWithNetworking(item, (ushort)emoteAction.Amount); } break; case EmoteType.TeachSpell: if (player != null) { player.LearnSpellWithNetworking((uint)emoteAction.SpellId); } break; case EmoteType.TeleportSelf: //if (WorldObject is Player) //(WorldObject as Player).Teleport(emote.Position); break; case EmoteType.TeleportTarget: //if (player != null) //player.Teleport(emote.Position); break; case EmoteType.Tell: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { player.Session.Network.EnqueueSend(new GameMessageHearDirectSpeech(sourceObject, emoteAction.Message, player, ChatMessageType.Tell)); }); break; case EmoteType.TellFellow: text = emoteAction.Message; if (player != null) { var fellowship = player.Fellowship; if (fellowship == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Tell)); } else { foreach (var fellow in fellowship.FellowshipMembers) { fellow.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Tell)); } } } break; case EmoteType.TextDirect: if (player != null) { // should these delays be moved to 1 place?? actionChain.AddDelaySeconds(emoteAction.Delay); text = emoteAction.Message; // no known instances of replace tokens in current text, but could be added in future actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); }); } break; case EmoteType.Turn: if (creature != null) { actionChain.AddDelaySeconds(emoteAction.Delay); var pos = new Position(creature.Location.Cell, creature.Location.PositionX, creature.Location.PositionY, creature.Location.PositionZ, emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 0); actionChain.AddAction(creature, () => { creature.TurnTo(pos); }); var rotateTime = creature.GetRotateDelay(pos); actionChain.AddDelaySeconds(rotateTime); } break; case EmoteType.TurnToTarget: if (creature != null && targetCreature != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(creature, () => { creature.Rotate(targetCreature); }); var rotateTime = creature.GetRotateDelay(targetCreature); actionChain.AddDelaySeconds(rotateTime); } break; case EmoteType.UntrainSkill: if (player != null) { player.UntrainSkill((Skill)emoteAction.Stat, 1); } break; case EmoteType.UpdateFellowQuest: break; case EmoteType.UpdateMyQuest: break; case EmoteType.UpdateQuest: // only delay seems to be with test NPC here // still, unsafe to use any emotes directly outside of a chain, // as they could be executed out-of-order if (player != null) { var questName = emoteAction.Message; player.QuestManager.Add(questName); var hasQuest = player.QuestManager.HasQuest(questName); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.WorldBroadcast: if (player != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { player.Session.Network.EnqueueSend(new GameMessageHearDirectSpeech(sourceObject, emoteAction.Message, player, ChatMessageType.WorldBroadcast)); }); } break; default: log.Debug($"EmoteManager.Execute - Encountered Unhandled EmoteType {(EmoteType)emoteAction.Type} for {sourceObject.Name} ({sourceObject.WeenieClassId})"); break; } }
/// <summary> /// Method used to perform the animation, sound, and vital update on consumption of food or potions /// </summary> /// <param name="consumableName">Name of the consumable</param> /// <param name="sound">Either Sound.Eat1 or Sound.Drink1</param> /// <param name="buffType">ConsumableBuffType.Spell,ConsumableBuffType.Health,ConsumableBuffType.Stamina,ConsumableBuffType.Mana</param> /// <param name="boostAmount">Amount the Vital is boosted by; can be null, if buffType = ConsumableBuffType.Spell</param> /// <param name="spellDID">Id of the spell cast by the consumable; can be null, if buffType != ConsumableBuffType.Spell</param> public void ApplyComsumable(string consumableName, Sound sound, ConsumableBuffType buffType, uint?boostAmount, uint?spellDID) { uint spellId = spellDID ?? 0; GameMessageSystemChat buffMessage; MotionCommand motionCommand; if (sound == Sound.Eat1) { motionCommand = MotionCommand.Eat; } else { motionCommand = MotionCommand.Drink; } var soundEvent = new GameMessageSound(Guid, sound, 1.0f); var motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem(motionCommand)); EnqueueBroadcastMotion(motion); if (buffType == ConsumableBuffType.Spell) { bool result = false; if (spellId != 0) { result = CreateSingleSpell(spellId); } var spell = new Server.Entity.Spell(spellId); if (!result) { buffMessage = new GameMessageSystemChat($"Consuming {consumableName} attempted to apply a spell not yet fully implemented.", ChatMessageType.System); } else { buffMessage = new GameMessageSystemChat($"{consumableName} applies {spell.Name} on you.", ChatMessageType.Craft); } } else { CreatureVital creatureVital; string vitalName; // Null check for safety if (boostAmount == null) { boostAmount = 0; } switch (buffType) { case ConsumableBuffType.Health: creatureVital = Health; vitalName = "Health"; break; case ConsumableBuffType.Mana: creatureVital = Mana; vitalName = "Mana"; break; default: creatureVital = Stamina; vitalName = "Stamina"; break; } var vitalChange = UpdateVitalDelta(creatureVital, (uint)boostAmount); buffMessage = new GameMessageSystemChat($"You regain {vitalChange} {vitalName}.", ChatMessageType.Craft); } Session.Network.EnqueueSend(soundEvent, buffMessage); // Wait for animation var motionChain = new ActionChain(); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId); var motionAnimationLength = motionTable.GetAnimationLength(MotionCommand.Eat); motionChain.AddDelaySeconds(motionAnimationLength); // Return to standing position after the animation delay motionChain.AddAction(this, () => EnqueueBroadcastMotion(new UniversalMotion(MotionStance.NonCombat))); motionChain.EnqueueChain(); }
/// <summary> /// Called every ~5 secs for equipped mana consuming items /// </summary> public void ManaConsumersTick() { if (!EquippedObjectsLoaded) { return; } var EquippedManaConsumers = EquippedObjects.Where(k => (k.Value.IsAffecting ?? false) && //k.Value.ManaRate.HasValue && k.Value.ItemMaxMana.HasValue && k.Value.ItemCurMana.HasValue && k.Value.ItemCurMana.Value > 0).ToList(); foreach (var k in EquippedManaConsumers) { var item = k.Value; // this was a bug in lootgen until 7/11/2019, mostly for clothing/armor/shields // tons of existing items on servers are in this bugged state, where they aren't ticking mana. // this retroactively fixes them when equipped // items such as Impious Staff are excluded from this via IsAffecting if (item.ManaRate == null) { item.ManaRate = LootGenerationFactory.GetManaRate(item); log.Warn($"{Name}.ManaConsumersTick(): {k.Value.Name} ({k.Value.Guid}) fixed missing ManaRate"); } var rate = item.ManaRate.Value; if (LumAugItemManaUsage != 0) { rate *= GetNegativeRatingMod(LumAugItemManaUsage * 5); } if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.UtcNow; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.UtcNow - mostRecentBurn).TotalSeconds; var delta = secondsSinceLastBurn / timePerBurn; var deltaChopped = (int)Math.Floor(delta); var deltaExtra = delta - deltaChopped; if (deltaChopped <= 0) { continue; } var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn); item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd); var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped); deltaChopped = Math.Clamp(deltaChopped, 0, 10); item.ItemCurMana -= deltaChopped; if (item.ItemCurMana < 1 || item.ItemCurMana == null) { item.IsAffecting = false; var msg = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic); var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted); Session.Network.EnqueueSend(msg, sound); if (item.WielderId != null) { if (item.Biota.PropertiesSpellBook != null) { // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { foreach (var spellId in item.Biota.GetKnownSpellsIds(item.BiotaDatabaseLock)) { RemoveItemSpell(item, (uint)spellId); } }); actionChain.EnqueueChain(); } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.UtcNow - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.UtcNow; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
public bool HandleActionRaiseAttribute(PropertyAttribute attribute, uint amount) { if (!Attributes.TryGetValue(attribute, out var creatureAttribute)) { log.Error($"{Name}.HandleActionRaiseAttribute({attribute}, {amount}) - invalid attribute"); return(false); } if (amount > AvailableExperience) { log.Error($"{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 void HandleGameAction(QueuedGameAction action, Player player) { switch (action.ActionType) { case GameActionType.TalkDirect: { // TODO: remove this hack (using TalkDirect) ASAP var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId)); HandleDeathMessage(obj, d); break; } case GameActionType.TeleToHouse: case GameActionType.TeleToLifestone: case GameActionType.TeleToMansion: case GameActionType.TeleToMarketPlace: case GameActionType.TeleToPkArena: case GameActionType.TeleToPklArena: { player.Teleport(action.ActionLocation); break; } case GameActionType.ApplyVisualEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var particleEffect = (PlayScript)action.SecondaryObjectId; HandleParticleEffectEvent(obj, particleEffect); break; } case GameActionType.ApplySoundEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var soundEffect = (Sound)action.SecondaryObjectId; HandleSoundEvent(obj, soundEffect); break; } case GameActionType.IdentifyObject: { // TODO: Throttle this request. The live servers did this, likely for a very good reason, so we should, too. var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var identifyResponse = new GameEventIdentifyObjectResponse(player.Session, action.ObjectId, obj); player.Session.Network.EnqueueSend(identifyResponse); break; } case GameActionType.Buy: { // todo: lots, need vendor list, money checks, etc. var money = new GameMessagePrivateUpdatePropertyInt(player.Session, PropertyInt.CoinValue, 4000); var sound = new GameMessageSound(player.Guid, Sound.PickUpItem, 1); var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(money, sound, sendUseDoneEvent); // send updated vendor inventory. player.Session.Network.EnqueueSend(new GameEventApproachVendor(player.Session, action.ObjectId)); // this is just some testing code for now. foreach (ItemProfile item in action.ProfileItems) { // todo: something with vendor id and profile list... iid list from vendor dbs. // todo: something with amounts.. if (item.Iid == 5) { while (item.Amount > 0) { item.Amount--; WorldObject loot = LootGenerationFactory.CreateTestWorldObject(5090); LootGenerationFactory.AddToContainer(loot, player); player.TrackObject(loot); } var rudecomment = "Who do you think you are, Johny Apple Seed ?"; var buyrudemsg = new GameMessageSystemChat(rudecomment, ChatMessageType.Tell); player.Session.Network.EnqueueSend(buyrudemsg); } else if (item.Iid == 10) { while (item.Amount > 0) { item.Amount--; WorldObject loot = LootGenerationFactory.CreateTestWorldObject(30537); LootGenerationFactory.AddToContainer(loot, player); player.TrackObject(loot); } var rudecomment = "That smells awful, Enjoy eating it!"; var buyrudemsg = new GameMessageSystemChat(rudecomment, ChatMessageType.Tell); player.Session.Network.EnqueueSend(buyrudemsg); } } break; } case GameActionType.PutItemInContainer: { var playerGuid = new ObjectGuid(action.ObjectId); var inventoryGuid = new ObjectGuid(action.SecondaryObjectId); // Has to be a player so need to check before I make the cast. // If he is not a player, something is bad wrong. Og II if (playerGuid.IsPlayer()) { var aPlayer = (Player)GetWorldObject(playerGuid); var inventoryItem = GetWorldObject(inventoryGuid); float arrivedRadiusSquared = 0.00f; bool validGuids; if (WithinUseRadius(playerGuid, inventoryGuid, out arrivedRadiusSquared, out validGuids)) { aPlayer.NotifyAndAddToInventory(inventoryItem); } else { if (validGuids) { aPlayer.SetDestinationInformation(inventoryItem.PhysicsData.Position, arrivedRadiusSquared); aPlayer.BlockedGameAction = action; aPlayer.OnAutonomousMove(inventoryItem.PhysicsData.Position, aPlayer.Sequences, MovementTypes.MoveToObject, inventoryGuid); } } } break; } case GameActionType.DropItem: { var g = new ObjectGuid(action.ObjectId); if (worldObjects.ContainsKey(g)) { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { var aPlayer = (Player)worldObjects[playerId]; aPlayer.NotifyAndDropItem(inventoryId); } } break; } case GameActionType.MovementEvent: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var motion = action.Motion; HandleMovementEvent(obj, motion); break; } case GameActionType.ObjectCreate: { AddWorldObject(action.WorldObject); break; } case GameActionType.ObjectDelete: { RemoveWorldObject(action.WorldObject.Guid, false); break; } case GameActionType.QueryHealth: { if (action.ObjectId == 0) { // Deselect the formerly selected Target player.SelectedTarget = 0; break; } object target = null; var targetId = new ObjectGuid(action.ObjectId); // Remember the selected Target player.SelectedTarget = action.ObjectId; // TODO: once items are implemented check if there are items that can trigger // the QueryHealth event. So far I believe it only gets triggered for players and creatures if (targetId.IsPlayer() || targetId.IsCreature()) { if (worldObjects.ContainsKey(targetId)) { target = worldObjects[targetId]; } if (target == null) { // check adjacent landblocks for the targetId foreach (var block in adjacencies) { if (block.Value != null) { if (block.Value.worldObjects.ContainsKey(targetId)) { target = block.Value.worldObjects[targetId]; } } } } if (target != null) { float healthPercentage = 0; if (targetId.IsPlayer()) { Player tmpTarget = (Player)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } if (targetId.IsCreature()) { Creature tmpTarget = (Creature)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage); player.Session.Network.EnqueueSend(updateHealth); } } break; } case GameActionType.Use: { var g = new ObjectGuid(action.ObjectId); if (worldObjects.ContainsKey(g)) { WorldObject obj = worldObjects[g]; if ((obj.DescriptionFlags & ObjectDescriptionFlag.LifeStone) != 0) { (obj as Lifestone).OnUse(player); } else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Portal) != 0) { // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse. (obj as Portal).OnCollide(player); } else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Door) != 0) { (obj as Door).OnUse(player); } else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Vendor) != 0) { (obj as Vendor).OnUse(player); } // switch (obj.Type) // { // case Enum.ObjectType.Portal: // { // // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse. // // (obj as Portal).OnCollide(player); // // break; // } // case Enum.ObjectType.LifeStone: // { // (obj as Lifestone).OnUse(player); // break; // } // } } break; } } }