1492 lines
52 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|