945 lines
31 KiB
C#
945 lines
31 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
using Server;
|
|
using Server.Mobiles;
|
|
using Server.Items;
|
|
|
|
namespace Server.Engines.BulkOrders
|
|
{
|
|
public enum PointsMode
|
|
{
|
|
Enabled,
|
|
Disabled,
|
|
Automatic
|
|
}
|
|
|
|
public interface IBOD
|
|
{
|
|
int AmountMax { get; set; }
|
|
bool RequireExceptional { get; set; }
|
|
bool Complete { get; }
|
|
BulkMaterialType Material { get; set; }
|
|
BODType BODType { get; }
|
|
}
|
|
|
|
public class BulkOrderSystem
|
|
{
|
|
// Logic (EA says 3 cached): 2 cached, 1 in the pipe if the last bod > 6 hours = 3
|
|
public static readonly int MaxCachedDeeds = 2;
|
|
public static readonly int Delay = 6;
|
|
|
|
public static bool NewSystemEnabled = Core.TOL;
|
|
public static BulkOrderSystem Instance { get; set; }
|
|
|
|
public Dictionary<PlayerMobile, BODContext> BODPlayerData { get; set; }
|
|
|
|
public BulkOrderSystem()
|
|
{
|
|
BODPlayerData = new Dictionary<PlayerMobile, BODContext>();
|
|
}
|
|
|
|
static BulkOrderSystem()
|
|
{
|
|
Instance = new BulkOrderSystem();
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(0);
|
|
|
|
writer.Write(BODPlayerData.Count);
|
|
foreach (KeyValuePair<PlayerMobile, BODContext> kvp in BODPlayerData)
|
|
{
|
|
writer.Write(kvp.Key);
|
|
kvp.Value.Serialize(writer);
|
|
}
|
|
}
|
|
|
|
public void Deserialize(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt();
|
|
|
|
int count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
PlayerMobile player = reader.ReadMobile() as PlayerMobile;
|
|
BODContext context = new BODContext(reader);
|
|
|
|
if (player != null)
|
|
BODPlayerData[player] = context;
|
|
}
|
|
}
|
|
|
|
public static BODContext GetContext(Mobile m, bool create = true)
|
|
{
|
|
BODContext context = null;
|
|
|
|
if (m is PlayerMobile)
|
|
{
|
|
PlayerMobile pm = m as PlayerMobile;
|
|
|
|
if (Instance.BODPlayerData.ContainsKey(pm))
|
|
{
|
|
context = Instance.BODPlayerData[pm];
|
|
}
|
|
else if (create)
|
|
{
|
|
Instance.BODPlayerData[pm] = context = new BODContext();
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
public static double GetPoints(Mobile m, BODType type)
|
|
{
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
if (context.Entries.ContainsKey(type))
|
|
{
|
|
return context.Entries[type].BankedPoints;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static void SetPoints(Mobile m, BODType type, double points)
|
|
{
|
|
if (points < 0)
|
|
points = 0.0;
|
|
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
if (context.Entries.ContainsKey(type))
|
|
{
|
|
context.Entries[type].BankedPoints += points;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void DeductPoints(Mobile m, BODType type, double points)
|
|
{
|
|
if (points < 0)
|
|
points = 0.0;
|
|
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
if (context.Entries.ContainsKey(type))
|
|
{
|
|
context.Entries[type].BankedPoints -= points;
|
|
|
|
if (context.Entries[type].BankedPoints < 0.0)
|
|
context.Entries[type].BankedPoints = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static double GetBODSkill(Mobile m, SkillName skill)
|
|
{
|
|
return Math.Max(m.Skills[skill].Base, m.GetRacialSkillBonus(skill));
|
|
}
|
|
|
|
public static List<CollectionItem> GetRewardCollection(BODType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
default:
|
|
case BODType.Smith: return SmithRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Tailor: return TailorRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Alchemy: return AlchemyRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Inscription: return InscriptionRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Tinkering: return TinkeringRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Fletching: return FletchingRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Carpentry: return CarpentryRewardCalculator.Instance.RewardCollection;
|
|
case BODType.Cooking: return CookingRewardCalculator.Instance.RewardCollection;
|
|
}
|
|
}
|
|
|
|
public static bool CanGetBulkOrder(Mobile m, BODType type)
|
|
{
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null && context.Entries.ContainsKey(type))
|
|
{
|
|
var entry = context.Entries[type];
|
|
|
|
if (entry != null)
|
|
{
|
|
entry.CheckCache();
|
|
|
|
if (entry.LastBulkOrder + TimeSpan.FromHours(Delay) < DateTime.UtcNow)
|
|
{
|
|
entry.LastBulkOrder = DateTime.UtcNow;
|
|
|
|
return true;
|
|
}
|
|
else if (entry.CachedDeeds > 0)
|
|
{
|
|
entry.CachedDeeds--;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static TimeSpan GetNextBulkOrder(BODType type, PlayerMobile pm)
|
|
{
|
|
BODContext context = GetContext(pm);
|
|
|
|
if (context != null)
|
|
{
|
|
if (NewSystemEnabled)
|
|
{
|
|
DateTime last = context.Entries[type].LastBulkOrder;
|
|
|
|
return (last + TimeSpan.FromHours(Delay)) - DateTime.UtcNow;
|
|
}
|
|
else if (context.Entries.ContainsKey(type))
|
|
{
|
|
DateTime dt = context.Entries[type].NextBulkOrder;
|
|
|
|
if (dt < DateTime.UtcNow)
|
|
return TimeSpan.Zero;
|
|
|
|
return DateTime.UtcNow - dt;
|
|
}
|
|
}
|
|
|
|
return TimeSpan.MaxValue;
|
|
}
|
|
|
|
public static void SetNextBulkOrder(BODType type, PlayerMobile pm, TimeSpan ts)
|
|
{
|
|
BODContext context = GetContext(pm);
|
|
|
|
if (context != null)
|
|
{
|
|
if (NewSystemEnabled)
|
|
{
|
|
if (context.Entries.ContainsKey(type))
|
|
{
|
|
if (context.Entries[type].LastBulkOrder < DateTime.UtcNow - TimeSpan.FromHours(Delay * MaxCachedDeeds))
|
|
context.Entries[type].LastBulkOrder = DateTime.UtcNow - TimeSpan.FromHours(Delay * MaxCachedDeeds);
|
|
else
|
|
context.Entries[type].LastBulkOrder = (context.Entries[type].LastBulkOrder + ts) - TimeSpan.FromHours(Delay);
|
|
}
|
|
}
|
|
else if (context.Entries.ContainsKey(type))
|
|
{
|
|
context.Entries[type].NextBulkOrder = DateTime.UtcNow + ts;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Item CreateBulkOrder(Mobile m, BODType type, bool fromContextMenu)
|
|
{
|
|
PlayerMobile pm = m as PlayerMobile;
|
|
|
|
if (pm == null)
|
|
return null;
|
|
|
|
if (pm.AccessLevel > AccessLevel.Player || fromContextMenu || 0.2 > Utility.RandomDouble())
|
|
{
|
|
SkillName sk = GetSkillForBOD(type);
|
|
double theirSkill = pm.Skills[sk].Base;
|
|
bool doLarge = theirSkill >= 70.1 && ((theirSkill - 40.0) / 300.0) > Utility.RandomDouble();
|
|
|
|
switch (type)
|
|
{
|
|
case BODType.Smith:
|
|
if (doLarge) return new LargeSmithBOD();
|
|
else return SmallSmithBOD.CreateRandomFor(pm);
|
|
case BODType.Tailor: if (doLarge) return new LargeTailorBOD();
|
|
else return SmallTailorBOD.CreateRandomFor(pm);
|
|
case BODType.Alchemy:
|
|
if (doLarge) return new LargeAlchemyBOD();
|
|
else return SmallAlchemyBOD.CreateRandomFor(pm);
|
|
case BODType.Inscription:
|
|
if (doLarge) return new LargeInscriptionBOD();
|
|
else return SmallInscriptionBOD.CreateRandomFor(pm);
|
|
case BODType.Tinkering:
|
|
if (doLarge) return new LargeTinkerBOD();
|
|
else return SmallTinkerBOD.CreateRandomFor(pm);
|
|
case BODType.Cooking:
|
|
if (doLarge) return new LargeCookingBOD();
|
|
else return SmallCookingBOD.CreateRandomFor(pm);
|
|
case BODType.Fletching:
|
|
if (doLarge) return new LargeFletchingBOD();
|
|
else return SmallFletchingBOD.CreateRandomFor(pm);
|
|
case BODType.Carpentry:
|
|
if (doLarge) return new LargeCarpentryBOD();
|
|
else return SmallCarpentryBOD.CreateRandomFor(pm);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static SkillName GetSkillForBOD(BODType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
default:
|
|
case BODType.Smith: return SkillName.Blacksmith;
|
|
case BODType.Tailor: return SkillName.Tailoring;
|
|
case BODType.Alchemy: return SkillName.Alchemy;
|
|
case BODType.Inscription: return SkillName.Inscribe;
|
|
case BODType.Tinkering: return SkillName.Tinkering;
|
|
case BODType.Cooking: return SkillName.Cooking;
|
|
case BODType.Fletching: return SkillName.Fletching;
|
|
case BODType.Carpentry: return SkillName.Carpentry;
|
|
}
|
|
}
|
|
|
|
public static int GetBodHue(BODType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
default:
|
|
case BODType.Smith: return 0x44E;
|
|
case BODType.Tailor: return 0x483;
|
|
case BODType.Alchemy: return 2505;
|
|
case BODType.Inscription: return 2598;
|
|
case BODType.Tinkering: return 1109;
|
|
case BODType.Cooking: return 1169;
|
|
case BODType.Fletching: return 1425;
|
|
case BODType.Carpentry: return 1512;
|
|
}
|
|
}
|
|
|
|
public static BOBFilter GetBOBFilter(PlayerMobile pm)
|
|
{
|
|
return GetContext(pm).BOBFilter;
|
|
}
|
|
|
|
public static void SetBOBFilter(PlayerMobile pm, BOBFilter filter)
|
|
{
|
|
BODContext context = GetContext(pm);
|
|
context.BOBFilter = filter;
|
|
}
|
|
|
|
public static int ComputePoints(SmallBOD bod)
|
|
{
|
|
switch (bod.BODType)
|
|
{
|
|
default:
|
|
case BODType.Smith: return SmithRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Tailor: return TailorRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Alchemy: return AlchemyRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Inscription: return InscriptionRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Tinkering: return TinkeringRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Cooking: return CookingRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Fletching: return FletchingRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Carpentry: return CarpentryRewardCalculator.Instance.ComputePoints(bod);
|
|
}
|
|
}
|
|
|
|
public static int ComputePoints(LargeBOD bod)
|
|
{
|
|
switch (bod.BODType)
|
|
{
|
|
default:
|
|
case BODType.Smith: return SmithRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Tailor: return TailorRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Alchemy: return AlchemyRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Inscription: return InscriptionRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Tinkering: return TinkeringRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Cooking: return CookingRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Fletching: return FletchingRewardCalculator.Instance.ComputePoints(bod);
|
|
case BODType.Carpentry: return CarpentryRewardCalculator.Instance.ComputePoints(bod);
|
|
}
|
|
}
|
|
|
|
public static bool ComputeGold(Type type, int quantity, out int gold)
|
|
{
|
|
if (GenericBuyInfo.BuyPrices.ContainsKey(type))
|
|
{
|
|
gold = (quantity * GenericBuyInfo.BuyPrices[type]) / 2;
|
|
return true;
|
|
}
|
|
|
|
gold = 0;
|
|
return false;
|
|
}
|
|
|
|
public static void ComputePoints(SmallBOD bod, out int points, out double banked)
|
|
{
|
|
points = 0;
|
|
banked = 0.0;
|
|
|
|
switch (bod.BODType)
|
|
{
|
|
default:
|
|
case BODType.Smith: points = SmithRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Tailor: points = TailorRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Alchemy: points = AlchemyRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Inscription: points = InscriptionRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Tinkering: points = TinkeringRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Cooking: points = CookingRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Fletching: points = FletchingRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Carpentry: points = CarpentryRewardCalculator.Instance.ComputePoints(bod); break;
|
|
}
|
|
|
|
banked = (double)points * 0.02;
|
|
}
|
|
|
|
public static void ComputePoints(LargeBOD bod, out int points, out double banked)
|
|
{
|
|
points = 0;
|
|
banked = 0.0;
|
|
|
|
switch (bod.BODType)
|
|
{
|
|
default:
|
|
case BODType.Smith: points = SmithRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Tailor: points = TailorRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Alchemy: points = AlchemyRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Inscription: points = InscriptionRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Tinkering: points = TinkeringRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Cooking: points = CookingRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Fletching: points = FletchingRewardCalculator.Instance.ComputePoints(bod); break;
|
|
case BODType.Carpentry: points = CarpentryRewardCalculator.Instance.ComputePoints(bod); break;
|
|
}
|
|
|
|
banked = (double)points * .2;
|
|
}
|
|
|
|
public static void AddToPending(Mobile m, BODType type, int points)
|
|
{
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
context.AddPending(type, points);
|
|
}
|
|
}
|
|
|
|
public static void RemovePending(Mobile m, BODType type)
|
|
{
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
context.RemovePending(type);
|
|
}
|
|
}
|
|
|
|
public static bool CanClaimRewards(Mobile m)
|
|
{
|
|
BODContext context = GetContext(m);
|
|
|
|
if (context != null)
|
|
{
|
|
return context.CanClaimRewards();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool CanExchangeBOD(Mobile from, BaseVendor vendor, IBOD bod, int cost)
|
|
{
|
|
if (bod.BODType != vendor.BODType)
|
|
{
|
|
vendor.SayTo(from, 1152298, 0x3B2); // I don't deal in those goods.
|
|
return false;
|
|
}
|
|
|
|
if ((bod is SmallBOD && ((SmallBOD)bod).AmountCur > 0) ||
|
|
(bod is LargeBOD && ((LargeBOD)bod).Entries != null &&
|
|
((LargeBOD)bod).Entries.FirstOrDefault(e => e.Amount > 0) != null))
|
|
{
|
|
vendor.SayTo(from, 1152299, 0x3B2); // I am sorry to say I cannot work with a deed that is even partially filled.
|
|
return false;
|
|
}
|
|
|
|
if (bod.AmountMax == 20 && (!CanBeExceptional(bod) || bod.RequireExceptional) &&
|
|
(!CanUseMaterial(bod) ||
|
|
(bod.Material == BulkMaterialType.Valorite ||
|
|
bod.Material == BulkMaterialType.Frostwood ||
|
|
bod.Material == BulkMaterialType.Barbed)))
|
|
{
|
|
vendor.SayTo(from, 1152291, 0x3B2); // I won't be able to replace that bulk order with a better one.
|
|
return false;
|
|
}
|
|
|
|
if (cost > -1 && !Banker.Withdraw(from, cost, true))
|
|
{
|
|
vendor.SayTo(from, 1152302, 0x3B2); // I am afraid your bank box does not contain the funds needed to complete this transaction.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static Type GetTypeFromBOD(IBOD bod)
|
|
{
|
|
Type t = null;
|
|
|
|
if (bod is SmallBOD)
|
|
{
|
|
t = ((SmallBOD)bod).Type;
|
|
}
|
|
else if (bod is LargeBOD && ((LargeBOD)bod).Entries != null && ((LargeBOD)bod).Entries.Length > 0)
|
|
{
|
|
t = ((LargeBOD)bod).Entries[0].Details.Type;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
public static bool CanBeExceptional(IBOD bod)
|
|
{
|
|
switch (bod.BODType)
|
|
{
|
|
default: return true;
|
|
case BODType.Alchemy:
|
|
case BODType.Inscription: return false;
|
|
case BODType.Tinkering:
|
|
case BODType.Cooking:
|
|
case BODType.Fletching:
|
|
return !IsInExceptionalExcludeList(bod);
|
|
}
|
|
}
|
|
|
|
private static bool IsInExceptionalExcludeList(IBOD bod)
|
|
{
|
|
Type t = GetTypeFromBOD(bod);
|
|
|
|
if (t == null)
|
|
return false;
|
|
|
|
return _ExceptionalExcluded.FirstOrDefault(type => type == t) != null;
|
|
}
|
|
|
|
public static bool CanUseMaterial(IBOD bod)
|
|
{
|
|
switch (bod.BODType)
|
|
{
|
|
default: return true;
|
|
case BODType.Alchemy:
|
|
case BODType.Inscription:
|
|
case BODType.Cooking: return false;
|
|
case BODType.Tinkering:
|
|
case BODType.Fletching: return !IsInExceptionalExcludeList(bod);
|
|
case BODType.Tailor: return BGTClassifier.Classify(BODType.Tailor, GetTypeFromBOD(bod)) == BulkGenericType.Leather;
|
|
}
|
|
}
|
|
|
|
private static Type[] _ExceptionalExcluded =
|
|
{
|
|
typeof(Arrow), typeof(Bolt), typeof(Kindling), typeof(Shaft),
|
|
|
|
typeof(EnchantedApple), typeof(TribalPaint), typeof(GrapesOfWrath),
|
|
typeof(EggBomb), typeof(CookedBird), typeof(FishSteak), typeof(FriedEggs),
|
|
typeof(LambLeg), typeof(Ribs),
|
|
|
|
typeof(Gears), typeof(Axle), typeof(Springs), typeof(AxleGears), typeof(ClockParts),
|
|
typeof(Clock), typeof(PotionKeg), typeof(ClockFrame), typeof(MetalContainerEngraver)
|
|
};
|
|
|
|
public static void MutateBOD(IBOD bod)
|
|
{
|
|
List<int> picker = new List<int>();
|
|
|
|
if (!bod.RequireExceptional && CanBeExceptional(bod))
|
|
{
|
|
picker.Add(0);
|
|
}
|
|
|
|
if (bod.AmountMax < 20)
|
|
{
|
|
picker.Add(1);
|
|
}
|
|
|
|
if (CanUseMaterial(bod) && bod.Material != BulkMaterialType.Frostwood && bod.Material != BulkMaterialType.Barbed && bod.Material != BulkMaterialType.Valorite)
|
|
{
|
|
picker.Add(2);
|
|
}
|
|
|
|
if (picker.Count == 0)
|
|
return;
|
|
|
|
switch (picker[Utility.Random(picker.Count)])
|
|
{
|
|
case 0: bod.RequireExceptional = true; break;
|
|
case 1: bod.AmountMax += 5; break;
|
|
case 2:
|
|
if (bod.Material == BulkMaterialType.None)
|
|
{
|
|
BulkGenericType type = BGTClassifier.Classify(bod.BODType, null);
|
|
|
|
switch (type)
|
|
{
|
|
case BulkGenericType.Iron: bod.Material = BulkMaterialType.DullCopper; break;
|
|
case BulkGenericType.Cloth: break;
|
|
case BulkGenericType.Leather: bod.Material = BulkMaterialType.Spined; break;
|
|
case BulkGenericType.Wood: bod.Material = BulkMaterialType.OakWood; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bod.Material++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
picker.Clear();
|
|
}
|
|
|
|
public static int GetBribe(IBOD bod)
|
|
{
|
|
int worth = 0;
|
|
|
|
if (bod.RequireExceptional)
|
|
{
|
|
worth += 400;
|
|
}
|
|
|
|
switch (bod.Material)
|
|
{
|
|
case BulkMaterialType.DullCopper: worth += 25; break;
|
|
case BulkMaterialType.ShadowIron: worth += 50; break;
|
|
case BulkMaterialType.Copper: worth += 100; break;
|
|
case BulkMaterialType.Bronze: worth += 200; break;
|
|
case BulkMaterialType.Gold: worth += 300; break;
|
|
case BulkMaterialType.Agapite: worth += 400; break;
|
|
case BulkMaterialType.Verite: worth += 500; break;
|
|
case BulkMaterialType.Valorite: worth += 600; break;
|
|
case BulkMaterialType.Spined: worth += 100; break;
|
|
case BulkMaterialType.Horned: worth += 250; break;
|
|
case BulkMaterialType.Barbed: worth += 500; break;
|
|
case BulkMaterialType.OakWood: worth += 100; break;
|
|
case BulkMaterialType.AshWood: worth += 200; break;
|
|
case BulkMaterialType.YewWood: worth += 300; break;
|
|
case BulkMaterialType.Heartwood: worth += 400; break;
|
|
case BulkMaterialType.Bloodwood: worth += 500; break;
|
|
case BulkMaterialType.Frostwood: worth += 600; break;
|
|
}
|
|
|
|
switch (bod.AmountMax)
|
|
{
|
|
default:
|
|
case 10:
|
|
case 15: worth += 100; break;
|
|
case 20: worth += 250; break;
|
|
}
|
|
|
|
if (bod is LargeBOD && ((LargeBOD)bod).Entries != null)
|
|
{
|
|
worth *= Math.Min(4, ((LargeBOD)bod).Entries.Length);
|
|
}
|
|
|
|
return worth;
|
|
}
|
|
|
|
public static void OnTick()
|
|
{
|
|
foreach (var kvp in Instance.BODPlayerData)
|
|
{
|
|
kvp.Value.CheckCache();
|
|
}
|
|
}
|
|
|
|
public static void OnLogin(LoginEventArgs e)
|
|
{
|
|
}
|
|
|
|
public static string FilePath = Path.Combine("Saves/CraftContext", "BODs.bin");
|
|
|
|
public static void Configure()
|
|
{
|
|
EventSink.WorldSave += OnSave;
|
|
EventSink.WorldLoad += OnLoad;
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
if (NewSystemEnabled)
|
|
{
|
|
Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), OnTick);
|
|
}
|
|
}
|
|
|
|
public static void OnSave(WorldSaveEventArgs e)
|
|
{
|
|
Persistence.Serialize(
|
|
FilePath,
|
|
writer =>
|
|
{
|
|
writer.Write(0);
|
|
|
|
Instance.Serialize(writer);
|
|
});
|
|
}
|
|
|
|
public static void OnLoad()
|
|
{
|
|
Persistence.Deserialize(
|
|
FilePath,
|
|
reader =>
|
|
{
|
|
int version = reader.ReadInt();
|
|
|
|
Instance.Deserialize(reader);
|
|
});
|
|
}
|
|
}
|
|
|
|
public class BODContext
|
|
{
|
|
public Dictionary<BODType, BODEntry> Entries { get; set; }
|
|
|
|
public BOBFilter BOBFilter { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public PointsMode PointsMode { get; set; }
|
|
|
|
public BODContext()
|
|
{
|
|
BOBFilter = new BOBFilter();
|
|
|
|
ConfigEntries();
|
|
}
|
|
|
|
public void AddPending(BODType type, int points)
|
|
{
|
|
if (Entries.ContainsKey(type))
|
|
{
|
|
Entries[type].PendingRewardPoints = points;
|
|
}
|
|
}
|
|
|
|
public void RemovePending(BODType type)
|
|
{
|
|
if (Entries.ContainsKey(type))
|
|
{
|
|
Entries[type].PendingRewardPoints = 0;
|
|
}
|
|
}
|
|
|
|
public int GetPendingRewardFor(BODType type)
|
|
{
|
|
if (Entries.ContainsKey(type))
|
|
{
|
|
return Entries[type].PendingRewardPoints;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public bool CanClaimRewards()
|
|
{
|
|
foreach (var kvp in Entries)
|
|
{
|
|
if (kvp.Value.PendingRewardPoints > 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public BODContext(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt();
|
|
ConfigEntries();
|
|
|
|
this.PointsMode = (PointsMode)reader.ReadInt();
|
|
BOBFilter = new BOBFilter(reader);
|
|
|
|
int count = reader.ReadInt();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
BODType type = (BODType)reader.ReadInt();
|
|
BODEntry entry = new BODEntry(reader);
|
|
|
|
if (Entries.ContainsKey(type))
|
|
Entries[type] = entry;
|
|
}
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(0);
|
|
|
|
writer.Write((int)this.PointsMode);
|
|
BOBFilter.Serialize(writer);
|
|
|
|
// Lets do this dynamically incase we add new bods in the future
|
|
writer.Write(Entries.Count);
|
|
foreach (KeyValuePair<BODType, BODEntry> kvp in Entries)
|
|
{
|
|
writer.Write((int)kvp.Key);
|
|
kvp.Value.Serialize(writer);
|
|
}
|
|
}
|
|
|
|
private void ConfigEntries()
|
|
{
|
|
Entries = new Dictionary<BODType, BODEntry>();
|
|
|
|
Entries[BODType.Smith] = new BODEntry();
|
|
Entries[BODType.Tailor] = new BODEntry();
|
|
Entries[BODType.Tinkering] = new BODEntry();
|
|
Entries[BODType.Carpentry] = new BODEntry();
|
|
Entries[BODType.Fletching] = new BODEntry();
|
|
Entries[BODType.Cooking] = new BODEntry();
|
|
Entries[BODType.Alchemy] = new BODEntry();
|
|
Entries[BODType.Inscription] = new BODEntry();
|
|
}
|
|
|
|
public void CheckCache()
|
|
{
|
|
foreach (var kvp in Entries)
|
|
{
|
|
kvp.Value.CheckCache();
|
|
}
|
|
}
|
|
}
|
|
|
|
[PropertyObject]
|
|
public class BODEntry
|
|
{
|
|
private int _CachedDeeds;
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int CachedDeeds
|
|
{
|
|
get { return _CachedDeeds; }
|
|
set
|
|
{
|
|
int old = _CachedDeeds;
|
|
|
|
_CachedDeeds = Math.Max(0, Math.Min(BulkOrderSystem.MaxCachedDeeds, value));
|
|
}
|
|
}
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public DateTime LastBulkOrder { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public double BankedPoints { get; set; }
|
|
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public int PendingRewardPoints { get; set; }
|
|
|
|
// Legacy System
|
|
private DateTime _NextBulkOrder;
|
|
|
|
public DateTime NextBulkOrder
|
|
{
|
|
get
|
|
{
|
|
return _NextBulkOrder;
|
|
}
|
|
set
|
|
{
|
|
_NextBulkOrder = value;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "...";
|
|
}
|
|
|
|
public BODEntry()
|
|
{
|
|
_CachedDeeds = BulkOrderSystem.MaxCachedDeeds;
|
|
}
|
|
|
|
public void CheckCache()
|
|
{
|
|
if (_CachedDeeds >= BulkOrderSystem.MaxCachedDeeds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (LastBulkOrder == DateTime.MinValue)
|
|
{
|
|
CachedDeeds = BulkOrderSystem.MaxCachedDeeds;
|
|
LastBulkOrder = DateTime.UtcNow - TimeSpan.FromHours(BulkOrderSystem.Delay);
|
|
}
|
|
else if (LastBulkOrder + TimeSpan.FromHours(BulkOrderSystem.Delay) < DateTime.UtcNow)
|
|
{
|
|
CachedDeeds++;
|
|
LastBulkOrder = LastBulkOrder + TimeSpan.FromHours(BulkOrderSystem.Delay);
|
|
}
|
|
}
|
|
|
|
public BODEntry(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt();
|
|
|
|
if (reader.ReadInt() == 0)
|
|
{
|
|
switch (version)
|
|
{
|
|
case 1:
|
|
case 0:
|
|
_CachedDeeds = reader.ReadInt();
|
|
LastBulkOrder = reader.ReadDateTime();
|
|
BankedPoints = reader.ReadDouble();
|
|
PendingRewardPoints = reader.ReadInt();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_NextBulkOrder = reader.ReadDateTime();
|
|
}
|
|
|
|
if (version == 0 && _CachedDeeds > BulkOrderSystem.MaxCachedDeeds)
|
|
{
|
|
_CachedDeeds = BulkOrderSystem.MaxCachedDeeds;
|
|
}
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(1);
|
|
|
|
if (BulkOrderSystem.NewSystemEnabled)
|
|
{
|
|
writer.Write(0);
|
|
writer.Write(_CachedDeeds);
|
|
writer.Write(LastBulkOrder);
|
|
writer.Write(BankedPoints);
|
|
writer.Write(PendingRewardPoints);
|
|
}
|
|
else
|
|
{
|
|
writer.Write(1);
|
|
writer.Write(_NextBulkOrder);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PendingBribe
|
|
{
|
|
public IBOD BOD { get; set; }
|
|
public int Amount { get; set; }
|
|
|
|
public PendingBribe(IBOD bod, int amount)
|
|
{
|
|
BOD = bod;
|
|
Amount = amount;
|
|
}
|
|
}
|
|
}
|