Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View File

@@ -0,0 +1,70 @@
using System;
namespace Server.Engines.Harvest
{
public class BonusHarvestResource
{
private readonly TextDefinition m_SuccessMessage;
private Type m_Type;
private double m_ReqSkill, m_Chance;
public BonusHarvestResource(double reqSkill, double chance, TextDefinition message, Type type)
: this(reqSkill, chance, message, type, null)
{ }
public BonusHarvestResource(double reqSkill, double chance, TextDefinition message, Type type, Map requiredMap)
{
this.m_ReqSkill = reqSkill;
this.m_Chance = chance;
this.m_Type = type;
this.m_SuccessMessage = message;
RequiredMap = requiredMap;
}
public Map RequiredMap { get; private set; }
public Type Type
{
get
{
return this.m_Type;
}
set
{
this.m_Type = value;
}
}
public double ReqSkill
{
get
{
return this.m_ReqSkill;
}
set
{
this.m_ReqSkill = value;
}
}
public double Chance
{
get
{
return this.m_Chance;
}
set
{
this.m_Chance = value;
}
}
public TextDefinition SuccessMessage
{
get
{
return this.m_SuccessMessage;
}
}
public void SendSuccessTo(Mobile m)
{
TextDefinition.SendMessageTo(m, this.m_SuccessMessage);
}
}
}

View File

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

View File

@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
namespace Server.Engines.Harvest
{
public class HarvestDefinition
{
public HarvestDefinition()
{
Banks = new Dictionary<Map, Dictionary<Point2D, HarvestBank>>();
}
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 int[] SpecialTiles { 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; set; }
public void SendMessageTo(Mobile from, object message)
{
if (message is int)
from.SendLocalizedMessage((int)message);
else if (message is string)
from.SendMessage((string)message);
}
public HarvestBank GetBank(Map map, int x, int y)
{
if (map == null || map == Map.Internal)
return null;
x /= BankWidth;
y /= BankHeight;
Banks.TryGetValue(map, out Dictionary<Point2D, HarvestBank> banks);
if (banks == null)
Banks[map] = banks = new Dictionary<Point2D, HarvestBank>();
Point2D key = new Point2D(x, y);
banks.TryGetValue(key, out HarvestBank bank);
if (bank == null)
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;
}
else
{
int dist = -1;
for (int i = 0; dist < 0 && i < Tiles.Length; ++i)
dist = Tiles[i] - tileID;
return dist == 0;
}
}
#region High Seas
public bool ValidateSpecial(int tileID)
{
//No Special tiles were initiated so always true
if (SpecialTiles == null || SpecialTiles.Length == 0)
return true;
for (int i = 0; i < SpecialTiles.Length; i++)
{
if (tileID == SpecialTiles[i])
return true;
}
return false;
}
#endregion
}
}

View File

@@ -0,0 +1,30 @@
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)
m.SendLocalizedMessage((int)SuccessMessage);
else if (SuccessMessage is string)
m.SendMessage((string)SuccessMessage);
}
}
}

View File

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

View File

@@ -0,0 +1,130 @@
using System;
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 readonly Item m_Tool;
private readonly HarvestSystem m_System;
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)
{
if (targeted is StaticTarget)
{
int itemID = ((StaticTarget)targeted).ItemID;
// grave
if (itemID == 0xED3 || itemID == 0xEDF || itemID == 0xEE0 || itemID == 0xEE1 || itemID == 0xEE2 || itemID == 0xEE8)
{
PlayerMobile player = from as PlayerMobile;
if (player != null)
{
QuestSystem qs = player.Quest;
if (qs is WitchApprenticeQuest)
{
FindIngredientObjective obj = qs.FindObjective(typeof(FindIngredientObjective)) as FindIngredientObjective;
if (obj != null && !obj.Completed && 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;
}
}
}
}
}
else if (targeted is LandTarget && ((LandTarget)targeted).TileID >= 113 && ((LandTarget)targeted).TileID <= 120)
{
if (Server.Engines.Quests.TheGreatVolcanoQuest.OnHarvest(from, m_Tool))
return;
}
}
if (m_System is Lumberjacking && targeted is IChopable)
((IChopable)targeted).OnChop(from);
else if (m_System is Lumberjacking && targeted is IAxe && m_Tool is BaseAxe)
{
IAxe obj = (IAxe)targeted;
Item item = (Item)targeted;
if (!item.IsChildOf(from.Backpack))
from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used.
else if (obj.Axe(from, (BaseAxe)m_Tool))
from.PlaySound(0x13E);
}
else if (m_System is Lumberjacking && targeted is ICarvable)
((ICarvable)targeted).Carve(from, (Item)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)
((TreasureMap)targeted).OnBeginDig(from);
#region High Seas
else if (m_System is Mining && targeted is NiterDeposit)
((NiterDeposit)targeted).OnMine(from, m_Tool);
else if (m_System is Lumberjacking && targeted is CrackedLavaRockEast)
((CrackedLavaRockEast)targeted).OnCrack(from);
else if (m_System is Lumberjacking && targeted is CrackedLavaRockSouth)
((CrackedLavaRockSouth)targeted).OnCrack(from);
#endregion
else
{
// If we got here and we're lumberjacking then we didn't target something that can be done from the pack
if (m_System is Lumberjacking && m_Tool.Parent != from)
{
from.SendLocalizedMessage(500487); // The axe must be equipped for any serious wood chopping.
return;
}
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;
}
else 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)
{
if (item is TrapableContainer)
(item as TrapableContainer).ExecuteTrap(from);
((Container)item).Destroy();
}
else
{
item.Delete();
}
}
}
}

View File

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

View File

@@ -0,0 +1,20 @@
using System;
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; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,784 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Targeting;
using Server.Engines.Quests;
using Server.Engines.Quests.Hag;
using Server.Mobiles;
using System.Linq;
namespace Server.Engines.Harvest
{
public abstract class HarvestSystem
{
public static void Configure()
{
EventSink.TargetByResourceMacro += TargetByResource;
}
public HarvestSystem()
{
Definitions = new List<HarvestDefinition>();
}
public List<HarvestDefinition> Definitions { get; }
public virtual bool CheckTool(Mobile from, Item tool)
{
bool wornOut = tool == null || tool.Deleted || (tool is IUsesRemaining && ((IUsesRemaining)tool).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 != null && 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;
EventSink.InvokeResourceHarvestAttempt(new ResourceHarvestAttemptEventArgs(from, tool, this));
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;
}
else if (!def.Validate(tileID) && !def.ValidateSpecial(tileID))
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
if (!CheckRange(from, tool, def, map, loc, true))
return;
else if (!CheckResources(from, tool, def, map, loc, true))
return;
else 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;
Type type = null;
if(CheckHarvestSkill(map, loc, from, resource, def))
{
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, tool);
if (item == null)
{
type = null;
}
else
{
int amount = def.ConsumedPerHarvest;
int feluccaAmount = def.ConsumedPerFeluccaHarvest;
if (item is BaseGranite)
feluccaAmount = 3;
Caddellite.OnHarvest(from, tool, this, item);
//The whole harvest system is kludgy and I'm sure this is just adding to it.
if (item.Stackable)
{
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 && !Siege.SiegeShard;
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;
// Void Pool Rewards
item.Amount += WoodsmansTalisman.CheckHarvest(from, type, this);
}
if (from.AccessLevel == AccessLevel.Player)
{
bank.Consume(amount, from);
}
if (Give(from, item, def.PlaceAtFeetIfFull))
{
SendSuccessTo(from, item, resource);
}
else
{
SendPackFullTo(from, item, def, resource);
item.Delete();
}
BonusHarvestResource bonus = def.GetBonusResource();
Item bonusItem = null;
if (bonus != null && bonus.Type != null && skillBase >= bonus.ReqSkill)
{
if (bonus.RequiredMap == null || bonus.RequiredMap == from.Map)
{
bonusItem = Construct(bonus.Type, from, tool);
Caddellite.OnHarvest(from, tool, this, bonusItem);
if (Give(from, bonusItem, true)) //Bonuses always allow placing at feet, even if pack is full irregrdless of def
{
bonus.SendSuccessTo(from);
}
else
{
bonusItem.Delete();
}
}
}
EventSink.InvokeResourceHarvestSuccess(new ResourceHarvestSuccessEventArgs(from, tool, item, bonusItem, this));
}
#region High Seas
OnToolUsed(from, tool, item != null);
#endregion
}
// Siege rules will take into account axes and polearms used for lumberjacking
if (tool is IUsesRemaining && (tool is BaseHarvestTool || tool is Pickaxe || tool is SturdyPickaxe || tool is GargoylesPickaxe || Siege.SiegeShard))
{
IUsesRemaining toolWithUses = (IUsesRemaining)tool;
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 bool CheckHarvestSkill(Map map, Point3D loc, Mobile from, HarvestResource resource, HarvestDefinition def)
{
return from.Skills[def.Skill].Value >= resource.ReqSkill && from.CheckSkill(def.Skill, resource.MinSkill, resource.MaxSkill);
}
public virtual void OnToolUsed(Mobile from, Item tool, bool caughtSomething)
{
}
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, Item tool)
{
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 || map == Map.Internal)
return false;
List<Item> atFeet = new List<Item>();
IPooledEnumerable eable = m.GetItemsInRange(0);
foreach (Item obj in eable)
atFeet.Add(obj);
eable.Free();
for (int i = 0; i < atFeet.Count; ++i)
{
Item check = atFeet[i];
if (check.StackWith(m, item, false))
return true;
}
ColUtility.Free(atFeet);
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)
{
if (resource.Types.Length > 0)
return resource.Types[Utility.Random(resource.Types.Length)];
return 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;
}
else if (!def.Validate(tileID) && !def.ValidateSpecial(tileID))
{
from.EndAction(locked);
OnBadHarvestTarget(from, tool, toHarvest);
return false;
}
else if (!CheckRange(from, tool, def, map, loc, true))
{
from.EndAction(locked);
return false;
}
else if (!CheckResources(from, tool, def, map, loc, true))
{
from.EndAction(locked);
return false;
}
else 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)
{
if (Core.SA)
{
from.Animate(AnimationType.Attack, Utility.RandomList(def.EffectActions));
}
else
{
from.Animate(Utility.RandomList(def.EffectActions), 5, 1, true, false, 0);
}
}
}
public virtual HarvestDefinition GetDefinition(int tileID)
{
return GetDefinition(tileID, null);
}
public virtual HarvestDefinition GetDefinition(int tileID, Item tool)
{
HarvestDefinition def = null;
for (int i = 0; def == null && i < Definitions.Count; ++i)
{
HarvestDefinition check = Definitions[i];
if (check.Validate(tileID))
def = check;
}
return def;
}
#region High Seas
public virtual HarvestDefinition GetDefinitionFromSpecialTile(int tileID)
{
HarvestDefinition def = null;
for (int i = 0; def == null && i < Definitions.Count; ++i)
{
HarvestDefinition check = Definitions[i];
if (check.ValidateSpecial(tileID))
def = check;
}
return def;
}
#endregion
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, tool);
if (def == null)
{
OnBadHarvestTarget(from, tool, toHarvest);
return;
}
if (!CheckRange(from, tool, def, map, loc, false))
return;
else if (!CheckResources(from, tool, def, map, loc, false))
return;
else 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 && !((Static)toHarvest).Movable)
{
Static obj = (Static)toHarvest;
tileID = (obj.ItemID & 0x3FFF) | 0x4000;
map = obj.Map;
loc = obj.GetWorldLocation();
}
else if (toHarvest is StaticTarget)
{
StaticTarget obj = (StaticTarget)toHarvest;
tileID = (obj.ItemID & 0x3FFF) | 0x4000;
map = from.Map;
loc = obj.Location;
}
else if (toHarvest is LandTarget obj)
{
tileID = obj.TileID;
map = from.Map;
loc = obj.Location;
}
else
{
tileID = 0;
map = null;
loc = Point3D.Zero;
return false;
}
return map != null && map != Map.Internal;
}
#region Enhanced Client
public static void TargetByResource(TargetByResourceMacroEventArgs e)
{
Mobile m = e.Mobile;
Item tool = e.Tool;
HarvestSystem system = null;
HarvestDefinition def = null;
if (tool is IHarvestTool)
{
system = ((IHarvestTool)tool).HarvestSystem;
}
if (system != null)
{
switch (e.ResourceType)
{
case 0: // ore
if (system is Mining)
def = ((Mining)system).OreAndStone;
break;
case 1: // sand
if (system is Mining)
def = ((Mining)system).Sand;
break;
case 2: // wood
if (system is Lumberjacking)
def = ((Lumberjacking)system).Definition;
break;
case 3: // grave
if (TryHarvestGrave(m))
return;
break;
case 4: // red shrooms
if (TryHarvestShrooms(m))
return;
break;
}
if (def != null && FindValidTile(m, def, out object toHarvest))
{
system.StartHarvesting(m, tool, toHarvest);
return;
}
system.OnBadHarvestTarget(m, tool, new LandTarget(new Point3D(0, 0, 0), Map.Felucca));
}
}
private static bool FindValidTile(Mobile m, HarvestDefinition definition, out object toHarvest)
{
Map map = m.Map;
toHarvest = null;
if (m == null || map == null || map == Map.Internal)
return false;
for (int x = m.X - 1; x <= m.X + 1; x++)
{
for (int y = m.Y - 1; y <= m.Y + 1; y++)
{
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y, false);
if (tiles.Length > 0)
{
foreach (var tile in tiles)
{
int id = (tile.ID & 0x3FFF) | 0x4000;
if (definition.Validate(id))
{
toHarvest = new StaticTarget(new Point3D(x, y, tile.Z), tile.ID);
return true;
}
}
}
LandTile lt = map.Tiles.GetLandTile(x, y);
if (definition.Validate(lt.ID))
{
toHarvest = new LandTarget(new Point3D(x, y, lt.Z), map);
return true;
}
}
}
return false;
}
public static bool TryHarvestGrave(Mobile m)
{
Map map = m.Map;
if (map == null)
return false;
for (int x = m.X - 1; x <= m.X + 1; x++)
{
for (int y = m.Y - 1; y <= m.Y + 1; y++)
{
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y, false);
foreach (var tile in tiles)
{
int itemID = tile.ID;
if (itemID == 0xED3 || itemID == 0xEDF || itemID == 0xEE0 || itemID == 0xEE1 || itemID == 0xEE2 || itemID == 0xEE8)
{
if (m is PlayerMobile player)
{
QuestSystem qs = player.Quest;
if (qs is WitchApprenticeQuest)
{
if (qs.FindObjective(typeof(FindIngredientObjective)) is FindIngredientObjective obj && !obj.Completed && 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 true;
}
}
}
}
}
}
}
return false;
}
public static bool TryHarvestShrooms(Mobile m)
{
Map map = m.Map;
if (map == null)
return false;
for (int x = m.X - 1; x <= m.X + 1; x++)
{
for (int y = m.Y - 1; y <= m.Y + 1; y++)
{
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y, false);
foreach (var tile in tiles)
{
int itemID = tile.ID;
if (itemID == 0xD15 || itemID == 0xD16)
{
if (m is PlayerMobile player)
{
QuestSystem qs = player.Quest;
if (qs is WitchApprenticeQuest)
{
if (qs.FindObjective(typeof(FindIngredientObjective)) is FindIngredientObjective obj && !obj.Completed && obj.Ingredient == Ingredient.RedMushrooms)
{
player.SendLocalizedMessage(1055036); // You slice a red cap mushroom from its stem.
obj.Complete();
return true;
}
}
}
}
}
}
}
return false;
}
#endregion
}
}
namespace Server
{
public interface IChopable
{
void OnChop(Mobile from);
}
public interface IHarvestTool : IEntity
{
Engines.Harvest.HarvestSystem HarvestSystem { get; }
}
[AttributeUsage(AttributeTargets.Class)]
public class FurnitureAttribute : Attribute
{
public FurnitureAttribute()
{
}
private static bool IsNotChoppables(Item item)
{
return _NotChoppables.Any(t => t == item.GetType());
}
private static Type[] _NotChoppables = new Type[]
{
typeof(CommodityDeedBox), typeof(ChinaCabinet), typeof(PieSafe), typeof(AcademicBookCase), typeof(JewelryBox),
typeof(WoodenBookcase), typeof(Countertop), typeof(Mailbox)
};
public static bool Check(Item item)
{
if (item == null)
{
return false;
}
if (IsNotChoppables(item))
{
return false;
}
if (item.GetType().IsDefined(typeof(FurnitureAttribute), false))
{
return true;
}
if (item is AddonComponent && ((AddonComponent)item).Addon != null && ((AddonComponent)item).Addon.GetType().IsDefined(typeof(FurnitureAttribute), false))
{
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,297 @@
using System;
using Server.Items;
using Server.Network;
using System.Linq;
namespace Server.Engines.Harvest
{
public class Lumberjacking : HarvestSystem
{
private static Lumberjacking m_System;
public static Lumberjacking System
{
get
{
if (m_System == null)
m_System = new Lumberjacking();
return m_System;
}
}
private readonly HarvestDefinition m_Definition;
public HarvestDefinition Definition
{
get
{
return this.m_Definition;
}
}
private Lumberjacking()
{
HarvestResource[] res;
HarvestVein[] veins;
#region Lumberjacking
HarvestDefinition lumber = new HarvestDefinition();
// Resource banks are every 4x3 tiles
lumber.BankWidth = 4;
lumber.BankHeight = 3;
// Every bank holds from 20 to 45 logs
lumber.MinTotal = 20;
lumber.MaxTotal = 45;
// A resource bank will respawn its content every 20 to 30 minutes
lumber.MinRespawn = TimeSpan.FromMinutes(20.0);
lumber.MaxRespawn = TimeSpan.FromMinutes(30.0);
// Skill checking is done on the Lumberjacking skill
lumber.Skill = SkillName.Lumberjacking;
// Set the list of harvestable tiles
lumber.Tiles = m_TreeTiles;
// Players must be within 2 tiles to harvest
lumber.MaxRange = 2;
// Ten logs per harvest action
lumber.ConsumedPerHarvest = 10;
lumber.ConsumedPerFeluccaHarvest = 20;
// The chopping effect
lumber.EffectActions = new int[] { Core.SA ? 7 : 13 };
lumber.EffectSounds = new int[] { 0x13E };
lumber.EffectCounts = (Core.AOS ? new int[] { 1 } : new int[] { 1, 2, 2, 2, 3 });
lumber.EffectDelay = TimeSpan.FromSeconds(1.6);
lumber.EffectSoundDelay = TimeSpan.FromSeconds(0.9);
lumber.NoResourcesMessage = 500493; // There's not enough wood here to harvest.
lumber.FailMessage = 500495; // You hack at the tree for a while, but fail to produce any useable wood.
lumber.OutOfRangeMessage = 500446; // That is too far away.
lumber.PackFullMessage = 500497; // You can't place any wood into your backpack!
lumber.ToolBrokeMessage = 500499; // You broke your axe.
if (Core.ML)
{
res = new HarvestResource[]
{
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 HarvestVein[]
{
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 BonusHarvestResource[]
{
new BonusHarvestResource(0, 82.0, 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, 01.0, 1072551, typeof(BrilliantAmber)),
new BonusHarvestResource(100, 01.0, 1113756, typeof(CrystalShards), Map.TerMur),
};
}
else
{
res = new HarvestResource[]
{
new HarvestResource(00.0, 00.0, 100.0, 500498, typeof(Log))
};
veins = new HarvestVein[]
{
new HarvestVein(100.0, 0.0, res[0], null)
};
}
lumber.Resources = res;
lumber.Veins = veins;
lumber.RaceBonus = Core.ML;
lumber.RandomizeVeins = Core.ML;
this.m_Definition = lumber;
this.Definitions.Add(lumber);
#endregion
}
public override Type MutateType(Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource)
{
var newType = type;
if (tool is HarvestersAxe && ((HarvestersAxe)tool).Charges > 0)
{
if (type == typeof(Log))
newType = typeof(Board);
else if (type == typeof(OakLog))
newType = typeof(OakBoard);
else if (type == typeof(AshLog))
newType = typeof(AshBoard);
else if (type == typeof(YewLog))
newType = typeof(YewBoard);
else if (type == typeof(HeartwoodLog))
newType = typeof(HeartwoodBoard);
else if (type == typeof(BloodwoodLog))
newType = typeof(BloodwoodBoard);
else if (type == typeof(FrostwoodLog))
newType = typeof(FrostwoodBoard);
if (newType != type)
{
((HarvestersAxe)tool).Charges--;
}
}
return newType;
}
public override void SendSuccessTo(Mobile from, Item item, HarvestResource resource)
{
if (item != null)
{
if (item != null && item.GetType().IsSubclassOf(typeof(BaseWoodBoard)))
{
from.SendLocalizedMessage(1158776); // The axe magically creates boards from your logs.
return;
}
else
{
foreach (var res in m_Definition.Resources.Where(r => r.Types != null))
{
foreach (var type in res.Types)
{
if (item.GetType() == type)
{
res.SendSuccessTo(from);
return;
}
}
}
}
}
base.SendSuccessTo(from, item, resource);
}
public override bool CheckHarvest(Mobile from, Item tool)
{
if (!base.CheckHarvest(from, tool))
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.Backpack != null && !tool.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1080058); // This must be in your backpack to use it.
return false;
}
return true;
}
public override Type GetResourceType(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource)
{
#region Void Pool Items
HarvestMap hmap = HarvestMap.CheckMapOnHarvest(from, loc, def);
if (hmap != null && hmap.Resource >= CraftResource.RegularWood && hmap.Resource <= CraftResource.Frostwood)
{
hmap.UsesRemaining--;
hmap.InvalidateProperties();
CraftResourceInfo info = CraftResources.GetInfo(hmap.Resource);
if (info != null)
return info.ResourceTypes[0];
}
#endregion
return base.GetResourceType(from, tool, def, map, loc, resource);
}
public override bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
{
if (HarvestMap.CheckMapOnHarvest(from, loc, def) == null)
return base.CheckResources(from, tool, def, map, loc, timed);
return true;
}
public override void OnBadHarvestTarget(Mobile from, Item tool, object toHarvest)
{
if (toHarvest is Mobile)
((Mobile)toHarvest).PrivateOverheadMessage(MessageType.Regular, 0x3B2, 500450, from.NetState); // You can only skin dead creatures.
else if (toHarvest is Item)
((Item)toHarvest).LabelTo(from, 500464); // Use this on corpses to carve away meat and hide
else if (toHarvest is Targeting.StaticTarget || toHarvest is Targeting.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);
}
#region Tile lists
private static readonly int[] m_TreeTiles = new int[]
{
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
}
}

View File

@@ -0,0 +1,566 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
using System.Linq;
namespace Server.Engines.Harvest
{
public class Mining : HarvestSystem
{
private static Mining m_System;
public static Mining System
{
get
{
if (m_System == null)
m_System = new Mining();
return m_System;
}
}
public HarvestDefinition OreAndStone { get; }
public HarvestDefinition Sand { get; }
private Mining()
{
HarvestResource[] res;
HarvestVein[] veins;
#region Mining for ore and stone
HarvestDefinition oreAndStone = OreAndStone = new HarvestDefinition();
// Resource banks are every 8x8 tiles
oreAndStone.BankWidth = 8;
oreAndStone.BankHeight = 8;
// Every bank holds from 10 to 34 ore
oreAndStone.MinTotal = 10;
oreAndStone.MaxTotal = 34;
// A resource bank will respawn its content every 10 to 20 minutes
oreAndStone.MinRespawn = TimeSpan.FromMinutes(10.0);
oreAndStone.MaxRespawn = TimeSpan.FromMinutes(20.0);
// Skill checking is done on the Mining skill
oreAndStone.Skill = SkillName.Mining;
// Set the list of harvestable tiles
oreAndStone.Tiles = m_MountainAndCaveTiles;
// Players must be within 2 tiles to harvest
oreAndStone.MaxRange = 2;
// One ore per harvest action
oreAndStone.ConsumedPerHarvest = 1;
oreAndStone.ConsumedPerFeluccaHarvest = 2;
// The digging effect
oreAndStone.EffectActions = new int[] { Core.SA ? 3 : 11 };
oreAndStone.EffectSounds = new int[] { 0x125, 0x126 };
oreAndStone.EffectCounts = new int[] { 1 };
oreAndStone.EffectDelay = TimeSpan.FromSeconds(1.6);
oreAndStone.EffectSoundDelay = TimeSpan.FromSeconds(0.9);
oreAndStone.NoResourcesMessage = 503040; // There is no metal here to mine.
oreAndStone.DoubleHarvestMessage = 503042; // Someone has gotten to the metal before you.
oreAndStone.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining.
oreAndStone.OutOfRangeMessage = 500446; // That is too far away.
oreAndStone.FailMessage = 503043; // You loosen some rocks but fail to find any useable ore.
oreAndStone.PackFullMessage = 1010481; // Your backpack is full, so the ore you mined is lost.
oreAndStone.ToolBrokeMessage = 1044038; // You have worn out your tool!
res = new HarvestResource[]
{
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))
};
veins = new HarvestVein[]
{
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 BonusHarvestResource[]
{
new BonusHarvestResource(0, 99.2, 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)),
new BonusHarvestResource(100, .1, 1077180, typeof(SmallPieceofBlackrock)),
new BonusHarvestResource(100, .1, 1113344, typeof(CrystallineBlackrock), Map.TerMur)
};
}
oreAndStone.RaceBonus = Core.ML;
oreAndStone.RandomizeVeins = Core.ML;
Definitions.Add(oreAndStone);
#endregion
#region Mining for sand
HarvestDefinition sand = Sand = new HarvestDefinition();
// Resource banks are every 8x8 tiles
sand.BankWidth = 8;
sand.BankHeight = 8;
// Every bank holds from 6 to 12 sand
sand.MinTotal = 6;
sand.MaxTotal = 13;
// A resource bank will respawn its content every 10 to 20 minutes
sand.MinRespawn = TimeSpan.FromMinutes(10.0);
sand.MaxRespawn = TimeSpan.FromMinutes(20.0);
// Skill checking is done on the Mining skill
sand.Skill = SkillName.Mining;
// Set the list of harvestable tiles
sand.Tiles = m_SandTiles;
// Players must be within 2 tiles to harvest
sand.MaxRange = 2;
// One sand per harvest action
sand.ConsumedPerHarvest = 1;
sand.ConsumedPerFeluccaHarvest = 2;
// The digging effect
sand.EffectActions = new int[] { Core.SA ? 3 : 11 };
sand.EffectSounds = new int[] { 0x125, 0x126 };
sand.EffectCounts = new int[] { 6 };
sand.EffectDelay = TimeSpan.FromSeconds(1.6);
sand.EffectSoundDelay = TimeSpan.FromSeconds(0.9);
sand.NoResourcesMessage = 1044629; // There is no sand here to mine.
sand.DoubleHarvestMessage = 1044629; // There is no sand here to mine.
sand.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining.
sand.OutOfRangeMessage = 500446; // That is too far away.
sand.FailMessage = 1044630; // You dig for a while but fail to find any of sufficient quality for glassblowing.
sand.PackFullMessage = 1044632; // Your backpack can't hold the sand, and it is lost!
sand.ToolBrokeMessage = 1044038; // You have worn out your tool!
res = new HarvestResource[]
{
new HarvestResource(100.0, 70.0, 100.0, 1044631, typeof(Sand))
};
veins = new HarvestVein[]
{
new HarvestVein(100.0, 0.0, res[0], null)
};
sand.Resources = res;
sand.Veins = veins;
Definitions.Add(sand);
#endregion
}
public override Type GetResourceType(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource)
{
if (def == OreAndStone)
{
#region Void Pool Items
HarvestMap hmap = HarvestMap.CheckMapOnHarvest(from, loc, def);
if (hmap != null && hmap.Resource >= CraftResource.Iron && hmap.Resource <= CraftResource.Valorite)
{
hmap.UsesRemaining--;
hmap.InvalidateProperties();
CraftResourceInfo info = CraftResources.GetInfo(hmap.Resource);
if (info != null)
return info.ResourceTypes[1];
}
#endregion
PlayerMobile pm = from as PlayerMobile;
if (tool is ImprovedRockHammer)
{
if (from.Skills[SkillName.Mining].Base >= 100.0)
return resource.Types[1];
else
return null;
}
if (pm != null && pm.GemMining && pm.ToggleMiningGem && from.Skills[SkillName.Mining].Base >= 100.0 && 0.1 > Utility.RandomDouble())
return Loot.GemTypes[Utility.Random(Loot.GemTypes.Length)];
double chance = tool is RockHammer ? 0.50 : 0.15;
if (pm != null && pm.StoneMining && (pm.ToggleMiningStone || pm.ToggleStoneOnly) && from.Skills[SkillName.Mining].Base >= 100.0 && chance > Utility.RandomDouble())
return resource.Types[1];
if (pm != null && pm.ToggleStoneOnly)
{
return null;
}
return resource.Types[0];
}
return base.GetResourceType(from, tool, def, map, loc, resource);
}
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 if (item is IGem)
from.SendLocalizedMessage(1112233); // You carefully extract a glistening gem from the vein!
else if (item != null)
{
foreach (var res in OreAndStone.Resources.Where(r => r.Types != null))
{
foreach (var type in res.Types)
{
if (item.GetType() == type)
{
res.SendSuccessTo(from);
return;
}
}
}
base.SendSuccessTo(from, item, resource);
}
}
public override bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed)
{
if (HarvestMap.CheckMapOnHarvest(from, loc, def) == null)
return base.CheckResources(from, tool, def, map, loc, timed);
return true;
}
public override bool CheckHarvest(Mobile from, Item tool)
{
if (!base.CheckHarvest(from, tool))
return false;
if (from.IsBodyMod && !from.Body.IsHuman)
{
from.SendLocalizedMessage(501865); // You can't mine while polymorphed.
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 (def == Sand && !(from is PlayerMobile && from.Skills[SkillName.Mining].Base >= 100.0 && ((PlayerMobile)from).SandMining))
{
OnBadHarvestTarget(from, tool, toHarvest);
return false;
}
else if (from.Mounted)
{
from.SendLocalizedMessage(501864); // You can't mine while riding.
return false;
}
else 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);
}
private static readonly int[] m_Offsets = new int[]
{
-1, -1,
-1, 0,
-1, 1,
0, -1,
0, 1,
1, -1,
1, 0,
1, 1
};
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() && HarvestMap.CheckMapOnHarvest(from, harvested, def) == null)
{
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], new object[] { 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;
}
else
{
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
{
}
}
}
}
#region High Seas
public override bool SpecialHarvest(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc)
{
if (!Core.HS)
return base.SpecialHarvest(from, tool, def, map, loc);
HarvestBank bank = def.GetBank(map, loc.X, loc.Y);
if (bank == null)
return false;
bool boat = Multis.BaseBoat.FindBoatAt(from, from.Map) != null;
bool dungeon = IsDungeonRegion(from);
if (!boat && !dungeon)
return false;
if (boat || !NiterDeposit.HasBeenChecked(bank))
{
int luck = from is PlayerMobile ? ((PlayerMobile)from).RealLuck : from.Luck;
double bonus = (from.Skills[SkillName.Mining].Value / 9999) + ((double)luck / 150000);
if (boat)
bonus -= bonus * .33;
if (dungeon)
NiterDeposit.AddBank(bank);
if (Utility.RandomDouble() < bonus)
{
int size = Utility.RandomMinMax(1, 5);
if (luck / 2500.0 > Utility.RandomDouble())
size++;
NiterDeposit niter = new NiterDeposit(size);
if (!dungeon)
{
niter.MoveToWorld(new Point3D(loc.X, loc.Y, from.Z + 3), from.Map);
from.SendLocalizedMessage(1149918, niter.Size.ToString()); //You have uncovered a ~1_SIZE~ deposit of niter! Mine it to obtain saltpeter.
NiterDeposit.AddBank(bank);
return true;
}
else
{
for (int i = 0; i < 50; i++)
{
int x = Utility.RandomMinMax(loc.X - 2, loc.X + 2);
int y = Utility.RandomMinMax(loc.Y - 2, loc.Y + 2);
int z = from.Z;
if (from.Map.CanSpawnMobile(x, y, z))
{
niter.MoveToWorld(new Point3D(x, y, z), from.Map);
from.SendLocalizedMessage(1149918, niter.Size.ToString()); //You have uncovered a ~1_SIZE~ deposit of niter! Mine it to obtain saltpeter.
return true;
}
}
}
niter.Delete();
}
}
return false;
}
private bool IsDungeonRegion(Mobile from)
{
if (from == null)
return false;
Map map = from.Map;
Region reg = from.Region;
Rectangle2D bounds = new Rectangle2D(0, 0, 5114, 4100);
if ((map == Map.Felucca || map == Map.Trammel) && bounds.Contains(new Point2D(from.X, from.Y)))
return false;
return reg != null && (reg.IsPartOf<Regions.DungeonRegion>() || map == Map.Ilshenar);
}
#endregion
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 if (!(toHarvest is LandTarget))
{
from.SendLocalizedMessage(501863); // You can't mine that.
}
else if (from.Mounted || from.Flying)
{
from.SendLocalizedMessage(501864); // You can't dig while riding or flying.
}
}
#region Tile lists
private static readonly int[] m_MountainAndCaveTiles = new int[]
{
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 readonly int[] m_SandTiles = new int[]
{
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
}
}