/// <summary> /// Public method for adding a new skill by spending skill credits. /// </summary> /// <remarks> /// The client will throw up more then one train skill dialog and the user has the chance to spend twice. /// </remarks> public void HandleActionTrainSkill(Skill skill, int creditsSpent) { if (AvailableSkillCredits >= creditsSpent) { // attempt to train the specified skill bool trainNewSkill = TrainSkill(skill, creditsSpent); // create an update to send to the client var currentCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0); // as long as the skill is sent, the train new triangle button on the client will not lock up. // Sending Skill.None with status untrained worked in test var trainSkillUpdate = new GameMessagePrivateUpdateSkill(this, Skill.None, SkillAdvancementClass.Untrained, 0, 0, 0); // create a string placeholder for the correct after string trainSkillMessageText; // if the skill has already been trained or we do not have enough credits, then trainNewSkill be set false if (trainNewSkill) { // replace the trainSkillUpdate message with the correct skill assignment: var creatureSkill = GetCreatureSkill(skill); trainSkillUpdate = new GameMessagePrivateUpdateSkill(this, creatureSkill); trainSkillMessageText = $"{skill.ToSentence()} trained. You now have {AvailableSkillCredits} credits available."; } else { trainSkillMessageText = $"Failed to train {skill.ToSentence()}! You now have {AvailableSkillCredits} credits available."; } // create the final game message and send to the client var message = new GameMessageSystemChat(trainSkillMessageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(trainSkillUpdate, currentCredits, message); } }
/// <summary> /// Handles the GameAction 0x47 - TrainSkill network message from client /// </summary> public bool HandleActionTrainSkill(Skill skill, int creditsSpent) { if (creditsSpent > AvailableSkillCredits) { log.Error($"{Name}.HandleActionTrainSkill({skill}, {creditsSpent}) - not enough skill credits ({AvailableSkillCredits})"); return(false); } // get the actual cost to train the skill. if (!DatManager.PortalDat.SkillTable.SkillBaseHash.TryGetValue((uint)skill, out var skillBase)) { log.Error($"{Name}.HandleActionTrainSkill({skill}, {creditsSpent}) - couldn't find skill base"); return(false); } if (creditsSpent != skillBase.TrainedCost) { log.Error($"{Name}.HandleActionTrainSkill({skill}, {creditsSpent}) - client value differs from skillBase.TrainedCost({skillBase.TrainedCost})"); return(false); } // attempt to train the specified skill var success = TrainSkill(skill, creditsSpent); var availableSkillCredits = $"You now have {AvailableSkillCredits} credits available."; if (success) { var updateSkill = new GameMessagePrivateUpdateSkill(this, GetCreatureSkill(skill)); var skillCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0); var msg = new GameMessageSystemChat($"{skill.ToSentence()} trained. {availableSkillCredits}", ChatMessageType.Advancement); Session.Network.EnqueueSend(updateSkill, skillCredits, msg); } else { Session.Network.EnqueueSend(new GameMessageSystemChat($"Failed to train {skill.ToSentence()}! {availableSkillCredits}", ChatMessageType.Advancement)); } return(success); }
public void SpendXp(Skill skill, uint amount) { uint baseValue = 0; uint result = SpendSkillXp(character.Skills[skill], amount); if (result > 0u) { uint ranks = character.Skills[skill].Ranks; uint newValue = character.Skills[skill].UnbuffedValue; var status = character.Skills[skill].Status; var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); var ablityUpdate = new GameMessagePrivateUpdateSkill(Session, skill, status, ranks, baseValue, result); var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.AbilityIncrease, 1f); var message = new GameMessageSystemChat($"Your base {skill} is now {newValue}!", ChatMessageType.Broadcast); NetworkManager.SendWorldMessages(Session, new GameMessage[] { xpUpdate, ablityUpdate, soundEvent, message }); } else { ChatPacket.SendServerMessage(Session, $"Your attempt to raise {skill} has failed.", ChatMessageType.Broadcast); } }
/// <summary> /// Spend xp Skill ranks /// </summary> public void SpendXp(Skill skill, uint amount) { uint baseValue = 0; uint result = SpendSkillXp(character.Skills[skill], amount); uint ranks = character.Skills[skill].Ranks; uint newValue = character.Skills[skill].UnbuffedValue; var status = character.Skills[skill].Status; var xpUpdate = new GameMessagePrivateUpdatePropertyInt64(Session, PropertyInt64.AvailableExperience, character.AvailableExperience); var skillUpdate = new GameMessagePrivateUpdateSkill(Session, skill, status, ranks, baseValue, result); var soundEvent = new GameMessageSound(this.Guid, Network.Enum.Sound.AbilityIncrease, 1f); string messageText = ""; if (result > 0u) { //if the skill ranks out at the top of our xp chart //then we will start fireworks effects and have special text! if (IsSkillMaxRank(ranks, status)) { //fireworks on rank up is 0x8D PlayParticleEffect(0x8D); messageText = $"Your base {skill} is now {newValue} and has reached its upper limit!"; } else { messageText = $"Your base {skill} is now {newValue}!"; } } else { messageText = $"Your attempt to raise {skill} has failed!"; } var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(xpUpdate, skillUpdate, soundEvent, message); }
/// <summary> /// Spend xp Skill ranks /// </summary> public void RaiseSkillGameAction(Skill skill, uint amount) { uint baseValue = 0; var creatureSkill = GetCreatureSkill(skill); uint result = SpendSkillXp(creatureSkill, amount); string messageText; if (result > 0u) { // if the skill ranks out at the top of our xp chart // then we will start fireworks effects and have special text! if (IsSkillMaxRank(creatureSkill.Ranks, creatureSkill.Status)) { // fireworks on rank up is 0x8D PlayParticleEffect(ACE.Entity.Enum.PlayScript.WeddingBliss, Guid); messageText = $"Your base {skill} is now {creatureSkill.Base} and has reached its upper limit!"; } else { messageText = $"Your base {skill} is now {creatureSkill.Base}!"; } } else { messageText = $"Your attempt to raise {skill} has failed!"; } var skillUpdate = new GameMessagePrivateUpdateSkill(Session, skill, creatureSkill.Status, creatureSkill.Ranks, baseValue, result); var soundEvent = new GameMessageSound(Guid, Sound.RaiseTrait, 1f); var message = new GameMessageSystemChat(messageText, ChatMessageType.Advancement); Session.Network.EnqueueSend(skillUpdate, soundEvent, message); }
/// <summary> /// Resets the skill, refunds all experience and skill credits, if allowed. /// </summary> public bool ResetSkill(Skill skill) { var creatureSkill = GetCreatureSkill(skill, false); if (creatureSkill == null || creatureSkill.AdvancementClass < SkillAdvancementClass.Trained) { return(false); } // gather skill credits to refund DatManager.PortalDat.SkillTable.SkillBaseHash.TryGetValue((uint)creatureSkill.Skill, out var skillBase); if (skillBase == null) { return(false); } // salvage / tinkering skills specialized via augmentations // cannot be untrained or unspecialized bool specAug = false; switch (creatureSkill.Skill) { case Skill.ArmorTinkering: specAug = AugmentationSpecializeArmorTinkering > 0; break; case Skill.ItemTinkering: specAug = AugmentationSpecializeItemTinkering > 0; break; case Skill.MagicItemTinkering: specAug = AugmentationSpecializeMagicItemTinkering > 0; break; case Skill.WeaponTinkering: specAug = AugmentationSpecializeWeaponTinkering > 0; break; case Skill.Salvaging: specAug = AugmentationSpecializeSalvaging > 0; break; } //if (specAug) // return false; // send message? var typeOfSkill = creatureSkill.AdvancementClass.ToString().ToLower() + " "; var untrainable = IsSkillUntrainable(skill); var creditRefund = creatureSkill.AdvancementClass == SkillAdvancementClass.Specialized || untrainable; if (creatureSkill.AdvancementClass == SkillAdvancementClass.Specialized && !specAug) { creatureSkill.AdvancementClass = SkillAdvancementClass.Trained; creatureSkill.InitLevel = 0; AvailableSkillCredits += skillBase.UpgradeCostFromTrainedToSpecialized; } // temple untraining 'always trained' skills: // cannot be untrained, but skill XP can be recovered if (untrainable && !specAug) { creatureSkill.AdvancementClass = SkillAdvancementClass.Untrained; creatureSkill.InitLevel = 0; AvailableSkillCredits += skillBase.TrainedCost; } RefundXP(creatureSkill.ExperienceSpent); creatureSkill.ExperienceSpent = 0; creatureSkill.Ranks = 0; var updateSkill = new GameMessagePrivateUpdateSkill(this, creatureSkill); var availableSkillCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0); var msg = $"Your {(untrainable && !specAug ? $"{typeOfSkill}" : "")}{skill.ToSentence()} skill has been {(untrainable && !specAug ? "removed" : "reset")}. "; msg += $"All the experience {(creditRefund && !specAug ? "and skill credits " : "")}that you spent on this skill have been refunded to you."; Session.Network.EnqueueSend(updateSkill, availableSkillCredits, new GameMessageSystemChat(msg, ChatMessageType.Broadcast)); return(true); }
/// <summary> /// Resets the skill, refunds all experience and skill credits, if allowed. /// </summary> public bool ResetSkill(Skill skill, bool refund = true) { var creatureSkill = GetCreatureSkill(skill, false); if (creatureSkill == null || creatureSkill.AdvancementClass < SkillAdvancementClass.Trained) { return(false); } // gather skill credits to refund DatManager.PortalDat.SkillTable.SkillBaseHash.TryGetValue((uint)creatureSkill.Skill, out var skillBase); if (skillBase == null) { return(false); } // salvage / tinkering skills specialized via augmentations // Salvaging cannot be untrained or unspecialized => skillIsSpecializedViaAugmentation && !untrainable IsSkillSpecializedViaAugmentation(creatureSkill.Skill, out var skillIsSpecializedViaAugmentation); var typeOfSkill = creatureSkill.AdvancementClass.ToString().ToLower() + " "; var untrainable = IsSkillUntrainable(skill); var creditRefund = (creatureSkill.AdvancementClass == SkillAdvancementClass.Specialized && !(skillIsSpecializedViaAugmentation && !untrainable)) || untrainable; if (creatureSkill.AdvancementClass == SkillAdvancementClass.Specialized && !(skillIsSpecializedViaAugmentation && !untrainable)) { creatureSkill.AdvancementClass = SkillAdvancementClass.Trained; creatureSkill.InitLevel = 0; if (!skillIsSpecializedViaAugmentation) // Tinkering skills can be unspecialized, but do not refund upgrade cost. { AvailableSkillCredits += skillBase.UpgradeCostFromTrainedToSpecialized; } } // temple untraining 'always trained' skills: // cannot be untrained, but skill XP can be recovered if (untrainable) { creatureSkill.AdvancementClass = SkillAdvancementClass.Untrained; creatureSkill.InitLevel = 0; AvailableSkillCredits += skillBase.TrainedCost; } if (refund) { RefundXP(creatureSkill.ExperienceSpent); } creatureSkill.ExperienceSpent = 0; creatureSkill.Ranks = 0; var updateSkill = new GameMessagePrivateUpdateSkill(this, creatureSkill); var availableSkillCredits = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.AvailableSkillCredits, AvailableSkillCredits ?? 0); var msg = $"Your {(untrainable ? $"{typeOfSkill}" : "")}{skill.ToSentence()} skill has been {(untrainable ? "removed" : "reset")}. "; msg += $"All the experience {(creditRefund ? "and skill credits " : "")}that you spent on this skill have been refunded to you."; if (refund) { Session.Network.EnqueueSend(updateSkill, availableSkillCredits, new GameMessageSystemChat(msg, ChatMessageType.Broadcast)); } else { Session.Network.EnqueueSend(updateSkill, new GameMessageSystemChat(msg, ChatMessageType.Broadcast)); } return(true); }