Reorganizes Project (#41)

This commit is contained in:
Kamron Batman 2019-08-02 18:13:40 -07:00 • committed by GitHub
parent 08bf44af9a
commit 3614a66aee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3499 changed files with 79 additions and 55 deletions

View file

@ -0,0 +1,29 @@
using System;
namespace Server.Engines.Harvest
{
public class BonusHarvestResource
{
public BonusHarvestResource(double reqSkill, double chance, TextDefinition message, Type type)
{
ReqSkill = reqSkill;
Chance = chance;
Type = type;
SuccessMessage = message;
}
public Type Type{ get; set; }
public double ReqSkill{ get; set; }
public double Chance{ get; set; }
public TextDefinition SuccessMessage{ get; }
public void SendSuccessTo(Mobile m)
{
TextDefinition.SendMessageTo(m, SuccessMessage);
}
}
}

View file

@ -0,0 +1,91 @@
using System;
namespace Server.Engines.Harvest
{
public class HarvestBank
{
private int m_Current;
private int m_Maximum;
private DateTime m_NextRespawn;
private HarvestVein m_Vein, m_DefaultVein;
public HarvestBank(HarvestDefinition def, HarvestVein defaultVein)
{
m_Maximum = Utility.RandomMinMax(def.MinTotal, def.MaxTotal);
m_Current = m_Maximum;
m_DefaultVein = defaultVein;
m_Vein = m_DefaultVein;
Definition = def;
}
public HarvestDefinition Definition{ get; }
public int Current
{
get
{
CheckRespawn();
return m_Current;
}
}
public HarvestVein Vein
{
get
{
CheckRespawn();
return m_Vein;
}
set => m_Vein = value;
}
public HarvestVein DefaultVein
{
get
{
CheckRespawn();
return m_DefaultVein;
}
}
public void CheckRespawn()
{
if (m_Current == m_Maximum || m_NextRespawn > DateTime.UtcNow)
return;
m_Current = m_Maximum;
if (Definition.RandomizeVeins) m_DefaultVein = Definition.GetVeinFrom(Utility.RandomDouble());
m_Vein = m_DefaultVein;
}
public void Consume(int amount, Mobile from)
{
CheckRespawn();
if (m_Current == m_Maximum)
{
double min = Definition.MinRespawn.TotalMinutes;
double max = Definition.MaxRespawn.TotalMinutes;
double rnd = Utility.RandomDouble();
m_Current = m_Maximum - amount;
double minutes = min + rnd * (max - min);
if (Definition.RaceBonus && from.Race == Race.Elf) //def.RaceBonus = Core.ML
minutes *= .75; //25% off the time.
m_NextRespawn = DateTime.UtcNow + TimeSpan.FromMinutes(minutes);
}
else
{
m_Current -= amount;
}
if (m_Current < 0)
m_Current = 0;
}
}
}

View file

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
namespace Server.Engines.Harvest
{
public class HarvestDefinition
{
public int BankWidth{ get; set; }
public int BankHeight{ get; set; }
public int MinTotal{ get; set; }
public int MaxTotal{ get; set; }
public int[] Tiles{ get; set; }
public bool RangedTiles{ get; set; }
public TimeSpan MinRespawn{ get; set; }
public TimeSpan MaxRespawn{ get; set; }
public int MaxRange{ get; set; }
public int ConsumedPerHarvest{ get; set; }
public int ConsumedPerFeluccaHarvest{ get; set; }
public bool PlaceAtFeetIfFull{ get; set; }
public SkillName Skill{ get; set; }
public int[] EffectActions{ get; set; }
public int[] EffectCounts{ get; set; }
public int[] EffectSounds{ get; set; }
public TimeSpan EffectSoundDelay{ get; set; }
public TimeSpan EffectDelay{ get; set; }
public object NoResourcesMessage{ get; set; }
public object OutOfRangeMessage{ get; set; }
public object TimedOutOfRangeMessage{ get; set; }
public object DoubleHarvestMessage{ get; set; }
public object FailMessage{ get; set; }
public object PackFullMessage{ get; set; }
public object ToolBrokeMessage{ get; set; }
public HarvestResource[] Resources{ get; set; }
public HarvestVein[] Veins{ get; set; }
public BonusHarvestResource[] BonusResources{ get; set; }
public bool RaceBonus{ get; set; }
public bool RandomizeVeins{ get; set; }
public Dictionary<Map, Dictionary<Point2D, HarvestBank>> Banks{ get; }
= new Dictionary<Map, Dictionary<Point2D, HarvestBank>>();
public void SendMessageTo(Mobile from, object message)
{
if (message is int messageInt)
from.SendLocalizedMessage(messageInt);
else
from.SendMessage(message.ToString());
}
public HarvestBank GetBank(Map map, int x, int y)
{
if (map == null || map == Map.Internal)
return null;
x /= BankWidth;
y /= BankHeight;
if (!Banks.TryGetValue(map, out Dictionary<Point2D, HarvestBank> banks))
Banks[map] = banks = new Dictionary<Point2D, HarvestBank>();
Point2D key = new Point2D(x, y);
if (!banks.TryGetValue(key, out HarvestBank bank))
banks[key] = bank = new HarvestBank(this, GetVeinAt(map, x, y));
return bank;
}
public HarvestVein GetVeinAt(Map map, int x, int y)
{
if (Veins.Length == 1)
return Veins[0];
double randomValue;
if (RandomizeVeins)
{
randomValue = Utility.RandomDouble();
}
else
{
Random random = new Random(x * 17 + y * 11 + map.MapID * 3);
randomValue = random.NextDouble();
}
return GetVeinFrom(randomValue);
}
public HarvestVein GetVeinFrom(double randomValue)
{
if (Veins.Length == 1)
return Veins[0];
randomValue *= 100;
for (int i = 0; i < Veins.Length; ++i)
{
if (randomValue <= Veins[i].VeinChance)
return Veins[i];
randomValue -= Veins[i].VeinChance;
}
return null;
}
public BonusHarvestResource GetBonusResource()
{
if (BonusResources == null)
return null;
double randomValue = Utility.RandomDouble() * 100;
for (int i = 0; i < BonusResources.Length; ++i)
{
if (randomValue <= BonusResources[i].Chance)
return BonusResources[i];
randomValue -= BonusResources[i].Chance;
}
return null;
}
public bool Validate(int tileID)
{
if (RangedTiles)
{
bool contains = false;
for (int i = 0; !contains && i < Tiles.Length; i += 2)
contains = tileID >= Tiles[i] && tileID <= Tiles[i + 1];
return contains;
}
int dist = -1;
for (int i = 0; dist < 0 && i < Tiles.Length; ++i)
dist = Tiles[i] - tileID;
return dist == 0;
}
}
}

View file

@ -0,0 +1,34 @@
using System;
namespace Server.Engines.Harvest
{
public class HarvestResource
{
public HarvestResource(double reqSkill, double minSkill, double maxSkill, object message, params Type[] types)
{
ReqSkill = reqSkill;
MinSkill = minSkill;
MaxSkill = maxSkill;
Types = types;
SuccessMessage = message;
}
public Type[] Types{ get; set; }
public double ReqSkill{ get; set; }
public double MinSkill{ get; set; }
public double MaxSkill{ get; set; }
public object SuccessMessage{ get; }
public void SendSuccessTo(Mobile m)
{
if (SuccessMessage is int messageInt)
m.SendLocalizedMessage(messageInt);
else
m.SendMessage(SuccessMessage.ToString());
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Engines.Harvest
{
public class HarvestSoundTimer : Timer
{
private HarvestDefinition m_Definition;
private Mobile m_From;
private bool m_Last;
private HarvestSystem m_System;
private object m_ToHarvest, m_Locked;
private Item m_Tool;
public HarvestSoundTimer(Mobile from, Item tool, HarvestSystem system, HarvestDefinition def, object toHarvest,
object locked, bool last) : base(def.EffectSoundDelay)
{
m_From = from;
m_Tool = tool;
m_System = system;
m_Definition = def;
m_ToHarvest = toHarvest;
m_Locked = locked;
m_Last = last;
}
protected override void OnTick()
{
m_System.DoHarvestingSound(m_From, m_Tool, m_Definition, m_ToHarvest);
if (m_Last)
m_System.FinishHarvesting(m_From, m_Tool, m_Definition, m_ToHarvest, m_Locked);
}
}
}

View file

@ -0,0 +1,472 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Items;
using Server.Targeting;
namespace Server.Engines.Harvest
{
public abstract class HarvestSystem
{
public HarvestSystem()
{
Definitions = new List<HarvestDefinition>();
}
public List<HarvestDefinition> Definitions{ get; }
public virtual bool CheckTool(Mobile from, Item tool)
{
bool wornOut = tool?.Deleted != false || tool is IUsesRemaining remaining && remaining.UsesRemaining <= 0;
if (wornOut)
from.SendLocalizedMessage(1044038); // You have worn out your tool!
return !wornOut;
}
public virtual bool CheckHarvest(Mobile from, Item tool)
{
return CheckTool(from, tool);
}
public virtual bool CheckHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
return CheckTool(from, tool);
}
public virtual bool CheckRange(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
{
bool inRange = from.Map == map && from.InRange(loc, def.MaxRange);
if (!inRange)
def.SendMessageTo(from, timed ? def.TimedOutOfRangeMessage : def.OutOfRangeMessage);
return inRange;
}
public virtual bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
{
HarvestBank bank = def.GetBank(map, loc.X, loc.Y);
bool available = bank?.Current >= def.ConsumedPerHarvest;
if (!available)
def.SendMessageTo(from, timed ? def.DoubleHarvestMessage : def.NoResourcesMessage);
return available;
}
public virtual void OnBadHarvestTarget(Mobile from, Item tool, object toHarvest)
{
}
public virtual object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
/* Here we prevent multiple harvesting.
*
* Some options:
* - 'return tool;' : This will allow the player to harvest more than once concurrently, but only if they use multiple tools. This seems to be as OSI.
* - 'return GetType();' : This will disallow multiple harvesting of the same type. That is, we couldn't mine more than once concurrently, but we could be both mining and lumberjacking.
* - 'return typeof( HarvestSystem );' : This will completely restrict concurrent harvesting.
*/
return tool;
}
public virtual void OnConcurrentHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
}
public virtual void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
}
public virtual bool BeginHarvesting(Mobile from, Item tool)
{
if (!CheckHarvest(from, tool))
return false;
from.Target = new HarvestTarget(tool, this);
return true;
}
public virtual void FinishHarvesting(Mobile from, Item tool, HarvestDefinition def, object toHarvest, object locked)
{
from.EndAction(locked);
if (!CheckHarvest(from, tool))
return;
if (!GetHarvestDetails(from, tool, toHarvest, out int tileID, out Map map, out Point3D loc))
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
if (!def.Validate(tileID))
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
if (!CheckRange(from, tool, def, map, loc, true))
return;
if (!CheckResources(from, tool, def, map, loc, true))
return;
if (!CheckHarvest(from, tool, def, toHarvest))
return;
if (SpecialHarvest(from, tool, def, map, loc))
return;
HarvestBank bank = def.GetBank(map, loc.X, loc.Y);
if (bank == null)
return;
HarvestVein vein = bank.Vein;
if (vein != null)
vein = MutateVein(from, tool, def, bank, toHarvest, vein);
if (vein == null)
return;
HarvestResource primary = vein.PrimaryResource;
HarvestResource fallback = vein.FallbackResource;
HarvestResource resource = MutateResource(from, tool, def, map, loc, vein, primary, fallback);
double skillBase = from.Skills[def.Skill].Base;
// double skillValue = from.Skills[def.Skill].Value;
Type type = null;
if (skillBase >= resource.ReqSkill && from.CheckSkill(def.Skill, resource.MinSkill, resource.MaxSkill))
{
type = GetResourceType(from, tool, def, map, loc, resource);
if (type != null)
type = MutateType(type, from, tool, def, map, loc, resource);
if (type != null)
{
Item item = Construct(type, from);
if (item == null)
{
type = null;
}
else
{
//The whole harvest system is kludgy and I'm sure this is just adding to it.
if (item.Stackable)
{
int amount = def.ConsumedPerHarvest;
int feluccaAmount = def.ConsumedPerFeluccaHarvest;
int racialAmount = (int)Math.Ceiling(amount * 1.1);
int feluccaRacialAmount = (int)Math.Ceiling(feluccaAmount * 1.1);
bool eligableForRacialBonus = def.RaceBonus && from.Race == Race.Human;
bool inFelucca = map == Map.Felucca;
if (eligableForRacialBonus && inFelucca && bank.Current >= feluccaRacialAmount &&
0.1 > Utility.RandomDouble())
item.Amount = feluccaRacialAmount;
else if (inFelucca && bank.Current >= feluccaAmount)
item.Amount = feluccaAmount;
else if (eligableForRacialBonus && bank.Current >= racialAmount && 0.1 > Utility.RandomDouble())
item.Amount = racialAmount;
else
item.Amount = amount;
}
bank.Consume(item.Amount, from);
if (Give(from, item, def.PlaceAtFeetIfFull))
{
SendSuccessTo(from, item, resource);
}
else
{
SendPackFullTo(from, item, def, resource);
item.Delete();
}
BonusHarvestResource bonus = def.GetBonusResource();
if (bonus?.Type != null && skillBase >= bonus.ReqSkill)
{
Item bonusItem = Construct(bonus.Type, from);
if (Give(from, bonusItem, true)
) //Bonuses always allow placing at feet, even if pack is full irregrdless of def
bonus.SendSuccessTo(from);
else
item.Delete();
}
if (tool is IUsesRemaining toolWithUses)
{
toolWithUses.ShowUsesRemaining = true;
if (toolWithUses.UsesRemaining > 0)
--toolWithUses.UsesRemaining;
if (toolWithUses.UsesRemaining < 1)
{
tool.Delete();
def.SendMessageTo(from, def.ToolBrokeMessage);
}
}
}
}
}
if (type == null)
def.SendMessageTo(from, def.FailMessage);
OnHarvestFinished(from, tool, def, vein, bank, resource, toHarvest);
}
public virtual void OnHarvestFinished(Mobile from, Item tool, HarvestDefinition def, HarvestVein vein,
HarvestBank bank, HarvestResource resource, object harvested)
{
}
public virtual bool SpecialHarvest(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc)
{
return false;
}
public virtual Item Construct(Type type, Mobile from)
{
try
{
return Activator.CreateInstance(type) as Item;
}
catch
{
return null;
}
}
public virtual HarvestVein MutateVein(Mobile from, Item tool, HarvestDefinition def, HarvestBank bank,
object toHarvest, HarvestVein vein)
{
return vein;
}
public virtual void SendSuccessTo(Mobile from, Item item, HarvestResource resource)
{
resource.SendSuccessTo(from);
}
public virtual void SendPackFullTo(Mobile from, Item item, HarvestDefinition def, HarvestResource resource)
{
def.SendMessageTo(from, def.PackFullMessage);
}
public virtual bool Give(Mobile m, Item item, bool placeAtFeet)
{
if (m.PlaceInBackpack(item))
return true;
if (!placeAtFeet)
return false;
Map map = m.Map;
if (map == null)
return false;
if (m.GetItemsInRange(0).Any(t => t.StackWith(m, item, false)))
return true;
item.MoveToWorld(m.Location, map);
return true;
}
public virtual Type MutateType(Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc,
HarvestResource resource)
{
return from.Region.GetResource(type);
}
public virtual Type GetResourceType(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc,
HarvestResource resource)
{
return resource.Types.Length > 0 ? resource.Types[Utility.Random(resource.Types.Length)] : null;
}
public virtual HarvestResource MutateResource(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc,
HarvestVein vein, HarvestResource primary, HarvestResource fallback)
{
bool racialBonus = def.RaceBonus && from.Race == Race.Elf;
if (vein.ChanceToFallback > Utility.RandomDouble() + (racialBonus ? .20 : 0))
return fallback;
double skillValue = from.Skills[def.Skill].Value;
if (fallback != null && (skillValue < primary.ReqSkill || skillValue < primary.MinSkill))
return fallback;
return primary;
}
public virtual bool OnHarvesting(Mobile from, Item tool, HarvestDefinition def, object toHarvest, object locked,
bool last)
{
if (!CheckHarvest(from, tool))
{
from.EndAction(locked);
return false;
}
if (!GetHarvestDetails(from, tool, toHarvest, out int tileID, out Map map, out Point3D loc))
{
from.EndAction(locked);
OnBadHarvestTarget(from, tool, toHarvest);
return false;
}
if (!def.Validate(tileID))
{
from.EndAction(locked);
OnBadHarvestTarget(from, tool, toHarvest);
return false;
}
if (!CheckRange(from, tool, def, map, loc, true))
{
from.EndAction(locked);
return false;
}
if (!CheckResources(from, tool, def, map, loc, true))
{
from.EndAction(locked);
return false;
}
if (!CheckHarvest(from, tool, def, toHarvest))
{
from.EndAction(locked);
return false;
}
DoHarvestingEffect(from, tool, def, map, loc);
new HarvestSoundTimer(from, tool, this, def, toHarvest, locked, last).Start();
return !last;
}
public virtual void DoHarvestingSound(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
if (def.EffectSounds.Length > 0)
from.PlaySound(Utility.RandomList(def.EffectSounds));
}
public virtual void DoHarvestingEffect(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc)
{
from.Direction = from.GetDirectionTo(loc);
if (!from.Mounted)
from.Animate(Utility.RandomList(def.EffectActions), 5, 1, true, false, 0);
}
public virtual HarvestDefinition GetDefinition(int tileID)
{
return Definitions.FirstOrDefault(check => check.Validate(tileID));
}
public virtual void StartHarvesting(Mobile from, Item tool, object toHarvest)
{
if (!CheckHarvest(from, tool))
return;
if (!GetHarvestDetails(from, tool, toHarvest, out int tileID, out Map map, out Point3D loc))
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
HarvestDefinition def = GetDefinition(tileID);
if (def == null)
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
if (!CheckRange(from, tool, def, map, loc, false))
return;
if (!CheckResources(from, tool, def, map, loc, false))
return;
if (!CheckHarvest(from, tool, def, toHarvest))
return;
object toLock = GetLock(from, tool, def, toHarvest);
if (!from.BeginAction(toLock))
{
OnConcurrentHarvest(from, tool, def, toHarvest);
return;
}
new HarvestTimer(from, tool, this, def, toHarvest, toLock).Start();
OnHarvestStarted(from, tool, def, toHarvest);
}
public virtual bool GetHarvestDetails(Mobile from, Item tool, object toHarvest, out int tileID, out Map map,
out Point3D loc)
{
if (toHarvest is Static staticObj && !staticObj.Movable)
{
tileID = (staticObj.ItemID & 0x3FFF) | 0x4000;
map = staticObj.Map;
loc = staticObj.GetWorldLocation();
}
else if (toHarvest is StaticTarget staticTarget)
{
tileID = (staticTarget.ItemID & 0x3FFF) | 0x4000;
map = from.Map;
loc = staticTarget.Location;
}
else if (toHarvest is LandTarget landTarget)
{
tileID = landTarget.TileID;
map = from.Map;
loc = landTarget.Location;
}
else
{
tileID = 0;
map = null;
loc = Point3D.Zero;
return false;
}
return map != null && map != Map.Internal;
}
}
}
namespace Server
{
public interface IChopable
{
void OnChop(Mobile from);
}
[AttributeUsage(AttributeTargets.Class)]
public class FurnitureAttribute : Attribute
{
public static bool Check(Item item)
{
return item?.GetType().IsDefined(typeof(FurnitureAttribute), false) == true;
}
}
}

View file

@ -0,0 +1,111 @@
using Server.Engines.Quests;
using Server.Engines.Quests.Hag;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Engines.Harvest
{
public class HarvestTarget : Target
{
private HarvestSystem m_System;
private Item m_Tool;
public HarvestTarget(Item tool, HarvestSystem system) : base(-1, true, TargetFlags.None)
{
m_Tool = tool;
m_System = system;
DisallowMultis = true;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_System is Mining && targeted is StaticTarget target)
{
int itemID = target.ItemID;
// grave
if (itemID == 0xED3 || itemID == 0xEDF || itemID == 0xEE0 || itemID == 0xEE1 || itemID == 0xEE2 ||
itemID == 0xEE8)
if (from is PlayerMobile player)
{
QuestSystem qs = player.Quest;
if (!(qs is WitchApprenticeQuest))
return;
FindIngredientObjective obj = qs.FindObjective<FindIngredientObjective>();
if (obj?.Completed == false && obj.Ingredient == Ingredient.Bones)
{
player.SendLocalizedMessage(
1055037); // You finish your grim work, finding some of the specific bones listed in the Hag's recipe.
obj.Complete();
return;
}
}
}
if (m_System is Lumberjacking && targeted is IChopable chopable)
{
chopable.OnChop(from);
}
else if (m_System is Lumberjacking && targeted is IAxe obj && m_Tool is BaseAxe axe)
{
Item item = (Item)obj;
if (!item.IsChildOf(from.Backpack))
from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used.
else if (obj.Axe(from, axe))
from.PlaySound(0x13E);
}
else if (m_System is Lumberjacking && targeted is ICarvable carvable)
{
carvable.Carve(from, m_Tool);
}
else if (m_System is Lumberjacking && FurnitureAttribute.Check(targeted as Item))
{
DestroyFurniture(from, (Item)targeted);
}
else if (m_System is Mining && targeted is TreasureMap map)
{
map.OnBeginDig(from);
}
else
{
m_System.StartHarvesting(from, m_Tool, targeted);
}
}
private void DestroyFurniture(Mobile from, Item item)
{
if (!from.InRange(item.GetWorldLocation(), 3))
{
from.SendLocalizedMessage(500446); // That is too far away.
return;
}
if (!item.IsChildOf(from.Backpack) && !item.Movable)
{
from.SendLocalizedMessage(500462); // You can't destroy that while it is here.
return;
}
from.SendLocalizedMessage(500461); // You destroy the item.
Effects.PlaySound(item.GetWorldLocation(), item.Map, 0x3B3);
if (item is Container container)
{
if (container is TrappableContainer trappableContainer)
trappableContainer.ExecuteTrap(from);
container.Destroy();
}
else
{
item.Delete();
}
}
}
}

View file

@ -0,0 +1,32 @@
using System;
namespace Server.Engines.Harvest
{
public class HarvestTimer : Timer
{
private HarvestDefinition m_Definition;
private Mobile m_From;
private int m_Index, m_Count;
private HarvestSystem m_System;
private object m_ToHarvest, m_Locked;
private Item m_Tool;
public HarvestTimer(Mobile from, Item tool, HarvestSystem system, HarvestDefinition def, object toHarvest,
object locked) : base(TimeSpan.Zero, def.EffectDelay)
{
m_From = from;
m_Tool = tool;
m_System = system;
m_Definition = def;
m_ToHarvest = toHarvest;
m_Locked = locked;
m_Count = Utility.RandomList(def.EffectCounts);
}
protected override void OnTick()
{
if (!m_System.OnHarvesting(m_From, m_Tool, m_Definition, m_ToHarvest, m_Locked, ++m_Index == m_Count))
Stop();
}
}
}

View file

@ -0,0 +1,22 @@
namespace Server.Engines.Harvest
{
public class HarvestVein
{
public HarvestVein(double veinChance, double chanceToFallback, HarvestResource primaryResource,
HarvestResource fallbackResource)
{
VeinChance = veinChance;
ChanceToFallback = chanceToFallback;
PrimaryResource = primaryResource;
FallbackResource = fallbackResource;
}
public double VeinChance{ get; set; }
public double ChanceToFallback{ get; set; }
public HarvestResource PrimaryResource{ get; set; }
public HarvestResource FallbackResource{ get; set; }
}
}