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; }
}
}

View file

@ -0,0 +1,508 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Engines.Quests;
using Server.Engines.Quests.Collector;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
namespace Server.Engines.Harvest
{
public class Fishing : HarvestSystem
{
private static Fishing m_System;
private static MutateEntry[] m_MutateTable =
{
new MutateEntry(80.0, 80.0, 4080.0, true, typeof(SpecialFishingNet)),
new MutateEntry(80.0, 80.0, 4080.0, true, typeof(BigFish)),
new MutateEntry(90.0, 80.0, 4080.0, true, typeof(TreasureMap)),
new MutateEntry(100.0, 80.0, 4080.0, true, typeof(MessageInABottle)),
new MutateEntry(0.0, 125.0, -2375.0, false, typeof(PrizedFish), typeof(WondrousFish), typeof(TrulyRareFish),
typeof(PeculiarFish)),
new MutateEntry(0.0, 105.0, -420.0, false, typeof(Boots), typeof(Shoes), typeof(Sandals), typeof(ThighBoots)),
new MutateEntry(0.0, 200.0, -200.0, false, new Type[1] { null })
};
private static int[] m_WaterTiles =
{
0x00A8, 0x00AB,
0x0136, 0x0137,
0x5797, 0x579C,
0x746E, 0x7485,
0x7490, 0x74AB,
0x74B5, 0x75D5
};
private Fishing()
{
HarvestDefinition fish = new HarvestDefinition
{
BankWidth = 8,
BankHeight = 8,
MinTotal = 5,
MaxTotal = 15,
MinRespawn = TimeSpan.FromMinutes(10.0),
MaxRespawn = TimeSpan.FromMinutes(20.0),
Skill = SkillName.Fishing,
Tiles = m_WaterTiles,
RangedTiles = true,
MaxRange = 4,
ConsumedPerHarvest = 1,
ConsumedPerFeluccaHarvest = 1,
EffectActions = new[] { 12 },
EffectSounds = new int[0],
EffectCounts = new[] { 1 },
EffectDelay = TimeSpan.Zero,
EffectSoundDelay = TimeSpan.FromSeconds(8.0),
NoResourcesMessage = 503172, // The fish don't seem to be biting here.
FailMessage = 503171, // You fish a while, but fail to catch anything.
TimedOutOfRangeMessage = 500976, // You need to be closer to the water to fish!
OutOfRangeMessage = 500976, // You need to be closer to the water to fish!
PackFullMessage = 503176, // You do not have room in your backpack for a fish.
ToolBrokeMessage = 503174 // You broke your fishing pole.
};
HarvestResource[] res = {
new HarvestResource(00.0, 00.0, 100.0, 1043297, typeof(Fish))
};
HarvestVein[] veins = {
new HarvestVein(100.0, 0.0, res[0], null)
};
fish.Resources = res;
fish.Veins = veins;
if (Core.ML)
fish.BonusResources = new[]
{
new BonusHarvestResource(0, 99.4, null, null), //set to same chance as mining ml gems
new BonusHarvestResource(80.0, .6, 1072597, typeof(WhitePearl))
};
Definition = fish;
Definitions.Add(fish);
}
public static Fishing System => m_System ?? (m_System = new Fishing());
public HarvestDefinition Definition{ get; }
public override void OnConcurrentHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
from.SendLocalizedMessage(500972); // You are already fishing.
}
public override bool SpecialHarvest(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc)
{
if (from is PlayerMobile player)
{
QuestSystem qs = player.Quest;
if (qs is CollectorQuest)
{
QuestObjective obj = qs.FindObjective<FishPearlsObjective>();
if (obj?.Completed == false)
{
if (Utility.RandomDouble() < 0.5)
{
player.SendLocalizedMessage(1055086, "",
0x59); // You pull a shellfish out of the water, and find a rainbow pearl inside of it.
obj.CurProgress++;
}
else
{
player.SendLocalizedMessage(1055087, "",
0x2C); // You pull a shellfish out of the water, but it doesn't have a rainbow pearl.
}
return true;
}
}
}
return false;
}
public override Type MutateType(Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc,
HarvestResource resource)
{
bool deepWater = SpecialFishingNet.FullValidation(map, loc.X, loc.Y);
double skillBase = from.Skills.Fishing.Base;
double skillValue = from.Skills.Fishing.Value;
for (int i = 0; i < m_MutateTable.Length; ++i)
{
MutateEntry entry = m_MutateTable[i];
if (!deepWater && entry.m_DeepWater)
continue;
if (skillBase >= entry.m_ReqSkill)
{
double chance = (skillValue - entry.m_MinSkill) / (entry.m_MaxSkill - entry.m_MinSkill);
if (chance > Utility.RandomDouble())
return entry.m_Types[Utility.Random(entry.m_Types.Length)];
}
}
return type;
}
private static Map SafeMap(Map map)
{
return map == null || map == Map.Internal ? Map.Trammel : map;
}
public override bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
{
return from?.Backpack?.FindItemsByType<SOS>().Any(sos =>
(from.Map == Map.Felucca || from.Map == Map.Trammel) && from.InRange(sos.TargetLocation, 60)) ??
base.CheckResources(from, tool, def, map, loc, timed);
}
public override Item Construct(Type type, Mobile from)
{
if (type == typeof(TreasureMap))
{
int level;
if (from is PlayerMobile mobile && mobile.Young && mobile.Map == Map.Trammel &&
TreasureMap.IsInHavenIsland(from))
level = 0;
else
level = 1;
return new TreasureMap(level, from.Map == Map.Felucca ? Map.Felucca : Map.Trammel);
}
if (type == typeof(MessageInABottle))
return new MessageInABottle(from.Map == Map.Felucca ? Map.Felucca : Map.Trammel);
Container pack = from.Backpack;
if (pack != null)
{
List<SOS> messages = pack.FindItemsByType<SOS>();
for (int i = 0; i < messages.Count; ++i)
{
SOS sos = messages[i];
if ((from.Map == Map.Felucca || from.Map == Map.Trammel) && from.InRange(sos.TargetLocation, 60))
{
Item preLoot = null;
switch (Utility.Random(8))
{
case 0: // Body parts
{
int[] list =
{
0x1CDD, 0x1CE5, // arm
0x1CE0, 0x1CE8, // torso
0x1CE1, 0x1CE9, // head
0x1CE2, 0x1CEC // leg
};
preLoot = new ShipwreckedItem(Utility.RandomList(list));
break;
}
case 1: // Bone parts
{
int[] list =
{
0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, // skulls
0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, 0x1B10, // bone piles
0x1B15, 0x1B16 // pelvis bones
};
preLoot = new ShipwreckedItem(Utility.RandomList(list));
break;
}
case 2: // Paintings and portraits
{
preLoot = new ShipwreckedItem(Utility.Random(0xE9F, 10));
break;
}
case 3: // Pillows
{
preLoot = new ShipwreckedItem(Utility.Random(0x13A4, 11));
break;
}
case 4: // Shells
{
preLoot = new ShipwreckedItem(Utility.Random(0xFC4, 9));
break;
}
case 5: //Hats
{
if (Utility.RandomBool())
preLoot = new SkullCap();
else
preLoot = new TricorneHat();
break;
}
case 6: // Misc
{
int[] list =
{
0x1EB5, // unfinished barrel
0xA2A, // stool
0xC1F, // broken clock
0x1047, 0x1048, // globe
0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4 // barrel staves
};
if (Utility.Random(list.Length + 1) == 0)
preLoot = new Candelabra();
else
preLoot = new ShipwreckedItem(Utility.RandomList(list));
break;
}
}
if (preLoot != null)
{
((IShipwreckedItem)preLoot).IsShipwreckedItem = true;
return preLoot;
}
LockableContainer chest;
if (Utility.RandomBool())
chest = new MetalGoldenChest();
else
chest = new WoodenChest();
if (sos.IsAncient)
chest.Hue = 0x481;
TreasureMapChest.Fill(chest, Math.Max(1, Math.Min(4, sos.Level)));
chest.DropItem(sos.IsAncient ? new FabledFishingNet() : new SpecialFishingNet());
chest.Movable = true;
chest.Locked = false;
chest.TrapType = TrapType.None;
chest.TrapPower = 0;
chest.TrapLevel = 0;
sos.Delete();
return chest;
}
}
}
return base.Construct(type, from);
}
public override bool Give(Mobile m, Item item, bool placeAtFeet)
{
if (item is TreasureMap || item is MessageInABottle || item is SpecialFishingNet)
{
BaseCreature serp;
if (0.25 > Utility.RandomDouble())
serp = new DeepSeaSerpent();
else
serp = new SeaSerpent();
int x = m.X, y = m.Y;
Map map = m.Map;
for (int i = 0; map != null && i < 20; ++i)
{
int tx = m.X - 10 + Utility.Random(21);
int ty = m.Y - 10 + Utility.Random(21);
LandTile t = map.Tiles.GetLandTile(tx, ty);
if (t.Z == -5 && (t.ID >= 0xA8 && t.ID <= 0xAB || t.ID >= 0x136 && t.ID <= 0x137) &&
!SpellHelper.CheckMulti(new Point3D(tx, ty, -5), map))
{
x = tx;
y = ty;
break;
}
}
serp.MoveToWorld(new Point3D(x, y, -5), map);
serp.Home = serp.Location;
serp.RangeHome = 10;
serp.PackItem(item);
m.SendLocalizedMessage(503170); // Uh oh! That doesn't look like a fish!
return true; // we don't want to give the item to the player, it's on the serpent
}
return base.Give(m, item, placeAtFeet || item is BigFish || item is WoodenChest || item is MetalGoldenChest);
}
public override void SendSuccessTo(Mobile from, Item item, HarvestResource resource)
{
if (item is BigFish fish)
{
from.SendLocalizedMessage(1042635); // Your fishing pole bends as you pull a big fish from the depths!
fish.Fisher = from;
}
else if (item is WoodenChest || item is MetalGoldenChest)
{
from.SendLocalizedMessage(503175); // You pull up a heavy chest from the depths of the ocean!
}
else
{
int number;
string name;
if (item is BaseMagicFish)
{
number = 1008124;
name = "a mess of small fish";
}
else if (item is Fish)
{
number = 1008124;
name = item.ItemData.Name;
}
else if (item is BaseShoes)
{
number = 1008124;
name = item.ItemData.Name;
}
else if (item is TreasureMap)
{
number = 1008125;
name = "a sodden piece of parchment";
}
else if (item is MessageInABottle)
{
number = 1008125;
name = "a bottle, with a message in it";
}
else if (item is SpecialFishingNet)
{
number = 1008125;
name = "a special fishing net"; // TODO: this is just a guess--what should it really be named?
}
else
{
number = 1043297;
if ((item.ItemData.Flags & TileFlag.ArticleA) != 0)
name = "a " + item.ItemData.Name;
else if ((item.ItemData.Flags & TileFlag.ArticleAn) != 0)
name = "an " + item.ItemData.Name;
else
name = item.ItemData.Name;
}
NetState ns = from.NetState;
if (ns == null)
return;
if (number == 1043297 || ns.HighSeas)
from.SendLocalizedMessage(number, name);
else
from.SendLocalizedMessage(number, true, name);
}
}
public override void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
base.OnHarvestStarted(from, tool, def, toHarvest);
if (GetHarvestDetails(from, tool, toHarvest, out _, out Map map, out Point3D loc))
Timer.DelayCall(TimeSpan.FromSeconds(1.5),
delegate
{
if (Core.ML)
from.RevealingAction();
Effects.SendLocationEffect(loc, map, 0x352D, 16, 4);
Effects.PlaySound(loc, map, 0x364);
});
}
public override void OnHarvestFinished(Mobile from, Item tool, HarvestDefinition def, HarvestVein vein,
HarvestBank bank, HarvestResource resource, object harvested)
{
base.OnHarvestFinished(from, tool, def, vein, bank, resource, harvested);
if (Core.ML)
from.RevealingAction();
}
public override object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
return this;
}
public override bool BeginHarvesting(Mobile from, Item tool)
{
if (!base.BeginHarvesting(from, tool))
return false;
from.SendLocalizedMessage(500974); // What water do you want to fish in?
return true;
}
public override bool CheckHarvest(Mobile from, Item tool)
{
if (!base.CheckHarvest(from, tool))
return false;
if (from.Mounted)
{
from.SendLocalizedMessage(500971); // You can't fish while riding!
return false;
}
return true;
}
public override bool CheckHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
if (!base.CheckHarvest(from, tool, def, toHarvest))
return false;
if (from.Mounted)
{
from.SendLocalizedMessage(500971); // You can't fish while riding!
return false;
}
return true;
}
private class MutateEntry
{
public bool m_DeepWater;
public double m_ReqSkill, m_MinSkill, m_MaxSkill;
public Type[] m_Types;
public MutateEntry(double reqSkill, double minSkill, double maxSkill, bool deepWater, params Type[] types)
{
m_ReqSkill = reqSkill;
m_MinSkill = minSkill;
m_MaxSkill = maxSkill;
m_DeepWater = deepWater;
m_Types = types;
}
}
}
}

View file

@ -0,0 +1,187 @@
using System;
using Server.Items;
using Server.Network;
using Server.Targeting;
namespace Server.Engines.Harvest
{
public class Lumberjacking : HarvestSystem
{
private static Lumberjacking m_System;
#region Tile lists
private static int[] m_TreeTiles =
{
0x4CCA, 0x4CCB, 0x4CCC, 0x4CCD, 0x4CD0, 0x4CD3, 0x4CD6, 0x4CD8,
0x4CDA, 0x4CDD, 0x4CE0, 0x4CE3, 0x4CE6, 0x4CF8, 0x4CFB, 0x4CFE,
0x4D01, 0x4D41, 0x4D42, 0x4D43, 0x4D44, 0x4D57, 0x4D58, 0x4D59,
0x4D5A, 0x4D5B, 0x4D6E, 0x4D6F, 0x4D70, 0x4D71, 0x4D72, 0x4D84,
0x4D85, 0x4D86, 0x52B5, 0x52B6, 0x52B7, 0x52B8, 0x52B9, 0x52BA,
0x52BB, 0x52BC, 0x52BD,
0x4CCE, 0x4CCF, 0x4CD1, 0x4CD2, 0x4CD4, 0x4CD5, 0x4CD7, 0x4CD9,
0x4CDB, 0x4CDC, 0x4CDE, 0x4CDF, 0x4CE1, 0x4CE2, 0x4CE4, 0x4CE5,
0x4CE7, 0x4CE8, 0x4CF9, 0x4CFA, 0x4CFC, 0x4CFD, 0x4CFF, 0x4D00,
0x4D02, 0x4D03, 0x4D45, 0x4D46, 0x4D47, 0x4D48, 0x4D49, 0x4D4A,
0x4D4B, 0x4D4C, 0x4D4D, 0x4D4E, 0x4D4F, 0x4D50, 0x4D51, 0x4D52,
0x4D53, 0x4D5C, 0x4D5D, 0x4D5E, 0x4D5F, 0x4D60, 0x4D61, 0x4D62,
0x4D63, 0x4D64, 0x4D65, 0x4D66, 0x4D67, 0x4D68, 0x4D69, 0x4D73,
0x4D74, 0x4D75, 0x4D76, 0x4D77, 0x4D78, 0x4D79, 0x4D7A, 0x4D7B,
0x4D7C, 0x4D7D, 0x4D7E, 0x4D7F, 0x4D87, 0x4D88, 0x4D89, 0x4D8A,
0x4D8B, 0x4D8C, 0x4D8D, 0x4D8E, 0x4D8F, 0x4D90, 0x4D95, 0x4D96,
0x4D97, 0x4D99, 0x4D9A, 0x4D9B, 0x4D9D, 0x4D9E, 0x4D9F, 0x4DA1,
0x4DA2, 0x4DA3, 0x4DA5, 0x4DA6, 0x4DA7, 0x4DA9, 0x4DAA, 0x4DAB,
0x52BE, 0x52BF, 0x52C0, 0x52C1, 0x52C2, 0x52C3, 0x52C4, 0x52C5,
0x52C6, 0x52C7
};
#endregion
private Lumberjacking()
{
HarvestResource[] res;
HarvestVein[] veins;
HarvestDefinition lumber = new HarvestDefinition
{
BankWidth = 4,
BankHeight = 3,
MinTotal = 20,
MaxTotal = 45,
MinRespawn = TimeSpan.FromMinutes(20.0),
MaxRespawn = TimeSpan.FromMinutes(30.0),
Skill = SkillName.Lumberjacking,
Tiles = m_TreeTiles,
MaxRange = 2,
ConsumedPerHarvest = 10,
ConsumedPerFeluccaHarvest = 20,
EffectActions = new[] { 13 },
EffectSounds = new[] { 0x13E },
EffectCounts = Core.AOS ? new[] { 1 } : new[] { 1, 2, 2, 2, 3 },
EffectDelay = TimeSpan.FromSeconds(1.6),
EffectSoundDelay = TimeSpan.FromSeconds(0.9),
NoResourcesMessage = 500493, // There's not enough wood here to harvest.
FailMessage = 500495, // You hack at the tree for a while, but fail to produce any useable wood.
OutOfRangeMessage = 500446, // That is too far away.
PackFullMessage = 500497, // You can't place any wood into your backpack!
ToolBrokeMessage = 500499 // You broke your axe.
};
if (Core.ML)
{
res = new[]
{
new HarvestResource(00.0, 00.0, 100.0, 1072540, typeof(Log)),
new HarvestResource(65.0, 25.0, 105.0, 1072541, typeof(OakLog)),
new HarvestResource(80.0, 40.0, 120.0, 1072542, typeof(AshLog)),
new HarvestResource(95.0, 55.0, 135.0, 1072543, typeof(YewLog)),
new HarvestResource(100.0, 60.0, 140.0, 1072544, typeof(HeartwoodLog)),
new HarvestResource(100.0, 60.0, 140.0, 1072545, typeof(BloodwoodLog)),
new HarvestResource(100.0, 60.0, 140.0, 1072546, typeof(FrostwoodLog))
};
veins = new[]
{
new HarvestVein(49.0, 0.0, res[0], null), // Ordinary Logs
new HarvestVein(30.0, 0.5, res[1], res[0]), // Oak
new HarvestVein(10.0, 0.5, res[2], res[0]), // Ash
new HarvestVein(05.0, 0.5, res[3], res[0]), // Yew
new HarvestVein(03.0, 0.5, res[4], res[0]), // Heartwood
new HarvestVein(02.0, 0.5, res[5], res[0]), // Bloodwood
new HarvestVein(01.0, 0.5, res[6], res[0]) // Frostwood
};
lumber.BonusResources = new[]
{
new BonusHarvestResource(0, 83.9, null, null), //Nothing
new BonusHarvestResource(100, 10.0, 1072548, typeof(BarkFragment)),
new BonusHarvestResource(100, 03.0, 1072550, typeof(LuminescentFungi)),
new BonusHarvestResource(100, 02.0, 1072547, typeof(SwitchItem)),
new BonusHarvestResource(100, 01.0, 1072549, typeof(ParasiticPlant)),
new BonusHarvestResource(100, 00.1, 1072551, typeof(BrilliantAmber))
};
}
else
{
res = new[]
{
new HarvestResource(00.0, 00.0, 100.0, 500498, typeof(Log))
};
veins = new[]
{
new HarvestVein(100.0, 0.0, res[0], null)
};
}
lumber.Resources = res;
lumber.Veins = veins;
lumber.RaceBonus = Core.ML;
lumber.RandomizeVeins = Core.ML;
Definition = lumber;
Definitions.Add(lumber);
}
public static Lumberjacking System => m_System ?? (m_System = new Lumberjacking());
public HarvestDefinition Definition{ get; }
public override bool CheckHarvest(Mobile from, Item tool)
{
if (!base.CheckHarvest(from, tool))
return false;
if (tool.Parent != from)
{
from.SendLocalizedMessage(500487); // The axe must be equipped for any serious wood chopping.
return false;
}
return true;
}
public override bool CheckHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
if (!base.CheckHarvest(from, tool, def, toHarvest))
return false;
if (tool.Parent != from)
{
from.SendLocalizedMessage(500487); // The axe must be equipped for any serious wood chopping.
return false;
}
return true;
}
public override void OnBadHarvestTarget(Mobile from, Item tool, object toHarvest)
{
if (toHarvest is Mobile mobile)
mobile.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500450,
from.NetState); // You can only skin dead creatures.
else if (toHarvest is Item item)
item.LabelTo(from, 500464); // Use this on corpses to carve away meat and hide
else if (toHarvest is StaticTarget || toHarvest is LandTarget)
from.SendLocalizedMessage(500489); // You can't use an axe on that.
else
from.SendLocalizedMessage(1005213); // You can't do that
}
public override void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
base.OnHarvestStarted(from, tool, def, toHarvest);
if (Core.ML)
from.RevealingAction();
}
public static void Initialize()
{
Array.Sort(m_TreeTiles);
}
}
}

View file

@ -0,0 +1,394 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Engines.Harvest
{
public class Mining : HarvestSystem
{
private static Mining m_System;
private static int[] m_Offsets =
{
-1, -1,
-1, 0,
-1, 1,
0, -1,
0, 1,
1, -1,
1, 0,
1, 1
};
private Mining()
{
#region Mining for ore and stone
OreAndStone = new HarvestDefinition
{
BankWidth = 8,
BankHeight = 8,
MinTotal = 10,
MaxTotal = 34,
MinRespawn = TimeSpan.FromMinutes(10.0),
MaxRespawn = TimeSpan.FromMinutes(20.0),
Skill = SkillName.Mining,
Tiles = m_MountainAndCaveTiles,
MaxRange = 2,
ConsumedPerHarvest = 1,
ConsumedPerFeluccaHarvest = 2,
EffectActions = new[] { 11 },
EffectSounds = new[] { 0x125, 0x126 },
EffectCounts = new[] { 1 },
EffectDelay = TimeSpan.FromSeconds(1.6),
EffectSoundDelay = TimeSpan.FromSeconds(0.9),
NoResourcesMessage = 503040, // There is no metal here to mine.
DoubleHarvestMessage = 503042, // Someone has gotten to the metal before you.
TimedOutOfRangeMessage = 503041, // You have moved too far away to continue mining.
OutOfRangeMessage = 500446, // That is too far away.
FailMessage = 503043, // You loosen some rocks but fail to find any useable ore.
PackFullMessage = 1010481, // Your backpack is full, so the ore you mined is lost.
ToolBrokeMessage = 1044038 // You have worn out your tool!
};
HarvestResource[] res = {
new HarvestResource(00.0, 00.0, 100.0, 1007072, typeof(IronOre), typeof(Granite)),
new HarvestResource(65.0, 25.0, 105.0, 1007073, typeof(DullCopperOre), typeof(DullCopperGranite),
typeof(DullCopperElemental)),
new HarvestResource(70.0, 30.0, 110.0, 1007074, typeof(ShadowIronOre), typeof(ShadowIronGranite),
typeof(ShadowIronElemental)),
new HarvestResource(75.0, 35.0, 115.0, 1007075, typeof(CopperOre), typeof(CopperGranite),
typeof(CopperElemental)),
new HarvestResource(80.0, 40.0, 120.0, 1007076, typeof(BronzeOre), typeof(BronzeGranite),
typeof(BronzeElemental)),
new HarvestResource(85.0, 45.0, 125.0, 1007077, typeof(GoldOre), typeof(GoldGranite),
typeof(GoldenElemental)),
new HarvestResource(90.0, 50.0, 130.0, 1007078, typeof(AgapiteOre), typeof(AgapiteGranite),
typeof(AgapiteElemental)),
new HarvestResource(95.0, 55.0, 135.0, 1007079, typeof(VeriteOre), typeof(VeriteGranite),
typeof(VeriteElemental)),
new HarvestResource(99.0, 59.0, 139.0, 1007080, typeof(ValoriteOre), typeof(ValoriteGranite),
typeof(ValoriteElemental))
};
HarvestVein[] veins = {
new HarvestVein(49.6, 0.0, res[0], null), // Iron
new HarvestVein(11.2, 0.5, res[1], res[0]), // Dull Copper
new HarvestVein(09.8, 0.5, res[2], res[0]), // Shadow Iron
new HarvestVein(08.4, 0.5, res[3], res[0]), // Copper
new HarvestVein(07.0, 0.5, res[4], res[0]), // Bronze
new HarvestVein(05.6, 0.5, res[5], res[0]), // Gold
new HarvestVein(04.2, 0.5, res[6], res[0]), // Agapite
new HarvestVein(02.8, 0.5, res[7], res[0]), // Verite
new HarvestVein(01.4, 0.5, res[8], res[0]) // Valorite
};
OreAndStone.Resources = res;
OreAndStone.Veins = veins;
if (Core.ML)
OreAndStone.BonusResources = new[]
{
new BonusHarvestResource(0, 99.4, null, null), //Nothing
new BonusHarvestResource(100, .1, 1072562, typeof(BlueDiamond)),
new BonusHarvestResource(100, .1, 1072567, typeof(DarkSapphire)),
new BonusHarvestResource(100, .1, 1072570, typeof(EcruCitrine)),
new BonusHarvestResource(100, .1, 1072564, typeof(FireRuby)),
new BonusHarvestResource(100, .1, 1072566, typeof(PerfectEmerald)),
new BonusHarvestResource(100, .1, 1072568, typeof(Turquoise))
};
OreAndStone.RaceBonus = Core.ML;
OreAndStone.RandomizeVeins = Core.ML;
Definitions.Add(OreAndStone);
#endregion
#region Mining for sand
Sand = new HarvestDefinition
{
BankWidth = 8,
BankHeight = 8,
MinTotal = 6,
MaxTotal = 12,
MinRespawn = TimeSpan.FromMinutes(10.0),
MaxRespawn = TimeSpan.FromMinutes(20.0),
Skill = SkillName.Mining,
Tiles = m_SandTiles,
MaxRange = 2,
ConsumedPerHarvest = 1,
ConsumedPerFeluccaHarvest = 1,
EffectActions = new[] { 11 },
EffectSounds = new[] { 0x125, 0x126 },
EffectCounts = new[] { 6 },
EffectDelay = TimeSpan.FromSeconds(1.6),
EffectSoundDelay = TimeSpan.FromSeconds(0.9),
NoResourcesMessage = 1044629, // There is no sand here to mine.
DoubleHarvestMessage = 1044629, // There is no sand here to mine.
TimedOutOfRangeMessage = 503041, // You have moved too far away to continue mining.
OutOfRangeMessage = 500446, // That is too far away.
FailMessage = 1044630, // You dig for a while but fail to find any of sufficient quality for glassblowing.
PackFullMessage = 1044632, // Your backpack can't hold the sand, and it is lost!
ToolBrokeMessage = 1044038 // You have worn out your tool!
};
res = new[]
{
new HarvestResource(100.0, 70.0, 400.0, 1044631, typeof(Sand))
};
veins = new[]
{
new HarvestVein(100.0, 0.0, res[0], null)
};
Sand.Resources = res;
Sand.Veins = veins;
Definitions.Add(Sand);
#endregion
}
public static Mining System => m_System ?? (m_System = new Mining());
public HarvestDefinition OreAndStone{ get; }
public HarvestDefinition Sand{ get; }
public override Type GetResourceType(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc,
HarvestResource resource)
{
if (def != OreAndStone)
return base.GetResourceType(from, tool, def, map, loc, resource);
if (from.Skills.Mining.Base >= 100.0 && from is PlayerMobile pm && pm.StoneMining && pm.ToggleMiningStone
&& 0.1 > Utility.RandomDouble())
return resource.Types[1];
return resource.Types[0];
}
public override bool CheckHarvest(Mobile from, Item tool)
{
if (!base.CheckHarvest(from, tool))
return false;
if (from.Mounted)
{
from.SendLocalizedMessage(501864); // You can't mine while riding.
return false;
}
if (from.IsBodyMod && !from.Body.IsHuman)
{
from.SendLocalizedMessage(501865); // You can't mine while polymorphed.
return false;
}
return true;
}
public override void SendSuccessTo(Mobile from, Item item, HarvestResource resource)
{
if (item is BaseGranite)
from.SendLocalizedMessage(1044606); // You carefully extract some workable stone from the ore vein!
else
base.SendSuccessTo(from, item, resource);
}
public override bool CheckHarvest(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
if (!base.CheckHarvest(from, tool, def, toHarvest))
return false;
if (def == Sand && !(from is PlayerMobile mobile && mobile.Skills.Mining.Base >= 100.0 &&
mobile.SandMining))
{
OnBadHarvestTarget(from, tool, toHarvest);
return false;
}
if (from.Mounted)
{
from.SendLocalizedMessage(501864); // You can't mine while riding.
return false;
}
if (from.IsBodyMod && !from.Body.IsHuman)
{
from.SendLocalizedMessage(501865); // You can't mine while polymorphed.
return false;
}
return true;
}
public override HarvestVein MutateVein(Mobile from, Item tool, HarvestDefinition def, HarvestBank bank,
object toHarvest, HarvestVein vein)
{
if (tool is GargoylesPickaxe && def == OreAndStone)
{
int veinIndex = Array.IndexOf(def.Veins, vein);
if (veinIndex >= 0 && veinIndex < def.Veins.Length - 1)
return def.Veins[veinIndex + 1];
}
return base.MutateVein(from, tool, def, bank, toHarvest, vein);
}
public override void OnHarvestFinished(Mobile from, Item tool, HarvestDefinition def, HarvestVein vein,
HarvestBank bank, HarvestResource resource, object harvested)
{
if (tool is GargoylesPickaxe && def == OreAndStone && 0.1 > Utility.RandomDouble())
{
HarvestResource res = vein.PrimaryResource;
if (res == resource && res.Types.Length >= 3)
try
{
Map map = from.Map;
if (map == null)
return;
if (Activator.CreateInstance(res.Types[2], 25) is BaseCreature spawned)
{
int offset = Utility.Random(8) * 2;
for (int i = 0; i < m_Offsets.Length; i += 2)
{
int x = from.X + m_Offsets[(offset + i) % m_Offsets.Length];
int y = from.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];
if (map.CanSpawnMobile(x, y, from.Z))
{
spawned.OnBeforeSpawn(new Point3D(x, y, from.Z), map);
spawned.MoveToWorld(new Point3D(x, y, from.Z), map);
spawned.Combatant = from;
return;
}
int z = map.GetAverageZ(x, y);
if (Math.Abs(z - from.Z) < 10 && map.CanSpawnMobile(x, y, z))
{
spawned.OnBeforeSpawn(new Point3D(x, y, z), map);
spawned.MoveToWorld(new Point3D(x, y, z), map);
spawned.Combatant = from;
return;
}
}
spawned.OnBeforeSpawn(from.Location, from.Map);
spawned.MoveToWorld(from.Location, from.Map);
spawned.Combatant = from;
}
}
catch
{
// ignored
}
}
}
public override bool BeginHarvesting(Mobile from, Item tool)
{
if (!base.BeginHarvesting(from, tool))
return false;
from.SendLocalizedMessage(503033); // Where do you wish to dig?
return true;
}
public override void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest)
{
base.OnHarvestStarted(from, tool, def, toHarvest);
if (Core.ML)
from.RevealingAction();
}
public override void OnBadHarvestTarget(Mobile from, Item tool, object toHarvest)
{
if (toHarvest is LandTarget)
from.SendLocalizedMessage(501862); // You can't mine there.
else
from.SendLocalizedMessage(501863); // You can't mine that.
}
#region Tile lists
private static int[] m_MountainAndCaveTiles =
{
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
230, 231, 236, 237, 238, 239, 240, 241, 242, 243,
244, 245, 246, 247, 252, 253, 254, 255, 256, 257,
258, 259, 260, 261, 262, 263, 268, 269, 270, 271,
272, 273, 274, 275, 276, 277, 278, 279, 286, 287,
288, 289, 290, 291, 292, 293, 294, 296, 296, 297,
321, 322, 323, 324, 467, 468, 469, 470, 471, 472,
473, 474, 476, 477, 478, 479, 480, 481, 482, 483,
484, 485, 486, 487, 492, 493, 494, 495, 543, 544,
545, 546, 547, 548, 549, 550, 551, 552, 553, 554,
555, 556, 557, 558, 559, 560, 561, 562, 563, 564,
565, 566, 567, 568, 569, 570, 571, 572, 573, 574,
575, 576, 577, 578, 579, 581, 582, 583, 584, 585,
586, 587, 588, 589, 590, 591, 592, 593, 594, 595,
596, 597, 598, 599, 600, 601, 610, 611, 612, 613,
1010, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749,
1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1771, 1772,
1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782,
1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1801, 1802,
1803, 1804, 1805, 1806, 1807, 1808, 1809, 1811, 1812, 1813,
1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823,
1824, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839,
1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849,
1850, 1851, 1852, 1853, 1854, 1861, 1862, 1863, 1864, 1865,
1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875,
1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1981,
1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991,
1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2002, 2003, 2004, 2028, 2029, 2030, 2031, 2032, 2033, 2100,
2101, 2102, 2103, 2104, 2105,
0x453B, 0x453C, 0x453D, 0x453E, 0x453F, 0x4540, 0x4541,
0x4542, 0x4543, 0x4544, 0x4545, 0x4546, 0x4547, 0x4548,
0x4549, 0x454A, 0x454B, 0x454C, 0x454D, 0x454E, 0x454F
};
private static int[] m_SandTiles =
{
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 68, 69, 70, 71, 72, 73, 74, 75,
286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
296, 297, 298, 299, 300, 301, 402, 424, 425, 426,
427, 441, 442, 443, 444, 445, 446, 447, 448, 449,
450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 642, 643, 644, 645,
650, 651, 652, 653, 654, 655, 656, 657, 821, 822,
823, 824, 825, 826, 827, 828, 833, 834, 835, 836,
845, 846, 847, 848, 849, 850, 851, 852, 857, 858,
859, 860, 951, 952, 953, 954, 955, 956, 957, 958,
967, 968, 969, 970,
1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455,
1456, 1457, 1458, 1611, 1612, 1613, 1614, 1615, 1616,
1617, 1618, 1623, 1624, 1625, 1626, 1635, 1636, 1637,
1638, 1639, 1640, 1641, 1642, 1647, 1648, 1649, 1650
};
#endregion
}
}