public UpgradeResult Upgrade(string upgradeReply)
{
var result = new UpgradeResult();
// Example: @upgrade:22518872341757176:broad_sword_balance1
var args = upgradeReply.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (args.Length != 3 || !long.TryParse(args[1], out result.ItemEntityId))
{
Log.Warning("NpcScript.Upgrade: Player '{0:X16}' (Account: {1}) sent invalid reply.", this.Player.EntityId, this.Player.Client.Account.Id);
return result;
}
// Get item
result.Item = this.Player.Inventory.GetItem(result.ItemEntityId);
if (result.Item == null || result.Item.OptionInfo.Upgraded == result.Item.OptionInfo.UpgradeMax)
{
Log.Warning("NpcScript.Upgrade: Player '{0:X16}' (Account: {1}) tried to upgrade invalid item.", this.Player.EntityId, this.Player.Client.Account.Id);
return result;
}
// Get upgrade and check item and NPCs
result.Upgrade = AuraData.ItemUpgradesDb.Find(args[2]);
if (result.Upgrade == null)
{
Log.Warning("NpcScript.Upgrade: Player '{0:X16}' (Account: {1}) tried to apply an unknown upgrade ({2}).", this.Player.EntityId, this.Player.Client.Account.Id, args[2]);
return result;
}
// Check upgrade and item
if (!result.Item.Data.HasTag(result.Upgrade.Filter) || result.Item.Proficiency < result.Upgrade.Exp || !Math2.Between(result.Item.OptionInfo.Upgraded, result.Upgrade.UpgradeMin, result.Upgrade.UpgradeMax))
{
Log.Warning("NpcScript.Upgrade: Player '{0:X16}' (Account: {1}) tried to apply upgrade to invalid item.", this.Player.EntityId, this.Player.Client.Account.Id);
return result;
}
if (!result.Upgrade.Npcs.Contains(this.NPC.Name.TrimStart('_').ToLower()))
{
Log.Warning("NpcScript.Upgrade: Player '{0:X16}' (Account: {1}) tried to apply upgrade '{2}' at an invalid NPC ({3}).", this.Player.EntityId, this.Player.Client.Account.Id, result.Upgrade.Ident, this.NPC.Name.TrimStart('_').ToLower());
return result;
}
// Check for disabled Artisan
// TODO: Feature check, once we do have Artisan.
if (result.Upgrade.Effects.Any(a => a.Key == "Artisan"))
{
Send.MsgBox(this.Player, Localization.Get("Artisan upgrades aren't available yet."));
return result;
}
// Check gold
if (this.Gold < result.Upgrade.Gold)
return result;
// Take gold and exp
result.Item.Proficiency -= result.Upgrade.Exp;
this.Gold -= result.Upgrade.Gold;
// Increase upgrade count
result.Item.OptionInfo.Upgraded++;
if (ChannelServer.Instance.Conf.World.UnlimitedUpgrades && result.Item.OptionInfo.Upgraded == result.Item.OptionInfo.UpgradeMax)
result.Item.OptionInfo.Upgraded = 0;
// Upgrade
foreach (var effect in result.Upgrade.Effects)
{
switch (effect.Key)
{
case "MinAttack": result.Item.OptionInfo.AttackMin = (ushort)Math2.Clamp(1, result.Item.OptionInfo.AttackMax, result.Item.OptionInfo.AttackMin + effect.Value[0]); break;
case "MaxAttack":
result.Item.OptionInfo.AttackMax = (ushort)Math2.Clamp(1, ushort.MaxValue, result.Item.OptionInfo.AttackMax + effect.Value[0]);
if (result.Item.OptionInfo.AttackMax < result.Item.OptionInfo.AttackMin)
result.Item.OptionInfo.AttackMin = result.Item.OptionInfo.AttackMax;
break;
case "MinInjury": result.Item.OptionInfo.InjuryMin = (ushort)Math2.Clamp(0, result.Item.OptionInfo.InjuryMax, result.Item.OptionInfo.InjuryMin + effect.Value[0]); break;
case "MaxInjury":
result.Item.OptionInfo.InjuryMax = (ushort)Math2.Clamp(0, ushort.MaxValue, result.Item.OptionInfo.InjuryMax + effect.Value[0]);
if (result.Item.OptionInfo.InjuryMax < result.Item.OptionInfo.InjuryMin)
result.Item.OptionInfo.InjuryMin = result.Item.OptionInfo.InjuryMax;
break;
case "Balance": result.Item.OptionInfo.Balance = (byte)Math2.Clamp(0, byte.MaxValue, result.Item.OptionInfo.Balance + effect.Value[0]); break;
case "Critical": result.Item.OptionInfo.Critical = (sbyte)Math2.Clamp(0, sbyte.MaxValue, result.Item.OptionInfo.Critical + effect.Value[0]); break;
case "Defense": result.Item.OptionInfo.Defense = (int)Math2.Clamp(0, int.MaxValue, result.Item.OptionInfo.Defense + (long)effect.Value[0]); break;
case "Protection": result.Item.OptionInfo.Protection = (short)Math2.Clamp(0, short.MaxValue, result.Item.OptionInfo.Protection + effect.Value[0]); break;
case "AttackRange": result.Item.OptionInfo.EffectiveRange = (short)Math2.Clamp(0, short.MaxValue, result.Item.OptionInfo.EffectiveRange + effect.Value[0]); break;
case "MaxDurability":
result.Item.OptionInfo.DurabilityMax = (int)Math2.Clamp(1000, int.MaxValue, result.Item.OptionInfo.DurabilityMax + (long)(effect.Value[0] * 1000));
if (result.Item.OptionInfo.DurabilityMax < result.Item.OptionInfo.Durability)
result.Item.OptionInfo.Durability = result.Item.OptionInfo.DurabilityMax;
break;
case "MagicDefense":
// MDEF:f:1.000000;MPROT:f:1.000000;MTWR:1:1;
var mdef = result.Item.MetaData1.GetFloat("MDEF");
result.Item.MetaData1.SetFloat("MDEF", Math2.Clamp(0, int.MaxValue, mdef + effect.Value[0]));
break;
case "MagicProtection":
// MDEF:f:1.000000;MPROT:f:1.000000;MTWR:1:1;
var mprot = result.Item.MetaData1.GetFloat("MPROT");
result.Item.MetaData1.SetFloat("MPROT", Math2.Clamp(0, int.MaxValue, mprot + effect.Value[0]));
break;
case "ManaUse":
// WU:s:00000003000000
var manaUseWU = new WUUpgrades(result.Item.MetaData1.GetString("WU"));
manaUseWU.ManaUse += (sbyte)effect.Value[0];
result.Item.MetaData1.SetString("WU", manaUseWU.ToString());
break;
case "ManaBurn":
var manaBurnWU = new WUUpgrades(result.Item.MetaData1.GetString("WU"));
// Prior to G15S2 players lost all their Mana when
// they unequipped a wand. This was removed via
// feature, but before that this upgrade allowed
// one to reduce the amount of Mana lost.
// Afterwards the ManaBurn upgrade was turned into
// a ManaUse automatically, but the bonus was halfed,
// meaning if a ManaBurn upgrade gave -4% burn,
// it gave -2% use after this update.
if (!this.IsEnabled("ManaBurnRemove"))
manaBurnWU.ManaBurn += (sbyte)effect.Value[0];
else
manaBurnWU.ManaUse += (sbyte)(effect.Value[0] / 2);
result.Item.MetaData1.SetString("WU", manaBurnWU.ToString());
break;
case "ChainCasting":
// Chain Casting: +4, Magic Attack: +21
// EHLV:4:5;MTWR:1:1;OWNER:s:username;WU:s:30201400000015;
var chainCastWU = new WUUpgrades(result.Item.MetaData1.GetString("WU"));
chainCastWU.ChainCastSkillId = (ushort)effect.Value[0];
chainCastWU.ChainCastLevel = (byte)effect.Value[1];
result.Item.MetaData1.SetString("WU", chainCastWU.ToString());
break;
case "MagicDamage":
// Charging Speed: +12%, MA: +16
// EHLV:4:5;MTWR:1:1;OWNER:s:username;WU:s:00000000000c10;
var magicDmgWU = new WUUpgrades(result.Item.MetaData1.GetString("WU"));
magicDmgWU.MagicDamage += (sbyte)effect.Value[0];
result.Item.MetaData1.SetString("WU", magicDmgWU.ToString());
break;
case "CastingSpeed":
// Charging Speed: +12%, MA: +16
// EHLV:4:5;MTWR:1:1;OWNER:s:username;WU:s:00000000000c10;
var castingSpeedWU = new WUUpgrades(result.Item.MetaData1.GetString("WU"));
castingSpeedWU.CastingSpeed += (sbyte)effect.Value[0];
result.Item.MetaData1.SetString("WU", castingSpeedWU.ToString());
break;
case "MusicBuffBonus":
// MBB:4:8;MBD:4:10;MTWR:1:2;OTU:1:1;SPTEC:1:1;
var musicBuff = result.Item.MetaData1.GetInt("MBB");
result.Item.MetaData1.SetInt("MBB", musicBuff + (int)effect.Value[0]);
break;
case "MusicBuffDuration":
// MBB:4:8;MBD:4:10;MTWR:1:2;OTU:1:1;SPTEC:1:1;
var musicBuffDur = result.Item.MetaData1.GetInt("MBD");
result.Item.MetaData1.SetInt("MBD", musicBuffDur + (int)effect.Value[0]);
break;
case "CollectionBonus":
// CTBONUS:2:40;CTSPEED:4:750;MTWR:1:1;
var collectionBonusBuff = result.Item.MetaData1.GetShort("CTBONUS");
result.Item.MetaData1.SetShort("CTBONUS", (short)(collectionBonusBuff + effect.Value[0]));
break;
case "CollectionBonusProduct":
// CTBONUSPT:4:64004;CTBONUS:2:20;
result.Item.MetaData1.SetInt("CTBONUSPT", (int)effect.Value[0]);
break;
case "CollectionSpeed":
// CTBONUS:2:40;CTSPEED:4:750;MTWR:1:1;
var collectionSpeedBuff = result.Item.MetaData1.GetInt("CTSPEED");
result.Item.MetaData1.SetInt("CTSPEED", collectionSpeedBuff + (int)effect.Value[0]);
break;
case "LancePiercing":
// EHLV:4:5;LKUP:8:262244;LP:1:4;LP_E:1:0;OWNER:s:character;SPTRP:1:1; << Piercing Level 4
// LP:1:1;QUAL:4:70; << Piercing Level 1
var lancePiercingBuff = result.Item.MetaData1.GetByte("LP");
result.Item.MetaData1.SetByte("LP", (byte)(lancePiercingBuff + effect.Value[0]));
break;
case "SplashRadius":
// SP_DMG:f:0.250000;SP_RAD:4:70;
var splashRadiusBuff = result.Item.MetaData1.GetInt("SP_RAD");
result.Item.MetaData1.SetInt("SP_RAD", splashRadiusBuff + (int)effect.Value[0]);
break;
case "SplashDamage":
// SP_DMG:f:0.250000;SP_RAD:4:70;
var splashDamageBuff = result.Item.MetaData1.GetFloat("SP_DMG");
result.Item.MetaData1.SetFloat("SP_DMG", splashDamageBuff + effect.Value[0]);
break;
case "ImmuneMelee":
// IM_MGC:f:0.050000;IM_MLE:f:0.050000;IM_RNG:f:0.050000;MDEF:f:2.000000;MPROT:f:3.000000;OTU:1:1;
var immuneMeleeBuff = result.Item.MetaData1.GetFloat("IM_MLE");
result.Item.MetaData1.SetFloat("IM_MLE", immuneMeleeBuff + effect.Value[0]);
break;
case "ImmuneRanged":
// IM_MGC:f:0.050000;IM_MLE:f:0.050000;IM_RNG:f:0.050000;MDEF:f:2.000000;MPROT:f:3.000000;OTU:1:1;
var immuneRangedBuff = result.Item.MetaData1.GetFloat("IM_RNG");
result.Item.MetaData1.SetFloat("IM_RNG", immuneRangedBuff + effect.Value[0]);
break;
case "ImmuneMagic":
// IM_MGC:f:0.050000;IM_MLE:f:0.050000;IM_RNG:f:0.050000;MDEF:f:2.000000;MPROT:f:3.000000;OTU:1:1;
var immuneMagicBuff = result.Item.MetaData1.GetFloat("IM_MGC");
result.Item.MetaData1.SetFloat("IM_MGC", immuneMagicBuff + effect.Value[0]);
break;
// TODO:
// - MaxBullets
// - Artisan
default:
Log.Unimplemented("Item upgrade '{0}'", effect.Key);
break;
}
}
// Personalization
if (result.Upgrade.Personalize)
{
result.Item.OptionInfo.Flags |= ItemFlags.Personalized;
result.Item.MetaData1.SetString("OWNER", this.Player.Name);
}
// Update item
Send.ItemUpdate(this.Player, result.Item);
// Send result
Send.ItemUpgradeResult(this.Player, result.Item, result.Upgrade.Ident);
result.Success = true;
this.Player.Keywords.Give("ExperienceUpgrade");
return result;
}