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