Example #1
0
        /// <summary>
        /// Called once ready to pull the fish out.
        /// </summary>
        /// <remarks>
        /// When you catch something just before running out of bait,
        /// and you send MotionCancel2 from Cancel, there's a
        /// visual bug on Aura, where the item keeps flying to you until
        /// you move. This does not happen on NA for unknown reason.
        /// The workaround: Check for cancellation in advance and only
        /// send the real in effect if the skill wasn't canceled.
        /// </remarks>
        /// <param name="creature"></param>
        /// <param name="method">Method used on this try</param>
        /// <param name="success">Success of manual try</param>
        public void OnResponse(Creature creature, FishingMethod method, bool success)
        {
            // Get skill
            var skill = creature.Skills.Get(SkillId.Fishing);

            if (skill == null)
            {
                Log.Error("Fishing.OnResponse: Missing skill.");
                return;
            }

            var rnd = RandomProvider.Get();

            // Update prop state
            creature.Temp.FishingProp.SetState("empty");

            // Get auto success
            if (method == FishingMethod.Auto)
            {
                success = rnd.NextDouble() < skill.RankData.Var3 / 100f;
            }

            // Perfect fishing
            if (ChannelServer.Instance.Conf.World.PerfectFishing)
            {
                success = true;
            }

            // Check fishing ground
            if (creature.Temp.FishingDrop == null)
            {
                Send.ServerMessage(creature, "Error: No items found.");
                Log.Error("Fishing.OnResponse: Failing, no drop found.");
                success = false;
            }

            // Check equipment
            if (!this.CheckEquipment(creature))
            {
                Send.ServerMessage(creature, "Error: Missing equipment.");
                Log.Error("Fishing.OnResponse: Failing, Missing equipment.");
                // TODO: Security violation once we're sure this can't happen
                //   without modding.
                success = false;
            }

            var cancel = false;

            // Reduce durability
            if (creature.RightHand != null)
            {
                creature.Inventory.ReduceDurability(creature.RightHand, 15);

                // Check rod durability
                if (creature.RightHand.Durability == 0)
                {
                    cancel = true;
                }
            }

            // Remove bait
            if (creature.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteBait)
            {
                creature.Inventory.Decrement(creature.Magazine);

                // Check if bait was removed because it was empty
                if (creature.Magazine == null)
                {
                    cancel = true;
                }
            }

            // Fail
            Item item = null;

            if (!success)
            {
                Send.Notice(creature, Localization.Get("I was hesistating for a bit, and it got away..."));                 // More responses?
                Send.Effect(creature, Effect.Fishing, (byte)FishingEffectType.Fall, true);
            }
            // Success
            else
            {
                var propName = "prop_caught_objbox_01";
                var propSize = 0;
                var size     = 0;
                var dropData = creature.Temp.FishingDrop;

                // Create item
                if (dropData.QuestId != 0)
                {
                    item = Item.CreateQuestScroll(dropData.QuestId);
                }
                else
                {
                    item = new Item(dropData);
                }

                // Check fish
                var fish = AuraData.FishDb.Find(dropData.ItemId);
                if (fish != null)
                {
                    propName = fish.PropName;
                    propSize = fish.PropSize;

                    // Random fish size, unofficial
                    if (fish.SizeMin + fish.SizeMax != 0)
                    {
                        var min = fish.SizeMin;
                        var max = fish.SizeMax;

                        // Var1 bonus
                        min += (int)skill.RankData.Var1;

                        // Var4 bonus
                        min += (int)Math.Max(0, (item.Data.BaseSize - fish.SizeMin) / 100f * skill.RankData.Var4);

                        // Modify min and max, so the size falls into big or
                        // small territory.
                        var mid = (max - min) / 2;
                        if (creature.Temp.CatchSize == CatchSize.BigOne)
                        {
                            min += mid;
                        }
                        else
                        {
                            max -= mid;
                        }

                        // Cap
                        if (max < min)
                        {
                            max = min;
                        }
                        if (min > max)
                        {
                            min = max;
                        }

                        size = Math2.Clamp(fish.SizeMin, fish.SizeMax, rnd.Next(min, max + 1));
                        var scale = (1f / item.Data.BaseSize * size);

                        item.MetaData1.SetFloat("SCALE", scale);
                    }
                }

                // Set equipment durability to 0, does not apply to
                // unrepairable items, like Gargoyle Swords.
                // http://wiki.mabinogiworld.com/view/Fishing#Details
                if (item.HasTag("/equip/") && !item.HasTag("/not_repairable/"))
                {
                    item.Durability = 0;
                }

                // Drop if inv add failed
                List <Item> changed;
                if (!creature.Inventory.Insert(item, false, out changed))
                {
                    item.Drop(creature.Region, creature.GetPosition(), 100, creature, false);
                }

                var itemEntityId = (changed == null || changed.Count == 0 ? item.EntityId : changed.First().EntityId);

                // Show acquire using the item's entity id if it wasn't added
                // to a stack, or using the stack's id if it was.
                Send.AcquireInfo2(creature, "fishing", itemEntityId);

                // Holding up fish effect
                if (!cancel)
                {
                    Send.Effect(creature, Effect.Fishing, (byte)FishingEffectType.ReelIn, true, creature.Temp.FishingProp.EntityId, item.Info.Id, size, propName, propSize);
                }
            }

            creature.Temp.FishingDrop = null;

            // Handle training
            this.Training(creature, skill, success, item);

            // Fishing event
            ChannelServer.Instance.Events.OnCreatureFished(creature, item);

            // Cancel
            if (cancel)
            {
                creature.Skills.CancelActiveSkill();
                return;
            }

            // Next round
            this.StartFishing(creature, 6000);
        }
Example #2
0
        /// <summary>
        /// Completes skill, creating the items.
        /// </summary>
        /// <param name="creature"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Complete(Creature creature, Skill skill, Packet packet)
        {
            var materials = new List <ProductionMaterial>();
            var stitches  = new List <Point>();

            var stage                = (Stage)packet.GetByte();
            var propEntityId         = packet.GetLong();
            var unkInt1              = packet.GetInt();
            var existingItemEntityId = packet.GetLong();
            var finishId             = packet.GetInt();

            if (stage == Stage.Progression)
            {
                // Materials
                if (!this.ReadMaterials(creature, packet, out materials))
                {
                    goto L_Fail;
                }
            }
            else if (stage == Stage.Finish)
            {
                // Stitches
                if (!this.ReadStitches(creature, packet, out stitches))
                {
                    goto L_Fail;
                }
            }
            else
            {
                Send.ServerMessage(creature, Localization.Get("Stage error, please report."));
                Log.Error("Tailoring: Unknown progress stage '{0}'.", stage);
                goto L_Fail;
            }

            // Check tools
            if (!CheckTools(creature))
            {
                goto L_Fail;
            }

            // Get manual
            var manualId   = creature.Magazine.MetaData1.GetInt("FORMID");
            var manualData = AuraData.ManualDb.Find(ManualCategory.Tailoring, manualId);

            if (manualData == null)
            {
                Log.Error("Tailoring.Complete: Manual '{0}' not found.", manualId);
                Send.ServerMessage(creature, Localization.Get("Failed to look up pattern, please report."));
                goto L_Fail;
            }

            // Check existing item
            Item existingItem = null;

            if (existingItemEntityId != 0)
            {
                // Get item
                existingItem = creature.Inventory.GetItem(existingItemEntityId);
                if (existingItem == null)
                {
                    Log.Warning("Tailoring.Complete: Creature '{0:X16}' tried to work on non-existing item.", creature.EntityId);
                    goto L_Fail;
                }

                // Check id against manual
                if (existingItem.Info.Id != manualData.ItemId)
                {
                    Log.Warning("Tailoring.Complete: Creature '{0:X16}' tried use an item with a different id than the manual.", creature.EntityId);
                    goto L_Fail;
                }

                // Check progress
                if (!existingItem.MetaData1.Has(ProgressVar))
                {
                    Log.Warning("Tailoring.Complete: Creature '{0:X16}' tried work on an item that is already finished.", creature.EntityId);
                    goto L_Fail;
                }
            }

            var rnd = RandomProvider.Get();

            // Materials are only sent to Complete for progression,
            // finish materials are handled in Prepare.
            if (stage == Stage.Progression)
            {
                var requiredMaterials = manualData.GetMaterialList();

                // Get items to decrement
                List <ProductionMaterial> toDecrement;
                if (!this.GetItemsToDecrement(creature, Stage.Progression, manualData, requiredMaterials, materials, out toDecrement))
                {
                    goto L_Fail;
                }

                // Decrement mats
                this.DecrementMaterialItems(creature, toDecrement, rnd);
            }

            // Reduce durability
            creature.Inventory.ReduceDurability(creature.RightHand, ToolDurabilityLoss);
            creature.Inventory.ReduceDurability(creature.Magazine, ManualDurabilityLoss);

            // Get to work
            var newItem = false;
            var success = false;
            var msg     = "";

            // Create new item
            if (existingItem == null)
            {
                existingItem = new Item(manualData.ItemId);
                existingItem.OptionInfo.Flags |= ItemFlags.Incomplete;
                existingItem.MetaData1.SetFloat(ProgressVar, 0);
                existingItem.MetaData1.SetLong(StclmtVar, DateTime.Now);

                newItem = true;
            }

            // Finish item if progress is >= 1, otherwise increase progress.
            var            progress = (newItem ? 0 : existingItem.MetaData1.GetFloat(ProgressVar));
            ProgressResult result;

            if (progress < 1)
            {
                // TODO: Random quality gain/loss?
                //   "The combination of tools and materials was quite good! (Quality +{0}%)"

                // Get success
                // Unofficial and mostly based on guessing. If process was
                // determined to be successful, a good result will happen,
                // if not, a bad one. Both are then split into very good
                // and very bad, based on another random number.
                var chance = this.GetSuccessChance(creature, skill, manualData.Rank);
                success = (rnd.NextDouble() * 100 < chance);
                var rngFailSuccess = rnd.NextDouble();

                // Calculate progress to add
                // Base line is between 50 and 100% of the max progress from
                // the db. For example, a Popo's skirt has 200%, which should
                // always put it on 100% instantly, as long as it's a success.
                var addProgress = rnd.Between(manualData.MaxProgress / 2, manualData.MaxProgress);
                var rankDiff    = ((int)skill.Info.Rank - (int)manualData.Rank);

                // Apply RNG fail/success
                if (!success)
                {
                    // 25% chance for very bad
                    if (rngFailSuccess < 0.25f)
                    {
                        msg         += Localization.Get("Catastrophic failure!");
                        addProgress /= 2f;
                        result       = ProgressResult.VeryBad;
                    }
                    // 75% chance for bad
                    else
                    {
                        msg         += Localization.Get("That didn't go so well...");
                        addProgress /= 1.5f;
                        result       = ProgressResult.Bad;
                    }
                }
                else
                {
                    // 25% chance for best, if manual is >= 2 ranks
                    if (rngFailSuccess < 0.25f && rankDiff <= -2)
                    {
                        msg         += Localization.Get("You created a masterpiece!");
                        addProgress *= 2f;
                        result       = ProgressResult.VeryGood;
                    }
                    // 75% chance for good
                    else
                    {
                        // Too easy if more than two ranks below, which counts
                        // as a training fail, according to the Wiki.
                        if (rankDiff >= 2)
                        {
                            msg   += Localization.Get("You did it, but that was way too easy.");
                            result = ProgressResult.Bad;
                        }
                        else
                        {
                            msg   += Localization.Get("Success!");
                            result = ProgressResult.Good;
                        }
                    }
                }

                // Weather bonus
                if (ChannelServer.Instance.Weather.GetWeatherType(creature.RegionId) == WeatherType.Rain)
                {
                    addProgress += manualData.RainBonus;
                }

                progress = Math.Min(1, progress + addProgress);
                existingItem.MetaData1.SetFloat(ProgressVar, progress);

                if (progress == 1)
                {
                    msg += Localization.Get("\nFinal Stage remaining");
                }
                else
                {
                    msg += string.Format(Localization.Get("\n{0}% completed."), (int)(progress * 100));
                }

                this.OnProgress(creature, skill, result);
                Send.Notice(creature, msg);

                // Event
                ChannelServer.Instance.Events.OnCreatureFinishedProductionOrCollection(creature, success);
            }
            else
            {
                var quality = this.CalculateQuality(stitches, creature.Temp.TailoringMiniGameX, creature.Temp.TailoringMiniGameY);
                this.FinishItem(creature, skill, manualData, 0, existingItem, quality);
                this.OnProgress(creature, skill, ProgressResult.Finish);

                // Creation event
                ChannelServer.Instance.Events.OnCreatureCreatedItem(new CreationEventArgs(creature, CreationMethod.Tailoring, existingItem, manualData.Rank));

                result  = ProgressResult.Finish;
                success = true;
            }

            // Add or update item
            if (!newItem)
            {
                Send.ItemUpdate(creature, existingItem);
            }
            else
            {
                creature.Inventory.Add(existingItem, true);
            }

            // Acquire info once it's finished and updated.
            if (result == ProgressResult.Finish)
            {
                Send.AcquireInfo2(creature, "tailoring", existingItem.EntityId);
            }

            // Success motion if it was a good result, otherwise keep
            // going to fail.
            if (success)
            {
                Send.UseMotion(creature, 14, 0);                 // Success motion
                Send.Echo(creature, packet);
                return;
            }

L_Fail:
            Send.UseMotion(creature, 14, 3);             // Fail motion
            Send.Echo(creature, packet);
        }
Example #3
0
        /// <summary>
        /// Called once ready to pull the fish out.
        /// </summary>
        /// <remarks>
        /// When you catch something just before running out of bait,
        /// and you send MotionCancel2 from Cancel, there's a
        /// visual bug on Aura, where the item keeps flying to you until
        /// you move. This does not happen on NA for unknown reason.
        /// The workaround: Check for cancellation in advance and only
        /// send the real in effect if the skill wasn't canceled.
        /// </remarks>
        /// <param name="creature"></param>
        /// <param name="method">Method used on this try</param>
        /// <param name="success">Success of manual try</param>
        public void OnResponse(Creature creature, FishingMethod method, bool success)
        {
            // Get skill
            var skill = creature.Skills.Get(SkillId.Fishing);

            if (skill == null)
            {
                Log.Error("Fishing.OnResponse: Missing skill.");
                return;
            }

            var rnd = RandomProvider.Get();

            // Update prop state
            // TODO: update prop state method
            creature.Temp.FishingProp.SetState("empty");

            // Get auto success
            if (method == FishingMethod.Auto)
            {
                success = rnd.NextDouble() < skill.RankData.Var3 / 100f;
            }

            // Perfect fishing
            if (ChannelServer.Instance.Conf.World.PerfectFishing)
            {
                success = true;
            }

            // Check fishing ground
            if (creature.Temp.FishingDrop == null)
            {
                Send.ServerMessage(creature, "Error: No items found.");
                Log.Error("Fishing.OnResponse: Failing, no drop found.");
                success = false;
            }

            // Check equipment
            if (!this.CheckEquipment(creature))
            {
                Send.ServerMessage(creature, "Error: Missing equipment.");
                Log.Error("Fishing.OnResponse: Failing, Missing equipment.");
                // TODO: Security violation once we're sure this can't happen
                //   without modding.
                success = false;
            }

            var cancel = false;

            // Reduce durability
            if (creature.RightHand != null && !ChannelServer.Instance.Conf.World.NoDurabilityLoss)
            {
                var reduce = 15;

                // Half dura loss if blessed
                if (creature.RightHand.IsBlessed)
                {
                    reduce = Math.Max(1, reduce / 2);
                }

                creature.RightHand.Durability -= reduce;
                Send.ItemDurabilityUpdate(creature, creature.RightHand);

                // Check rod durability
                if (creature.RightHand.Durability == 0)
                {
                    cancel = true;
                }
            }

            // Remove bait
            if (creature.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteBait)
            {
                creature.Inventory.Decrement(creature.Magazine);

                // Check if bait was removed because it was empty
                if (creature.Magazine == null)
                {
                    cancel = true;
                }
            }

            // Fail
            Item item = null;

            if (!success)
            {
                Send.Notice(creature, Localization.Get("I was hesistating for a bit, and it got away..."));                 // More responses?
                Send.Effect(creature, Effect.Fishing, (byte)FishingEffectType.Fall, true);
            }
            // Success
            else
            {
                var propName = "prop_caught_objbox_01";
                var propSize = 0;
                var size     = 0;

                // Create item
                item = new Item(creature.Temp.FishingDrop);

                // Check fish
                var fish = AuraData.FishDb.Find(creature.Temp.FishingDrop.ItemId);
                if (fish != null)
                {
                    propName = fish.PropName;
                    propSize = fish.PropSize;

                    // Random fish size, unofficial
                    if (fish.SizeMin + fish.SizeMax != 0)
                    {
                        var min = fish.SizeMin + (int)Math.Max(0, (item.Data.BaseSize - fish.SizeMin) / 100f * skill.RankData.Var4);
                        var max = fish.SizeMax;

                        size = Math2.Clamp(fish.SizeMin, fish.SizeMax, rnd.Next(min, max + 1) + (int)skill.RankData.Var1);
                        var scale = (1f / item.Data.BaseSize * size);

                        item.MetaData1.SetFloat("SCALE", scale);
                    }
                }

                // Set equipment durability
                if (item.HasTag("/equip/") && item.OptionInfo.DurabilityMax >= 1)
                {
                    item.Durability = 0;
                }

                // Drop if inv add failed
                List <Item> changed;
                if (!creature.Inventory.Insert(item, false, out changed))
                {
                    item.Drop(creature.Region, creature.GetPosition().GetRandomInRange(100, rnd), creature, false);
                }

                var itemEntityId = (changed == null || changed.Count == 0 ? item.EntityId : changed.First().EntityId);

                // Show acquire using the item's entity id if it wasn't added
                // to a stack, or using the stack's id if it was.
                Send.AcquireInfo2(creature, "fishing", itemEntityId);

                // Holding up fish effect
                if (!cancel)
                {
                    Send.Effect(creature, Effect.Fishing, (byte)FishingEffectType.ReelIn, true, creature.Temp.FishingProp.EntityId, item.Info.Id, size, propName, propSize);
                }
            }

            creature.Temp.FishingDrop = null;

            // Handle training
            this.Training(creature, skill, success, item);

            // Cancel
            if (cancel)
            {
                creature.Skills.CancelActiveSkill();
                return;
            }

            // Next round
            this.StartFishing(creature, 6000);
        }