Files
abysmal-isle/Scripts/Services/Seasonal Events/RisingTide/MannedCannon.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

535 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Server;
using Server.Mobiles;
using Server.Multis;
using Server.Spells;
namespace Server.Items
{
public abstract class MannedCannon : Item
{
public virtual TimeSpan ScanDelay { get { return TimeSpan.FromSeconds(Utility.RandomMinMax(5, 10)); } }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Operator { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Direction Facing { get { return GetFacing(); } }
public DateTime NextScan { get; set; }
public bool CanFireUnmanned { get; set; }
public abstract CannonPower Power { get; }
public abstract int Range { get; }
public virtual AmmunitionType AmmoType { get { return AmmunitionType.Cannonball; } }
public virtual int LateralOffset { get { return 1; } }
public MannedCannon(Mobile opera, Direction facing)
: base(0)
{
ItemID = GetID(facing, Power);
Operator = opera;
Movable = false;
}
private static int GetID(Direction facing, CannonPower power)
{
switch (facing)
{
default:
case Direction.South:
return BaseGalleon.CannonIDs[0][(int)power];
case Direction.West:
return BaseGalleon.CannonIDs[1][(int)power];
case Direction.North:
return BaseGalleon.CannonIDs[2][(int)power];
case Direction.East:
return BaseGalleon.CannonIDs[3][(int)power];
}
}
public Direction GetFacing()
{
if (BaseGalleon.CannonIDs[0].Any(id => id == ItemID))
{
return Direction.South;
}
if (BaseGalleon.CannonIDs[1].Any(id => id == ItemID))
{
return Direction.West;
}
if (BaseGalleon.CannonIDs[2].Any(id => id == ItemID))
{
return Direction.North;
}
return Direction.East;
}
public bool Scan(bool shoot)
{
var targets = AcquireTarget();
bool acquiredTarget = false;
if (targets != null && targets.Length > 0)
{
foreach (var t in targets)
{
if (t.Entity is BaseGalleon && AmmoType != AmmunitionType.Grapeshot)
{
if (shoot)
{
DoShootEffects();
TimeSpan delay = TimeSpan.FromSeconds((double)t.Range / 10.0);
Timer.DelayCall(delay, new TimerStateCallback(OnShipHit), new object[] { (BaseGalleon)t.Entity, t.Location, AmmoType });
}
acquiredTarget = true;
}
else if (t.Entity is Mobile && AmmoType == AmmunitionType.Grapeshot)
{
var m = t.Entity as Mobile;
if (shoot)
{
DoShootEffects();
TimeSpan delay = TimeSpan.FromSeconds((double)t.Range / 10.0);
Timer.DelayCall(delay, new TimerStateCallback(OnMobileHit), new object[] { m, t.Location, AmmoType });
}
acquiredTarget = true;
}
}
}
return acquiredTarget;
}
public Target[] AcquireTarget()
{
AmmoInfo ammo = AmmoInfo.GetAmmoInfo(AmmoType);
int xOffset = 0; int yOffset = 0;
int currentRange = 0;
Point3D pnt = Location;
Map map = Map;
switch (GetFacing())
{
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;
while (currentRange++ <= Range)
{
xOffset = xo;
yOffset = yo;
if (LateralOffset > 1 && currentRange % LateralOffset == 0)
lateralOffset++;
TimeSpan delay = TimeSpan.FromSeconds((double)currentRange / 10.0);
switch (AmmoType)
{
case AmmunitionType.Empty: break;
case AmmunitionType.Cannonball:
case AmmunitionType.FrostCannonball:
case AmmunitionType.FlameCannonball:
{
Point3D newPoint = pnt;
//List<IEntity> list = new List<IEntity>();
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);
BaseGalleon g = FindValidBoatTarget(newPoint, map, ammo);
if (g != null && g.DamageTaken < DamageLevel.Severely && g.Owner is PlayerMobile)
{
var target = new Target();
target.Entity = g;
target.Location = newPoint;
target.Range = currentRange;
return new Target[] { target };
}
}
}
break;
case AmmunitionType.Grapeshot:
{
Point3D newPoint = pnt;
List<Target> mobiles = new List<Target>();
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);
foreach (Mobile m in GetTargets(newPoint, map))
{
var target = new Target();
target.Entity = m;
target.Location = newPoint;
target.Range = currentRange;
mobiles.Add(target);
}
if (mobiles.Count > 0 && ammo.SingleTarget)
{
var toHit = mobiles[Utility.Random(mobiles.Count)];
ColUtility.Free(mobiles);
return new Target[] { toHit };
}
}
if (mobiles.Count > 0)
{
return mobiles.ToArray();
}
}
break;
}
}
return null;
}
private IEnumerable<Mobile> GetTargets(Point3D newPoint, Map map)
{
if (Operator != null)
{
foreach (Mobile m in SpellHelper.AcquireIndirectTargets(Operator, newPoint, map, 0).OfType<Mobile>())
{
yield return m;
}
yield break;
}
IPooledEnumerable eable = map.GetMobilesInRange(newPoint, 0);
foreach (Mobile m in eable)
{
if (m is PlayerMobile || (m is BaseCreature && ((BaseCreature)m).GetMaster() is PlayerMobile))
{
yield return m;
}
}
}
public struct Target
{
public IEntity Entity { get; set; }
public Point3D Location { get; set; }
public int Range { get; set; }
}
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 virtual void OnShipHit(object obj)
{
object[] list = (object[])obj;
BaseBoat target = list[0] as BaseBoat;
Point3D pnt = (Point3D)list[1];
var ammoInfo = AmmoInfo.GetAmmoInfo((AmmunitionType)list[2]);
if (ammoInfo != null && target != null)
{
int damage = (Utility.RandomMinMax(ammoInfo.MinDamage, ammoInfo.MaxDamage));
damage /= 7;
target.OnTakenDamage(Operator, 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);
if (Operator != null && (!Operator.Deleted || CanFireUnmanned))
{
Mobile victim = target.Owner;
if (victim != null && target.Contains(victim) && Operator.CanBeHarmful(victim, false))
{
Operator.DoHarmful(victim);
}
else
{
List<Mobile> candidates = new List<Mobile>();
SecurityLevel highest = SecurityLevel.Passenger;
foreach (var mob in target.GetMobilesOnBoard().OfType<PlayerMobile>().Where(pm => Operator.CanBeHarmful(pm, false)))
{
if (target is BaseGalleon && ((BaseGalleon)target).GetSecurityLevel(mob) > highest)
{
candidates.Insert(0, mob);
}
else
{
candidates.Add(mob);
}
}
if (candidates.Count > 0)
{
Operator.DoHarmful(candidates[0]);
}
else if (victim != null && Operator.IsHarmfulCriminal(victim))
{
Operator.CriminalAction(false);
}
ColUtility.Free(candidates);
}
}
}
}
public virtual void OnMobileHit(object obj)
{
object[] objects = (object[])obj;
Mobile toHit = objects[0] as Mobile;
Point3D pnt = (Point3D)objects[1];
AmmoInfo ammoInfo = AmmoInfo.GetAmmoInfo((AmmunitionType)objects[2]);
if (ammoInfo != null)
{
int damage = (int)(Utility.RandomMinMax(ammoInfo.MinDamage, ammoInfo.MaxDamage));
if (Operator != null)
{
Operator.DoHarmful(toHit);
}
AOS.Damage(toHit, Operator, damage, ammoInfo.PhysicalDamage, ammoInfo.FireDamage, ammoInfo.ColdDamage, ammoInfo.PoisonDamage, ammoInfo.EnergyDamage);
Effects.SendLocationEffect(toHit.Location, toHit.Map, Utility.RandomBool() ? 14000 : 14013, 15, 10);
Effects.PlaySound(toHit.Location, toHit.Map, 0x207);
}
}
public void DoShootEffects()
{
Point3D p = Location;
Map map = 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 MannedCannon(Serial serial) : base(serial) { }
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)1);
writer.Write(Operator);
writer.Write(CanFireUnmanned);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
Operator = reader.ReadMobile();
CanFireUnmanned = reader.ReadBool();
break;
}
}
}
public class MannedCulverin : MannedCannon
{
public override int Range { get { return 10; } }
public override CannonPower Power { get { return CannonPower.Light; } }
public MannedCulverin(Mobile oper, Direction facing)
: base(oper, facing)
{
}
public MannedCulverin(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 MannedCarronade : MannedCannon
{
public override int Range { get { return 10; } }
public override CannonPower Power { get { return CannonPower.Heavy; } }
public MannedCarronade(Mobile oper, Direction facing)
: base(oper, facing)
{
}
public MannedCarronade(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 MannedBlundercannon : MannedCannon
{
public override int LabelNumber { get { return 1158942; } } // Blundercannon
public override int Range { get { return 12; } }
public override CannonPower Power { get { return CannonPower.Massive; } }
public MannedBlundercannon(Mobile oper, Direction facing)
: base(oper, facing)
{
}
public MannedBlundercannon(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();
}
}
}