public void HandleActionUseOnTarget(Player player, WorldObject target) { ActionChain chain = new ActionChain(); chain.AddAction(player, () => { if (player == null) { return; } if (!player.IsWithinUseRadiusOf(target)) { player.DoMoveTo(target); } else { var sendUseDoneEvent = new GameEventUseDone(player.Session); if (target.WeenieType == WeenieType.Door) { Door door = target as Door; Door.UnlockDoorResults results = door.UnlockDoor(KeyCode); switch (results) { case Entity.Door.UnlockDoorResults.UnlockSuccess: Structure--; if (Structure < 1) { player.HandleActionRemoveItemFromInventory(Guid.Full, player.Guid.Full, 1); } player.Session.Network.EnqueueSend(sendUseDoneEvent); player.Session.Network.EnqueueSend(new GameMessagePublicUpdatePropertyInt(this.Sequences, Guid, PropertyInt.Structure, (uint)Structure)); break; case Entity.Door.UnlockDoorResults.DoorOpen: var messageDoorOpen = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_0481); // TODO: Messages are not quiet right. Need to find right one. player.Session.Network.EnqueueSend(sendUseDoneEvent, messageDoorOpen); break; case Entity.Door.UnlockDoorResults.AlreadyUnlocked: var messageAlreadyUnlocked = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04B2); // TODO: Messages are not quiet right. Need to find right one. player.Session.Network.EnqueueSend(sendUseDoneEvent, messageAlreadyUnlocked); break; default: var messageIncorrectKey = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04B2); player.Session.Network.EnqueueSend(sendUseDoneEvent, messageIncorrectKey); break; } } else if (target.WeenieType == WeenieType.Chest) { var message = new GameMessageSystemChat($"Unlocking {target.Name} has not been implemented, yet!", ChatMessageType.System); player.Session.Network.EnqueueSend(sendUseDoneEvent, message); } else { var message = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_0480); player.Session.Network.EnqueueSend(sendUseDoneEvent, message); } } }); chain.EnqueueChain(); }
public void HandleActionUseOnTarget(Player player, WorldObject target) { ActionChain chain = new ActionChain(); chain.AddAction(player, () => { if (target.WeenieType == WeenieType.Door) { var door = (Door)target; if (player.Skills[Skill.Lockpick].Status != SkillStatus.Trained && player.Skills[Skill.Lockpick].Status != SkillStatus.Specialized) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking)); return; } void consume(Player plr, Lockpick lp) { lp.Structure--; if (lp.Structure < 1) { plr.TryRemoveItemFromInventoryWithNetworking(this, 1); } plr.Session.Network.EnqueueSend(new GameEventUseDone(plr.Session)); plr.Session.Network.EnqueueSend(new GameMessagePublicUpdatePropertyInt(lp, PropertyInt.Structure, (int)lp.Structure)); plr.Session.Network.EnqueueSend(new GameMessageSystemChat($"Uses reamaining: {Structure}", ChatMessageType.System)); } Door.UnlockDoorResults results = door.UnlockDoor(player.Skills[Skill.Lockpick].Current); switch (results) { case Door.UnlockDoorResults.UnlockSuccess: consume(player, this); break; case Door.UnlockDoorResults.DoorOpen: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen)); break; case Door.UnlockDoorResults.AlreadyUnlocked: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked)); break; case Door.UnlockDoorResults.PickLockFailed: consume(player, this); break; default: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock)); break; } } else if (target.WeenieType == WeenieType.Chest) { var message = new GameMessageSystemChat($"Unlocking {target.Name} has not been implemented, yet!", ChatMessageType.System); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session), message); } else { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat)); } }); chain.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 // this bug should hopefully be fixed by now, commenting out this block /*if (item.ManaRate == null) * { * if ((item.MaxStackSize ?? 1) > 1) * continue; * * var maxBaseMana = LootGenerationFactory.GetMaxBaseMana(item); * * item.ManaRate = LootGenerationFactory.CalculateManaRate(maxBaseMana); * * 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)); } } } }
/// <summary> /// Called for a spell projectile to damage its target /// </summary> public void DamageTarget(WorldObject _target, double?damage, bool critical) { var player = ProjectileSource as Player; var target = _target as Creature; var targetPlayer = _target as Player; if (targetPlayer != null && ((targetPlayer.Invincible ?? false) || targetPlayer.IsDead)) { return; } { uint amount; var percent = 0.0f; var heritageMod = 1.0f; var sneakAttackMod = 1.0f; // handle life projectiles for stamina / mana if (Spell.School == MagicSchool.LifeMagic && (Spell.Name.Contains("Blight") || Spell.Name.Contains("Tenacity"))) { if (Spell.Name.Contains("Blight")) { percent = (float)damage / targetPlayer.Mana.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Mana, (int)-Math.Round(damage.Value)); } else { percent = (float)damage / targetPlayer.Stamina.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Stamina, (int)-Math.Round(damage.Value)); } } else { // for possibly applying sneak attack to magic projectiles, // only do this for health-damaging projectiles? if (player != null) { // TODO: use target direction vs. projectile position, instead of player position // could sneak attack be applied to void DoTs? sneakAttackMod = player.GetSneakAttackMod(target); //Console.WriteLine("Magic sneak attack: + sneakAttackMod); heritageMod = player.GetHeritageBonus(WeaponType.Magic) ? 1.05f : 1.0f; } // DR / DRR applies for magic too? var creatureSource = ProjectileSource as Creature; var damageRating = creatureSource != null?creatureSource.GetDamageRating() : 0; var damageRatingMod = Creature.AdditiveCombine(Creature.GetPositiveRatingMod(damageRating), heritageMod, sneakAttackMod); var damageResistRatingMod = Creature.GetNegativeRatingMod(target.GetDamageResistRating()); damage *= damageRatingMod * damageResistRatingMod; //Console.WriteLine($"Damage rating: " + Creature.ModToRating(damageRatingMod)); percent = (float)damage / target.Health.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Health, (int)-Math.Round(damage.Value)); target.DamageHistory.Add(ProjectileSource, Spell.DamageType, amount); if (targetPlayer != null && targetPlayer.Fellowship != null) { targetPlayer.Fellowship.OnVitalUpdate(targetPlayer); } } amount = (uint)Math.Round(damage.Value); // full amount for debugging if (critical) { target.EmoteManager.OnReceiveCritical(player); } if (target.IsAlive) { string verb = null, plural = null; Strings.GetAttackVerb(Spell.DamageType, percent, ref verb, ref plural); var type = Spell.DamageType.GetName().ToLower(); var critMsg = critical ? "Critical hit! " : ""; var sneakMsg = sneakAttackMod > 1.0f ? "Sneak Attack! " : ""; if (player != null) { var attackerMsg = new GameMessageSystemChat($"{critMsg}{sneakMsg}You {verb} {target.Name} for {amount} points with {Spell.Name}.", ChatMessageType.Magic); var updateHealth = new GameEventUpdateHealth(player.Session, target.Guid.Full, (float)target.Health.Current / target.Health.MaxValue); player.Session.Network.EnqueueSend(attackerMsg, updateHealth); } if (targetPlayer != null) { targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"{critMsg}{sneakMsg}{ProjectileSource.Name} {plural} you for {amount} points with {Spell.Name}.", ChatMessageType.Magic)); } } else { target.OnDeath(ProjectileSource, Spell.DamageType, critical); target.Die(); } } }
public void DoWorldBroadcast(string message, ChatMessageType messageType) { GameMessageSystemChat sysMessage = new GameMessageSystemChat(message, messageType); WorldManager.BroadcastToAll(sysMessage); }
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); }
public static void UseObjectOnTarget(Player player, WorldObject source, WorldObject target) { if (source == target) { var message = new GameMessageSystemChat($"The {source.Name} cannot be combined with itself.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } var recipe = DatabaseManager.World.GetCachedCookbook(source.WeenieClassId, target.WeenieClassId); if (recipe == null) { var message = new GameMessageSystemChat($"The {source.Name} cannot be used on the {target.Name}.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } // verify requirements if (!VerifyRequirements(recipe.Recipe, player, source, target)) { player.SendUseDoneEvent(WeenieError.YouDoNotPassCraftingRequirements); return; } if (source.ItemType == ItemType.TinkeringMaterial) { HandleTinkering(player, source, target); return; } ActionChain craftChain = new ActionChain(); CreatureSkill skill = null; bool success = true; // assume success, unless there's a skill check double percentSuccess = 1; var motion = new Motion(MotionStance.NonCombat, MotionCommand.ClapHands); craftChain.AddAction(player, () => player.EnqueueBroadcastMotion(motion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(player.MotionTableId); var craftAnimationLength = motionTable.GetAnimationLength(MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); craftChain.AddAction(player, () => { if (recipe.Recipe.Skill > 0 && recipe.Recipe.Difficulty > 0) { // there's a skill associated with this Skill skillId = (Skill)recipe.Recipe.Skill; // this shouldn't happen, but sanity check for unexpected nulls skill = player.GetCreatureSkill(skillId); if (skill == null) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } //Console.WriteLine("Skill difficulty: " + recipe.Recipe.Difficulty); percentSuccess = skill.GetPercentSuccess(recipe.Recipe.Difficulty); //FIXME: Pretty certain this is broken } if (skill != null) { // check for pre-MoA skill // convert into appropriate post-MoA skill // pre-MoA melee weapons: get highest melee weapons skill var newSkill = player.ConvertToMoASkill(skill.Skill); skill = player.GetCreatureSkill(newSkill); //Console.WriteLine("Required skill: " + skill.Skill); if (skill.AdvancementClass <= SkillAdvancementClass.Untrained) { var message = new GameEventWeenieError(player.Session, WeenieError.YouAreNotTrainedInThatTradeSkill); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(WeenieError.YouAreNotTrainedInThatTradeSkill); return; } } // perform skill check, if applicable if (skill != null) { success = ThreadSafeRandom.Next(0.0f, 1.0f) <= percentSuccess; } CreateDestroyItems(player, recipe.Recipe, source, target, success); player.SendUseDoneEvent(); }); craftChain.EnqueueChain(); }
/// <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} applies {spell.Name} on you.", ChatMessageType.Craft); } 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); 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(); }
/// <summary> /// Determines if the player has advanced a level /// </summary> private void CheckForLevelup() { var xpTable = DatManager.PortalDat.XpTable; var maxLevel = GetMaxLevel(); if (Level == maxLevel) { return; } var startingLevel = Level; bool creditEarned = false; // increases until the correct level is found while ((ulong)(TotalExperience ?? 0) >= xpTable.CharacterLevelXPList[(Level ?? 0) + 1]) { Level++; // increase the skill credits if the chart allows this level to grant a credit if (xpTable.CharacterLevelSkillCreditList[Level ?? 0] > 0) { AvailableSkillCredits += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0]; TotalSkillCredits += (int)xpTable.CharacterLevelSkillCreditList[Level ?? 0]; creditEarned = true; } // break if we reach max if (Level == maxLevel) { PlayParticleEffect(ACE.Entity.Enum.PlayScript.WeddingBliss, Guid); break; } } if (Level > startingLevel) { string levelUpMessageText = (Level == maxLevel) ? $"You have reached the maximum level of {Level}!" : $"You are now level {Level}!"; var levelUpMessage = new GameMessageSystemChat(levelUpMessageText, ChatMessageType.Advancement); string xpUpdateText = (AvailableSkillCredits > 0) ? $"You have {AvailableExperience:#,###0} experience points and {AvailableSkillCredits} skill credits available to raise skills and attributes." : $"You have {AvailableExperience:#,###0} experience points available to raise skills and attributes."; var xpUpdateMessage = new GameMessageSystemChat(xpUpdateText, ChatMessageType.Advancement); var levelUp = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.Level, Level ?? 1); var currentCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0); if (Level != maxLevel && !creditEarned) { var nextLevelWithCredits = 0; for (int i = (Level ?? 0) + 1; i <= maxLevel; i++) { if (xpTable.CharacterLevelSkillCreditList[i] > 0) { nextLevelWithCredits = i; break; } } string nextCreditAtText = $"You will earn another skill credit at {nextLevelWithCredits}"; var nextCreditMessage = new GameMessageSystemChat(nextCreditAtText, ChatMessageType.Advancement); Session.Network.EnqueueSend(levelUp, levelUpMessage, xpUpdateMessage, currentCredits, nextCreditMessage); } else { Session.Network.EnqueueSend(levelUp, levelUpMessage, xpUpdateMessage, currentCredits); } if (Fellowship != null) { Fellowship.OnFellowLevelUp(); } // play level up effect PlayParticleEffect(ACE.Entity.Enum.PlayScript.LevelUp, Guid); } }
private static void HandleHealingRecipe(Player player, WorldObject source, WorldObject target, AceRecipe recipe) { ActionChain chain = new ActionChain(); // skill will be null since the difficulty is calculated manually if (recipe.SkillId == null) { log.Warn($"healing recipe has null skill id (should almost certainly be healing, but who knows). recipe id {recipe.RecipeGuid}."); player.SendUseDoneEvent(); return; } if (!(target is Player)) { var message = new GameMessageSystemChat($"The {source.Name} cannot be used on {target.Name}.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } Player targetPlayer = (Player)target; //Ability vital = (Ability?)recipe.HealingAttribute ?? Ability.Health; // there's a skill associated with this Skill skillId = (Skill)recipe.SkillId.Value; var skill = player.GetCreatureSkill(skillId); // this shouldn't happen, but sanity check for unexpected nulls if (skill == null) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } // at this point, we've validated that the target is a player, and the target is below max health if (target.Guid != player.Guid) { // TODO: validate range } MotionCommand cmd = MotionCommand.SkillHealSelf; if (target.Guid != player.Guid) { cmd = MotionCommand.Woah; // guess? nothing else stood out } // everything pre-validatable is validated. action will be attempted unless cancelled, so // queue up the animation and action UniversalMotion motion = new UniversalMotion(MotionStance.Standing, new MotionItem(cmd)); chain.AddAction(player, () => player.HandleActionMotion(motion)); chain.AddDelaySeconds(0.5); //chain.AddAction(player, () => //{ // // TODO: revalidate range if other player (they could have moved) // double difficulty = 2 * (targetPlayer.Vitals[vital].MaxValue - targetPlayer.Vitals[vital].Current); // if (difficulty <= 0) // { // // target is at max (or higher?) health, do nothing // var text = "You are already at full health."; // if (target.Guid != player.Guid) // text = $"{target.Name} is already at full health"; // player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Craft)); // player.SendUseDoneEvent(); // return; // } // if (player.CombatMode != CombatMode.NonCombat && player.CombatMode != CombatMode.Undef) // difficulty *= 1.1; // int boost = source.Boost ?? 0; // double multiplier = source.HealkitMod ?? 1; // double playerSkill = skill.Current + boost; // if (skill.Status == SkillStatus.Trained) // playerSkill *= 1.1; // else if (skill.Status == SkillStatus.Specialized) // playerSkill *= 1.5; // // usage is inevitable at this point, consume the use // if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemUsesDecrement) > 0) // { // if (source.Structure <= 1) // player.DestroyInventoryItem(source); // else // { // source.Structure--; // source.SendPartialUpdates(player.Session, _updateStructure); // } // } // double percentSuccess = CreatureSkill.GetPercentSuccess((uint)playerSkill, (uint)difficulty); // if (_random.NextDouble() <= percentSuccess) // { // string expertly = ""; // if (_random.NextDouble() < 0.1d) // { // expertly = "expertly "; // multiplier *= 1.2; // } // // calculate amount restored // uint maxRestore = targetPlayer.Vitals[vital].MaxValue - targetPlayer.Vitals[vital].Current; // // TODO: get actual forumula for healing. this is COMPLETELY wrong. this is 60 + random(1-60). // double amountRestored = 60d + _random.Next(1, 61); // amountRestored *= multiplier; // uint actualRestored = (uint)Math.Min(maxRestore, amountRestored); // targetPlayer.Vitals[vital].Current += actualRestored; // var updateVital = new GameMessagePrivateUpdateAttribute2ndLevel(player.Session, vital.GetVital(), targetPlayer.Vitals[vital].Current); // player.Session.Network.EnqueueSend(updateVital); // if (targetPlayer.Guid != player.Guid) // { // // tell the other player they got healed // var updateVitalToTarget = new GameMessagePrivateUpdateAttribute2ndLevel(targetPlayer.Session, vital.GetVital(), targetPlayer.Vitals[vital].Current); // targetPlayer.Session.Network.EnqueueSend(updateVitalToTarget); // } // string name = "yourself"; // if (targetPlayer.Guid != player.Guid) // name = targetPlayer.Name; // string vitalName = "Health"; // if (vital == Ability.Stamina) // vitalName = "Stamina"; // else if (vital == Ability.Mana) // vitalName = "Mana"; // string uses = source.Structure == 1 ? "use" : "uses"; // var text = string.Format(recipe.SuccessMessage, expertly, name, actualRestored, vitalName, source.Name, source.Structure, uses); // var message = new GameMessageSystemChat(text, ChatMessageType.Craft); // player.Session.Network.EnqueueSend(message); // if (targetPlayer.Guid != player.Guid) // { // // send text to the other player too // text = string.Format(recipe.AlternateMessage, player.Name, expertly, actualRestored, vitalName); // message = new GameMessageSystemChat(text, ChatMessageType.Craft); // targetPlayer.Session.Network.EnqueueSend(message); // } // } // player.SendUseDoneEvent(); //}); //chain.EnqueueChain(); }
private static void HandleCreateItemRecipe(Player player, WorldObject source, WorldObject target, AceRecipe recipe) { ActionChain craftChain = new ActionChain(); CreatureSkill skill = null; bool skillSuccess = true; // assume success, unless there's a skill check double percentSuccess = 1; UniversalMotion motion = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.ClapHands)); craftChain.AddAction(player, () => player.HandleActionMotion(motion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)player.MotionTableId); var craftAnimationLength = motionTable.GetAnimationLength(MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); // craftChain.AddDelaySeconds(0.5); craftChain.AddAction(player, () => { if (recipe.SkillId != null && recipe.SkillDifficulty != null) { // there's a skill associated with this Skill skillId = (Skill)recipe.SkillId.Value; // this shouldn't happen, but sanity check for unexpected nulls skill = player.GetCreatureSkill(skillId); if (skill == null) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } percentSuccess = skill.GetPercentSuccess(recipe.SkillDifficulty.Value); } // straight skill check, if applciable if (skill != null) { skillSuccess = _random.NextDouble() < percentSuccess; } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemDestroyed) > 0) { player.TryDestroyFromInventoryWithNetworking(source); } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemDestroyed) > 0) { player.TryDestroyFromInventoryWithNetworking(target); } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemUsesDecrement) > 0) { if (source.Structure <= 1) { player.TryDestroyFromInventoryWithNetworking(source); } else { source.Structure--; source.SendPartialUpdates(player.Session, _updateStructure); } } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemUsesDecrement) > 0) { if (target.Structure <= 1) { player.TryDestroyFromInventoryWithNetworking(target); } else { target.Structure--; target.SendPartialUpdates(player.Session, _updateStructure); } } if (skillSuccess) { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem1) > 0 && recipe.SuccessItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.SuccessItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem2) > 0 && recipe.SuccessItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.SuccessItem2Wcid.Value); } var text = string.Format(recipe.SuccessMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } else { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem1) > 0 && recipe.FailureItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.FailureItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem2) > 0 && recipe.FailureItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.FailureItem2Wcid.Value); } var text = string.Format(recipe.FailMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } player.SendUseDoneEvent(); }); craftChain.EnqueueChain(); }
public override void OnUse(Session session) { bool success = true; string failReason = "You are unable to read the scroll."; switch (Power) { // research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling case spellLevel2: // Level 2 case spellLevel3: // Level 3 case spellLevel4: // Level 4 case spellLevel5: // Level 5 case spellLevel6: // Level 6 if (session.Player.CanReadScroll(School, Power)) { success = true; } else { success = false; failReason = "You are not skilled enough in the inscribed spell's school of magic to understand the writing on this scroll."; } break; default: // Level 1 or Level 7+ never fail success = true; break; } if (!session.Player.UnknownSpell(SpellId)) { success = false; failReason = "You already know the spell inscribed upon this scroll."; } ActionChain readScrollChain = new ActionChain(); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReading)); readScrollChain.AddDelaySeconds(2); if (success) { readScrollChain.AddAction(session.Player, () => session.Player.HandleActionLearnSpell(SpellId)); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady)); var removeObjMessage = new GameMessageRemoveObject(this); var destroyMessage = new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Magic); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(destroyMessage, removeObjMessage)); readScrollChain.AddAction(session.Player, () => session.Player.RemoveFromInventory(session.Player.InventoryObjects, Guid)); } else { readScrollChain.AddDelaySeconds(2); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady)); var failMessage = new GameMessageSystemChat($"{failReason}", ChatMessageType.Magic); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(failMessage)); } var sendUseDoneEvent = new GameEventUseDone(session.Player.Session); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(sendUseDoneEvent)); readScrollChain.EnqueueChain(); }
/// <summary> /// Called for a spell projectile to damage its target /// </summary> public void DamageTarget(WorldObject _target, double?damage, bool critical) { var player = ProjectileSource as Player; var target = _target as Creature; var targetPlayer = _target as Player; { uint amount; var percent = 0.0f; var sneakAttackMod = 1.0f; // handle life projectiles for stamina / mana if (Spell.School == MagicSchool.LifeMagic && (Spell.Name.Contains("Blight") || Spell.Name.Contains("Tenacity"))) { if (Spell.Name.Contains("Blight")) { percent = (float)damage / targetPlayer.Mana.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Mana, (int)-Math.Round(damage.Value)); } else { percent = (float)damage / targetPlayer.Stamina.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Stamina, (int)-Math.Round(damage.Value)); } } else { // for possibly applying sneak attack to magic projectiles, // only do this for health-damaging projectiles? if (player != null) { // TODO: use target direction vs. projectile position, instead of player position // could sneak attack be applied to void DoTs? sneakAttackMod = player.GetSneakAttackMod(target); //Console.WriteLine("Magic sneak attack: + sneakAttackMod); damage *= sneakAttackMod; } // DR / DRR applies for magic too? var damageRatingMod = Creature.GetRatingMod(ProjectileSource.EnchantmentManager.GetDamageRating()); var damageResistRatingMod = Creature.GetNegativeRatingMod(target.EnchantmentManager.GetDamageResistRating()); damage *= damageRatingMod * damageResistRatingMod; percent = (float)damage / target.Health.MaxValue; amount = (uint)-target.UpdateVitalDelta(target.Health, (int)-Math.Round(damage.Value)); target.DamageHistory.Add(ProjectileSource, Spell.DamageType, amount); } amount = (uint)Math.Round(damage.Value); // full amount for debugging if (target.IsAlive) { string verb = null, plural = null; Strings.GetAttackVerb(Spell.DamageType, percent, ref verb, ref plural); var type = Spell.DamageType.GetName().ToLower(); var critMsg = critical ? "Critical hit! " : ""; var sneakMsg = sneakAttackMod > 1.0f ? "Sneak Attack! " : ""; if (player != null) { var attackerMsg = new GameMessageSystemChat($"{critMsg}{sneakMsg}You {verb} {target.Name} for {amount} points of {type} damage!", ChatMessageType.Magic); var updateHealth = new GameEventUpdateHealth(player.Session, target.Guid.Full, (float)target.Health.Current / target.Health.MaxValue); player.Session.Network.EnqueueSend(attackerMsg, updateHealth); } if (targetPlayer != null) { targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"{critMsg}{sneakMsg}{ProjectileSource.Name} {plural} you for {amount} points of {type} damage!", ChatMessageType.Magic)); } } else { target.OnDeath(ProjectileSource, Spell.DamageType, critical); target.Die(); if (player != null) { player.EarnXP((long)target.XpOverride); // TODO: refactor to common Creature.OnDeath() } } } }
public override void HandleActionOnUse(ObjectGuid playerId) { string serverMessage; // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage var rangeCheck = 5.0f; ActionChain chain = new ActionChain(); CurrentLandblock.ChainOnObject(chain, playerId, (WorldObject wo) => { Player player = wo as Player; if (player == null) { return; } // Can check location by guid // NOTE: Must use CurrentLandblock.GetPosition() for the portal's position... if (CurrentLandblock.GetPosition(Guid).SquaredDistanceTo(player.Location) < rangeCheck) { if (Destination != null) { #if DEBUG serverMessage = "Checking requirements for " + this.Name; var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); player.Session.Network.EnqueueSend(usePortalMessage); #endif // Check player level -- requires remote query to player (ugh)... if ((player.Level >= MinimumLevel) && ((player.Level <= MaximumLevel) || (MaximumLevel == 0))) { Position portalDest = Destination; switch (WeenieClassId) { /// <summary> /// Setup correct racial portal destination for the Central Courtyard in the Training Academy /// </summary> case (ushort)SpecialPortalWCID.CentralCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiCCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiCCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqCCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqCCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarCCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarCCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgCCLanding); break; } } portalDest.PositionX = Destination.PositionX; portalDest.PositionY = Destination.PositionY; portalDest.PositionZ = Destination.PositionZ; portalDest.RotationX = Destination.RotationX; portalDest.RotationY = Destination.RotationY; portalDest.RotationZ = Destination.RotationZ; portalDest.RotationW = Destination.RotationW; break; } /// <summary> /// Setup correct racial portal destination for the Outer Courtyard in the Training Academy /// </summary> case (ushort)SpecialPortalWCID.OuterCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiOCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiOCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqOCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqOCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarOCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarOCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgOCLanding); break; } } portalDest.PositionX = Destination.PositionX; portalDest.PositionY = Destination.PositionY; portalDest.PositionZ = Destination.PositionZ; portalDest.RotationX = Destination.RotationX; portalDest.RotationY = Destination.RotationY; portalDest.RotationZ = Destination.RotationZ; portalDest.RotationW = Destination.RotationW; break; } /// <summary> /// All other portals don't need adjustments. /// </summary> default: { break; } } #if DEBUG serverMessage = "Portal sending player to destination"; usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); player.Session.Network.EnqueueSend(usePortalMessage); #endif player.Session.Player.Teleport(portalDest); // If the portal just used is able to be recalled to, // save the destination coordinates to the LastPortal character position save table if (IsRecallable) { player.SetCharacterPosition(PositionType.LastPortal, portalDest); } // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } else if ((player.Level > MaximumLevel) && (MaximumLevel != 0)) { // You are too powerful to interact with that portal! var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AC); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent); } else { // You are not powerful enough to interact with that portal! var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AB); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent); } } else { serverMessage = "Portal destination for portal ID " + this.WeenieClassId + " not yet implemented!"; var failedUsePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(failedUsePortalMessage, sendUseDoneEvent); } } else { // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } }); // Run on the player chain.EnqueueChain(); }
public override void HandleActionOnCollide(ObjectGuid playerId) { string serverMessage; Player player = CurrentLandblock.GetObject(playerId) as Player; if (player == null) { return; } if (Destination != null) { #if DEBUG serverMessage = "Checking requirements for " + this.Name; var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); player.Session.Network.EnqueueSend(usePortalMessage); #endif // Check player level -- requires remote query to player (ugh)... if ((player.Level >= MinimumLevel) && ((player.Level <= MaximumLevel) || (MaximumLevel == 0))) { Position portalDest = Destination; switch (WeenieClassId) { // Setup correct racial portal destination for the Central Courtyard in the Training Academy case (ushort)SpecialPortalWCID.CentralCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiCCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiCCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqCCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqCCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarCCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarCCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgCCLanding); break; } } portalDest.PositionX = Destination.PositionX; portalDest.PositionY = Destination.PositionY; portalDest.PositionZ = Destination.PositionZ; portalDest.RotationX = Destination.RotationX; portalDest.RotationY = Destination.RotationY; portalDest.RotationZ = Destination.RotationZ; portalDest.RotationW = Destination.RotationW; break; } // Setup correct racial portal destination for the Outer Courtyard in the Training Academy case (ushort)SpecialPortalWCID.OuterCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiOCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiOCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqOCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqOCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarOCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarOCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgOCLanding); break; } } portalDest.PositionX = Destination.PositionX; portalDest.PositionY = Destination.PositionY; portalDest.PositionZ = Destination.PositionZ; portalDest.RotationX = Destination.RotationX; portalDest.RotationY = Destination.RotationY; portalDest.RotationZ = Destination.RotationZ; portalDest.RotationW = Destination.RotationW; break; } // All other portals don't need adjustments. default: { break; } } #if DEBUG serverMessage = "Portal sending player to destination"; usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); player.Session.Network.EnqueueSend(usePortalMessage); #endif player.Session.Player.Teleport(portalDest); // If the portal just used is able to be recalled to, // save the destination coordinates to the LastPortal character position save table if (IsRecallable) { player.SetCharacterPosition(PositionType.LastPortal, portalDest); } } else if ((player.Level > MaximumLevel) && (MaximumLevel != 0)) { // You are too powerful to interact with that portal! var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.YouAreTooPowerfulToUsePortal); player.Session.Network.EnqueueSend(failedUsePortalMessage); } else { // You are not powerful enough to interact with that portal! var failedUsePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.YouAreNotPowerfulEnoughToUsePortal); player.Session.Network.EnqueueSend(failedUsePortalMessage); } } else { serverMessage = "Portal destination for portal ID " + this.WeenieClassId + " not yet implemented!"; var failedUsePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); player.Session.Network.EnqueueSend(failedUsePortalMessage); } }
/// <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) { 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.Standing, new MotionItem(motionCommand)); DoMotion(motion); if (buffType == ConsumableBuffType.Spell) { // Null check for safety if (spellDID == null) { spellDID = 0; } // TODO: Handle spell cast buffMessage = new GameMessageSystemChat($"Consuming {consumableName} 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); 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, () => DoMotion(new UniversalMotion(MotionStance.Standing))); motionChain.EnqueueChain(); }
/// <summary> /// Sends a broadcast message to the player /// </summary> public void WorldBroadcast(string broadcastMessage) { var worldBroadcastMessage = new GameMessageSystemChat(broadcastMessage, ChatMessageType.Broadcast); Network.EnqueueSend(worldBroadcastMessage); }
public static void UseObjectOnTarget(Player player, WorldObject source, WorldObject target) { var recipe = DatabaseManager.World.GetCachedCookbook(source.WeenieClassId, target.WeenieClassId); if (recipe == null) { var message = new GameMessageSystemChat($"The {source.Name} cannot be used on the {target.Name}.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } ActionChain craftChain = new ActionChain(); CreatureSkill skill = null; bool skillSuccess = true; // assume success, unless there's a skill check double percentSuccess = 1; UniversalMotion motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem(MotionCommand.ClapHands)); craftChain.AddAction(player, () => player.EnqueueBroadcastMotion(motion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(player.MotionTableId); var craftAnimationLength = motionTable.GetAnimationLength(MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); craftChain.AddAction(player, () => { if (recipe.Recipe.Skill > 0 && recipe.Recipe.Difficulty > 0) { // there's a skill associated with this Skill skillId = (Skill)recipe.Recipe.Skill; // this shouldn't happen, but sanity check for unexpected nulls skill = player.GetCreatureSkill(skillId); if (skill == null) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } percentSuccess = skill.GetPercentSuccess(recipe.Recipe.Difficulty); //FIXME: Pretty certain this is broken } if (skill != null) { if (skill.AdvancementClass == SkillAdvancementClass.Untrained) { var message = new GameEventWeenieError(player.Session, WeenieError.YouAreNotTrainedInThatTradeSkill); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(WeenieError.YouAreNotTrainedInThatTradeSkill); return; } } // straight skill check, if applicable if (skill != null) { skillSuccess = _random.NextDouble() < percentSuccess; } if (skillSuccess) { bool destroyTarget = _random.NextDouble() < recipe.Recipe.SuccessDestroyTargetChance; bool destroySource = _random.NextDouble() < recipe.Recipe.SuccessDestroySourceChance; if (destroyTarget) { if (target.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(target, (ushort)recipe.Recipe.SuccessDestroyTargetAmount); } else if (target.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(target)) { throw new Exception($"Failed to remove {target.Name} from player inventory."); } } else { target.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.SuccessDestroyTargetMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.SuccessDestroyTargetMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (destroySource) { if (source.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(source, (ushort)recipe.Recipe.SuccessDestroySourceAmount); } else if (source.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(source)) { throw new Exception($"Failed to remove {source.Name} from player inventory."); } } else { source.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.SuccessDestroySourceMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.SuccessDestroySourceMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (recipe.Recipe.SuccessWCID > 0) { var wo = WorldObjectFactory.CreateNewWorldObject(recipe.Recipe.SuccessWCID); if (wo != null) { if (recipe.Recipe.SuccessAmount > 1) { wo.StackSize = (ushort)recipe.Recipe.SuccessAmount; } player.TryCreateInInventoryWithNetworking(wo); } } var message = new GameMessageSystemChat(recipe.Recipe.SuccessMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } else { bool destroyTarget = _random.NextDouble() < recipe.Recipe.FailDestroyTargetChance; bool destroySource = _random.NextDouble() < recipe.Recipe.FailDestroySourceChance; if (destroyTarget) { if (target.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(target, (ushort)recipe.Recipe.FailDestroyTargetAmount); } else if (target.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(target)) { throw new Exception($"Failed to remove {target.Name} from player inventory."); } } else { target.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.FailDestroyTargetMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.FailDestroyTargetMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (destroySource) { if (source.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(source, (ushort)recipe.Recipe.FailDestroySourceAmount); } else if (source.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(source)) { throw new Exception($"Failed to remove {source.Name} from player inventory."); } } else { source.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.FailDestroySourceMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.FailDestroySourceMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (recipe.Recipe.FailWCID > 0) { var wo = WorldObjectFactory.CreateNewWorldObject(recipe.Recipe.FailWCID); if (wo != null) { if (recipe.Recipe.FailAmount > 1) { wo.StackSize = (ushort)recipe.Recipe.FailAmount; } player.TryCreateInInventoryWithNetworking(wo); } } var message = new GameMessageSystemChat(recipe.Recipe.FailMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } player.SendUseDoneEvent(); }); craftChain.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($"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)); 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, () => { sourceObject.DoMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } else { if (sourceObject.CurrentMotionState.Commands.Count > 0 && sourceObject.CurrentMotionState.Commands[0].Motion == startingMotion.Commands[0].Motion) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(motion); sourceObject.CurrentMotionState = motion; }); 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, () => { sourceObject.DoMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } } } else { var motion = new UniversalMotion(MotionStance.Standing, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(motion); sourceObject.CurrentMotionState = 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.CurrentLandblock?.EnqueueBroadcastSound(targetObject, (Sound)emoteAction.Sound); 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> /// Called when a player dies, in conjunction with Die() /// </summary> /// <param name="lastDamager">The last damager that landed the death blow</param> /// <param name="damageType">The damage type for the death message</param> public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false) { if (DamageHistory.TopDamager is Player pkPlayer) { if (IsPKDeath(pkPlayer)) { pkPlayer.PkTimestamp = Time.GetUnixTime(); pkPlayer.PlayerKillsPk++; var globalPKDe = $"{lastDamager.Name} has defeated {Name}!"; if ((Location.Cell & 0xFFFF) < 0x100) { globalPKDe += $" The kill occured at {Location.GetMapCoordStr()}"; } globalPKDe += "\n[PKDe]"; PlayerManager.BroadcastToAll(new GameMessageSystemChat(globalPKDe, ChatMessageType.Broadcast)); } else if (IsPKLiteDeath(pkPlayer)) { pkPlayer.PlayerKillsPkl++; } } var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit); if (lastDamager != null) { lastDamager.EmoteManager.OnKill(this); } var playerMsg = ""; if (lastDamager != null) { playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name); } else { playerMsg = deathMessage.Victim; } var msgYourDeath = new GameEventYourDeath(Session, playerMsg); Session.Network.EnqueueSend(msgYourDeath); // broadcast to nearby players var nearbyMsg = ""; if (lastDamager != null) { nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name); } else { nearbyMsg = deathMessage.Broadcast; } var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast); log.Info(nearbyMsg); var excludePlayers = new List <Player>(); if (lastDamager is Player lastDamagerPlayer) { excludePlayers.Add(lastDamagerPlayer); } var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg); excludePlayers.AddRange(nearbyPlayers); excludePlayers.Add(this); // exclude self if (Fellowship != null) { Fellowship.OnDeath(this); } // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock) { // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet... // this existing broadcast method is also based on the current visible objects to the player, // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid); // instead, we get all of the players in the lifestone landblock + adjacent landblocks, // and possibly limit that to some radius around the landblock? var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true); lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg); } return(deathMessage); }
public override void OnCollide(Player player) { // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage float rangeCheck = 5.0f; if (player.Location.SquaredDistanceTo(this.Location) < rangeCheck) { PortalDestination portalDestination = DatabaseManager.World.GetPortalDestination(this.WeenieClassid); if (portalDestination != null) { if ((player.Level >= portalDestination.MinLvl) && ((player.Level <= portalDestination.MaxLvl) || (portalDestination.MaxLvl == 0))) { Position portalDest = portalDestination.Position; switch (this.WeenieClassid) { /// <summary> /// Setup correct racial portal destination for the Central Courtyard in the Training Academy /// </summary> case (ushort)SpecialPortalWCID.CentralCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiCCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiCCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqCCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqCCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarCCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarCCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgCCLanding); break; } } portalDest.PositionX = portalDestination.PosX; portalDest.PositionY = portalDestination.PosY; portalDest.PositionZ = portalDestination.PosZ; portalDest.RotationX = portalDestination.QX; portalDest.RotationY = portalDestination.QY; portalDest.RotationZ = portalDestination.QZ; portalDest.RotationW = portalDestination.QW; break; } /// <summary> /// Setup correct racial portal destination for the Outer Courtyard in the Training Academy /// </summary> case (ushort)SpecialPortalWCID.OuterCourtyard: { uint playerLandblockId = player.Location.LandblockId.Raw; switch (playerLandblockId) { case (uint)SpecialPortalLandblockID.ShoushiOCLaunch: // Shoushi { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.ShoushiOCLanding); break; } case (uint)SpecialPortalLandblockID.YaraqOCLaunch: // Yaraq { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.YaraqOCLanding); break; } case (uint)SpecialPortalLandblockID.SanamarOCLaunch: // Sanamar { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.SanamarOCLanding); break; } default: // Holtburg { portalDest.LandblockId = new LandblockId((uint)SpecialPortalLandblockID.HoltburgOCLanding); break; } } portalDest.PositionX = portalDestination.PosX; portalDest.PositionY = portalDestination.PosY; portalDest.PositionZ = portalDestination.PosZ; portalDest.RotationX = portalDestination.QX; portalDest.RotationY = portalDestination.QY; portalDest.RotationZ = portalDestination.QZ; portalDest.RotationW = portalDestination.QW; break; } /// <summary> /// All other portals don't need adjustments. /// </summary> default: { break; } } player.Session.Player.Teleport(portalDest); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } else if ((player.Level > portalDestination.MaxLvl) && (portalDestination.MaxLvl != 0)) { // You are too powerful to interact with that portal! var usePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AC); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent); } else { // You are not powerful enough to interact with that portal! var usePortalMessage = new GameEventDisplayStatusMessage(player.Session, StatusMessageType1.Enum_04AB); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent); } } else { string serverMessage = "Portal destination for portal ID " + this.WeenieClassid + " not yet implemented!"; var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent); } } else { // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } }
public static void HandleAccountCreate(Session session, params string[] parameters) { AccessLevel defaultAccessLevel = (AccessLevel)Common.ConfigManager.Config.Server.Accounts.DefaultAccessLevel; if (!Enum.IsDefined(typeof(AccessLevel), defaultAccessLevel)) { defaultAccessLevel = AccessLevel.Player; } var accessLevel = defaultAccessLevel; if (parameters.Length > 2) { if (Enum.TryParse(parameters[2], true, out accessLevel)) { if (!Enum.IsDefined(typeof(AccessLevel), accessLevel)) { accessLevel = defaultAccessLevel; } } } string articleAorAN = "a"; if (accessLevel == AccessLevel.Advocate || accessLevel == AccessLevel.Admin || accessLevel == AccessLevel.Envoy) { articleAorAN = "an"; } string message = ""; var accountExists = DatabaseManager.Authentication.GetAccountByName(parameters[0]); if (accountExists != null) { message = "Account already exists. Try a new name."; } else { try { var account = DatabaseManager.Authentication.CreateAccount(parameters[0].ToLower(), parameters[1], accessLevel); message = ("Account successfully created for " + account.AccountName + " (" + account.AccountId + ") with access rights as " + articleAorAN + " " + Enum.GetName(typeof(AccessLevel), accessLevel) + "."); } catch { message = "Account already exists. Try a new name."; } } if (session == null) { Console.WriteLine(message); } else { var sysChatMsg = new GameMessageSystemChat(message, ChatMessageType.WorldBroadcast); session.Network.EnqueueSend(sysChatMsg); } }
/// <summary> /// Called when a player dies, in conjunction with Die() /// </summary> /// <param name="lastDamager">The last damager that landed the death blow</param> /// <param name="damageType">The damage type for the death message</param> public override DeathMessage OnDeath(DamageHistoryInfo lastDamager, DamageType damageType, bool criticalHit = false) { var topDamager = DamageHistory.GetTopDamager(false); HandlePKDeathBroadcast(lastDamager, topDamager); var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit); var lastDamagerObj = lastDamager?.TryGetAttacker(); if (lastDamagerObj != null) { lastDamagerObj.EmoteManager.OnKill(this); } var playerMsg = ""; if (lastDamager != null) { playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name); } else { playerMsg = deathMessage.Victim; } var msgYourDeath = new GameEventYourDeath(Session, playerMsg); Session.Network.EnqueueSend(msgYourDeath); // broadcast to nearby players var nearbyMsg = ""; if (lastDamager != null) { nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name); } else { nearbyMsg = deathMessage.Broadcast; } var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast); log.Debug("[CORPSE] " + nearbyMsg); var excludePlayers = new List <Player>(); if (lastDamagerObj is Player lastDamagerPlayer) { excludePlayers.Add(lastDamagerPlayer); } var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg); excludePlayers.AddRange(nearbyPlayers); excludePlayers.Add(this); // exclude self if (Fellowship != null) { Fellowship.OnDeath(this); } // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock if (PropertyManager.GetBool("lifestone_broadcast_death").Item&& Sanctuary != null && Location.Landblock != Sanctuary.Landblock) { // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet... // this existing broadcast method is also based on the current visible objects to the player, // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid); // instead, we get all of the players in the lifestone landblock + adjacent landblocks, // and possibly limit that to some radius around the landblock? var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true); lifestoneBlock.EnqueueBroadcast(excludePlayers, true, Sanctuary, LocalBroadcastRangeSq, broadcastMsg); } return(deathMessage); }
private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas) { Player player; Player.HandleNoLogLandblock(playerBiota); var stripAdminProperties = false; var addAdminProperties = false; var addSentinelProperties = false; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel <= AccessLevel.Advocate) // check for elevated characters { if (playerBiota.WeenieType == WeenieType.Admin || playerBiota.WeenieType == WeenieType.Sentinel) // Downgrade weenie { character.IsPlussed = false; playerBiota.WeenieType = WeenieType.Creature; stripAdminProperties = true; } } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Admin) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Sentinel; addSentinelProperties = true; } } else // Developers and Admins { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Sentinel) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Admin; addAdminProperties = true; } } } if (playerBiota.WeenieType == WeenieType.Admin) { player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else if (playerBiota.WeenieType == WeenieType.Sentinel) { player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else { player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } session.SetPlayer(player); if (stripAdminProperties) // continue stripping properties { player.CloakStatus = CloakStatus.Undef; player.Attackable = true; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, true); player.AdvocateLevel = null; player.ChannelsActive = null; player.ChannelsAllowed = null; player.Invincible = false; player.Cloaked = null; player.IgnoreHouseBarriers = false; player.IgnorePortalRestrictions = false; player.SafeSpellComponents = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } if (addSentinelProperties || addAdminProperties) // continue restoring properties to default { WorldObject weenie; if (addAdminProperties) { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } else { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } if (weenie != null) { player.CloakStatus = CloakStatus.Off; player.Attackable = weenie.Attackable; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, false); player.AdvocateLevel = weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.AdvocateLevel); player.ChannelsActive = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsActive); player.ChannelsAllowed = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsAllowed); player.Invincible = false; player.Cloaked = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } } // If the client is missing a location, we start them off in the starter town they chose if (session.Player.Location == null) { if (session.Player.Instantiation != null) { session.Player.Location = new Position(session.Player.Instantiation); } else { session.Player.Location = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback } } session.Player.PlayerEnterWorld(); var success = LandblockManager.AddObject(session.Player, true); if (!success) { // send to lifestone, or fallback location var fixLoc = session.Player.Sanctuary ?? new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); log.Error($"WorldManager.DoPlayerEnterWorld: failed to spawn {session.Player.Name}, relocating to {fixLoc.ToLOCString()}"); session.Player.Location = new Position(fixLoc); LandblockManager.AddObject(session.Player, true); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(5.0f); actionChain.AddAction(session.Player, () => { if (session != null && session.Player != null) { session.Player.Teleport(fixLoc); } }); actionChain.EnqueueChain(); } // These warnings are set by DDD_InterrogationResponse if ((session.DatWarnCell || session.DatWarnLanguage || session.DatWarnPortal) && PropertyManager.GetBool("show_dat_warning").Item) { var msg = PropertyManager.GetString("dat_warning_msg").Item; var chatMsg = new GameMessageSystemChat(msg, ChatMessageType.System); session.Network.EnqueueSend(chatMsg); } var popup_header = PropertyManager.GetString("popup_header").Item; var popup_motd = PropertyManager.GetString("popup_motd").Item; var popup_welcome = PropertyManager.GetString("popup_welcome").Item; if (character.TotalLogins <= 1) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome))); } else if (!string.IsNullOrEmpty(popup_motd)) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd))); } var info = "Welcome to Asheron's Call\n powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n"; session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast)); var server_motd = PropertyManager.GetString("server_motd").Item; if (!string.IsNullOrEmpty(server_motd)) { session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast)); } }
/// <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; var rate = item.ManaRate.Value; if (LumAugItemManaUsage != 0) { rate *= GetNegativeRatingMod(LumAugItemManaUsage); } if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.Now; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.Now - 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.BiotaPropertiesSpellBook != 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, () => { for (int i = 0; i < item.Biota.BiotaPropertiesSpellBook.Count; i++) { RemoveItemSpell(item, (uint)item.Biota.BiotaPropertiesSpellBook.ElementAt(i).Spell); } }); actionChain.EnqueueChain(); } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.Now - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.Now; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
/// <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; } 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) { // 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"; 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); }