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,61 @@
using Server.Engines.Craft;
namespace Server.Items
{
[Flippable(0xFAF, 0xFB0)]
[Anvil]
public class Anvil : Item
{
[Constructible]
public Anvil() : base(0xFAF)
{
Movable = false;
}
public Anvil(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
[Forge]
public class Forge : Item
{
[Constructible]
public Forge() : base(0xFB1)
{
Movable = false;
}
public Forge(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,348 @@
using Server.Engines.Craft;
namespace Server.Items
{
[Forge]
public class LargeForgeWest : Item
{
private InternalItem m_Item;
private InternalItem2 m_Item2;
[Constructible]
public LargeForgeWest() : base(0x199A)
{
Movable = false;
m_Item = new InternalItem(this);
m_Item2 = new InternalItem2(this);
}
public LargeForgeWest(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X, Y + 1, Z);
if (m_Item2 != null)
m_Item2.Location = new Point3D(X, Y + 2, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
if (m_Item2 != null)
m_Item2.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
m_Item2?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
writer.Write(m_Item2);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as InternalItem;
m_Item2 = reader.ReadItem() as InternalItem2;
}
[Forge]
private class InternalItem : Item
{
private LargeForgeWest m_Item;
public InternalItem(LargeForgeWest item) : base(0x1996)
{
Movable = false;
m_Item = item;
}
public InternalItem(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X, Y - 1, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as LargeForgeWest;
}
}
[Forge]
private class InternalItem2 : Item
{
private LargeForgeWest m_Item;
public InternalItem2(LargeForgeWest item) : base(0x1992)
{
Movable = false;
m_Item = item;
}
public InternalItem2(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X, Y - 2, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as LargeForgeWest;
}
}
}
[Forge]
public class LargeForgeEast : Item
{
private InternalItem m_Item;
private InternalItem2 m_Item2;
[Constructible]
public LargeForgeEast() : base(0x197A)
{
Movable = false;
m_Item = new InternalItem(this);
m_Item2 = new InternalItem2(this);
}
public LargeForgeEast(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X + 1, Y, Z);
if (m_Item2 != null)
m_Item2.Location = new Point3D(X + 2, Y, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
if (m_Item2 != null)
m_Item2.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
m_Item2?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
writer.Write(m_Item2);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as InternalItem;
m_Item2 = reader.ReadItem() as InternalItem2;
}
[Forge]
private class InternalItem : Item
{
private LargeForgeEast m_Item;
public InternalItem(LargeForgeEast item) : base(0x197E)
{
Movable = false;
m_Item = item;
}
public InternalItem(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X - 1, Y, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as LargeForgeEast;
}
}
[Forge]
private class InternalItem2 : Item
{
private LargeForgeEast m_Item;
public InternalItem2(LargeForgeEast item) : base(0x1982)
{
Movable = false;
m_Item = item;
}
public InternalItem2(Serial serial) : base(serial)
{
}
public override void OnLocationChange(Point3D oldLocation)
{
if (m_Item != null)
m_Item.Location = new Point3D(X - 2, Y, Z);
}
public override void OnMapChange()
{
if (m_Item != null)
m_Item.Map = Map;
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Item?.Delete();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Item);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Item = reader.ReadItem() as LargeForgeEast;
}
}
}
}

View file

@ -0,0 +1,130 @@
using System;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Items
{
[Flippable(0xA57, 0xA58, 0xA59)]
public class Bedroll : Item
{
[Constructible]
public Bedroll() : base(0xA57)
{
Weight = 5.0;
}
public Bedroll(Serial serial) : base(serial)
{
}
public override void OnDoubleClick(Mobile from)
{
if (Parent != null || !VerifyMove(from))
return;
if (!from.InRange(this, 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
if (ItemID == 0xA57) // rolled
{
Direction dir = PlayerMobile.GetDirection4(from.Location, Location);
if (dir == Direction.North || dir == Direction.South)
ItemID = 0xA55;
else
ItemID = 0xA56;
}
else // unrolled
{
ItemID = 0xA57;
if (!from.HasGump<LogoutGump>())
{
CampfireEntry entry = Campfire.GetEntry(from);
if (entry?.Safe == true)
from.SendGump(new LogoutGump(entry, this));
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
private class LogoutGump : Gump
{
private Bedroll m_Bedroll;
private Timer m_CloseTimer;
private CampfireEntry m_Entry;
public LogoutGump(CampfireEntry entry, Bedroll bedroll) : base(100, 0)
{
m_Entry = entry;
m_Bedroll = bedroll;
m_CloseTimer = Timer.DelayCall(TimeSpan.FromSeconds(10.0), CloseGump);
AddBackground(0, 0, 400, 350, 0xA28);
AddHtmlLocalized(100, 20, 200, 35, 1011015); // <center>Logging out via camping</center>
/* Using a bedroll in the safety of a camp will log you out of the game safely.
* If this is what you wish to do choose CONTINUE and you will be logged out.
* Otherwise, select the CANCEL button to avoid logging out at this time.
* The camp will remain secure for 10 seconds at which time this window will close
* and you not be logged out.
*/
AddHtmlLocalized(50, 55, 300, 140, 1011016, true, true);
AddButton(45, 298, 0xFA5, 0xFA7, 1);
AddHtmlLocalized(80, 300, 110, 35, 1011011); // CONTINUE
AddButton(200, 298, 0xFA5, 0xFA7, 0);
AddHtmlLocalized(235, 300, 110, 35, 1011012); // CANCEL
}
public override void OnResponse(NetState sender, RelayInfo info)
{
PlayerMobile pm = m_Entry.Player;
m_CloseTimer.Stop();
if (Campfire.GetEntry(pm) != m_Entry)
return;
if (info.ButtonID == 1 && m_Entry.Safe && m_Bedroll.Parent == null && m_Bedroll.IsAccessibleTo(pm)
&& m_Bedroll.VerifyMove(pm) && m_Bedroll.Map == pm.Map && pm.InRange(m_Bedroll, 2))
{
pm.PlaceInBackpack(m_Bedroll);
pm.BedrollLogout = true;
sender.Dispose();
}
Campfire.RemoveEntry(m_Entry);
}
private void CloseGump()
{
Campfire.RemoveEntry(m_Entry);
m_Entry.Player.CloseGump<LogoutGump>();
}
}
}
}

View file

@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Mobiles;
using Server.Network;
namespace Server.Items
{
public enum CampfireStatus
{
Burning,
Extinguishing,
Off
}
public class Campfire : Item
{
public static readonly int SecureRange = 7;
private static readonly Dictionary<Mobile, CampfireEntry> m_Table = new Dictionary<Mobile, CampfireEntry>();
private List<CampfireEntry> m_Entries;
private Timer m_Timer;
public Campfire() : base(0xDE3)
{
Movable = false;
Light = LightType.Circle300;
m_Entries = new List<CampfireEntry>();
Created = DateTime.UtcNow;
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick);
}
public Campfire(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public DateTime Created{ get; }
[CommandProperty(AccessLevel.GameMaster)]
public CampfireStatus Status
{
get
{
switch (ItemID)
{
case 0xDE3:
return CampfireStatus.Burning;
case 0xDE9:
return CampfireStatus.Extinguishing;
default:
return CampfireStatus.Off;
}
}
set
{
if (Status == value)
return;
switch (value)
{
case CampfireStatus.Burning:
ItemID = 0xDE3;
Light = LightType.Circle300;
break;
case CampfireStatus.Extinguishing:
ItemID = 0xDE9;
Light = LightType.Circle150;
break;
default:
ItemID = 0xDEA;
Light = LightType.ArchedWindowEast;
ClearEntries();
break;
}
}
}
public static CampfireEntry GetEntry(Mobile player)
{
m_Table.TryGetValue(player, out CampfireEntry value);
return value;
}
public static void RemoveEntry(CampfireEntry entry)
{
m_Table.Remove(entry.Player);
entry.Fire.m_Entries.Remove(entry);
}
private void OnTick()
{
DateTime now = DateTime.UtcNow;
TimeSpan age = now - Created;
if (age >= TimeSpan.FromSeconds(100.0))
Delete();
else if (age >= TimeSpan.FromSeconds(90.0))
Status = CampfireStatus.Off;
else if (age >= TimeSpan.FromSeconds(60.0))
Status = CampfireStatus.Extinguishing;
if (Status == CampfireStatus.Off || Deleted)
return;
foreach (CampfireEntry entry in m_Entries.ToList())
if (!entry.Valid || entry.Player.NetState == null)
RemoveEntry(entry);
else if (!entry.Safe && now - entry.Start >= TimeSpan.FromSeconds(30.0))
{
entry.Safe = true;
entry.Player.SendLocalizedMessage(500621); // The camp is now secure.
}
IPooledEnumerable<NetState> eable = GetClientsInRange(SecureRange);
foreach (NetState state in eable)
if (state.Mobile is PlayerMobile pm && GetEntry(pm) == null)
{
CampfireEntry entry = new CampfireEntry(pm, this);
m_Table[pm] = entry;
m_Entries.Add(entry);
pm.SendLocalizedMessage(500620); // You feel it would take a few moments to secure your camp.
}
eable.Free();
}
private void ClearEntries()
{
if (m_Entries == null)
return;
foreach (CampfireEntry entry in m_Entries.ToList())
RemoveEntry(entry);
}
public override void OnAfterDelete()
{
m_Timer?.Stop();
ClearEntries();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Delete();
}
}
public class CampfireEntry
{
private bool m_Safe;
public CampfireEntry(PlayerMobile player, Campfire fire)
{
Player = player;
Fire = fire;
Start = DateTime.UtcNow;
m_Safe = false;
}
public PlayerMobile Player{ get; }
public Campfire Fire{ get; }
public DateTime Start{ get; }
public bool Valid => !Fire.Deleted && Fire.Status != CampfireStatus.Off && Player.Map == Fire.Map &&
Player.InRange(Fire, Campfire.SecureRange);
public bool Safe
{
get => Valid && m_Safe;
set => m_Safe = value;
}
}
}

View file

@ -0,0 +1,112 @@
using System.Collections.Generic;
using Server.Network;
using Server.Regions;
namespace Server.Items
{
public class Kindling : Item
{
[Constructible]
public Kindling(int amount = 1) : base(0xDE1)
{
Stackable = true;
Weight = 5.0;
Amount = amount;
}
public Kindling(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (!VerifyMove(from))
return;
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
return;
}
Point3D fireLocation = GetFireLocation(from);
if (fireLocation == Point3D.Zero)
{
from.SendLocalizedMessage(501695); // There is not a spot nearby to place your campfire.
}
else if (!from.CheckSkill(SkillName.Camping, 0.0, 100.0))
{
from.SendLocalizedMessage(501696); // You fail to ignite the campfire.
}
else
{
Consume();
if (!Deleted && Parent == null)
from.PlaceInBackpack(this);
new Campfire().MoveToWorld(fireLocation, from.Map);
}
}
private Point3D GetFireLocation(Mobile from)
{
if (from.Region.IsPartOf<DungeonRegion>())
return Point3D.Zero;
if (Parent == null)
return Location;
List<Point3D> list = new List<Point3D>(4);
AddOffsetLocation(from, 0, -1, list);
AddOffsetLocation(from, -1, 0, list);
AddOffsetLocation(from, 0, 1, list);
AddOffsetLocation(from, 1, 0, list);
if (list.Count == 0)
return Point3D.Zero;
int idx = Utility.Random(list.Count);
return list[idx];
}
private void AddOffsetLocation(Mobile from, int offsetX, int offsetY, List<Point3D> list)
{
Map map = from.Map;
int x = from.X + offsetX;
int y = from.Y + offsetY;
Point3D loc = new Point3D(x, y, from.Z);
if (map.CanFit(loc, 1) && from.InLOS(loc))
{
list.Add(loc);
}
else
{
loc = new Point3D(x, y, map.GetAverageZ(x, y));
if (map.CanFit(loc, 1) && from.InLOS(loc))
list.Add(loc);
}
}
}
}

View file

@ -0,0 +1,278 @@
namespace Server.Items
{
[Flippable(0x1BD7, 0x1BDA)]
public class Board : Item, ICommodity
{
private CraftResource m_Resource;
[Constructible]
public Board(int amount = 1)
: this(CraftResource.RegularWood, amount)
{
}
public Board(Serial serial)
: base(serial)
{
}
[Constructible]
public Board(CraftResource resource, int amount = 1)
: base(0x1BD7)
{
Stackable = true;
Amount = amount;
m_Resource = resource;
Hue = CraftResources.GetHue(resource);
}
[CommandProperty(AccessLevel.GameMaster)]
public CraftResource Resource
{
get => m_Resource;
set
{
m_Resource = value;
InvalidateProperties();
}
}
int ICommodity.DescriptionNumber
{
get
{
if (m_Resource >= CraftResource.OakWood && m_Resource <= CraftResource.YewWood)
return 1075052 + ((int)m_Resource - (int)CraftResource.OakWood);
switch (m_Resource)
{
case CraftResource.Bloodwood: return 1075055;
case CraftResource.Frostwood: return 1075056;
case CraftResource.Heartwood: return 1075062; //WHY Osi. Why?
}
return LabelNumber;
}
}
bool ICommodity.IsDeedable => true;
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (!CraftResources.IsStandard(m_Resource))
{
int num = CraftResources.GetLocalizationNumber(m_Resource);
if (num > 0)
list.Add(num);
else
list.Add(CraftResources.GetName(m_Resource));
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(3);
writer.Write((int)m_Resource);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 3:
case 2:
{
m_Resource = (CraftResource)reader.ReadInt();
break;
}
}
if (version == 0 && Weight == 0.1 || version <= 2 && Weight == 2)
Weight = -1;
if (version <= 1)
m_Resource = CraftResource.RegularWood;
}
}
public class HeartwoodBoard : Board
{
[Constructible]
public HeartwoodBoard(int amount = 1)
: base(CraftResource.Heartwood, amount)
{
}
public HeartwoodBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class BloodwoodBoard : Board
{
[Constructible]
public BloodwoodBoard(int amount = 1)
: base(CraftResource.Bloodwood, amount)
{
}
public BloodwoodBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FrostwoodBoard : Board
{
[Constructible]
public FrostwoodBoard(int amount = 1)
: base(CraftResource.Frostwood, amount)
{
}
public FrostwoodBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class OakBoard : Board
{
[Constructible]
public OakBoard(int amount = 1)
: base(CraftResource.OakWood, amount)
{
}
public OakBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class AshBoard : Board
{
[Constructible]
public AshBoard(int amount = 1)
: base(CraftResource.AshWood, amount)
{
}
public AshBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class YewBoard : Board
{
[Constructible]
public YewBoard(int amount = 1)
: base(CraftResource.YewWood, amount)
{
}
public YewBoard(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,528 @@
using System;
using Server.Mobiles;
using Server.Multis;
using Server.Targeting;
namespace Server.Items
{
[Flippable(0x1EBA, 0x1EBB)]
public class TaxidermyKit : Item
{
private static TrophyInfo[] m_Table =
{
new TrophyInfo(typeof(BrownBear), 0x1E60, 1041093, 1041107),
new TrophyInfo(typeof(GreatHart), 0x1E61, 1041095, 1041109),
new TrophyInfo(typeof(BigFish), 0x1E62, 1041096, 1041110),
new TrophyInfo(typeof(Gorilla), 0x1E63, 1041091, 1041105),
new TrophyInfo(typeof(Orc), 0x1E64, 1041090, 1041104),
new TrophyInfo(typeof(PolarBear), 0x1E65, 1041094, 1041108),
new TrophyInfo(typeof(Troll), 0x1E66, 1041092, 1041106)
};
[Constructible]
public TaxidermyKit() : base(0x1EBA)
{
Weight = 1.0;
}
public TaxidermyKit(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041279; // a taxidermy kit
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else if (from.Skills.Carpentry.Base < 90.0)
{
from.SendLocalizedMessage(1042594); // You do not understand how to use this.
}
else
{
from.SendLocalizedMessage(1042595); // Target the corpse to make a trophy out of.
from.Target = new CorpseTarget(this);
}
}
public class TrophyInfo
{
public TrophyInfo(Type type, int id, int deedNum, int addonNum)
{
CreatureType = type;
NorthID = id;
DeedNumber = deedNum;
AddonNumber = addonNum;
}
public Type CreatureType{ get; }
public int NorthID{ get; }
public int DeedNumber{ get; }
public int AddonNumber{ get; }
}
private class CorpseTarget : Target
{
private TaxidermyKit m_Kit;
public CorpseTarget(TaxidermyKit kit) : base(3, false, TargetFlags.None)
{
m_Kit = kit;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Kit.Deleted)
return;
Corpse corpse = targeted as Corpse;
if (!(corpse != null || targeted is BigFish))
{
from.SendLocalizedMessage(1042600); // That is not a corpse!
}
else if (corpse?.VisitedByTaxidermist == true)
{
from.SendLocalizedMessage(1042596); // That corpse seems to have been visited by a taxidermist already.
}
else if (!m_Kit.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
else if (from.Skills.Carpentry.Base < 90.0)
{
from.SendLocalizedMessage(1042603); // You would not understand how to use the kit.
}
else
{
object obj = corpse?.Owner ?? targeted;
foreach (TrophyInfo t in m_Table)
{
if (t.CreatureType != obj.GetType())
continue;
Container pack = from.Backpack;
if (pack?.ConsumeTotal(typeof(Board), 10) == true)
{
from.SendLocalizedMessage(
1042278); // You review the corpse and find it worthy of a trophy.
from.SendLocalizedMessage(1042602); // You use your kit up making the trophy.
Mobile hunter = null;
int weight = 0;
if (targeted is BigFish fish)
{
hunter = fish.Fisher;
weight = (int)fish.Weight;
fish.Consume();
}
from.AddToBackpack(new TrophyDeed(t, hunter, weight));
if (corpse != null)
corpse.VisitedByTaxidermist = true;
m_Kit.Delete();
return;
}
from.SendLocalizedMessage(1042598); // You do not have enough boards.
return;
}
from.SendLocalizedMessage(1042599); // That does not look like something you want hanging on a wall.
}
}
}
}
public class TrophyAddon : Item, IAddon
{
private int m_AddonNumber;
private int m_AnimalWeight;
private Mobile m_Hunter;
public TrophyAddon(Mobile from, int itemID, int westID, int northID, int deedNumber,
int addonNumber, Mobile hunter = null, int animalWeight = 0) : base(itemID)
{
WestID = westID;
NorthID = northID;
DeedNumber = deedNumber;
m_AddonNumber = addonNumber;
m_Hunter = hunter;
m_AnimalWeight = animalWeight;
Movable = false;
MoveToWorld(from.Location, from.Map);
}
public TrophyAddon(Serial serial) : base(serial)
{
}
public override bool ForceShowProperties => ObjectPropertyList.Enabled;
[CommandProperty(AccessLevel.GameMaster)]
public int WestID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int NorthID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int DeedNumber{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int AddonNumber
{
get => m_AddonNumber;
set
{
m_AddonNumber = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Hunter
{
get => m_Hunter;
set
{
m_Hunter = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int AnimalWeight
{
get => m_AnimalWeight;
set
{
m_AnimalWeight = value;
InvalidateProperties();
}
}
public override int LabelNumber => m_AddonNumber;
public bool CouldFit(IPoint3D p, Map map)
{
if (!map.CanFit(p.X, p.Y, p.Z, ItemData.Height))
return false;
if (ItemID == NorthID)
return BaseAddon.IsWall(p.X, p.Y - 1, p.Z, map); // North wall
return BaseAddon.IsWall(p.X - 1, p.Y, p.Z, map); // West wall
}
public Item Deed => new TrophyDeed(WestID, NorthID, DeedNumber, m_AddonNumber, m_Hunter, m_AnimalWeight);
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_AnimalWeight >= 20)
{
if (m_Hunter != null)
list.Add(1070857, m_Hunter.Name); // Caught by ~1_fisherman~
list.Add(1070858, m_AnimalWeight.ToString()); // ~1_weight~ stones
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_Hunter);
writer.Write(m_AnimalWeight);
writer.Write(WestID);
writer.Write(NorthID);
writer.Write(DeedNumber);
writer.Write(m_AddonNumber);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_Hunter = reader.ReadMobile();
m_AnimalWeight = reader.ReadInt();
goto case 0;
}
case 0:
{
WestID = reader.ReadInt();
NorthID = reader.ReadInt();
DeedNumber = reader.ReadInt();
m_AddonNumber = reader.ReadInt();
break;
}
}
Timer.DelayCall(TimeSpan.Zero, FixMovingCrate);
}
private void FixMovingCrate()
{
if (Deleted)
return;
if (Movable || IsLockedDown)
{
Item deed = Deed;
if (Parent is Item item)
{
item.AddItem(deed);
deed.Location = Location;
}
else
{
deed.MoveToWorld(Location, Map);
}
Delete();
}
}
public override void OnDoubleClick(Mobile from)
{
BaseHouse house = BaseHouse.FindHouseAt(this);
if (house?.IsCoOwner(from) == true)
{
if (from.InRange(GetWorldLocation(), 1))
{
from.AddToBackpack(Deed);
Delete();
}
else
{
from.SendLocalizedMessage(500295); // You are too far away to do that.
}
}
}
}
[Flippable(0x14F0, 0x14EF)]
public class TrophyDeed : Item
{
private int m_AnimalWeight;
private int m_DeedNumber;
private Mobile m_Hunter;
public TrophyDeed(int westID, int northID, int deedNumber, int addonNumber,
Mobile hunter = null, int animalWeight = 0) : base(0x14F0)
{
WestID = westID;
NorthID = northID;
m_DeedNumber = deedNumber;
AddonNumber = addonNumber;
m_Hunter = hunter;
m_AnimalWeight = animalWeight;
}
public TrophyDeed(TaxidermyKit.TrophyInfo info, Mobile hunter, int animalWeight)
: this(info.NorthID + 7, info.NorthID, info.DeedNumber, info.AddonNumber, hunter, animalWeight)
{
}
public TrophyDeed(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int WestID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int NorthID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int DeedNumber
{
get => m_DeedNumber;
set
{
m_DeedNumber = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int AddonNumber{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Hunter
{
get => m_Hunter;
set
{
m_Hunter = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int AnimalWeight
{
get => m_AnimalWeight;
set
{
m_AnimalWeight = value;
InvalidateProperties();
}
}
public override int LabelNumber => m_DeedNumber;
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_AnimalWeight >= 20)
{
if (m_Hunter != null)
list.Add(1070857, m_Hunter.Name); // Caught by ~1_fisherman~
list.Add(1070858, m_AnimalWeight.ToString()); // ~1_weight~ stones
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_Hunter);
writer.Write(m_AnimalWeight);
writer.Write(WestID);
writer.Write(NorthID);
writer.Write(m_DeedNumber);
writer.Write(AddonNumber);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_Hunter = reader.ReadMobile();
m_AnimalWeight = reader.ReadInt();
goto case 0;
}
case 0:
{
WestID = reader.ReadInt();
NorthID = reader.ReadInt();
m_DeedNumber = reader.ReadInt();
AddonNumber = reader.ReadInt();
break;
}
}
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
BaseHouse house = BaseHouse.FindHouseAt(from);
if (house?.IsCoOwner(from) == true)
{
bool northWall = BaseAddon.IsWall(from.X, from.Y - 1, from.Z, from.Map);
bool westWall = BaseAddon.IsWall(from.X - 1, from.Y, from.Z, from.Map);
if (northWall && westWall)
switch (from.Direction & Direction.Mask)
{
case Direction.North:
case Direction.South:
westWall = false;
break;
case Direction.East:
case Direction.West:
northWall = false;
break;
default:
from.SendMessage("Turn to face the wall on which to hang this trophy.");
return;
}
int itemID = 0;
if (northWall)
itemID = NorthID;
else if (westWall)
itemID = WestID;
else
from.SendLocalizedMessage(1042626); // The trophy must be placed next to a wall.
if (itemID > 0)
{
house.Addons.Add(new TrophyAddon(from, itemID, WestID, NorthID, m_DeedNumber, AddonNumber, m_Hunter,
m_AnimalWeight));
Delete();
}
}
else
{
from.SendLocalizedMessage(502092); // You must be in your house to do this.
}
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
}
}

View file

@ -0,0 +1,69 @@
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Engines.Harvest;
using Server.Network;
namespace Server.Items
{
public class FishingPole : Item
{
[Constructible]
public FishingPole() : base(0x0DC0)
{
Layer = Layer.TwoHanded;
Weight = 8.0;
}
public FishingPole(Serial serial) : base(serial)
{
}
public override void OnDoubleClick(Mobile from)
{
Point3D loc = GetWorldLocation();
if (!from.InLOS(loc) || !from.InRange(loc, 2))
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that
else
Fishing.System.BeginHarvesting(from, this);
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
BaseHarvestTool.AddContextMenuEntries(from, this, list, Fishing.System);
}
public override bool CheckConflictingLayer(Mobile m, Item item, Layer layer)
{
if (base.CheckConflictingLayer(m, item, layer))
return true;
if (layer == Layer.OneHanded)
{
m.SendLocalizedMessage(500214); // You already have something in both hands.
return true;
}
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version < 1 && Layer == Layer.OneHanded)
Layer = Layer.TwoHanded;
}
}
}

View file

@ -0,0 +1,103 @@
using System;
using Server.Network;
namespace Server.Items
{
public class MessageInABottle : Item
{
private int m_Level;
public MessageInABottle(Map map = null) : this(map, GetRandomLevel())
{
}
[Constructible]
public MessageInABottle(Map map, int level) : base(0x099F)
{
Weight = 1.0;
TargetMap = map ?? Map.Trammel;
m_Level = level;
}
public MessageInABottle(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041080; // a message in a bottle
[CommandProperty(AccessLevel.GameMaster)]
public Map TargetMap{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Level
{
get => m_Level;
set => m_Level = Math.Max(1, Math.Min(value, 4));
}
public static int GetRandomLevel()
{
if (Core.AOS && 1 > Utility.Random(25))
return 4; // ancient
return Utility.RandomMinMax(1, 3);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(3); // version
writer.Write(m_Level);
writer.Write(TargetMap);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 3:
case 2:
{
m_Level = reader.ReadInt();
goto case 1;
}
case 1:
{
TargetMap = reader.ReadMap();
break;
}
case 0:
{
TargetMap = Map.Trammel;
break;
}
}
if (version < 2)
m_Level = GetRandomLevel();
if (version < 3 && TargetMap == Map.Tokuno)
TargetMap = Map.Trammel;
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
ReplaceWith(new SOS(TargetMap, m_Level));
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 501891); // You extract the message from the bottle.
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
}
}

View file

@ -0,0 +1,326 @@
using System;
using Server.Gumps;
namespace Server.Items
{
[Flippable(0x14ED, 0x14EE)]
public class SOS : Item
{
public override int LabelNumber
{
get
{
if (IsAncient)
return 1063450; // an ancient SOS
return 1041081; // a waterstained SOS
}
}
private int m_Level;
[CommandProperty(AccessLevel.GameMaster)]
public bool IsAncient => m_Level >= 4;
[CommandProperty(AccessLevel.GameMaster)]
public int Level
{
get => m_Level;
set
{
m_Level = Math.Max(1, Math.Min(value, 4));
UpdateHue();
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public Map TargetMap{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Point3D TargetLocation{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int MessageIndex{ get; set; }
public void UpdateHue()
{
if (IsAncient)
Hue = 0x481;
else
Hue = 0;
}
[Constructible]
public SOS(Map map = null) : this(map, MessageInABottle.GetRandomLevel())
{
}
[Constructible]
public SOS(Map map, int level) : base(0x14EE)
{
Weight = 1.0;
m_Level = level;
MessageIndex = Utility.Random(MessageEntry.Entries.Length);
TargetMap = map ?? Map.Trammel;
TargetLocation = FindLocation(TargetMap);
UpdateHue();
}
public SOS(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(4); // version
writer.Write(m_Level);
writer.Write(TargetMap);
writer.Write(TargetLocation);
writer.Write(MessageIndex);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 4:
case 3:
case 2:
{
m_Level = reader.ReadInt();
goto case 1;
}
case 1:
{
TargetMap = reader.ReadMap();
TargetLocation = reader.ReadPoint3D();
MessageIndex = reader.ReadInt();
break;
}
case 0:
{
TargetMap = Map;
if (TargetMap == null || TargetMap == Map.Internal)
TargetMap = Map.Trammel;
TargetLocation = FindLocation(TargetMap);
MessageIndex = Utility.Random(MessageEntry.Entries.Length);
break;
}
}
if (version < 2)
m_Level = MessageInABottle.GetRandomLevel();
if (version < 3)
UpdateHue();
if (version < 4 && TargetMap == Map.Tokuno)
TargetMap = Map.Trammel;
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
MessageEntry entry;
if (MessageIndex >= 0 && MessageIndex < MessageEntry.Entries.Length)
entry = MessageEntry.Entries[MessageIndex];
else
entry = MessageEntry.Entries[MessageIndex = Utility.Random(MessageEntry.Entries.Length)];
//from.CloseGump( typeof( MessageGump ) );
from.SendGump(new MessageGump(entry, TargetMap, TargetLocation));
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
private static int[] m_WaterTiles =
{
0x00A8, 0x00AB,
0x0136, 0x0137
};
private static Rectangle2D[] m_BritRegions = { new Rectangle2D(0, 0, 5120, 4096) };
private static Rectangle2D[] m_IlshRegions =
{ new Rectangle2D(1472, 272, 304, 240), new Rectangle2D(1240, 1000, 312, 160) };
private static Rectangle2D[] m_MalasRegions = { new Rectangle2D(1376, 1520, 464, 280) };
public static Point3D FindLocation(Map map)
{
if (map == null || map == Map.Internal)
return Point3D.Zero;
Rectangle2D[] regions;
if (map == Map.Felucca || map == Map.Trammel)
regions = m_BritRegions;
else if (map == Map.Ilshenar)
regions = m_IlshRegions;
else if (map == Map.Malas)
regions = m_MalasRegions;
else
regions = new[] { new Rectangle2D(0, 0, map.Width, map.Height) };
if (regions.Length == 0)
return Point3D.Zero;
for (int i = 0; i < 50; ++i)
{
Rectangle2D reg = regions[Utility.Random(regions.Length)];
int x = Utility.Random(reg.X, reg.Width);
int y = Utility.Random(reg.Y, reg.Height);
if (!ValidateDeepWater(map, x, y))
continue;
bool valid = true;
for (int j = 1, offset = 5; valid && j <= 5; ++j, offset += 5)
if (!ValidateDeepWater(map, x + offset, y + offset))
valid = false;
else if (!ValidateDeepWater(map, x + offset, y - offset))
valid = false;
else if (!ValidateDeepWater(map, x - offset, y + offset))
valid = false;
else if (!ValidateDeepWater(map, x - offset, y - offset))
valid = false;
if (valid)
return new Point3D(x, y, 0);
}
return Point3D.Zero;
}
private static bool ValidateDeepWater(Map map, int x, int y)
{
int tileID = map.Tiles.GetLandTile(x, y).ID;
bool water = false;
for (int i = 0; !water && i < m_WaterTiles.Length; i += 2)
water = tileID >= m_WaterTiles[i] && tileID <= m_WaterTiles[i + 1];
return water;
}
#if false
private class MessageGump : Gump
{
public MessageGump( MessageEntry entry, Map map, Point3D loc ) : base( (640 - entry.Width) / 2, (480 - entry.Height) / 2 )
{
int xLong = 0, yLat = 0;
int xMins = 0, yMins = 0;
bool xEast = false, ySouth = false;
string fmt;
if ( Sextant.Format( loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) )
fmt =
String.Format( "{0}°{1}'{2},{3}°{4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" );
else
fmt = "?????";
AddPage( 0 );
AddBackground( 0, 0, entry.Width, entry.Height, 2520 );
AddHtml( 38, 38, entry.Width - 83, entry.Height - 86, String.Format( entry.Message, fmt ), false, false );
}
}
#else
private class MessageGump : Gump
{
public MessageGump(MessageEntry entry, Map map, Point3D loc) : base(150, 50)
{
int xLong = 0, yLat = 0;
int xMins = 0, yMins = 0;
bool xEast = false, ySouth = false;
string fmt;
if (Sextant.Format(loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth))
fmt = $"{yLat}°{yMins}'{(ySouth ? "S" : "N")},{xLong}°{xMins}'{(xEast ? "E" : "W")}";
else
fmt = "?????";
AddPage(0);
AddBackground(0, 40, 350, 300, 2520);
AddHtmlLocalized(30, 80, 285, 160, 1018326, true,
true); /* This is a message hastily scribbled by a passenger aboard a sinking ship.
* While it is probably too late to save the passengers and crew,
* perhaps some treasure went down with the ship!
* The message gives the ship's last known sextant co-ordinates.
*/
AddHtml(35, 240, 230, 20, fmt);
AddButton(35, 265, 4005, 4007, 0);
AddHtmlLocalized(70, 265, 100, 20, 1011036); // OKAY
}
}
#endif
private class MessageEntry
{
public MessageEntry(int width, int height, string message)
{
Width = width;
Height = height;
Message = message;
}
public int Width{ get; }
public int Height{ get; }
public string Message{ get; }
public static MessageEntry[] Entries{ get; } =
{
new MessageEntry(280, 180,
"...Ar! {0} and a fair wind! No chance... storms, though--ar! Is that a sea serp...<br><br>uh oh."),
new MessageEntry(280, 215,
"...been inside this whale for three days now. I've run out of food I can pick out of his teeth. I took a sextant reading through the blowhole: {0}. I'll never see my treasure again..."),
new MessageEntry(280, 285,
"...grand adventure! Captain Quacklebush had me swab down the decks daily...<br> ...pirates came, I was in the rigging practicing with my sextant. {0} if I am not mistaken...<br> ....scuttled the ship, and our precious cargo went with her and the screaming pirates, down to the bottom of the sea..."),
new MessageEntry(280, 180,
"Help! Ship going dow...n heavy storms...precious cargo...st reach dest...current coordinates {0}...ve any survivors... ease!"),
new MessageEntry(280, 215,
"...know that the wreck is near {0} but have not found it. Could the message passed down in my family for generations be wrong? No... I swear on the soul of my grandfather, I will find..."),
new MessageEntry(280, 195,
"...never expected an iceberg...silly woman on bow crushed instantly...send help to {0}...ey'll never forget the tragedy of the sinking of the Miniscule..."),
new MessageEntry(280, 265,
"...nobody knew I was a girl. They just assumed I was another sailor...then we met the undine. {0}. It was demanded sacrifice...I was youngset, they figured...<br> ...grabbed the captain's treasure, screamed, 'It'll go down with me!'<br> ...they took me up on it."),
new MessageEntry(280, 230,
"...so I threw the treasure overboard, before the curse could get me too. But I was too late. Now I am doomed to wander these seas, a ghost forever. Join me: seek ye at {0} if thou wishest my company..."),
new MessageEntry(280, 285,
"...then the ship exploded. A dragon swooped by. The slime swallowed Bertie whole--he screamed, it was amazing. The sky glowed orange. A sextant reading put us at {0}. Norma was chattering about sailing over the edge of the world. I looked at my hands and saw through them..."),
new MessageEntry(280, 285,
"...trapped on a deserted island, with a magic fountain supplying wood, fresh water springs, gorgeous scenery, and my lovely young wife. I know the ship with all our life's earnings sank at {0} but I don't know what our coordinates are... someone has GOT to rescue me before Sunday's finals game or I'll go mad..."),
new MessageEntry(280, 160,
"WANTED: divers exp...d in shipwre...overy. Must have own vess...pply at {0}<br>...good benefits, flexible hours..."),
new MessageEntry(280, 250,
"...was a cad and a boor, no matter what momma s...rew him overboard! Oh, Anna, 'twas so exciting!<br> Unfort...y he grabbe...est, and all his riches went with him!<br> ...sked the captain, and he says we're at {0}<br>...so maybe...")
};
}
}
}

View file

@ -0,0 +1,159 @@
using Server.Network;
namespace Server.Items
{
public class Sextant : Item
{
[Constructible]
public Sextant() : base(0x1058)
{
Weight = 2.0;
}
public Sextant(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void OnDoubleClick(Mobile from)
{
int xLong = 0, yLat = 0;
int xMins = 0, yMins = 0;
bool xEast = false, ySouth = false;
if (Format(from.Location, from.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth))
{
string location = $"{yLat}<7D> {yMins}'{(ySouth ? "S" : "N")}, {xLong}<7D> {xMins}'{(xEast ? "E" : "W")}";
from.LocalOverheadMessage(MessageType.Regular, from.SpeechHue, false, location);
}
}
public static bool ComputeMapDetails(Map map, int x, int y, out int xCenter, out int yCenter, out int xWidth,
out int yHeight)
{
xWidth = 5120;
yHeight = 4096;
if (map == Map.Trammel || map == Map.Felucca)
{
if (x >= 0 && y >= 0 && x < 5120 && y < 4096)
{
xCenter = 1323;
yCenter = 1624;
}
else if (x >= 5120 && y >= 2304 && x < 6144 && y < 4096)
{
xCenter = 5936;
yCenter = 3112;
}
else
{
xCenter = 0;
yCenter = 0;
return false;
}
}
else if (x >= 0 && y >= 0 && x < map.Width && y < map.Height)
{
xCenter = 1323;
yCenter = 1624;
}
else
{
xCenter = 0;
yCenter = 0;
return false;
}
return true;
}
public static Point3D ReverseLookup(Map map, int xLong, int yLat, int xMins, int yMins, bool xEast, bool ySouth)
{
if (map == null || map == Map.Internal)
return Point3D.Zero;
if (!ComputeMapDetails(map, 0, 0, out int xCenter, out int yCenter, out int xWidth, out int yHeight))
return Point3D.Zero;
double absLong = xLong + (double)xMins / 60;
double absLat = yLat + (double)yMins / 60;
if (!xEast)
absLong = 360.0 - absLong;
if (!ySouth)
absLat = 360.0 - absLat;
int x = xCenter + (int)(absLong * xWidth / 360);
int y = yCenter + (int)(absLat * yHeight / 360);
if (x < 0)
x += xWidth;
else if (x >= xWidth)
x -= xWidth;
if (y < 0)
y += yHeight;
else if (y >= yHeight)
y -= yHeight;
int z = map.GetAverageZ(x, y);
return new Point3D(x, y, z);
}
public static bool Format(Point3D p, Map map, ref int xLong, ref int yLat, ref int xMins, ref int yMins,
ref bool xEast, ref bool ySouth)
{
if (map == null || map == Map.Internal)
return false;
int x = p.X, y = p.Y;
if (!ComputeMapDetails(map, x, y, out int xCenter, out int yCenter, out int xWidth, out int yHeight))
return false;
double absLong = (double)((x - xCenter) * 360) / xWidth;
double absLat = (double)((y - yCenter) * 360) / yHeight;
if (absLong > 180.0)
absLong = -180.0 + absLong % 180.0;
if (absLat > 180.0)
absLat = -180.0 + absLat % 180.0;
bool east = absLong >= 0, south = absLat >= 0;
if (absLong < 0.0)
absLong = -absLong;
if (absLat < 0.0)
absLat = -absLat;
xLong = (int)absLong;
yLat = (int)absLat;
xMins = (int)(absLong % 1.0 * 60);
yMins = (int)(absLat % 1.0 * 60);
xEast = east;
ySouth = south;
return true;
}
}
}

View file

@ -0,0 +1,74 @@
namespace Server.Items
{
public interface IShipwreckedItem
{
bool IsShipwreckedItem{ get; set; }
}
public class ShipwreckedItem : Item, IDyable, IShipwreckedItem
{
public ShipwreckedItem(int itemID) : base(itemID)
{
int weight = ItemData.Weight;
if (weight >= 255)
weight = 1;
Weight = weight;
}
public ShipwreckedItem(Serial serial) : base(serial)
{
}
public bool Dye(Mobile from, DyeTub sender)
{
if (Deleted)
return false;
if (ItemID >= 0x13A4 && ItemID <= 0x13AE)
{
Hue = sender.DyedHue;
return true;
}
from.SendLocalizedMessage(sender.FailMessage);
return false;
}
#region IShipwreckedItem Members
bool IShipwreckedItem.IsShipwreckedItem
{
get => true;
set { }
}
#endregion
public override void OnSingleClick(Mobile from)
{
LabelTo(from, 1050039, $"#{LabelNumber}\t#1041645");
}
public override void AddNameProperties(ObjectPropertyList list)
{
base.AddNameProperties(list);
list.Add(1041645); // recovered from a shipwreck
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,424 @@
using System;
using Server.Mobiles;
using Server.Multis;
using Server.Spells;
using Server.Targeting;
namespace Server.Items
{
public class SpecialFishingNet : Item
{
private static int[] m_Hues =
{
0x09B,
0x0CD,
0x0D3,
0x14D,
0x1DD,
0x1E9,
0x1F4,
0x373,
0x451,
0x47F,
0x489,
0x492,
0x4B5,
0x8AA
};
private static int[] m_WaterTiles =
{
0x00A8, 0x00AB,
0x0136, 0x0137
};
private static int[] m_UndeepWaterTiles =
{
0x1797, 0x179C
};
[Constructible]
public SpecialFishingNet() : base(0x0DCA)
{
Weight = 1.0;
if (0.01 > Utility.RandomDouble())
Hue = Utility.RandomList(m_Hues);
else
Hue = 0x8A0;
}
public SpecialFishingNet(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041079; // a special fishing net
[CommandProperty(AccessLevel.GameMaster)]
public bool InUse{ get; set; }
public virtual bool RequireDeepWater => true;
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
AddNetProperties(list);
}
protected virtual void AddNetProperties(ObjectPropertyList list)
{
// as if the name wasn't enough..
list.Add(1017410); // Special Fishing Net
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(InUse);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
InUse = reader.ReadBool();
if (InUse)
Delete();
break;
}
}
Stackable = false;
}
public override void OnDoubleClick(Mobile from)
{
if (InUse)
{
from.SendLocalizedMessage(1010483); // Someone is already using that net!
}
else if (IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1010484); // Where do you wish to use the net?
from.BeginTarget(-1, true, TargetFlags.None, OnTarget);
}
else
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
}
public void OnTarget(Mobile from, object obj)
{
if (Deleted || InUse)
return;
if (!(obj is IPoint3D p3D))
return;
Map map = from.Map;
if (map == null || map == Map.Internal)
return;
int x = p3D.X, y = p3D.Y, z = map.GetAverageZ(x, y); // OSI just takes the targeted Z
if (!from.InRange(p3D, 6))
{
from.SendLocalizedMessage(500976); // You need to be closer to the water to fish!
}
else if (!from.InLOS(obj))
{
from.SendLocalizedMessage(500979); // You cannot see that location.
}
else if (RequireDeepWater
? FullValidation(map, x, y)
: ValidateDeepWater(map, x, y) || ValidateUndeepWater(map, obj, ref z))
{
Point3D p = new Point3D(x, y, z);
if (GetType() == typeof(SpecialFishingNet))
for (int i = 1; i < Amount; ++i) // these were stackable before, doh
from.AddToBackpack(new SpecialFishingNet());
InUse = true;
Movable = false;
MoveToWorld(p, map);
SpellHelper.Turn(from, p);
from.Animate(12, 5, 1, true, false, 0);
Effects.SendLocationEffect(p, map, 0x352D, 16, 4);
Effects.PlaySound(p, map, 0x364);
int index = 0;
Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.25), 14,
() => DoEffect(from, p, index++));
from.SendLocalizedMessage(RequireDeepWater
? 1010487
: 1074492); // You plunge the net into the sea... / You plunge the net into the water...
}
else
{
from.SendLocalizedMessage(RequireDeepWater
? 1010485
: 1074491); // You can only use this net in deep water! / You can only use this net in water!
}
}
private void DoEffect(Mobile from, Point3D p, int index)
{
if (Deleted)
return;
if (index == 1)
{
Effects.SendLocationEffect(p, Map, 0x352D, 16, 4);
Effects.PlaySound(p, Map, 0x364);
}
else if (index <= 7 || index == 14)
{
if (RequireDeepWater)
for (int i = 0; i < 3; ++i)
{
int x, y;
switch (Utility.Random(8))
{
default:
case 0:
x = -1;
y = -1;
break;
case 1:
x = -1;
y = 0;
break;
case 2:
x = -1;
y = +1;
break;
case 3:
x = 0;
y = -1;
break;
case 4:
x = 0;
y = +1;
break;
case 5:
x = +1;
y = -1;
break;
case 6:
x = +1;
y = 0;
break;
case 7:
x = +1;
y = +1;
break;
}
Effects.SendLocationEffect(new Point3D(p.X + x, p.Y + y, p.Z), Map, 0x352D, 16, 4);
}
else
Effects.SendLocationEffect(p, Map, 0x352D, 16, 4);
if (Utility.RandomBool())
Effects.PlaySound(p, Map, 0x364);
if (index == 14)
FinishEffect(p, Map, from);
else
Z -= 1;
}
}
protected virtual int GetSpawnCount()
{
int count = Utility.RandomMinMax(1, 3);
if (Hue != 0x8A0)
count += Utility.RandomMinMax(1, 2);
return count;
}
protected void Spawn(Point3D p, Map map, BaseCreature spawn)
{
if (map == null)
{
spawn.Delete();
return;
}
int x = p.X, y = p.Y;
for (int j = 0; j < 20; ++j)
{
int tx = p.X - 2 + Utility.Random(5);
int ty = p.Y - 2 + Utility.Random(5);
LandTile t = map.Tiles.GetLandTile(tx, ty);
if (t.Z == p.Z && (t.ID >= 0xA8 && t.ID <= 0xAB || t.ID >= 0x136 && t.ID <= 0x137) &&
!SpellHelper.CheckMulti(new Point3D(tx, ty, p.Z), map))
{
x = tx;
y = ty;
break;
}
}
spawn.MoveToWorld(new Point3D(x, y, p.Z), map);
if (spawn is Kraken && 0.2 > Utility.RandomDouble())
spawn.PackItem(new MessageInABottle(map == Map.Felucca ? Map.Felucca : Map.Trammel));
}
protected virtual void FinishEffect(Point3D p, Map map, Mobile from)
{
from.RevealingAction();
int count = GetSpawnCount();
for (int i = 0; map != null && i < count; ++i)
{
BaseCreature spawn;
switch (Utility.Random(4))
{
default:
case 0:
spawn = new SeaSerpent();
break;
case 1:
spawn = new DeepSeaSerpent();
break;
case 2:
spawn = new WaterElemental();
break;
case 3:
spawn = new Kraken();
break;
}
Spawn(p, map, spawn);
spawn.Combatant = from;
}
Delete();
}
public static bool FullValidation(Map map, int x, int y)
{
bool valid = ValidateDeepWater(map, x, y);
for (int j = 1, offset = 5; valid && j <= 5; ++j, offset += 5)
if (!ValidateDeepWater(map, x + offset, y + offset))
valid = false;
else if (!ValidateDeepWater(map, x + offset, y - offset))
valid = false;
else if (!ValidateDeepWater(map, x - offset, y + offset))
valid = false;
else if (!ValidateDeepWater(map, x - offset, y - offset))
valid = false;
return valid;
}
private static bool ValidateDeepWater(Map map, int x, int y)
{
int tileID = map.Tiles.GetLandTile(x, y).ID;
bool water = false;
for (int i = 0; !water && i < m_WaterTiles.Length; i += 2)
water = tileID >= m_WaterTiles[i] && tileID <= m_WaterTiles[i + 1];
return water;
}
private static bool ValidateUndeepWater(Map map, object obj, ref int z)
{
if (!(obj is StaticTarget))
return false;
StaticTarget target = (StaticTarget)obj;
if (BaseHouse.FindHouseAt(target.Location, map, 0) != null)
return false;
int itemID = target.ItemID;
for (int i = 0; i < m_UndeepWaterTiles.Length; i += 2)
if (itemID >= m_UndeepWaterTiles[i] && itemID <= m_UndeepWaterTiles[i + 1])
{
z = target.Z;
return true;
}
return false;
}
}
public class FabledFishingNet : SpecialFishingNet
{
[Constructible]
public FabledFishingNet()
{
Hue = 0x481;
}
public FabledFishingNet(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1063451; // a fabled fishing net
protected override void AddNetProperties(ObjectPropertyList list)
{
}
protected override int GetSpawnCount()
{
return base.GetSpawnCount() + 4;
}
protected override void FinishEffect(Point3D p, Map map, Mobile from)
{
Spawn(p, map, new Leviathan(from));
base.FinishEffect(p, map, from);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Engines.Craft;
using Server.Engines.Harvest;
using Server.Mobiles;
using Server.Network;
namespace Server.Items
{
public interface IUsesRemaining
{
int UsesRemaining{ get; set; }
bool ShowUsesRemaining{ get; set; }
}
public abstract class BaseHarvestTool : Item, IUsesRemaining, ICraftable
{
private Mobile m_Crafter;
private ToolQuality m_Quality;
private int m_UsesRemaining;
public BaseHarvestTool(int itemID, int usesRemaining = 50) : base(itemID)
{
m_UsesRemaining = usesRemaining;
m_Quality = ToolQuality.Regular;
}
public BaseHarvestTool(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Crafter
{
get => m_Crafter;
set
{
m_Crafter = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public ToolQuality Quality
{
get => m_Quality;
set
{
UnscaleUses();
m_Quality = value;
InvalidateProperties();
ScaleUses();
}
}
public abstract HarvestSystem HarvestSystem{ get; }
#region ICraftable Members
public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool,
CraftItem craftItem, int resHue)
{
Quality = (ToolQuality)quality;
if (makersMark)
Crafter = from;
return quality;
}
#endregion
[CommandProperty(AccessLevel.GameMaster)]
public int UsesRemaining
{
get => m_UsesRemaining;
set
{
m_UsesRemaining = value;
InvalidateProperties();
}
}
bool IUsesRemaining.ShowUsesRemaining
{
get => true;
set { }
}
public void ScaleUses()
{
m_UsesRemaining = m_UsesRemaining * GetUsesScalar() / 100;
InvalidateProperties();
}
public void UnscaleUses()
{
m_UsesRemaining = m_UsesRemaining * 100 / GetUsesScalar();
}
public int GetUsesScalar()
{
if (m_Quality == ToolQuality.Exceptional)
return 200;
return 100;
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
// Makers mark not displayed on OSI
//if ( m_Crafter != null )
// list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~
if (m_Quality == ToolQuality.Exceptional)
list.Add(1060636); // exceptional
list.Add(1060584, m_UsesRemaining.ToString()); // uses remaining: ~1_val~
}
public virtual void DisplayDurabilityTo(Mobile m)
{
LabelToAffix(m, 1017323, AffixType.Append, ": " + m_UsesRemaining); // Durability
}
public override void OnSingleClick(Mobile from)
{
DisplayDurabilityTo(from);
base.OnSingleClick(from);
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack) || Parent == from)
HarvestSystem.BeginHarvesting(from, this);
else
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
AddContextMenuEntries(from, this, list, HarvestSystem);
}
public static void AddContextMenuEntries(Mobile from, Item item, List<ContextMenuEntry> list, HarvestSystem system)
{
if (system != Mining.System)
return;
if (!item.IsChildOf(from.Backpack) && item.Parent != from)
return;
if (!(from is PlayerMobile pm))
return;
ContextMenuEntry miningEntry = new ContextMenuEntry(pm.ToggleMiningStone ? 6179 : 6178);
miningEntry.Color = 0x421F;
list.Add(miningEntry);
list.Add(new ToggleMiningStoneEntry(pm, false, 6176));
list.Add(new ToggleMiningStoneEntry(pm, true, 6177));
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_Crafter);
writer.Write((int)m_Quality);
writer.Write(m_UsesRemaining);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_Crafter = reader.ReadMobile();
m_Quality = (ToolQuality)reader.ReadInt();
goto case 0;
}
case 0:
{
m_UsesRemaining = reader.ReadInt();
break;
}
}
}
private class ToggleMiningStoneEntry : ContextMenuEntry
{
private PlayerMobile m_Mobile;
private bool m_Value;
public ToggleMiningStoneEntry(PlayerMobile mobile, bool value, int number) : base(number)
{
m_Mobile = mobile;
m_Value = value;
bool stoneMining = mobile.StoneMining && mobile.Skills.Mining.Base >= 100.0;
if (mobile.ToggleMiningStone == value || value && !stoneMining)
Flags |= CMEFlags.Disabled;
}
public override void OnClick()
{
bool oldValue = m_Mobile.ToggleMiningStone;
if (m_Value)
{
if (oldValue)
{
m_Mobile.SendLocalizedMessage(1054023); // You are already set to mine both ore and stone!
}
else if (!m_Mobile.StoneMining || m_Mobile.Skills.Mining.Base < 100.0)
{
m_Mobile.SendLocalizedMessage(
1054024); // You have not learned how to mine stone or you do not have enough skill!
}
else
{
m_Mobile.ToggleMiningStone = true;
m_Mobile.SendLocalizedMessage(1054022); // You are now set to mine both ore and stone.
}
}
else
{
if (oldValue)
{
m_Mobile.ToggleMiningStone = false;
m_Mobile.SendLocalizedMessage(1054020); // You are now set to mine only ore.
}
else
{
m_Mobile.SendLocalizedMessage(1054021); // You are already set to mine only ore!
}
}
}
}
}
}

View file

@ -0,0 +1,63 @@
using Server.Engines.Harvest;
namespace Server.Items
{
public class GargoylesPickaxe : BaseAxe, IUsesRemaining
{
[Constructible]
public GargoylesPickaxe() : this(Utility.RandomMinMax(101, 125))
{
}
[Constructible]
public GargoylesPickaxe(int uses) : base(0xE85 + Utility.Random(2))
{
Weight = 11.0;
UsesRemaining = uses;
ShowUsesRemaining = true;
}
public GargoylesPickaxe(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1041281; // a gargoyle's pickaxe
public override HarvestSystem HarvestSystem => Mining.System;
public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike;
public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm;
public override int AosStrengthReq => 50;
public override int AosMinDamage => 13;
public override int AosMaxDamage => 15;
public override int AosSpeed => 35;
public override float MlSpeed => 3.00f;
public override int OldStrengthReq => 25;
public override int OldMinDamage => 1;
public override int OldMaxDamage => 15;
public override int OldSpeed => 35;
public override int InitMinHits => 31;
public override int InitMaxHits => 60;
public override WeaponAnimation DefAnimation => WeaponAnimation.Slash1H;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (Hue == 0x973)
Hue = 0x0;
}
}
}

View file

@ -0,0 +1,177 @@
using System;
using Server.Engines.Harvest;
using Server.Targeting;
namespace Server.Items
{
public class ProspectorsTool : BaseBashing, IUsesRemaining
{
private int m_UsesRemaining;
[Constructible]
public ProspectorsTool() : base(0xFB4)
{
Weight = 9.0;
UsesRemaining = 50;
}
public ProspectorsTool(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1049065; // prospector's tool
public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow;
public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike;
public override int AosStrengthReq => 40;
public override int AosMinDamage => 13;
public override int AosMaxDamage => 15;
public override int AosSpeed => 33;
public override int OldStrengthReq => 10;
public override int OldMinDamage => 6;
public override int OldMaxDamage => 8;
public override int OldSpeed => 33;
[CommandProperty(AccessLevel.GameMaster)]
public int UsesRemaining
{
get => m_UsesRemaining;
set
{
m_UsesRemaining = value;
InvalidateProperties();
}
}
bool IUsesRemaining.ShowUsesRemaining
{
get => true;
set { }
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack) || Parent == from)
from.Target = new InternalTarget(this);
else
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
}
public void Prospect(Mobile from, object toProspect)
{
if (!IsChildOf(from.Backpack) && Parent != from)
{
from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it.
return;
}
HarvestSystem system = Mining.System;
if (!system.GetHarvestDetails(from, this, toProspect, out int tileID, out Map map, out Point3D loc))
{
from.SendLocalizedMessage(1049048); // You cannot use your prospector tool on that.
return;
}
HarvestDefinition def = system.GetDefinition(tileID);
if (def == null || def.Veins.Length <= 1)
{
from.SendLocalizedMessage(1049048); // You cannot use your prospector tool on that.
return;
}
HarvestBank bank = def.GetBank(map, loc.X, loc.Y);
if (bank == null)
{
from.SendLocalizedMessage(1049048); // You cannot use your prospector tool on that.
return;
}
HarvestVein vein = bank.Vein, defaultVein = bank.DefaultVein;
if (vein == null || defaultVein == null)
{
from.SendLocalizedMessage(1049048); // You cannot use your prospector tool on that.
return;
}
if (vein != defaultVein)
{
from.SendLocalizedMessage(1049049); // That ore looks to be prospected already.
return;
}
int veinIndex = Array.IndexOf(def.Veins, vein);
if (veinIndex < 0)
{
from.SendLocalizedMessage(1049048); // You cannot use your prospector tool on that.
}
else if (veinIndex >= def.Veins.Length - 1)
{
from.SendLocalizedMessage(1049061); // You cannot improve valorite ore through prospecting.
}
else
{
bank.Vein = def.Veins[veinIndex + 1];
from.SendLocalizedMessage(1049050 + veinIndex);
--UsesRemaining;
if (UsesRemaining <= 0)
{
from.SendLocalizedMessage(1049062); // You have used up your prospector's tool.
Delete();
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(m_UsesRemaining);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_UsesRemaining = reader.ReadInt();
break;
}
case 0:
{
m_UsesRemaining = 50;
break;
}
}
}
private class InternalTarget : Target
{
private ProspectorsTool m_Tool;
public InternalTarget(ProspectorsTool tool) : base(2, true, TargetFlags.None)
{
m_Tool = tool;
}
protected override void OnTarget(Mobile from, object targeted)
{
m_Tool.Prospect(from, targeted);
}
}
}
}

View file

@ -0,0 +1,33 @@
using Server.Engines.Harvest;
namespace Server.Items
{
public class Shovel : BaseHarvestTool
{
[Constructible]
public Shovel(int uses = 50) : base(0xF39, uses)
{
Weight = 5.0;
}
public Shovel(Serial serial) : base(serial)
{
}
public override HarvestSystem HarvestSystem => Mining.System;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,53 @@
using Server.Engines.Harvest;
namespace Server.Items
{
public class SturdyPickaxe : BaseAxe, IUsesRemaining
{
[Constructible]
public SturdyPickaxe(int uses = 180) : base(0xE86)
{
Weight = 11.0;
Hue = 0x973;
UsesRemaining = uses;
ShowUsesRemaining = true;
}
public SturdyPickaxe(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1045126; // sturdy pickaxe
public override HarvestSystem HarvestSystem => Mining.System;
public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike;
public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm;
public override int AosStrengthReq => 50;
public override int AosMinDamage => 13;
public override int AosMaxDamage => 15;
public override int AosSpeed => 35;
public override float MlSpeed => 3.00f;
public override int OldStrengthReq => 25;
public override int OldMinDamage => 1;
public override int OldMaxDamage => 15;
public override int OldSpeed => 35;
public override WeaponAnimation DefAnimation => WeaponAnimation.Slash1H;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,35 @@
using Server.Engines.Harvest;
namespace Server.Items
{
public class SturdyShovel : BaseHarvestTool
{
[Constructible]
public SturdyShovel(int uses = 180) : base(0xF39, uses)
{
Weight = 5.0;
Hue = 0x973;
}
public SturdyShovel(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1045125; // sturdy shovel
public override HarvestSystem HarvestSystem => Mining.System;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,334 @@
namespace Server.Items
{
[Flippable(0x1bdd, 0x1be0)]
public class Log : Item, ICommodity, IAxe
{
private CraftResource m_Resource;
[Constructible]
public Log(int amount = 1) : this(CraftResource.RegularWood, amount)
{
}
[Constructible]
public Log(CraftResource resource)
: this(resource, 1)
{
}
[Constructible]
public Log(CraftResource resource, int amount)
: base(0x1BDD)
{
Stackable = true;
Weight = 2.0;
Amount = amount;
m_Resource = resource;
Hue = CraftResources.GetHue(resource);
}
public Log(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public CraftResource Resource
{
get => m_Resource;
set
{
m_Resource = value;
InvalidateProperties();
}
}
public virtual bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 0, new Board()))
return false;
return true;
}
int ICommodity.DescriptionNumber => CraftResources.IsStandard(m_Resource)
? LabelNumber
: 1075062 + ((int)m_Resource - (int)CraftResource.RegularWood);
bool ICommodity.IsDeedable => true;
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (!CraftResources.IsStandard(m_Resource))
{
int num = CraftResources.GetLocalizationNumber(m_Resource);
if (num > 0)
list.Add(num);
else
list.Add(CraftResources.GetName(m_Resource));
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write((int)m_Resource);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_Resource = (CraftResource)reader.ReadInt();
break;
}
}
if (version == 0)
m_Resource = CraftResource.RegularWood;
}
public virtual bool TryCreateBoards(Mobile from, double skill, Item item)
{
if (Deleted || !from.CanSee(this))
return false;
if (from.Skills.Carpentry.Value < skill &&
from.Skills.Lumberjacking.Value < skill)
{
item.Delete();
from.SendLocalizedMessage(1072652); // You cannot work this strange and unusual wood.
return false;
}
base.ScissorHelper(from, item, 1, false);
return true;
}
}
public class HeartwoodLog : Log
{
[Constructible]
public HeartwoodLog(int amount = 1)
: base(CraftResource.Heartwood, amount)
{
}
public HeartwoodLog(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 100, new HeartwoodBoard()))
return false;
return true;
}
}
public class BloodwoodLog : Log
{
[Constructible]
public BloodwoodLog(int amount = 1)
: base(CraftResource.Bloodwood, amount)
{
}
public BloodwoodLog(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 100, new BloodwoodBoard()))
return false;
return true;
}
}
public class FrostwoodLog : Log
{
[Constructible]
public FrostwoodLog(int amount = 1)
: base(CraftResource.Frostwood, amount)
{
}
public FrostwoodLog(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 100, new FrostwoodBoard()))
return false;
return true;
}
}
public class OakLog : Log
{
[Constructible]
public OakLog(int amount = 1)
: base(CraftResource.OakWood, amount)
{
}
public OakLog(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 65, new OakBoard()))
return false;
return true;
}
}
public class AshLog : Log
{
[Constructible]
public AshLog(int amount = 1)
: base(CraftResource.AshWood, amount)
{
}
public AshLog(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 80, new AshBoard()))
return false;
return true;
}
}
public class YewLog : Log
{
[Constructible]
public YewLog(int amount = 1)
: base(CraftResource.YewWood, amount)
{
}
public YewLog(Serial serial)
: base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override bool Axe(Mobile from, BaseAxe axe)
{
if (!TryCreateBoards(from, 95, new YewBoard()))
return false;
return true;
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class BookOfBushido : Spellbook
{
[Constructible]
public BookOfBushido(ulong content = 0x3F) : base(content, 0x238C)
{
Layer = Core.ML ? Layer.OneHanded : Layer.Invalid;
}
public BookOfBushido(Serial serial) : base(serial)
{
}
public override SpellbookType SpellbookType => SpellbookType.Samurai;
public override int BookOffset => 400;
public override int BookCount => 6;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0 && Core.ML)
Layer = Layer.OneHanded;
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class BookOfChivalry : Spellbook
{
[Constructible]
public BookOfChivalry(ulong content = 0x3FF) : base(content, 0x2252)
{
Layer = Core.ML ? Layer.OneHanded : Layer.Invalid;
}
public BookOfChivalry(Serial serial) : base(serial)
{
}
public override SpellbookType SpellbookType => SpellbookType.Paladin;
public override int BookOffset => 200;
public override int BookCount => 10;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0 && Core.ML)
Layer = Layer.OneHanded;
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class BookOfNinjitsu : Spellbook
{
[Constructible]
public BookOfNinjitsu(ulong content = 0xFF) : base(content, 0x23A0)
{
Layer = Core.ML ? Layer.OneHanded : Layer.Invalid;
}
public BookOfNinjitsu(Serial serial) : base(serial)
{
}
public override SpellbookType SpellbookType => SpellbookType.Ninja;
public override int BookOffset => 500;
public override int BookCount => 8;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0 && Core.ML)
Layer = Layer.OneHanded;
}
}
}

View file

@ -0,0 +1,34 @@
namespace Server.Items
{
public class BlankScroll : Item, ICommodity
{
[Constructible]
public BlankScroll(int amount = 1) : base(0xEF3)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public BlankScroll(Serial serial) : base(serial)
{
}
int ICommodity.DescriptionNumber => LabelNumber;
bool ICommodity.IsDeedable => Core.ML;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,35 @@
namespace Server.Items
{
public class Bottle : Item, ICommodity
{
[Constructible]
public Bottle(int amount = 1) : base(0xF0E)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public Bottle(Serial serial) : base(serial)
{
}
int ICommodity.DescriptionNumber => LabelNumber;
bool ICommodity.IsDeedable => Core.ML;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,393 @@
using System;
using Server.Factions;
using Server.Gumps;
using Server.Misc;
using Server.Mobiles;
using Server.Network;
using Server.Regions;
namespace Server.Items
{
[DispellableField]
public class Moongate : Item
{
[Constructible]
public Moongate(bool dispellable = true) :
this(Point3D.Zero, null, dispellable)
{
}
[Constructible]
public Moongate(Point3D target, Map targetMap = null, bool dispellable = true) : base(0xF6C)
{
Movable = false;
Light = LightType.Circle300;
Target = target;
TargetMap = targetMap;
Dispellable = dispellable;
}
public Moongate(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public Point3D Target{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Map TargetMap{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool Dispellable{ get; set; }
public virtual bool ShowFeluccaWarning => false;
public override void OnDoubleClick(Mobile from)
{
if (!from.Player)
return;
if (from.InRange(GetWorldLocation(), 1))
CheckGate(from, 1);
else
from.SendLocalizedMessage(500446); // That is too far away.
}
public override bool OnMoveOver(Mobile m)
{
if (m.Player)
CheckGate(m, 0);
return true;
}
public virtual void CheckGate(Mobile m, int range)
{
#region Mondain's Legacy
if (m.Hidden && m.AccessLevel == AccessLevel.Player && Core.ML)
m.RevealingAction();
#endregion
new DelayTimer(m, this, range).Start();
}
public virtual void OnGateUsed(Mobile m)
{
}
public virtual void UseGate(Mobile m)
{
ClientFlags flags = m.NetState?.Flags ?? ClientFlags.None;
if (Sigil.ExistsOn(m))
{
m.SendLocalizedMessage(1061632); // You can't do that while carrying the sigil.
}
else if (TargetMap == Map.Felucca && m is PlayerMobile mobile && mobile.Young)
{
mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young.
}
else if (m.Kills >= 5 && TargetMap != Map.Felucca ||
TargetMap == Map.Tokuno && (flags & ClientFlags.Tokuno) == 0 ||
TargetMap == Map.Malas && (flags & ClientFlags.Malas) == 0 ||
TargetMap == Map.Ilshenar && (flags & ClientFlags.Ilshenar) == 0)
{
m.SendLocalizedMessage(1019004); // You are not allowed to travel there.
}
else if (m.Spell != null)
{
m.SendLocalizedMessage(1049616); // You are too busy to do that at the moment.
}
else if (TargetMap != null && TargetMap != Map.Internal)
{
BaseCreature.TeleportPets(m, Target, TargetMap);
m.MoveToWorld(Target, TargetMap);
if (m.AccessLevel == AccessLevel.Player || !m.Hidden)
m.PlaySound(0x1FE);
OnGateUsed(m);
}
else
{
m.SendMessage("This moongate does not seem to go anywhere.");
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write(Target);
writer.Write(TargetMap);
// Version 1
writer.Write(Dispellable);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Target = reader.ReadPoint3D();
TargetMap = reader.ReadMap();
if (version >= 1)
Dispellable = reader.ReadBool();
}
public virtual bool ValidateUse(Mobile from, bool message)
{
if (from.Deleted || Deleted)
return false;
if (from.Map != Map || !from.InRange(this, 1))
{
if (message)
from.SendLocalizedMessage(500446); // That is too far away.
return false;
}
return true;
}
public virtual void BeginConfirmation(Mobile from)
{
if (IsInTown(from.Location, from.Map) && !IsInTown(Target, TargetMap) ||
from.Map != Map.Felucca && TargetMap == Map.Felucca && ShowFeluccaWarning)
{
if (from.AccessLevel == AccessLevel.Player || !from.Hidden)
from.Send(new PlaySound(0x20E, from.Location));
from.CloseGump<MoongateConfirmGump>();
from.SendGump(new MoongateConfirmGump(from, this));
}
else
{
EndConfirmation(from);
}
}
public virtual void EndConfirmation(Mobile from)
{
if (!ValidateUse(from, true))
return;
UseGate(from);
}
public virtual void DelayCallback(Mobile from, int range)
{
if (!ValidateUse(from, false) || !from.InRange(this, range))
return;
if (TargetMap != null)
BeginConfirmation(from);
else
from.SendMessage("This moongate does not seem to go anywhere.");
}
public static bool IsInTown(Point3D p, Map map)
{
return map != null && Region.Find(p, map).GetRegion<GuardedRegion>()?.IsDisabled() == false;
}
private class DelayTimer : Timer
{
private Mobile m_From;
private Moongate m_Gate;
private int m_Range;
public DelayTimer(Mobile from, Moongate gate, int range) : base(TimeSpan.FromSeconds(1.0))
{
m_From = from;
m_Gate = gate;
m_Range = range;
}
protected override void OnTick()
{
m_Gate.DelayCallback(m_From, m_Range);
}
}
}
public class ConfirmationMoongate : Moongate
{
[Constructible]
public ConfirmationMoongate() : this(Point3D.Zero)
{
}
[Constructible]
public ConfirmationMoongate(Point3D target, Map targetMap = null) : base(target, targetMap)
{
}
public ConfirmationMoongate(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int GumpWidth{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int GumpHeight{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int TitleColor{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int MessageColor{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int TitleNumber{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int MessageNumber{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public string MessageString{ get; set; }
public virtual void Warning_Callback(Mobile from, bool okay)
{
if (okay)
EndConfirmation(from);
}
public override void BeginConfirmation(Mobile from)
{
if (GumpWidth > 0 && GumpHeight > 0 && TitleNumber > 0 && (MessageNumber > 0 || MessageString != null))
{
from.CloseGump<WarningGump>();
from.SendGump(new WarningGump(TitleNumber, TitleColor,
MessageString == null ? MessageNumber : (object)MessageString, MessageColor, GumpWidth, GumpHeight,
okay => Warning_Callback(from, okay)));
}
else
{
base.BeginConfirmation(from);
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.WriteEncodedInt(GumpWidth);
writer.WriteEncodedInt(GumpHeight);
writer.WriteEncodedInt(TitleColor);
writer.WriteEncodedInt(MessageColor);
writer.WriteEncodedInt(TitleNumber);
writer.WriteEncodedInt(MessageNumber);
writer.Write(MessageString);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
GumpWidth = reader.ReadEncodedInt();
GumpHeight = reader.ReadEncodedInt();
TitleColor = reader.ReadEncodedInt();
MessageColor = reader.ReadEncodedInt();
TitleNumber = reader.ReadEncodedInt();
MessageNumber = reader.ReadEncodedInt();
MessageString = reader.ReadString();
break;
}
}
}
}
public class MoongateConfirmGump : Gump
{
private Mobile m_From;
private Moongate m_Gate;
public MoongateConfirmGump(Mobile from, Moongate gate) : base(Core.AOS ? 110 : 20, Core.AOS ? 100 : 30)
{
m_From = from;
m_Gate = gate;
if (Core.AOS)
{
Closable = false;
AddPage(0);
AddBackground(0, 0, 420, 280, 5054);
AddImageTiled(10, 10, 400, 20, 2624);
AddAlphaRegion(10, 10, 400, 20);
AddHtmlLocalized(10, 10, 400, 20, 1062051, 30720); // Gate Warning
AddImageTiled(10, 40, 400, 200, 2624);
AddAlphaRegion(10, 40, 400, 200);
if (from.Map != Map.Felucca && gate.TargetMap == Map.Felucca && gate.ShowFeluccaWarning)
AddHtmlLocalized(10, 40, 400, 200, 1062050, 32512, false,
true); // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here
else
AddHtmlLocalized(10, 40, 400, 200, 1062049, 32512, false,
true); // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here
AddImageTiled(10, 250, 400, 20, 2624);
AddAlphaRegion(10, 250, 400, 20);
AddButton(10, 250, 4005, 4007, 1);
AddHtmlLocalized(40, 250, 170, 20, 1011036, 32767); // OKAY
AddButton(210, 250, 4005, 4007, 0);
AddHtmlLocalized(240, 250, 170, 20, 1011012, 32767); // CANCEL
}
else
{
AddPage(0);
AddBackground(0, 0, 420, 400, 5054);
AddBackground(10, 10, 400, 380, 3000);
AddHtml(20, 40, 380, 60,
@"Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here");
AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL
AddButton(20, 110, 4005, 4007, 0);
AddHtmlLocalized(55, 140, 290, 40, 1011011); // CONTINUE
AddButton(20, 140, 4005, 4007, 1);
}
}
public override void OnResponse(NetState state, RelayInfo info)
{
if (info.ButtonID == 1)
m_Gate.EndConfirmation(m_From);
}
}
}

View file

@ -0,0 +1,342 @@
using System;
using Server.Network;
namespace Server.Items
{
public class PotionKeg : Item
{
private int m_Held;
private PotionEffect m_Type;
[Constructible]
public PotionKeg() : base(0x1940)
{
UpdateWeight();
}
public PotionKeg(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int Held
{
get => m_Held;
set
{
if (m_Held != value)
{
m_Held = value;
UpdateWeight();
InvalidateProperties();
}
}
}
[CommandProperty(AccessLevel.GameMaster)]
public PotionEffect Type
{
get => m_Type;
set
{
m_Type = value;
InvalidateProperties();
}
}
public override int LabelNumber
{
get
{
if (m_Held > 0 && (int)m_Type >= (int)PotionEffect.Conflagration)
return 1072658 + (int)m_Type - (int)PotionEffect.Conflagration;
return m_Held > 0 ? 1041620 + (int)m_Type : 1041641;
}
}
public virtual void UpdateWeight()
{
int held = Math.Max(0, Math.Min(m_Held, 100));
Weight = 20 + held * 80 / 100;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write((int)m_Type);
writer.Write(m_Held);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
case 0:
{
m_Type = (PotionEffect)reader.ReadInt();
m_Held = reader.ReadInt();
break;
}
}
if (version < 1)
Timer.DelayCall(TimeSpan.Zero, UpdateWeight);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
int number;
if (m_Held <= 0)
number = 502246; // The keg is empty.
else if (m_Held < 5)
number = 502248; // The keg is nearly empty.
else if (m_Held < 20)
number = 502249; // The keg is not very full.
else if (m_Held < 30)
number = 502250; // The keg is about one quarter full.
else if (m_Held < 40)
number = 502251; // The keg is about one third full.
else if (m_Held < 47)
number = 502252; // The keg is almost half full.
else if (m_Held < 54)
number = 502254; // The keg is approximately half full.
else if (m_Held < 70)
number = 502253; // The keg is more than half full.
else if (m_Held < 80)
number = 502255; // The keg is about three quarters full.
else if (m_Held < 96)
number = 502256; // The keg is very full.
else if (m_Held < 100)
number = 502257; // The liquid is almost to the top of the keg.
else
number = 502258; // The keg is completely full.
list.Add(number);
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
int number;
if (m_Held <= 0)
number = 502246; // The keg is empty.
else if (m_Held < 5)
number = 502248; // The keg is nearly empty.
else if (m_Held < 20)
number = 502249; // The keg is not very full.
else if (m_Held < 30)
number = 502250; // The keg is about one quarter full.
else if (m_Held < 40)
number = 502251; // The keg is about one third full.
else if (m_Held < 47)
number = 502252; // The keg is almost half full.
else if (m_Held < 54)
number = 502254; // The keg is approximately half full.
else if (m_Held < 70)
number = 502253; // The keg is more than half full.
else if (m_Held < 80)
number = 502255; // The keg is about three quarters full.
else if (m_Held < 96)
number = 502256; // The keg is very full.
else if (m_Held < 100)
number = 502257; // The liquid is almost to the top of the keg.
else
number = 502258; // The keg is completely full.
LabelTo(from, number);
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(GetWorldLocation(), 2))
{
if (m_Held > 0)
{
Container pack = from.Backpack;
if (pack?.ConsumeTotal(typeof(Bottle)) == true)
{
from.SendLocalizedMessage(502242); // You pour some of the keg's contents into an empty bottle...
BasePotion pot = FillBottle();
if (pack.TryDropItem(from, pot, false))
{
from.SendLocalizedMessage(502243); // ...and place it into your backpack.
from.PlaySound(0x240);
if (--Held == 0)
from.SendLocalizedMessage(502245); // The keg is now empty.
}
else
{
from.SendLocalizedMessage(502244); // ...but there is no room for the bottle in your backpack.
pot.Delete();
}
}
}
else
{
from.SendLocalizedMessage(502246); // The keg is empty.
}
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
}
}
public override bool OnDragDrop(Mobile from, Item item)
{
if (!(item is BasePotion pot))
{
from.SendLocalizedMessage(502232); // The keg is not designed to hold that type of object.
return false;
}
int toHold = Math.Min(100 - m_Held, pot.Amount);
if (toHold <= 0)
{
from.SendLocalizedMessage(502233); // The keg will not hold any more!
return false;
}
if (m_Held == 0)
{
#region Mondain's Legacy
if ((int)pot.PotionEffect >= (int)PotionEffect.Invisibility)
{
from.SendLocalizedMessage(502232); // The keg is not designed to hold that type of object.
return false;
}
#endregion
if (GiveBottle(from, toHold))
{
m_Type = pot.PotionEffect;
Held = toHold;
from.PlaySound(0x240);
from.SendLocalizedMessage(502237); // You place the empty bottle in your backpack.
pot.Consume(toHold);
if (!pot.Deleted)
pot.Bounce(from);
return true;
}
from.SendLocalizedMessage(502238); // You don't have room for the empty bottle in your backpack.
return false;
}
if (pot.PotionEffect != m_Type)
{
from.SendLocalizedMessage(
502236); // You decide that it would be a bad idea to mix different types of potions.
return false;
}
if (GiveBottle(from, toHold))
{
Held += toHold;
from.PlaySound(0x240);
from.SendLocalizedMessage(502237); // You place the empty bottle in your backpack.
pot.Consume(toHold);
if (!pot.Deleted)
pot.Bounce(from);
return true;
}
from.SendLocalizedMessage(502238); // You don't have room for the empty bottle in your backpack.
return false;
}
public bool GiveBottle(Mobile m, int amount)
{
Container pack = m.Backpack;
Bottle bottle = new Bottle(amount);
if (pack?.TryDropItem(m, bottle, false) != true)
{
bottle.Delete();
return false;
}
return true;
}
public BasePotion FillBottle()
{
switch (m_Type)
{
default:
case PotionEffect.Nightsight: return new NightSightPotion();
case PotionEffect.CureLesser: return new LesserCurePotion();
case PotionEffect.Cure: return new CurePotion();
case PotionEffect.CureGreater: return new GreaterCurePotion();
case PotionEffect.Agility: return new AgilityPotion();
case PotionEffect.AgilityGreater: return new GreaterAgilityPotion();
case PotionEffect.Strength: return new StrengthPotion();
case PotionEffect.StrengthGreater: return new GreaterStrengthPotion();
case PotionEffect.PoisonLesser: return new LesserPoisonPotion();
case PotionEffect.Poison: return new PoisonPotion();
case PotionEffect.PoisonGreater: return new GreaterPoisonPotion();
case PotionEffect.PoisonDeadly: return new DeadlyPoisonPotion();
case PotionEffect.Refresh: return new RefreshPotion();
case PotionEffect.RefreshTotal: return new TotalRefreshPotion();
case PotionEffect.HealLesser: return new LesserHealPotion();
case PotionEffect.Heal: return new HealPotion();
case PotionEffect.HealGreater: return new GreaterHealPotion();
case PotionEffect.ExplosionLesser: return new LesserExplosionPotion();
case PotionEffect.Explosion: return new ExplosionPotion();
case PotionEffect.ExplosionGreater: return new GreaterExplosionPotion();
case PotionEffect.Conflagration: return new ConflagrationPotion();
case PotionEffect.ConflagrationGreater: return new GreaterConflagrationPotion();
case PotionEffect.ConfusionBlast: return new ConfusionBlastPotion();
case PotionEffect.ConfusionBlastGreater: return new GreaterConfusionBlastPotion();
}
}
public static void Initialize()
{
TileData.ItemTable[0x1940].Height = 4;
}
}
}

View file

@ -0,0 +1,308 @@
using Server.Multis;
using Server.Prompts;
using Server.Regions;
namespace Server.Items
{
[Flippable(0x1f14, 0x1f15, 0x1f16, 0x1f17)]
public class RecallRune : Item
{
private const string RuneFormat = "a recall rune for {0}";
private string m_Description;
private BaseHouse m_House;
private bool m_Marked;
private Map m_TargetMap;
[Constructible]
public RecallRune() : base(0x1F14)
{
Weight = 1.0;
CalculateHue();
}
public RecallRune(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public BaseHouse House
{
get
{
if (m_House?.Deleted == true)
House = null;
return m_House;
}
set
{
m_House = value;
CalculateHue();
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public string Description
{
get => m_Description;
set
{
m_Description = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public bool Marked
{
get => m_Marked;
set
{
if (m_Marked != value)
{
m_Marked = value;
CalculateHue();
InvalidateProperties();
}
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public Point3D Target{ get; set; }
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public Map TargetMap
{
get => m_TargetMap;
set
{
if (m_TargetMap != value)
{
m_TargetMap = value;
CalculateHue();
InvalidateProperties();
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
if (m_House?.Deleted == false)
{
writer.Write(1); // version
writer.Write(m_House);
}
else
{
writer.Write(0); // version
}
writer.Write(m_Description);
writer.Write(m_Marked);
writer.Write(Target);
writer.Write(m_TargetMap);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
m_House = reader.ReadItem() as BaseHouse;
goto case 0;
}
case 0:
{
m_Description = reader.ReadString();
m_Marked = reader.ReadBool();
Target = reader.ReadPoint3D();
m_TargetMap = reader.ReadMap();
CalculateHue();
break;
}
}
}
private void CalculateHue()
{
if (!m_Marked)
Hue = 0;
else if (m_TargetMap == Map.Trammel)
Hue = House != null ? 0x47F : 50;
else if (m_TargetMap == Map.Felucca)
Hue = House != null ? 0x66D : 0;
else if (m_TargetMap == Map.Ilshenar)
Hue = House != null ? 0x55F : 1102;
else if (m_TargetMap == Map.Malas)
Hue = House != null ? 0x55F : 1102;
else if (m_TargetMap == Map.Tokuno)
Hue = House != null ? 0x47F : 1154;
}
public void Mark(Mobile m)
{
m_Marked = true;
bool setDesc = false;
if (Core.AOS)
{
m_House = BaseHouse.FindHouseAt(m);
if (m_House == null)
{
Target = m.Location;
m_TargetMap = m.Map;
}
else
{
HouseSign sign = m_House.Sign;
if (sign != null)
m_Description = sign.Name;
else
m_Description = null;
if (m_Description == null || (m_Description = m_Description.Trim()).Length == 0)
m_Description = "an unnamed house";
setDesc = true;
int x = m_House.BanLocation.X;
int y = m_House.BanLocation.Y + 2;
int z = m_House.BanLocation.Z;
Map map = m_House.Map;
if (map?.CanFit(x, y, z, 16, false, false) == false)
z = map.GetAverageZ(x, y);
Target = new Point3D(x, y, z);
m_TargetMap = map;
}
}
else
{
m_House = null;
Target = m.Location;
m_TargetMap = m.Map;
}
if (!setDesc)
m_Description = BaseRegion.GetRuneNameFor(Region.Find(Target, m_TargetMap));
CalculateHue();
InvalidateProperties();
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_Marked)
{
string desc;
if ((desc = m_Description) == null || (desc = desc.Trim()).Length == 0)
desc = "an unknown location";
if (m_TargetMap == Map.Tokuno)
list.Add(House != null ? 1063260 : 1063259, RuneFormat, desc); // ~1_val~ (Tokuno Islands)[(House)]
else if (m_TargetMap == Map.Malas)
list.Add(House != null ? 1062454 : 1060804, RuneFormat, desc); // ~1_val~ (Malas)[(House)]
else if (m_TargetMap == Map.Felucca)
list.Add(House != null ? 1062452 : 1060805, RuneFormat, desc); // ~1_val~ (Felucca)[(House)]
else if (m_TargetMap == Map.Trammel)
list.Add(House != null ? 1062453 : 1060806, RuneFormat, desc); // ~1_val~ (Trammel)[(House)]
else
list.Add(House != null ? "{0} ({1})(House)" : "{0} ({1})", string.Format(RuneFormat, desc), m_TargetMap);
}
}
public override void OnSingleClick(Mobile from)
{
if (m_Marked)
{
string desc;
if ((desc = m_Description) == null || (desc = desc.Trim()).Length == 0)
desc = "an unknown location";
if (m_TargetMap == Map.Tokuno)
LabelTo(from, House != null ? 1063260 : 1063259,
string.Format(RuneFormat, desc)); // ~1_val~ (Tokuno Islands)[(House)]
else if (m_TargetMap == Map.Malas)
LabelTo(from, House != null ? 1062454 : 1060804,
string.Format(RuneFormat, desc)); // ~1_val~ (Malas)[(House)]
else if (m_TargetMap == Map.Felucca)
LabelTo(from, House != null ? 1062452 : 1060805,
string.Format(RuneFormat, desc)); // ~1_val~ (Felucca)[(House)]
else if (m_TargetMap == Map.Trammel)
LabelTo(from, House != null ? 1062453 : 1060806,
string.Format(RuneFormat, desc)); // ~1_val~ (Trammel)[(House)]
else
LabelTo(from, House != null ? "{0} ({1})(House)" : "{0} ({1})", string.Format(RuneFormat, desc),
m_TargetMap);
}
else
{
LabelTo(from, "an unmarked recall rune");
}
}
public override void OnDoubleClick(Mobile from)
{
int number;
if (!IsChildOf(from.Backpack))
{
number = 1042001; // That must be in your pack for you to use it.
}
else if (House != null)
{
number = 1062399; // You cannot edit the description for this rune.
}
else if (m_Marked)
{
number = 501804; // Please enter a description for this marked object.
from.Prompt = new RenamePrompt(this);
}
else
{
number = 501805; // That rune is not yet marked.
}
from.SendLocalizedMessage(number);
}
private class RenamePrompt : Prompt
{
private RecallRune m_Rune;
public RenamePrompt(RecallRune rune)
{
m_Rune = rune;
}
public override void OnResponse(Mobile from, string text)
{
if (m_Rune.House == null && m_Rune.Marked)
{
m_Rune.Description = text;
from.SendLocalizedMessage(1010474); // The etching on the rune has been changed.
}
}
}
}
}

View file

@ -0,0 +1,37 @@
namespace Server.Items
{
public class MysticSpellbook : Spellbook
{
[Constructible]
public MysticSpellbook(ulong content = 0)
: base(content, 0x2D9D)
{
Layer = Layer.OneHanded;
}
public MysticSpellbook(Serial serial)
: base(serial)
{
}
public override SpellbookType SpellbookType => SpellbookType.Mystic;
public override int BookOffset => 677;
public override int BookCount => 16;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
/*int version = */
reader.ReadInt();
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class NecromancerSpellbook : Spellbook
{
[Constructible]
public NecromancerSpellbook(ulong content = 0) : base(content, 0x2253)
{
Layer = Core.ML ? Layer.OneHanded : Layer.Invalid;
}
public NecromancerSpellbook(Serial serial) : base(serial)
{
}
public override SpellbookType SpellbookType => SpellbookType.Necromancer;
public override int BookOffset => 100;
public override int BookCount => Core.SE ? 17 : 16;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0 && Core.ML)
Layer = Layer.OneHanded;
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Server.Items
{
public class AgilityPotion : BaseAgilityPotion
{
[Constructible]
public AgilityPotion() : base(PotionEffect.Agility)
{
}
public AgilityPotion(Serial serial) : base(serial)
{
}
public override int DexOffset => 10;
public override TimeSpan Duration => TimeSpan.FromMinutes(2.0);
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using Server.Engines.ConPVP;
using Server.Spells;
namespace Server.Items
{
public abstract class BaseAgilityPotion : BasePotion
{
public BaseAgilityPotion(PotionEffect effect) : base(0xF08, effect)
{
}
public BaseAgilityPotion(Serial serial) : base(serial)
{
}
public abstract int DexOffset{ get; }
public abstract TimeSpan Duration{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public bool DoAgility(Mobile from)
{
// TODO: Verify scaled; is it offset, duration, or both?
if (SpellHelper.AddStatOffset(from, StatType.Dex, Scale(from, DexOffset), Duration))
{
from.FixedEffect(0x375A, 10, 15);
from.PlaySound(0x1E7);
return true;
}
from.SendLocalizedMessage(502173); // You are already under a similar effect.
return false;
}
public override void Drink(Mobile from)
{
if (DoAgility(from))
{
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Server.Items
{
public class GreaterAgilityPotion : BaseAgilityPotion
{
[Constructible]
public GreaterAgilityPotion() : base(PotionEffect.AgilityGreater)
{
}
public GreaterAgilityPotion(Serial serial) : base(serial)
{
}
public override int DexOffset => 20;
public override TimeSpan Duration => TimeSpan.FromMinutes(2.0);
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,263 @@
using System;
using System.Collections.Generic;
using Server.Engines.ConPVP;
using Server.Engines.Craft;
namespace Server.Items
{
public enum PotionEffect
{
Nightsight,
CureLesser,
Cure,
CureGreater,
Agility,
AgilityGreater,
Strength,
StrengthGreater,
PoisonLesser,
Poison,
PoisonGreater,
PoisonDeadly,
Refresh,
RefreshTotal,
HealLesser,
Heal,
HealGreater,
ExplosionLesser,
Explosion,
ExplosionGreater,
Conflagration,
ConflagrationGreater,
MaskOfDeath, // Mask of Death is not available in OSI but does exist in cliloc files
MaskOfDeathGreater, // included in enumeration for compatibility if later enabled by OSI
ConfusionBlast,
ConfusionBlastGreater,
Invisibility,
Parasitic,
Darkglow
}
public abstract class BasePotion : Item, ICraftable, ICommodity
{
private PotionEffect m_PotionEffect;
public BasePotion(int itemID, PotionEffect effect) : base(itemID)
{
m_PotionEffect = effect;
Stackable = Core.ML;
Weight = 1.0;
}
public BasePotion(Serial serial) : base(serial)
{
}
public PotionEffect PotionEffect
{
get => m_PotionEffect;
set
{
m_PotionEffect = value;
InvalidateProperties();
}
}
public override int LabelNumber => 1041314 + (int)m_PotionEffect;
public virtual bool RequireFreeHand => true;
int ICommodity.DescriptionNumber => LabelNumber;
bool ICommodity.IsDeedable => Core.ML;
#region ICraftable Members
public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool,
CraftItem craftItem, int resHue)
{
if (craftSystem is DefAlchemy)
{
Container pack = from.Backpack;
if (pack != null)
{
if ((int)PotionEffect >= (int)PotionEffect.Invisibility)
return 1;
List<PotionKeg> kegs = pack.FindItemsByType<PotionKeg>();
for (int i = 0; i < kegs.Count; ++i)
{
PotionKeg keg = kegs[i];
// Should never happen
// if (keg == null)
// continue;
if (keg.Held <= 0 || keg.Held >= 100)
continue;
if (keg.Type != PotionEffect)
continue;
++keg.Held;
Consume();
from.AddToBackpack(new Bottle());
return -1; // signal placed in keg
}
}
}
return 1;
}
#endregion
public static bool HasFreeHand(Mobile m)
{
Item handOne = m.FindItemOnLayer(Layer.OneHanded);
Item handTwo = m.FindItemOnLayer(Layer.TwoHanded);
if (handTwo is BaseWeapon)
handOne = handTwo;
if (handTwo is BaseRanged ranged && ranged.Balanced)
return true;
return handOne == null || handTwo == null;
}
public override void OnDoubleClick(Mobile from)
{
if (!Movable)
return;
if (from.InRange(GetWorldLocation(), 1))
{
if (!RequireFreeHand || HasFreeHand(from))
{
if (this is BaseExplosionPotion && Amount > 1)
{
BasePotion pot = (BasePotion)Activator.CreateInstance(GetType());
Amount--;
if (from.Backpack?.Deleted != false)
from.Backpack.DropItem(pot);
else
pot.MoveToWorld(from.Location, from.Map);
pot.Drink(from);
}
else
{
Drink(from);
}
}
else
{
from.SendLocalizedMessage(502172); // You must have a free hand to drink a potion.
}
}
else
{
from.SendLocalizedMessage(502138); // That is too far away for you to use
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.Write((int)m_PotionEffect);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
case 0:
{
m_PotionEffect = (PotionEffect)reader.ReadInt();
break;
}
}
if (version == 0)
Stackable = Core.ML;
}
public abstract void Drink(Mobile from);
public static void PlayDrinkEffect(Mobile m)
{
m.RevealingAction();
m.PlaySound(0x2D6);
#region Dueling
if (!DuelContext.IsFreeConsume(m))
m.AddToBackpack(new Bottle());
#endregion
if (m.Body.IsHuman && !m.Mounted)
m.Animate(34, 5, 1, true, false, 0);
}
public static int EnhancePotions(Mobile m)
{
int EP = AosAttributes.GetValue(m, AosAttribute.EnhancePotions);
int skillBonus = m.Skills.Alchemy.Fixed / 330 * 10;
if (Core.ML && EP > 50 && m.AccessLevel <= AccessLevel.Player)
EP = 50;
return EP + skillBonus;
}
public static TimeSpan Scale(Mobile m, TimeSpan v)
{
if (!Core.AOS)
return v;
double scalar = 1.0 + 0.01 * EnhancePotions(m);
return TimeSpan.FromSeconds(v.TotalSeconds * scalar);
}
public static double Scale(Mobile m, double v)
{
if (!Core.AOS)
return v;
double scalar = 1.0 + 0.01 * EnhancePotions(m);
return v * scalar;
}
public static int Scale(Mobile m, int v)
{
if (!Core.AOS)
return v;
return AOS.Scale(v, 100 + EnhancePotions(m));
}
public override bool StackWith(Mobile from, Item dropped, bool playSound)
{
return dropped is BasePotion potion && potion.m_PotionEffect == m_PotionEffect &&
base.StackWith(from, potion, playSound);
}
}
}

View file

@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using Server.Spells;
using Server.Targeting;
namespace Server.Items
{
public abstract class BaseConflagrationPotion : BasePotion
{
private List<Mobile> m_Users = new List<Mobile>();
public BaseConflagrationPotion(PotionEffect effect) : base(0xF06, effect)
{
Hue = 0x489;
}
public BaseConflagrationPotion(Serial serial) : base(serial)
{
}
public abstract int MinDamage{ get; }
public abstract int MaxDamage{ get; }
public override bool RequireFreeHand => false;
public override void Drink(Mobile from)
{
if (Core.AOS && (from.Paralyzed || from.Frozen || from.Spell != null && from.Spell.IsCasting))
{
from.SendLocalizedMessage(1062725); // You can not use that potion while paralyzed.
return;
}
int delay = GetDelay(from);
if (delay > 0)
{
from.SendLocalizedMessage(1072529,
$"{delay}\t{(delay > 1 ? "seconds." : "second.")}"); // You cannot use that for another ~1_NUM~ ~2_TIMEUNITS~
return;
}
if (from.Target is ThrowTarget targ && targ.Potion == this)
return;
from.RevealingAction();
if (!m_Users.Contains(from))
m_Users.Add(from);
from.Target = new ThrowTarget(this);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public virtual void Explode(Mobile from, Point3D loc, Map map)
{
if (Deleted || map == null)
return;
Consume();
// Check if any other players are using this potion
for (int i = 0; i < m_Users.Count; i++)
if (m_Users[i].Target is ThrowTarget targ && targ.Potion == this)
Target.Cancel(from);
// Effects
Effects.PlaySound(loc, map, 0x20C);
for (int i = -2; i <= 2; i++)
for (int j = -2; j <= 2; j++)
{
Point3D p = new Point3D(loc.X + i, loc.Y + j, loc.Z);
if (map.CanFit(p, 12, true, false) && from.InLOS(p))
new InternalItem(from, p, map, MinDamage, MaxDamage);
}
}
private class ThrowTarget : Target
{
public ThrowTarget(BaseConflagrationPotion potion) : base(12, true, TargetFlags.None)
{
Potion = potion;
}
public BaseConflagrationPotion Potion{ get; }
protected override void OnTarget(Mobile from, object targeted)
{
if (Potion.Deleted || Potion.Map == Map.Internal)
return;
if (!(targeted is IPoint3D p) || from.Map == null)
return;
// Add delay
AddDelay(from);
SpellHelper.GetSurfaceTop(ref p);
from.RevealingAction();
IEntity to;
if (p is Mobile mobile)
to = mobile;
else
to = new Entity(Serial.Zero, new Point3D(p), from.Map);
Effects.SendMovingEffect(from, to, 0xF0D, 7, 0, false, false, Potion.Hue);
Timer.DelayCall(TimeSpan.FromSeconds(1.5), () => Potion.Explode(from, new Point3D(p), from.Map));
}
}
public class InternalItem : Item
{
private DateTime m_End;
private int m_MaxDamage;
private int m_MinDamage;
private Timer m_Timer;
public InternalItem(Mobile from, Point3D loc, Map map, int min, int max) : base(0x398C)
{
Movable = false;
Light = LightType.Circle300;
MoveToWorld(loc, map);
From = from;
m_End = DateTime.UtcNow + TimeSpan.FromSeconds(10);
SetDamage(min, max);
m_Timer = new InternalTimer(this, m_End);
m_Timer.Start();
}
public InternalItem(Serial serial) : base(serial)
{
}
public Mobile From{ get; private set; }
public override bool BlocksFit => true;
public override void OnAfterDelete()
{
base.OnAfterDelete();
m_Timer?.Stop();
}
public int GetDamage()
{
return Utility.RandomMinMax(m_MinDamage, m_MaxDamage);
}
private void SetDamage(int min, int max)
{
/* new way to apply alchemy bonus according to Stratics' calculator.
this gives a mean to values 25, 50, 75 and 100. Stratics' calculator is outdated.
Those goals will give 2 to alchemy bonus. It's not really OSI-like but it's an approximation. */
m_MinDamage = min;
m_MaxDamage = max;
if (From == null)
return;
int alchemySkill = From.Skills.Alchemy.Fixed;
int alchemyBonus = alchemySkill / 125 + alchemySkill / 250;
m_MinDamage = Scale(From, m_MinDamage + alchemyBonus);
m_MaxDamage = Scale(From, m_MaxDamage + alchemyBonus);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(From);
writer.Write(m_End);
writer.Write(m_MinDamage);
writer.Write(m_MaxDamage);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
From = reader.ReadMobile();
m_End = reader.ReadDateTime();
m_MinDamage = reader.ReadInt();
m_MaxDamage = reader.ReadInt();
m_Timer = new InternalTimer(this, m_End);
m_Timer.Start();
}
public override bool OnMoveOver(Mobile m)
{
if (Visible && From != null && (!Core.AOS || m != From) && SpellHelper.ValidIndirectTarget(From, m) &&
From.CanBeHarmful(m, false))
{
From.DoHarmful(m);
AOS.Damage(m, From, GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
}
return true;
}
private class InternalTimer : Timer
{
private DateTime m_End;
private InternalItem m_Item;
public InternalTimer(InternalItem item, DateTime end) : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0))
{
m_Item = item;
m_End = end;
Priority = TimerPriority.FiftyMS;
}
protected override void OnTick()
{
if (m_Item.Deleted)
return;
if (DateTime.UtcNow > m_End)
{
m_Item.Delete();
Stop();
return;
}
Mobile from = m_Item.From;
if (m_Item.Map == null || from == null)
return;
foreach (Mobile m in m_Item.GetMobilesInRange(0))
{
if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != from) &&
SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false))
{
from.DoHarmful(m);
AOS.Damage(m, from, m_Item.GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
}
}
}
}
}
#region Delay
private static Dictionary<Mobile, Timer> m_Delay = new Dictionary<Mobile, Timer>();
public static void AddDelay(Mobile m)
{
m_Delay.TryGetValue(m, out Timer timer);
timer?.Stop();
m_Delay[m] = Timer.DelayCall(TimeSpan.FromSeconds(30), EndDelay, m);
}
public static int GetDelay(Mobile m)
{
if (m_Delay.TryGetValue(m, out Timer timer) && timer.Next > DateTime.UtcNow)
return (int)(timer.Next - DateTime.UtcNow).TotalSeconds;
return 0;
}
public static void EndDelay(Mobile m)
{
if (m_Delay.TryGetValue(m, out Timer timer))
{
timer.Stop();
m_Delay.Remove(m);
}
}
#endregion
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class ConflagrationPotion : BaseConflagrationPotion
{
[Constructible]
public ConflagrationPotion() : base(PotionEffect.Conflagration)
{
}
public ConflagrationPotion(Serial serial) : base(serial)
{
}
public override int MinDamage => 2;
public override int MaxDamage => 4;
public override int LabelNumber => 1072095; // a Conflagration potion
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class GreaterConflagrationPotion : BaseConflagrationPotion
{
[Constructible]
public GreaterConflagrationPotion() : base(PotionEffect.ConflagrationGreater)
{
}
public GreaterConflagrationPotion(Serial serial) : base(serial)
{
}
public override int MinDamage => 4;
public override int MaxDamage => 8;
public override int LabelNumber => 1072098; // a Greater Conflagration potion
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using Server.Misc;
using Server.Mobiles;
using Server.Spells;
using Server.Targeting;
namespace Server.Items
{
public abstract class BaseConfusionBlastPotion : BasePotion
{
private List<Mobile> m_Users = new List<Mobile>();
public BaseConfusionBlastPotion(PotionEffect effect) : base(0xF06, effect)
{
Hue = 0x48D;
}
public BaseConfusionBlastPotion(Serial serial) : base(serial)
{
}
public abstract int Radius{ get; }
public override bool RequireFreeHand => false;
public override void Drink(Mobile from)
{
if (Core.AOS && (from.Paralyzed || from.Frozen || from.Spell != null && from.Spell.IsCasting))
{
from.SendLocalizedMessage(1062725); // You can not use that potion while paralyzed.
return;
}
int delay = GetDelay(from);
if (delay > 0)
{
from.SendLocalizedMessage(1072529,
$"{delay}\t{(delay > 1 ? "seconds." : "second.")}"); // You cannot use that for another ~1_NUM~ ~2_TIMEUNITS~
return;
}
if (from.Target is ThrowTarget targ && targ.Potion == this)
return;
from.RevealingAction();
if (!m_Users.Contains(from))
m_Users.Add(from);
from.Target = new ThrowTarget(this);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public virtual void Explode(Mobile from, Point3D loc, Map map)
{
if (Deleted || map == null)
return;
Consume();
// Check if any other players are using this potion
for (int i = 0; i < m_Users.Count; i++)
if (m_Users[i].Target is ThrowTarget targ && targ.Potion == this)
Target.Cancel(from);
// Effects
Effects.PlaySound(loc, map, 0x207);
Geometry.Circle2D(loc, map, Radius, BlastEffect, 270, 90);
Timer.DelayCall(TimeSpan.FromSeconds(0.3), () => CircleEffect2(loc, map));
foreach (Mobile mobile in map.GetMobilesInRange(loc, Radius))
if (mobile is BaseCreature mon)
{
if (mon.Controlled || mon.Summoned)
continue;
mon.Pacify(from, DateTime.UtcNow + TimeSpan.FromSeconds(5.0)); // TODO check
}
}
private class ThrowTarget : Target
{
public ThrowTarget(BaseConfusionBlastPotion potion) : base(12, true, TargetFlags.None)
{
Potion = potion;
}
public BaseConfusionBlastPotion Potion{ get; }
protected override void OnTarget(Mobile from, object targeted)
{
if (Potion.Deleted || Potion.Map == Map.Internal)
return;
if (!(targeted is IPoint3D p) || from.Map == null)
return;
// Add delay
AddDelay(from);
SpellHelper.GetSurfaceTop(ref p);
from.RevealingAction();
IEntity to;
if (p is Mobile mobile)
to = mobile;
else
to = new Entity(Serial.Zero, new Point3D(p), from.Map);
Effects.SendMovingEffect(from, to, 0xF0D, 7, 0, false, false, Potion.Hue);
Timer.DelayCall(TimeSpan.FromSeconds(1.0), () => Potion.Explode(from, new Point3D(p), from.Map));
}
}
#region Effects
public virtual void BlastEffect(Point3D p, Map map)
{
if (map.CanFit(p, 12, true, false))
Effects.SendLocationEffect(p, map, 0x376A, 4, 9);
}
public void CircleEffect2(Point3D p, Map m)
{
Geometry.Circle2D(p, m, Radius, BlastEffect, 90, 270);
}
#endregion
#region Delay
private static Dictionary<Mobile, Timer> m_Delay = new Dictionary<Mobile, Timer>();
public static void AddDelay(Mobile m)
{
m_Delay.TryGetValue(m, out Timer timer);
timer?.Stop();
m_Delay[m] = Timer.DelayCall(TimeSpan.FromSeconds(60), EndDelay, m);
}
public static int GetDelay(Mobile m)
{
if (m_Delay.TryGetValue(m, out Timer timer) && timer.Next > DateTime.UtcNow)
return (int)(timer.Next - DateTime.UtcNow).TotalSeconds;
return 0;
}
public static void EndDelay(Mobile m)
{
if (m_Delay.TryGetValue(m, out Timer timer))
{
timer.Stop();
m_Delay.Remove(m);
}
}
#endregion
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class ConfusionBlastPotion : BaseConfusionBlastPotion
{
[Constructible]
public ConfusionBlastPotion() : base(PotionEffect.ConfusionBlast)
{
}
public ConfusionBlastPotion(Serial serial) : base(serial)
{
}
public override int Radius => 5;
public override int LabelNumber => 1072105; // a Confusion Blast potion
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class GreaterConfusionBlastPotion : BaseConfusionBlastPotion
{
[Constructible]
public GreaterConfusionBlastPotion() : base(PotionEffect.ConfusionBlastGreater)
{
}
public GreaterConfusionBlastPotion(Serial serial) : base(serial)
{
}
public override int Radius => 7;
public override int LabelNumber => 1072108; // a Greater Confusion Blast potion
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,100 @@
using Server.Engines.ConPVP;
using Server.Spells;
using Server.Spells.Necromancy;
namespace Server.Items
{
public class CureLevelInfo
{
public CureLevelInfo(Poison poison, double chance)
{
Poison = poison;
Chance = chance;
}
public Poison Poison{ get; }
public double Chance{ get; }
}
public abstract class BaseCurePotion : BasePotion
{
public BaseCurePotion(PotionEffect effect) : base(0xF07, effect)
{
}
public BaseCurePotion(Serial serial) : base(serial)
{
}
public abstract CureLevelInfo[] LevelInfo{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public void DoCure(Mobile from)
{
bool cure = false;
CureLevelInfo[] info = LevelInfo;
for (int i = 0; i < info.Length; ++i)
{
CureLevelInfo li = info[i];
if (li.Poison == from.Poison && Scale(from, li.Chance) > Utility.RandomDouble())
{
cure = true;
break;
}
}
if (cure && from.CurePoison(from))
{
from.SendLocalizedMessage(500231); // You feel cured of poison!
from.FixedEffect(0x373A, 10, 15);
from.PlaySound(0x1E0);
}
else if (!cure)
{
from.SendLocalizedMessage(500232); // That potion was not strong enough to cure your ailment!
}
}
public override void Drink(Mobile from)
{
if (TransformationSpellHelper.UnderTransformation(from, typeof(VampiricEmbraceSpell)))
{
from.SendLocalizedMessage(1061652); // The garlic in the potion would surely kill you.
}
else if (from.Poisoned)
{
DoCure(from);
PlayDrinkEffect(from);
from.FixedParticles(0x373A, 10, 15, 5012, EffectLayer.Waist);
from.PlaySound(0x1E0);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
else
{
from.SendLocalizedMessage(1042000); // You are not poisoned.
}
}
}
}

View file

@ -0,0 +1,47 @@
namespace Server.Items
{
public class CurePotion : BaseCurePotion
{
private static CureLevelInfo[] m_OldLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 1.00), // 100% chance to cure lesser poison
new CureLevelInfo(Poison.Regular, 0.75), // 75% chance to cure regular poison
new CureLevelInfo(Poison.Greater, 0.50), // 50% chance to cure greater poison
new CureLevelInfo(Poison.Deadly, 0.15) // 15% chance to cure deadly poison
};
private static CureLevelInfo[] m_AosLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 1.00),
new CureLevelInfo(Poison.Regular, 0.95),
new CureLevelInfo(Poison.Greater, 0.75),
new CureLevelInfo(Poison.Deadly, 0.50),
new CureLevelInfo(Poison.Lethal, 0.25)
};
[Constructible]
public CurePotion() : base(PotionEffect.Cure)
{
}
public CurePotion(Serial serial) : base(serial)
{
}
public override CureLevelInfo[] LevelInfo => Core.AOS ? m_AosLevelInfo : m_OldLevelInfo;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,48 @@
namespace Server.Items
{
public class GreaterCurePotion : BaseCurePotion
{
private static CureLevelInfo[] m_OldLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 1.00), // 100% chance to cure lesser poison
new CureLevelInfo(Poison.Regular, 1.00), // 100% chance to cure regular poison
new CureLevelInfo(Poison.Greater, 1.00), // 100% chance to cure greater poison
new CureLevelInfo(Poison.Deadly, 0.75), // 75% chance to cure deadly poison
new CureLevelInfo(Poison.Lethal, 0.25) // 25% chance to cure lethal poison
};
private static CureLevelInfo[] m_AosLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 1.00),
new CureLevelInfo(Poison.Regular, 1.00),
new CureLevelInfo(Poison.Greater, 1.00),
new CureLevelInfo(Poison.Deadly, 0.95),
new CureLevelInfo(Poison.Lethal, 0.75)
};
[Constructible]
public GreaterCurePotion() : base(PotionEffect.CureGreater)
{
}
public GreaterCurePotion(Serial serial) : base(serial)
{
}
public override CureLevelInfo[] LevelInfo => Core.AOS ? m_AosLevelInfo : m_OldLevelInfo;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,44 @@
namespace Server.Items
{
public class LesserCurePotion : BaseCurePotion
{
private static CureLevelInfo[] m_OldLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 0.75), // 75% chance to cure lesser poison
new CureLevelInfo(Poison.Regular, 0.50), // 50% chance to cure regular poison
new CureLevelInfo(Poison.Greater, 0.15) // 15% chance to cure greater poison
};
private static CureLevelInfo[] m_AosLevelInfo =
{
new CureLevelInfo(Poison.Lesser, 0.75),
new CureLevelInfo(Poison.Regular, 0.50),
new CureLevelInfo(Poison.Greater, 0.25)
};
[Constructible]
public LesserCurePotion() : base(PotionEffect.CureLesser)
{
}
public LesserCurePotion(Serial serial) : base(serial)
{
}
public override CureLevelInfo[] LevelInfo => Core.AOS ? m_AosLevelInfo : m_OldLevelInfo;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,36 @@
namespace Server.Items
{
public class DarkglowPotion : BasePoisonPotion
{
[Constructible]
public DarkglowPotion() : base(PotionEffect.Darkglow)
{
Hue = 0x96;
}
public DarkglowPotion(Serial serial) : base(serial)
{
}
public override Poison Poison => Poison.Greater; /* MUST be restored when prerequisites are done */
public override double MinPoisoningSkill => 95.0;
public override double MaxPoisoningSkill => 100.0;
public override int LabelNumber => 1072849; // Darkglow Poison
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Network;
using Server.Spells;
using Server.Targeting;
namespace Server.Items
{
public abstract class BaseExplosionPotion : BasePotion
{
private const int ExplosionRange = 2; // How long is the blast radius?
private static bool LeveledExplosion = false; // Should explosion potions explode other nearby potions?
private static bool InstantExplosion = false; // Should explosion potions explode on impact?
private static bool RelativeLocation = false; // Is the explosion target location relative for mobiles?
private Timer m_Timer;
public BaseExplosionPotion(PotionEffect effect) : base(0xF0D, effect)
{
}
public BaseExplosionPotion(Serial serial) : base(serial)
{
}
public abstract int MinDamage{ get; }
public abstract int MaxDamage{ get; }
public override bool RequireFreeHand => false;
public List<Mobile> Users{ get; private set; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public virtual IEntity FindParent(Mobile from)
{
if (HeldBy?.Holding == this)
return HeldBy;
if (RootParent != null)
return RootParent;
if (Map == Map.Internal)
return from;
return this;
}
public override void Drink(Mobile from)
{
if (Core.AOS && (from.Paralyzed || from.Frozen || from.Spell != null && from.Spell.IsCasting))
{
from.SendLocalizedMessage(1062725); // You can not use a purple potion while paralyzed.
return;
}
ThrowTarget targ = from.Target as ThrowTarget;
Stackable = false; // Scavenged explosion potions won't stack with those ones in backpack, and still will explode.
if (targ?.Potion == this)
return;
from.RevealingAction();
if (Users == null)
Users = new List<Mobile>();
if (!Users.Contains(from))
Users.Add(from);
from.Target = new ThrowTarget(this);
if (m_Timer == null)
{
from.SendLocalizedMessage(500236); // You should throw it now!
int timer = 3;
if (Core.ML)
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.25), 5,
() => Detonate_OnTick(from, timer--)); // 3.6 seconds explosion delay
else
m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(0.75), TimeSpan.FromSeconds(1.0), 4,
() => Detonate_OnTick(from, timer--)); // 2.6 seconds explosion delay
}
}
private void Detonate_OnTick(Mobile from, int timer)
{
if (Deleted)
return;
IEntity parent = FindParent(from);
if (timer == 0)
{
Point3D loc;
Map map;
if (parent is Item item)
{
loc = item.GetWorldLocation();
map = item.Map;
}
else if (parent is Mobile m)
{
loc = m.Location;
map = m.Map;
}
else
{
return;
}
Explode(from, true, loc, map);
m_Timer = null;
}
else
{
if (parent is Item item)
item.PublicOverheadMessage(MessageType.Regular, 0x22, false, timer.ToString());
else if (parent is Mobile mobile)
mobile.PublicOverheadMessage(MessageType.Regular, 0x22, false, timer.ToString());
}
}
private void Reposition_OnTick(Mobile from, Point3D loc, Map map)
{
if (Deleted)
return;
if (InstantExplosion)
Explode(from, true, loc, map);
else
MoveToWorld(loc, map);
}
public void Explode(Mobile from, bool direct, Point3D loc, Map map)
{
if (Deleted)
return;
Consume();
for (int i = 0; Users != null && i < Users.Count; ++i)
{
Mobile m = Users[i];
if (m.Target is ThrowTarget targ && targ.Potion == this)
Target.Cancel(m);
}
if (map == null)
return;
Effects.PlaySound(loc, map, 0x307);
Effects.SendLocationEffect(loc, map, 0x36B0, 9, 10, 0, 0);
int alchemyBonus = 0;
if (direct)
alchemyBonus = (int)(from.Skills.Alchemy.Value / (Core.AOS ? 5 : 10));
IPooledEnumerable<IEntity> eable = map.GetObjectsInRange(loc, ExplosionRange, LeveledExplosion);
int toDamage = 0;
List<IEntity> toExplode = eable.Where(o =>
{
if (!(o is Mobile mobile) || from != null &&
(!SpellHelper.ValidIndirectTarget(from, mobile) || !from.CanBeHarmful(mobile, false)))
return o is BaseExplosionPotion && o != this;
++toDamage;
return true;
}).ToList();
eable.Free();
int min = Scale(from, MinDamage);
int max = Scale(from, MaxDamage);
for (int i = 0; i < toExplode.Count; ++i)
{
IEntity o = toExplode[i];
if (o is Mobile m)
{
from?.DoHarmful(m);
int damage = Utility.RandomMinMax(min, max);
damage += alchemyBonus;
if (!Core.AOS && damage > 40)
damage = 40;
else if (Core.AOS && toDamage > 2)
damage /= toDamage - 1;
AOS.Damage(m, from, damage, 0, 100, 0, 0, 0);
}
else if (o is BaseExplosionPotion pot)
{
pot.Explode(from, false, pot.GetWorldLocation(), pot.Map);
}
}
}
private class ThrowTarget : Target
{
public ThrowTarget(BaseExplosionPotion potion) : base(12, true, TargetFlags.None)
{
Potion = potion;
}
public BaseExplosionPotion Potion{ get; }
protected override void OnTarget(Mobile from, object targeted)
{
if (Potion.Deleted || Potion.Map == Map.Internal)
return;
if (!(targeted is IPoint3D p))
return;
Map map = from.Map;
if (map == null)
return;
SpellHelper.GetSurfaceTop(ref p);
from.RevealingAction();
IEntity to = new Entity(Serial.Zero, new Point3D(p), map);
if (p is Mobile m)
{
if (!RelativeLocation) // explosion location = current mob location.
p = m.Location;
else
to = m;
}
Effects.SendMovingEffect(from, to, Potion.ItemID, 7, 0, false, false, Potion.Hue);
if (Potion.Amount > 1) Mobile.LiftItemDupe(Potion, 1);
Potion.Internalize();
Timer.DelayCall(TimeSpan.FromSeconds(1.0), () => Potion.Reposition_OnTick(from, new Point3D(p), map));
}
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class ExplosionPotion : BaseExplosionPotion
{
[Constructible]
public ExplosionPotion() : base(PotionEffect.Explosion)
{
}
public ExplosionPotion(Serial serial) : base(serial)
{
}
public override int MinDamage => 10;
public override int MaxDamage => 20;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class GreaterExplosionPotion : BaseExplosionPotion
{
[Constructible]
public GreaterExplosionPotion() : base(PotionEffect.ExplosionGreater)
{
}
public GreaterExplosionPotion(Serial serial) : base(serial)
{
}
public override int MinDamage => Core.AOS ? 20 : 15;
public override int MaxDamage => Core.AOS ? 40 : 30;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class LesserExplosionPotion : BaseExplosionPotion
{
[Constructible]
public LesserExplosionPotion() : base(PotionEffect.ExplosionLesser)
{
}
public LesserExplosionPotion(Serial serial) : base(serial)
{
}
public override int MinDamage => 5;
public override int MaxDamage => 10;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,79 @@
using System;
using Server.Engines.ConPVP;
using Server.Network;
namespace Server.Items
{
public abstract class BaseHealPotion : BasePotion
{
public BaseHealPotion(PotionEffect effect) : base(0xF0C, effect)
{
}
public BaseHealPotion(Serial serial) : base(serial)
{
}
public abstract int MinHeal{ get; }
public abstract int MaxHeal{ get; }
public abstract double Delay{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public void DoHeal(Mobile from)
{
int min = Scale(from, MinHeal);
int max = Scale(from, MaxHeal);
from.Heal(Utility.RandomMinMax(min, max));
}
public override void Drink(Mobile from)
{
if (from.Hits < from.HitsMax)
{
if (from.Poisoned || MortalStrike.IsWounded(from))
{
from.LocalOverheadMessage(MessageType.Regular, 0x22,
1005000); // You can not heal yourself in your current state.
}
else
{
if (from.BeginAction<BaseHealPotion>())
{
DoHeal(from);
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
Timer.DelayCall(TimeSpan.FromSeconds(Delay), from.EndAction<BaseHealPotion>);
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x22,
500235); // You must wait 10 seconds before using another healing potion.
}
}
}
else
{
from.SendLocalizedMessage(
1049547); // You decide against drinking this potion, as you are already at full health.
}
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class GreaterHealPotion : BaseHealPotion
{
[Constructible]
public GreaterHealPotion() : base(PotionEffect.HealGreater)
{
}
public GreaterHealPotion(Serial serial) : base(serial)
{
}
public override int MinHeal => Core.AOS ? 20 : 9;
public override int MaxHeal => Core.AOS ? 25 : 30;
public override double Delay => 10.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class HealPotion : BaseHealPotion
{
[Constructible]
public HealPotion() : base(PotionEffect.Heal)
{
}
public HealPotion(Serial serial) : base(serial)
{
}
public override int MinHeal => Core.AOS ? 13 : 6;
public override int MaxHeal => Core.AOS ? 16 : 20;
public override double Delay => Core.AOS ? 8.0 : 10.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,32 @@
namespace Server.Items
{
public class LesserHealPotion : BaseHealPotion
{
[Constructible]
public LesserHealPotion() : base(PotionEffect.HealLesser)
{
}
public LesserHealPotion(Serial serial) : base(serial)
{
}
public override int MinHeal => Core.AOS ? 6 : 3;
public override int MaxHeal => Core.AOS ? 8 : 10;
public override double Delay => Core.AOS ? 3.0 : 10.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
namespace Server.Items
{
public class InvisibilityPotion : BasePotion
{
private static Dictionary<Mobile, Timer> m_Table = new Dictionary<Mobile, Timer>();
[Constructible]
public InvisibilityPotion() : base(0xF0A, PotionEffect.Invisibility)
{
Hue = 0x48D;
}
public InvisibilityPotion(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1072941; // Potion of Invisibility
public override void Drink(Mobile from)
{
if (from.Hidden)
{
from.SendLocalizedMessage(1073185); // You are already unseen.
return;
}
if (HasTimer(from))
{
from.SendLocalizedMessage(1073186); // An invisibility potion is already taking effect on your person.
return;
}
Consume();
m_Table[from] = Timer.DelayCall(TimeSpan.FromSeconds(2), Hide, from);
PlayDrinkEffect(from);
}
public static void Hide(Mobile m)
{
Effects.SendLocationParticles(
EffectItem.Create(new Point3D(m.X, m.Y, m.Z + 16), m.Map, EffectItem.DefaultDuration), 0x376A, 10, 15, 5045);
m.PlaySound(0x3C4);
m.Hidden = true;
BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth);
BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825)); //Invisibility/Invisible
RemoveTimer(m);
Timer.DelayCall(TimeSpan.FromSeconds(30), EndHide, m);
}
public static void EndHide(Mobile m)
{
m.RevealingAction();
RemoveTimer(m);
}
public static bool HasTimer(Mobile m)
{
return m_Table.ContainsKey(m);
}
public static void RemoveTimer(Mobile m, bool interrupted = false)
{
if (m_Table.TryGetValue(m, out Timer timer))
{
if (interrupted)
m.SendLocalizedMessage(1073187); // The invisibility effect is interrupted.
timer.Stop();
m_Table.Remove(m);
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,51 @@
using Server.Engines.ConPVP;
namespace Server.Items
{
public class NightSightPotion : BasePotion
{
[Constructible]
public NightSightPotion() : base(0xF06, PotionEffect.Nightsight)
{
}
public NightSightPotion(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void Drink(Mobile from)
{
if (from.BeginAction<LightCycle>())
{
new LightCycle.NightSightTimer(from).Start();
from.LightLevel = LightCycle.DungeonLevel / 2;
from.FixedParticles(0x376A, 9, 32, 5007, EffectLayer.Waist);
from.PlaySound(0x1E3);
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
else
{
from.SendMessage("You already have nightsight.");
}
}
}
}

View file

@ -0,0 +1,37 @@
namespace Server.Items
{
public class ParasiticPotion : BasePoisonPotion
{
[Constructible]
public ParasiticPotion() : base(PotionEffect.Parasitic)
{
Hue = 0x17C;
}
public ParasiticPotion(Serial serial) : base(serial)
{
}
/* public override Poison Poison => Poison.Darkglow; MUST be restored when prerequisites are done */
public override Poison Poison => Poison.Greater;
public override double MinPoisoningSkill => 95.0;
public override double MaxPoisoningSkill => 100.0;
public override int LabelNumber => 1072848; // Parasitic Poison
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,49 @@
using Server.Engines.ConPVP;
namespace Server.Items
{
public abstract class BasePoisonPotion : BasePotion
{
public BasePoisonPotion(PotionEffect effect) : base(0xF0A, effect)
{
}
public BasePoisonPotion(Serial serial) : base(serial)
{
}
public abstract Poison Poison{ get; }
public abstract double MinPoisoningSkill{ get; }
public abstract double MaxPoisoningSkill{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public void DoPoison(Mobile from)
{
from.ApplyPoison(from, Poison);
}
public override void Drink(Mobile from)
{
DoPoison(from);
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class DeadlyPoisonPotion : BasePoisonPotion
{
[Constructible]
public DeadlyPoisonPotion() : base(PotionEffect.PoisonDeadly)
{
}
public DeadlyPoisonPotion(Serial serial) : base(serial)
{
}
public override Poison Poison => Poison.Deadly;
public override double MinPoisoningSkill => 95.0;
public override double MaxPoisoningSkill => 100.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class GreaterPoisonPotion : BasePoisonPotion
{
[Constructible]
public GreaterPoisonPotion() : base(PotionEffect.PoisonGreater)
{
}
public GreaterPoisonPotion(Serial serial) : base(serial)
{
}
public override Poison Poison => Poison.Greater;
public override double MinPoisoningSkill => 60.0;
public override double MaxPoisoningSkill => 100.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class LesserPoisonPotion : BasePoisonPotion
{
[Constructible]
public LesserPoisonPotion() : base(PotionEffect.PoisonLesser)
{
}
public LesserPoisonPotion(Serial serial) : base(serial)
{
}
public override Poison Poison => Poison.Lesser;
public override double MinPoisoningSkill => 0.0;
public override double MaxPoisoningSkill => 60.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,33 @@
namespace Server.Items
{
public class PoisonPotion : BasePoisonPotion
{
[Constructible]
public PoisonPotion() : base(PotionEffect.Poison)
{
}
public PoisonPotion(Serial serial) : base(serial)
{
}
public override Poison Poison => Poison.Regular;
public override double MinPoisoningSkill => 30.0;
public override double MaxPoisoningSkill => 70.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,48 @@
using Server.Engines.ConPVP;
namespace Server.Items
{
public abstract class BaseRefreshPotion : BasePotion
{
public BaseRefreshPotion(PotionEffect effect) : base(0xF0B, effect)
{
}
public BaseRefreshPotion(Serial serial) : base(serial)
{
}
public abstract double Refresh{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public override void Drink(Mobile from)
{
if (from.Stam < from.StamMax)
{
from.Stam += Scale(from, (int)(Refresh * from.StamMax));
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
else
{
from.SendMessage("You decide against drinking this potion, as you are already at full stamina.");
}
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class RefreshPotion : BaseRefreshPotion
{
[Constructible]
public RefreshPotion() : base(PotionEffect.Refresh)
{
}
public RefreshPotion(Serial serial) : base(serial)
{
}
public override double Refresh => 0.25;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,30 @@
namespace Server.Items
{
public class TotalRefreshPotion : BaseRefreshPotion
{
[Constructible]
public TotalRefreshPotion() : base(PotionEffect.RefreshTotal)
{
}
public TotalRefreshPotion(Serial serial) : base(serial)
{
}
public override double Refresh => 1.0;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using Server.Engines.ConPVP;
using Server.Spells;
namespace Server.Items
{
public abstract class BaseStrengthPotion : BasePotion
{
public BaseStrengthPotion(PotionEffect effect) : base(0xF09, effect)
{
}
public BaseStrengthPotion(Serial serial) : base(serial)
{
}
public abstract int StrOffset{ get; }
public abstract TimeSpan Duration{ get; }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public bool DoStrength(Mobile from)
{
// TODO: Verify scaled; is it offset, duration, or both?
if (SpellHelper.AddStatOffset(from, StatType.Str, Scale(from, StrOffset), Duration))
{
from.FixedEffect(0x375A, 10, 15);
from.PlaySound(0x1E7);
return true;
}
from.SendLocalizedMessage(502173); // You are already under a similar effect.
return false;
}
public override void Drink(Mobile from)
{
if (DoStrength(from))
{
PlayDrinkEffect(from);
if (!DuelContext.IsFreeConsume(from))
Consume();
}
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Server.Items
{
public class GreaterStrengthPotion : BaseStrengthPotion
{
[Constructible]
public GreaterStrengthPotion() : base(PotionEffect.StrengthGreater)
{
}
public GreaterStrengthPotion(Serial serial) : base(serial)
{
}
public override int StrOffset => 20;
public override TimeSpan Duration => TimeSpan.FromMinutes(2.0);
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Server.Items
{
public class StrengthPotion : BaseStrengthPotion
{
[Constructible]
public StrengthPotion() : base(PotionEffect.Strength)
{
}
public StrengthPotion(Serial serial) : base(serial)
{
}
public override int StrOffset => 10;
public override TimeSpan Duration => TimeSpan.FromMinutes(2.0);
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,484 @@
using System;
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Engines.Craft;
using Server.Gumps;
using Server.Mobiles;
using Server.Multis;
using Server.Network;
namespace Server.Items
{
public class Runebook : Item, ISecurable, ICraftable
{
public static readonly TimeSpan UseDelay = TimeSpan.FromSeconds(7.0);
private Mobile m_Crafter;
private int m_DefaultIndex;
private string m_Description;
private BookQuality m_Quality;
[Constructible]
public Runebook() : this(Core.SE ? 12 : 6)
{
}
[Constructible]
public Runebook(int maxCharges) : base(Core.AOS ? 0x22C5 : 0xEFA)
{
Weight = Core.SE ? 1.0 : 3.0;
LootType = LootType.Blessed;
Hue = 0x461;
Layer = Core.AOS ? Layer.Invalid : Layer.OneHanded;
Entries = new List<RunebookEntry>();
MaxCharges = maxCharges;
m_DefaultIndex = -1;
Level = SecureLevel.CoOwners;
}
public Runebook(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public BookQuality Quality
{
get => m_Quality;
set
{
m_Quality = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public DateTime NextUse{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Crafter
{
get => m_Crafter;
set
{
m_Crafter = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public string Description
{
get => m_Description;
set
{
m_Description = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int CurCharges{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int MaxCharges{ get; set; }
public List<Mobile> Openers{ get; set; } = new List<Mobile>();
public override int LabelNumber => 1041267; // runebook
public List<RunebookEntry> Entries{ get; private set; }
public RunebookEntry Default
{
get
{
if (m_DefaultIndex >= 0 && m_DefaultIndex < Entries.Count)
return Entries[m_DefaultIndex];
return null;
}
set
{
if (value == null)
m_DefaultIndex = -1;
else
m_DefaultIndex = Entries.IndexOf(value);
}
}
public override bool DisplayLootType => Core.AOS;
#region ICraftable Members
public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool,
CraftItem craftItem, int resHue)
{
int charges = 5 + quality + (int)(from.Skills.Inscribe.Value / 30);
if (charges > 10)
charges = 10;
MaxCharges = Core.SE ? charges * 2 : charges;
if (makersMark)
Crafter = from;
m_Quality = (BookQuality)(quality - 1);
return quality;
}
#endregion
[CommandProperty(AccessLevel.GameMaster)]
public SecureLevel Level{ get; set; }
public override bool AllowEquippedCast(Mobile from)
{
return true;
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
SetSecureLevelEntry.AddTo(from, this, list);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(3);
writer.Write((byte)m_Quality);
writer.Write(m_Crafter);
writer.Write((int)Level);
writer.Write(Entries.Count);
for (int i = 0; i < Entries.Count; ++i)
Entries[i].Serialize(writer);
writer.Write(m_Description);
writer.Write(CurCharges);
writer.Write(MaxCharges);
writer.Write(m_DefaultIndex);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
LootType = LootType.Blessed;
if (Core.SE && Weight == 3.0)
Weight = 1.0;
int version = reader.ReadInt();
switch (version)
{
case 3:
{
m_Quality = (BookQuality)reader.ReadByte();
goto case 2;
}
case 2:
{
m_Crafter = reader.ReadMobile();
goto case 1;
}
case 1:
{
Level = (SecureLevel)reader.ReadInt();
goto case 0;
}
case 0:
{
int count = reader.ReadInt();
Entries = new List<RunebookEntry>(count);
for (int i = 0; i < count; ++i)
Entries.Add(new RunebookEntry(reader));
m_Description = reader.ReadString();
CurCharges = reader.ReadInt();
MaxCharges = reader.ReadInt();
m_DefaultIndex = reader.ReadInt();
break;
}
}
}
public void DropRune(Mobile from, RunebookEntry e, int index)
{
if (m_DefaultIndex > index)
m_DefaultIndex -= 1;
else if (m_DefaultIndex == index)
m_DefaultIndex = -1;
Entries.RemoveAt(index);
RecallRune rune = new RecallRune();
rune.Target = e.Location;
rune.TargetMap = e.Map;
rune.Description = e.Description;
rune.House = e.House;
rune.Marked = true;
from.AddToBackpack(rune);
from.SendLocalizedMessage(502421); // You have removed the rune.
}
public bool IsOpen(Mobile toCheck)
{
NetState ns = toCheck.NetState;
if (ns == null)
return false;
foreach (Gump gump in ns.Gumps)
if (gump is RunebookGump bookGump && bookGump.Book == this)
return true;
return false;
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_Quality == BookQuality.Exceptional)
list.Add(1063341); // exceptional
if (m_Crafter != null)
list.Add(1050043, m_Crafter.Name); // crafted by ~1_NAME~
if (!string.IsNullOrEmpty(m_Description))
list.Add(m_Description);
}
public override bool OnDragLift(Mobile from)
{
if (from.HasGump<RunebookGump>())
{
from.SendLocalizedMessage(500169); // You cannot pick that up.
return false;
}
foreach (Mobile m in Openers)
if (IsOpen(m))
m.CloseGump<RunebookGump>();
Openers.Clear();
return true;
}
public override void OnSingleClick(Mobile from)
{
if (m_Description?.Length > 0)
LabelTo(from, m_Description);
base.OnSingleClick(from);
if (m_Crafter != null)
LabelTo(from, 1050043, m_Crafter.Name);
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(GetWorldLocation(), Core.ML ? 3 : 1) && CheckAccess(from))
{
if (RootParent is BaseCreature)
{
from.SendLocalizedMessage(502402); // That is inaccessible.
return;
}
if (DateTime.UtcNow < NextUse)
{
from.SendLocalizedMessage(502406); // This book needs time to recharge.
return;
}
from.CloseGump<RunebookGump>();
from.SendGump(new RunebookGump(from, this));
Openers.Add(from);
}
}
public virtual void OnTravel()
{
if (!Core.SA)
NextUse = DateTime.UtcNow + UseDelay;
}
public override void OnAfterDuped(Item newItem)
{
if (!(newItem is Runebook book))
return;
book.Entries = new List<RunebookEntry>();
for (int i = 0; i < Entries.Count; i++)
{
RunebookEntry entry = Entries[i];
book.Entries.Add(new RunebookEntry(entry.Location, entry.Map, entry.Description, entry.House));
}
}
public bool CheckAccess(Mobile m)
{
if (!IsLockedDown || m.AccessLevel >= AccessLevel.GameMaster)
return true;
BaseHouse house = BaseHouse.FindHouseAt(this);
return (house?.IsAosRules != true || house.Public && !house.IsBanned(m) || house.HasAccess(m)) &&
house?.HasSecureAccess(m, Level) == true;
}
public override bool OnDragDrop(Mobile from, Item dropped)
{
if (dropped is RecallRune rune)
{
if (IsLockedDown && from.AccessLevel < AccessLevel.GameMaster)
{
from.SendLocalizedMessage(502413, null, 0x35); // That cannot be done while the book is locked down.
}
else if (IsOpen(from))
{
from.SendLocalizedMessage(1005571); // You cannot place objects in the book while viewing the contents.
}
else if (Entries.Count < 16)
{
if (rune.Marked && rune.TargetMap != null)
{
Entries.Add(new RunebookEntry(rune.Target, rune.TargetMap, rune.Description, rune.House));
rune.Delete();
from.Send(new PlaySound(0x42, GetWorldLocation()));
string desc = rune.Description;
if (desc == null || (desc = desc.Trim()).Length == 0)
desc = "(indescript)";
from.SendMessage(desc);
return true;
}
from.SendLocalizedMessage(502409); // This rune does not have a marked location.
}
else
{
from.SendLocalizedMessage(502401); // This runebook is full.
}
}
else if (dropped is RecallScroll)
{
if (CurCharges < MaxCharges)
{
from.Send(new PlaySound(0x249, GetWorldLocation()));
int amount = dropped.Amount;
if (amount > MaxCharges - CurCharges)
{
dropped.Consume(MaxCharges - CurCharges);
CurCharges = MaxCharges;
}
else
{
CurCharges += amount;
dropped.Delete();
return true;
}
}
else
{
from.SendLocalizedMessage(502410); // This book already has the maximum amount of charges.
}
}
return false;
}
}
public class RunebookEntry
{
public RunebookEntry(Point3D loc, Map map, string desc, BaseHouse house = null)
{
Location = loc;
Map = map;
Description = desc;
House = house;
}
public RunebookEntry(GenericReader reader)
{
int version = reader.ReadByte();
switch (version)
{
case 1:
{
House = reader.ReadItem() as BaseHouse;
goto case 0;
}
case 0:
{
Location = reader.ReadPoint3D();
Map = reader.ReadMap();
Description = reader.ReadString();
break;
}
}
}
public Point3D Location{ get; }
public Map Map{ get; }
public string Description{ get; }
public BaseHouse House{ get; }
public void Serialize(GenericWriter writer)
{
if (House?.Deleted == false)
{
writer.Write((byte)1); // version
writer.Write(House);
}
else
{
writer.Write((byte)0); // version
}
writer.Write(Location);
writer.Write(Map);
writer.Write(Description);
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class EarthquakeScroll : SpellScroll
{
[Constructible]
public EarthquakeScroll(int amount = 1) : base(56, 0x1F65, amount)
{
}
public EarthquakeScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class EnergyVortexScroll : SpellScroll
{
[Constructible]
public EnergyVortexScroll(int amount = 1) : base(57, 0x1F66, amount)
{
}
public EnergyVortexScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ResurrectionScroll : SpellScroll
{
[Constructible]
public ResurrectionScroll(int amount = 1) : base(58, 0x1F67, amount)
{
}
public ResurrectionScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonAirElementalScroll : SpellScroll
{
[Constructible]
public SummonAirElementalScroll(int amount = 1) : base(59, 0x1F68, amount)
{
}
public SummonAirElementalScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonDaemonScroll : SpellScroll
{
[Constructible]
public SummonDaemonScroll(int amount = 1) : base(60, 0x1F69, amount)
{
}
public SummonDaemonScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonEarthElementalScroll : SpellScroll
{
[Constructible]
public SummonEarthElementalScroll(int amount = 1) : base(61, 0x1F6A, amount)
{
}
public SummonEarthElementalScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonFireElementalScroll : SpellScroll
{
[Constructible]
public SummonFireElementalScroll(int amount = 1) : base(62, 0x1F6B, amount)
{
}
public SummonFireElementalScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonWaterElementalScroll : SpellScroll
{
[Constructible]
public SummonWaterElementalScroll(int amount = 1) : base(63, 0x1F6C, amount)
{
}
public SummonWaterElementalScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class BladeSpiritsScroll : SpellScroll
{
[Constructible]
public BladeSpiritsScroll(int amount = 1) : base(32, 0x1F4D, amount)
{
}
public BladeSpiritsScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class DispelFieldScroll : SpellScroll
{
[Constructible]
public DispelFieldScroll(int amount = 1) : base(33, 0x1F4E, amount)
{
}
public DispelFieldScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class IncognitoScroll : SpellScroll
{
[Constructible]
public IncognitoScroll(int amount = 1) : base(34, 0x1F4F, amount)
{
}
public IncognitoScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class MagicReflectScroll : SpellScroll
{
[Constructible]
public MagicReflectScroll(int amount = 1) : base(35, 0x1F50, amount)
{
}
public MagicReflectScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class MindBlastScroll : SpellScroll
{
[Constructible]
public MindBlastScroll(int amount = 1) : base(36, 0x1F51, amount)
{
}
public MindBlastScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ParalyzeScroll : SpellScroll
{
[Constructible]
public ParalyzeScroll(int amount = 1) : base(37, 0x1F52, amount)
{
}
public ParalyzeScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class PoisonFieldScroll : SpellScroll
{
[Constructible]
public PoisonFieldScroll(int amount = 1) : base(38, 0x1F53, amount)
{
}
public PoisonFieldScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class SummonCreatureScroll : SpellScroll
{
[Constructible]
public SummonCreatureScroll(int amount = 1) : base(39, 0x1F54, amount)
{
}
public SummonCreatureScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ClumsyScroll : SpellScroll
{
[Constructible]
public ClumsyScroll(int amount = 1) : base(0, 0x1F2E, amount)
{
}
public ClumsyScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class CreateFoodScroll : SpellScroll
{
[Constructible]
public CreateFoodScroll(int amount = 1) : base(1, 0x1F2F, amount)
{
}
public CreateFoodScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class FeeblemindScroll : SpellScroll
{
[Constructible]
public FeeblemindScroll(int amount = 1) : base(2, 0x1F30, amount)
{
}
public FeeblemindScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class HealScroll : SpellScroll
{
[Constructible]
public HealScroll(int amount = 1) : base(3, 0x1F31, amount)
{
}
public HealScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class MagicArrowScroll : SpellScroll
{
[Constructible]
public MagicArrowScroll(int amount = 1) : base(4, 0x1F32, amount)
{
}
public MagicArrowScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class NightSightScroll : SpellScroll
{
[Constructible]
public NightSightScroll(int amount = 1) : base(5, 0x1F33, amount)
{
}
public NightSightScroll(Serial ser) : base(ser)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ReactiveArmorScroll : SpellScroll
{
[Constructible]
public ReactiveArmorScroll(int amount = 1) : base(6, 0x1F2D, amount)
{
}
public ReactiveArmorScroll(Serial ser) : base(ser)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class WeakenScroll : SpellScroll
{
[Constructible]
public WeakenScroll(int amount = 1) : base(7, 0x1F34, amount)
{
}
public WeakenScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ArchProtectionScroll : SpellScroll
{
[Constructible]
public ArchProtectionScroll(int amount = 1) : base(25, 0x1F46, amount)
{
}
public ArchProtectionScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ArchCureScroll : SpellScroll
{
[Constructible]
public ArchCureScroll(int amount = 1) : base(24, 0x1F45, amount)
{
}
public ArchCureScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class CurseScroll : SpellScroll
{
[Constructible]
public CurseScroll(int amount = 1) : base(26, 0x1F47, amount)
{
}
public CurseScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class FireFieldScroll : SpellScroll
{
[Constructible]
public FireFieldScroll(int amount = 1) : base(27, 0x1F48, amount)
{
}
public FireFieldScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class GreaterHealScroll : SpellScroll
{
[Constructible]
public GreaterHealScroll(int amount = 1) : base(28, 0x1F49, amount)
{
}
public GreaterHealScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class LightningScroll : SpellScroll
{
[Constructible]
public LightningScroll(int amount = 1) : base(29, 0x1F4A, amount)
{
}
public LightningScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class ManaDrainScroll : SpellScroll
{
[Constructible]
public ManaDrainScroll(int amount = 1) : base(30, 0x1F4B, amount)
{
}
public ManaDrainScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,28 @@
namespace Server.Items
{
public class RecallScroll : SpellScroll
{
[Constructible]
public RecallScroll(int amount = 1) : base(31, 0x1F4C, amount)
{
}
public RecallScroll(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

Some files were not shown because too many files have changed in this diff Show more