GetProductionSuccessChance() public method

Calculates and returns general production skill success chance.
Unofficial, but seems to work fine in most cases. Dex bonus: http://mabination.com/threads/57123-Chaos-Life-Skill-Guide-Refining
public GetProductionSuccessChance ( Skill skill, ProductionCategory category, int baseChance, int rainBonus ) : float
skill Skill
category ProductionCategory
baseChance int
rainBonus int
return float
Esempio n. 1
0
		/// <summary>
		/// Completes production.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public void Complete(Creature creature, Skill skill, Packet packet)
		{
			var unkByte = packet.GetByte();
			var propEntityId = 0L;
			var unkInt = 0;
			if (packet.Peek() == PacketElementType.Long) // Rule unknown
			{
				propEntityId = packet.GetLong();
				unkInt = packet.GetInt();
			}
			var productId = packet.GetInt();
			var unkShort = packet.GetShort();
			var category = (ProductionCategory)packet.GetShort();
			var amountToProduce = packet.GetShort();
			var count = packet.GetByte();
			var materials = new List<ProductionMaterial>(count);
			for (int i = 0; i < count; ++i)
			{
				var entityId = packet.GetLong();
				var amount = packet.GetShort();

				// Check item
				var item = creature.Inventory.GetItem(entityId);
				if (item == null)
				{
					Log.Warning("ProductionSkill.Prepare: Creature '{0:X16}' tried to use non-existent item as material.", creature.EntityId);
					return;
				}

				materials.Add(new ProductionMaterial(item, amount));
			}

			// Check prop
			if (!this.CheckProp(creature, propEntityId))
				goto L_Fail;

			// Check category
			if (!this.CheckCategory(creature, category))
			{
				Log.Warning("ProductionSkill.Complete: Creature '{0:X16}' tried to use category '{1}' with skill '{2}'.", creature.EntityId, category, this.GetType().Name);
				goto L_Fail;
			}

			// Get potential products
			// Some productions can produce items of varying quality (cheap,
			// common, fine, finest)
			var potentialProducts = AuraData.ProductionDb.Find(category, productId);
			if (potentialProducts.Length == 0)
			{
				Send.ServerMessage(creature, "Unknown product.");
				goto L_Fail;
			}

			// Get reference product for checks and mats
			var productData = potentialProducts[0];

			// Check tools
			if (!this.CheckTools(creature, skill, productData))
				goto L_Fail;

			// Check mana
			if (!this.CheckMana(creature, productData))
				goto L_Fail;

			if (productData.Mana > 0)
			{
				creature.Mana -= productData.Mana;
				Send.StatUpdate(creature, StatUpdateType.Private, Stat.Mana);
			}

			// Check materials
			var requiredMaterials = productData.GetMaterialList();
			var toReduce = new List<ProductionMaterial>();
			var inUse = new HashSet<long>();
			foreach (var reqMat in requiredMaterials)
			{
				// Check all selected items for tag matches
				foreach (var material in materials)
				{
					// Check item and stack item for tag, pouches can be put
					// into the window, reducing the contained items.
					var match =
						material.Item.HasTag(reqMat.Tag) ||
						(material.Item.IsGatheringPouch && material.Item.Data.StackItem != null && material.Item.Data.StackItem.HasTag(reqMat.Tag));

					// Satisfy requirement with item, up to the max amount
					// needed or available
					if (match)
					{
						// Cancel if one item matches multiple materials.
						// It's unknown how this would be handled, can it even
						// happen? Can one item maybe only be used as one material?
						if (inUse.Contains(material.Item.EntityId))
						{
							Send.ServerMessage(creature, Localization.Get("Unable to handle request, please report, with this information: ({0}/{1})."), material.Item.Info.Id, productData.Id);
							Log.Warning("ProductionSkill.Complete: Item '{0}' matches multiple materials for product '{1}'.", material.Item.Info.Id, productData.Id);
							goto L_Fail;
						}

						var reduce = Math.Min(reqMat.Amount, material.Item.Amount);
						reqMat.Amount -= reduce;
						toReduce.Add(new ProductionMaterial(material.Item, reduce));
						inUse.Add(material.Item.EntityId);
					}

					// Break once we got what we need
					if (reqMat.Amount == 0)
						break;
				}
			}

			if (requiredMaterials.Any(a => a.Amount != 0))
			{
				// Unofficial, the client should normally prevent this.
				Send.ServerMessage(creature, Localization.Get("Insufficient materials."));
				goto L_Fail;
			}

			// Check success
			var rank = skill.Info.Rank <= SkillRank.R1 ? skill.Info.Rank : SkillRank.R1;
			var baseChance = potentialProducts.Sum(a => a.SuccessRates[rank]);
			var rainBonus = productData.RainBonus;
			var chance = creature.GetProductionSuccessChance(skill, category, baseChance, rainBonus);
			var rnd = RandomProvider.Get();
			var success = (rnd.Next(100) < chance);

			// Debug
			if (creature.Titles.SelectedTitle == TitleId.devCAT)
				Send.ServerMessage(creature, "Debug: Chance {0}%", chance);

			// Select random product
			// Do this here, so we have the data for skill training,
			// no matter the outcome.
			if (potentialProducts.Length > 1)
			{
				var itemId = 0;
				var num = rnd.NextDouble() * baseChance;
				var n = 0.0;
				foreach (var potentialProduct in potentialProducts)
				{
					n += potentialProduct.SuccessRates[rank];
					if (num <= n)
					{
						itemId = potentialProduct.ItemId;
						productData = potentialProduct;
						break;
					}
				}

				// Sanity check
				if (itemId == 0)
				{
					Log.Error("ProductionSkill.Complete: Failed to select random product item for {0}/{1}, num: {2}.", category, productId, num);
					Send.ServerMessage(creature, "Failed to generate product.");
					goto L_Fail;
				}
			}

			// Update tool's durability and proficiency
			this.UpdateTool(creature, productData);

			// Skill training
			this.SkillTraining(creature, skill, productData, success);

			// Reduce mats
			foreach (var material in toReduce)
			{
				// On fail of non-queued productions you lose 1~amount of
				// materials randomly
				var reduce = success ? material.Amount : rnd.Next(1, material.Amount + 1);
				if (reduce > 0)
					creature.Inventory.Decrement(material.Item, (ushort)reduce);
			}

			if (success)
			{
				// Check item
				var productItemData = AuraData.ItemDb.Find(productData.ItemId);
				if (productItemData == null)
				{
					Log.Error("ProductionSkill.Complete: Unknown product item '{0}'.", productData.ItemId);
					Send.ServerMessage(creature, "Unknown product item.");
					goto L_Fail;
				}

				// Create product
				var productItem = new Item(productData.ItemId);
				productItem.Amount = productData.Amount;

				// Add product to inventory
				creature.Inventory.Insert(productItem, true);

				// Material creation event
				ChannelServer.Instance.Events.OnCreatureProducedItem(new ProductionEventArgs(creature, productData, true, productItem));

				// Success
				Send.UseMotion(creature, 14, 0); // Success motion
				Send.Notice(creature, Localization.Get("{0} created successfully!"), productItem.Data.Name);
				Send.Echo(creature, Op.SkillComplete, packet);

				return;
			}

			// Material creation event
			ChannelServer.Instance.Events.OnCreatureProducedItem(new ProductionEventArgs(creature, productData, false, null));

		L_Fail:
			// Unofficial
			Send.UseMotion(creature, 14, 3); // Fail motion
			Send.Echo(creature, Op.SkillComplete, packet);
		}