public void Complete(Creature creature, Skill skill, Packet packet)
{
var rnd = RandomProvider.Get();
var materials = new List<ProductionMaterial>();
var hits = new List<HammerHit>();
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)
{
// Hits
if (!this.ReadHits(creature, packet, out hits))
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 (!this.CheckTools(creature))
goto L_Fail;
// Get manual
var manualId = creature.Magazine.MetaData1.GetInt("FORMID");
var manualData = AuraData.ManualDb.Find(ManualCategory.Blacksmithing, manualId);
if (manualData == null)
{
Log.Error("Blacksmithing.Complete: Manual '{0}' not found.", manualId);
Send.ServerMessage(creature, Localization.Get("Failed to look up manual, please report."));
goto L_Fail;
}
// 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);
var success = true;
var isNewItem = false;
Item item = null;
// Get item to work on
if (existingItemEntityId == 0)
{
// Create new item
item = new Item(manualData.ItemId);
item.OptionInfo.Flags |= ItemFlags.Incomplete;
item.MetaData1.SetFloat(ProgressVar, 0);
isNewItem = true;
}
else
{
// Get item
item = creature.Inventory.GetItem(existingItemEntityId);
if (item == null)
{
Log.Warning("Blacksmithing.Complete: Creature '{0:X16}' tried to work on non-existing item.", creature.EntityId);
goto L_Fail;
}
// Check id against manual
if (item.Info.Id != manualData.ItemId)
{
Log.Warning("Blacksmithing.Complete: Creature '{0:X16}' tried use an item with a different id than the manual.", creature.EntityId);
goto L_Fail;
}
// Check progress
if (!item.MetaData1.Has(ProgressVar))
{
Log.Warning("Blacksmithing.Complete: Creature '{0:X16}' tried work on an item that is already finished.", creature.EntityId);
goto L_Fail;
}
}
// Finish item if progress is >= 1, otherwise increase progress.
var progress = item.MetaData1.GetFloat(ProgressVar);
if (progress < 1)
{
// 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);
var msg = "";
ProgressResult result;
// Apply RNG fail/success
if (!success)
{
// 25% chance for very bad
if (rngFailSuccess < 0.25f)
{
msg += Localization.Get("Yikes. That was terrible. Are you feeling okay?");
addProgress /= 2f;
result = ProgressResult.VeryBad;
}
// 75% chance for bad
else
{
msg += Localization.Get("Failed...");
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("A smashing success!!!");
addProgress *= 2f;
result = ProgressResult.VeryGood;
}
// 85% 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("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
progress = Math.Min(1, progress + addProgress);
item.MetaData1.SetFloat(ProgressVar, progress);
// Message
if (progress == 1)
msg += Localization.Get("\nFinal Stage remaining");
else
msg += string.Format(Localization.Get("\n{0}% completed."), (int)(progress * 100));
Send.Notice(creature, msg);
this.OnProgress(creature, skill, item, result);
// Event
ChannelServer.Instance.Events.OnCreatureFinishedProductionOrCollection(creature, result >= ProgressResult.Good);
}
else
{
var quality = this.CalculateQuality(hits, creature.Temp.BlacksmithingMiniGameDots);
this.FinishItem(creature, skill, manualData, creature.Temp.CreationFinishId, item, quality);
this.OnProgress(creature, skill, item, ProgressResult.Finish);
// Creation event
ChannelServer.Instance.Events.OnCreatureCreatedItem(new CreationEventArgs(creature, CreationMethod.Blacksmithing, item, manualData.Rank));
}
// Add or update item
if (!isNewItem)
Send.ItemUpdate(creature, item);
else
creature.Inventory.Add(item, true);
// 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);
}