/// <summary> /// Requests to change the job. /// </summary> /// <param name="cpkt"></param> private void CM_CHARACTER_JOBCHANGE(CMSG_CHANGEJOB cpkt) { JobChangeCollection collection = this.character.Tag as JobChangeCollection; if (collection == null || !collection.IsJobAvailable(cpkt.Job)) { //Job change failed SMSG_JOBCHANGED spkt = new SMSG_JOBCHANGED(); spkt.Job = this.character.job; spkt.Result = 1; spkt.SessionId = this.character.id; spkt.SourceActor = this.character.id; this.Send((byte[])spkt); return; } else if (collection.GetJobTrasferFee(cpkt.Job) > this.character.ZENY) { //Not enough money Common.Errors.GeneralErrorMessage(this.character, (uint)Generalerror.NotEnoughMoney); SMSG_JOBCHANGED spkt = new SMSG_JOBCHANGED(); spkt.Job = this.character.job; spkt.Result = 1; spkt.SessionId = this.character.id; spkt.SourceActor = this.character.id; this.Send((byte[])spkt); return; } else { this.character.ZENY -= collection.GetJobTrasferFee(cpkt.Job); CommonFunctions.UpdateZeny(this.character); } //Helper variables List<int> EnabledEquipment = new List<int>(); List<int> DisabledEquipment = new List<int>(); bool ChangeWeapon = cpkt.ChangeWeapon == 1; bool IsActiveWeapon = this.character.weapons.IsActiveSlot(cpkt.WeaponSlot); Weapon selectedWeapon = this.character.weapons[cpkt.WeaponSlot]; #region Update Character Information lock (this.character) { //Change the job and joblevel int hpbefore = Singleton.CharacterConfiguration.CalculateMaximumHP(this.character); int spbefore = Singleton.CharacterConfiguration.CalculateMaximumSP(this.character); this.character.CharacterJobLevel[this.character.job] = this.character.jlvl; this.character.jlvl = this.character.CharacterJobLevel[cpkt.Job]; this.character.job = cpkt.Job; this.character.Jexp = Singleton.experience.FindRequiredJexp(this.character.jlvl); int hpafter = Singleton.CharacterConfiguration.CalculateMaximumHP(this.character); int spafter = Singleton.CharacterConfiguration.CalculateMaximumSP(this.character); this.character._status.CurrentHp += (ushort)(hpbefore - hpafter); this.character._status.CurrentSp += (ushort)(spbefore - spafter); this.character._status.Updates |= 1; } #endregion Update Character Information #region Refresh Weapon //Deapply current weapon info if (ChangeWeapon && IsActiveWeapon && selectedWeapon != null) { BattleStatus status = this.character._status; status.MaxWMAttack -= (ushort)selectedWeapon.Info.max_magic_attack; status.MinWMAttack -= (ushort)selectedWeapon.Info.min_magic_attack; status.MaxWPAttack -= (ushort)selectedWeapon.Info.max_short_attack; status.MinWPAttack -= (ushort)selectedWeapon.Info.min_short_attack; status.MaxWRAttack -= (ushort)selectedWeapon.Info.max_range_attack; status.MinWRAttack -= (ushort)selectedWeapon.Info.min_range_attack; status.Updates |= 2; //Reapplies alterstone additions for (int i = 0; i < 8; i++) { uint addition = selectedWeapon.Slots[i]; if (addition > 0) { Singleton.Additions.DeapplyAddition(addition, character); } } } #endregion Refresh Weapon #region Refresh Weapon if (ChangeWeapon && selectedWeapon != null) { Singleton.Weapons.ChangeWeapon(cpkt.Job, cpkt.PostFix, selectedWeapon); SMSG_WEAPONCHANGE spkt = new SMSG_WEAPONCHANGE(); spkt.Auge = selectedWeapon._augeskill; spkt.SessionId = this.character.id; spkt.Suffix = cpkt.PostFix; spkt.Index = cpkt.WeaponSlot; spkt.WeaponType = (byte)selectedWeapon._type; this.Send((byte[])spkt); } #endregion Refresh Weapon #region Refresh Skills { //Remove all skills foreach (Skill skill in this.character.learnedskills) { //Remove the skill SMSG_SKILLREMOVE spkt = new SMSG_SKILLREMOVE(); spkt.Unknown = skill.info.skilltype; spkt.SessionId = this.character.id; spkt.SkillId = skill.info.skillid; this.Send((byte[])spkt); //Save only experience if it's maxed-out. Singleton.Database.UpdateSkill(this.character, skill.Id, (skill.Experience == skill.info.maximumexperience) ? skill.info.maximumexperience : 0); //Deapply passive skills bool canUse = Singleton.SpellManager.CanUse(this.character, skill.info); if (skill.info.skilltype == 2 && canUse) Singleton.Additions.DeapplyAddition(skill.info.addition, character); } //Remove all learned skills in an instant this.character.learnedskills.Clear(); Singleton.Database.LoadSkills(this.character); //Retrieve job speciafic skills foreach (Skill skill in this.character.learnedskills) { //Add the skill SMSG_SKILLADD spkt = new SMSG_SKILLADD(); spkt.SessionId = this.character.id; spkt.SkillId = skill.info.skillid; spkt.Slot = 0; this.Send((byte[])spkt); //Deapply passive skills bool canUse = Singleton.SpellManager.CanUse(this.character, skill.info); if (skill.info.skilltype == 2 && canUse) Singleton.Additions.ApplyAddition(skill.info.addition, character); } } #endregion Refresh Skills #region Refresh Weapon //Apply current weapon info if (ChangeWeapon && IsActiveWeapon && selectedWeapon != null) { //Apply status BattleStatus status = this.character._status; status.MaxWMAttack += (ushort)selectedWeapon.Info.max_magic_attack; status.MinWMAttack += (ushort)selectedWeapon.Info.min_magic_attack; status.MaxWPAttack += (ushort)selectedWeapon.Info.max_short_attack; status.MinWPAttack += (ushort)selectedWeapon.Info.min_short_attack; status.MaxWRAttack += (ushort)selectedWeapon.Info.max_range_attack; status.MinWRAttack += (ushort)selectedWeapon.Info.min_range_attack; status.Updates |= 2; //Reapplies alterstone additions for (int i = 0; i < 8; i++) { uint addition = selectedWeapon.Slots[i]; if (addition > 0) { Singleton.Additions.ApplyAddition(addition, character); } } } #endregion Refresh Weapon #region Refresh Equipment { //Disable equipment for (int i = 0; i < 16; i++) { //Verify if item exists Rag2Item item = this.character.Equipment[i]; if (item == null || item.info == null) continue; //Verify if the item changed states bool Active = item.active == 1; bool NewActive = this.character.jlvl >= item.info.JobRequirement[cpkt.Job - 1]; if (Active == NewActive) continue; item.active = (byte)((NewActive == true) ? 1 : 0); //Adjust the item SMSG_ITEMADJUST spkt = new SMSG_ITEMADJUST(); spkt.Container = 1; spkt.Function = 5; spkt.Slot = (byte)i; spkt.SessionId = this.character.id; spkt.Value = item.active; this.Send((byte[])spkt); //Deapply additions if (NewActive) { EnabledEquipment.Add(i); Singleton.Additions.ApplyAddition(item.info.option_id, character); } else { DisabledEquipment.Add(i); Singleton.Additions.DeapplyAddition(item.info.option_id, character); } } //Update other stats //Common.Internal.CheckWeaponary(this.character); //CommonFunctions.UpdateCharacterInfo(this.character, 0); //CommonFunctions.SendBattleStatus(this.character); } #endregion Refresh Equipment #region Refresh Appereance { Regiontree tree = this.character.currentzone.Regiontree; foreach (Character regionObject in tree.SearchActors(SearchFlags.Characters)) { SMSG_JOBCHANGED spkt = new SMSG_JOBCHANGED(); spkt.SessionId = regionObject.id; spkt.SourceActor = this.character.id; spkt.Job = cpkt.Job; regionObject.client.Send((byte[])spkt); if (regionObject.id != this.character.id) { if (IsActiveWeapon) { uint auge = (character.FindRequiredRootSkill(selectedWeapon.Info.weapon_skill)) ? selectedWeapon._augeskill : 0; SMSG_SHOWWEAPON spkt2 = new SMSG_SHOWWEAPON(); spkt2.ActorID = this.character.id; spkt2.AugeID = auge; spkt2.SessionId = regionObject.id; regionObject.client.Send((byte[])spkt2); } foreach (byte i in EnabledEquipment) { Rag2Item item = this.character.Equipment[i]; SMSG_CHANGEEQUIPMENT spkt2 = new SMSG_CHANGEEQUIPMENT(); spkt2.SessionId = regionObject.id; spkt2.ActorID = this.character.id; spkt2.Slot = i; spkt2.ItemID = item.info.item; spkt2.Dye = item.dyecolor; regionObject.client.Send((byte[])spkt2); } foreach (byte i in DisabledEquipment) { SMSG_CHANGEEQUIPMENT spkt2 = new SMSG_CHANGEEQUIPMENT(); spkt2.SessionId = regionObject.id; spkt2.ActorID = this.character.id; spkt2.Slot = i; spkt2.ItemID = 0; spkt2.Dye = 0; regionObject.client.Send((byte[])spkt2); } } } } #endregion Refresh Appereance #region Refresh Party { //Process party members SMSG_PARTYMEMBERJOB spkt = new SMSG_PARTYMEMBERJOB(); spkt.Job = cpkt.Job; spkt.SessionId = this.character.id; spkt.ActorId = this.character.id; spkt.Index = 1; SMSG_PARTYMEMBERJLVL spkt2 = new SMSG_PARTYMEMBERJLVL(); spkt2.Jvl = this.character.jlvl; spkt2.ActorId = this.character.id; spkt2.Index = 1; if (this.character.sessionParty != null) { foreach (Character target in this.character.sessionParty.GetCharacters()) { //Send over Job change spkt.SessionId = target.id; target.client.Send((byte[])spkt); //Send over jlvl change spkt2.SessionId = target.id; target.client.Send((byte[])spkt2); } } } #endregion Refresh Party #region Refresh LifeCycle Tasks.LifeCycle.Update(this.character); #endregion Refresh LifeCycle }
/// <summary> /// Learns a new skill from a skillbook /// </summary> /// <param name="cpkt"></param> private void CM_SKILLS_LEARNFROMSKILLBOOK(CMSG_SKILLLEARN cpkt) { byte result = 1; try { Saga.Factory.Spells.Info spellinfo; Rag2Item item = character.container[cpkt.Index]; if (item != null) { //Helpers predicates Predicate<Skill> FindSkill = delegate(Skill skill) { return skill.Id == item.info.skill; }; Predicate<Skill> FindPreviousSkill = delegate(Skill skill) { return skill.Id == item.info.skill - 1; }; //HELPER VARIABLES uint baseskill = (item.info.skill / 100) * 100 + 1; int newLength = character.container[cpkt.Index].count - 1; List<Skill> learnedSpells = this.character.learnedskills; Singleton.SpellManager.TryGetSpell(item.info.skill, out spellinfo); Skill CurrentSkill = learnedSpells.FindLast(FindSkill); Skill PreviousSkill = learnedSpells.FindLast(FindPreviousSkill); bool IsBaseSkill = item.info.skill == baseskill; //CHECK IF THE CURRENT JOB CAN LEARN THE SPELL if (item.info.JobRequirement[this.character.job - 1] > this.character.jlvl) { result = (byte)Generalerror.ConditionsNotMet; } //CHECK IF WE ALREADY LEARNED THE SPELL else if (CurrentSkill != null) { result = (byte)Generalerror.AlreadyLearntSkill; } //CHECK IF A PREVIOUS SKILL WAS FOUND else if (!IsBaseSkill && PreviousSkill == null) { result = (byte)Generalerror.PreviousSkillNotFound; } //CHECK SKILL EXP else if (PreviousSkill != null && PreviousSkill.Experience < PreviousSkill.info.maximumexperience) { result = (byte)Generalerror.NotEnoughSkillExperience; } else { //ADD A NEW SKILL if (IsBaseSkill) { //Passive skill bool canUse = Singleton.SpellManager.CanUse(this.character, spellinfo); if (spellinfo.skilltype == 2 && canUse) { Singleton.Additions.ApplyAddition(spellinfo.addition, this.character); int ActiveWeaponIndex = (this.character.weapons.ActiveWeaponIndex == 1) ? this.character.weapons.SeconairyWeaponIndex : this.character.weapons.PrimaryWeaponIndex; if (ActiveWeaponIndex < this.character.weapons.UnlockedWeaponSlots) { Weapon weapon = this.character.weapons[ActiveWeaponIndex]; if ((baseskill - 1) == weapon.Info.weapon_skill) { BattleStatus status = character._status; status.MaxWMAttack += (ushort)weapon.Info.max_magic_attack; status.MinWMAttack += (ushort)weapon.Info.min_magic_attack; status.MaxWPAttack += (ushort)weapon.Info.max_short_attack; status.MinWPAttack += (ushort)weapon.Info.min_short_attack; status.MaxWRAttack += (ushort)weapon.Info.max_range_attack; status.MinWRAttack += (ushort)weapon.Info.min_range_attack; status.Updates |= 2; } } } Singleton.Database.InsertNewSkill(this.character, item.info.skill, spellinfo.maximumexperience); CurrentSkill = new Skill(); CurrentSkill.info = spellinfo; CurrentSkill.Id = item.info.skill; CurrentSkill.Experience = spellinfo.maximumexperience; learnedSpells.Add(CurrentSkill); } //UPDATE A OLD SKILL else { //Passive skill if (spellinfo.skilltype == 2) { Saga.Factory.Spells.Info oldSpellinfo; Singleton.SpellManager.TryGetSpell(PreviousSkill.info.skillid, out oldSpellinfo); bool canUseOld = Singleton.SpellManager.CanUse(this.character, oldSpellinfo); bool canUseNew = Singleton.SpellManager.CanUse(this.character, spellinfo); if (canUseOld) { Singleton.Additions.DeapplyAddition(oldSpellinfo.addition, this.character); } if (canUseNew) { Singleton.Additions.ApplyAddition(spellinfo.addition, this.character); } } Singleton.Database.UpgradeSkill(this.character, PreviousSkill.info.skillid, item.info.skill, spellinfo.maximumexperience); PreviousSkill.info = spellinfo; PreviousSkill.Id = item.info.skill; PreviousSkill.Experience = spellinfo.maximumexperience; } SMSG_SKILLADD spkt2 = new SMSG_SKILLADD(); spkt2.Slot = 0; spkt2.SkillId = item.info.skill; spkt2.SessionId = this.character.id; this.Send((byte[])spkt2); if (newLength > 0) { this.character.container[cpkt.Index].count = newLength; SMSG_UPDATEITEM spkt = new SMSG_UPDATEITEM(); spkt.Amount = (byte)newLength; spkt.UpdateReason = 8; spkt.UpdateType = 4; spkt.Container = 2; spkt.SessionId = this.character.id; spkt.Index = cpkt.Index; this.Send((byte[])spkt); } else { this.character.container.RemoveAt(cpkt.Index); SMSG_DELETEITEM spkt = new SMSG_DELETEITEM(); spkt.UpdateReason = 8; spkt.Container = 2; spkt.Index = cpkt.Index; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } Common.Internal.CheckWeaponary(this.character); Tasks.LifeCycle.Update(this.character); result = 0; } } } finally { //OUTPUT THE RESULT SMSG_SKILLLEARN spkt = new SMSG_SKILLLEARN(); spkt.Result = result; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } }