Files
abysmal-isle/Scripts/Services/Expansions/High Seas/Items/Cannons and Ammo/Cannon.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

1492 lines
52 KiB
C#

using Server;
using System;
using Server.Mobiles;
using Server.ContextMenus;
using Server.Targeting;
using System.Collections.Generic;
using Server.Gumps;
using Server.Misc;
using Server.Multis;
using Server.Engines.Quests;
using System.Linq;
namespace Server.Items
{
public enum ShipPosition
{
Bow,
BowPort,
BowStarboard,
AmidShipPort,
AmidShipStarboard,
AftPort,
AftStarboard,
Aft
}
public interface IShipCannon : IEntity
{
int Hits { get; set; }
int Range { get; }
AmmunitionType AmmoType { get; set; }
BaseGalleon Galleon { get; set; }
DamageLevel DamageState { get; set; }
Direction Facing { get; }
ShipPosition Position { get; set; }
ShipCannonDeed GetDeed { get; }
bool CanLight { get; }
Direction GetFacing();
void OnDamage(int damage, Mobile shooter);
void LightFuse(Mobile from);
void Shoot(object cannoneer);
void DoAreaMessage(int cliloc, int range, Mobile from);
}
public abstract class BaseCannon : Item, IShipCannon
{
private int m_Hits;
private bool m_Cleaned;
private bool m_Charged;
private bool m_Primed;
private Type m_LoadedAmmo;
private BaseGalleon m_Galleon;
private AmmunitionType m_AmmoType;
private ShipPosition m_Position;
private DamageLevel m_DamageState;
[CommandProperty(AccessLevel.GameMaster)]
public int Hits { get { return m_Hits; } set { m_Hits = value; InvalidateDamageState(); } }
[CommandProperty(AccessLevel.GameMaster)]
public bool Cleaned { get { return m_Cleaned; } set { m_Cleaned = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public bool Charged { get { return m_Charged; } set { m_Charged = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public bool Primed { get { return m_Primed; } set { m_Primed = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public Type LoadedAmmo { get { return m_LoadedAmmo; } set { m_LoadedAmmo = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public BaseGalleon Galleon { get { return m_Galleon; } set { m_Galleon = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public AmmunitionType AmmoType { get { return m_AmmoType; } set { m_AmmoType = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public ShipPosition Position { get { return m_Position; } set { m_Position = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public DamageLevel DamageState { get { return m_DamageState; } set { m_DamageState = value; } }
[CommandProperty(AccessLevel.GameMaster)]
public Direction Facing { get { return GetFacing(); } }
public virtual ShipCannonDeed GetDeed { get { return null; } }
public virtual bool HitMultipleMobs { get { return false; } }
public virtual int Range { get { return 0; } }
public virtual int MaxHits { get { return 100; } }
public virtual TimeSpan ActionTime { get { return TimeSpan.FromSeconds(1.5); } }
public virtual Type[] LoadTypes { get { return null; } }
[CommandProperty(AccessLevel.GameMaster)]
public bool CanLight { get { return m_Cleaned && m_Charged && m_Primed && m_AmmoType != AmmunitionType.Empty && m_LoadedAmmo != null; } }
[CommandProperty(AccessLevel.GameMaster)]
public double Durability { get { return ((double)m_Hits / (double)MaxHits) * 100.0; } }
public override bool ForceShowProperties { get { return true; } }
public BaseCannon(BaseGalleon galleon)
{
Movable = false;
m_Cleaned = true;
m_Charged = false;
m_Primed = false;
m_Galleon = galleon;
m_AmmoType = AmmunitionType.Empty;
m_Hits = MaxHits;
m_DamageState = DamageLevel.Pristine;
}
public override bool HandlesOnMovement { get { return true; } }
public override void OnMovement(Mobile m, Point3D oldLocation)
{
Gump g = m.FindGump(typeof(CannonGump));
if (g != null && g is CannonGump && ((CannonGump)g).Cannon == this && !m.InRange(this.Location, 3))
m.CloseGump(typeof(CannonGump));
}
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(this.Location, 3))
from.SendLocalizedMessage(1149687); //You are too far away.
else if (m_Galleon.GetSecurityLevel(from) >= SecurityLevel.Crewman)
{
ResendGump(from);
switch (m_DamageState)
{
case DamageLevel.Slightly: from.SendLocalizedMessage(1116606); break;//The cannon is lightly damaged and needs some minor repair.
case DamageLevel.Moderately: from.SendLocalizedMessage(1116607); break;//The cannon is moderately damaged and needs some repairs. This cannon is safe to fire.
case DamageLevel.Severely: from.SendLocalizedMessage(1116608); break;//The cannon is severely damaged. It needs major repairs before it is safe to fire.
}
}
else
from.SendMessage("Only the ship crew can operate the cannon!");
}
public Direction GetFacing()
{
if (ItemID == 16918 || ItemID == 16922)
return Direction.South;
if (ItemID == 16919 || ItemID == 16923)
return Direction.West;
if (ItemID == 16920 || ItemID == 16924)
return Direction.North;
if (ItemID == 16921 || ItemID == 16925)
return Direction.East;
return Direction.North;
}
public virtual int GetDamage(AmmoInfo info)
{
return Utility.RandomMinMax(info.MinDamage, info.MaxDamage);
}
public virtual bool TryLoadAmmo(Item ammo)
{
return false;
}
public void DoAreaMessage(int cliloc, int range, Mobile from)
{
if (from == null)
return;
IPooledEnumerable eable = this.GetMobilesInRange(6);
foreach (Mobile mob in eable)
{
if (mob is PlayerMobile && mob.InLOS(this))
{
if (from != null)
mob.SendLocalizedMessage(cliloc, from.Name);
else
mob.SendLocalizedMessage(cliloc);
}
}
eable.Free();
}
public void TryLightFuse(Mobile from)
{
if (from == null)
return;
Container pack = from.Backpack;
if (pack != null)
{
Item[] items = pack.FindItemsByType(typeof(Matches));
if (items != null)
{
foreach (Item item in items)
{
if (item is Matches && ((Matches)item).IsLight)
{
LightFuse(from);
return;
}
}
}
items = pack.FindItemsByType(typeof(Torch));
if (items != null)
{
foreach (Item item in items)
{
if (item is Torch && ((Torch)item).Burning)
{
LightFuse(from);
return;
}
}
}
}
Item i = from.FindItemOnLayer(Layer.TwoHanded);
if (i != null && i is Matches && ((Matches)i).IsLight)
{
LightFuse(from);
return;
}
else if (i != null && i is Torch && ((Torch)i).Burning)
{
LightFuse(from);
return;
}
AddAction(from, 1149669); //Need a lighted match.
}
public void LightFuse(Mobile from)
{
if (!CheckRegion(from))
return;
switch (m_DamageState)
{
case DamageLevel.Pristine:
break;
case DamageLevel.Severely:
from.SendLocalizedMessage(1116608); //The cannon is severely damaged. It needs major repairs before it is safe to fire.
return;
case DamageLevel.Heavily:
case DamageLevel.Moderately:
from.SendLocalizedMessage(1116607); //The cannon is moderately damaged and needs some repairs. This cannon is safe to fire.
break;
default:
from.SendLocalizedMessage(1116606); //The cannon is lightly damaged and needs some minor repair.
break;
}
DoAreaMessage(1116080, 10, from);
AddAction(from, 1149683); //The fuse is lit!
Effects.PlaySound(this.Location, this.Map, 0x666);
Timer.DelayCall(TimeSpan.FromSeconds(2), new TimerStateCallback(Shoot), from);
}
public bool CheckRegion(Mobile from)
{
Region r = Region.Find(from.Location, from.Map);
if (r is Server.Regions.GuardedRegion && !((Server.Regions.GuardedRegion)r).IsDisabled())
{
from.SendMessage("You are forbidden from discharging cannons within the town limits.");
return false;
}
return true;
}
public virtual void Shoot(object cannoneer)
{
AmmoInfo ammo = AmmoInfo.GetAmmoInfo(m_LoadedAmmo);
if (ammo == null)
return;
Mobile shooter = null;
if (cannoneer is Mobile)
shooter = (Mobile)cannoneer;
if (shooter != null && shooter.Player)
m_Hits -= Utility.RandomMinMax(0, 4);
DoShootEffects();
AddAction(shooter, 1149691); //Fired successfully.
int xOffset = 0; int yOffset = 0;
int currentRange = 0;
Point3D pnt = this.Location;
Map map = this.Map;
Direction d = GetFacing();
switch (d)
{
case Direction.North:
xOffset = 0; yOffset = -1; break;
case Direction.South:
xOffset = 0; yOffset = 1; break;
case Direction.West:
xOffset = -1; yOffset = 0; break;
case Direction.East:
xOffset = 1; yOffset = 0; break;
}
int xo = xOffset;
int yo = yOffset;
int lateralOffset = 1;
int latDist = ammo.LateralOffset;
int range = Range;
bool hit = false;
while (currentRange++ <= range)
{
xOffset = xo;
yOffset = yo;
if (currentRange % latDist == 0)
lateralOffset++;
TimeSpan delay = TimeSpan.FromSeconds((double)currentRange / 10.0);
Type ammoType = m_LoadedAmmo;
switch (m_AmmoType)
{
case AmmunitionType.Empty: break;
case AmmunitionType.Cannonball:
{
Point3D newPoint = pnt;
List<IEntity> list = new List<IEntity>();
List<Mobile> mobs = new List<Mobile>();
for (int i = -lateralOffset; i <= lateralOffset; i++)
{
if (xOffset == 0)
newPoint = new Point3D(pnt.X + (xOffset + i), pnt.Y + (yOffset * currentRange), pnt.Z);
else
newPoint = new Point3D(pnt.X + (xOffset * currentRange), pnt.Y + (yOffset + i), pnt.Z);
//For Testing
/*if (i == -lateralOffset || i == lateralOffset)
{
Effects.SendLocationParticles(EffectItem.Create(newPoint, this.Map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
}*/
BaseGalleon g = FindValidBoatTarget(newPoint, map, ammo);
if (g != null && g != m_Galleon && g.IsEnemy(m_Galleon))
list.Add(g);
mobs.AddRange(FindMobiles(shooter, newPoint, map, false, false, false, true));
}
foreach (Mobile m in mobs)
list.Add(m);
if (list.Count > 0)
{
IEntity toHit = list[Utility.Random(list.Count)];
if (toHit is Mobile)
{
Timer.DelayCall(delay, new TimerStateCallback(OnMobileHit), new object[] { mobs, newPoint, ammo, shooter });
hit = true;
}
else if (toHit is BaseGalleon)
{
Timer.DelayCall(delay, new TimerStateCallback(OnShipHit), new object[] { (BaseGalleon)toHit, newPoint, ammo, shooter });
hit = true;
}
}
}
break;
case AmmunitionType.Grapeshot:
{
Point3D newPoint = pnt;
List<Mobile> mobiles = new List<Mobile>();
for (int i = -lateralOffset; i <= lateralOffset; i++)
{
if (xOffset == 0)
newPoint = new Point3D(pnt.X + (xOffset + i), pnt.Y + (yOffset * currentRange), pnt.Z);
else
newPoint = new Point3D(pnt.X + (xOffset * currentRange), pnt.Y + (yOffset + i), pnt.Z);
mobiles.AddRange(FindMobiles(shooter, newPoint, map, true, true, true, true));
//For Testing
/*if (i == -lateralOffset || i == lateralOffset)
{
Effects.SendLocationParticles(EffectItem.Create(newPoint, this.Map, EffectItem.DefaultDuration), 0x3709, 10, 30, 5052);
}*/
}
if (mobiles.Count > 0)
{
Timer.DelayCall(delay, new TimerStateCallback(OnMobileHit), new object[] { mobiles, newPoint, ammo, shooter });
hit = true;
}
}
break;
}
if (hit && ammo.SingleTarget)
break;
}
ClearCannon();
InvalidateDamageState();
if (shooter != null && shooter.HasGump(typeof(CannonGump)))
ResendGump(shooter);
}
private BaseGalleon FindValidBoatTarget(Point3D newPoint, Map map, AmmoInfo info)
{
BaseGalleon galleon = BaseGalleon.FindGalleonAt(newPoint, map);
if (galleon != null && info.RequiresSurface)
{
int d = galleon is BritannianShip ? 3 : 2;
switch (galleon.Facing)
{
case Direction.North:
case Direction.South:
if (newPoint.X <= galleon.X - d || newPoint.X >= galleon.X + d)
return null;
break;
case Direction.East:
case Direction.West:
if (newPoint.Y <= galleon.Y - d || newPoint.Y >= galleon.Y + d)
return null;
break;
}
StaticTile[] tiles = map.Tiles.GetStaticTiles(newPoint.X, newPoint.Y, true);
foreach (StaticTile tile in tiles)
{
ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
bool isWater = (tile.ID >= 0x1796 && tile.ID <= 0x17B2);
if (!isWater && id.Surface && !id.Impassable)
{
return galleon;
}
}
return null;
}
return galleon;
}
public void DoShootEffects()
{
Point3D p = this.Location;
Map map = this.Map;
p.Z -= 3;
switch (Facing)
{
case Direction.North: p.Y--; break;
case Direction.East: p.X++; break;
case Direction.South: p.Y++; break;
case Direction.West: p.X--; break;
}
Effects.SendLocationEffect(p, map, 14120, 15, 10);
Effects.PlaySound(p, map, 0x664);
}
public void InvalidateDamageState()
{
InvalidateDamageState(null);
}
public void InvalidateDamageState(Mobile from)
{
if (Durability >= 100)
m_DamageState = DamageLevel.Pristine;
else if (Durability >= 75.0)
m_DamageState = DamageLevel.Slightly;
else if (Durability >= 50.0)
m_DamageState = DamageLevel.Moderately;
else if (Durability >= 25.0)
m_DamageState = DamageLevel.Heavily;
else
m_DamageState = DamageLevel.Severely;
if (Durability <= 0)
{
DoAreaMessage(1116297, 5, null); //The ship cannon has been destroyed!
Delete();
if (from != null && from.InRange(this.Location, 5))
from.SendLocalizedMessage(1116297); //The ship cannon has been destroyed!
}
InvalidateProperties();
}
private int m_Cleansliness;
public void CheckDirty()
{
if (m_Cleansliness++ >= 10)
m_Cleaned = false;
}
public void ClearCannon()
{
CheckDirty();
m_Charged = false;
m_Primed = false;
m_AmmoType = AmmunitionType.Empty;
m_LoadedAmmo = null;
InvalidateProperties();
}
public virtual void OnShipHit(object obj)
{
object[] list = (object[])obj;
BaseBoat target = list[0] as BaseBoat;
Point3D pnt = (Point3D)list[1];
AmmoInfo ammoInfo = list[2] as AmmoInfo;
Mobile shooter = list[3] as Mobile;
if (target != null && m_Galleon != null)
{
int damage = (int)(GetDamage(ammoInfo) * m_Galleon.CannonDamageMod);
damage /= 7;
target.OnTakenDamage(shooter, damage);
int z = target.ZSurface;
if (target.TillerMan != null && target.TillerMan is IEntity)
{
z = ((IEntity)target.TillerMan).Z;
}
Direction d = Utility.GetDirection(this, pnt);
int xOffset = 0;
int yOffset = 0;
Point3D hit = pnt;
if (!ammoInfo.RequiresSurface)
{
switch (d)
{
default:
case Direction.North:
xOffset = Utility.RandomMinMax(-1, 1);
yOffset = Utility.RandomMinMax(-2, 0);
hit = new Point3D(pnt.X + xOffset, pnt.Y + yOffset, z);
break;
case Direction.South:
xOffset = Utility.RandomMinMax(-1, 1);
yOffset = Utility.RandomMinMax(0, 2);
hit = new Point3D(pnt.X + xOffset, pnt.Y + yOffset, z);
break;
case Direction.East:
xOffset = Utility.RandomMinMax(0, 2);
yOffset = Utility.RandomMinMax(-1, 1);
hit = new Point3D(pnt.X + xOffset, pnt.Y + yOffset, z);
break;
case Direction.West:
xOffset = Utility.RandomMinMax(-2, 0);
yOffset = Utility.RandomMinMax(-1, 1);
hit = new Point3D(pnt.X + xOffset, pnt.Y + yOffset, z);
break;
}
}
Effects.SendLocationEffect(hit, target.Map, Utility.RandomBool() ? 14000 : 14013, 15, 10);
Effects.PlaySound(hit, target.Map, 0x207);
Mobile victim = target.Owner;
if (victim != null && target.Contains(victim) && shooter.CanBeHarmful(victim, false))
shooter.DoHarmful(victim);
else
{
List<Mobile> candidates = new List<Mobile>();
SecurityLevel highest = SecurityLevel.Passenger;
foreach (var mob in target.GetMobilesOnBoard().OfType<PlayerMobile>().Where(pm => shooter.CanBeHarmful(pm, false)))
{
if (m_Galleon.GetSecurityLevel(mob) > highest)
candidates.Insert(0, mob);
else
candidates.Add(mob);
}
if (candidates.Count > 0)
shooter.DoHarmful(candidates[0]);
else if (victim != null && shooter.IsHarmfulCriminal(victim))
shooter.CriminalAction(false);
ColUtility.Free(candidates);
}
if (m_Galleon.Map != null)
{
IPooledEnumerable eable = m_Galleon.Map.GetItemsInRange(hit, 1);
foreach (Item item in eable)
{
if (item is BaseCannon && !m_Galleon.Contains(item))
((BaseCannon)item).OnDamage(damage, shooter);
}
eable.Free();
}
}
}
public virtual void OnMobileHit(object obj)
{
object[] objects = (object[])obj;
List<Mobile> mobsToHit = objects[0] as List<Mobile>;
Point3D pnt = (Point3D)objects[1];
AmmoInfo info = objects[2] as AmmoInfo;
Mobile shooter = objects[3] as Mobile;
int damage = (int)(Utility.RandomMinMax(info.MinDamage, info.MaxDamage) * m_Galleon.CannonDamageMod);
if (info == null)
return;
Mobile toHit = null;
if (!info.SingleTarget)
{
foreach (Mobile mob in mobsToHit)
{
toHit = mob;
if (toHit is BaseSeaChampion && info.AmmoType != AmmunitionType.Empty && info.AmmoType == AmmunitionType.Cannonball)
damage *= 100;
shooter.DoHarmful(toHit);
AOS.Damage(toHit, shooter, damage, info.PhysicalDamage, info.FireDamage, info.ColdDamage, info.PoisonDamage, info.EnergyDamage);
Effects.SendLocationEffect(toHit.Location, toHit.Map, Utility.RandomBool() ? 14000 : 14013, 15, 10);
Effects.PlaySound(toHit.Location, toHit.Map, 0x207);
}
}
else
{
toHit = mobsToHit[Utility.Random(mobsToHit.Count)];
if (toHit != null)
{
//only cannonballs will get the damage bonus
if (toHit is BaseSeaChampion && info.AmmoType != AmmunitionType.Empty && info.AmmoType == AmmunitionType.Cannonball)
damage *= 75;
shooter.DoHarmful(toHit);
AOS.Damage(toHit, shooter, damage, info.PhysicalDamage, info.FireDamage, info.ColdDamage, info.PoisonDamage, info.EnergyDamage);
Effects.SendLocationEffect(toHit.Location, toHit.Map, Utility.RandomBool() ? 14000 : 14013, 15, 10);
Effects.PlaySound(toHit.Location, toHit.Map, 0x207);
}
}
}
public List<Mobile> FindMobiles(Mobile shooter, Point3D pnt, Map map, bool player, bool pet, bool monsters, bool seacreature)
{
List<Mobile> list = new List<Mobile>();
if (map == null || map == Map.Internal || m_Galleon == null)
return list;
IPooledEnumerable eable = map.GetMobilesInRange(pnt, 0);
foreach (Mobile mob in eable)
{
if (!shooter.CanBeHarmful(mob, false) || m_Galleon.Contains(mob))
continue;
if (player && mob is PlayerMobile)
list.Add(mob);
if (monsters && mob is BaseCreature && !((BaseCreature)mob).Controlled && !((BaseCreature)mob).Summoned)
list.Add(mob);
if (pet && mob is BaseCreature && (((BaseCreature)mob).Controlled || ((BaseCreature)mob).Summoned))
list.Add(mob);
if (seacreature && mob is BaseSeaChampion)
list.Add(mob);
}
eable.Free();
return list;
}
public void TryRepairCannon(Mobile from)
{
Container pack = from.Backpack;
Container hold = m_Galleon.GalleonHold;
if (pack == null)
return;
double ingotsNeeded = 36 * (100 - Durability);
ingotsNeeded -= ((double)from.Skills[SkillName.Blacksmith].Value / 200.0) * ingotsNeeded;
double min = ingotsNeeded / 10;
double ingots1 = pack.GetAmount(typeof(IronIngot));
double ingots2 = hold != null ? hold.GetAmount(typeof(IronIngot)) : 0;
double ingots = ingots1 + ingots2;
double ingotsUsed, percRepaired;
if (ingots < min)
{
from.SendLocalizedMessage(1116603, ((int)min).ToString()); //You need a minimum of ~1_METAL~ iron ingots to repair this cannon.
return;
}
if (ingots >= ingotsNeeded)
{
ingotsUsed = ingotsNeeded;
percRepaired = 100;
}
else
{
ingotsUsed = ingots;
percRepaired = (ingots / ingotsNeeded) * 100;
}
double toConsume = 0;
double temp = ingotsUsed;
if (ingotsUsed > 0 && ingots1 > 0)
{
toConsume = Math.Min(ingotsUsed, ingots1);
pack.ConsumeTotal(typeof(IronIngot), (int)toConsume);
ingotsUsed -= toConsume;
}
if (hold != null && ingotsUsed > 0 && ingots2 > 0)
{
toConsume = Math.Min(ingotsUsed, ingots2);
hold.ConsumeTotal(typeof(IronIngot), (int)toConsume);
}
m_Hits += (int)((MaxHits - m_Hits) * (percRepaired / 100));
if (m_Hits > MaxHits) m_Hits = MaxHits;
InvalidateDamageState();
percRepaired += Durability;
if (percRepaired > 100) percRepaired = 100;
from.SendLocalizedMessage(1116605, String.Format("{0}\t{1}", ((int)temp).ToString(), ((int)percRepaired).ToString())); //You make repairs to the cannon using ~1_METAL~ ingots. The cannon is now ~2_DMGPCT~% repaired.
}
public bool VerifyAmmo(Type type)
{
foreach (Type ammoType in LoadTypes)
{
if (type == ammoType || type.IsSubclassOf(ammoType))
return true;
}
return false;
}
public bool TryClean(Mobile from)
{
if (m_Cleaned && m_Cleansliness == 0)
from.SendLocalizedMessage(1116007); //The cannon is already clean
else if (CheckForItem(typeof(Swab), from))
{
AddAction(from, 1149641); //Cleaning started.
DoAreaMessage(1116034, 10, from); //~1_NAME~ begins cleaning the cannon with a cannon swab.
Timer.DelayCall(ActionTime, new TimerStateCallback(Clean), from);
return true;
}
else
{
AddAction(from, 1149659);
from.SendLocalizedMessage(1149659); //You need a swab.
}
return false;
}
public bool TryCharge(Mobile from)
{
Type charge = this is LightShipCannon ? typeof(LightPowderCharge) : typeof(HeavyPowderCharge);
if (m_Charged)
from.SendLocalizedMessage(1116012); //The cannon is already charged.
else if (!m_Cleaned)
from.SendMessage("The cannon needs to be cleaned before you can use it again.");
else if (CheckForItem(charge, from))
{
AddAction(from, 1149644); //Charging started.
DoAreaMessage(1116035, 10, from); //~1_NAME~ begins loading the cannon with a powder charge.
Timer.DelayCall(ActionTime, new TimerStateCallback(Charge), new object[] { from, charge });
return true;
}
else
{
AddAction(from, 1149665); //Need powder charge.
from.SendLocalizedMessage(1149665);
}
return false;
}
public bool TryLoad(Mobile from)
{
if (!m_Charged)
from.SendLocalizedMessage(1116018); //The cannon needs to be charged with a rammer and powder charge before it can be loaded.
else if (CheckForItem(typeof(Ramrod), from))
{
AddAction(from, 1149666); //Select Ammunition
from.Target = new LoadCannonTarget(this);
return true;
}
else
{
AddAction(from, 1149660);
from.SendLocalizedMessage(1149660); //You need a ramrod.
}
return false;
}
public bool TryPrime(Mobile from)
{
if (m_Primed)
from.SendLocalizedMessage(1116019); //The cannon is already primed and ready to be fired.
else if (!m_Charged || m_AmmoType == AmmunitionType.Empty)
from.SendLocalizedMessage(1116021); //The cannon needs to be charged and loaded before it can be primed.
else if (CheckForItem(typeof(FuseCord), from))
{
AddAction(from, 1149650); //Priming started
DoAreaMessage(1116038, 10, from); //~1_NAME~ begins priming the cannon with a cannon fuse.
Timer.DelayCall(ActionTime, new TimerStateCallback(Prime), new object[] { from, typeof(FuseCord) });
return true;
}
else
{
AddAction(from, 1149661);
from.SendLocalizedMessage(1149661); //you need a fuse.
}
return false;
}
public void DoLoad(Mobile from, Item ammo)
{
Timer.DelayCall(ActionTime, new TimerStateCallback(Load), new object[] { from, ammo });
int cliloc = ammo is ICannonAmmo && ((ICannonAmmo)ammo).AmmoType == AmmunitionType.Cannonball ? 1116036 : 1116037;
AddAction(from, 1149647); //loading started.
DoAreaMessage(cliloc, 10, from);
}
public void Clean(object state)
{
Mobile from = (Mobile)state;
if (from.InRange(this.Location, 3))
{
m_Cleaned = true;
m_Cleansliness = 0;
AddAction(from, 1149643); //cleaning finished.
DoAreaMessage(1116060, 10, from); //~1_NAME~ finishes cleaning the cannon.
}
else
{
AddAction(from, 1149642); //Cleaning canceled.
DoAreaMessage(1116055, 10, from); //~1_NAME~ cancels the effort of cleaning the cannon.
}
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void Charge(object state)
{
object[] obj = (object[])state;
Mobile from = obj[0] as Mobile;
Type type = obj[1] as Type;
if (from.InRange(this.Location, 3))
{
m_Charged = true;
AddAction(from, 1149646); //Charging finished.
DoAreaMessage(1116061, 10, from); //~1_NAME~ finishes charging the cannon.
if (type != null && from.Backpack != null)
from.Backpack.ConsumeTotal(type, 1);
}
else
{
AddAction(from, 1149645); //Charging canceled.
DoAreaMessage(1116056, 10, from); //~1_NAME~ cancels the effort of charging the cannon and retrieves the powder charge.
}
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void Prime(object state)
{
object[] obj = (object[])state;
Mobile from = obj[0] as Mobile;
Type type = obj[1] as Type;
if (from.InRange(this.Location, 3))
{
m_Primed = true;
AddAction(from, 1149652); //Ready to Fire
DoAreaMessage(1116064, 10, from); //~1_NAME~ finishes priming the cannon. It is ready to be fired!
if (type != null && from.Backpack != null)
from.Backpack.ConsumeTotal(type, 1);
}
else
{
AddAction(from, 1149651); //Priming Canceled
DoAreaMessage(1116059, 10, from); //~1_NAME~ cancels the effort of priming the cannon and retrieves the cannon fuse.
}
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void Load(object state)
{
object[] obj = (object[])state;
Mobile from = obj[0] as Mobile;
Item ammo = obj[1] as Item;
int cliloc = 1116062;
if (ammo is ICannonAmmo && ((ICannonAmmo)ammo).AmmoType == AmmunitionType.Grapeshot)
cliloc = 1116063;
if (m_AmmoType != AmmunitionType.Empty)
{
AddAction(from, 1149663); //Must unload first.
from.SendLocalizedMessage(1149663);
}
else if (from.InRange(this.Location, 3))
{
if (TryLoadAmmo(ammo) && ammo is ICannonAmmo)
{
AddAction(from, 1149649); //Loading finished
DoAreaMessage(cliloc, 10, from); //~1_NAME~ finishes loading the cannon with a cannonball.
m_AmmoType = ((ICannonAmmo)ammo).AmmoType;
m_LoadedAmmo = ammo.GetType();
ammo.Consume();
}
}
else
{
cliloc = ammo is ICannonAmmo && ((ICannonAmmo)ammo).AmmoType == AmmunitionType.Cannonball ? 1116057 : 1116058;
AddAction(from, 1149648); //Loading canceled.
DoAreaMessage(cliloc, 10, from); //~1_NAME~ cancels the effort of loading the cannon and retrieves the cannonball.
}
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void RemoveCharge(Mobile from)
{
if (from == null || !m_Charged)
return;
Type type = this is LightShipCannon ? typeof(LightPowderCharge) : typeof(HeavyPowderCharge);
Item item = Loot.Construct(type);
if (item != null)
{
Container pack = from.Backpack;
if (pack != null || !pack.TryDropItem(from, item, false))
item.MoveToWorld(from.Location, from.Map);
}
m_Charged = false;
AddAction(from, 1149684); //Powder charge removed.
DoAreaMessage(1116065, 10, from); //~1_NAME~ carefully removes the powder charge from the cannon.
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void RemoveLoad(Mobile from)
{
if (from == null || m_AmmoType == AmmunitionType.Empty)
return;
if (m_Charged)
AddAction(from, 1149662); //Must remove charge first.
else
{
int cliloc = 0;
switch (m_AmmoType)
{
case AmmunitionType.Cannonball:
cliloc = 1116066; //~1_NAME~ carefully removes the cannonball from the cannon.
break;
case AmmunitionType.Grapeshot:
cliloc = 1116067; //~1_NAME~ carefully removes the grapeshot from the cannon.
break;
}
Item item = Loot.Construct(m_LoadedAmmo);
if (item != null)
{
Container pack = from.Backpack;
if (pack != null || !pack.TryDropItem(from, item, false))
item.MoveToWorld(from.Location, from.Map);
}
m_AmmoType = AmmunitionType.Empty;
AddAction(from, 1149685); //Ammunition removed.
m_LoadedAmmo = null;
if (cliloc > 0)
DoAreaMessage(cliloc, 10, from); //~1_NAME~ carefully removes the powder charge from the cannon.
}
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public void RemovePrime(Mobile from)
{
if (from == null || !m_Primed)
return;
if (m_AmmoType != AmmunitionType.Empty)
AddAction(from, 1149663); //Must unload first.
Item item = Loot.Construct(typeof(FuseCord));
if (item != null)
{
Container pack = from.Backpack;
if (pack != null || !pack.TryDropItem(from, item, false))
item.MoveToWorld(from.Location, from.Map);
}
m_Primed = false;
AddAction(from, 1149686); //Fuse removed
DoAreaMessage(1116068, 10, from); //~1_NAME~ carefully removes the cannon fuse from the cannon.
if (from.HasGump(typeof(CannonGump)))
ResendGump(from);
InvalidateProperties();
}
public static bool CheckForItem(Type type, Mobile toCheck)
{
Container pack = toCheck.Backpack;
if (pack != null)
return pack.GetAmount(type) >= 1;
return false;
}
public void ResendGump(Mobile from)
{
from.CloseGump(typeof(CannonGump));
from.SendGump(new CannonGump(this, from));
}
public void OnDamage(int damage, Mobile from)
{
m_Hits -= damage;
InvalidateDamageState(from);
}
public Dictionary<Mobile, List<int>> Actions { get { return m_Actions; } }
private Dictionary<Mobile, List<int>> m_Actions = new Dictionary<Mobile, List<int>>();
public void AddAction(Mobile from, int cliloc)
{
if (from == null)
return;
if (!m_Actions.ContainsKey(from))
m_Actions[from] = new List<int>();
List<int> list = m_Actions[from];
if (list.Count == 0 || list[list.Count - 1] != cliloc)
list.Add(cliloc);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1116025, String.Format("#{0}", m_Cleaned ? 1116031 : 1116032)); //Cleaned: ~1_VALUE~
list.Add(1116026, String.Format("#{0}", m_Charged ? 1116031 : 1116032)); //Charged: ~1_VALUE~
list.Add(1116027, AmmoInfo.GetAmmoName(this).ToString()); //Ammo: ~1_VALUE~
list.Add(1116028, String.Format("#{0}", m_Primed ? 1116031 : 1116032)); //Primed: ~1_VALUE~
list.Add(1116580 + (int)m_DamageState);
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
if (m_Galleon.GetSecurityLevel(from) >= SecurityLevel.Officer)
{
if (!m_Cleaned)
list.Add(new CleanContext(this, from));
if (!m_Charged)
list.Add(new ChargeContext(this, from));
if (m_AmmoType == AmmunitionType.Empty)
list.Add(new LoadContext(this, from));
if (!m_Primed)
list.Add(new PrimeContext(this, from));
list.Add(new DismantleContext(this, from));
list.Add(new RepairContext(this, from));
}
}
private class CleanContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public CleanContext(BaseCannon cannon, Mobile from) : base(1149626, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon != null)
m_Cannon.TryClean(m_From);
}
}
private class ChargeContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public ChargeContext(BaseCannon cannon, Mobile from) : base(1149630, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon != null)
m_Cannon.TryCharge(m_From);
}
}
private class LoadContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public LoadContext(BaseCannon cannon, Mobile from)
: base(1149635, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon != null)
m_From.Target = new BaseCannon.LoadCannonTarget(m_Cannon);
}
}
private class PrimeContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public PrimeContext(BaseCannon cannon, Mobile from) : base(1149637, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon != null)
m_Cannon.TryPrime(m_From);
}
}
private class DismantleContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public DismantleContext(BaseCannon cannon, Mobile from)
: base(1116069, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon != null)
{
if (!m_Cannon.Cleaned || m_Cannon.Primed || m_Cannon.Charged || m_Cannon.AmmoType != AmmunitionType.Empty)
m_From.SendLocalizedMessage(1116321); //The ship cannon must be cleaned and fully unloaded before it can be dismantled.
else if (m_Cannon.DamageState != DamageLevel.Pristine)
m_From.SendLocalizedMessage(1116322); //The ship cannon must be fully repaired before it can be dismantled.
else
{
ShipCannonDeed deed = m_Cannon.GetDeed;
m_Cannon.DoAreaMessage(1116073, 10, m_From); //~1_NAME~ dismantles the ship cannon.
m_Cannon.Delete();
Container pack = m_From.Backpack;
if (pack == null || !pack.TryDropItem(m_From, deed, false))
deed.MoveToWorld(m_From.Location, m_From.Map);
}
}
}
}
private class RepairContext : ContextMenuEntry
{
private Mobile m_From;
private BaseCannon m_Cannon;
public RepairContext(BaseCannon cannon, Mobile from)
: base(1116602, 3)
{
m_From = from;
m_Cannon = cannon;
}
public override void OnClick()
{
if (m_Cannon.DamageState == DamageLevel.Pristine)
m_From.SendLocalizedMessage(1116604); //The cannon is in pristine condition and does not need repairs.
else
{
m_Cannon.TryRepairCannon(m_From);
}
}
}
private class LoadCannonTarget : Target
{
private BaseCannon m_Cannon;
public LoadCannonTarget(BaseCannon cannon) : base(3, false, TargetFlags.None)
{
m_Cannon = cannon;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Cannon == null || !(targeted is Item))
return;
Item item = (Item)targeted;
if (targeted is Item && m_Cannon.VerifyAmmo(item.GetType()))
m_Cannon.DoLoad(from, item);
else
{
from.SendMessage("You must target the proper ammunition for this type of cannon.");
m_Cannon.AddAction(from, 1149667); //Invalid target.
from.Target = new LoadCannonTarget(m_Cannon);
}
}
}
public override void Delete()
{
if (m_Galleon != null)
m_Galleon.RemoveCannon(this);
base.Delete();
}
public BaseCannon(Serial serial) : base(serial) { }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)2);
writer.Write(m_Cleansliness);
writer.Write(m_LoadedAmmo != null);
if (m_LoadedAmmo != null)
writer.Write(m_LoadedAmmo.Name);
writer.Write(m_Cleaned);
writer.Write(m_Charged);
writer.Write(m_Primed);
writer.Write(m_Galleon);
writer.Write(m_Hits);
writer.Write((int)m_AmmoType);
writer.Write((int)m_Position);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version > 1)
m_Cleansliness = reader.ReadInt();
else
{
m_Cleaned = true;
m_Cleansliness = 0;
}
if (version > 0)
{
if (reader.ReadBool())
{
string name = reader.ReadString();
m_LoadedAmmo = ScriptCompiler.FindTypeByName(name);
}
}
m_Cleaned = reader.ReadBool();
m_Charged = reader.ReadBool();
m_Primed = reader.ReadBool();
m_Galleon = reader.ReadItem() as BaseGalleon;
m_Hits = reader.ReadInt();
m_AmmoType = (AmmunitionType)reader.ReadInt();
m_Position = (ShipPosition)reader.ReadInt();
if (m_LoadedAmmo == null && m_AmmoType != AmmunitionType.Empty)
m_AmmoType = AmmunitionType.Empty;
InvalidateDamageState();
if (Core.EJ)
{
Timer.DelayCall(() => Replace());
}
}
private void Replace()
{
if (m_Galleon != null && !m_Galleon.Deleted)
{
BaseShipCannon newCannon = null;
var loc = Location;
Delete();
if (this is HeavyShipCannon)
{
newCannon = new Carronade(m_Galleon);
}
else if (this is LightShipCannon)
{
newCannon = new Culverin(m_Galleon);
}
if (newCannon != null)
{
if (!m_Galleon.TryAddCannon(null, loc, newCannon, null))
{
var deed = GetDeed;
if (deed != null)
{
m_Galleon.GalleonHold.DropItem(deed);
}
newCannon.Delete();
}
}
}
else
{
Delete();
}
}
}
public class LightShipCannon : BaseCannon
{
public override int Range { get { return 8; } }
public override ShipCannonDeed GetDeed { get { return new LightShipCannonDeed(); } }
public override Type[] LoadTypes { get { return new Type[] { typeof(LightCannonball), typeof(LightGrapeshot),
typeof(LightFlameCannonball), typeof(LightFrostCannonball) }; } }
public LightShipCannon(BaseGalleon g) : base(g)
{
}
public override bool TryLoadAmmo(Item ammo)
{
return ammo is LightCannonball || ammo is LightGrapeshot;
}
public LightShipCannon(Serial serial) : base(serial) { }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class HeavyShipCannon : BaseCannon
{
public override int Range { get { return 10; } }
public override TimeSpan ActionTime { get { return TimeSpan.FromSeconds(2.0); } }
public override int LabelNumber { get { return 0; } }
public override Type[] LoadTypes { get { return new Type[] { typeof(HeavyCannonball), typeof(HeavyGrapeshot),
typeof(HeavyFrostCannonball), typeof(HeavyFlameCannonball) }; } }
public HeavyShipCannon(BaseGalleon g) : base(g)
{
}
public override bool TryLoadAmmo(Item ammo)
{
return ammo is HeavyCannonball || ammo is HeavyGrapeshot;
}
public HeavyShipCannon(Serial serial) : base(serial) { }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)0);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}