/// <title>Saga.AddStep</title> /// <code> /// Saga.AddStep(cid, QuestID, StepId, State); /// </code> /// <description> /// Adds a step to the quest. This can only be done during initialisation. /// 0 = Hidden, 1 = Visible, 2 = Completed /// </description> /// <example> ///function QUEST_START(cid) /// -- Initialize all quest steps /// -- Initialize all starting navigation points /// /// Saga.AddStep(cid, QuestID, 101, 1); /// Saga.AddStep(cid, QuestID, 102, 0); /// Saga.AddStep(cid, QuestID, 103, 0); /// Saga.InsertQuest(cid, QuestID, 1); /// /// return 0; ///end /// </example> public static bool NpcTakeItem(uint CID, uint ItemId, int ItemCount) { Character Character; if (LifeCycle.TryGetById(CID, out Character)) { foreach (KeyValuePair<byte, Rag2Item> pair in Character.container.GetAllItems()) { if (pair.Value.info.item != ItemId || pair.Value.count < ItemCount) continue; int newCount = pair.Value.count -= ItemCount; if (newCount > 0) { SMSG_ITEMADJUST spkt = new SMSG_ITEMADJUST(); spkt.Container = 2; spkt.Function = 4; spkt.Slot = pair.Key; spkt.UpdateReason = (byte)ItemUpdateReason.GiveToNpc; spkt.Value = (byte)pair.Value.count; spkt.SessionId = Character.id; Character.client.Send((byte[])spkt); } else { Character.container.RemoveAt(pair.Key); SMSG_DELETEITEM spkt = new SMSG_DELETEITEM(); spkt.Container = 2; spkt.Index = pair.Key; spkt.UpdateReason = (byte)ItemUpdateReason.GiveToNpc; spkt.SessionId = Character.id; Character.client.Send((byte[])spkt); } return true; } } return false; }
/// <summary> /// Looses durabillity loss on defensive equipment /// </summary> /// <remarks> /// Durabillity loss on kro2 is calculated with a ratio of 50 : 1. /// Shield, Shirt, Pants, Shoes each durabillity loss it will rotate /// over the next equipment set. /// </remarks> public static void DoEquipment(Character target, uint damage) { if (target == null) return; bool canupdate = false; int NewDurabillity = 0; byte Slot = 0; target.TakenDamage += damage; while (target.TakenDamage > 50) { target.TakenDamage -= 50; switch (target.LastEquipmentDuraLoss) { case 0: //Shield Rag2Item equip = target.Equipment[15]; if (equip == null || equip.active == 1 || equip.durabillty == 0) goto case 1; Slot = 15; equip.durabillty -= 1; NewDurabillity = equip.durabillty; target.LastEquipmentDuraLoss = 1; canupdate = true; break; case 1: //Shirt Rag2Item shirt = target.Equipment[3]; if (shirt == null || shirt.active == 1 || shirt.durabillty == 0) goto case 2; Slot = 3; shirt.durabillty -= 1; NewDurabillity = shirt.durabillty; target.LastEquipmentDuraLoss = 2; canupdate = true; break; case 2: //Pants Rag2Item pants = target.Equipment[4]; if (pants == null || pants.active == 1 || pants.durabillty == 0) goto case 3; Slot = 4; pants.durabillty -= 1; NewDurabillity = pants.durabillty; target.LastEquipmentDuraLoss = 3; canupdate = true; break; case 3: //Shoes Rag2Item shoes = target.Equipment[5]; if (shoes == null || shoes.active == 1 || shoes.durabillty == 0) return; Slot = 5; shoes.durabillty -= 1; NewDurabillity = shoes.durabillty; target.LastEquipmentDuraLoss = 0; canupdate = true; break; } if (canupdate == true) { SMSG_ITEMADJUST spkt = new SMSG_ITEMADJUST(); spkt.Container = 1; spkt.Function = 3; spkt.Slot = Slot; spkt.SessionId = target.id; spkt.Value = (uint)NewDurabillity; target.client.Send((byte[])spkt); } } }
/// <summary> /// Occurs when repairing equipment /// </summary> /// <param name="cpkt"></param> private void CM_REPAIREQUIPMENT(CMSG_REPAIRITEM cpkt) { bool HasBadIndex = false; double price = 0; int amount = cpkt.Amount; List<KeyValuePair<byte, byte>> list = new List<KeyValuePair<byte, byte>>(); for (int i = 0; i < amount; i++) { //Read the equipment information byte index; byte container; cpkt.ReadEquipmentInfo(out index, out container); list.Add(new KeyValuePair<byte, byte>(container, index)); // Item from weaponary if (container == 8) { //CHECK IF WEAPON EXISTS Weapon current = this.character.weapons[index]; if (current == null) { HasBadIndex = true; continue; } //Compute the repair costs float scalar = (float)(current.Info.max_durabillity - current._durabillity) / (float)current.Info.max_durabillity; double costs = (double)(Singleton.experience.FindRepairCosts(current._weaponlevel) * scalar); //Increment the repair costs price += costs; } //Increment the repair costs else if (container == 1) { //Check if equipment exists Rag2Item current = this.character.Equipment[index]; if (current == null) { HasBadIndex = true; continue; } //Compute the repair costs double scalar = (double)(current.info.max_durability - current.durabillty) / (double)current.info.max_durability; double costs = (double) Rag2Item.CalculateRepairCosts( (double)current.info.price, (double)current.clvl, (double)current.info.max_durability ) * scalar; //Increment the repair costs price += costs; } //Item from inventory else if (container == 2) { //Check if equipment exists Rag2Item current = this.character.container[index]; if (current == null) { HasBadIndex = true; continue; } //Compute the repair costs double scalar = (double)(current.info.max_durability - current.durabillty) / (double)current.info.max_durability; double costs = (double) Rag2Item.CalculateRepairCosts( (double)current.info.price, (double)current.clvl, (double)current.info.max_durability ) * scalar; //Increment the repair costs price += costs; } } price = Math.Ceiling(price); //Check if a bad index occured if (HasBadIndex) { Common.Errors.GeneralErrorMessage( this.character, (uint)Generalerror.WrongServerIndex ); } //Check if our price is right uint aprice = (uint)price; if (this.character.ZENY >= aprice) { this.character.ZENY -= aprice; foreach (KeyValuePair<byte, byte> pair in list) { // Item from weaponary if (pair.Key == 8) { Weapon current = this.character.weapons[pair.Value]; if (current._durabillity == 0 && Common.Skills.HasRootSkillPresent(this.character, current.Info.weapon_skill)) { current._active = 1; SMSG_WEAPONADJUST spkt2 = new SMSG_WEAPONADJUST(); spkt2.SessionId = this.character.id; spkt2.Function = 6; spkt2.Value = 1; spkt2.Slot = pair.Value; this.Send((byte[])spkt2); } current._durabillity = (ushort)current.Info.max_durabillity; SMSG_WEAPONADJUST spkt = new SMSG_WEAPONADJUST(); spkt.SessionId = this.character.id; spkt.Function = 3; spkt.Value = current.Info.max_durabillity; spkt.Slot = pair.Value; this.Send((byte[])spkt); } //Equipment else if (pair.Key == 1) { Rag2Item current = this.character.Equipment[pair.Value]; if (current.durabillty == 0 && this.character.jlvl >= current.info.JobRequirement[this.character.job - 1]) { current.active = 1; SMSG_ITEMADJUST spkt2 = new SMSG_ITEMADJUST(); spkt2.Function = 5; spkt2.Container = pair.Key; spkt2.Slot = pair.Value; spkt2.Value = 1; spkt2.SessionId = this.character.id; this.Send((byte[])spkt2); #warning "Equipment applied" current.Activate(AdditionContext.Applied, this.character); } current.durabillty = (int)current.info.max_durability; SMSG_ITEMADJUST spkt = new SMSG_ITEMADJUST(); spkt.Function = 3; spkt.Container = pair.Key; spkt.Slot = pair.Value; spkt.Value = (uint)current.durabillty; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } //Inventory else if (pair.Key == 2) { Rag2Item current = this.character.container[pair.Value]; current.durabillty = (int)current.info.max_durability; SMSG_ITEMADJUST spkt = new SMSG_ITEMADJUST(); spkt.Function = 3; spkt.Container = pair.Key; spkt.Slot = pair.Value; spkt.Value = (uint)current.durabillty; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } } //Flush all pending updates LifeCycle.Update(this.character); //Update zeny CommonFunctions.UpdateZeny( this.character ); } else { Common.Errors.GeneralErrorMessage( this.character, (uint)Generalerror.NotEnoughMoneyToRepair ); } }
/// <summary> /// This function is sent when you use an dying item /// </summary> /// <param name="cpkt"></param> private void CM_USEDYEITEM(CMSG_USEDYEITEM cpkt) { Rag2Item dyeitem = null; Rag2Item item = null; byte result = 0; try { //Get the dyeitem dyeitem = this.character.container[cpkt.Index]; //Get the equipment item switch (cpkt.Container) { case 1: item = this.character.Equipment[cpkt.Slot]; break; case 2: item = this.character.container[cpkt.Slot]; break; case 3: item = this.character.STORAGE[cpkt.Slot]; break; } //Check if equipment is found if (item == null) { result = (byte)Generalerror.InventoryItemNotFound; } //Check if the dye is found else if (dyeitem == null) { result = (byte)Generalerror.InventoryItemNotFound; } //Everything is okay else { //Set the dye color switch (dyeitem.info.skill) { case 0: item.dyecolor = 0; break; //Nothing case 1: item.dyecolor = (byte)Saga.Utils.Generator.Random(2, 2); break; //red case 2: item.dyecolor = (byte)Saga.Utils.Generator.Random(10, 10); break; //Yellow case 3: item.dyecolor = (byte)Saga.Utils.Generator.Random(17, 17); break; //Green case 4: item.dyecolor = (byte)Saga.Utils.Generator.Random(21, 21); break; //Blue case 5: item.dyecolor = (byte)Saga.Utils.Generator.Random(34, 34); break; //Purple } //The appearance has changed bool IsEquipment = cpkt.Container == 1; Regiontree tree = this.character.currentzone.Regiontree; foreach (Character target in tree.SearchActors(this.character, SearchFlags.Characters)) { if (!Point.IsInSightRangeByRadius(this.character.Position, target.Position)) continue; if (target.id == this.character.id) { //Adjust the item SMSG_ITEMADJUST spkt2 = new SMSG_ITEMADJUST(); spkt2.Container = cpkt.Container; spkt2.Slot = cpkt.Slot; spkt2.Value = item.dyecolor; spkt2.Function = 6; spkt2.SessionId = this.character.id; this.Send((byte[])spkt2); } else if (IsEquipment) { SMSG_CHANGEEQUIPMENT spkt = new SMSG_CHANGEEQUIPMENT(); spkt.ActorID = this.character.id; spkt.ItemID = (item != null) ? item.info.item : 0; spkt.Dye = (item != null) ? (byte)item.dyecolor : (byte)0; spkt.Slot = cpkt.Slot; spkt.SessionId = target.id; target.client.Send((byte[])spkt); } } //Remove item if (dyeitem.count - 1 > 0) { dyeitem.count -= 1; SMSG_ITEMADJUST spkt3 = new SMSG_ITEMADJUST(); spkt3.Container = 2; spkt3.Slot = cpkt.Index; spkt3.Value = (byte)dyeitem.count; spkt3.Function = 4; spkt3.SessionId = this.character.id; this.Send((byte[])spkt3); } else { this.character.container.RemoveAt(cpkt.Index); SMSG_DELETEITEM spkt3 = new SMSG_DELETEITEM(); spkt3.UpdateReason = 0; spkt3.Index = cpkt.Index; spkt3.SessionId = this.character.id; spkt3.Container = 2; this.Send((byte[])spkt3); } } } finally { SMSG_USEDYEITEM spkt = new SMSG_USEDYEITEM(); if (dyeitem != null) spkt.ItemId = dyeitem.info.item; spkt.Equipment = cpkt.Slot; spkt.Container = cpkt.Container; spkt.Result = result; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } }
/// <summary> /// Function is called when a user tries to unlock a new /// weapon slot. /// </summary> /// <param name="cpkt"></param> private void CM_USEADMISSIONWEAPON(CMSG_USEWEAPONADMISSION cpkt) { byte error = 0; try { Rag2Item item = this.character.container[cpkt.Index]; if (item == null) { error = (byte)Generalerror.InventoryItemNotFound; } else if (this.character.weapons.UnlockedWeaponSlots >= 5) { error = (byte)Generalerror.AllWeaponSlotsOpen; } else { this.character.weapons.UnlockedWeaponSlots++; } if (item.count - 1 > 0) { item.count -= 1; SMSG_ITEMADJUST spkt3 = new SMSG_ITEMADJUST(); spkt3.Container = 2; spkt3.Slot = cpkt.Index; spkt3.Value = (byte)item.count; spkt3.Function = 4; spkt3.SessionId = this.character.id; this.Send((byte[])spkt3); } else { this.character.container.RemoveAt(cpkt.Index); SMSG_DELETEITEM spkt3 = new SMSG_DELETEITEM(); spkt3.UpdateReason = 0; spkt3.Index = cpkt.Index; spkt3.SessionId = this.character.id; spkt3.Container = 2; this.Send((byte[])spkt3); } } finally { SMSG_WEAPONMAX spkt = new SMSG_WEAPONMAX(); spkt.Result = error; spkt.Count = this.character.weapons.UnlockedWeaponSlots; spkt.SessionId = this.character.id; this.Send((byte[])spkt); } }
/// <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 }