/// <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);
            }
        }