context: using System;
  using Server.Mobiles;
  using Server.Items;
  using System.Collections.Generic;
  
  namespace Server.Engines.BulkOrders
  {
      public delegate Item ConstructCallback(int type);
  
      public sealed class RewardType
      {
          private readonly int m_Points;
          private readonly Type[] m_Types;
  
          public int Points
          {
              get
              {
                  return this.m_Points;
              }
          }
          public Type[] Types
          {
              get
              {
                  return this.m_Types;
              }
          }
  
          public RewardType(int points, params Type[] types)
          {
              this.m_Points = points;
              this.m_Types = types;
          }
  
          public bool Contains(Type type)
          {
              for (int i = 0; i < this.m_Types.Length; ++i)
              {
                  if (this.m_Types[i] == type)
                      return true;
              }
  
              return false;
          }
      }
  
      public sealed class RewardItem
      {
          private readonly int m_Weight;
          private readonly ConstructCallback m_Constructor;
          private readonly int m_Type;
  
          public int Weight
          {
              get
              {
                  return this.m_Weight;
              }
          }
          public ConstructCallback Constructor
          {
              get
              {
                  return this.m_Constructor;
              }
          }
          public int Type
          {
              get
              {
                  return this.m_Type;
              }
          }
  
          public RewardItem(int weight, ConstructCallback constructor)
              : this(weight, constructor, 0)
          {
          }
  
          public RewardItem(int weight, ConstructCallback constructor, int type)
          {
              this.m_Weight = weight;
              this.m_Constructor = constructor;
              this.m_Type = type;
          }
  
          public Item Construct()
          {
              try
              {
                  return this.m_Constructor(this.m_Type);
              }
              catch
              {
                  return null;
              }
          }
      }
  
      public class BODCollectionItem : CollectionItem
      { 
          public ConstructCallback Constructor { get; set; }
          public int RewardType { get; set; }
  
          public BODCollectionItem(int itemID, int tooltip, int hue, double points, ConstructCallback constructor, int type = 0)
              : base(null, itemID, tooltip, hue, points, false)
          {
              Constructor = constructor;
              RewardType = type;
          }
  		
  		
      }
  	
  	 public class BODCollectionItemS : CollectionItem
      { 
          public ConstructCallback Constructor { get; set; }
          public int RewardType { get; set; }
  
            public BODCollectionItemS(int itemID, string tooltip, int hue, double points, ConstructCallback constructor, int type = 0)
              : base(null, itemID, tooltip, hue, points, false)
          {
              Constructor = constructor;
              RewardType = type;
          }
  		
      }
  
      public sealed class RewardGroup
      {
          private readonly int m_Points;
          private readonly RewardItem[] m_Items;
  
          public int Points
          {
              get
              {
                  return this.m_Points;
              }
          }
          public RewardItem[] Items
          {
              get
              {
                  return this.m_Items;
              }
          }
  
          public RewardGroup(int points, params RewardItem[] items)
          {
              this.m_Points = points;
              this.m_Items = items;
          }
  
          public RewardItem AcquireItem()
          {
              if (this.m_Items.Length == 0)
                  return null;
              else if (this.m_Items.Length == 1)
                  return this.m_Items[0];
  
              int totalWeight = 0;
  
              for (int i = 0; i < this.m_Items.Length; ++i)
                  totalWeight += this.m_Items[i].Weight;
  
              int randomWeight = Utility.Random(totalWeight);
  
              for (int i = 0; i < this.m_Items.Length; ++i)
              {
                  RewardItem item = this.m_Items[i];
  
                  if (randomWeight < item.Weight)
                      return item;
  
                  randomWeight -= item.Weight;
              }
  
              return null;
          }
      }
  
      public abstract class RewardCalculator
      {
          private RewardGroup[] m_Groups;
  
          public RewardGroup[] Groups
          {
              get
              {
                  return this.m_Groups;
              }
              set
              {
                  this.m_Groups = value;
              }
          }
  
          public List<CollectionItem> RewardCollection { get; set; }
  
          public abstract int ComputePoints(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type);
  
          public abstract int ComputeGold(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type);
  
          public virtual int ComputeFame(SmallBOD bod)
          {
              int points = this.ComputePoints(bod) / 50;
  
              return points * points;
          }
  
          public virtual int ComputeFame(LargeBOD bod)
          {
              int points = this.ComputePoints(bod) / 50;
  
              return points * points;
          }
  
          public virtual int ComputePoints(SmallBOD bod)
          {
              return this.ComputePoints(bod.AmountMax, bod.RequireExceptional, bod.Material, 1, bod.Type);
          }
  
          public virtual int ComputePoints(LargeBOD bod)
          {
              Type type = bod.Entries == null || bod.Entries.Length == 0 ? null : bod.Entries[0].Details.Type;
  
              return this.ComputePoints(bod.AmountMax, bod.RequireExceptional, bod.Material, bod.Entries.Length, type);
          }
  
          public virtual int ComputeGold(SmallBOD bod)
          {
              return this.ComputeGold(bod.AmountMax, bod.RequireExceptional, bod.Material, 1, bod.Type);
          }
  
          public virtual int ComputeGold(LargeBOD bod)
          {
              return this.ComputeGold(bod.AmountMax, bod.RequireExceptional, bod.Material, bod.Entries.Length, bod.Entries[0].Details.Type);
          }
  
          public virtual RewardGroup LookupRewards(int points)
          {
              for (int i = this.m_Groups.Length - 1; i >= 1; --i)
              {
                  RewardGroup group = this.m_Groups[i];
  
                  if (points >= group.Points)
                      return group;
              }
  
              return this.m_Groups[0];
          }
  
          public virtual int LookupTypePoints(RewardType[] types, Type type)
          {
              for (int i = 0; i < types.Length; ++i)
              {
                  if (type == null || types[i].Contains(type))
                      return types[i].Points;
              }
  
              return 0;
          }
  
          protected static Item RewardTitle(int type)
          {
              return new BODRewardTitleDeed(type);
          }
  
          protected static Item NaturalDye(int type)
          {
              switch (type)
              {
                  default:
                  case 0: return new SpecialNaturalDye(DyeType.WindAzul);
                  case 1: return new SpecialNaturalDye(DyeType.DullRuby);
                  case 2: return new SpecialNaturalDye(DyeType.PoppieWhite);
                  case 3: return new SpecialNaturalDye(DyeType.WindAzul, true);
                  case 4: return new SpecialNaturalDye(DyeType.UmbranViolet, true);
                  case 5: return new SpecialNaturalDye(DyeType.ZentoOrchid, true);
                  case 6: return new SpecialNaturalDye(DyeType.DullRuby, true);
                  case 7: return new SpecialNaturalDye(DyeType.PoppieWhite, true);
                  case 8: return new SpecialNaturalDye(DyeType.UmbranViolet);
                  case 9: return new SpecialNaturalDye(DyeType.ZentoOrchid);
              }
          }
  
          protected static Item RockHammer(int type)
          {
              return new RockHammer();
          }
  
          protected static Item HarvestMap(int type)
          {
              return new HarvestMap((CraftResource)type);
          }
  
          protected static Item Recipe(int type)
          {
              switch (type)
              {
                  case 0: return new RecipeScroll(170);
                  case 1: return new RecipeScroll(457);
                  case 2: return new RecipeScroll(458);
                  case 3: return new RecipeScroll(800);
                  case 4: return new RecipeScroll(599);
              }
  
              return null;
          }
  
          protected static Item SmeltersTalisman(int type)
          {
              return new SmeltersTalisman((CraftResource)type);
          }
  
          protected static Item WoodsmansTalisman(int type)
          {
              return new WoodsmansTalisman((CraftResource)type);
          }
  
          public RewardCalculator()
          {
          }
      }
  
      #region Smith Rewards
      public sealed class SmithRewardCalculator : RewardCalculator
      {
          public SmithRewardCalculator()
          {
              if (BulkOrderSystem.NewSystemEnabled)
              {
                  RewardCollection = new List<CollectionItem>();
  
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157219, 0, 10, SmithHammer));
                  RewardCollection.Add(new BODCollectionItem(0xF39, 1157084, 0x973, 10, SturdyShovel));
                  RewardCollection.Add(new BODCollectionItem(0xE86, 1157085, 0x973, 25, SturdyPickaxe));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157181, 0, 25, RewardTitle, 0));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157182, 0, 100, RewardTitle, 1));
                  RewardCollection.Add(new BODCollectionItem(0x13C6, 1157086, 0, 100, MiningGloves, 1));
                  RewardCollection.Add(new BODCollectionItem(0x13D5, 1157087, 0, 200, MiningGloves, 3));
                  RewardCollection.Add(new BODCollectionItem(0xFB4, 1157090, 0, 200, ProspectorsTool));
                  RewardCollection.Add(new BODCollectionItem(0xE86, 1157089, 0, 200, GargoylesPickaxe));
                  RewardCollection.Add(new BODCollectionItem(0x2F5B, 1152674, CraftResources.GetHue(CraftResource.Gold), 350, SmeltersTalisman, (int)CraftResource.Gold));
                  RewardCollection.Add(new BODCollectionItem(0x9E2A, 1157264, 0, 400, CraftsmanTalisman, 10));
                  RewardCollection.Add(new BODCollectionItem(0x13EB, 1157088, 0, 450, MiningGloves, 5));
                  RewardCollection.Add(new BODCollectionItem(4102, 1157091, 0, 450, PowderOfTemperament));
                  RewardCollection.Add(new BODCollectionItem(0x2F5B, 1152675, CraftResources.GetHue(CraftResource.Agapite), 475, SmeltersTalisman, (int)CraftResource.Agapite));
                  RewardCollection.Add(new BODCollectionItem(0x9E7E, 1157216, 0, 500, RockHammer));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157092, CraftResources.GetHue(CraftResource.DullCopper), 500, RunicHammer, 1));
                  RewardCollection.Add(new BODCollectionItem(0x2F5B, 1152676, CraftResources.GetHue(CraftResource.Verite), 525, SmeltersTalisman, (int)CraftResource.Verite));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157093, CraftResources.GetHue(CraftResource.ShadowIron), 550, RunicHammer, 2));
                  RewardCollection.Add(new BODCollectionItem(0x9E2A, 1157218, 0, 550, CraftsmanTalisman, 25));
                  RewardCollection.Add(new BODCollectionItem(0x2F5B, 1152677, CraftResources.GetHue(CraftResource.Valorite), 575, SmeltersTalisman, (int)CraftResource.Valorite));
                  RewardCollection.Add(new BODCollectionItem(0x14EC, 1152665, CraftResources.GetHue(CraftResource.Gold), 600, HarvestMap, (int)CraftResource.Gold));
                  RewardCollection.Add(new BODCollectionItem(0xFAF, 1157100, 0, 625, ColoredAnvil));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157105, 0x481, 625, PowerScroll, 5));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157094, CraftResources.GetHue(CraftResource.Copper), 650, RunicHammer, 3));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157106, 0x481, 675, PowerScroll, 10));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157095, CraftResources.GetHue(CraftResource.Bronze), 700, RunicHammer, 4));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157101, 0x482, 750, AncientHammer, 10));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157107, 0x481, 800, PowerScroll, 15));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157102, 0x482, 850, AncientHammer, 15));
                  RewardCollection.Add(new BODCollectionItem(0x14EC, 1152666, CraftResources.GetHue(CraftResource.Agapite), 850, HarvestMap, (int)CraftResource.Agapite));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157108, 0x481, 900, PowerScroll, 20));
                  RewardCollection.Add(new BODCollectionItem(0x9E2A, 1157265, 0, 900, CraftsmanTalisman, 50));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157096, CraftResources.GetHue(CraftResource.Gold), 950, RunicHammer, 5));
                  RewardCollection.Add(new BODCollectionItem(0x14EC, 1152667, CraftResources.GetHue(CraftResource.Verite), 950, HarvestMap, (int)CraftResource.Verite));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157103, 0x482, 1000, AncientHammer, 30));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157097, CraftResources.GetHue(CraftResource.Agapite), 1050, RunicHammer, 6));
                  RewardCollection.Add(new BODCollectionItem(0x14EC, 1152668, CraftResources.GetHue(CraftResource.Valorite), 1050, HarvestMap, (int)CraftResource.Valorite));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157104, 0x482, 1100, AncientHammer, 60));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157098, CraftResources.GetHue(CraftResource.Verite), 1150, RunicHammer, 7));
                  RewardCollection.Add(new BODCollectionItem(0x13E3, 1157099, CraftResources.GetHue(CraftResource.Valorite), 1200, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157293, CraftResources.GetHue(CraftResource.Blaze), 1250, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157294, CraftResources.GetHue(CraftResource.Ice), 1300, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157295, CraftResources.GetHue(CraftResource.Toxic), 1350, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157296, CraftResources.GetHue(CraftResource.Electrum), 1400, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157297, CraftResources.GetHue(CraftResource.Platinum), 1450, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157298, CraftResources.GetHue(CraftResource.Royalite), 1500, RunicHammer, 8));
  				RewardCollection.Add(new BODCollectionItem(0x13E3, 1157299, CraftResources.GetHue(CraftResource.Danite), 1550, RunicHammer, 8));
  																																   
              }
              else
              {
                  this.Groups = new RewardGroup[]
                  {
                      new RewardGroup(0, new RewardItem(1, SturdyShovel)),
                      new RewardGroup(25, new RewardItem(1, SturdyPickaxe)),
                      new RewardGroup(50, new RewardItem(45, SturdyShovel), new RewardItem(45, SturdyPickaxe), new RewardItem(10, MiningGloves, 1)),
                      new RewardGroup(200, new RewardItem(45, GargoylesPickaxe), new RewardItem(45, ProspectorsTool), new RewardItem(10, MiningGloves, 3)),
                      new RewardGroup(400, new RewardItem(2, GargoylesPickaxe), new RewardItem(2, ProspectorsTool), new RewardItem(1, PowderOfTemperament)),
                      new RewardGroup(450, new RewardItem(9, PowderOfTemperament), new RewardItem(1, MiningGloves, 5)),
                      new RewardGroup(500, new RewardItem(1, RunicHammer, 1)),
                      new RewardGroup(550, new RewardItem(3, RunicHammer, 1), new RewardItem(2, RunicHammer, 2)),
                      new RewardGroup(600, new RewardItem(1, RunicHammer, 2)),
                      new RewardGroup(625, new RewardItem(3, RunicHammer, 2), new RewardItem(6, PowerScroll, 5), new RewardItem(1, ColoredAnvil)),
                      new RewardGroup(650, new RewardItem(1, RunicHammer, 3)),
                      new RewardGroup(675, new RewardItem(1, ColoredAnvil), new RewardItem(6, PowerScroll, 10), new RewardItem(3, RunicHammer, 3)),
                      new RewardGroup(700, new RewardItem(1, RunicHammer, 4)),
                      new RewardGroup(750, new RewardItem(1, AncientHammer, 10)),
                      new RewardGroup(800, new RewardItem(1, PowerScroll, 15)),
                      new RewardGroup(850, new RewardItem(1, AncientHammer, 15)),
                      new RewardGroup(900, new RewardItem(1, PowerScroll, 20)),
                      new RewardGroup(950, new RewardItem(1, RunicHammer, 5)),
                      new RewardGroup(1000, new RewardItem(1, AncientHammer, 30)),
                      new RewardGroup(1050, new RewardItem(1, RunicHammer, 6)),
                      new RewardGroup(1100, new RewardItem(1, AncientHammer, 60)),
                      new RewardGroup(1150, new RewardItem(1, RunicHammer, 7)),
                      new RewardGroup(1200, new RewardItem(1, RunicHammer, 8))
                  };
              }
          }
  
          #region Constructors
          private static readonly ConstructCallback SmithHammer = new ConstructCallback(CreateSmithHammer);
          private static readonly ConstructCallback SturdyShovel = new ConstructCallback(CreateSturdyShovel);
          private static readonly ConstructCallback SturdyPickaxe = new ConstructCallback(CreateSturdyPickaxe);
          private static readonly ConstructCallback MiningGloves = new ConstructCallback(CreateMiningGloves);
          private static readonly ConstructCallback GargoylesPickaxe = new ConstructCallback(CreateGargoylesPickaxe);
          private static readonly ConstructCallback ProspectorsTool = new ConstructCallback(CreateProspectorsTool);
          private static readonly ConstructCallback PowderOfTemperament = new ConstructCallback(CreatePowderOfTemperament);
          private static readonly ConstructCallback RunicHammer = new ConstructCallback(CreateRunicHammer);
          private static readonly ConstructCallback PowerScroll = new ConstructCallback(CreatePowerScroll);
          private static readonly ConstructCallback ColoredAnvil = new ConstructCallback(CreateColoredAnvil);
          private static readonly ConstructCallback AncientHammer = new ConstructCallback(CreateAncientHammer);
  		
  		
  		
  
          private static Item CreateSmithHammer(int type)
          {
              var hammer = new SmithHammer();
              hammer.UsesRemaining = 250;
  
              return hammer;
          }
  
          private static Item CreateSturdyShovel(int type)
          {
              return new SturdyShovel();
          }
  
          private static Item CreateSturdyPickaxe(int type)
          {
              return new SturdyPickaxe();
          }
  
          private static Item CreateMiningGloves(int type)
          {
              if (type == 1)
                  return new LeatherGlovesOfMining(1);
              else if (type == 3)
                  return new StuddedGlovesOfMining(3);
              else if (type == 5)
                  return new RingmailGlovesOfMining(5);
  
              throw new InvalidOperationException();
          }
  
          private static Item CreateGargoylesPickaxe(int type)
          {
              return new GargoylesPickaxe();
          }
  
          private static Item CreateProspectorsTool(int type)
          {
              return new ProspectorsTool();
          }
  
          private static Item CreatePowderOfTemperament(int type)
          {
              return new PowderOfTemperament();
          }
  
          private static Item CreateRunicHammer(int type)
          {
              if (type >= 1 && type <= 8)
                  return new RunicHammer(CraftResource.Iron + type, Core.AOS ? (55 - (type * 5)) : 50);
  
              throw new InvalidOperationException();
          }
  
          private static Item CreatePowerScroll(int type)
          {
              if (type == 5 || type == 10 || type == 15 || type == 20)
                  return new PowerScroll(SkillName.Blacksmith, 100 + type);
  
              throw new InvalidOperationException();
          }
  
          private static Item CreateColoredAnvil(int type)
          {
              return new ColoredAnvil();
          }
  
          private static Item CreateAncientHammer(int type)
          {
              if (type == 10 || type == 15 || type == 30 || type == 60)
                  return new AncientSmithyHammer(type);
  
              throw new InvalidOperationException();
          }
  
          private static Item CraftsmanTalisman(int type)
          {
              return new MasterCraftsmanTalisman(type, 0x9E2A, TalismanSkill.Blacksmithy);
          }
          #endregion
  
          public static readonly SmithRewardCalculator Instance = new SmithRewardCalculator();
  
          private readonly RewardType[] m_Types = new RewardType[]
          {
              // Armors
              new RewardType(200, typeof(RingmailGloves), typeof(RingmailChest), typeof(RingmailArms), typeof(RingmailLegs)),
              new RewardType(300, typeof(ChainCoif), typeof(ChainLegs), typeof(ChainChest)),
              new RewardType(400, typeof(PlateArms), typeof(PlateLegs), typeof(PlateHelm), typeof(PlateGorget), typeof(PlateGloves), typeof(PlateChest)),
  
              // Weapons
              new RewardType(200, typeof(Bardiche), typeof(Halberd)),
              new RewardType(300, typeof(Dagger), typeof(ShortSpear), typeof(Spear), typeof(WarFork), typeof(Kryss)), //OSI put the dagger in there.  Odd, ain't it.
              new RewardType(350, typeof(Axe), typeof(BattleAxe), typeof(DoubleAxe), typeof(ExecutionersAxe), typeof(LargeBattleAxe), typeof(TwoHandedAxe)),
              new RewardType(350, typeof(Broadsword), typeof(Cutlass), typeof(Katana), typeof(Longsword), typeof(Scimitar), /*typeof( ThinLongsword ),*/ typeof(VikingSword)),
              new RewardType(350, typeof(WarAxe), typeof(HammerPick), typeof(Mace), typeof(Maul), typeof(WarHammer), typeof(WarMace))
          };
  
          public override int ComputePoints(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type)
          {
              int points = 0;
  
              if (quantity == 10)
                  points += 10;
              else if (quantity == 15)
                  points += 25;
              else if (quantity == 20)
                  points += 50;
  
              if (exceptional)
                  points += 200;
  
              if (itemCount > 1)
                  points += this.LookupTypePoints(this.m_Types, type);
  
              if (material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Danite)
                  points += 200 + (50 * (material - BulkMaterialType.DullCopper));
  
              return points;
          }
  
             private static readonly int[][][] m_GoldTable = new int[][][]
          {
               new int[][] // 1-part (regular)
              {
                  new int[]{ 100, 200, 300,  400,  500,  600,  700,  800,  900, 1000, 1100, 1200, 1300, 1400, 1500, 1600 },
                  new int[]{ 200, 400, 600,  800, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3200 },
                  new int[]{ 300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000, 3300, 3600, 3900, 4200, 4500, 4800 }
              },
              new int[][] // 1-part (exceptional)
              {
                  new int[]{ 250, 500,  750, 1000, 1250, 1500, 1750, 2000,  2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000 },
                  new int[]{ 350, 700, 1050, 1400, 1750, 2100, 2450, 2800,  3150, 3500, 3850, 4200, 4550, 4900, 5250, 5500 },
                  new int[]{ 450, 900, 1350, 1800, 2250, 2700, 3150, 3600,  4050, 4500, 4950, 5400, 5850, 6300, 6850, 7500 }
              },
              new int[][] // Ringmail (regular)
              {
                  new int[]{ 2000, 4000,  6000,  8000, 10000, 12000, 14000, 16000, 18000, 20000, 22000, 24000, 26000, 28000, 30000 ,32000  },
                  new int[]{ 3000, 6000,  9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000, 33000, 36000, 39000, 42000, 45000,48000  },
                  new int[]{ 4000, 8000, 12000, 16000, 20000, 24000, 28000, 32000, 36000, 40000, 44000, 48000, 52000, 56000, 60000,64000  }
              },
              new int[][] // Ringmail (exceptional)
              {
                  new int[]{ 4000,  8000, 12000, 16000, 20000, 24000, 28000, 32000, 36000, 40000, 44000, 48000,  52000,  56000, 60000 ,64000  },
                  new int[]{ 6000, 12000, 18000, 24000, 30000, 36000, 42000, 48000, 54000, 60000, 66000, 72000,  78000,  84000,90000 ,96000  },
                  new int[]{ 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 72000, 80000, 88000, 96000, 104000, 112000, 120000 ,128000  }
              },
              new int[][] // Chainmail (regular)
              {
                  new int[]{ 4000,  8000, 12000, 16000, 20000, 24000, 28000, 32000, 36000, 40000, 44000, 48000,  52000,  56000,60000 ,64000  },
                  new int[]{ 6000, 12000, 18000, 24000, 30000, 36000, 42000, 48000, 54000, 60000, 66000, 72000,  78000,  84000,90000 ,96000  },
                  new int[]{ 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 72000, 80000, 88000, 96000, 104000, 112000, 120000 ,128000  }
              },
              new int[][] // Chainmail (exceptional)
              {
                  new int[]{  7000, 14000, 21000, 28000, 35000, 42000,  49000,  56000,  63000,  70000,  77000,  84000,  91000,  98000, 105000 , 112000  },
                  new int[]{ 10000, 20000, 30000, 40000, 50000, 60000,  70000,  80000,  90000, 100000, 110000, 120000, 130000, 140000, 150000 ,160000  },
                  new int[]{ 15000, 30000, 45000, 60000, 75000, 90000, 105000, 120000, 135000, 150000, 165000, 180000, 195000, 210000,225000 ,240000  }
              },
              new int[][] // Platemail (regular)
              {
                  new int[]{  5000, 10000, 15000, 20000, 25000, 30000,  35000,  40000,  45000,  50000,  55000,  60000,  65000,  70000, 75000,80000  },
                  new int[]{  7500, 15000, 22500, 30000, 37500, 45000,  52500,  60000,  67500,  75000,  82500,  90000,  97500, 105000, 112500 ,119000  },
                  new int[]{ 10000, 20000, 30000, 40000, 50000, 60000,  70000,  80000,  90000, 100000, 110000, 120000, 130000, 140000, 150000 , 160000 }
              },
              new int[][] // Platemail (exceptional)
              {
                  new int[]{ 10000, 20000, 30000, 40000,  50000,  60000,  70000,  80000,  90000, 100000, 110000, 120000, 130000, 140000, 150000,160000  },
                  new int[]{ 15000, 30000, 45000, 60000,  75000,  90000, 105000, 120000, 135000, 150000, 165000, 180000, 195000, 210000, 225000,240000  },
                  new int[]{ 20000, 40000, 60000, 80000, 100000, 120000, 140000, 160000, 180000, 200000, 220000, 240000, 260000, 280000, 300000,320000  }
              },
              new int[][] // 2-part weapons (regular)
              {
                  new int[]{ 3000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 4500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
              },
              new int[][] // 2-part weapons (exceptional)
              {
                  new int[]{ 5000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 10000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
              },
              new int[][] // 5-part weapons (regular)
              {
                  new int[]{ 4000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                  new int[]{ 8000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
              },
              new int[][] // 5-part weapons (exceptional)
              {
                  new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 11250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 15000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0}
              },
              new int[][] // 6-part weapons (regular)
              {
                  new int[]{ 4000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 10000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0}
              },
              new int[][] // 6-part weapons (exceptional)
              {
                  new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 11250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0},
                  new int[]{ 15000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0}
              }
          };
  
  
          private int ComputeType(Type type, int itemCount)
          {
              // Item count of 1 means it's a small BOD.
              if (itemCount == 1)
                  return 0;
  
              int typeIdx;
  
              // Loop through the RewardTypes defined earlier and find the correct one.
              for (typeIdx = 0; typeIdx < 7; ++typeIdx)
              {
                  if (this.m_Types[typeIdx].Contains(type))
                      break;
              }
  
              // Types 5, 6 and 7 are Large Weapon BODs with the same rewards.
              if (typeIdx > 5)
                  typeIdx = 5;
  
              return (typeIdx + 1) * 2;
          }
  
          public override int ComputeGold(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type)
          {
              int gold = 0;
  
              if (itemCount == 1 && BulkOrderSystem.NewSystemEnabled && BulkOrderSystem.ComputeGold(type, quantity, out gold))
              {
                  return gold;
              }
  
              int[][][] goldTable = m_GoldTable;
  
              int typeIndex = this.ComputeType(type, itemCount);
              int quanIndex = (quantity == 20 ? 2 : quantity == 15 ? 1 : 0);
              int mtrlIndex = (material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Danite) ? 1 + (int)(material - BulkMaterialType.DullCopper) : 0;
  
              if (exceptional)
                  typeIndex++;
  
              gold = goldTable[typeIndex][quanIndex][mtrlIndex];
  
              int min = (gold * 9) / 10;
              int max = (gold * 10) / 9;
  
              return Utility.RandomMinMax(min, max);
          }
      }
      #endregion
  
      #region Tailor Rewards
      public sealed class TailorRewardCalculator : RewardCalculator
      {
          public TailorRewardCalculator()
          {
              if (BulkOrderSystem.NewSystemEnabled)
              {
                  RewardCollection = new List<CollectionItem>();
  
                  RewardCollection.Add(new BODCollectionItem(0xF9D, 1157219, 0, 10, SewingKit));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157183, 0, 10, RewardTitle, 2));
                  RewardCollection.Add(new BODCollectionItem(0x1765, 1157109, 0, 10, Cloth, 0));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157184, 0, 25, RewardTitle, 3));
                  RewardCollection.Add(new BODCollectionItem(0x1761, 1157109, 0, 50, Cloth, 1));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157185, 0, 100, RewardTitle, 4));
                  RewardCollection.Add(new BODCollectionItem(0x1765, 1157109, 0, 100, Cloth, 2));
                  RewardCollection.Add(new BODCollectionItem(0x1765, 1157109, 0, 150, Cloth, 3));
                  RewardCollection.Add(new BODCollectionItem(0x170D, 1157110, 0, 150, Sandals, 3));
                  RewardCollection.Add(new BODCollectionItem(0x1765, 1157109, 0, 200, Cloth, 4));
                  RewardCollection.Add(new BODCollectionItem(0x9E25, 1157264, 0, 200, CraftsmanTalisman, 10)); // todo: Get id
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157111, 0, 300, StretchedHide));
                  RewardCollection.Add(new BODCollectionItem(0x1765, 1157109, 0, 300, Cloth, 5)); // TODO: Get other 4 colors
                  RewardCollection.Add(new BODCollectionItem(0x9E25, 1157218, 0, 300, CraftsmanTalisman, 25)); // todo: Get id
                  RewardCollection.Add(new BODCollectionItem(0xF9D, 1157115, CraftResources.GetHue(CraftResource.SpinedLeather), 350, RunicKit, 1));
                  RewardCollection.Add(new BODCollectionItem(0x9E25, 1157265, 0, 350, CraftsmanTalisman, 50)); // todo: Get id
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157118, 0x481, 400, PowerScroll, 5));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157112, 0, 400, Tapestry));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157113, 0, 450, BearRug));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157119, 0x481, 500, PowerScroll, 10));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157114, 0, 550, ClothingBlessDeed));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157120, 0x481, 575, PowerScroll, 15));
                  RewardCollection.Add(new BODCollectionItem(0xF9D, 1157116, CraftResources.GetHue(CraftResource.HornedLeather), 600, RunicKit, 2));
                  RewardCollection.Add(new BODCollectionItem(0x14F0, 1157121, 0x481, 650, PowerScroll, 20));
                  RewardCollection.Add(new BODCollectionItem(0xF9D, 1157117, CraftResources.GetHue(CraftResource.BarbedLeather), 700, RunicKit, 3));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.PolarLeather), 800, RunicKit, 4));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.SyntheticLeather), 900, RunicKit, 5));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.BlazeLeather), 1000, RunicKit, 6));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.DaemonicLeather), 1100, RunicKit, 7));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.ShadowLeather), 1200, RunicKit, 8));
  				RewardCollection.Add(new BODCollectionItem(0xF9D, 6000001, CraftResources.GetHue(CraftResource.EtherealLeather), 1300, RunicKit, 9));
              }
              else
              {
                  this.Groups = new RewardGroup[]
                  {
                      new RewardGroup(0, new RewardItem(1, Cloth, 0)),
                      new RewardGroup(50, new RewardItem(1, Cloth, 1)),
                      new RewardGroup(100, new RewardItem(1, Cloth, 2)),
                      new RewardGroup(150, new RewardItem(9, Cloth, 3), new RewardItem(1, Sandals)),
                      new RewardGroup(200, new RewardItem(4, Cloth, 4), new RewardItem(1, Sandals)),
                      new RewardGroup(300, new RewardItem(1, StretchedHide)),
                      new RewardGroup(350, new RewardItem(1, RunicKit, 1)),
                      new RewardGroup(400, new RewardItem(2, PowerScroll, 5), new RewardItem(3, Tapestry)),
                      new RewardGroup(450, new RewardItem(1, BearRug)),
                      new RewardGroup(500, new RewardItem(1, PowerScroll, 10)),
                      new RewardGroup(550, new RewardItem(1, ClothingBlessDeed)),
                      new RewardGroup(575, new RewardItem(1, PowerScroll, 15)),
                      new RewardGroup(600, new RewardItem(1, RunicKit, 2)),
                      new RewardGroup(650, new RewardItem(1, PowerScroll, 20)),
                      new RewardGroup(700, new RewardItem(1, RunicKit, 3))
                  };
              }
          }
  
          #region Constructors
          private static readonly ConstructCallback SewingKit = new ConstructCallback(CreateSewingKit);
          private static readonly ConstructCallback Cloth = new ConstructCallback(CreateCloth);
          private static readonly ConstructCallback Sandals = new ConstructCallback(CreateSandals);
          private static readonly ConstructCallback StretchedHide = new ConstructCallback(CreateStretchedHide);
          private static readonly ConstructCallback RunicKit = new ConstructCallback(CreateRunicKit);
          private static readonly ConstructCallback Tapestry = new ConstructCallback(CreateTapestry);
          private static readonly ConstructCallback PowerScroll = new ConstructCallback(CreatePowerScroll);
          private static readonly ConstructCallback BearRug = new ConstructCallback(CreateBearRug);
          private static readonly ConstructCallback ClothingBlessDeed = new ConstructCallback(CreateCBD);
          private static readonly ConstructCallback CraftsmanTalisman = new ConstructCallback(CreateCraftsmanTalisman);
  
          private static Item CreateSewingKit(int type)
          {
              var kit = new SewingKit();
              kit.UsesRemaining = 250;
  
              return kit;
          }
  
          private static readonly int[][] m_ClothHues = new int[][]
          {
              new int[] { 0x483, 0x48C, 0x488, 0x48A },
              new int[] { 0x495, 0x48B, 0x486, 0x485 },
              new int[] { 0x48D, 0x490, 0x48E, 0x491 },
              new int[] { 0x48F, 0x494, 0x484, 0x497 },
              new int[] { 0x489, 0x47F, 0x482, 0x47E },
              new int[] { 0xAAC, 0xAB4, 0xAAF, 0xAB5, 0xAAB },
          };
  
          private static Item CreateCloth(int type)
          {
              if (type >= 0 && type < m_ClothHues.Length)
              {
                  UncutCloth cloth = new UncutCloth(100);
                  cloth.Hue = m_ClothHues[type][Utility.Random(m_ClothHues[type].Length)];
                  return cloth;
              }
  
              throw new InvalidOperationException();
          }
  
          private static readonly int[] m_SandalHues = new int[]
          {
              0x489, 0x47F, 0x482,
              0x47E, 0x48F, 0x494,
              0x484, 0x497
          };
  
          private static Item CreateSandals(int type)
          {
              return new Sandals(m_SandalHues[Utility.Random(m_SandalHues.Length)]);
          }
  
          private static Item CreateStretchedHide(int type)
          {
              switch ( Utility.Random(4) )
              {
                  default:
                  case 0:
                      return new SmallStretchedHideEastDeed();
                  case 1:
                      return new SmallStretchedHideSouthDeed();
                  case 2:
                      return new MediumStretchedHideEastDeed();
                  case 3:
                      return new MediumStretchedHideSouthDeed();
              }
          }
  
          private static Item CreateTapestry(int type)
          {
              switch ( Utility.Random(4) )
              {
                  default:
                  case 0:
                      return new LightFlowerTapestryEastDeed();
                  case 1:
                      return new LightFlowerTapestrySouthDeed();
                  case 2:
                      return new DarkFlowerTapestryEastDeed();
                  case 3:
                      return new DarkFlowerTapestrySouthDeed();
              }
          }
  
          private static Item CreateBearRug(int type)
          {
              switch ( Utility.Random(4) )
              {
                  default:
                  case 0:
                      return new BrownBearRugEastDeed();
                  case 1:
                      return new BrownBearRugSouthDeed();
                  case 2:
                      return new PolarBearRugEastDeed();
                  case 3:
                      return new PolarBearRugSouthDeed();
              }
          }
  
          private static Item CreateRunicKit(int type)
          {
  			int charges = 60;
  			switch (type) {
  				case 1: charges = 60; break;
  				case 2: charges = 50;break;
  				case 3: charges = 40;break;
  				case 4: charges = 30;break;
  				case 5: charges = 20;break;
  				case 6: charges = 15;break;
  				case 7: charges = 10;break;
  				case 8: charges = 8;break;
  				case 9: charges = 5;break;
  				default: charges = 60;	break;
  			}
              if (type >= 1 && type <= 9)
                  return new RunicSewingKit(CraftResource.RegularLeather + type, charges);
  
              throw new InvalidOperationException();
          }
  
          private static Item CreatePowerScroll(int type)
          {
              if (type == 5 || type == 10 || type == 15 || type == 20)
                  return new PowerScroll(SkillName.Tailoring, 100 + type);
  
              throw new InvalidOperationException();
          }
  
          private static Item CreateCBD(int type)
          {
              return new ClothingBlessDeed();
          }
  
          private static Item CreateCraftsmanTalisman(int type)
          {
              return new MasterCraftsmanTalisman(type, 0x9E25, TalismanSkill.Tailoring);
          }
  
          #endregion
  
          public static readonly TailorRewardCalculator Instance = new TailorRewardCalculator();
  
          public override int ComputePoints(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type)
          {
              int points = 0;
  
              if (quantity == 10)
                  points += 10;
              else if (quantity == 15)
                  points += 25;
              else if (quantity == 20)
                  points += 50;
  
              if (exceptional)
                  points += 100;
  
              if (itemCount == 4)
                  points += 300;
              else if (itemCount == 5)
                  points += 400;
              else if (itemCount == 6)
                  points += 500;
  
              if (material == BulkMaterialType.Spined)
                  points += 50;
              else if (material == BulkMaterialType.Horned)
                  points += 100;
              else if (material == BulkMaterialType.Barbed)
                  points += 150;
  
              return points;
          }
  
          private static readonly int[][][] m_AosGoldTable = new int[][][]
          {
              new int[][] // 1-part (regular)
              {
                  new int[] { 150, 150, 300, 300 },
                  new int[] { 225, 225, 450, 450 },
                  new int[] { 300, 400, 600, 750 }
              },
              new int[][] // 1-part (exceptional)
              {
                  new int[] { 300, 300, 600, 600 },
                  new int[] { 450, 450, 900, 900 },
                  new int[] { 600, 750, 1200, 1800 }
              },
              new int[][] // 4-part (regular)
              {
                  new int[] { 4000, 4000, 5000, 5000 },
                  new int[] { 6000, 6000, 7500, 7500 },
                  new int[] { 8000, 10000, 10000, 15000 }
              },
              new int[][] // 4-part (exceptional)
              {
                  new int[] { 5000, 5000, 7500, 7500 },
                  new int[] { 7500, 7500, 11250, 11250 },
                  new int[] { 10000, 15000, 15000, 20000 }
              },
              new int[][] // 5-part (regular)
              {
                  new int[] { 5000, 5000, 7500, 7500 },
                  new int[] { 7500, 7500, 11250, 11250 },
                  new int[] { 10000, 15000, 15000, 20000 }
              },
              new int[][] // 5-part (exceptional)
              {
                  new int[] { 7500, 7500, 10000, 10000 },
                  new int[] { 11250, 11250, 15000, 15000 },
                  new int[] { 15000, 20000, 20000, 30000 }
              },
              new int[][] // 6-part (regular)
              {
                  new int[] { 7500, 7500, 10000, 10000 },
                  new int[] { 11250, 11250, 15000, 15000 },
                  new int[] { 15000, 20000, 20000, 30000 }
              },
              new int[][] // 6-part (exceptional)
              {
                  new int[] { 10000, 10000, 15000, 15000 },
                  new int[] { 15000, 15000, 22500, 22500 },
                  new int[] { 20000, 30000, 30000, 50000 }
              }
          };
  
          private static readonly int[][][] m_OldGoldTable = new int[][][]
          {
              new int[][] // 1-part (regular)
              {
                  new int[] { 150, 150, 300, 300 },
                  new int[] { 225, 225, 450, 450 },
                  new int[] { 300, 400, 600, 750 }
              },
              new int[][] // 1-part (exceptional)
              {
                  new int[] { 300, 300, 600, 600 },
                  new int[] { 450, 450, 900, 900 },
                  new int[] { 600, 750, 1200, 1800 }
              },
              new int[][] // 4-part (regular)
              {
                  new int[] { 3000, 3000, 4000, 4000 },
                  new int[] { 4500, 4500, 6000, 6000 },
                  new int[] { 6000, 8000, 8000, 10000 }
              },
              new int[][] // 4-part (exceptional)
              {
                  new int[] { 4000, 4000, 5000, 5000 },
                  new int[] { 6000, 6000, 7500, 7500 },
                  new int[] { 8000, 10000, 10000, 15000 }
              },
              new int[][] // 5-part (regular)
              {
                  new int[] { 4000, 4000, 5000, 5000 },
                  new int[] { 6000, 6000, 7500, 7500 },
                  new int[] { 8000, 10000, 10000, 15000 }
              },
              new int[][] // 5-part (exceptional)
              {
                  new int[] { 5000, 5000, 7500, 7500 },
                  new int[] { 7500, 7500, 11250, 11250 },
                  new int[] { 10000, 15000, 15000, 20000 }
              },
              new int[][] // 6-part (regular)
              {
                  new int[] { 5000, 5000, 7500, 7500 },
                  new int[] { 7500, 7500, 11250, 11250 },
                  new int[] { 10000, 15000, 15000, 20000 }
              },
              new int[][] // 6-part (exceptional)
              {
                  new int[] { 7500, 7500, 10000, 10000 },
                  new int[] { 11250, 11250, 15000, 15000 },
                  new int[] { 15000, 20000, 20000, 30000 }
              }
          };
  
          public override int ComputeGold(int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type)
          {
              int gold = 0;
  
              if (itemCount == 1 && BulkOrderSystem.NewSystemEnabled && BulkOrderSystem.ComputeGold(type, quantity, out gold))
              {
                  return gold;
              }
  
              int[][][] goldTable = (Core.AOS ? m_AosGoldTable : m_OldGoldTable);
  
              int typeIndex = ((itemCount == 6 ? 3 : itemCount == 5 ? 2 : itemCount == 4 ? 1 : 0) * 2) + (exceptional ? 1 : 0);
              int quanIndex = (quantity == 20 ? 2 : quantity == 15 ? 1 : 0);
              int mtrlIndex = (material == BulkMaterialType.Barbed ? 3 : material == BulkMaterialType.Horned ? 2 : material == BulkMaterialType.Spined ? 1 : 0);
  
              gold = goldTable[typeIndex][quanIndex][mtrlIndex];
  
              int min = (gold * 9) / 10;
  
gt:             int max = (gold * 10) / 9;