using Preparation.Interface;
using Preparation.Utility;
using System;
namespace GameClass.GameObj
{
public abstract class Bullet : ObjOfCharacter
{
///
/// //攻击力
///
public abstract int AP { get; }
public abstract int Speed { get; }
private readonly bool hasSpear;
///
/// 是否有矛
///
public bool HasSpear => hasSpear;
///
/// 与THUAI4不同的一个攻击判定方案,通过这个函数判断爆炸时能否伤害到target
///
/// 被尝试攻击者
/// 是否可以攻击到
public abstract bool CanAttack(GameObj target);
protected override bool IgnoreCollideExecutor(IGameObj targetObj)
{
if (targetObj.Type == GameObjType.BirthPoint || targetObj.Type == GameObjType.Prop || targetObj.Type == GameObjType.Bullet)
return true;
return false;
}
public Bullet(Character player, int radius) :
base(player.Position, radius, PlaceType.Null, GameObjType.Bullet)
{
this.CanMove = true;
this.moveSpeed = this.Speed;
this.hasSpear = player.HasSpear;
this.Parent = player;
}
public override bool IsRigid => true; // 默认为true
public override ShapeType Shape => ShapeType.Circle; // 默认为圆形
public abstract BulletType TypeOfBullet { get; }
}
internal sealed class AtomBomb : Bullet
{
public AtomBomb(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
{
}
public const double BulletBombRange = GameData.basicBulletBombRange / 3 * 7;
public const double BulletAttackRange = GameData.basicAttackRange / 9 * 7;
public override int AP => GameData.basicAp / 3 * 7;
public override int Speed => GameData.basicBulletMoveSpeed / 3 * 2;
public override bool CanAttack(GameObj target)
{
// 圆形攻击范围
return XY.Distance(this.Position, target.Position) <= BulletBombRange;
}
public override BulletType TypeOfBullet => BulletType.AtomBomb;
}
internal sealed class OrdinaryBullet : Bullet // 1倍攻击范围,1倍攻击力,一倍速
{
public OrdinaryBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
{
}
public const double BulletBombRange = GameData.basicBulletBombRange / 6 * 5;
public const double BulletAttackRange = GameData.basicAttackRange / 2;
public override int AP => GameData.basicAp / 6 * 5;
public override int Speed => GameData.basicBulletMoveSpeed / 6 * 5;
public override bool CanAttack(GameObj target)
{
// 圆形攻击范围
return XY.Distance(this.Position, target.Position) <= BulletBombRange;
}
public override BulletType TypeOfBullet => BulletType.OrdinaryBullet;
}
internal sealed class FastBullet : Bullet // 1倍攻击范围,0.2倍攻击力,2倍速
{
public FastBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
{
}
public const double BulletBombRange = GameData.basicBulletBombRange / 4 * 2;
public const double BulletAttackRange = GameData.basicAttackRange;
public override int AP => (int)(0.5 * GameData.basicAp);
public override int Speed => 5 * GameData.basicBulletMoveSpeed / 3;
public override bool CanAttack(GameObj target)
{
// 圆形攻击范围
return XY.Distance(this.Position, target.Position) <= BulletBombRange;
}
public override BulletType TypeOfBullet => BulletType.FastBullet;
}
internal sealed class LineBullet : Bullet // 直线爆炸,宽度1格,长度为2倍爆炸范围
{
public LineBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
{
}
public const double BulletBombRange = GameData.basicBulletBombRange / 3 * 4;
public const double BulletAttackRange = 0.1 * GameData.basicAttackRange;
public override int AP => GameData.basicAp / 3 * 2;
public override int Speed => GameData.basicBulletMoveSpeed / 3;
public override bool CanAttack(GameObj target)
{
double FacingAngle = Math.Atan2(this.FacingDirection.y, this.FacingDirection.x);
if (Math.Abs(FacingAngle - Math.PI / 2) < 1e-2)
{
if (target.Position.y - this.Position.y > BulletBombRange)
return false;
if (target.Position.x < this.Position.x + GameData.numOfPosGridPerCell / 2 && target.Position.x > this.Position.x - GameData.numOfPosGridPerCell / 2)
return true;
return false;
}
else if (Math.Abs(FacingAngle - Math.PI * 3 / 2) < 1e-2)
{
if (target.Position.y - this.Position.y < -BulletBombRange)
return false;
if (target.Position.x < this.Position.x + GameData.numOfPosGridPerCell / 2 && target.Position.x > this.Position.x - GameData.numOfPosGridPerCell / 2)
return true;
return false;
}
else if (Math.Abs(FacingAngle) < 1e-2)
{
if (target.Position.x - this.Position.x > BulletBombRange)
return false;
if (target.Position.y < this.Position.y + GameData.numOfPosGridPerCell / 2 && target.Position.y > this.Position.y - GameData.numOfPosGridPerCell / 2)
return true;
return false;
}
else if (Math.Abs(FacingAngle - Math.PI) < 1e-2)
{
if (target.Position.x - this.Position.x < -BulletBombRange)
return false;
if (target.Position.y < this.Position.y + GameData.numOfPosGridPerCell / 2 && target.Position.y > this.Position.y - GameData.numOfPosGridPerCell / 2)
return true;
return false;
}
double vertical = Math.Tan(FacingAngle + Math.PI / 2);
bool posValue = vertical * Math.Cos(FacingAngle) - Math.Sin(FacingAngle) > 0;
double dist;
dist = vertical * (target.Position.x - this.Position.x) - (target.Position.y - this.Position.y) / Math.Sqrt(1 + vertical * vertical);
if (Math.Abs(dist) > BulletBombRange)
return false;
else if (dist < 0 && posValue) // 位于直线两侧
return false;
vertical = Math.Tan(FacingAngle);
dist = Math.Abs(vertical * (target.Position.x - this.Position.x) - (target.Position.y - this.Position.y)) / Math.Sqrt(1 + vertical * vertical);
if (dist > GameData.numOfPosGridPerCell / 2)
return false;
return true;
}
public override BulletType TypeOfBullet => BulletType.LineBullet;
}
public static class BulletFactory
{
public static Bullet? GetBullet(Character character)
{
Bullet? newBullet = null;
switch (character.BulletOfPlayer)
{
case BulletType.AtomBomb:
newBullet = new AtomBomb(character);
break;
case BulletType.LineBullet:
newBullet = new LineBullet(character);
break;
case BulletType.FastBullet:
newBullet = new FastBullet(character);
break;
case BulletType.OrdinaryBullet:
newBullet = new OrdinaryBullet(character);
break;
default:
break;
}
return newBullet;
}
public static int BulletRadius(BulletType bulletType)
{
switch (bulletType)
{
case BulletType.AtomBomb:
case BulletType.LineBullet:
case BulletType.FastBullet:
case BulletType.OrdinaryBullet:
default:
return GameData.bulletRadius;
}
}
public static double BulletAttackRange(BulletType bulletType)
{
switch (bulletType)
{
case BulletType.AtomBomb:
return AtomBomb.BulletAttackRange;
case BulletType.LineBullet:
return LineBullet.BulletAttackRange;
case BulletType.FastBullet:
return FastBullet.BulletAttackRange;
case BulletType.OrdinaryBullet:
return OrdinaryBullet.BulletAttackRange;
default:
return 0;
}
}
public static double BulletBombRange(BulletType bulletType)
{
switch (bulletType)
{
case BulletType.AtomBomb:
return AtomBomb.BulletBombRange;
case BulletType.LineBullet:
return LineBullet.BulletBombRange;
case BulletType.FastBullet:
return FastBullet.BulletBombRange;
case BulletType.OrdinaryBullet:
return OrdinaryBullet.BulletBombRange;
default:
return 0;
}
}
}
}