From 6ff5cf9da1abb60cf8a0920ac1803f6491a7a27c Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 21 Oct 2023 21:21:35 +0800 Subject: [PATCH 01/14] feat: :sparkles: basic frame & gamedata --- .vs/slnx.sqlite | Bin 0 -> 90112 bytes .vs/slnx.sqlite-journal | Bin 0 -> 21032 bytes dependency/proto/Proto.sln | 25 + dependency/proto/Protos.csproj | 34 + logic/Client/Client.csproj | 33 + logic/Client/Client.sln | 37 + logic/Client/Logo.png | Bin 0 -> 56319 bytes logic/ClientTest/ClientTest.csproj | 20 + logic/GameClass/GameClass.csproj | 13 + logic/GameClass/GameObj/Bullet.cs | 216 ++++++ logic/GameClass/GameObj/GameObj.cs | 43 ++ logic/GameClass/GameObj/Home.cs | 13 + logic/GameClass/GameObj/Immovable.cs | 12 + logic/GameClass/GameObj/Map.cs | 255 +++++++ logic/GameClass/GameObj/MapGameTimer.cs | 46 ++ logic/GameClass/GameObj/Movable.cs | 127 ++++ logic/GameClass/GameObj/ObjOfShip.cs | 33 + logic/GameClass/GameObj/OutOfBoundBlock.cs | 19 + logic/GameClass/GameObj/Ship.cs | 198 ++++++ logic/GameClass/GameObj/Team.cs | 113 +++ logic/GameEngine/CollisionChecker.cs | 227 ++++++ logic/GameEngine/GameEngine.csproj | 17 + logic/GameEngine/MoveEngine.cs | 234 +++++++ logic/Gaming/AttackManager.cs | 14 + logic/Gaming/Game.cs | 27 + logic/Gaming/Gaming.csproj | 14 + logic/Gaming/HealthManager.cs | 13 + logic/Gaming/ModuleManager.cs | 15 + logic/Gaming/ShipManager.cs | 28 + logic/Preparation/Interface/IGameObj.cs | 16 + logic/Preparation/Interface/IHome.cs | 12 + logic/Preparation/Interface/IMap.cs | 21 + logic/Preparation/Interface/IModule.cs | 447 ++++++++++++ logic/Preparation/Interface/IMovable.cs | 45 ++ logic/Preparation/Interface/IObjOfShip.cs | 7 + logic/Preparation/Interface/IOccupation.cs | 56 ++ logic/Preparation/Interface/IOutOfBound.cs | 7 + logic/Preparation/Interface/IShip.cs | 17 + logic/Preparation/Interface/ITimer.cs | 9 + logic/Preparation/Interface/IWormhole.cs | 11 + logic/Preparation/Preparation.csproj | 19 + logic/Preparation/Utility/Debugger.cs | 20 + logic/Preparation/Utility/EnumType.cs | 155 ++++ logic/Preparation/Utility/GameData.cs | 195 ++++++ logic/Preparation/Utility/Logger.cs | 21 + logic/Preparation/Utility/MapEncoder.cs | 17 + logic/Preparation/Utility/SafeValue.cs | 778 +++++++++++++++++++++ logic/Preparation/Utility/XY.cs | 98 +++ logic/Program.cs | 2 - logic/Server/Server.csproj | 32 + logic/logic.csproj | 10 - logic/logic.sln | 64 +- playback/Playback/Playback.csproj | 17 + playback/Program.cs | 2 - playback/playback.csproj | 10 - 55 files changed, 3883 insertions(+), 31 deletions(-) create mode 100644 .vs/slnx.sqlite create mode 100644 .vs/slnx.sqlite-journal create mode 100644 dependency/proto/Proto.sln create mode 100644 dependency/proto/Protos.csproj create mode 100644 logic/Client/Client.csproj create mode 100644 logic/Client/Client.sln create mode 100644 logic/Client/Logo.png create mode 100644 logic/ClientTest/ClientTest.csproj create mode 100644 logic/GameClass/GameClass.csproj create mode 100644 logic/GameClass/GameObj/Bullet.cs create mode 100644 logic/GameClass/GameObj/GameObj.cs create mode 100644 logic/GameClass/GameObj/Home.cs create mode 100644 logic/GameClass/GameObj/Immovable.cs create mode 100644 logic/GameClass/GameObj/Map.cs create mode 100644 logic/GameClass/GameObj/MapGameTimer.cs create mode 100644 logic/GameClass/GameObj/Movable.cs create mode 100644 logic/GameClass/GameObj/ObjOfShip.cs create mode 100644 logic/GameClass/GameObj/OutOfBoundBlock.cs create mode 100644 logic/GameClass/GameObj/Ship.cs create mode 100644 logic/GameClass/GameObj/Team.cs create mode 100644 logic/GameEngine/CollisionChecker.cs create mode 100644 logic/GameEngine/GameEngine.csproj create mode 100644 logic/GameEngine/MoveEngine.cs create mode 100644 logic/Gaming/AttackManager.cs create mode 100644 logic/Gaming/Game.cs create mode 100644 logic/Gaming/Gaming.csproj create mode 100644 logic/Gaming/HealthManager.cs create mode 100644 logic/Gaming/ModuleManager.cs create mode 100644 logic/Gaming/ShipManager.cs create mode 100644 logic/Preparation/Interface/IGameObj.cs create mode 100644 logic/Preparation/Interface/IHome.cs create mode 100644 logic/Preparation/Interface/IMap.cs create mode 100644 logic/Preparation/Interface/IModule.cs create mode 100644 logic/Preparation/Interface/IMovable.cs create mode 100644 logic/Preparation/Interface/IObjOfShip.cs create mode 100644 logic/Preparation/Interface/IOccupation.cs create mode 100644 logic/Preparation/Interface/IOutOfBound.cs create mode 100644 logic/Preparation/Interface/IShip.cs create mode 100644 logic/Preparation/Interface/ITimer.cs create mode 100644 logic/Preparation/Interface/IWormhole.cs create mode 100644 logic/Preparation/Preparation.csproj create mode 100644 logic/Preparation/Utility/Debugger.cs create mode 100644 logic/Preparation/Utility/EnumType.cs create mode 100644 logic/Preparation/Utility/GameData.cs create mode 100644 logic/Preparation/Utility/Logger.cs create mode 100644 logic/Preparation/Utility/MapEncoder.cs create mode 100644 logic/Preparation/Utility/SafeValue.cs create mode 100644 logic/Preparation/Utility/XY.cs delete mode 100644 logic/Program.cs create mode 100644 logic/Server/Server.csproj delete mode 100644 logic/logic.csproj create mode 100644 playback/Playback/Playback.csproj delete mode 100644 playback/Program.cs delete mode 100644 playback/playback.csproj diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..102ea2266b6ac30659b6b5035c66e604f1e277ca GIT binary patch literal 90112 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 + + + net6.0 + enable + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/logic/Client/Client.csproj b/logic/Client/Client.csproj new file mode 100644 index 00000000..02642032 --- /dev/null +++ b/logic/Client/Client.csproj @@ -0,0 +1,33 @@ + + + + WinExe + net6.0-windows + enable + true + eesast_software_trans_enlarged.ico + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logic/Client/Client.sln b/logic/Client/Client.sln new file mode 100644 index 00000000..aedf36b1 --- /dev/null +++ b/logic/Client/Client.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client.csproj", "{5AD8481D-90EF-410C-BD48-355DB97EEAB3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playback", "..\..\playback\Playback\Playback.csproj", "{662FDB27-FBF3-4D2D-BDA4-B4BF4D35B866}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protos", "..\..\dependency\proto\Protos.csproj", "{A0F72D3B-9A82-48EB-90AF-B3770151AD83}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5AD8481D-90EF-410C-BD48-355DB97EEAB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5AD8481D-90EF-410C-BD48-355DB97EEAB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5AD8481D-90EF-410C-BD48-355DB97EEAB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5AD8481D-90EF-410C-BD48-355DB97EEAB3}.Release|Any CPU.Build.0 = Release|Any CPU + {662FDB27-FBF3-4D2D-BDA4-B4BF4D35B866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {662FDB27-FBF3-4D2D-BDA4-B4BF4D35B866}.Debug|Any CPU.Build.0 = Debug|Any CPU + {662FDB27-FBF3-4D2D-BDA4-B4BF4D35B866}.Release|Any CPU.ActiveCfg = Release|Any CPU + {662FDB27-FBF3-4D2D-BDA4-B4BF4D35B866}.Release|Any CPU.Build.0 = Release|Any CPU + {A0F72D3B-9A82-48EB-90AF-B3770151AD83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0F72D3B-9A82-48EB-90AF-B3770151AD83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0F72D3B-9A82-48EB-90AF-B3770151AD83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0F72D3B-9A82-48EB-90AF-B3770151AD83}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {89A74B1B-445C-49EB-9C93-506DC243C227} + EndGlobalSection +EndGlobal diff --git a/logic/Client/Logo.png b/logic/Client/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6d7d511416087278769370e779613dc11547e983 GIT binary patch literal 56319 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 Lo^wyTNWS|o;SVEq literal 0 HcmV?d00001 diff --git a/logic/ClientTest/ClientTest.csproj b/logic/ClientTest/ClientTest.csproj new file mode 100644 index 00000000..e76f9356 --- /dev/null +++ b/logic/ClientTest/ClientTest.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/logic/GameClass/GameClass.csproj b/logic/GameClass/GameClass.csproj new file mode 100644 index 00000000..de8abd4c --- /dev/null +++ b/logic/GameClass/GameClass.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + + + + + + + + diff --git a/logic/GameClass/GameObj/Bullet.cs b/logic/GameClass/GameObj/Bullet.cs new file mode 100644 index 00000000..5154db5d --- /dev/null +++ b/logic/GameClass/GameObj/Bullet.cs @@ -0,0 +1,216 @@ +using Preparation.Interface; +using Preparation.Utility; +using System; +using System.Threading; + +namespace GameClass.GameObj +{ + public abstract class Bullet : ObjOfShip + { + public abstract double BulletBombRange { get; } + public abstract double AttackDistance { get; } + private AtomicInt ap = new(0); + public AtomicInt AP { get => ap; } + public abstract int Speed { get; } + public abstract int CastTime { get; } + public abstract int SwingTime { get; } + public abstract int CD { get; } + public abstract int MaxBulletNum { get; } + public override bool IsRigid => true; // 默认为true + public override ShapeType Shape => ShapeType.Circle; // 默认为圆形 + public abstract BulletType TypeOfBullet { get; } + public abstract bool CanAttack(GameObj target); + public abstract bool CanBeBombed(GameObjType gameObjType); + public override bool IgnoreCollideExecutor(IGameObj targetObj) + { + if (targetObj == Parent) return true; + if (targetObj.Type == GameObjType.Bullet) + return true; + return false; + } + public Bullet(Ship ship, int radius, XY Position) : + base(Position, radius, GameObjType.Bullet) + { + this.CanMove.SetReturnOri(true); + this.MoveSpeed.SetReturnOri(this.Speed); + this.Parent = ship; + } + } + public static class BulletFactory + { + public static Bullet? GetBullet(Ship ship, XY pos, BulletType bulletType) + { + switch (bulletType) + { + case BulletType.Laser: + return new Laser(ship, pos); + case BulletType.Plasma: + return new Plasma(ship, pos); + case BulletType.Shell: + return new Shell(ship, pos); + case BulletType.Missile: + return new Missile(ship, pos); + case BulletType.Arc: + return new Arc(ship, pos); + default: + return null; + } + } + } + internal sealed class Laser : Bullet + { + public Laser(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.LaserDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.LaserRange; + public override int Speed => GameData.LaserSpeed; + public override int CastTime => GameData.LaserCastTime; + public override int SwingTime => GameData.LaserSwingTime; + private const int cd = GameData.LaserSwingTime; + public override int CD => cd; + public override int MaxBulletNum => 1; + public override BulletType TypeOfBullet => BulletType.Laser; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } + } + internal sealed class Plasma : Bullet + { + public Plasma(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.PlasmaDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.PlasmaRange; + public override int Speed => GameData.PlasmaSpeed; + public override int CastTime => GameData.PlasmaCastTime; + public override int SwingTime => GameData.PlasmaSwingTime; + private const int cd = GameData.PlasmaSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Plasma; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } + } + internal sealed class Shell : Bullet + { + public Shell(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.ShellDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.ShellRange; + public override int Speed => GameData.ShellSpeed; + public override int CastTime => GameData.ShellCastTime; + public override int SwingTime => GameData.ShellSwingTime; + private const int cd = GameData.ShellSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Shell; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } + } + internal sealed class Missile : Bullet + { + public Missile(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.MissileDamage); + } + public override double BulletBombRange => GameData.MissileBombRange; + public override double AttackDistance => GameData.MissileRange; + public override int Speed => GameData.MissileSpeed; + public override int CastTime => GameData.MissileCastTime; + public override int SwingTime => GameData.ShellSwingTime; + private const int cd = GameData.ShellSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Missile; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + //return true; + return false; + } + } + internal sealed class Arc : Bullet + { + public Arc(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + Random random = new Random(); + this.AP.SetReturnOri(random.Next(GameData.ArcDamageMin, GameData.ArcDamageMax)); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.ArcRange; + public override int Speed => GameData.ArcSpeed; + public override int CastTime => GameData.ArcCastTime; + public override int SwingTime => GameData.ArcSwingTime; + private const int cd = GameData.ArcSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Arc; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } + } +} diff --git a/logic/GameClass/GameObj/GameObj.cs b/logic/GameClass/GameObj/GameObj.cs new file mode 100644 index 00000000..0adbdb35 --- /dev/null +++ b/logic/GameClass/GameObj/GameObj.cs @@ -0,0 +1,43 @@ +using Preparation.Interface; +using Preparation.Utility; +using System.Threading; + +namespace GameClass.GameObj +{ + /// + /// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR + /// + public abstract class GameObj : IGameObj + { + private readonly ReaderWriterLockSlim gameObjReaderWriterLock = new(); + public ReaderWriterLockSlim GameObjReaderWriterLock => gameObjReaderWriterLock; + protected readonly object gameObjLock = new(); + public object GameLock => gameObjLock; + protected readonly XY birthPos; + private readonly GameObjType type; + public GameObjType Type => type; + private static long currentMaxID = 0; // 目前游戏对象的最大ID + public const long invalidID = long.MaxValue; // 无效的ID + public long ID { get; } + protected XY position; + public abstract XY Position { get; } + public abstract bool IsRigid { get; } + public abstract ShapeType Shape { get; } + + private AtomicBool isRemoved = new(false); + public AtomicBool IsRemoved { get => isRemoved; } + public virtual bool TryToRemove() + { + return IsRemoved.TrySet(true); + } + public int Radius { get; } + public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; + public GameObj(XY initPos, int initRadius, GameObjType initType) + { + this.position = this.birthPos = initPos; + this.Radius = initRadius; + this.type = initType; + ID = Interlocked.Increment(ref currentMaxID); + } + } +} diff --git a/logic/GameClass/GameObj/Home.cs b/logic/GameClass/GameObj/Home.cs new file mode 100644 index 00000000..d0dc8812 --- /dev/null +++ b/logic/GameClass/GameObj/Home.cs @@ -0,0 +1,13 @@ +using Preparation.Interface; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameClass.GameObj +{ + public class Home : IHome + { + } +} diff --git a/logic/GameClass/GameObj/Immovable.cs b/logic/GameClass/GameObj/Immovable.cs new file mode 100644 index 00000000..00c769f8 --- /dev/null +++ b/logic/GameClass/GameObj/Immovable.cs @@ -0,0 +1,12 @@ +using Preparation.Utility; + +namespace GameClass.GameObj +{ + public abstract class Immovable : GameObj + { + public override XY Position => position; + public Immovable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) + { + } + } +} diff --git a/logic/GameClass/GameObj/Map.cs b/logic/GameClass/GameObj/Map.cs new file mode 100644 index 00000000..8a4d00f2 --- /dev/null +++ b/logic/GameClass/GameObj/Map.cs @@ -0,0 +1,255 @@ +using System.Collections.Generic; +using System.Threading; +using Preparation.Interface; +using Preparation.Utility; +using System; + +namespace GameClass.GameObj +{ + public partial class Map : IMap + { + private Dictionary> gameObjDict; + public Dictionary> GameObjDict => gameObjDict; + private Dictionary gameObjLockDict; + public Dictionary GameObjLockDict => gameObjLockDict; + public readonly uint[,] protoGameMap; + public uint[,] ProtoGameMap => protoGameMap; + public PlaceType GetPlaceType(IGameObj obj) + { + try + { + return (PlaceType)protoGameMap[obj.Position.x / GameData.NumOfPosGridPerCell, obj.Position.y / GameData.NumOfPosGridPerCell]; + } + catch + { + return PlaceType.Null; + } + } + public PlaceType GetPlaceType(XY pos) + { + try + { + return (PlaceType)protoGameMap[pos.x / GameData.NumOfPosGridPerCell, pos.y / GameData.NumOfPosGridPerCell]; + } + catch + { + return PlaceType.Null; + } + } + public bool IsOutOfBound(IGameObj obj) + { + return obj.Position.x >= GameData.MapLength - obj.Radius || obj.Position.x <= obj.Radius || obj.Position.y >= GameData.MapLength - obj.Radius || obj.Position.y <= obj.Radius; + } + public IOutOfBound GetOutOfBound(XY pos) + { + return new OutOfBoundBlock(pos); + } + public Ship? FindShipInID(long ID) + { + Ship? ship = null; + gameObjLockDict[GameObjType.Ship].EnterReadLock(); + try + { + foreach (Ship s in gameObjDict[GameObjType.Ship]) + { + if (s.ShipID == ID) + { + ship = s; + break; + } + } + } + finally + { + gameObjLockDict[GameObjType.Ship].ExitReadLock(); + } + return ship; + } + public GameObj? OneForInteract(XY Pos, GameObjType gameObjType) + { + GameObj? GameObjForInteract = null; + GameObjLockDict[gameObjType].EnterReadLock(); + try + { + foreach (GameObj gameObj in GameObjDict[gameObjType]) + { + if (GameData.ApproachToInteract(gameObj.Position, Pos)) + { + GameObjForInteract = gameObj; + break; + } + } + } + finally + { + GameObjLockDict[gameObjType].ExitReadLock(); + } + return GameObjForInteract; + } + public GameObj? OneInTheSameCell(XY Pos, GameObjType gameObjType) + { + GameObj? GameObjForInteract = null; + GameObjLockDict[gameObjType].EnterReadLock(); + try + { + foreach (GameObj gameObj in GameObjDict[gameObjType]) + { + if (GameData.IsInTheSameCell(gameObj.Position, Pos)) + { + GameObjForInteract = gameObj; + break; + } + } + } + finally + { + GameObjLockDict[gameObjType].ExitReadLock(); + } + return GameObjForInteract; + } + public GameObj? PartInTheSameCell(XY Pos, GameObjType gameObjType) + { + GameObj? GameObjForInteract = null; + GameObjLockDict[gameObjType].EnterReadLock(); + try + { + foreach (GameObj gameObj in GameObjDict[gameObjType]) + { + if (GameData.PartInTheSameCell(gameObj.Position, Pos)) + { + GameObjForInteract = gameObj; + break; + } + } + } + finally + { + GameObjLockDict[gameObjType].ExitReadLock(); + } + return GameObjForInteract; + } + public GameObj? OneForInteractInACross(XY Pos, GameObjType gameObjType) + { + GameObj? GameObjForInteract = null; + GameObjLockDict[gameObjType].EnterReadLock(); + try + { + foreach (GameObj gameObj in GameObjDict[gameObjType]) + { + if (GameData.ApproachToInteractInACross(gameObj.Position, Pos)) + { + GameObjForInteract = gameObj; + break; + } + } + } + finally + { + GameObjLockDict[gameObjType].ExitReadLock(); + } + return GameObjForInteract; + } + public bool CanSee(Ship ship, GameObj gameObj) + { + XY pos1 = ship.Position; + XY pos2 = gameObj.Position; + XY del = pos1 - pos2; + if (del * del > ship.ViewRange * ship.ViewRange) + return false; + if (del.x > del.y) + { + if (GetPlaceType(pos1) == PlaceType.Shadow && GetPlaceType(pos2) == PlaceType.Shadow) + { + for (int x = GameData.PosGridToCellX(pos1) + GameData.NumOfPosGridPerCell; x < GameData.PosGridToCellX(pos2); x += GameData.NumOfPosGridPerCell) + { + if (GetPlaceType(pos1 + del * (x / del.x)) != PlaceType.Shadow) + return false; + } + } + else + { + for (int x = GameData.PosGridToCellX(pos1) + GameData.NumOfPosGridPerCell; x < GameData.PosGridToCellX(pos2); x += GameData.NumOfPosGridPerCell) + { + if (GetPlaceType(pos1 + del * (x / del.x)) == PlaceType.Ruin) + return false; + } + } + } + else + { + if (GetPlaceType(pos1) == PlaceType.Shadow && GetPlaceType(pos2) == PlaceType.Shadow) + { + for (int y = GameData.PosGridToCellY(pos1) + GameData.NumOfPosGridPerCell; y < GameData.PosGridToCellY(pos2); y += GameData.NumOfPosGridPerCell) + { + if (GetPlaceType(pos1 + del * (y / del.y)) != PlaceType.Shadow) + return false; + } + } + else + { + for (int y = GameData.PosGridToCellY(pos1) + GameData.NumOfPosGridPerCell; y < GameData.PosGridToCellY(pos2); y += GameData.NumOfPosGridPerCell) + { + if (GetPlaceType(pos1 + del * (y / del.y)) == PlaceType.Ruin) + return false; + } + } + } + return true; + } + public bool Remove(GameObj gameObj) + { + GameObj? ToDel = null; + GameObjLockDict[gameObj.Type].EnterWriteLock(); + try + { + foreach (GameObj obj in GameObjDict[gameObj.Type]) + { + if (gameObj.ID == obj.ID) + { + ToDel = obj; + break; + } + } + if (ToDel != null) + { + GameObjDict[gameObj.Type].Remove(ToDel); + ToDel.TryToRemove(); + } + } + finally + { + GameObjLockDict[gameObj.Type].ExitWriteLock(); + } + return ToDel != null; + } + public bool RemoveJustFromMap(GameObj gameObj) + { + GameObjLockDict[gameObj.Type].EnterWriteLock(); + try + { + if (GameObjDict[gameObj.Type].Remove(gameObj)) + { + gameObj.TryToRemove(); + return true; + } + return false; + } + finally + { + GameObjLockDict[gameObj.Type].ExitWriteLock(); + } + } + public void Add(GameObj gameObj) + { + GameObjLockDict[gameObj.Type].EnterWriteLock(); + try + { + GameObjDict[gameObj.Type].Add(gameObj); + } + finally + { + GameObjLockDict[gameObj.Type].ExitWriteLock(); + } + } + } +} diff --git a/logic/GameClass/GameObj/MapGameTimer.cs b/logic/GameClass/GameObj/MapGameTimer.cs new file mode 100644 index 00000000..c8cb262e --- /dev/null +++ b/logic/GameClass/GameObj/MapGameTimer.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading; +using Preparation.Interface; + +namespace GameClass.GameObj +{ + public partial class Map + { + // xfgg说:爱因斯坦说,每个坐标系都有与之绑定的时钟,(x, y, z, ict) 构成四维时空坐标,在洛伦兹变换下满足矢量性(狗头) + private readonly GameTimer timer = new(); + public ITimer Timer => timer; + + public class GameTimer : ITimer + { + private long startTime; + public int nowTime() => (int)(Environment.TickCount64 - startTime); + + private bool isGaming = false; + public bool IsGaming + { + get => isGaming; + set + { + lock (isGamingLock) + isGaming = value; + } + } + + readonly object isGamingLock = new(); + + public bool StartGame(int timeInMilliseconds) + { + lock (isGamingLock) + { + if (isGaming) + return false; + isGaming = true; + startTime = Environment.TickCount64; + } + Thread.Sleep(timeInMilliseconds); + isGaming = false; + return true; + } + } + } +} diff --git a/logic/GameClass/GameObj/Movable.cs b/logic/GameClass/GameObj/Movable.cs new file mode 100644 index 00000000..0a157bfd --- /dev/null +++ b/logic/GameClass/GameObj/Movable.cs @@ -0,0 +1,127 @@ +using Preparation.Interface; +using Preparation.Utility; +using System.Threading; + +namespace GameClass.GameObj +{ + public abstract class Movable : GameObj, IMovable + { + protected readonly object actionLock = new(); + /// + /// Player.ActionLock > 其他.ActionLock / 其他Lock,应当避免两个Player的Actionlock互锁 + /// + public object ActionLock => actionLock; + private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); + /// + /// 规定MoveReaderWriterLock < ActionLock + /// + public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; + public Semaphore ThreadNum { get; } = new(1, 1); + protected long stateNum = 0; + public long StateNum + { + get + { + lock (actionLock) + return stateNum; + } + set + { + lock (actionLock) stateNum = value; + } + } + protected RunningStateType runningState = RunningStateType.Null; + public RunningStateType RunningState + { + get + { + lock (actionLock) return runningState; + } + set + { + lock (actionLock) + runningState = value; + } + } + public override XY Position + { + get + { + lock (actionLock) + return position; + } + } + protected XY facingDirection = new(1, 0); + public XY FacingDirection + { + get + { + lock (actionLock) + return facingDirection; + } + set + { + lock (actionLock) + facingDirection = value; + } + } + public AtomicBool IsMoving { get; } = new(false); + /// + /// 移动,改变坐标 + /// + public long MovingSetPos(XY moveVec, long stateNo) + { + + if (moveVec.x != 0 || moveVec.y != 0) + { + lock (actionLock) + { + if (!CanMove || IsRemoved) return -1; + if (stateNo != stateNum) return -1; + facingDirection = moveVec; + this.position += moveVec; + } + } + return moveVec * moveVec; + } + + public void ReSetPos(XY position) + { + lock (actionLock) + { + this.position = position; + } + } + private AtomicBool canMove = new(false); + public AtomicBool CanMove { get => canMove; } + public bool IsAvailableForMove => !IsMoving && CanMove && !IsRemoved; // 是否能接收移动指令 + private AtomicInt moveSpeed = new(0); + /// + /// 移动速度 + /// + public AtomicInt MoveSpeed { get => moveSpeed; } + protected int orgMoveSpeed; + /// + /// 原初移动速度 + /// + public int OrgMoveSpeed => orgMoveSpeed; + /* /// + /// 复活时数据重置 + /// + public virtual void Reset(PlaceType place) + { + lock (gameObjLock) + { + this.FacingDirection = new XY(1, 0); + isMoving = false; + CanMove = false; + IsRemoved = true; + this.Position = birthPos; + this.Place= place; + } + }*/ + public Movable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) + { + } + } +} diff --git a/logic/GameClass/GameObj/ObjOfShip.cs b/logic/GameClass/GameObj/ObjOfShip.cs new file mode 100644 index 00000000..061f14e9 --- /dev/null +++ b/logic/GameClass/GameObj/ObjOfShip.cs @@ -0,0 +1,33 @@ +using Preparation.Interface; +using Preparation.Utility; +using System.Threading; + +namespace GameClass.GameObj +{ + /// + /// 所有物,具有主人(Parent)(特定玩家)属性的对象 + /// + public abstract class ObjOfShip : Movable, IObjOfShip + { + private ReaderWriterLockSlim objOfShipReaderWriterLock = new(); + public ReaderWriterLockSlim ObjOfShipReaderWriterLock => objOfShipReaderWriterLock; + private IShip? parent = null; + public IShip? Parent + { + get + { + lock (objOfShipReaderWriterLock) + return parent; + } + set + { + lock (objOfShipReaderWriterLock) + parent = value; + } + } + public ObjOfShip(XY initPos, int initRadius, GameObjType initType) : + base(initPos, initRadius, initType) + { + } + } +} diff --git a/logic/GameClass/GameObj/OutOfBoundBlock.cs b/logic/GameClass/GameObj/OutOfBoundBlock.cs new file mode 100644 index 00000000..8f0c45d5 --- /dev/null +++ b/logic/GameClass/GameObj/OutOfBoundBlock.cs @@ -0,0 +1,19 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj +{ + /// + /// 逻辑墙 + /// + public class OutOfBoundBlock : Immovable, IOutOfBound + { + public OutOfBoundBlock(XY initPos) : + base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) + { + } + + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Square; + } +} diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs new file mode 100644 index 00000000..582939fb --- /dev/null +++ b/logic/GameClass/GameObj/Ship.cs @@ -0,0 +1,198 @@ +using Preparation.Interface; +using Preparation.Utility; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace GameClass.GameObj +{ + public class Ship : Movable, IShip + { + public AtomicLong TeamID { get; } = new AtomicLong(long.MaxValue); + public AtomicLong ShipID { get; } = new AtomicLong(long.MaxValue); + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Circle; + private readonly int viewRange; + public int ViewRange => viewRange; + public override bool IgnoreCollideExecutor(IGameObj targetObj) + { + if (IsRemoved) + return true; + if (targetObj.Type == GameObjType.Ship && XY.DistanceCeil3(targetObj.Position, this.Position) < this.Radius + targetObj.Radius - GameData.AdjustLength) + return true; + return false; + } + public LongWithVariableRange HP { get; } + public LongWithVariableRange Armor { get; } + public LongWithVariableRange Shield { get; } + private ShipType shipType = ShipType.Null; + public ShipType ShipType => shipType; + private ShipStateType shipState = ShipStateType.Null; + public ShipStateType ShipState => shipState; + private readonly IOccupation occupation; + public IOccupation Occupation => occupation; + public IntNumUpdateByCD BulletNum { get; } + private IProducer? producer = null; + public IProducer? ProducerModule => producer; + private IConstructor? constructor = null; + public IConstructor? ConstructorModule => constructor; + private IArmor? armor = null; + public IArmor? ArmorModule => armor; + private IShield? shield = null; + public IShield? ShieldModule => shield; + private IWeapon? weapon = null; + public IWeapon? WeaponModule => weapon; + private GameObj? whatInteractingWith = null; + public GameObj? WhatInteractingWith + { + get + { + lock (actionLock) + { + return whatInteractingWith; + } + } + } + private long ChangeShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + { + //只能被SetShipState引用 + if (runningState == RunningStateType.RunningSleepily) + { + ThreadNum.Release(); + } + runningState = running; + whatInteractingWith = gameObj; + shipState = value; + return ++stateNum; + } + private long ChangeShipStateInOneThread(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + { + if (runningState == RunningStateType.RunningSleepily) + { + ThreadNum.Release(); + } + runningState = running; + //只能被SetPlayerState引用 + whatInteractingWith = gameObj; + shipState = value; + return stateNum; + } + public long SetShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + GameObj? gameObj = (GameObj?)obj; + lock (actionLock) + { + ShipStateType nowShipState = ShipState; + if (nowShipState == value) return -1; + GameObj? lastObj = whatInteractingWith; + switch (nowShipState) + { + case ShipStateType.Attacking: + if (value == ShipStateType.Null || value == ShipStateType.Stunned || value == ShipStateType.Swinging) + return ChangeShipState(running, value, gameObj); + else return -1; + case ShipStateType.Stunned: + if (value == ShipStateType.Null) + return ChangeShipState(running, value, gameObj); + else return -1; + case ShipStateType.Swinging: + if (value == ShipStateType.Null || value == ShipStateType.Stunned) + return ChangeShipState(running, value, gameObj); + else return -1; + default: + return ChangeShipState(running, value, gameObj); + } + } + } + public long SetShipStateNaturally() + { + lock(actionLock) + { + runningState = RunningStateType.Null; + whatInteractingWith = null; + shipState = ShipStateType.Null; + return ++stateNum; + } + } + public bool ResetShipState(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + lock (actionLock) + { + if (state != stateNum) return false; + this.runningState = running; + whatInteractingWith = (GameObj?)obj; + shipState = value; + ++stateNum; + return true; + } + } + public bool ResetShipStateInOneThread(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + lock (actionLock) + { + if (state != stateNum) return false; + this.runningState = running; + whatInteractingWith = (GameObj?)obj; + shipState = value; + return true; + } + } + public bool StartThread(long stateNum, RunningStateType runningState) + { + lock (ActionLock) + { + if (this.StateNum == stateNum) + { + this.runningState = runningState; + return true; + } + } + return false; + } + public bool TryToRemoveFromGame(ShipStateType shipStateType) + { + lock (actionLock) + { + if (SetShipState(RunningStateType.RunningForcibly, shipStateType) == -1) return false; + TryToRemove(); + CanMove.SetReturnOri(false); + position = GameData.PosNotInGame; + } + return true; + } + public Ship(XY initPos, int initRadius, ShipType shipType) : + base(initPos, initRadius, GameObjType.Ship) + { + this.CanMove.SetReturnOri(true); + this.occupation = OccupationFactory.FindIOccupation(shipType); + this.viewRange = occupation.ViewRange; + this.HP = new(Occupation.MaxHp); + this.MoveSpeed.SetReturnOri(this.orgMoveSpeed = Occupation.MoveSpeed); + this.shipType = shipType; + switch (shipType) + { + case ShipType.CivilShip: + this.producer = new CivilProducer1(); + this.constructor = new CivilConstructor1(); + this.armor = null; + this.shield = null; + this.weapon = null; + break; + case ShipType.WarShip: + this.producer = null; + this.constructor = null; + this.armor = null; + this.shield = null; + this.weapon = new WarLaserGun(); + break; + case ShipType.FlagShip: + this.producer = null; + this.constructor = null; + this.armor = null; + this.shield = null; + this.weapon = new FlagLaserGun(); + break; + } + } + } +} diff --git a/logic/GameClass/GameObj/Team.cs b/logic/GameClass/GameObj/Team.cs new file mode 100644 index 00000000..57b8b623 --- /dev/null +++ b/logic/GameClass/GameObj/Team.cs @@ -0,0 +1,113 @@ +using Preparation.Utility; +using System.Collections.Generic; + +namespace GameClass.GameObj +{ + public class Team + { + private static long currentMaxTeamID = 0; + public static long CurrentMaxTeamID => currentMaxTeamID; + private readonly long teamID; + public long TeamID => teamID; + public const long invalidTeamID = long.MaxValue; + public const long noneTeamID = long.MinValue; + private readonly List shipList; + private Home home = new Home(); + public int Score { get; private set; } = 0; + public Ship? GetShip(long shipID) + { + foreach (Ship ship in shipList) + { + if (ship.ShipID == shipID) + return ship; + } + return null; + } + public bool AddShip(Ship ship) + { + switch(ship.ShipType) + { + case ShipType.CivilShip: + if (GetCivilShipNum() >= GameData.MaxCivilShipNum) + return false; + break; + case ShipType.WarShip: + if (GetWarShipNum() >= GameData.MaxWarShipNum) + return false; + break; + case ShipType.FlagShip: + if (GetFlagShipNum() >= GameData.MaxFlagShipNum) + return false; + break; + default: + return false; + } + shipList.Add(ship); + return true; + } + public int GetShipNum() + { + return shipList.Count; + } + public int GetCivilShipNum() + { + int num = 0; + foreach (Ship ship in shipList) + { + if (ship.ShipType == ShipType.CivilShip) + num++; + } + return num; + } + public int GetWarShipNum() + { + int num = 0; + foreach (Ship ship in shipList) + { + if (ship.ShipType == ShipType.WarShip) + num++; + } + return num; + } + public int GetFlagShipNum() + { + int num = 0; + foreach (Ship ship in shipList) + { + if (ship.ShipType == ShipType.FlagShip) + num++; + } + return num; + } + public void RemoveShip(Ship ship) + { + int i; + for (i = 0; i < shipList.Count; i++) + { + if (shipList[i] == ship) + break; + } + if (i < shipList.Count) + shipList.RemoveAt(i); + } + public long[] GetShipIDs() + { + long[] shipIDs = new long[shipList.Count]; + int i = 0; + foreach (Ship ship in shipList) + { + shipIDs[i++] = ship.ShipID; + } + return shipIDs; + } + public static bool TeamExists(long teamID) + { + return teamID < currentMaxTeamID; + } + public Team() + { + teamID = currentMaxTeamID++; + shipList = new List(); + } + } +} diff --git a/logic/GameEngine/CollisionChecker.cs b/logic/GameEngine/CollisionChecker.cs new file mode 100644 index 00000000..c61cdff2 --- /dev/null +++ b/logic/GameEngine/CollisionChecker.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Preparation.Interface; +using Preparation.Utility; + +namespace GameEngine +{ + internal class CollisionChecker + { + public IGameObj? CheckCollision(IMovable obj, XY Pos) + { + // 在列表中检查碰撞 + Func, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = + (IEnumerable lst, ReaderWriterLockSlim listLock) => + { + IGameObj? collisionObj = null; + listLock.EnterReadLock(); + try + { + foreach (var listObj in lst) + { + if (obj.WillCollideWith(listObj, Pos)) + { + collisionObj = listObj; + break; + } + } + } + finally + { + listLock.ExitReadLock(); + } + return collisionObj; + }; + + IGameObj? collisionObj; + foreach (var list in lists) + { + if ((collisionObj = CheckCollisionInList(list.Item1, list.Item2)) != null) + { + return collisionObj; + } + } + + return null; + } + /// + /// 碰撞检测,如果这样行走是否会与之碰撞,返回与之碰撞的物体 + /// + /// 移动的物体 + /// 移动的位移向量 + /// 和它碰撞的物体 + public IGameObj? CheckCollisionWhenMoving(IMovable obj, XY moveVec) + { + XY nextPos = obj.Position + moveVec; + if (!obj.IsRigid) + { + if (gameMap.IsOutOfBound(obj)) + return gameMap.GetOutOfBound(nextPos); + return null; + } + return CheckCollision(obj, nextPos); + } + /// + /// /// 可移动物体(圆)向矩形物体移动时,可移动且不会碰撞的最大距离。直接用double计算,防止误差 + /// + /// + /// 矩形的中心坐标 + /// + // private double MaxMoveToSquare(IMoveable obj, IGameObj square) + //{ + // double tmpMax; + // double angle = Math.Atan2(square.Position.y - obj.Position.y, square.Position.x - obj.Position.x); + // if (obj.WillCollideWith(square, obj.Position)) + // tmpMax = 0; + // else tmpMax = + // Math.Abs(XYPosition.DistanceFloor3(obj.Position, square.Position) - obj.Radius - + // (square.Radius / Math.Min(Math.Abs(Math.Cos(angle)), Math.Abs(Math.Sin(angle))))); + // return tmpMax; + // } + + // private double FindMaxOnlyConsiderWall(IMoveable obj, Vector moveVec) + //{ + // var desination = moveVec; + // double maxOnlyConsiderWall = moveVec.length; + // if (desination.length > 0) //如果length足够长,还是有可能穿墙的 + // { + // XYPosition nextXY = Vector.Vector2XY(desination) + obj.Position + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle)), (int)(obj.Radius * Math.Sin(moveVec.angle))); + // if (gameMap.IsWall(nextXY)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // maxOnlyConsiderWall = MaxMoveToSquare(obj, gameMap.GetCell(nextXY)); + // } + // else //考虑物体宽度 + // { + // double dist = 0; + // XYPosition nextXYConsiderWidth; + // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle + Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle + Math.PI / 4))); + // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); + // if (dist < maxOnlyConsiderWall) + // maxOnlyConsiderWall = dist; + // } + // nextXYConsiderWidth = nextXY + new XYPosition((int)(obj.Radius * Math.Cos(moveVec.angle - Math.PI / 4)), (int)(obj.Radius * Math.Sin(moveVec.angle - Math.PI / 4))); + // if (gameMap.IsWall(nextXYConsiderWidth)) //对下一步的位置进行检查,但这里只是考虑移动物体的宽度,只是考虑下一步能达到的最远位置 + // { + // dist = MaxMoveToSquare(obj, gameMap.GetCell(nextXYConsiderWidth)); + // if (dist < maxOnlyConsiderWall) + // maxOnlyConsiderWall = dist; + // } + // } + // } + // return maxOnlyConsiderWall; + // } + + /// + /// 寻找最大可能移动距离 + /// + /// 移动物体,默认obj.Rigid为true + /// 下一步要到达的位置 + /// 移动的位移向量,默认与nextPos协调 + /// 最大可能的移动距离 + public double FindMax(IMovable obj, XY nextPos, XY moveVec) + { + double tmpMax = uint.MaxValue; // 暂存最大值 + + double maxDistance = uint.MaxValue; + foreach (var listWithLock in lists) + { + var lst = listWithLock.Item1; + var listLock = listWithLock.Item2; + listLock.EnterReadLock(); + try + { + foreach (IGameObj listObj in lst) + { + // 如果再走一步发生碰撞 + if (obj.WillCollideWith(listObj, nextPos)) + { + { + switch (listObj.Shape) // 默认obj为圆形 + { + case ShapeType.Circle: + { + // 计算两者之间的距离 + double mod = XY.DistanceFloor3(listObj.Position, obj.Position); + int orgDeltaX = listObj.Position.x - obj.Position.x; + int orgDeltaY = listObj.Position.y - obj.Position.y; + + if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠 + { + tmpMax = 0; + } + else + { + double tmp = mod - obj.Radius - listObj.Radius; + // 计算能走的最长距离,好像这么算有一点误差? + tmp = ((int)(tmp * 1000 / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.Angle()))); + if (tmp < 0 || tmp > uint.MaxValue || double.IsNaN(tmp)) + { + tmpMax = uint.MaxValue; + } + else + tmpMax = tmp / 1000.0; + } + break; + } + case ShapeType.Square: + { + // if (obj.WillCollideWith(listObj, obj.Position)) + // tmpMax = 0; + // else tmpMax = MaxMoveToSquare(obj, listObj); + // break; + if (obj.WillCollideWith(listObj, obj.Position)) + tmpMax = 0; + else + { + // 二分查找最大可能移动距离 + int left = 0, right = (int)moveVec.Length(); + while (left < right - 1) + { + int mid = (right - left) / 2 + left; + if (obj.WillCollideWith(listObj, obj.Position + new XY(moveVec, mid))) + { + right = mid; + } + else + left = mid; + } + tmpMax = (uint)left; + } + break; + } + default: + tmpMax = uint.MaxValue; + break; + } + if (tmpMax < maxDistance) + maxDistance = tmpMax; + } + } + } + } + finally + { + listLock.ExitReadLock(); + } + } + return maxDistance; + } + + readonly IMap gameMap; + private readonly Tuple, ReaderWriterLockSlim>[] lists; + + public CollisionChecker(IMap gameMap) + { + this.gameMap = gameMap; + lists = new Tuple, ReaderWriterLockSlim>[gameMap.GameObjDict.Count]; + int i = 0; + foreach (var keyValuePair in gameMap.GameObjDict) + { + lists[i++] = new Tuple, ReaderWriterLockSlim>(keyValuePair.Value as IList, gameMap.GameObjLockDict[keyValuePair.Key]); + } + } + } +} diff --git a/logic/GameEngine/GameEngine.csproj b/logic/GameEngine/GameEngine.csproj new file mode 100644 index 00000000..f6227783 --- /dev/null +++ b/logic/GameEngine/GameEngine.csproj @@ -0,0 +1,17 @@ + + + + Library + net6.0 + enable + + + + + + + + + + + diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs new file mode 100644 index 00000000..f8b108ae --- /dev/null +++ b/logic/GameEngine/MoveEngine.cs @@ -0,0 +1,234 @@ +using System; +using System.Threading; +using Preparation.Interface; +using Preparation.Utility; +using Timothy.FrameRateTask; + +namespace GameEngine +{ + public class MoveEngine + { + /// + /// 碰撞结束后要做的事情 + /// + public enum AfterCollision + { + ContinueCheck = 0, // 碰撞后继续检查其他碰撞,暂时没用 + MoveMax = 1, // 行走最远距离 + Destroyed = 2 // 物体已经毁坏 + } + + private readonly ITimer gameTimer; + private readonly Action EndMove; + + public IGameObj? CheckCollision(IMovable obj, XY Pos) + { + return collisionChecker.CheckCollision(obj, Pos); + } + + private readonly CollisionChecker collisionChecker; + private readonly Func OnCollision; + /// + /// Constrctor + /// + /// 游戏地图 + /// 发生碰撞时要做的事情,第一个参数为移动的物体,第二个参数为撞到的物体,第三个参数为移动的位移向量,返回值见AfterCollision的定义 + /// 结束碰撞时要做的事情 + public MoveEngine( + IMap gameMap, + Func OnCollision, + Action EndMove + ) + { + this.gameTimer = gameMap.Timer; + this.EndMove = EndMove; + this.OnCollision = OnCollision; + this.collisionChecker = new CollisionChecker(gameMap); + } + + /// + /// 在无碰撞的前提下行走最远的距离 + /// + /// 移动物体,默认obj.Rigid为true + /// 移动的位移向量 + private bool MoveMax(IMovable obj, XY moveVec, long stateNum) + { + /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ + XY nextPos = obj.Position + moveVec; + double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec); + maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.NumOfStepPerSecond); + return (obj.MovingSetPos(new XY(moveVec, maxLen), stateNum)) >= 0; + } + + private bool LoopDo(IMovable obj, double direction, ref double deltaLen, long stateNum) + { + double moveVecLength = obj.MoveSpeed / GameData.NumOfStepPerSecond; + XY res = new(direction, moveVecLength); + + // 越界情况处理:如果越界,则与越界方块碰撞 + bool flag; // 循环标志 + do + { + flag = false; + IGameObj? collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res); + if (collisionObj == null) + break; + + switch (OnCollision(obj, collisionObj, res)) + { + case AfterCollision.ContinueCheck: + flag = true; + break; + case AfterCollision.Destroyed: + Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); + return false; + case AfterCollision.MoveMax: + if (!MoveMax(obj, res, stateNum)) return false; + moveVecLength = 0; + res = new XY(direction, moveVecLength); + break; + } + } while (flag); + + long moveL = obj.MovingSetPos(res, stateNum); + if (moveL == -1) return false; + deltaLen = deltaLen + moveVecLength - Math.Sqrt(moveL); + return true; + } + + public void MoveObj(IMovable obj, int moveTime, double direction, long stateNum) + { + if (!gameTimer.IsGaming) return; + lock (obj.ActionLock) + { + if (!obj.IsAvailableForMove) { EndMove(obj); return; } + obj.IsMoving.SetReturnOri(true); + } + + new Thread + ( + () => + { + double moveVecLength = 0.0; + XY res = new(direction, moveVecLength); + double deltaLen = (double)0.0; // 转向,并用deltaLen存储行走的误差 + IGameObj? collisionObj = null; + bool isEnded = false; + + bool flag; // 循环标志 + do + { + flag = false; + collisionObj = collisionChecker.CheckCollision(obj, obj.Position); + if (collisionObj == null) + break; + + switch (OnCollision(obj, collisionObj, res)) + { + case AfterCollision.ContinueCheck: + flag = true; + break; + case AfterCollision.Destroyed: + Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); + isEnded = true; + break; + case AfterCollision.MoveMax: + break; + } + } while (flag); + + if (isEnded) + { + obj.IsMoving.SetReturnOri(false); + EndMove(obj); + return; + } + else + { + if (moveTime >= GameData.NumOfPosGridPerCell / GameData.NumOfStepPerSecond) + { + Thread.Sleep(GameData.NumOfPosGridPerCell / GameData.NumOfStepPerSecond); + new FrameRateTaskExecutor( + () => gameTimer.IsGaming, + () => + { + if (obj.StateNum != stateNum || !obj.CanMove || obj.IsRemoved) + return !(isEnded = true); + return !(isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum)); + }, + GameData.NumOfPosGridPerCell / GameData.NumOfStepPerSecond, + () => + { + return 0; + }, + maxTotalDuration: moveTime - GameData.NumOfPosGridPerCell / GameData.NumOfStepPerSecond + ) + { + AllowTimeExceed = true, + MaxTolerantTimeExceedCount = ulong.MaxValue, + TimeExceedAction = b => + { + if (b) + Console.WriteLine("Fatal Error: The computer runs so slow that the object cannot finish moving during this time!!!!!!"); + +#if DEBUG + else + { + Console.WriteLine("Debug info: Object moving time exceed for once."); + } +#endif + } + }.Start(); + if (!isEnded && obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) + isEnded = !LoopDo(obj, direction, ref deltaLen, stateNum); + } + if (isEnded) + { + obj.IsMoving.SetReturnOri(false); + EndMove(obj); + return; + } + if (obj.StateNum == stateNum && obj.CanMove && !obj.IsRemoved) + { + int leftTime = moveTime % (GameData.NumOfPosGridPerCell / GameData.NumOfStepPerSecond); + if (leftTime > 0) + { + Thread.Sleep(leftTime); // 多移动的在这里补回来 + } + do + { + flag = false; + moveVecLength = (double)deltaLen + leftTime * obj.MoveSpeed / GameData.NumOfPosGridPerCell; + res = new XY(direction, moveVecLength); + if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) + { + obj.MovingSetPos(res, stateNum); + } + else + { + switch (OnCollision(obj, collisionObj, res)) + { + case AfterCollision.ContinueCheck: + flag = true; + break; + case AfterCollision.Destroyed: + Debugger.Output(obj, " collide with " + collisionObj.ToString() + " and has been removed from the game."); + isEnded = true; + break; + case AfterCollision.MoveMax: + MoveMax(obj, res, stateNum); + moveVecLength = 0; + res = new XY(direction, moveVecLength); + break; + } + } + } while (flag); + } + obj.IsMoving.SetReturnOri(false); // 结束移动 + EndMove(obj); + } + } + ).Start(); + } + } +} diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs new file mode 100644 index 00000000..1a0f56f8 --- /dev/null +++ b/logic/Gaming/AttackManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading; +using System.Collections.Generic; +using GameClass.GameObj; +using Preparation.Utility; +using GameEngine; +using Preparation.Interface; + +namespace Gaming +{ + internal class AttackManager + { + } +} diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs new file mode 100644 index 00000000..f6b6dd11 --- /dev/null +++ b/logic/Gaming/Game.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Collections.Generic; +using Preparation.Utility; +using Preparation.Interface; +using GameClass.GameObj; + +namespace Gaming +{ + public partial class Game + { + public struct ShipInitInfo + { + public long teamID; + public long playerID; + public ShipInitInfo(long teamID, long playerID) + { + this.teamID = teamID; + this.playerID = playerID; + } + } + private readonly List teamList; + public List TeamList => teamList; + private readonly Map gameMap; + public Map GameMap => gameMap; + } +} diff --git a/logic/Gaming/Gaming.csproj b/logic/Gaming/Gaming.csproj new file mode 100644 index 00000000..5d635c7f --- /dev/null +++ b/logic/Gaming/Gaming.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + + + + + + + + + diff --git a/logic/Gaming/HealthManager.cs b/logic/Gaming/HealthManager.cs new file mode 100644 index 00000000..4aa1d614 --- /dev/null +++ b/logic/Gaming/HealthManager.cs @@ -0,0 +1,13 @@ +using Preparation.Utility; +using GameClass.GameObj; + +namespace Gaming +{ + public partial class Game + { + private readonly HealthManager healthManager; + private partial class HealthManager + { + } + } +} diff --git a/logic/Gaming/ModuleManager.cs b/logic/Gaming/ModuleManager.cs new file mode 100644 index 00000000..4e4e401a --- /dev/null +++ b/logic/Gaming/ModuleManager.cs @@ -0,0 +1,15 @@ +using Preparation.Utility; +using GameClass.GameObj; + +namespace Gaming +{ + public partial class Game + { + private readonly ModuleManager moduleManager; + private partial class ModuleManager + { + public bool PurchaseProducer(Ship ship, ProducerType producerType, int parameter) + { } + } + } +} diff --git a/logic/Gaming/ShipManager.cs b/logic/Gaming/ShipManager.cs new file mode 100644 index 00000000..0bb9c581 --- /dev/null +++ b/logic/Gaming/ShipManager.cs @@ -0,0 +1,28 @@ +using System.Threading; +using GameClass.GameObj; +using Preparation.Utility; +using Preparation.Interface; + +namespace Gaming +{ + public partial class Game + { + private readonly ShipManager shipManager; + private partial class ShipManager + { + readonly Map gameMap; + public ShipManager(Map gameMap) + { + this.gameMap = gameMap; + } + } + public Ship? AddShip(XY pos, long teamID, long shipID, ShipType shipType) + { + Ship newShip = new Ship(pos, GameData.ShipRadius, shipType); + gameMap.Add(newShip); + newShip.TeamID.SetReturnOri(teamID); + newShip.ShipID.SetReturnOri(shipID); + return newShip; + } + } +} diff --git a/logic/Preparation/Interface/IGameObj.cs b/logic/Preparation/Interface/IGameObj.cs new file mode 100644 index 00000000..f7cac913 --- /dev/null +++ b/logic/Preparation/Interface/IGameObj.cs @@ -0,0 +1,16 @@ +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IGameObj + { + public GameObjType Type { get; } + public long ID { get; } + public XY Position { get; } // if Square, Pos equals the center + public bool IsRigid { get; } + public AtomicBool IsRemoved { get; } + public ShapeType Shape { get; } + public int Radius { get; } // if Square, Radius equals half length of one side + public bool IgnoreCollideExecutor(IGameObj targetObj); // 忽略碰撞,在具体类中实现 + } +} diff --git a/logic/Preparation/Interface/IHome.cs b/logic/Preparation/Interface/IHome.cs new file mode 100644 index 00000000..76cf7443 --- /dev/null +++ b/logic/Preparation/Interface/IHome.cs @@ -0,0 +1,12 @@ +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IHome + { + public AtomicLong TeamID { get; } + public LongWithVariableRange HP { get; } + public long Score { get; } + public void AddScore(long add); + } +} diff --git a/logic/Preparation/Interface/IMap.cs b/logic/Preparation/Interface/IMap.cs new file mode 100644 index 00000000..72d1c013 --- /dev/null +++ b/logic/Preparation/Interface/IMap.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading; +using System.Timers; +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IMap + { + ITimer Timer { get; } + + // the two dicts must have same keys + Dictionary> GameObjDict { get; } + Dictionary GameObjLockDict { get; } + + public uint[,] ProtoGameMap { get; } + public PlaceType GetPlaceType(IGameObj obj); + public bool IsOutOfBound(IGameObj obj); + public IOutOfBound GetOutOfBound(XY pos); // 返回新建的一个OutOfBound对象 + } +} diff --git a/logic/Preparation/Interface/IModule.cs b/logic/Preparation/Interface/IModule.cs new file mode 100644 index 00000000..6222ca7b --- /dev/null +++ b/logic/Preparation/Interface/IModule.cs @@ -0,0 +1,447 @@ +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IModule + { + public int Cost { get; } + } + public interface IProducer : IModule + { + public int ProduceSpeed { get; } + } + public interface IConstructor : IModule + { + public int ConstructSpeed { get; } + } + public interface IArmor : IModule + { + public int ArmorHP { get; } + } + public interface IShield : IModule + { + public int ShieldHP { get; } + } + public interface IWeapon : IModule + { + public BulletType BulletType { get; } + } + public class CivilProducer1 : IProducer + { + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer1PerSecond; + public int ProduceSpeed => produceSpeed; + } + public class CivilProducer2 : IProducer + { + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer2PerSecond; + public int ProduceSpeed => produceSpeed; + } + public class CivilProducer3 : IProducer + { + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer3PerSecond; + public int ProduceSpeed => produceSpeed; + } + public class CivilConstructor1 : IConstructor + { + private const int cost = GameData.CivilShipConstructor1Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor1Speed; + public int ConstructSpeed => constructSpeed; + } + public class CivilConstructor2 : IConstructor + { + private const int cost = GameData.CivilShipConstructor2Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor2Speed; + public int ConstructSpeed => constructSpeed; + } + public class CivilConstructor3 : IConstructor + { + private const int cost = GameData.CivilShipConstructor3Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor2Speed; + public int ConstructSpeed => constructSpeed; + } + public class CivilArmor1 : IArmor + { + private const int cost = GameData.CivilShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; + } + public class CivilShield1 : IShield + { + private const int cost = GameData.CivilShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; + } + public class CivilLaserGun : IWeapon + { + private const int cost = GameData.CivilShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; + } + public class WarArmor1 : IArmor + { + private const int cost = GameData.WarShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; + } + public class WarArmor2 : IArmor + { + private const int cost = GameData.WarShipArmor2Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor2; + public int ArmorHP => armorHP; + } + public class WarArmor3 : IArmor + { + private const int cost = GameData.WarShipArmor3Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor3; + public int ArmorHP => armorHP; + } + public class WarShield1 : IShield + { + private const int cost = GameData.WarShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; + } + public class WarShield2 : IShield + { + private const int cost = GameData.WarShipShield2Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield2; + public int ShieldHP => shieldHP; + } + public class WarShield3 : IShield + { + private const int cost = GameData.WarShipShield3Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield3; + public int ShieldHP => shieldHP; + } + public class WarLaserGun : IWeapon + { + private const int cost = GameData.WarShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; + } + public class WarPlasmaGun : IWeapon + { + private const int cost = GameData.WarShipPlasmaGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Plasma; + public BulletType BulletType => bulletType; + } + public class WarShellGun : IWeapon + { + private const int cost = GameData.WarShipShellGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Shell; + public BulletType BulletType => bulletType; + } + public class WarMissileGun : IWeapon + { + private const int cost = GameData.WarShipMissileGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Missile; + public BulletType BulletType => bulletType; + } + public class WarArcGun : IWeapon + { + private const int cost = GameData.WarShipArcGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Arc; + public BulletType BulletType => bulletType; + } + public class FlagProducer1 : IProducer + { + private const int cost = GameData.FlagShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer1PerSecond; + public int ProduceSpeed => produceSpeed; + } + public class FlagConstructor1 : IConstructor + { + private const int cost = GameData.FlagShipConstructor1Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor1Speed; + public int ConstructSpeed => constructSpeed; + } + public class FlagArmor1 : IArmor + { + private const int cost = GameData.FlagShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; + } + public class FlagArmor2 : IArmor + { + private const int cost = GameData.FlagShipArmor2Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor2; + public int ArmorHP => armorHP; + } + public class FlagArmor3 : IArmor + { + private const int cost = GameData.FlagShipArmor3Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor3; + public int ArmorHP => armorHP; + } + public class FlagShield1 : IShield + { + private const int cost = GameData.FlagShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; + } + public class FlagShield2 : IShield + { + private const int cost = GameData.FlagShipShield2Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield2; + public int ShieldHP => shieldHP; + } + public class FlagShield3 : IShield + { + private const int cost = GameData.FlagShipShield3Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield3; + public int ShieldHP => shieldHP; + } + public class FlagLaserGun : IWeapon + { + private const int cost = GameData.FlagShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; + } + public class FlagPlasmaGun : IWeapon + { + private const int cost = GameData.FlagShipPlasmaGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Plasma; + public BulletType BulletType => bulletType; + } + public class FlagShellGun : IWeapon + { + private const int cost = GameData.FlagShipShellGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Shell; + public BulletType BulletType => bulletType; + } + public class FlagMissileGun : IWeapon + { + private const int cost = GameData.FlagShipMissileGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Missile; + public BulletType BulletType => bulletType; + } + public class FlagArcGun : IWeapon + { + private const int cost = GameData.FlagShipArcGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Arc; + public BulletType BulletType => bulletType; + } + public static class ModuleFactory + { + public static IProducer FindIProducer(ShipType shipType, ProducerType producerType) + { + switch (shipType) + { + case ShipType.CivilShip: + switch (producerType) + { + case ProducerType.Producer1: + return new CivilProducer1(); + case ProducerType.Producer2: + return new CivilProducer2(); + case ProducerType.Producer3: + return new CivilProducer3(); + default: + return new CivilProducer1(); + } + case ShipType.FlagShip: + switch (producerType) + { + case ProducerType.Producer1: + return new FlagProducer1(); + default: + return new FlagProducer1(); + } + default: + return new CivilProducer1(); + } + } + public static IConstructor FindIConstructor(ShipType shipType, ConstructorType constructorType) + { + switch (shipType) + { + case ShipType.CivilShip: + switch (constructorType) + { + case ConstructorType.Constructor1: + return new CivilConstructor1(); + case ConstructorType.Constructor2: + return new CivilConstructor2(); + case ConstructorType.Constructor3: + return new CivilConstructor3(); + default: + return new CivilConstructor1(); + } + case ShipType.FlagShip: + switch (constructorType) + { + case ConstructorType.Constructor1: + return new FlagConstructor1(); + default: + return new FlagConstructor1(); + } + default: + return new CivilConstructor1(); + } + } + public static IArmor FindIArmor(ShipType shipType, ArmorType armorType) + { + switch (shipType) + { + case ShipType.CivilShip: + switch (armorType) + { + case ArmorType.Armor1: + return new CivilArmor1(); + default: + return new CivilArmor1(); + } + case ShipType.WarShip: + switch (armorType) + { + case ArmorType.Armor1: + return new WarArmor1(); + case ArmorType.Armor2: + return new WarArmor2(); + case ArmorType.Armor3: + return new WarArmor3(); + default: + return new WarArmor1(); + } + case ShipType.FlagShip: + switch (armorType) + { + case ArmorType.Armor1: + return new FlagArmor1(); + case ArmorType.Armor2: + return new FlagArmor2(); + case ArmorType.Armor3: + return new FlagArmor3(); + default: + return new FlagArmor1(); + } + default: + return new CivilArmor1(); + } + } + public static IShield FindIShield(ShipType shipType, ShieldType shieldType) + { + switch (shipType) + { + case ShipType.CivilShip: + switch (shieldType) + { + case ShieldType.Shield1: + return new CivilShield1(); + default: + return new CivilShield1(); + } + case ShipType.WarShip: + switch (shieldType) + { + case ShieldType.Shield1: + return new WarShield1(); + case ShieldType.Shield2: + return new WarShield2(); + case ShieldType.Shield3: + return new WarShield3(); + default: + return new WarShield1(); + } + case ShipType.FlagShip: + switch (shieldType) + { + case ShieldType.Shield1: + return new FlagShield1(); + case ShieldType.Shield2: + return new FlagShield2(); + case ShieldType.Shield3: + return new FlagShield3(); + default: + return new FlagShield1(); + } + default: + return new CivilShield1(); + } + } + public static IWeapon FindIWeapon(ShipType shipType, WeaponType weaponType) + { + switch (shipType) + { + case ShipType.CivilShip: + switch (weaponType) + { + case WeaponType.LaserGun: + return new CivilLaserGun(); + default: + return new CivilLaserGun(); + } + case ShipType.WarShip: + switch (weaponType) + { + case WeaponType.LaserGun: + return new WarLaserGun(); + case WeaponType.PlasmaGun: + return new WarPlasmaGun(); + case WeaponType.ShellGun: + return new WarShellGun(); + case WeaponType.MissileGun: + return new WarMissileGun(); + case WeaponType.ArcGun: + return new WarArcGun(); + default: + return new WarLaserGun(); + } + case ShipType.FlagShip: + switch (weaponType) + { + case WeaponType.LaserGun: + return new FlagLaserGun(); + case WeaponType.PlasmaGun: + return new FlagPlasmaGun(); + case WeaponType.ShellGun: + return new FlagShellGun(); + case WeaponType.MissileGun: + return new FlagMissileGun(); + case WeaponType.ArcGun: + return new FlagArcGun(); + default: + return new FlagLaserGun(); + } + default: + return new CivilLaserGun(); + } + } + } +} diff --git a/logic/Preparation/Interface/IMovable.cs b/logic/Preparation/Interface/IMovable.cs new file mode 100644 index 00000000..ab9b35e9 --- /dev/null +++ b/logic/Preparation/Interface/IMovable.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IMovable : IGameObj + { + public XY FacingDirection { get; set; } + object ActionLock { get; } + public AtomicInt MoveSpeed { get; } + public AtomicBool CanMove { get; } + public AtomicBool IsMoving { get; } + public bool IsAvailableForMove { get; } + public long StateNum { get; } + public Semaphore ThreadNum { get; } + public long MovingSetPos(XY moveVec, long stateNum); + public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 + { + if (targetObj == null) + return false; + // 会移动的只有子弹和人物,都是Circle + if (!targetObj.IsRigid || targetObj.ID == ID) + return false; + + if (IgnoreCollideExecutor(targetObj) || targetObj.IgnoreCollideExecutor(this)) + return false; + + if (targetObj.Shape == ShapeType.Circle) + { + return XY.DistanceCeil3(nextPos, targetObj.Position) < targetObj.Radius + Radius; + } + else // Square + { + long deltaX = Math.Abs(nextPos.x - targetObj.Position.x), deltaY = Math.Abs(nextPos.y - targetObj.Position.y); + if (deltaX >= targetObj.Radius + Radius || deltaY >= targetObj.Radius + Radius) + return false; + if (deltaX < targetObj.Radius || deltaY < targetObj.Radius) + return true; + else + return ((long)(deltaX - targetObj.Radius) * (deltaX - targetObj.Radius)) + ((long)(deltaY - targetObj.Radius) * (deltaY - targetObj.Radius)) <= (long)Radius * (long)Radius; + } + } + } +} diff --git a/logic/Preparation/Interface/IObjOfShip.cs b/logic/Preparation/Interface/IObjOfShip.cs new file mode 100644 index 00000000..78625632 --- /dev/null +++ b/logic/Preparation/Interface/IObjOfShip.cs @@ -0,0 +1,7 @@ +namespace Preparation.Interface +{ + public interface IObjOfShip : IGameObj + { + IShip? Parent { get; set; } + } +} diff --git a/logic/Preparation/Interface/IOccupation.cs b/logic/Preparation/Interface/IOccupation.cs new file mode 100644 index 00000000..b12e34da --- /dev/null +++ b/logic/Preparation/Interface/IOccupation.cs @@ -0,0 +1,56 @@ +using Preparation.Utility; +using System.Collections.Generic; + +namespace Preparation.Interface +{ + public interface IOccupation + { + public int MoveSpeed { get; } + public int MaxHp { get; } + public int ViewRange { get; } + } + public class CivilShip : IOccupation + { + private const int moveSpeed = GameData.CivilShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.CivilShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.CivilShipViewRange; + public int ViewRange => viewRange; + } + public class WarShip : IOccupation + { + private const int moveSpeed = GameData.WarShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.WarShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.WarShipViewRange; + public int ViewRange => viewRange; + } + public class FlagShip : IOccupation + { + private const int moveSpeed = GameData.FlagShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.FlagShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.FlagShipViewRange; + public int ViewRange => viewRange; + } + public static class OccupationFactory + { + public static IOccupation FindIOccupation(ShipType shipType) + { + switch (shipType) + { + case ShipType.CivilShip: + return new CivilShip(); + case ShipType.WarShip: + return new WarShip(); + case ShipType.FlagShip: + return new FlagShip(); + default: + return new CivilShip(); + } + } + } +} diff --git a/logic/Preparation/Interface/IOutOfBound.cs b/logic/Preparation/Interface/IOutOfBound.cs new file mode 100644 index 00000000..5fe0e9d6 --- /dev/null +++ b/logic/Preparation/Interface/IOutOfBound.cs @@ -0,0 +1,7 @@ +namespace Preparation.Interface +{ + public interface IOutOfBound : IGameObj + { + // 接口不定义内容,为引擎使用 + } +} diff --git a/logic/Preparation/Interface/IShip.cs b/logic/Preparation/Interface/IShip.cs new file mode 100644 index 00000000..b02092ec --- /dev/null +++ b/logic/Preparation/Interface/IShip.cs @@ -0,0 +1,17 @@ +using Preparation.Utility; + +namespace Preparation.Interface +{ + public interface IShip : IMovable + { + public AtomicLong TeamID { get; } + public LongWithVariableRange HP { get; } + public LongWithVariableRange Armor { get; } + public LongWithVariableRange Shield { get; } + public ShipType ShipType { get; } + public ShipStateType ShipState { get; } + public IntNumUpdateByCD BulletNum { get; } + public long SetShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, IGameObj? obj = null); + public bool ResetShipState(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null); + } +} diff --git a/logic/Preparation/Interface/ITimer.cs b/logic/Preparation/Interface/ITimer.cs new file mode 100644 index 00000000..af9c5cd2 --- /dev/null +++ b/logic/Preparation/Interface/ITimer.cs @@ -0,0 +1,9 @@ +namespace Preparation.Interface +{ + public interface ITimer + { + bool IsGaming { get; set; } + public int nowTime(); + public bool StartGame(int timeInMilliseconds); + } +} diff --git a/logic/Preparation/Interface/IWormhole.cs b/logic/Preparation/Interface/IWormhole.cs new file mode 100644 index 00000000..e46d6dbf --- /dev/null +++ b/logic/Preparation/Interface/IWormhole.cs @@ -0,0 +1,11 @@ +using Preparation.Utility; +using System.Collections.Generic; + +namespace Preparation.Interface +{ + public interface IWormhole : IGameObj + { + public List Entrance { get; } + public List Content { get; } + } +} diff --git a/logic/Preparation/Preparation.csproj b/logic/Preparation/Preparation.csproj new file mode 100644 index 00000000..322b60fb --- /dev/null +++ b/logic/Preparation/Preparation.csproj @@ -0,0 +1,19 @@ + + + + Library + net6.0 + + + enable + + + + + + + + + + + diff --git a/logic/Preparation/Utility/Debugger.cs b/logic/Preparation/Utility/Debugger.cs new file mode 100644 index 00000000..8627e316 --- /dev/null +++ b/logic/Preparation/Utility/Debugger.cs @@ -0,0 +1,20 @@ +using System; + +namespace Preparation.Utility +{ + public class Debugger + { + static public void Output(object current, string str) + { +#if DEBUG + Console.WriteLine(current.GetType() + " " + current.ToString() + " " + str); +#endif + } + static public void Output(string str) + { +#if DEBUG + Console.WriteLine(str); +#endif + } + } +} diff --git a/logic/Preparation/Utility/EnumType.cs b/logic/Preparation/Utility/EnumType.cs new file mode 100644 index 00000000..83bc113c --- /dev/null +++ b/logic/Preparation/Utility/EnumType.cs @@ -0,0 +1,155 @@ +namespace Preparation.Utility +{ + /// + /// 装甲类型 + /// + public enum ArmorType + { + Null = 0, + Armor1 = 1, + Armor2 = 2, + Armor3 = 3, + } + /// + /// 子弹类型 + /// + public enum BulletType + { + Null = 0, + Laser = 1, + Plasma = 2, + Shell = 3, + Missile = 4, + Arc = 5, + } + /// + /// 建造器类型 + /// + public enum ConstructorType + { + Null = 0, + Constructor1 = 1, + Constructor2 = 2, + Constructor3 = 3, + } + /// + /// 游戏对象类型 + /// + public enum GameObjType + { + Null = 0, + Ship = 1, + Bullet = 2, + BombedBullet = 3, + + Ruin = 4, + Shadow = 5, + Asteroid = 6, + Resource = 7, + Construction = 8, + Wormhole = 9, + Home = 10, + OutOfBoundBlock = 11, + } + /// + /// 武器类型 + /// + public enum GunType + { + Null = 0, + LaserGun = 1, + PlasmaGun = 2, + ShellGun = 3, + MissileGun = 4, + ArcGun = 5, + } + public enum PlaceType + { + Null = 0, + BirthPoint = 1, + Home = 2, + Ruin = 3, + Shadow = 4, + Asteroid = 5, + Resource = 6, + Construction = 7, + Wormhole = 8, + } + /// + /// 采集器类型 + /// + public enum ProducerType + { + Null = 0, + Producer1 = 1, + Producer2 = 2, + Producer3 = 3, + } + /// + /// 运动状态类型 + /// + public enum RunningStateType + { + Null = 0, + Waiting = 1, + RunningActively = 2, + RunningSleepily = 3, + RunningForcibly = 4, + } + /// + /// 形状类型 + /// + public enum ShapeType + { + Null = 0, + Circle = 1, + Square = 2 + } + /// + /// 护盾类型 + /// + public enum ShieldType + { + Null = 0, + Shield1 = 1, + Shield2 = 2, + Shield3 = 3, + } + /// + /// 舰船状态类型 + /// + public enum ShipStateType + { + Null = 0, + Producing = 1, + Constructing = 2, + Recovering = 3, + Recycling = 4, + Attacking = 5, + Swinging = 6, + Stunned = 7, + Moving = 8, + } + /// + /// 舰船类型 + /// + public enum ShipType + { + Null = 0, + CivilShip = 1, + WarShip = 2, + FlagShip = 3, + } + /// + /// 武器类型 + /// + public enum WeaponType + { + Null = 0, + LaserGun = 1, + PlasmaGun = 2, + ShellGun = 3, + MissileGun = 4, + ArcGun = 5, + } +} \ No newline at end of file diff --git a/logic/Preparation/Utility/GameData.cs b/logic/Preparation/Utility/GameData.cs new file mode 100644 index 00000000..9f30a1d7 --- /dev/null +++ b/logic/Preparation/Utility/GameData.cs @@ -0,0 +1,195 @@ +using System; + + +namespace Preparation.Utility +{ + public static class GameData + { + public const int NumOfStepPerSecond = 100; // 每秒行走的步数 + public const int FrameDuration = 50; // 每帧时长 + public const int CheckInterval = 10; // 检查间隔 + public const long GameDuration = 600000; // 游戏时长 + public const int LimitOfStopAndMove = 15; // 停止和移动的最大间隔 + + public const int TolerancesLength = 3; + public const int AdjustLength = 3; + + public const int MaxShipNum = 8; // 最大舰船数量 + public const int MaxCivilShipNum = 3; // 最大民用舰船数量 + public const int MaxWarShipNum = 4; // 最大军用舰船数量 + public const int MaxFlagShipNum = 1; // 最大旗舰数量 + + public const int NumOfPosGridPerCell = 1000; // 每格的【坐标单位】数 + public const int MapLength = 50000; // 地图长度 + public const int MapRows = 50; // 行数 + public const int MapCols = 50; // 列数 + + public static bool IsGameObjMap(GameObjType gameObjType) + { + return (uint)gameObjType > 3; + } + public static XY GetCellCenterPos(int x, int y) // 求格子的中心坐标 + { + XY ret = new(x * NumOfPosGridPerCell + NumOfPosGridPerCell / 2, y * NumOfPosGridPerCell + NumOfPosGridPerCell / 2); + return ret; + } + public static int PosGridToCellX(XY pos) // 求坐标所在的格子的x坐标 + { + return pos.x / NumOfPosGridPerCell; + } + public static int PosGridToCellY(XY pos) // 求坐标所在的格子的y坐标 + { + return pos.y / NumOfPosGridPerCell; + } + public static XY PosGridToCellXY(XY pos) // 求坐标所在的格子的xy坐标 + { + return new XY(pos.x / NumOfPosGridPerCell, pos.y / NumOfPosGridPerCell); + } + public static bool IsInTheSameCell(XY pos1, XY pos2) + { + return PosGridToCellX(pos1) == PosGridToCellX(pos2) && PosGridToCellY(pos1) == PosGridToCellY(pos2); + } + public static bool PartInTheSameCell(XY pos1, XY pos2) + { + return Math.Abs((pos1 - pos2).x) < ShipRadius + (NumOfPosGridPerCell / 2) + && Math.Abs((pos1 - pos2).y) < ShipRadius + (NumOfPosGridPerCell / 2); + } + public static bool ApproachToInteract(XY pos1, XY pos2) + { + return Math.Abs(PosGridToCellX(pos1) - PosGridToCellX(pos2)) <= 1 && Math.Abs(PosGridToCellY(pos1) - PosGridToCellY(pos2)) <= 1; + } + public static bool ApproachToInteractInACross(XY pos1, XY pos2) + { + if (pos1 == pos2) return false; + return (Math.Abs(PosGridToCellX(pos1) - PosGridToCellX(pos2)) + Math.Abs(PosGridToCellY(pos1) - PosGridToCellY(pos2))) <= 1; + } + + public const int ShipRadius = 400; + public static XY PosNotInGame = new XY(1, 1); + + public const int BulletRadius = 200; // 子弹半径 + public const int LaserRange = 4000; // 激光射程 + public const int LaserDamage = 1200; // 激光伤害 + public const double LaserArmorModifier = 1.5; // 激光装甲修正 + public const double LaserShieldModifier = 0.6; // 激光护盾修正 + public const int LaserSpeed = 20000; // 激光速度 + public const int LaserCastTime = 300; // 激光前摇时间 + public const int LaserSwingTime = 300; // 激光后摇时间 + public const int PlasmaRange = 4000; // 等离子射程 + public const int PlasmaDamage = 1300; // 等离子伤害 + public const double PlasmaArmorModifier = 2.0; // 等离子装甲修正 + public const double PlasmaShieldModifier = 0.4; // 等离子护盾修正 + public const int PlasmaSpeed = 10000; // 等离子速度 + public const int PlasmaCastTime = 400; // 等离子前摇时间 + public const int PlasmaSwingTime = 400; // 等离子后摇时间 + public const int ShellRange = 4000; // 炮弹射程 + public const int ShellDamage = 1800; // 炮弹伤害 + public const double ShellArmorModifier = 0.4; // 炮弹装甲修正 + public const double ShellShieldModifier = 1.5; // 炮弹护盾修正 + public const int ShellSpeed = 8000; // 炮弹速度 + public const int ShellCastTime = 200; // 炮弹前摇时间 + public const int ShellSwingTime = 200; // 炮弹后摇时间 + public const int MissileRange = 8000; // 导弹射程 + public const int MissileBombRange = 1600; // 导弹爆炸范围 + public const int MissileDamage = 1600; // 导弹伤害 + public const double MissileArmorModifier = 1.0; // 导弹装甲修正 + public const int MissileSpeed = 6000; // 导弹速度 + public const int MissileCastTime = 600; // 导弹前摇时间 + public const int MissileSwingTime = 600; // 导弹后摇时间 + public const int ArcRange = 8000; // 电弧射程 + public const int ArcDamageMin = 100; // 电弧伤害 + public const int ArcDamageMax = 3200; // 电弧伤害 + public const double ArcArmorModifier = 2.0; // 电弧装甲修正 + public const double ArcShieldModifier = 2.0; // 电弧护盾修正 + public const int ArcSpeed = 8000; // 电弧速度 + public const int ArcCastTime = 600; // 电弧前摇时间 + public const int ArcSwingTime = 600; // 电弧后摇时间 + + public const int CivilShipMaxHP = 3000; + public const int CivilShipMoveSpeed = 3000; + public const int CivilShipViewRange = 8000; + public const int CivilShipBaseArmor = 0; + public const int CivilShipBaseShield = 0; + public const int CivilShipProducer1Cost = 0; + public const int CivilShipProducer2Cost = 40; + public const int CivilShipProducer3Cost = 80; + public const int CivilShipConstructor1Cost = 0; + public const int CivilShipConstructor2Cost = 40; + public const int CivilShipConstructor3Cost = 80; + public const int CivilShipArmor1Cost = 60; + public const int CivilShipShield1Cost = 60; + public const int CivilShipLaserGunCost = 100; + + public const int WarShipMaxHP = 4000; + public const int WarShipMoveSpeed = 2800; + public const int WarShipViewRange = 8000; + public const int WarShipBaseArmor = 400; + public const int WarShipBaseShield = 400; + public const int WarShipArmor1Cost = 60; + public const int WarShipArmor2Cost = 120; + public const int WarShipArmor3Cost = 180; + public const int WarShipShield1Cost = 60; + public const int WarShipShield2Cost = 120; + public const int WarShipShield3Cost = 180; + public const int WarShipLaserGunCost = 0; + public const int WarShipPlasmaGunCost = 120; + public const int WarShipShellGunCost = 130; + public const int WarShipMissileGunCost = 180; + public const int WarShipArcGunCost = 240; + + public const int FlagShipMaxHP = 12000; + public const int FlagShipMoveSpeed = 2700; + public const int FlagShipViewRange = 8000; + public const int FlagShipBaseArmor = 800; + public const int FlagShipBaseShield = 800; + public const int FlagShipProducer1Cost = 40; + public const int FlagShipConstructor1Cost = 40; + public const int FlagShipArmor1Cost = 60; + public const int FlagShipArmor2Cost = 120; + public const int FlagShipArmor3Cost = 180; + public const int FlagShipShield1Cost = 60; + public const int FlagShipShield2Cost = 120; + public const int FlagShipShield3Cost = 180; + public const int FlagShipLaserGunCost = 0; + public const int FlagShipPlasmaGunCost = 120; + public const int FlagShipShellGunCost = 130; + public const int FlagShipMissileGunCost = 180; + public const int FlagShipArcGunCost = 240; + + public const int ScoreHomePerSecond = 1; + public const int ScoreFactoryPerSecond = 3; + public const int ScoreProducer1PerSecond = 5; + public const int ScoreProducer2PerSecond = 7; + public const int ScoreProducer3PerSecond = 10; + public const int ScoreConstructionDamaged = 200; + public static int ScoreShipKilled(int totalScore) + { + return totalScore / 5; + } + public static int ScoreShipRecovered(int totalRecovery) + { + return totalRecovery * 6 / 5; + } + public static int ScoreShipRecycled(int remainingHP) + { + return remainingHP / 2; + } + + public const int Constructor1Speed = 500; + public const int Constructor2Speed = 750; + public const int Constructor3Speed = 1000; + public const int Armor1 = 2000; + public const int Armor2 = 3000; + public const int Armor3 = 4000; + public const int Shield1 = 2000; + public const int Shield2 = 3000; + public const int Shield3 = 4000; + + public const int ResourceHP = 16000; + public const int FactoryHP = 8000; + public const int CommunityHP = 6000; + public const int FortHP = 12000; + public const int WormholeHP = 18000; + public const int HomeHP = 24000; + } +} diff --git a/logic/Preparation/Utility/Logger.cs b/logic/Preparation/Utility/Logger.cs new file mode 100644 index 00000000..27cd453f --- /dev/null +++ b/logic/Preparation/Utility/Logger.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; + +namespace Preparation.Utility +{ + public class Logger + { + static public void Writelog(object current, string str) + { + string path = "log.txt"; + string log = string.Format("[{0}] {1} {2} {3}", DateTime.Now, current.GetType(), current.ToString(), str); + File.AppendAllText(path, log + Environment.NewLine); + } + static public void Writelog(string str) + { + string path = "log.txt"; + string log = string.Format("[{0}] {1}", DateTime.Now, str); + File.AppendAllText(path, log + Environment.NewLine); + } + } +} diff --git a/logic/Preparation/Utility/MapEncoder.cs b/logic/Preparation/Utility/MapEncoder.cs new file mode 100644 index 00000000..d8e2c4e4 --- /dev/null +++ b/logic/Preparation/Utility/MapEncoder.cs @@ -0,0 +1,17 @@ +using System; + +namespace Preparation.Utility +{ + public class MapEncoder + { + static public char Dec2Hex(int d) + { + return char.Parse(d.ToString("X")); + } + static public int Hex2Dec(char h) + { + string hexabet = "0123456789ABCDEF"; + return hexabet.IndexOf(h); + } + } +} diff --git a/logic/Preparation/Utility/SafeValue.cs b/logic/Preparation/Utility/SafeValue.cs new file mode 100644 index 00000000..5f73bb17 --- /dev/null +++ b/logic/Preparation/Utility/SafeValue.cs @@ -0,0 +1,778 @@ +using System; +using System.Threading; + +namespace Preparation.Utility +{ + //理论上结构体最好不可变,这里采用了可变结构。 + //其对应属性不应当有set访问器,避免不安全的=赋值 + public class AtomicInt + { + private int v; + public AtomicInt(int x) + { + v = x; + } + public override string ToString() => Interlocked.CompareExchange(ref v, -1, -1).ToString(); + public int Get() => Interlocked.CompareExchange(ref v, -1, -1); + public static implicit operator int(AtomicInt aint) => Interlocked.CompareExchange(ref aint.v, -1, -1); + /// 返回操作前的值 + public int SetReturnOri(int value) => Interlocked.Exchange(ref v, value); + public int Add(int x) => Interlocked.Add(ref v, x); + public int Sub(int x) => Interlocked.Add(ref v, -x); + public int Inc() => Interlocked.Increment(ref v); + public int Dec() => Interlocked.Decrement(ref v); + /// 返回操作前的值 + public int CompareExReturnOri(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + + public class AtomicLong + { + private long v; + public AtomicLong(long x) + { + v = x; + } + public override string ToString() => Interlocked.Read(ref v).ToString(); + public long Get() => Interlocked.Read(ref v); + public static implicit operator long(AtomicLong aint) => Interlocked.Read(ref aint.v); + /// 返回操作前的值 + public long SetReturnOri(long value) => Interlocked.Exchange(ref v, value); + public long Add(long x) => Interlocked.Add(ref v, x); + public long Sub(long x) => Interlocked.Add(ref v, -x); + public long Inc() => Interlocked.Increment(ref v); + public long Dec() => Interlocked.Decrement(ref v); + /// 返回操作前的值 + public long CompareExReturnOri(long newV, long compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + + public class AtomicBool + { + private int v;//v==0为false,v==1为true + public AtomicBool(bool x) + { + v = x ? 1 : 0; + } + public override string ToString() => (Interlocked.CompareExchange(ref v, -2, -2) == 0) ? "false" : "true"; + public bool Get() => (Interlocked.CompareExchange(ref v, -1, -1) != 0); + public static implicit operator bool(AtomicBool abool) => (Interlocked.CompareExchange(ref abool.v, -1, -1) != 0); + /// 返回操作前的值 + public bool SetReturnOri(bool value) => (Interlocked.Exchange(ref v, value ? 1 : 0) != 0); + /// 赋值前的值是否与将赋予的值不相同 + public bool TrySet(bool value) + { + return (Interlocked.CompareExchange(ref v, value ? 1 : 0, value ? 0 : 1) ^ (value ? 1 : 0)) != 0; + } + public bool And(bool x) => Interlocked.And(ref v, x ? 1 : 0) != 0; + public bool Or(bool x) => Interlocked.Or(ref v, x ? 1 : 0) != 0; + } + + /// + /// 根据时间推算Start后完成多少进度的进度条(long)。 + /// 只允许Start时修改needTime(请确保大于0); + /// 支持TrySet0使未完成的进度条终止清零;支持Set0使进度条强制终止清零; + /// 通过原子操作实现。 + /// + public class LongProgressByTime + { + private long endT = long.MaxValue; + private long needT; + + public LongProgressByTime(long needTime) + { + if (needTime <= 0) Debugger.Output("Bug:LongProgressByTime.needTime (" + needTime.ToString() + ") is less than 0."); + this.needT = needTime; + } + public LongProgressByTime() + { + this.needT = 0; + } + public long GetEndTime() => Interlocked.CompareExchange(ref endT, -2, -2); + public long GetNeedTime() => Interlocked.CompareExchange(ref needT, -2, -2); + public override string ToString() => "EndTime:" + Interlocked.CompareExchange(ref endT, -2, -2).ToString() + " ms, NeedTime:" + Interlocked.CompareExchange(ref needT, -2, -2).ToString() + " ms"; + public bool IsFinished() + { + return Interlocked.CompareExchange(ref endT, -2, -2) <= Environment.TickCount64; + } + public bool IsOpened() => Interlocked.Read(ref endT) != long.MaxValue; + /// + /// GetProgress<0则表明未开始 + /// + public long GetProgress() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + } + public long GetNonNegativeProgress() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + return progress < 0 ? 0 : progress; + } + /// + /// GetProgress<0则表明未开始 + /// + public long GetProgress(long time) + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - time; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + } + public long GetNonNegativeProgress(long time) + { + long cutime = Interlocked.Read(ref endT) - time; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + return progress < 0 ? 0 : progress; + } + /// + /// <0则表明未开始 + /// + public static implicit operator long(LongProgressByTime pLong) => pLong.GetProgress(); + + /// + /// GetProgressDouble<0则表明未开始 + /// + public double GetProgressDouble() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return 1; + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (needTime == 0) return 0; + return 1.0 - ((double)cutime / needTime); + } + public double GetNonNegativeProgressDouble(long time) + { + long cutime = Interlocked.Read(ref endT) - time; + if (cutime <= 0) return 1; + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (needTime <= cutime) return 0; + return 1.0 - ((double)cutime / needTime); + } + + public bool Start(long needTime) + { + if (needTime <= 0) + { + Debugger.Output("Warning:Start LongProgressByTime with the needTime (" + needTime.ToString() + ") which is less than 0."); + return false; + } + //规定只有Start可以修改needT,且需要先访问endTime,从而避免锁(某种程度上endTime可以认为是needTime的锁) + if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; + if (needTime <= 2) Debugger.Output("Warning:the field of LongProgressByTime is " + needTime.ToString() + ",which is too small."); + Interlocked.Exchange(ref needT, needTime); + return true; + } + public bool Start() + { + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; + return true; + } + /// + /// 使进度条强制终止清零 + /// + public void Set0() => Interlocked.Exchange(ref endT, long.MaxValue); + /// + /// 使未完成的进度条终止清零 + /// + public bool TrySet0() + { + if (Environment.TickCount64 < Interlocked.CompareExchange(ref endT, -2, -2)) + { + Interlocked.Exchange(ref endT, long.MaxValue); + return true; + } + return false; + } + //增加其他新的写操作可能导致不安全 + } + + /* + /// + /// 记录(不是根据时间)完成多少进度的进度条(long)。 + /// + public struct IntProgressByAdding + { + private int completedProgress = -1; + private int requiredProgress; + public IntProgressByAdding(int completedProgress, int requiredProgress) + { + this.completedProgress = completedProgress; + this.requiredProgress = requiredProgress; + } + public IntProgressByAdding(int requiredProgress) + { + this.requiredProgress = requiredProgress; + } + public IntProgressByAdding() + { + this.requiredProgress=int.MaxValue; + } + } + */ + + /// + /// 一个保证在[0,maxValue]的可变int,支持可变的maxValue(请确保大于0) + /// + public class IntWithVariableRange + { + private int v; + private int maxV; + private readonly object vLock = new(); + public IntWithVariableRange(int value, int maxValue) + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set IntWithVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = value < maxValue ? value : maxValue; + this.maxV = maxValue; + } + /// + /// 默认使Value=maxValue + /// + public IntWithVariableRange(int maxValue) + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set IntWithVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = this.maxV = maxValue; + } + public IntWithVariableRange() + { + v = this.maxV = int.MaxValue; + } + + public override string ToString() + { + lock (vLock) + { + return "value:" + v.ToString() + " ,maxValue:" + maxV.ToString(); + } + } + public int GetValue() { lock (vLock) return v; } + public static implicit operator int(IntWithVariableRange aint) => aint.GetValue(); + public int GetMaxV() { lock (vLock) return maxV; } + + /// + /// 若maxValue<=0则maxValue设为0并返回False + /// + public bool SetMaxV(int maxValue) + { + if (maxValue < 0) maxValue = 0; + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + return maxValue > 0; + } + /// + /// 应当保证该maxValue>=0 + /// + public void SetPositiveMaxV(int maxValue) + { + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + } + /// + /// 应当保证该value>=0 + /// + public int SetPositiveV(int value) + { + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + public int SetV(int value) + { + if (value < 0) value = 0; + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + /// + /// 返回实际改变量 + /// + public int AddV(int addV) + { + lock (vLock) + { + int previousV = v; + v += addV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证该增加值大于0,返回实际改变量 + /// + public int AddPositiveV(int addPositiveV) + { + lock (vLock) + { + addPositiveV = Math.Min(addPositiveV, maxV - v); + v += addPositiveV; + } + return addPositiveV; + } + /// + /// 应当保证该减少值大于0,返回实际改变量 + /// + public int SubPositiveV(int subPositiveV) + { + lock (vLock) + { + subPositiveV = Math.Min(subPositiveV, v); + v -= subPositiveV; + } + return subPositiveV; + } + + /// + /// 试图加到满,如果无法加到maxValue则不加并返回-1 + /// + public int TryAddAll(int addV) + { + lock (vLock) + { + if (maxV - v <= addV) + { + addV = maxV - v; + v = maxV; + return addV; + } + return 0; + } + } + } + + /// + /// 一个保证在[0,maxValue]的可变long,支持可变的maxValue(请确保大于0) + /// + public class LongWithVariableRange + { + private long v; + private long maxV; + private readonly object vLock = new(); + public LongWithVariableRange(long value, long maxValue) + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set SafaValues.LongWithVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = value < maxValue ? value : maxValue; + this.maxV = maxValue; + } + /// + /// 默认使Value=maxValue + /// + public LongWithVariableRange(long maxValue) + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set SafaValues.LongWithVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = this.maxV = maxValue; + } + public LongWithVariableRange() + { + v = this.maxV = long.MaxValue; + } + + public override string ToString() + { + lock (vLock) + { + return "value:" + v.ToString() + " ,maxValue:" + maxV.ToString(); + } + } + public long GetValue() { lock (vLock) return v; } + public static implicit operator long(LongWithVariableRange aint) => aint.GetValue(); + public long GetMaxV() { lock (vLock) return maxV; } + + /// + /// 若maxValue<=0则maxValue设为0并返回False + /// + public bool SetMaxV(long maxValue) + { + if (maxValue < 0) maxValue = 0; + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + return maxValue > 0; + } + /// + /// 应当保证该maxValue>=0 + /// + public void SetPositiveMaxV(long maxValue) + { + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + } + /// + /// 应当保证该value>=0 + /// + public long SetPositiveV(long value) + { + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + public long SetV(long value) + { + if (value < 0) value = 0; + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + /// + /// 返回实际改变量 + /// + public long AddV(long addV) + { + lock (vLock) + { + long previousV = v; + v += addV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证该增加值大于0,返回实际改变量 + /// + public long AddPositiveV(long addPositiveV) + { + lock (vLock) + { + addPositiveV = Math.Min(addPositiveV, maxV - v); + v += addPositiveV; + } + return addPositiveV; + } + /// + /// 应当保证该减少值大于0,返回实际改变量 + /// + public long SubPositiveV(long subPositiveV) + { + lock (vLock) + { + subPositiveV = Math.Min(subPositiveV, v); + v -= subPositiveV; + } + return subPositiveV; + } + + /// + /// 试图加到满,如果无法加到maxValue则不加并返回-1 + /// + public long TryAddAll(long addV) + { + lock (vLock) + { + if (maxV - v <= addV) + { + addV = maxV - v; + v = maxV; + return addV; + } + return -1; + } + } + } + + /// + /// 一个保证在[0,maxNum],每CDms自动+1的int,支持可变的CD、maxNum(请确保大于0) + /// + public class IntNumUpdateByCD + { + private int num; + private int maxNum; + private int cd; + private long updateTime = 0; + private readonly object numLock = new(); + public IntNumUpdateByCD(int num, int maxNum, int cd) + { + if (num < 0) Debugger.Output("Bug:IntNumUpdateByCD.num (" + num.ToString() + ") is less than 0."); + if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateByCD.maxNum (" + maxNum.ToString() + ") is less than 0."); + if (cd <= 0) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 0."); + this.num = (num < maxNum) ? num : maxNum; + this.maxNum = maxNum; + this.cd = cd; + this.updateTime = Environment.TickCount64; + } + /// + /// 默认使num=maxNum + /// + public IntNumUpdateByCD(int maxNum, int cd) + { + if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateByCD.maxNum (" + maxNum.ToString() + ") is less than 0."); + if (cd <= 0) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 0."); + this.num = this.maxNum = maxNum; + this.cd = cd; + } + public IntNumUpdateByCD() + { + this.num = this.maxNum = 0; + this.cd = int.MaxValue; + } + + public int GetMaxNum() { lock (numLock) return maxNum; } + public int GetCD() { lock (numLock) return cd; } + public int GetNum(long time) + { + lock (numLock) + { + if (num < maxNum && time - updateTime >= cd) + { + int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); + updateTime += add * cd; + return (num += add); + } + return num; + } + } + public static implicit operator int(IntNumUpdateByCD aint) => aint.GetNum(Environment.TickCount64); + + /// + /// 应当保证该subV>=0 + /// + public int TrySub(int subV) + { + if (subV < 0) Debugger.Output("Bug:IntNumUpdateByCD Try to sub " + subV.ToString() + ", which is less than 0."); + long time = Environment.TickCount64; + lock (numLock) + { + if (num < maxNum && time - updateTime >= cd) + { + int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); + updateTime += add * cd; + num += add; + } + if (num == maxNum) updateTime = time; + num -= subV = Math.Min(subV, num); + } + return subV; + } + /// + /// 应当保证该addV>=0 + /// + public void TryAdd(int addV) + { + if (addV < 0) Debugger.Output("Bug:IntNumUpdateByCD Try to add " + addV.ToString() + ", which is less than 0."); + lock (numLock) + { + num += Math.Min(addV, maxNum - num); + } + } + /// + /// 若maxNum<=0则maxNum及Num设为0并返回False + /// + public bool SetMaxNumAndNum(int maxNum) + { + if (maxNum < 0) maxNum = 0; + lock (numLock) + { + this.num = this.maxNum = maxNum; + } + return maxNum > 0; + } + /// + /// 应当保证该maxnum>=0 + /// + public void SetPositiveMaxNumAndNum(int maxNum) + { + lock (numLock) + { + this.num = this.maxNum = maxNum; + } + } + /// + /// 应当保证该maxnum>=0 + /// + public void SetPositiveMaxNum(int maxNum) + { + lock (numLock) + { + if ((this.maxNum = maxNum) < num) + num = maxNum; + } + } + /// + /// 若maxNum<=0则maxNum及Num设为0并返回False + /// + public bool SetMaxNum(int maxNum) + { + if (maxNum < 0) maxNum = 0; + lock (numLock) + { + if ((this.maxNum = maxNum) < num) + num = maxNum; + } + return maxNum > 0; + } + /// + /// 若num<0则num设为0并返回False + /// + public bool SetNum(int num) + { + lock (numLock) + { + if (num < 0) + { + this.num = 0; + updateTime = Environment.TickCount64; + return false; + } + if (num < maxNum) + { + if (this.num == maxNum) updateTime = Environment.TickCount64; + this.num = num; + } + else this.num = maxNum; + return true; + } + } + /// + /// 应当保证该num>=0 + /// + public void SetPositiveNum(int num) + { + lock (numLock) + { + if (num < maxNum) + { + if (this.num == maxNum) updateTime = Environment.TickCount64; + this.num = num; + } + else this.num = maxNum; + } + } + public void SetCD(int cd) + { + lock (numLock) + { + if (cd <= 0) Debugger.Output("Bug:SetReturnOri IntNumUpdateByCD.cd to " + cd.ToString() + "."); + this.cd = cd; + } + } + } + + /// + /// 一个每CDms自动更新冷却的bool,支持可变的无锁CD,不支持查看当前进度,初始为True + /// + public class BoolCoolingDownByCD + { + private long cd; + private long nextUpdateTime = 0; + public BoolCoolingDownByCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public BoolCoolingDownByCD(long cd) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public BoolCoolingDownByCD(long cd, long startTime) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + this.nextUpdateTime = startTime; + } + + public long GetCD() => Interlocked.Read(ref cd); + + public bool TryUse() + { + long needTime = Interlocked.Exchange(ref nextUpdateTime, long.MaxValue); + if (needTime <= Environment.TickCount64) + { + Interlocked.Exchange(ref nextUpdateTime, Environment.TickCount64 + Interlocked.Read(ref cd)); + return true; + } + Interlocked.Exchange(ref nextUpdateTime, needTime); + return false; + } + public void SetCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:SetReturnOri IntNumUpdateByCD.cd to " + cd.ToString() + "."); + Interlocked.Exchange(ref this.cd, cd); + } + } + + /// + /// 一个每CDms自动更新的进度条,支持可变的CD,初始为满 + /// + public class LongProgressCoolingDownByCD + { + private int isusing = 0; + private long cd; + private long nextUpdateTime = 0; + public LongProgressCoolingDownByCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public LongProgressCoolingDownByCD(long cd) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public LongProgressCoolingDownByCD(long cd, long startTime) + { + if (cd <= 1) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + this.nextUpdateTime = startTime; + } + + public long GetRemainingTime() + { + long v = Interlocked.Read(ref nextUpdateTime) - Environment.TickCount64; + return v < 0 ? 0 : v; + } + public long GetCD() => Interlocked.Read(ref cd); + + public bool TryUse() + { + if (Interlocked.Exchange(ref isusing, 1) == 1) return false; + long needTime = Interlocked.Read(ref nextUpdateTime); + if (needTime <= Environment.TickCount64) + { + Interlocked.Exchange(ref nextUpdateTime, Environment.TickCount64 + Interlocked.Read(ref cd)); + Interlocked.Exchange(ref isusing, 0); + return true; + } + Interlocked.Exchange(ref isusing, 0); + return false; + } + public void SetCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:SetReturnOri IntNumUpdateByCD.cd to " + cd.ToString() + "."); + Interlocked.Exchange(ref this.cd, cd); + } + } +} diff --git a/logic/Preparation/Utility/XY.cs b/logic/Preparation/Utility/XY.cs new file mode 100644 index 00000000..ddf27615 --- /dev/null +++ b/logic/Preparation/Utility/XY.cs @@ -0,0 +1,98 @@ +using System; + +namespace Preparation.Utility +{ + public struct XY + { + public int x; + public int y; + public XY(int x, int y) + { + this.x = x; + this.y = y; + } + public XY(double angle, double length) + { + this.x = (int)(length * Math.Cos(angle)); + this.y = (int)(length * Math.Sin(angle)); + } + public XY(XY Direction, double length) + { + if (Direction.x == 0 && Direction.y == 0) + { + this.x = 0; + this.y = 0; + } + else + { + this.x = (int)(length * Math.Cos(Direction.Angle())); + this.y = (int)(length * Math.Sin(Direction.Angle())); + } + } + public override string ToString() + { + return "(" + x.ToString() + "," + y.ToString() + ")"; + } + public static int operator *(XY v1, XY v2) + { + return (v1.x * v2.x) + (v1.y * v2.y); + } + public static XY operator *(int a, XY v2) + { + return new XY(a * v2.x, a * v2.y); + } + public static XY operator *(XY v2, int a) + { + return new XY(a * v2.x, a * v2.y); + } + public static XY operator +(XY v1, XY v2) + { + return new XY(v1.x + v2.x, v1.y + v2.y); + } + public static XY operator -(XY v1, XY v2) + { + return new XY(v1.x - v2.x, v1.y - v2.y); + } + public static bool operator ==(XY v1, XY v2) + { + return v1.x == v2.x && v1.y == v2.y; + } + public static bool operator !=(XY v1, XY v2) + { + return v1.x != v2.x || v1.y != v2.y; + } + + public static double DistanceFloor3(XY p1, XY p2) + { + long c = (((long)(p1.x - p2.x) * (p1.x - p2.x)) + ((long)(p1.y - p2.y) * (p1.y - p2.y))) * 1000000; + long t = c / 2 + 1; + while (t * t > c || (t + 1) * (t + 1) <= c) + t = (c / t + t) / 2; + return (double)t / 1000.0; + } + public static double DistanceCeil3(XY p1, XY p2) + { + long c = (((long)(p1.x - p2.x) * (p1.x - p2.x)) + ((long)(p1.y - p2.y) * (p1.y - p2.y))) * 1000000; + long t = c / 2 + 1; + while (t * t > c || (t + 1) * (t + 1) <= c) + t = (c / t + t) / 2; + if (t * t == c) return (double)t / 1000.0; + else return (double)(t + 1) / 1000.0; + } + public double Length() + { + return Math.Sqrt(((long)x * x) + ((long)y * y)); + } + public double Angle() + { + return Math.Atan2(y, x); + } + + public override bool Equals(object? obj) => obj is not null && obj is XY xy && this == xy; + + public override int GetHashCode() + { + return this.x.GetHashCode() ^ this.y.GetHashCode(); + } + } +} diff --git a/logic/Program.cs b/logic/Program.cs deleted file mode 100644 index 3751555c..00000000 --- a/logic/Program.cs +++ /dev/null @@ -1,2 +0,0 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); diff --git a/logic/Server/Server.csproj b/logic/Server/Server.csproj new file mode 100644 index 00000000..b7b53efc --- /dev/null +++ b/logic/Server/Server.csproj @@ -0,0 +1,32 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/logic/logic.csproj b/logic/logic.csproj deleted file mode 100644 index 74abf5c9..00000000 --- a/logic/logic.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - diff --git a/logic/logic.sln b/logic/logic.sln index ce2952f3..1d5f2dc3 100644 --- a/logic/logic.sln +++ b/logic/logic.sln @@ -1,9 +1,27 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.6.33712.159 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "logic", "logic.csproj", "{F60FBD87-F152-4CDD-A579-5014B8FA104A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{D033B809-2FB7-4340-B8B4-DDA30D6CA6FF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameClass", "GameClass\GameClass.csproj", "{39D838F6-2B84-49E1-9CAF-1DFF22960B5D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gaming", "Gaming\Gaming.csproj", "{BE9E3584-93C0-4E0F-8DAC-967CF4792709}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Preparation", "Preparation\Preparation.csproj", "{E3DC4A37-8A83-40CC-AE47-68E70064C9DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientTest", "ClientTest\ClientTest.csproj", "{F3C98717-DD4F-45B8-B0F0-C217E7E2B5D4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protos", "..\dependency\proto\Protos.csproj", "{9ADA1EF8-DF2F-4C2E-9DE2-BC94DF89B44D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameEngine", "GameEngine\GameEngine.csproj", "{1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playback", "..\playback\Playback\Playback.csproj", "{FF22960A-6BD9-4C80-A029-9A39FB8F64C4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14C1A4DC-3F6C-4A3E-8CD0-2E595C99F1C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{F8CD6F8B-0EEE-43B6-BEDF-240E1DBB013D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +29,47 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F60FBD87-F152-4CDD-A579-5014B8FA104A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F60FBD87-F152-4CDD-A579-5014B8FA104A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F60FBD87-F152-4CDD-A579-5014B8FA104A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F60FBD87-F152-4CDD-A579-5014B8FA104A}.Release|Any CPU.Build.0 = Release|Any CPU + {D033B809-2FB7-4340-B8B4-DDA30D6CA6FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D033B809-2FB7-4340-B8B4-DDA30D6CA6FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D033B809-2FB7-4340-B8B4-DDA30D6CA6FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D033B809-2FB7-4340-B8B4-DDA30D6CA6FF}.Release|Any CPU.Build.0 = Release|Any CPU + {39D838F6-2B84-49E1-9CAF-1DFF22960B5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39D838F6-2B84-49E1-9CAF-1DFF22960B5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39D838F6-2B84-49E1-9CAF-1DFF22960B5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39D838F6-2B84-49E1-9CAF-1DFF22960B5D}.Release|Any CPU.Build.0 = Release|Any CPU + {BE9E3584-93C0-4E0F-8DAC-967CF4792709}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE9E3584-93C0-4E0F-8DAC-967CF4792709}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE9E3584-93C0-4E0F-8DAC-967CF4792709}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE9E3584-93C0-4E0F-8DAC-967CF4792709}.Release|Any CPU.Build.0 = Release|Any CPU + {E3DC4A37-8A83-40CC-AE47-68E70064C9DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3DC4A37-8A83-40CC-AE47-68E70064C9DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3DC4A37-8A83-40CC-AE47-68E70064C9DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3DC4A37-8A83-40CC-AE47-68E70064C9DB}.Release|Any CPU.Build.0 = Release|Any CPU + {F3C98717-DD4F-45B8-B0F0-C217E7E2B5D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3C98717-DD4F-45B8-B0F0-C217E7E2B5D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3C98717-DD4F-45B8-B0F0-C217E7E2B5D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3C98717-DD4F-45B8-B0F0-C217E7E2B5D4}.Release|Any CPU.Build.0 = Release|Any CPU + {9ADA1EF8-DF2F-4C2E-9DE2-BC94DF89B44D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9ADA1EF8-DF2F-4C2E-9DE2-BC94DF89B44D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9ADA1EF8-DF2F-4C2E-9DE2-BC94DF89B44D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9ADA1EF8-DF2F-4C2E-9DE2-BC94DF89B44D}.Release|Any CPU.Build.0 = Release|Any CPU + {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D1D07F3-C332-4407-AC1B-EAD73F8BB3F3}.Release|Any CPU.Build.0 = Release|Any CPU + {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF22960A-6BD9-4C80-A029-9A39FB8F64C4}.Release|Any CPU.Build.0 = Release|Any CPU + {F8CD6F8B-0EEE-43B6-BEDF-240E1DBB013D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8CD6F8B-0EEE-43B6-BEDF-240E1DBB013D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8CD6F8B-0EEE-43B6-BEDF-240E1DBB013D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8CD6F8B-0EEE-43B6-BEDF-240E1DBB013D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A357F19C-9A71-49AD-AF2F-61B177CD8BAC} + SolutionGuid = {F8195A94-4DCA-4F27-BCB8-9E6310A1127E} EndGlobalSection EndGlobal diff --git a/playback/Playback/Playback.csproj b/playback/Playback/Playback.csproj new file mode 100644 index 00000000..f3e0f979 --- /dev/null +++ b/playback/Playback/Playback.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/playback/Program.cs b/playback/Program.cs deleted file mode 100644 index 3751555c..00000000 --- a/playback/Program.cs +++ /dev/null @@ -1,2 +0,0 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); diff --git a/playback/playback.csproj b/playback/playback.csproj deleted file mode 100644 index 74abf5c9..00000000 --- a/playback/playback.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - From 0889a59ad60d114f7c2993145a2d1a41469d8e73 Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 21 Oct 2023 21:25:34 +0800 Subject: [PATCH 02/14] feat: :memo: update Proto.sln --- dependency/proto/Proto.sln | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/dependency/proto/Proto.sln b/dependency/proto/Proto.sln index 1983d80c..2cdbe95d 100644 --- a/dependency/proto/Proto.sln +++ b/dependency/proto/Proto.sln @@ -1,4 +1,3 @@ -<<<<<<< HEAD  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 @@ -24,30 +23,3 @@ Global SolutionGuid = {CB1C0B2E-5D0E-4420-94B5-FE8AECC7E106} EndGlobalSection EndGlobal -======= - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.32328.378 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto", "Proto.csproj", "{B9A3CDD7-7852-4220-A2AA-2B986E0C19E8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B9A3CDD7-7852-4220-A2AA-2B986E0C19E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9A3CDD7-7852-4220-A2AA-2B986E0C19E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9A3CDD7-7852-4220-A2AA-2B986E0C19E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9A3CDD7-7852-4220-A2AA-2B986E0C19E8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CB1C0B2E-5D0E-4420-94B5-FE8AECC7E106} - EndGlobalSection -EndGlobal ->>>>>>> dd05e626b734f5bc4b70c9830ecf9df26a3bd6c6 From 7faa028bf2fb2c0de89216adec7669219f261248 Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 21 Oct 2023 21:31:05 +0800 Subject: [PATCH 03/14] docs: :memo: update .gitignore --- .gitignore | 404 ++++++++++++++++++++++++++++++++++++++++ .vs/slnx.sqlite | Bin 90112 -> 0 bytes .vs/slnx.sqlite-journal | Bin 21032 -> 0 bytes 3 files changed, 404 insertions(+) create mode 100644 .gitignore delete mode 100644 .vs/slnx.sqlite delete mode 100644 .vs/slnx.sqlite-journal diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..26949852 --- /dev/null +++ b/.gitignore @@ -0,0 +1,404 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +#THUAI playback file +*.thuaipb + +#private cmd +cmd/gameServerOfSanford.cmd \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite deleted file mode 100644 index 102ea2266b6ac30659b6b5035c66e604f1e277ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90112 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 Date: Sun, 22 Oct 2023 12:04:29 +0800 Subject: [PATCH 04/14] style: cs format Add whitespace in Ship.cs(109, 17), Team.cs(28, 19) --- logic/GameClass/GameObj/Ship.cs | 2 +- logic/GameClass/GameObj/Team.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs index 582939fb..95ad01a9 100644 --- a/logic/GameClass/GameObj/Ship.cs +++ b/logic/GameClass/GameObj/Ship.cs @@ -106,7 +106,7 @@ public long SetShipState(RunningStateType running, ShipStateType value = ShipSta } public long SetShipStateNaturally() { - lock(actionLock) + lock (actionLock) { runningState = RunningStateType.Null; whatInteractingWith = null; diff --git a/logic/GameClass/GameObj/Team.cs b/logic/GameClass/GameObj/Team.cs index 57b8b623..ab858227 100644 --- a/logic/GameClass/GameObj/Team.cs +++ b/logic/GameClass/GameObj/Team.cs @@ -25,7 +25,7 @@ public class Team } public bool AddShip(Ship ship) { - switch(ship.ShipType) + switch (ship.ShipType) { case ShipType.CivilShip: if (GetCivilShipNum() >= GameData.MaxCivilShipNum) From d118e5008a7ad3e2f5828bd0683d0283312d5464 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Sun, 22 Oct 2023 12:40:43 +0800 Subject: [PATCH 05/14] refactor(GameClass): add namespace GameObj.Bullets - split BulletFactory - split Laser, Plasma, Shell, Missile, Arc - remove the braces of namespace - refactor AP --- logic/GameClass/GameObj/Bullet.cs | 233 ++---------------- logic/GameClass/GameObj/Bullets/Arc.cs | 37 +++ .../GameObj/Bullets/BulletFactory.cs | 19 ++ logic/GameClass/GameObj/Bullets/Laser.cs | 37 +++ logic/GameClass/GameObj/Bullets/Missile.cs | 36 +++ logic/GameClass/GameObj/Bullets/Plasma.cs | 35 +++ logic/GameClass/GameObj/Bullets/Shell.cs | 35 +++ 7 files changed, 225 insertions(+), 207 deletions(-) create mode 100644 logic/GameClass/GameObj/Bullets/Arc.cs create mode 100644 logic/GameClass/GameObj/Bullets/BulletFactory.cs create mode 100644 logic/GameClass/GameObj/Bullets/Laser.cs create mode 100644 logic/GameClass/GameObj/Bullets/Missile.cs create mode 100644 logic/GameClass/GameObj/Bullets/Plasma.cs create mode 100644 logic/GameClass/GameObj/Bullets/Shell.cs diff --git a/logic/GameClass/GameObj/Bullet.cs b/logic/GameClass/GameObj/Bullet.cs index 5154db5d..3fb409f5 100644 --- a/logic/GameClass/GameObj/Bullet.cs +++ b/logic/GameClass/GameObj/Bullet.cs @@ -1,216 +1,35 @@ using Preparation.Interface; using Preparation.Utility; -using System; -using System.Threading; -namespace GameClass.GameObj +namespace GameClass.GameObj; + +public abstract class Bullet : ObjOfShip { - public abstract class Bullet : ObjOfShip - { - public abstract double BulletBombRange { get; } - public abstract double AttackDistance { get; } - private AtomicInt ap = new(0); - public AtomicInt AP { get => ap; } - public abstract int Speed { get; } - public abstract int CastTime { get; } - public abstract int SwingTime { get; } - public abstract int CD { get; } - public abstract int MaxBulletNum { get; } - public override bool IsRigid => true; // 默认为true - public override ShapeType Shape => ShapeType.Circle; // 默认为圆形 - public abstract BulletType TypeOfBullet { get; } - public abstract bool CanAttack(GameObj target); - public abstract bool CanBeBombed(GameObjType gameObjType); - public override bool IgnoreCollideExecutor(IGameObj targetObj) - { - if (targetObj == Parent) return true; - if (targetObj.Type == GameObjType.Bullet) - return true; - return false; - } - public Bullet(Ship ship, int radius, XY Position) : - base(Position, radius, GameObjType.Bullet) - { - this.CanMove.SetReturnOri(true); - this.MoveSpeed.SetReturnOri(this.Speed); - this.Parent = ship; - } - } - public static class BulletFactory - { - public static Bullet? GetBullet(Ship ship, XY pos, BulletType bulletType) - { - switch (bulletType) - { - case BulletType.Laser: - return new Laser(ship, pos); - case BulletType.Plasma: - return new Plasma(ship, pos); - case BulletType.Shell: - return new Shell(ship, pos); - case BulletType.Missile: - return new Missile(ship, pos); - case BulletType.Arc: - return new Arc(ship, pos); - default: - return null; - } - } - } - internal sealed class Laser : Bullet - { - public Laser(Ship ship, XY pos, int radius = GameData.BulletRadius) : - base(ship, radius, pos) - { - this.AP.SetReturnOri(GameData.LaserDamage); - } - public override double BulletBombRange => 0; - public override double AttackDistance => GameData.LaserRange; - public override int Speed => GameData.LaserSpeed; - public override int CastTime => GameData.LaserCastTime; - public override int SwingTime => GameData.LaserSwingTime; - private const int cd = GameData.LaserSwingTime; - public override int CD => cd; - public override int MaxBulletNum => 1; - public override BulletType TypeOfBullet => BulletType.Laser; - public override bool CanAttack(GameObj target) - { - //if (target.Type == GameObjType.Ship - // || target.Type == GameObjType.Construction - // || target.Type == GameObjType.Wormhole - // || target.Type == GameObjType.Home) - // return true; - return false; - } - public override bool CanBeBombed(GameObjType gameObjType) - { - return false; - } - } - internal sealed class Plasma : Bullet - { - public Plasma(Ship ship, XY pos, int radius = GameData.BulletRadius) : - base(ship, radius, pos) - { - this.AP.SetReturnOri(GameData.PlasmaDamage); - } - public override double BulletBombRange => 0; - public override double AttackDistance => GameData.PlasmaRange; - public override int Speed => GameData.PlasmaSpeed; - public override int CastTime => GameData.PlasmaCastTime; - public override int SwingTime => GameData.PlasmaSwingTime; - private const int cd = GameData.PlasmaSwingTime; - public override int CD => cd; - public const int maxBulletNum = 1; - public override int MaxBulletNum => maxBulletNum; - public override BulletType TypeOfBullet => BulletType.Plasma; - public override bool CanAttack(GameObj target) - { - //if (target.Type == GameObjType.Ship - // || target.Type == GameObjType.Construction - // || target.Type == GameObjType.Wormhole - // || target.Type == GameObjType.Home) - // return true; - return false; - } - public override bool CanBeBombed(GameObjType gameObjType) - { - return false; - } - } - internal sealed class Shell : Bullet - { - public Shell(Ship ship, XY pos, int radius = GameData.BulletRadius) : - base(ship, radius, pos) - { - this.AP.SetReturnOri(GameData.ShellDamage); - } - public override double BulletBombRange => 0; - public override double AttackDistance => GameData.ShellRange; - public override int Speed => GameData.ShellSpeed; - public override int CastTime => GameData.ShellCastTime; - public override int SwingTime => GameData.ShellSwingTime; - private const int cd = GameData.ShellSwingTime; - public override int CD => cd; - public const int maxBulletNum = 1; - public override int MaxBulletNum => maxBulletNum; - public override BulletType TypeOfBullet => BulletType.Shell; - public override bool CanAttack(GameObj target) - { - //if (target.Type == GameObjType.Ship - // || target.Type == GameObjType.Construction - // || target.Type == GameObjType.Wormhole - // || target.Type == GameObjType.Home) - // return true; - return false; - } - public override bool CanBeBombed(GameObjType gameObjType) - { - return false; - } - } - internal sealed class Missile : Bullet + public abstract double BulletBombRange { get; } + public abstract double AttackDistance { get; } + public AtomicInt AP { get; } = new(0); + public abstract int Speed { get; } + public abstract int CastTime { get; } + public abstract int SwingTime { get; } + public abstract int CD { get; } + public abstract int MaxBulletNum { get; } + public override bool IsRigid => true; // 默认为true + public override ShapeType Shape => ShapeType.Circle; // 默认为圆形 + public abstract BulletType TypeOfBullet { get; } + public abstract bool CanAttack(GameObj target); + public abstract bool CanBeBombed(GameObjType gameObjType); + public override bool IgnoreCollideExecutor(IGameObj targetObj) { - public Missile(Ship ship, XY pos, int radius = GameData.BulletRadius) : - base(ship, radius, pos) - { - this.AP.SetReturnOri(GameData.MissileDamage); - } - public override double BulletBombRange => GameData.MissileBombRange; - public override double AttackDistance => GameData.MissileRange; - public override int Speed => GameData.MissileSpeed; - public override int CastTime => GameData.MissileCastTime; - public override int SwingTime => GameData.ShellSwingTime; - private const int cd = GameData.ShellSwingTime; - public override int CD => cd; - public const int maxBulletNum = 1; - public override int MaxBulletNum => maxBulletNum; - public override BulletType TypeOfBullet => BulletType.Missile; - public override bool CanAttack(GameObj target) - { - //if (target.Type == GameObjType.Ship - // || target.Type == GameObjType.Construction - // || target.Type == GameObjType.Wormhole - // || target.Type == GameObjType.Home) - // return true; - return false; - } - public override bool CanBeBombed(GameObjType gameObjType) - { - //return true; - return false; - } + if (targetObj == Parent) return true; + if (targetObj.Type == GameObjType.Bullet) + return true; + return false; } - internal sealed class Arc : Bullet + public Bullet(Ship ship, int radius, XY Position) : + base(Position, radius, GameObjType.Bullet) { - public Arc(Ship ship, XY pos, int radius = GameData.BulletRadius) : - base(ship, radius, pos) - { - Random random = new Random(); - this.AP.SetReturnOri(random.Next(GameData.ArcDamageMin, GameData.ArcDamageMax)); - } - public override double BulletBombRange => 0; - public override double AttackDistance => GameData.ArcRange; - public override int Speed => GameData.ArcSpeed; - public override int CastTime => GameData.ArcCastTime; - public override int SwingTime => GameData.ArcSwingTime; - private const int cd = GameData.ArcSwingTime; - public override int CD => cd; - public const int maxBulletNum = 1; - public override int MaxBulletNum => maxBulletNum; - public override BulletType TypeOfBullet => BulletType.Arc; - public override bool CanAttack(GameObj target) - { - //if (target.Type == GameObjType.Ship - // || target.Type == GameObjType.Construction - // || target.Type == GameObjType.Wormhole - // || target.Type == GameObjType.Home) - // return true; - return false; - } - public override bool CanBeBombed(GameObjType gameObjType) - { - return false; - } + this.CanMove.SetReturnOri(true); + this.MoveSpeed.SetReturnOri(this.Speed); + this.Parent = ship; } } diff --git a/logic/GameClass/GameObj/Bullets/Arc.cs b/logic/GameClass/GameObj/Bullets/Arc.cs new file mode 100644 index 00000000..a45d8b47 --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/Arc.cs @@ -0,0 +1,37 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Bullets; + +internal sealed class Arc : Bullet +{ + public Arc(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + Random random = new Random(); + this.AP.SetReturnOri(random.Next(GameData.ArcDamageMin, GameData.ArcDamageMax)); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.ArcRange; + public override int Speed => GameData.ArcSpeed; + public override int CastTime => GameData.ArcCastTime; + public override int SwingTime => GameData.ArcSwingTime; + private const int cd = GameData.ArcSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Arc; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/BulletFactory.cs b/logic/GameClass/GameObj/Bullets/BulletFactory.cs new file mode 100644 index 00000000..ddd0be0d --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/BulletFactory.cs @@ -0,0 +1,19 @@ +using Preparation.Utility; + +namespace GameClass.GameObj.Bullets; + +public static class BulletFactory +{ + public static Bullet? GetBullet(Ship ship, XY pos, BulletType bulletType) + { + return bulletType switch + { + BulletType.Laser => new Laser(ship, pos), + BulletType.Plasma => new Plasma(ship, pos), + BulletType.Shell => new Shell(ship, pos), + BulletType.Missile => new Missile(ship, pos), + BulletType.Arc => new Arc(ship, pos), + _ => null, + }; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/Laser.cs b/logic/GameClass/GameObj/Bullets/Laser.cs new file mode 100644 index 00000000..21ed681d --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/Laser.cs @@ -0,0 +1,37 @@ +using Preparation.Interface; +using Preparation.Utility; +using System; +using System.Threading; + +namespace GameClass.GameObj.Bullets; + +internal sealed class Laser : Bullet +{ + public Laser(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.LaserDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.LaserRange; + public override int Speed => GameData.LaserSpeed; + public override int CastTime => GameData.LaserCastTime; + public override int SwingTime => GameData.LaserSwingTime; + private const int cd = GameData.LaserSwingTime; + public override int CD => cd; + public override int MaxBulletNum => 1; + public override BulletType TypeOfBullet => BulletType.Laser; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/Missile.cs b/logic/GameClass/GameObj/Bullets/Missile.cs new file mode 100644 index 00000000..db767bd8 --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/Missile.cs @@ -0,0 +1,36 @@ +using Preparation.Utility; + +namespace GameClass.GameObj.Bullets; + +internal sealed class Missile : Bullet +{ + public Missile(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.MissileDamage); + } + public override double BulletBombRange => GameData.MissileBombRange; + public override double AttackDistance => GameData.MissileRange; + public override int Speed => GameData.MissileSpeed; + public override int CastTime => GameData.MissileCastTime; + public override int SwingTime => GameData.ShellSwingTime; + private const int cd = GameData.ShellSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Missile; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + //return true; + return false; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/Plasma.cs b/logic/GameClass/GameObj/Bullets/Plasma.cs new file mode 100644 index 00000000..a407fbf7 --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/Plasma.cs @@ -0,0 +1,35 @@ +using Preparation.Utility; + +namespace GameClass.GameObj.Bullets; + +internal sealed class Plasma : Bullet +{ + public Plasma(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.PlasmaDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.PlasmaRange; + public override int Speed => GameData.PlasmaSpeed; + public override int CastTime => GameData.PlasmaCastTime; + public override int SwingTime => GameData.PlasmaSwingTime; + private const int cd = GameData.PlasmaSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Plasma; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/Shell.cs b/logic/GameClass/GameObj/Bullets/Shell.cs new file mode 100644 index 00000000..a18cca6f --- /dev/null +++ b/logic/GameClass/GameObj/Bullets/Shell.cs @@ -0,0 +1,35 @@ +using Preparation.Utility; + +namespace GameClass.GameObj.Bullets; + +internal sealed class Shell : Bullet +{ + public Shell(Ship ship, XY pos, int radius = GameData.BulletRadius) : + base(ship, radius, pos) + { + this.AP.SetReturnOri(GameData.ShellDamage); + } + public override double BulletBombRange => 0; + public override double AttackDistance => GameData.ShellRange; + public override int Speed => GameData.ShellSpeed; + public override int CastTime => GameData.ShellCastTime; + public override int SwingTime => GameData.ShellSwingTime; + private const int cd = GameData.ShellSwingTime; + public override int CD => cd; + public const int maxBulletNum = 1; + public override int MaxBulletNum => maxBulletNum; + public override BulletType TypeOfBullet => BulletType.Shell; + public override bool CanAttack(GameObj target) + { + //if (target.Type == GameObjType.Ship + // || target.Type == GameObjType.Construction + // || target.Type == GameObjType.Wormhole + // || target.Type == GameObjType.Home) + // return true; + return false; + } + public override bool CanBeBombed(GameObjType gameObjType) + { + return false; + } +} \ No newline at end of file From 2c1c7e59e6f65b417362ec0f792f69f44be9ea39 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Wed, 25 Oct 2023 22:13:50 +0800 Subject: [PATCH 06/14] refactor: :recycle: add namespace Modules - split all the modules from namespace Interface to GameClass - split ModuleFactory --- .../GameObj/Modules/Civil/CivilArmor.cs | 12 + .../GameObj/Modules/Civil/CivilConstructor.cs | 26 + .../GameObj/Modules/Civil/CivilProducer.cs | 26 + .../GameObj/Modules/Civil/CivilShield.cs | 12 + .../GameObj/Modules/Civil/CivilWeapon.cs | 12 + .../GameObj/Modules/Flag/FlagArmor.cs | 26 + .../GameObj/Modules/Flag/FlagConstructor.cs | 12 + .../GameObj/Modules/Flag/FlagProducer.cs | 12 + .../GameObj/Modules/Flag/FlagShield.cs | 26 + .../GameObj/Modules/Flag/FlagWeapon.cs | 40 ++ .../GameObj/Modules/ModuleFactory.cs | 128 +++++ .../GameClass/GameObj/Modules/War/WarArmor.cs | 26 + .../GameObj/Modules/War/WarShield.cs | 26 + .../GameObj/Modules/War/WarWeapon.cs | 40 ++ logic/Preparation/Interface/IModule.cs | 467 +----------------- 15 files changed, 448 insertions(+), 443 deletions(-) create mode 100644 logic/GameClass/GameObj/Modules/Civil/CivilArmor.cs create mode 100644 logic/GameClass/GameObj/Modules/Civil/CivilConstructor.cs create mode 100644 logic/GameClass/GameObj/Modules/Civil/CivilProducer.cs create mode 100644 logic/GameClass/GameObj/Modules/Civil/CivilShield.cs create mode 100644 logic/GameClass/GameObj/Modules/Civil/CivilWeapon.cs create mode 100644 logic/GameClass/GameObj/Modules/Flag/FlagArmor.cs create mode 100644 logic/GameClass/GameObj/Modules/Flag/FlagConstructor.cs create mode 100644 logic/GameClass/GameObj/Modules/Flag/FlagProducer.cs create mode 100644 logic/GameClass/GameObj/Modules/Flag/FlagShield.cs create mode 100644 logic/GameClass/GameObj/Modules/Flag/FlagWeapon.cs create mode 100644 logic/GameClass/GameObj/Modules/ModuleFactory.cs create mode 100644 logic/GameClass/GameObj/Modules/War/WarArmor.cs create mode 100644 logic/GameClass/GameObj/Modules/War/WarShield.cs create mode 100644 logic/GameClass/GameObj/Modules/War/WarWeapon.cs diff --git a/logic/GameClass/GameObj/Modules/Civil/CivilArmor.cs b/logic/GameClass/GameObj/Modules/Civil/CivilArmor.cs new file mode 100644 index 00000000..0ed1b35c --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Civil/CivilArmor.cs @@ -0,0 +1,12 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class CivilArmor1 : IArmor +{ + private const int cost = GameData.CivilShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Civil/CivilConstructor.cs b/logic/GameClass/GameObj/Modules/Civil/CivilConstructor.cs new file mode 100644 index 00000000..ced19df0 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Civil/CivilConstructor.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class CivilConstructor1 : IConstructor +{ + private const int cost = GameData.CivilShipConstructor1Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor1Speed; + public int ConstructSpeed => constructSpeed; +} +public class CivilConstructor2 : IConstructor +{ + private const int cost = GameData.CivilShipConstructor2Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor2Speed; + public int ConstructSpeed => constructSpeed; +} +public class CivilConstructor3 : IConstructor +{ + private const int cost = GameData.CivilShipConstructor3Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor2Speed; + public int ConstructSpeed => constructSpeed; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Civil/CivilProducer.cs b/logic/GameClass/GameObj/Modules/Civil/CivilProducer.cs new file mode 100644 index 00000000..5acffe38 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Civil/CivilProducer.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class CivilProducer1 : IProducer +{ + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer1PerSecond; + public int ProduceSpeed => produceSpeed; +} +public class CivilProducer2 : IProducer +{ + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer2PerSecond; + public int ProduceSpeed => produceSpeed; +} +public class CivilProducer3 : IProducer +{ + private const int cost = GameData.CivilShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer3PerSecond; + public int ProduceSpeed => produceSpeed; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Civil/CivilShield.cs b/logic/GameClass/GameObj/Modules/Civil/CivilShield.cs new file mode 100644 index 00000000..485548cf --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Civil/CivilShield.cs @@ -0,0 +1,12 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class CivilShield1 : IShield +{ + private const int cost = GameData.CivilShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Civil/CivilWeapon.cs b/logic/GameClass/GameObj/Modules/Civil/CivilWeapon.cs new file mode 100644 index 00000000..d5028ea8 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Civil/CivilWeapon.cs @@ -0,0 +1,12 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class CivilLaserGun : IWeapon +{ + private const int cost = GameData.CivilShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Flag/FlagArmor.cs b/logic/GameClass/GameObj/Modules/Flag/FlagArmor.cs new file mode 100644 index 00000000..ae6bd93d --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Flag/FlagArmor.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class FlagArmor1 : IArmor +{ + private const int cost = GameData.FlagShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; +} +public class FlagArmor2 : IArmor +{ + private const int cost = GameData.FlagShipArmor2Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor2; + public int ArmorHP => armorHP; +} +public class FlagArmor3 : IArmor +{ + private const int cost = GameData.FlagShipArmor3Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor3; + public int ArmorHP => armorHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Flag/FlagConstructor.cs b/logic/GameClass/GameObj/Modules/Flag/FlagConstructor.cs new file mode 100644 index 00000000..3dc4f277 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Flag/FlagConstructor.cs @@ -0,0 +1,12 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class FlagConstructor1 : IConstructor +{ + private const int cost = GameData.FlagShipConstructor1Cost; + public int Cost => cost; + private const int constructSpeed = GameData.Constructor1Speed; + public int ConstructSpeed => constructSpeed; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Flag/FlagProducer.cs b/logic/GameClass/GameObj/Modules/Flag/FlagProducer.cs new file mode 100644 index 00000000..142055ba --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Flag/FlagProducer.cs @@ -0,0 +1,12 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class FlagProducer1 : IProducer +{ + private const int cost = GameData.FlagShipProducer1Cost; + public int Cost => cost; + private const int produceSpeed = GameData.ScoreProducer1PerSecond; + public int ProduceSpeed => produceSpeed; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Flag/FlagShield.cs b/logic/GameClass/GameObj/Modules/Flag/FlagShield.cs new file mode 100644 index 00000000..d575533c --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Flag/FlagShield.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class FlagShield1 : IShield +{ + private const int cost = GameData.FlagShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; +} +public class FlagShield2 : IShield +{ + private const int cost = GameData.FlagShipShield2Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield2; + public int ShieldHP => shieldHP; +} +public class FlagShield3 : IShield +{ + private const int cost = GameData.FlagShipShield3Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield3; + public int ShieldHP => shieldHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/Flag/FlagWeapon.cs b/logic/GameClass/GameObj/Modules/Flag/FlagWeapon.cs new file mode 100644 index 00000000..8a6606df --- /dev/null +++ b/logic/GameClass/GameObj/Modules/Flag/FlagWeapon.cs @@ -0,0 +1,40 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class FlagLaserGun : IWeapon +{ + private const int cost = GameData.FlagShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; +} +public class FlagPlasmaGun : IWeapon +{ + private const int cost = GameData.FlagShipPlasmaGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Plasma; + public BulletType BulletType => bulletType; +} +public class FlagShellGun : IWeapon +{ + private const int cost = GameData.FlagShipShellGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Shell; + public BulletType BulletType => bulletType; +} +public class FlagMissileGun : IWeapon +{ + private const int cost = GameData.FlagShipMissileGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Missile; + public BulletType BulletType => bulletType; +} +public class FlagArcGun : IWeapon +{ + private const int cost = GameData.FlagShipArcGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Arc; + public BulletType BulletType => bulletType; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/ModuleFactory.cs b/logic/GameClass/GameObj/Modules/ModuleFactory.cs new file mode 100644 index 00000000..382daebc --- /dev/null +++ b/logic/GameClass/GameObj/Modules/ModuleFactory.cs @@ -0,0 +1,128 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public static class ModuleFactory +{ + public static IProducer FindIProducer(ShipType shipType, ProducerType producerType) + { + return shipType switch + { + ShipType.CivilShip => producerType switch + { + ProducerType.Producer1 => new CivilProducer1(), + ProducerType.Producer2 => new CivilProducer2(), + ProducerType.Producer3 => new CivilProducer3(), + _ => new CivilProducer1(), + }, + ShipType.FlagShip => producerType switch + { + ProducerType.Producer1 => new FlagProducer1(), + _ => new FlagProducer1(), + }, + _ => new CivilProducer1(), + }; + } + public static IConstructor FindIConstructor(ShipType shipType, ConstructorType constructorType) + { + return shipType switch + { + ShipType.CivilShip => constructorType switch + { + ConstructorType.Constructor1 => new CivilConstructor1(), + ConstructorType.Constructor2 => new CivilConstructor2(), + ConstructorType.Constructor3 => new CivilConstructor3(), + _ => new CivilConstructor1(), + }, + ShipType.FlagShip => constructorType switch + { + ConstructorType.Constructor1 => new FlagConstructor1(), + _ => new FlagConstructor1(), + }, + _ => new CivilConstructor1(), + }; + } + public static IArmor FindIArmor(ShipType shipType, ArmorType armorType) + { + return shipType switch + { + ShipType.CivilShip => armorType switch + { + ArmorType.Armor1 => new CivilArmor1(), + _ => new CivilArmor1(), + }, + ShipType.WarShip => armorType switch + { + ArmorType.Armor1 => new WarArmor1(), + ArmorType.Armor2 => new WarArmor2(), + ArmorType.Armor3 => new WarArmor3(), + _ => new WarArmor1(), + }, + ShipType.FlagShip => armorType switch + { + ArmorType.Armor1 => new FlagArmor1(), + ArmorType.Armor2 => new FlagArmor2(), + ArmorType.Armor3 => new FlagArmor3(), + _ => new FlagArmor1(), + }, + _ => new CivilArmor1(), + }; + } + public static IShield FindIShield(ShipType shipType, ShieldType shieldType) + { + return shipType switch + { + ShipType.CivilShip => shieldType switch + { + ShieldType.Shield1 => new CivilShield1(), + _ => new CivilShield1(), + }, + ShipType.WarShip => shieldType switch + { + ShieldType.Shield1 => new WarShield1(), + ShieldType.Shield2 => new WarShield2(), + ShieldType.Shield3 => new WarShield3(), + _ => new WarShield1(), + }, + ShipType.FlagShip => shieldType switch + { + ShieldType.Shield1 => new FlagShield1(), + ShieldType.Shield2 => new FlagShield2(), + ShieldType.Shield3 => new FlagShield3(), + _ => new FlagShield1(), + }, + _ => new CivilShield1(), + }; + } + public static IWeapon FindIWeapon(ShipType shipType, WeaponType weaponType) + { + return shipType switch + { + ShipType.CivilShip => weaponType switch + { + WeaponType.LaserGun => new CivilLaserGun(), + _ => new CivilLaserGun(), + }, + ShipType.WarShip => weaponType switch + { + WeaponType.LaserGun => new WarLaserGun(), + WeaponType.PlasmaGun => new WarPlasmaGun(), + WeaponType.ShellGun => new WarShellGun(), + WeaponType.MissileGun => new WarMissileGun(), + WeaponType.ArcGun => new WarArcGun(), + _ => new WarLaserGun(), + }, + ShipType.FlagShip => weaponType switch + { + WeaponType.LaserGun => new FlagLaserGun(), + WeaponType.PlasmaGun => new FlagPlasmaGun(), + WeaponType.ShellGun => new FlagShellGun(), + WeaponType.MissileGun => new FlagMissileGun(), + WeaponType.ArcGun => new FlagArcGun(), + _ => new FlagLaserGun(), + }, + _ => new CivilLaserGun(), + }; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/War/WarArmor.cs b/logic/GameClass/GameObj/Modules/War/WarArmor.cs new file mode 100644 index 00000000..6083ebe8 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/War/WarArmor.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class WarArmor1 : IArmor +{ + private const int cost = GameData.WarShipArmor1Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor1; + public int ArmorHP => armorHP; +} +public class WarArmor2 : IArmor +{ + private const int cost = GameData.WarShipArmor2Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor2; + public int ArmorHP => armorHP; +} +public class WarArmor3 : IArmor +{ + private const int cost = GameData.WarShipArmor3Cost; + public int Cost => cost; + private const int armorHP = GameData.Armor3; + public int ArmorHP => armorHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/War/WarShield.cs b/logic/GameClass/GameObj/Modules/War/WarShield.cs new file mode 100644 index 00000000..ac9f90b4 --- /dev/null +++ b/logic/GameClass/GameObj/Modules/War/WarShield.cs @@ -0,0 +1,26 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class WarShield1 : IShield +{ + private const int cost = GameData.WarShipShield1Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield1; + public int ShieldHP => shieldHP; +} +public class WarShield2 : IShield +{ + private const int cost = GameData.WarShipShield2Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield2; + public int ShieldHP => shieldHP; +} +public class WarShield3 : IShield +{ + private const int cost = GameData.WarShipShield3Cost; + public int Cost => cost; + private const int shieldHP = GameData.Shield3; + public int ShieldHP => shieldHP; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Modules/War/WarWeapon.cs b/logic/GameClass/GameObj/Modules/War/WarWeapon.cs new file mode 100644 index 00000000..bbdce2af --- /dev/null +++ b/logic/GameClass/GameObj/Modules/War/WarWeapon.cs @@ -0,0 +1,40 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Modules; + +public class WarLaserGun : IWeapon +{ + private const int cost = GameData.WarShipLaserGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Laser; + public BulletType BulletType => bulletType; +} +public class WarPlasmaGun : IWeapon +{ + private const int cost = GameData.WarShipPlasmaGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Plasma; + public BulletType BulletType => bulletType; +} +public class WarShellGun : IWeapon +{ + private const int cost = GameData.WarShipShellGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Shell; + public BulletType BulletType => bulletType; +} +public class WarMissileGun : IWeapon +{ + private const int cost = GameData.WarShipMissileGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Missile; + public BulletType BulletType => bulletType; +} +public class WarArcGun : IWeapon +{ + private const int cost = GameData.WarShipArcGunCost; + public int Cost => cost; + private const BulletType bulletType = BulletType.Arc; + public BulletType BulletType => bulletType; +} \ No newline at end of file diff --git a/logic/Preparation/Interface/IModule.cs b/logic/Preparation/Interface/IModule.cs index 6222ca7b..b550b2be 100644 --- a/logic/Preparation/Interface/IModule.cs +++ b/logic/Preparation/Interface/IModule.cs @@ -1,447 +1,28 @@ using Preparation.Utility; -namespace Preparation.Interface +namespace Preparation.Interface; + +public interface IModule +{ + public int Cost { get; } +} +public interface IProducer : IModule +{ + public int ProduceSpeed { get; } +} +public interface IConstructor : IModule +{ + public int ConstructSpeed { get; } +} +public interface IArmor : IModule +{ + public int ArmorHP { get; } +} +public interface IShield : IModule +{ + public int ShieldHP { get; } +} +public interface IWeapon : IModule { - public interface IModule - { - public int Cost { get; } - } - public interface IProducer : IModule - { - public int ProduceSpeed { get; } - } - public interface IConstructor : IModule - { - public int ConstructSpeed { get; } - } - public interface IArmor : IModule - { - public int ArmorHP { get; } - } - public interface IShield : IModule - { - public int ShieldHP { get; } - } - public interface IWeapon : IModule - { - public BulletType BulletType { get; } - } - public class CivilProducer1 : IProducer - { - private const int cost = GameData.CivilShipProducer1Cost; - public int Cost => cost; - private const int produceSpeed = GameData.ScoreProducer1PerSecond; - public int ProduceSpeed => produceSpeed; - } - public class CivilProducer2 : IProducer - { - private const int cost = GameData.CivilShipProducer1Cost; - public int Cost => cost; - private const int produceSpeed = GameData.ScoreProducer2PerSecond; - public int ProduceSpeed => produceSpeed; - } - public class CivilProducer3 : IProducer - { - private const int cost = GameData.CivilShipProducer1Cost; - public int Cost => cost; - private const int produceSpeed = GameData.ScoreProducer3PerSecond; - public int ProduceSpeed => produceSpeed; - } - public class CivilConstructor1 : IConstructor - { - private const int cost = GameData.CivilShipConstructor1Cost; - public int Cost => cost; - private const int constructSpeed = GameData.Constructor1Speed; - public int ConstructSpeed => constructSpeed; - } - public class CivilConstructor2 : IConstructor - { - private const int cost = GameData.CivilShipConstructor2Cost; - public int Cost => cost; - private const int constructSpeed = GameData.Constructor2Speed; - public int ConstructSpeed => constructSpeed; - } - public class CivilConstructor3 : IConstructor - { - private const int cost = GameData.CivilShipConstructor3Cost; - public int Cost => cost; - private const int constructSpeed = GameData.Constructor2Speed; - public int ConstructSpeed => constructSpeed; - } - public class CivilArmor1 : IArmor - { - private const int cost = GameData.CivilShipArmor1Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor1; - public int ArmorHP => armorHP; - } - public class CivilShield1 : IShield - { - private const int cost = GameData.CivilShipShield1Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield1; - public int ShieldHP => shieldHP; - } - public class CivilLaserGun : IWeapon - { - private const int cost = GameData.CivilShipLaserGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Laser; - public BulletType BulletType => bulletType; - } - public class WarArmor1 : IArmor - { - private const int cost = GameData.WarShipArmor1Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor1; - public int ArmorHP => armorHP; - } - public class WarArmor2 : IArmor - { - private const int cost = GameData.WarShipArmor2Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor2; - public int ArmorHP => armorHP; - } - public class WarArmor3 : IArmor - { - private const int cost = GameData.WarShipArmor3Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor3; - public int ArmorHP => armorHP; - } - public class WarShield1 : IShield - { - private const int cost = GameData.WarShipShield1Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield1; - public int ShieldHP => shieldHP; - } - public class WarShield2 : IShield - { - private const int cost = GameData.WarShipShield2Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield2; - public int ShieldHP => shieldHP; - } - public class WarShield3 : IShield - { - private const int cost = GameData.WarShipShield3Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield3; - public int ShieldHP => shieldHP; - } - public class WarLaserGun : IWeapon - { - private const int cost = GameData.WarShipLaserGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Laser; - public BulletType BulletType => bulletType; - } - public class WarPlasmaGun : IWeapon - { - private const int cost = GameData.WarShipPlasmaGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Plasma; - public BulletType BulletType => bulletType; - } - public class WarShellGun : IWeapon - { - private const int cost = GameData.WarShipShellGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Shell; - public BulletType BulletType => bulletType; - } - public class WarMissileGun : IWeapon - { - private const int cost = GameData.WarShipMissileGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Missile; - public BulletType BulletType => bulletType; - } - public class WarArcGun : IWeapon - { - private const int cost = GameData.WarShipArcGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Arc; - public BulletType BulletType => bulletType; - } - public class FlagProducer1 : IProducer - { - private const int cost = GameData.FlagShipProducer1Cost; - public int Cost => cost; - private const int produceSpeed = GameData.ScoreProducer1PerSecond; - public int ProduceSpeed => produceSpeed; - } - public class FlagConstructor1 : IConstructor - { - private const int cost = GameData.FlagShipConstructor1Cost; - public int Cost => cost; - private const int constructSpeed = GameData.Constructor1Speed; - public int ConstructSpeed => constructSpeed; - } - public class FlagArmor1 : IArmor - { - private const int cost = GameData.FlagShipArmor1Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor1; - public int ArmorHP => armorHP; - } - public class FlagArmor2 : IArmor - { - private const int cost = GameData.FlagShipArmor2Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor2; - public int ArmorHP => armorHP; - } - public class FlagArmor3 : IArmor - { - private const int cost = GameData.FlagShipArmor3Cost; - public int Cost => cost; - private const int armorHP = GameData.Armor3; - public int ArmorHP => armorHP; - } - public class FlagShield1 : IShield - { - private const int cost = GameData.FlagShipShield1Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield1; - public int ShieldHP => shieldHP; - } - public class FlagShield2 : IShield - { - private const int cost = GameData.FlagShipShield2Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield2; - public int ShieldHP => shieldHP; - } - public class FlagShield3 : IShield - { - private const int cost = GameData.FlagShipShield3Cost; - public int Cost => cost; - private const int shieldHP = GameData.Shield3; - public int ShieldHP => shieldHP; - } - public class FlagLaserGun : IWeapon - { - private const int cost = GameData.FlagShipLaserGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Laser; - public BulletType BulletType => bulletType; - } - public class FlagPlasmaGun : IWeapon - { - private const int cost = GameData.FlagShipPlasmaGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Plasma; - public BulletType BulletType => bulletType; - } - public class FlagShellGun : IWeapon - { - private const int cost = GameData.FlagShipShellGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Shell; - public BulletType BulletType => bulletType; - } - public class FlagMissileGun : IWeapon - { - private const int cost = GameData.FlagShipMissileGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Missile; - public BulletType BulletType => bulletType; - } - public class FlagArcGun : IWeapon - { - private const int cost = GameData.FlagShipArcGunCost; - public int Cost => cost; - private const BulletType bulletType = BulletType.Arc; - public BulletType BulletType => bulletType; - } - public static class ModuleFactory - { - public static IProducer FindIProducer(ShipType shipType, ProducerType producerType) - { - switch (shipType) - { - case ShipType.CivilShip: - switch (producerType) - { - case ProducerType.Producer1: - return new CivilProducer1(); - case ProducerType.Producer2: - return new CivilProducer2(); - case ProducerType.Producer3: - return new CivilProducer3(); - default: - return new CivilProducer1(); - } - case ShipType.FlagShip: - switch (producerType) - { - case ProducerType.Producer1: - return new FlagProducer1(); - default: - return new FlagProducer1(); - } - default: - return new CivilProducer1(); - } - } - public static IConstructor FindIConstructor(ShipType shipType, ConstructorType constructorType) - { - switch (shipType) - { - case ShipType.CivilShip: - switch (constructorType) - { - case ConstructorType.Constructor1: - return new CivilConstructor1(); - case ConstructorType.Constructor2: - return new CivilConstructor2(); - case ConstructorType.Constructor3: - return new CivilConstructor3(); - default: - return new CivilConstructor1(); - } - case ShipType.FlagShip: - switch (constructorType) - { - case ConstructorType.Constructor1: - return new FlagConstructor1(); - default: - return new FlagConstructor1(); - } - default: - return new CivilConstructor1(); - } - } - public static IArmor FindIArmor(ShipType shipType, ArmorType armorType) - { - switch (shipType) - { - case ShipType.CivilShip: - switch (armorType) - { - case ArmorType.Armor1: - return new CivilArmor1(); - default: - return new CivilArmor1(); - } - case ShipType.WarShip: - switch (armorType) - { - case ArmorType.Armor1: - return new WarArmor1(); - case ArmorType.Armor2: - return new WarArmor2(); - case ArmorType.Armor3: - return new WarArmor3(); - default: - return new WarArmor1(); - } - case ShipType.FlagShip: - switch (armorType) - { - case ArmorType.Armor1: - return new FlagArmor1(); - case ArmorType.Armor2: - return new FlagArmor2(); - case ArmorType.Armor3: - return new FlagArmor3(); - default: - return new FlagArmor1(); - } - default: - return new CivilArmor1(); - } - } - public static IShield FindIShield(ShipType shipType, ShieldType shieldType) - { - switch (shipType) - { - case ShipType.CivilShip: - switch (shieldType) - { - case ShieldType.Shield1: - return new CivilShield1(); - default: - return new CivilShield1(); - } - case ShipType.WarShip: - switch (shieldType) - { - case ShieldType.Shield1: - return new WarShield1(); - case ShieldType.Shield2: - return new WarShield2(); - case ShieldType.Shield3: - return new WarShield3(); - default: - return new WarShield1(); - } - case ShipType.FlagShip: - switch (shieldType) - { - case ShieldType.Shield1: - return new FlagShield1(); - case ShieldType.Shield2: - return new FlagShield2(); - case ShieldType.Shield3: - return new FlagShield3(); - default: - return new FlagShield1(); - } - default: - return new CivilShield1(); - } - } - public static IWeapon FindIWeapon(ShipType shipType, WeaponType weaponType) - { - switch (shipType) - { - case ShipType.CivilShip: - switch (weaponType) - { - case WeaponType.LaserGun: - return new CivilLaserGun(); - default: - return new CivilLaserGun(); - } - case ShipType.WarShip: - switch (weaponType) - { - case WeaponType.LaserGun: - return new WarLaserGun(); - case WeaponType.PlasmaGun: - return new WarPlasmaGun(); - case WeaponType.ShellGun: - return new WarShellGun(); - case WeaponType.MissileGun: - return new WarMissileGun(); - case WeaponType.ArcGun: - return new WarArcGun(); - default: - return new WarLaserGun(); - } - case ShipType.FlagShip: - switch (weaponType) - { - case WeaponType.LaserGun: - return new FlagLaserGun(); - case WeaponType.PlasmaGun: - return new FlagPlasmaGun(); - case WeaponType.ShellGun: - return new FlagShellGun(); - case WeaponType.MissileGun: - return new FlagMissileGun(); - case WeaponType.ArcGun: - return new FlagArcGun(); - default: - return new FlagLaserGun(); - } - default: - return new CivilLaserGun(); - } - } - } + public BulletType BulletType { get; } } From e6fb4361c4079ed992ae92cd1f5a867a22999483 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Wed, 25 Oct 2023 22:52:12 +0800 Subject: [PATCH 07/14] fix: :bug: using namespace Modules in Ship.cs --- logic/GameClass/GameObj/Ship.cs | 325 ++++++++++++++++---------------- 1 file changed, 161 insertions(+), 164 deletions(-) diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs index 95ad01a9..11c8bb71 100644 --- a/logic/GameClass/GameObj/Ship.cs +++ b/logic/GameClass/GameObj/Ship.cs @@ -1,198 +1,195 @@ using Preparation.Interface; using Preparation.Utility; -using System; -using System.Collections.Generic; -using System.Threading; +using GameClass.GameObj.Modules; -namespace GameClass.GameObj +namespace GameClass.GameObj; + +public class Ship : Movable, IShip { - public class Ship : Movable, IShip + public AtomicLong TeamID { get; } = new AtomicLong(long.MaxValue); + public AtomicLong ShipID { get; } = new AtomicLong(long.MaxValue); + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Circle; + private readonly int viewRange; + public int ViewRange => viewRange; + public override bool IgnoreCollideExecutor(IGameObj targetObj) { - public AtomicLong TeamID { get; } = new AtomicLong(long.MaxValue); - public AtomicLong ShipID { get; } = new AtomicLong(long.MaxValue); - public override bool IsRigid => true; - public override ShapeType Shape => ShapeType.Circle; - private readonly int viewRange; - public int ViewRange => viewRange; - public override bool IgnoreCollideExecutor(IGameObj targetObj) - { - if (IsRemoved) - return true; - if (targetObj.Type == GameObjType.Ship && XY.DistanceCeil3(targetObj.Position, this.Position) < this.Radius + targetObj.Radius - GameData.AdjustLength) - return true; - return false; - } - public LongWithVariableRange HP { get; } - public LongWithVariableRange Armor { get; } - public LongWithVariableRange Shield { get; } - private ShipType shipType = ShipType.Null; - public ShipType ShipType => shipType; - private ShipStateType shipState = ShipStateType.Null; - public ShipStateType ShipState => shipState; - private readonly IOccupation occupation; - public IOccupation Occupation => occupation; - public IntNumUpdateByCD BulletNum { get; } - private IProducer? producer = null; - public IProducer? ProducerModule => producer; - private IConstructor? constructor = null; - public IConstructor? ConstructorModule => constructor; - private IArmor? armor = null; - public IArmor? ArmorModule => armor; - private IShield? shield = null; - public IShield? ShieldModule => shield; - private IWeapon? weapon = null; - public IWeapon? WeaponModule => weapon; - private GameObj? whatInteractingWith = null; - public GameObj? WhatInteractingWith + if (IsRemoved) + return true; + if (targetObj.Type == GameObjType.Ship && XY.DistanceCeil3(targetObj.Position, this.Position) < this.Radius + targetObj.Radius - GameData.AdjustLength) + return true; + return false; + } + public LongWithVariableRange HP { get; } + public LongWithVariableRange Armor { get; } + public LongWithVariableRange Shield { get; } + private ShipType shipType = ShipType.Null; + public ShipType ShipType => shipType; + private ShipStateType shipState = ShipStateType.Null; + public ShipStateType ShipState => shipState; + private readonly IOccupation occupation; + public IOccupation Occupation => occupation; + public IntNumUpdateByCD BulletNum { get; } + private IProducer? producer = null; + public IProducer? ProducerModule => producer; + private IConstructor? constructor = null; + public IConstructor? ConstructorModule => constructor; + private IArmor? armor = null; + public IArmor? ArmorModule => armor; + private IShield? shield = null; + public IShield? ShieldModule => shield; + private IWeapon? weapon = null; + public IWeapon? WeaponModule => weapon; + private GameObj? whatInteractingWith = null; + public GameObj? WhatInteractingWith + { + get { - get + lock (actionLock) { - lock (actionLock) - { - return whatInteractingWith; - } + return whatInteractingWith; } } - private long ChangeShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + } + private long ChangeShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + { + //只能被SetShipState引用 + if (runningState == RunningStateType.RunningSleepily) { - //只能被SetShipState引用 - if (runningState == RunningStateType.RunningSleepily) - { - ThreadNum.Release(); - } - runningState = running; - whatInteractingWith = gameObj; - shipState = value; - return ++stateNum; + ThreadNum.Release(); } - private long ChangeShipStateInOneThread(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + runningState = running; + whatInteractingWith = gameObj; + shipState = value; + return ++stateNum; + } + private long ChangeShipStateInOneThread(RunningStateType running, ShipStateType value = ShipStateType.Null, GameObj? gameObj = null) + { + if (runningState == RunningStateType.RunningSleepily) { - if (runningState == RunningStateType.RunningSleepily) - { - ThreadNum.Release(); - } - runningState = running; - //只能被SetPlayerState引用 - whatInteractingWith = gameObj; - shipState = value; - return stateNum; + ThreadNum.Release(); } - public long SetShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + runningState = running; + //只能被SetPlayerState引用 + whatInteractingWith = gameObj; + shipState = value; + return stateNum; + } + public long SetShipState(RunningStateType running, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + GameObj? gameObj = (GameObj?)obj; + lock (actionLock) { - GameObj? gameObj = (GameObj?)obj; - lock (actionLock) + ShipStateType nowShipState = ShipState; + if (nowShipState == value) return -1; + GameObj? lastObj = whatInteractingWith; + switch (nowShipState) { - ShipStateType nowShipState = ShipState; - if (nowShipState == value) return -1; - GameObj? lastObj = whatInteractingWith; - switch (nowShipState) - { - case ShipStateType.Attacking: - if (value == ShipStateType.Null || value == ShipStateType.Stunned || value == ShipStateType.Swinging) - return ChangeShipState(running, value, gameObj); - else return -1; - case ShipStateType.Stunned: - if (value == ShipStateType.Null) - return ChangeShipState(running, value, gameObj); - else return -1; - case ShipStateType.Swinging: - if (value == ShipStateType.Null || value == ShipStateType.Stunned) - return ChangeShipState(running, value, gameObj); - else return -1; - default: + case ShipStateType.Attacking: + if (value == ShipStateType.Null || value == ShipStateType.Stunned || value == ShipStateType.Swinging) return ChangeShipState(running, value, gameObj); - } + else return -1; + case ShipStateType.Stunned: + if (value == ShipStateType.Null) + return ChangeShipState(running, value, gameObj); + else return -1; + case ShipStateType.Swinging: + if (value == ShipStateType.Null || value == ShipStateType.Stunned) + return ChangeShipState(running, value, gameObj); + else return -1; + default: + return ChangeShipState(running, value, gameObj); } } - public long SetShipStateNaturally() + } + public long SetShipStateNaturally() + { + lock (actionLock) { - lock (actionLock) - { - runningState = RunningStateType.Null; - whatInteractingWith = null; - shipState = ShipStateType.Null; - return ++stateNum; - } + runningState = RunningStateType.Null; + whatInteractingWith = null; + shipState = ShipStateType.Null; + return ++stateNum; } - public bool ResetShipState(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + } + public bool ResetShipState(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + lock (actionLock) { - lock (actionLock) - { - if (state != stateNum) return false; - this.runningState = running; - whatInteractingWith = (GameObj?)obj; - shipState = value; - ++stateNum; - return true; - } + if (state != stateNum) return false; + this.runningState = running; + whatInteractingWith = (GameObj?)obj; + shipState = value; + ++stateNum; + return true; } - public bool ResetShipStateInOneThread(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + } + public bool ResetShipStateInOneThread(long state, RunningStateType running = RunningStateType.Null, ShipStateType value = ShipStateType.Null, IGameObj? obj = null) + { + lock (actionLock) { - lock (actionLock) - { - if (state != stateNum) return false; - this.runningState = running; - whatInteractingWith = (GameObj?)obj; - shipState = value; - return true; - } + if (state != stateNum) return false; + this.runningState = running; + whatInteractingWith = (GameObj?)obj; + shipState = value; + return true; } - public bool StartThread(long stateNum, RunningStateType runningState) + } + public bool StartThread(long stateNum, RunningStateType runningState) + { + lock (ActionLock) { - lock (ActionLock) + if (this.StateNum == stateNum) { - if (this.StateNum == stateNum) - { - this.runningState = runningState; - return true; - } + this.runningState = runningState; + return true; } - return false; } - public bool TryToRemoveFromGame(ShipStateType shipStateType) + return false; + } + public bool TryToRemoveFromGame(ShipStateType shipStateType) + { + lock (actionLock) { - lock (actionLock) - { - if (SetShipState(RunningStateType.RunningForcibly, shipStateType) == -1) return false; - TryToRemove(); - CanMove.SetReturnOri(false); - position = GameData.PosNotInGame; - } - return true; + if (SetShipState(RunningStateType.RunningForcibly, shipStateType) == -1) return false; + TryToRemove(); + CanMove.SetReturnOri(false); + position = GameData.PosNotInGame; } - public Ship(XY initPos, int initRadius, ShipType shipType) : - base(initPos, initRadius, GameObjType.Ship) + return true; + } + public Ship(XY initPos, int initRadius, ShipType shipType) : + base(initPos, initRadius, GameObjType.Ship) + { + this.CanMove.SetReturnOri(true); + this.occupation = OccupationFactory.FindIOccupation(shipType); + this.viewRange = occupation.ViewRange; + this.HP = new(Occupation.MaxHp); + this.MoveSpeed.SetReturnOri(this.orgMoveSpeed = Occupation.MoveSpeed); + this.shipType = shipType; + switch (shipType) { - this.CanMove.SetReturnOri(true); - this.occupation = OccupationFactory.FindIOccupation(shipType); - this.viewRange = occupation.ViewRange; - this.HP = new(Occupation.MaxHp); - this.MoveSpeed.SetReturnOri(this.orgMoveSpeed = Occupation.MoveSpeed); - this.shipType = shipType; - switch (shipType) - { - case ShipType.CivilShip: - this.producer = new CivilProducer1(); - this.constructor = new CivilConstructor1(); - this.armor = null; - this.shield = null; - this.weapon = null; - break; - case ShipType.WarShip: - this.producer = null; - this.constructor = null; - this.armor = null; - this.shield = null; - this.weapon = new WarLaserGun(); - break; - case ShipType.FlagShip: - this.producer = null; - this.constructor = null; - this.armor = null; - this.shield = null; - this.weapon = new FlagLaserGun(); - break; - } + case ShipType.CivilShip: + this.producer = new CivilProducer1(); + this.constructor = new CivilConstructor1(); + this.armor = null; + this.shield = null; + this.weapon = null; + break; + case ShipType.WarShip: + this.producer = null; + this.constructor = null; + this.armor = null; + this.shield = null; + this.weapon = new WarLaserGun(); + break; + case ShipType.FlagShip: + this.producer = null; + this.constructor = null; + this.armor = null; + this.shield = null; + this.weapon = new FlagLaserGun(); + break; } } } From 43d4009cff57e9d262a273f543141a973306bd26 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Wed, 25 Oct 2023 23:17:23 +0800 Subject: [PATCH 08/14] refactor: :recycle: add namespace Occupations --- .../GameObj/Occupations/CivilShip.cs | 14 +++++ .../GameClass/GameObj/Occupations/FlagShip.cs | 14 +++++ .../GameObj/Occupations/OccupationFactory.cs | 18 ++++++ .../GameClass/GameObj/Occupations/WarShip.cs | 14 +++++ logic/GameClass/GameObj/Ship.cs | 1 + logic/Preparation/Interface/IOccupation.cs | 58 ++----------------- 6 files changed, 67 insertions(+), 52 deletions(-) create mode 100644 logic/GameClass/GameObj/Occupations/CivilShip.cs create mode 100644 logic/GameClass/GameObj/Occupations/FlagShip.cs create mode 100644 logic/GameClass/GameObj/Occupations/OccupationFactory.cs create mode 100644 logic/GameClass/GameObj/Occupations/WarShip.cs diff --git a/logic/GameClass/GameObj/Occupations/CivilShip.cs b/logic/GameClass/GameObj/Occupations/CivilShip.cs new file mode 100644 index 00000000..ab84d254 --- /dev/null +++ b/logic/GameClass/GameObj/Occupations/CivilShip.cs @@ -0,0 +1,14 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Occupations; + +public class CivilShip : IOccupation +{ + private const int moveSpeed = GameData.CivilShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.CivilShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.CivilShipViewRange; + public int ViewRange => viewRange; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Occupations/FlagShip.cs b/logic/GameClass/GameObj/Occupations/FlagShip.cs new file mode 100644 index 00000000..f5b7d122 --- /dev/null +++ b/logic/GameClass/GameObj/Occupations/FlagShip.cs @@ -0,0 +1,14 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Occupations; + +public class FlagShip : IOccupation +{ + private const int moveSpeed = GameData.FlagShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.FlagShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.FlagShipViewRange; + public int ViewRange => viewRange; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Occupations/OccupationFactory.cs b/logic/GameClass/GameObj/Occupations/OccupationFactory.cs new file mode 100644 index 00000000..05436500 --- /dev/null +++ b/logic/GameClass/GameObj/Occupations/OccupationFactory.cs @@ -0,0 +1,18 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Occupations; + +public static class OccupationFactory +{ + public static IOccupation FindIOccupation(ShipType shipType) + { + return shipType switch + { + ShipType.CivilShip => new CivilShip(), + ShipType.WarShip => new WarShip(), + ShipType.FlagShip => new FlagShip(), + _ => new CivilShip(), + }; + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Occupations/WarShip.cs b/logic/GameClass/GameObj/Occupations/WarShip.cs new file mode 100644 index 00000000..acbf0139 --- /dev/null +++ b/logic/GameClass/GameObj/Occupations/WarShip.cs @@ -0,0 +1,14 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Occupations; + +public class WarShip : IOccupation +{ + private const int moveSpeed = GameData.WarShipMoveSpeed; + public int MoveSpeed => moveSpeed; + private const int maxHp = GameData.WarShipMaxHP; + public int MaxHp => maxHp; + private const int viewRange = GameData.WarShipViewRange; + public int ViewRange => viewRange; +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs index 11c8bb71..a7e0e2d8 100644 --- a/logic/GameClass/GameObj/Ship.cs +++ b/logic/GameClass/GameObj/Ship.cs @@ -1,6 +1,7 @@ using Preparation.Interface; using Preparation.Utility; using GameClass.GameObj.Modules; +using GameClass.GameObj.Occupations; namespace GameClass.GameObj; diff --git a/logic/Preparation/Interface/IOccupation.cs b/logic/Preparation/Interface/IOccupation.cs index b12e34da..da613d7e 100644 --- a/logic/Preparation/Interface/IOccupation.cs +++ b/logic/Preparation/Interface/IOccupation.cs @@ -1,56 +1,10 @@ using Preparation.Utility; -using System.Collections.Generic; -namespace Preparation.Interface +namespace Preparation.Interface; + +public interface IOccupation { - public interface IOccupation - { - public int MoveSpeed { get; } - public int MaxHp { get; } - public int ViewRange { get; } - } - public class CivilShip : IOccupation - { - private const int moveSpeed = GameData.CivilShipMoveSpeed; - public int MoveSpeed => moveSpeed; - private const int maxHp = GameData.CivilShipMaxHP; - public int MaxHp => maxHp; - private const int viewRange = GameData.CivilShipViewRange; - public int ViewRange => viewRange; - } - public class WarShip : IOccupation - { - private const int moveSpeed = GameData.WarShipMoveSpeed; - public int MoveSpeed => moveSpeed; - private const int maxHp = GameData.WarShipMaxHP; - public int MaxHp => maxHp; - private const int viewRange = GameData.WarShipViewRange; - public int ViewRange => viewRange; - } - public class FlagShip : IOccupation - { - private const int moveSpeed = GameData.FlagShipMoveSpeed; - public int MoveSpeed => moveSpeed; - private const int maxHp = GameData.FlagShipMaxHP; - public int MaxHp => maxHp; - private const int viewRange = GameData.FlagShipViewRange; - public int ViewRange => viewRange; - } - public static class OccupationFactory - { - public static IOccupation FindIOccupation(ShipType shipType) - { - switch (shipType) - { - case ShipType.CivilShip: - return new CivilShip(); - case ShipType.WarShip: - return new WarShip(); - case ShipType.FlagShip: - return new FlagShip(); - default: - return new CivilShip(); - } - } - } + public int MoveSpeed { get; } + public int MaxHp { get; } + public int ViewRange { get; } } From 66b0b61fd32db119956112b294bcb27676f1a479 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Thu, 26 Oct 2023 23:51:03 +0800 Subject: [PATCH 09/14] fix: revise the locks in ObjOfShip.cs - revise ObjOfShipReaderWriterLock to ObjOfShipLock - revise ActionLock to actionLock in Ship.cs --- logic/GameClass/GameObj/ObjOfShip.cs | 7 +++---- logic/GameClass/GameObj/Ship.cs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/logic/GameClass/GameObj/ObjOfShip.cs b/logic/GameClass/GameObj/ObjOfShip.cs index 061f14e9..83d0c5f5 100644 --- a/logic/GameClass/GameObj/ObjOfShip.cs +++ b/logic/GameClass/GameObj/ObjOfShip.cs @@ -9,19 +9,18 @@ namespace GameClass.GameObj /// public abstract class ObjOfShip : Movable, IObjOfShip { - private ReaderWriterLockSlim objOfShipReaderWriterLock = new(); - public ReaderWriterLockSlim ObjOfShipReaderWriterLock => objOfShipReaderWriterLock; + public object ObjOfShipLock { get; } = new(); private IShip? parent = null; public IShip? Parent { get { - lock (objOfShipReaderWriterLock) + lock (ObjOfShipLock) return parent; } set { - lock (objOfShipReaderWriterLock) + lock (ObjOfShipLock) parent = value; } } diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs index a7e0e2d8..36d7bf14 100644 --- a/logic/GameClass/GameObj/Ship.cs +++ b/logic/GameClass/GameObj/Ship.cs @@ -138,7 +138,7 @@ public bool ResetShipStateInOneThread(long state, RunningStateType running = Run } public bool StartThread(long stateNum, RunningStateType runningState) { - lock (ActionLock) + lock (actionLock) { if (this.StateNum == stateNum) { From 7e3e45c0e78bac1e39bfb37013925e77d68c455e Mon Sep 17 00:00:00 2001 From: Panxuc Date: Fri, 27 Oct 2023 11:04:43 +0800 Subject: [PATCH 10/14] feat: :sparkles: add basic server --- logic/Server/ArgumentOptions.cs | 44 ++++ logic/Server/PlaybackServer.cs | 332 ++++++++++++++++++++++++++ logic/Server/Program.cs | 74 ++++++ logic/Server/ServerBase.cs | 10 + playback/Playback/MessageReader.cs | 147 ++++++++++++ playback/Playback/MessageWriter.cs | 75 ++++++ playback/Playback/PlaybackConstant.cs | 8 + 7 files changed, 690 insertions(+) create mode 100644 logic/Server/ArgumentOptions.cs create mode 100644 logic/Server/PlaybackServer.cs create mode 100644 logic/Server/Program.cs create mode 100644 logic/Server/ServerBase.cs create mode 100644 playback/Playback/MessageReader.cs create mode 100644 playback/Playback/MessageWriter.cs create mode 100644 playback/Playback/PlaybackConstant.cs diff --git a/logic/Server/ArgumentOptions.cs b/logic/Server/ArgumentOptions.cs new file mode 100644 index 00000000..74836bc2 --- /dev/null +++ b/logic/Server/ArgumentOptions.cs @@ -0,0 +1,44 @@ +using CommandLine; + +namespace Server +{ + public class ArgumentOptions + { + [Option("ip", Required = false, HelpText = "Server listening ip")] + public string ServerIP { get; set; } = "0.0.0.0"; + [Option('p', "port", Required = true, HelpText = "Server listening port")] + public ushort ServerPort { get; set; } = 8888; + [Option("teamCount", Required = false, HelpText = "The number of teams, 2 by defualt")] + public ushort TeamCount { get; set; } = 2; + [Option('g', "gameTimeInSecond", Required = false, HelpText = "The time of the game in second, 10 minutes by default")] + public uint GameTimeInSecond { get; set; } = 10 * 60; + [Option('f', "fileName", Required = false, HelpText = "The file to store playback file or to read file.")] + public string FileName { get; set; } = "114514"; + [Option("notAllowSpectator", Required = false, HelpText = "Whether to allow a spectator to watch the game.")] + public bool NotAllowSpectator { get; set; } = false; + [Option('b', "playback", Required = false, HelpText = "Whether open the server in a playback mode.")] + public bool Playback { get; set; } = false; + [Option("playbackSpeed", Required = false, HelpText = "The speed of the playback, between 0.25 and 4.0")] + public double PlaybackSpeed { get; set; } = 1.0; + [Option("resultOnly", Required = false, HelpText = "In playback mode to get the result directly")] + public bool ResultOnly { get; set; } = false; + [Option('k', "token", Required = false, HelpText = "Web API Token")] + public string Token { get; set; } = "114514"; + [Option('u', "url", Required = false, HelpText = "Web Url")] + public string Url { get; set; } = "114514"; + [Option('m', "mapResource", Required = false, HelpText = "Map Resource Path")] + public string mapResource { get; set; } = "114514"; + [Option("requestOnly", Required = false, HelpText = "Only send web requests")] + public bool RequestOnly { get; set; } = false; + [Option("finalGame", Required = false, HelpText = "Whether it is the final game")] + public bool FinalGame { get; set; } = false; + [Option("cheatMode", Required = false, HelpText = "Whether to open the cheat code")] + public bool CheatMode { get; set; } = false; + [Option("resultFileName", Required = false, HelpText = "Result file name, saved as .json")] + public string ResultFileName { get; set; } = "114514"; + [Option("startLockFile", Required = false, HelpText = "Whether to create a file that identifies whether the game has started")] + public string StartLockFile { get; set; } = "114514"; + [Option("mode", Required = false, HelpText = "Whether to run final competition")] + public int Mode { get; set; } = 0; + } +} diff --git a/logic/Server/PlaybackServer.cs b/logic/Server/PlaybackServer.cs new file mode 100644 index 00000000..dc347ce7 --- /dev/null +++ b/logic/Server/PlaybackServer.cs @@ -0,0 +1,332 @@ +using Gaming; +using Grpc.Core; +using Playback; +using Protobuf; +using System.Collections.Concurrent; +using Timothy.FrameRateTask; + +namespace Server +{ + class PlaybackServer : ServerBase + { + protected readonly ArgumentOptions options; + private int[,] teamScore; + private ConcurrentDictionary semaDict = new(); + // private object semaDictLock = new(); + private MessageToClient? currentGameInfo = new(); + private MessageOfObj currentMapMsg = new(); + private uint spectatorMinPlayerID = 2023; + // private List spectatorList = new List(); + public int TeamCount => options.TeamCount; + private object spetatorJoinLock = new(); + protected object spectatorLock = new object(); + protected bool isSpectatorJoin = false; + protected bool IsSpectatorJoin + { + get + { + lock (spectatorLock) + return isSpectatorJoin; + } + + set + { + lock (spectatorLock) + isSpectatorJoin = value; + } + } + private bool IsGaming { get; set; } + private int[] finalScore; + public int[] FinalScore + { + get + { + return finalScore; + } + } + public override int[] GetScore() => FinalScore; + public PlaybackServer(ArgumentOptions options) + { + this.options = options; + IsGaming = true; + teamScore = new int[0, 0]; + finalScore = new int[0]; + } + + public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter responseStream, ServerCallContext context) + { + Console.WriteLine($"AddPlayer: {request.PlayerId}"); + if (request.PlayerId >= spectatorMinPlayerID && options.NotAllowSpectator == false) + { + // 观战模式 + lock (spetatorJoinLock) // 具体原因见另一个上锁的地方 + { + if (semaDict.TryAdd(request.PlayerId, (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)))) + { + Console.WriteLine("A new spectator comes to watch this game."); + IsSpectatorJoin = true; + } + else + { + Console.WriteLine($"Duplicated Spectator ID {request.PlayerId}"); + return; + } + } + do + { + semaDict[request.PlayerId].Item1.Wait(); + try + { + if (currentGameInfo != null) + { + await responseStream.WriteAsync(currentGameInfo); + //Console.WriteLine("Send!"); + } + } + catch (InvalidOperationException) + { + if (semaDict.TryRemove(request.PlayerId, out var semas)) + { + try + { + semas.Item1.Release(); + semas.Item2.Release(); + } + catch { } + Console.WriteLine($"The spectator {request.PlayerId} exited"); + return; + } + } + catch (Exception) + { + // Console.WriteLine(ex); + } + finally + { + try + { + semaDict[request.PlayerId].Item2.Release(); + } + catch { } + } + } while (IsGaming); + return; + } + } + + public void ReportGame(MessageToClient? msg) + { + currentGameInfo = msg; + if (currentGameInfo != null && currentGameInfo.GameState == GameState.GameStart) + { + currentMapMsg = currentGameInfo.ObjMessage[0]; + } + + if (currentGameInfo != null && IsSpectatorJoin) + { + currentGameInfo.ObjMessage.Add(currentMapMsg); + IsSpectatorJoin = false; + } + + foreach (var kvp in semaDict) + { + kvp.Value.Item1.Release(); + } + + foreach (var kvp in semaDict) + { + kvp.Value.Item2.Wait(); + } + } + + public override void WaitForEnd() + { + try + { + if (options.ResultOnly) + { + using (MessageReader mr = new MessageReader(options.FileName)) + { + Console.WriteLine("Parsing playback file..."); + teamScore = new int[mr.teamCount, mr.playerCount]; + finalScore = new int[mr.teamCount]; + int infoNo = 0; + object cursorLock = new object(); + var initialTop = Console.CursorTop; + var initialLeft = Console.CursorLeft; + while (true) + { + MessageToClient? msg = null; + for (int i = 0; i < mr.teamCount; ++i) + { + for (int j = 0; j < mr.playerCount; ++j) + { + msg = mr.ReadOne(); + if (msg == null) + { + Console.WriteLine("The game doesn't come to an end because of timing up!"); + IsGaming = false; + goto endParse; + } + + lock (cursorLock) + { + var curTop = Console.CursorTop; + var curLeft = Console.CursorLeft; + Console.SetCursorPosition(initialLeft, initialTop); + Console.WriteLine($"Parsing messages... Current message number: {infoNo}"); + Console.SetCursorPosition(curLeft, curTop); + } + + if (msg != null) + { + //teamScore[i] = msg.TeamScore; + } + } + } + + ++infoNo; + + if (msg == null) + { + Console.WriteLine("No game information in this file!"); + goto endParse; + } + if (msg.GameState == GameState.GameEnd) + { + Console.WriteLine("Game over normally!"); + finalScore[0] = msg.AllMessage.StudentScore; + finalScore[1] = msg.AllMessage.TrickerScore; + goto endParse; + } + } + + endParse: + + Console.WriteLine($"Successfully parsed {infoNo} informations!"); + } + } + else + { + long timeInterval = GameServer.SendMessageToClientIntervalInMilliseconds; + if (options.PlaybackSpeed != 1.0) + { + options.PlaybackSpeed = Math.Max(0.25, Math.Min(4.0, options.PlaybackSpeed)); + timeInterval = (int)Math.Round(timeInterval / options.PlaybackSpeed); + } + using (MessageReader mr = new MessageReader(options.FileName)) + { + teamScore = new int[mr.teamCount, mr.playerCount]; + finalScore = new int[mr.teamCount]; + int infoNo = 0; + object cursorLock = new object(); + var msgCurTop = Console.CursorTop; + var msgCurLeft = Console.CursorLeft; + var frt = new FrameRateTaskExecutor + ( + loopCondition: () => true, + loopToDo: () => + { + MessageToClient? msg = null; + + msg = mr.ReadOne(); + if (msg == null) + { + Console.WriteLine("The game doesn't come to an end because of timing up!"); + IsGaming = false; + ReportGame(msg); + return false; + } + ReportGame(msg); + lock (cursorLock) + { + var curTop = Console.CursorTop; + var curLeft = Console.CursorLeft; + Console.SetCursorPosition(msgCurLeft, msgCurTop); + Console.WriteLine($"Sending messages... Current message number: {infoNo}."); + Console.SetCursorPosition(curLeft, curTop); + } + if (msg != null) + { + foreach (var item in msg.ObjMessage) + { + if (item.StudentMessage != null) + teamScore[0, item.StudentMessage.PlayerId] = item.StudentMessage.Score; + if (item.TrickerMessage != null) + teamScore[1, item.TrickerMessage.PlayerId - options.MaxStudentCount] = item.TrickerMessage.Score; // 这里默认 Tricker 的 PlayerId 从 MaxStudentCount = 4 开始 + } + } + + ++infoNo; + if (msg == null) + { + Console.WriteLine("No game information in this file!"); + IsGaming = false; + ReportGame(msg); + return false; + } + if (msg.GameState == GameState.GameEnd) + { + Console.WriteLine("Game over normally!"); + IsGaming = false; + finalScore[0] = msg.AllMessage.StudentScore; + finalScore[1] = msg.AllMessage.TrickerScore; + ReportGame(msg); + return false; + } + return true; + }, + timeInterval: timeInterval, + finallyReturn: () => 0 + ) + { AllowTimeExceed = true, MaxTolerantTimeExceedCount = 5 }; + + Console.WriteLine("The server is well prepared! Please MAKE SURE that you have opened all the clients to watch the game!"); + Console.WriteLine("If ALL clients have opened, press any key to start."); + Console.ReadKey(); + + new Thread + ( + () => + { + var rateCurTop = Console.CursorTop; + var rateCurLeft = Console.CursorLeft; + lock (cursorLock) + { + rateCurTop = Console.CursorTop; + rateCurLeft = Console.CursorLeft; + Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); + } + while (!frt.Finished) + { + lock (cursorLock) + { + var curTop = Console.CursorTop; + var curLeft = Console.CursorLeft; + Console.SetCursorPosition(rateCurLeft, rateCurTop); + Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); + Console.SetCursorPosition(curLeft, curTop); + } + Thread.Sleep(1000); + } + } + ) + { IsBackground = true }.Start(); + + lock (cursorLock) + { + msgCurLeft = Console.CursorLeft; + msgCurTop = Console.CursorTop; + Console.WriteLine("Sending messages..."); + } + frt.Start(); + } + } + } + finally + { + teamScore ??= new int[0, 0]; + } + } + } +} diff --git a/logic/Server/Program.cs b/logic/Server/Program.cs new file mode 100644 index 00000000..70dcd292 --- /dev/null +++ b/logic/Server/Program.cs @@ -0,0 +1,74 @@ +using CommandLine; +using Grpc.Core; +using Protobuf; + +namespace Server +{ + public class Program + { + const string welcome = +@" + _____ _ _ _ _ _ ___ _____ + |_ _| | | | | | | / \ |_ _|___ | + | | | |_| | | | |/ _ \ | | / / + | | | _ | |_| / ___ \ | | / / + |_| |_| |_|\___/_/ \_\___|/_/ +"; + static ServerBase CreateServer(ArgumentOptions options) + { + //return options.Playback ? new PlaybackServer(options) : new GameServer(options); + return new PlaybackServer(options); + } + + static int Main(string[] args) + { + foreach (var arg in args) + { + Console.Write($"{arg} "); + } + Console.WriteLine(); + + ArgumentOptions? options = null; + _ = Parser.Default.ParseArguments(args).WithParsed(o => { options = o; }); + if (options == null) + { + Console.WriteLine("Argument parsing failed!"); + return 1; + } + + if (options.StartLockFile == "114514") + { + Console.WriteLine(welcome); + } + Console.WriteLine("Server begins to run: " + options.ServerPort.ToString()); + + try + { + var server = CreateServer(options); + Grpc.Core.Server rpcServer = new Grpc.Core.Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) + { + Services = { AvailableService.BindService(server) }, + Ports = { new ServerPort(options.ServerIP, options.ServerPort, ServerCredentials.Insecure) } + }; + rpcServer.Start(); + + Console.WriteLine("Server begins to listen!"); + server.WaitForEnd(); + Console.WriteLine("Server end!"); + rpcServer.ShutdownAsync().Wait(); + + Thread.Sleep(50); + Console.WriteLine(""); + Console.WriteLine("=================== Final Score ===================="); + Console.WriteLine($"Studnet: {server.GetScore()[0]}"); + Console.WriteLine($"Tricker: {server.GetScore()[1]}"); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + Console.WriteLine(ex.StackTrace); + } + return 0; + } + } +} diff --git a/logic/Server/ServerBase.cs b/logic/Server/ServerBase.cs new file mode 100644 index 00000000..f23a2c1c --- /dev/null +++ b/logic/Server/ServerBase.cs @@ -0,0 +1,10 @@ +using Protobuf; + +namespace Server +{ + abstract class ServerBase : AvailableService.AvailableServiceBase + { + public abstract void WaitForEnd(); + public abstract int[] GetScore(); + } +} diff --git a/playback/Playback/MessageReader.cs b/playback/Playback/MessageReader.cs new file mode 100644 index 00000000..71f6616d --- /dev/null +++ b/playback/Playback/MessageReader.cs @@ -0,0 +1,147 @@ +using Google.Protobuf; +using Protobuf; +using System; +using System.IO; +using System.IO.Compression; + +namespace Playback +{ + public class FileFormatNotLegalException : Exception + { + private readonly string fileName; + public FileFormatNotLegalException(string fileName) + { + this.fileName = fileName; + } + public override string Message => $"The file: " + this.fileName + " is not a legal playback file for THUAI6."; + } + + public class MessageReader : IDisposable + { + private FileStream? fs; + private CodedInputStream cos; + private GZipStream gzs; + private byte[] buffer; + public bool Finished { get; private set; } = false; + + public readonly uint teamCount; + public readonly uint playerCount; + + const int bufferMaxSize = 10 * 1024 * 1024; // 10M + + public MessageReader(string fileName) + { + if (!fileName.EndsWith(PlayBackConstant.ExtendedName)) + { + fileName += PlayBackConstant.ExtendedName; + } + + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + try + { + var prefixLen = PlayBackConstant.Prefix.Length; + byte[] bt = new byte[prefixLen + sizeof(UInt32) * 2]; + fs.Read(bt, 0, bt.Length); + for (int i = 0; i < prefixLen; ++i) + { + if (bt[i] != PlayBackConstant.Prefix[i]) throw new FileFormatNotLegalException(fileName); + } + + teamCount = BitConverter.ToUInt32(bt, prefixLen); + playerCount = BitConverter.ToUInt32(bt, prefixLen + sizeof(UInt32)); + } + catch + { + throw new FileFormatNotLegalException(fileName); + } + + gzs = new GZipStream(fs, CompressionMode.Decompress); + var tmpBuffer = new byte[bufferMaxSize]; + var bufferSize = gzs.Read(tmpBuffer); + if (bufferSize == 0) + { + buffer = tmpBuffer; + Finished = true; + } + else if (bufferSize != bufferMaxSize) // 不留空位,防止 CodedInputStream 获取信息错误 + { + if (bufferSize == 0) + { + Finished = true; + } + buffer = new byte[bufferSize]; + Array.Copy(tmpBuffer, buffer, bufferSize); + } + else + { + buffer = tmpBuffer; + } + cos = new CodedInputStream(buffer); + } + + public MessageToClient? ReadOne() + { + beginRead: + if (Finished) + return null; + var pos = cos.Position; + try + { + MessageToClient? msg = new MessageToClient(); + cos.ReadMessage(msg); + return msg; + } + catch (InvalidProtocolBufferException) + { + var leftByte = buffer.Length - pos; // 上次读取剩余的字节 + if (buffer.Length < bufferMaxSize / 2) + { + var newBuffer = new byte[bufferMaxSize]; + for (int i = 0; i < leftByte; i++) + { + newBuffer[i] = buffer[pos + i]; + } + buffer = newBuffer; + } + else + { + for (int i = 0; i < leftByte; ++i) + { + buffer[i] = buffer[pos + i]; + } + } + var bufferSize = gzs.Read(buffer, (int)leftByte, (int)(buffer.Length - leftByte)) + leftByte; + if (bufferSize == leftByte) + { + Finished = true; + return null; + } + if (bufferSize != buffer.Length) // 不留空位,防止 CodedInputStream 获取信息错误 + { + var tmpBuffer = new byte[bufferSize]; + Array.Copy(buffer, tmpBuffer, bufferSize); + buffer = tmpBuffer; + } + cos = new CodedInputStream(buffer); + goto beginRead; + } + } + + public void Dispose() + { + Finished = true; + if (fs == null) + return; + if (fs.CanRead) + { + fs.Close(); + } + } + + ~MessageReader() + { + Dispose(); + } + } +} diff --git a/playback/Playback/MessageWriter.cs b/playback/Playback/MessageWriter.cs new file mode 100644 index 00000000..2d814224 --- /dev/null +++ b/playback/Playback/MessageWriter.cs @@ -0,0 +1,75 @@ +using Google.Protobuf; +using Protobuf; +using System; +using System.IO; +using System.IO.Compression; + +namespace Playback +{ + public class MessageWriter : IDisposable + { + private FileStream fs; + private CodedOutputStream cos; + private MemoryStream ms; + private GZipStream gzs; + private const int memoryCapacity = 10 * 1024 * 1024; // 10M + + private static void ClearMemoryStream(MemoryStream msToClear) + { + msToClear.Position = 0; + msToClear.SetLength(0); + } + + public MessageWriter(string fileName, uint teamCount, uint playerCount) + { + if (!fileName.EndsWith(PlayBackConstant.ExtendedName)) + { + fileName += PlayBackConstant.ExtendedName; + } + + fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); + fs.Write(PlayBackConstant.Prefix); // 写入前缀 + + fs.Write(BitConverter.GetBytes((UInt32)teamCount)); // 写入队伍数 + fs.Write(BitConverter.GetBytes((UInt32)playerCount)); // 写入每队的玩家人数 + ms = new MemoryStream(memoryCapacity); + cos = new CodedOutputStream(ms); + gzs = new GZipStream(fs, CompressionMode.Compress); + } + + public void WriteOne(MessageToClient msg) + { + cos.WriteMessage(msg); + if (ms.Length > memoryCapacity) + Flush(); + } + + public void Flush() + { + if (fs.CanWrite) + { + cos.Flush(); + gzs.Write(ms.GetBuffer(), 0, (int)ms.Length); + gzs.Flush(); + ClearMemoryStream(ms); + fs.Flush(); + } + } + + public void Dispose() + { + if (fs.CanWrite) + { + Flush(); + cos.Dispose(); + gzs.Dispose(); + fs.Dispose(); + } + } + + ~MessageWriter() + { + Dispose(); + } + } +} diff --git a/playback/Playback/PlaybackConstant.cs b/playback/Playback/PlaybackConstant.cs new file mode 100644 index 00000000..8c34a431 --- /dev/null +++ b/playback/Playback/PlaybackConstant.cs @@ -0,0 +1,8 @@ +namespace Playback +{ + public static class PlayBackConstant + { + public static string ExtendedName = ".thuaipb"; + public static byte[] Prefix = { (byte)'P', (byte)'B', 6, 0 }; // 文件前缀,用于标识文件类型,版本号为6 + } +} From 65532b5dec98e7f9d12ac9f1aa66268ab8c40ba9 Mon Sep 17 00:00:00 2001 From: 964293341 Date: Fri, 27 Oct 2023 23:13:33 +0800 Subject: [PATCH 11/14] feat: :sparkles: add Areas - add GameObj of Areas - add AreaFactory - adjust the switch-expr of other factories --- logic/GameClass/GameObj/Areas/AreaFactory.cs | 19 ++ logic/GameClass/GameObj/Areas/Asteroid.cs | 14 ++ logic/GameClass/GameObj/Areas/Construction.cs | 14 ++ logic/GameClass/GameObj/Areas/Home.cs | 22 ++ .../GameObj/Areas/OutOfBoundBlock.cs | 18 ++ logic/GameClass/GameObj/Areas/Resource.cs | 14 ++ logic/GameClass/GameObj/Areas/Ruin.cs | 14 ++ logic/GameClass/GameObj/Areas/Shadow.cs | 14 ++ logic/GameClass/GameObj/Areas/Wormhole.cs | 18 ++ .../GameObj/Bullets/BulletFactory.cs | 19 +- logic/GameClass/GameObj/Home.cs | 13 -- logic/GameClass/GameObj/Immovable.cs | 12 +- .../GameObj/Modules/ModuleFactory.cs | 205 ++++++++---------- .../GameObj/Occupations/OccupationFactory.cs | 15 +- logic/GameClass/GameObj/OutOfBoundBlock.cs | 19 -- logic/GameClass/GameObj/Team.cs | 5 +- 16 files changed, 265 insertions(+), 170 deletions(-) create mode 100644 logic/GameClass/GameObj/Areas/AreaFactory.cs create mode 100644 logic/GameClass/GameObj/Areas/Asteroid.cs create mode 100644 logic/GameClass/GameObj/Areas/Construction.cs create mode 100644 logic/GameClass/GameObj/Areas/Home.cs create mode 100644 logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs create mode 100644 logic/GameClass/GameObj/Areas/Resource.cs create mode 100644 logic/GameClass/GameObj/Areas/Ruin.cs create mode 100644 logic/GameClass/GameObj/Areas/Shadow.cs create mode 100644 logic/GameClass/GameObj/Areas/Wormhole.cs delete mode 100644 logic/GameClass/GameObj/Home.cs delete mode 100644 logic/GameClass/GameObj/OutOfBoundBlock.cs diff --git a/logic/GameClass/GameObj/Areas/AreaFactory.cs b/logic/GameClass/GameObj/Areas/AreaFactory.cs new file mode 100644 index 00000000..5f89d9f3 --- /dev/null +++ b/logic/GameClass/GameObj/Areas/AreaFactory.cs @@ -0,0 +1,19 @@ +using Preparation.Utility; + +namespace GameClass.GameObj.Areas; + +public static class AreaFactory +{ + public static Immovable GetArea(XY pos, PlaceType placeType) => placeType switch + { + PlaceType.Home => new Home(pos, GameObjType.Home), + PlaceType.Ruin => new Ruin(pos, GameObjType.Ruin), + PlaceType.Shadow => new Shadow(pos, GameObjType.Shadow), + PlaceType.Asteroid => new Asteroid(pos, GameObjType.Asteroid), + PlaceType.Resource => new Resource(pos, GameObjType.Resource), + PlaceType.Construction => new Construction(pos, GameObjType.Construction), + PlaceType.Wormhole => new Wormhole(pos, GameObjType.Wormhole), + _ => throw new System.NotImplementedException() + }; + public static OutOfBoundBlock GetOutOfBoundBlock(XY pos) => new(pos); +} diff --git a/logic/GameClass/GameObj/Areas/Asteroid.cs b/logic/GameClass/GameObj/Areas/Asteroid.cs new file mode 100644 index 00000000..e4a10832 --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Asteroid.cs @@ -0,0 +1,14 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Asteroid : Immovable +{ + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Asteroid(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} diff --git a/logic/GameClass/GameObj/Areas/Construction.cs b/logic/GameClass/GameObj/Areas/Construction.cs new file mode 100644 index 00000000..bc012e4a --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Construction.cs @@ -0,0 +1,14 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Construction : Immovable +{ + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Construction(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} diff --git a/logic/GameClass/GameObj/Areas/Home.cs b/logic/GameClass/GameObj/Areas/Home.cs new file mode 100644 index 00000000..0f75bbce --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Home.cs @@ -0,0 +1,22 @@ +using Preparation.Interface; +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Home : Immovable, IHome +{ + public AtomicLong TeamID => throw new NotImplementedException(); + public LongWithVariableRange HP => throw new NotImplementedException(); + public long Score => throw new NotImplementedException(); + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public void AddScore(long add) + { + throw new NotImplementedException(); + } + public Home(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} diff --git a/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs b/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs new file mode 100644 index 00000000..984adebd --- /dev/null +++ b/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs @@ -0,0 +1,18 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj.Areas; + +/// +/// 逻辑墙 +/// +public class OutOfBoundBlock : Immovable, IOutOfBound +{ + public OutOfBoundBlock(XY initPos) + : base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) + { + } + + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Square; +} diff --git a/logic/GameClass/GameObj/Areas/Resource.cs b/logic/GameClass/GameObj/Areas/Resource.cs new file mode 100644 index 00000000..4bbd2df6 --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Resource.cs @@ -0,0 +1,14 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Resource : Immovable +{ + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Resource(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} diff --git a/logic/GameClass/GameObj/Areas/Ruin.cs b/logic/GameClass/GameObj/Areas/Ruin.cs new file mode 100644 index 00000000..350124a9 --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Ruin.cs @@ -0,0 +1,14 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Ruin : Immovable +{ + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Ruin(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} diff --git a/logic/GameClass/GameObj/Areas/Shadow.cs b/logic/GameClass/GameObj/Areas/Shadow.cs new file mode 100644 index 00000000..afe4392e --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Shadow.cs @@ -0,0 +1,14 @@ +using Preparation.Utility; +using System; + +namespace GameClass.GameObj.Areas; + +public class Shadow : Immovable +{ + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Shadow(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Areas/Wormhole.cs b/logic/GameClass/GameObj/Areas/Wormhole.cs new file mode 100644 index 00000000..1993f423 --- /dev/null +++ b/logic/GameClass/GameObj/Areas/Wormhole.cs @@ -0,0 +1,18 @@ +using Preparation.Interface; +using Preparation.Utility; +using System; +using System.Collections.Generic; + +namespace GameClass.GameObj.Areas; + +public class Wormhole : Immovable, IWormhole +{ + public List Entrance => throw new NotImplementedException(); + public List Content => throw new NotImplementedException(); + public override bool IsRigid => throw new NotImplementedException(); + public override ShapeType Shape => ShapeType.Square; + public Wormhole(XY initPos, GameObjType initType) + : base(initPos, int.MaxValue, initType) + { + } +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Bullets/BulletFactory.cs b/logic/GameClass/GameObj/Bullets/BulletFactory.cs index ddd0be0d..6dae1c49 100644 --- a/logic/GameClass/GameObj/Bullets/BulletFactory.cs +++ b/logic/GameClass/GameObj/Bullets/BulletFactory.cs @@ -4,16 +4,13 @@ namespace GameClass.GameObj.Bullets; public static class BulletFactory { - public static Bullet? GetBullet(Ship ship, XY pos, BulletType bulletType) + public static Bullet? GetBullet(Ship ship, XY pos, BulletType bulletType) => bulletType switch { - return bulletType switch - { - BulletType.Laser => new Laser(ship, pos), - BulletType.Plasma => new Plasma(ship, pos), - BulletType.Shell => new Shell(ship, pos), - BulletType.Missile => new Missile(ship, pos), - BulletType.Arc => new Arc(ship, pos), - _ => null, - }; - } + BulletType.Laser => new Laser(ship, pos), + BulletType.Plasma => new Plasma(ship, pos), + BulletType.Shell => new Shell(ship, pos), + BulletType.Missile => new Missile(ship, pos), + BulletType.Arc => new Arc(ship, pos), + _ => throw new System.NotImplementedException() + }; } \ No newline at end of file diff --git a/logic/GameClass/GameObj/Home.cs b/logic/GameClass/GameObj/Home.cs deleted file mode 100644 index d0dc8812..00000000 --- a/logic/GameClass/GameObj/Home.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Preparation.Interface; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GameClass.GameObj -{ - public class Home : IHome - { - } -} diff --git a/logic/GameClass/GameObj/Immovable.cs b/logic/GameClass/GameObj/Immovable.cs index 00c769f8..07211f59 100644 --- a/logic/GameClass/GameObj/Immovable.cs +++ b/logic/GameClass/GameObj/Immovable.cs @@ -1,12 +1,12 @@ using Preparation.Utility; -namespace GameClass.GameObj +namespace GameClass.GameObj; + +public abstract class Immovable : GameObj { - public abstract class Immovable : GameObj + public override XY Position => position; + public Immovable(XY initPos, int initRadius, GameObjType initType) + : base(initPos, initRadius, initType) { - public override XY Position => position; - public Immovable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) - { - } } } diff --git a/logic/GameClass/GameObj/Modules/ModuleFactory.cs b/logic/GameClass/GameObj/Modules/ModuleFactory.cs index 382daebc..aef2dd48 100644 --- a/logic/GameClass/GameObj/Modules/ModuleFactory.cs +++ b/logic/GameClass/GameObj/Modules/ModuleFactory.cs @@ -5,124 +5,109 @@ namespace GameClass.GameObj.Modules; public static class ModuleFactory { - public static IProducer FindIProducer(ShipType shipType, ProducerType producerType) + public static IProducer FindIProducer(ShipType shipType, ProducerType producerType) => shipType switch { - return shipType switch + ShipType.CivilShip => producerType switch { - ShipType.CivilShip => producerType switch - { - ProducerType.Producer1 => new CivilProducer1(), - ProducerType.Producer2 => new CivilProducer2(), - ProducerType.Producer3 => new CivilProducer3(), - _ => new CivilProducer1(), - }, - ShipType.FlagShip => producerType switch - { - ProducerType.Producer1 => new FlagProducer1(), - _ => new FlagProducer1(), - }, - _ => new CivilProducer1(), - }; - } - public static IConstructor FindIConstructor(ShipType shipType, ConstructorType constructorType) + ProducerType.Producer1 => new CivilProducer1(), + ProducerType.Producer2 => new CivilProducer2(), + ProducerType.Producer3 => new CivilProducer3(), + _ => throw new System.NotImplementedException() + }, + ShipType.FlagShip => producerType switch + { + ProducerType.Producer1 => new FlagProducer1(), + _ => throw new System.NotImplementedException() + }, + _ => throw new System.NotImplementedException() + }; + public static IConstructor FindIConstructor(ShipType shipType, ConstructorType constructorType) => shipType switch { - return shipType switch + ShipType.CivilShip => constructorType switch + { + ConstructorType.Constructor1 => new CivilConstructor1(), + ConstructorType.Constructor2 => new CivilConstructor2(), + ConstructorType.Constructor3 => new CivilConstructor3(), + _ => throw new System.NotImplementedException() + }, + ShipType.FlagShip => constructorType switch { - ShipType.CivilShip => constructorType switch - { - ConstructorType.Constructor1 => new CivilConstructor1(), - ConstructorType.Constructor2 => new CivilConstructor2(), - ConstructorType.Constructor3 => new CivilConstructor3(), - _ => new CivilConstructor1(), - }, - ShipType.FlagShip => constructorType switch - { - ConstructorType.Constructor1 => new FlagConstructor1(), - _ => new FlagConstructor1(), - }, - _ => new CivilConstructor1(), - }; - } - public static IArmor FindIArmor(ShipType shipType, ArmorType armorType) + ConstructorType.Constructor1 => new FlagConstructor1(), + _ => throw new System.NotImplementedException() + }, + _ => throw new System.NotImplementedException() + }; + public static IArmor FindIArmor(ShipType shipType, ArmorType armorType) => shipType switch { - return shipType switch + ShipType.CivilShip => armorType switch + { + ArmorType.Armor1 => new CivilArmor1(), + _ => throw new System.NotImplementedException() + }, + ShipType.WarShip => armorType switch + { + ArmorType.Armor1 => new WarArmor1(), + ArmorType.Armor2 => new WarArmor2(), + ArmorType.Armor3 => new WarArmor3(), + _ => throw new System.NotImplementedException() + }, + ShipType.FlagShip => armorType switch { - ShipType.CivilShip => armorType switch - { - ArmorType.Armor1 => new CivilArmor1(), - _ => new CivilArmor1(), - }, - ShipType.WarShip => armorType switch - { - ArmorType.Armor1 => new WarArmor1(), - ArmorType.Armor2 => new WarArmor2(), - ArmorType.Armor3 => new WarArmor3(), - _ => new WarArmor1(), - }, - ShipType.FlagShip => armorType switch - { - ArmorType.Armor1 => new FlagArmor1(), - ArmorType.Armor2 => new FlagArmor2(), - ArmorType.Armor3 => new FlagArmor3(), - _ => new FlagArmor1(), - }, - _ => new CivilArmor1(), - }; - } - public static IShield FindIShield(ShipType shipType, ShieldType shieldType) + ArmorType.Armor1 => new FlagArmor1(), + ArmorType.Armor2 => new FlagArmor2(), + ArmorType.Armor3 => new FlagArmor3(), + _ => throw new System.NotImplementedException() + }, + _ => throw new System.NotImplementedException() + }; + public static IShield FindIShield(ShipType shipType, ShieldType shieldType) => shipType switch { - return shipType switch + ShipType.CivilShip => shieldType switch { - ShipType.CivilShip => shieldType switch - { - ShieldType.Shield1 => new CivilShield1(), - _ => new CivilShield1(), - }, - ShipType.WarShip => shieldType switch - { - ShieldType.Shield1 => new WarShield1(), - ShieldType.Shield2 => new WarShield2(), - ShieldType.Shield3 => new WarShield3(), - _ => new WarShield1(), - }, - ShipType.FlagShip => shieldType switch - { - ShieldType.Shield1 => new FlagShield1(), - ShieldType.Shield2 => new FlagShield2(), - ShieldType.Shield3 => new FlagShield3(), - _ => new FlagShield1(), - }, - _ => new CivilShield1(), - }; - } - public static IWeapon FindIWeapon(ShipType shipType, WeaponType weaponType) + ShieldType.Shield1 => new CivilShield1(), + _ => throw new System.NotImplementedException() + }, + ShipType.WarShip => shieldType switch + { + ShieldType.Shield1 => new WarShield1(), + ShieldType.Shield2 => new WarShield2(), + ShieldType.Shield3 => new WarShield3(), + _ => throw new System.NotImplementedException() + }, + ShipType.FlagShip => shieldType switch + { + ShieldType.Shield1 => new FlagShield1(), + ShieldType.Shield2 => new FlagShield2(), + ShieldType.Shield3 => new FlagShield3(), + _ => throw new System.NotImplementedException() + }, + _ => throw new System.NotImplementedException() + }; + public static IWeapon FindIWeapon(ShipType shipType, WeaponType weaponType) => shipType switch { - return shipType switch + ShipType.CivilShip => weaponType switch + { + WeaponType.LaserGun => new CivilLaserGun(), + _ => throw new System.NotImplementedException() + }, + ShipType.WarShip => weaponType switch + { + WeaponType.LaserGun => new WarLaserGun(), + WeaponType.PlasmaGun => new WarPlasmaGun(), + WeaponType.ShellGun => new WarShellGun(), + WeaponType.MissileGun => new WarMissileGun(), + WeaponType.ArcGun => new WarArcGun(), + _ => throw new System.NotImplementedException() + }, + ShipType.FlagShip => weaponType switch { - ShipType.CivilShip => weaponType switch - { - WeaponType.LaserGun => new CivilLaserGun(), - _ => new CivilLaserGun(), - }, - ShipType.WarShip => weaponType switch - { - WeaponType.LaserGun => new WarLaserGun(), - WeaponType.PlasmaGun => new WarPlasmaGun(), - WeaponType.ShellGun => new WarShellGun(), - WeaponType.MissileGun => new WarMissileGun(), - WeaponType.ArcGun => new WarArcGun(), - _ => new WarLaserGun(), - }, - ShipType.FlagShip => weaponType switch - { - WeaponType.LaserGun => new FlagLaserGun(), - WeaponType.PlasmaGun => new FlagPlasmaGun(), - WeaponType.ShellGun => new FlagShellGun(), - WeaponType.MissileGun => new FlagMissileGun(), - WeaponType.ArcGun => new FlagArcGun(), - _ => new FlagLaserGun(), - }, - _ => new CivilLaserGun(), - }; - } + WeaponType.LaserGun => new FlagLaserGun(), + WeaponType.PlasmaGun => new FlagPlasmaGun(), + WeaponType.ShellGun => new FlagShellGun(), + WeaponType.MissileGun => new FlagMissileGun(), + WeaponType.ArcGun => new FlagArcGun(), + _ => throw new System.NotImplementedException() + }, + _ => throw new System.NotImplementedException() + }; } \ No newline at end of file diff --git a/logic/GameClass/GameObj/Occupations/OccupationFactory.cs b/logic/GameClass/GameObj/Occupations/OccupationFactory.cs index 05436500..8ee514a1 100644 --- a/logic/GameClass/GameObj/Occupations/OccupationFactory.cs +++ b/logic/GameClass/GameObj/Occupations/OccupationFactory.cs @@ -5,14 +5,11 @@ namespace GameClass.GameObj.Occupations; public static class OccupationFactory { - public static IOccupation FindIOccupation(ShipType shipType) + public static IOccupation FindIOccupation(ShipType shipType) => shipType switch { - return shipType switch - { - ShipType.CivilShip => new CivilShip(), - ShipType.WarShip => new WarShip(), - ShipType.FlagShip => new FlagShip(), - _ => new CivilShip(), - }; - } + ShipType.CivilShip => new CivilShip(), + ShipType.WarShip => new WarShip(), + ShipType.FlagShip => new FlagShip(), + _ => throw new System.NotImplementedException(), + }; } \ No newline at end of file diff --git a/logic/GameClass/GameObj/OutOfBoundBlock.cs b/logic/GameClass/GameObj/OutOfBoundBlock.cs deleted file mode 100644 index 8f0c45d5..00000000 --- a/logic/GameClass/GameObj/OutOfBoundBlock.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Preparation.Interface; -using Preparation.Utility; - -namespace GameClass.GameObj -{ - /// - /// 逻辑墙 - /// - public class OutOfBoundBlock : Immovable, IOutOfBound - { - public OutOfBoundBlock(XY initPos) : - base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) - { - } - - public override bool IsRigid => true; - public override ShapeType Shape => ShapeType.Square; - } -} diff --git a/logic/GameClass/GameObj/Team.cs b/logic/GameClass/GameObj/Team.cs index ab858227..6d97b0f8 100644 --- a/logic/GameClass/GameObj/Team.cs +++ b/logic/GameClass/GameObj/Team.cs @@ -1,4 +1,5 @@ -using Preparation.Utility; +using GameClass.GameObj.Areas; +using Preparation.Utility; using System.Collections.Generic; namespace GameClass.GameObj @@ -12,7 +13,7 @@ public class Team public const long invalidTeamID = long.MaxValue; public const long noneTeamID = long.MinValue; private readonly List shipList; - private Home home = new Home(); + private Home home = new(); public int Score { get; private set; } = 0; public Ship? GetShip(long shipID) { From 79dbb180e3d5785bc0d638216854d53e22a69442 Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 28 Oct 2023 16:56:18 +0800 Subject: [PATCH 12/14] feat: :sparkles: add Map related --- logic/GameClass/GameObj/Areas/AreaFactory.cs | 14 +++--- logic/GameClass/GameObj/Areas/Asteroid.cs | 6 +-- logic/GameClass/GameObj/Areas/Construction.cs | 9 ++-- logic/GameClass/GameObj/Areas/Home.cs | 6 +-- .../GameObj/Areas/OutOfBoundBlock.cs | 7 ++- logic/GameClass/GameObj/Areas/Resource.cs | 6 +-- logic/GameClass/GameObj/Areas/Ruin.cs | 6 +-- logic/GameClass/GameObj/Areas/Shadow.cs | 6 +-- logic/GameClass/GameObj/Areas/Wormhole.cs | 15 ++++-- logic/GameClass/GameObj/Map.cs | 49 ++++++++++++++++++- logic/GameClass/GameObj/Team.cs | 6 ++- logic/Gaming/Game.cs | 9 ++++ logic/Preparation/Utility/EnumType.cs | 13 +++++ logic/Server/Program.cs | 4 +- 14 files changed, 119 insertions(+), 37 deletions(-) diff --git a/logic/GameClass/GameObj/Areas/AreaFactory.cs b/logic/GameClass/GameObj/Areas/AreaFactory.cs index 5f89d9f3..c2259b65 100644 --- a/logic/GameClass/GameObj/Areas/AreaFactory.cs +++ b/logic/GameClass/GameObj/Areas/AreaFactory.cs @@ -6,13 +6,13 @@ public static class AreaFactory { public static Immovable GetArea(XY pos, PlaceType placeType) => placeType switch { - PlaceType.Home => new Home(pos, GameObjType.Home), - PlaceType.Ruin => new Ruin(pos, GameObjType.Ruin), - PlaceType.Shadow => new Shadow(pos, GameObjType.Shadow), - PlaceType.Asteroid => new Asteroid(pos, GameObjType.Asteroid), - PlaceType.Resource => new Resource(pos, GameObjType.Resource), - PlaceType.Construction => new Construction(pos, GameObjType.Construction), - PlaceType.Wormhole => new Wormhole(pos, GameObjType.Wormhole), + PlaceType.Home => new Home(pos), + PlaceType.Ruin => new Ruin(pos), + PlaceType.Shadow => new Shadow(pos), + PlaceType.Asteroid => new Asteroid(pos), + PlaceType.Resource => new Resource(pos), + PlaceType.Construction => new Construction(pos), + PlaceType.Wormhole => new Wormhole(pos), _ => throw new System.NotImplementedException() }; public static OutOfBoundBlock GetOutOfBoundBlock(XY pos) => new(pos); diff --git a/logic/GameClass/GameObj/Areas/Asteroid.cs b/logic/GameClass/GameObj/Areas/Asteroid.cs index e4a10832..4f0b12b6 100644 --- a/logic/GameClass/GameObj/Areas/Asteroid.cs +++ b/logic/GameClass/GameObj/Areas/Asteroid.cs @@ -5,10 +5,10 @@ namespace GameClass.GameObj.Areas; public class Asteroid : Immovable { - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - public Asteroid(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public Asteroid(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Asteroid) { } } diff --git a/logic/GameClass/GameObj/Areas/Construction.cs b/logic/GameClass/GameObj/Areas/Construction.cs index bc012e4a..c06ffa06 100644 --- a/logic/GameClass/GameObj/Areas/Construction.cs +++ b/logic/GameClass/GameObj/Areas/Construction.cs @@ -5,10 +5,13 @@ namespace GameClass.GameObj.Areas; public class Construction : Immovable { - public override bool IsRigid => throw new NotImplementedException(); + public LongWithVariableRange HP => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - public Construction(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + private ConstructionType constructionType = ConstructionType.Null; + public ConstructionType ConstructionType => constructionType; + public Construction(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Construction) { } } diff --git a/logic/GameClass/GameObj/Areas/Home.cs b/logic/GameClass/GameObj/Areas/Home.cs index 0f75bbce..3be1a89d 100644 --- a/logic/GameClass/GameObj/Areas/Home.cs +++ b/logic/GameClass/GameObj/Areas/Home.cs @@ -9,14 +9,14 @@ public class Home : Immovable, IHome public AtomicLong TeamID => throw new NotImplementedException(); public LongWithVariableRange HP => throw new NotImplementedException(); public long Score => throw new NotImplementedException(); - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; public void AddScore(long add) { throw new NotImplementedException(); } - public Home(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public Home(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Home) { } } diff --git a/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs b/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs index 984adebd..6d6d13a4 100644 --- a/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs +++ b/logic/GameClass/GameObj/Areas/OutOfBoundBlock.cs @@ -8,11 +8,10 @@ namespace GameClass.GameObj.Areas; /// public class OutOfBoundBlock : Immovable, IOutOfBound { + public override bool IsRigid => true; + public override ShapeType Shape => ShapeType.Square; public OutOfBoundBlock(XY initPos) - : base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) + : base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) { } - - public override bool IsRigid => true; - public override ShapeType Shape => ShapeType.Square; } diff --git a/logic/GameClass/GameObj/Areas/Resource.cs b/logic/GameClass/GameObj/Areas/Resource.cs index 4bbd2df6..5879665d 100644 --- a/logic/GameClass/GameObj/Areas/Resource.cs +++ b/logic/GameClass/GameObj/Areas/Resource.cs @@ -5,10 +5,10 @@ namespace GameClass.GameObj.Areas; public class Resource : Immovable { - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - public Resource(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public Resource(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Resource) { } } diff --git a/logic/GameClass/GameObj/Areas/Ruin.cs b/logic/GameClass/GameObj/Areas/Ruin.cs index 350124a9..b3da5f49 100644 --- a/logic/GameClass/GameObj/Areas/Ruin.cs +++ b/logic/GameClass/GameObj/Areas/Ruin.cs @@ -5,10 +5,10 @@ namespace GameClass.GameObj.Areas; public class Ruin : Immovable { - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - public Ruin(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public Ruin(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Ruin) { } } diff --git a/logic/GameClass/GameObj/Areas/Shadow.cs b/logic/GameClass/GameObj/Areas/Shadow.cs index afe4392e..3705a760 100644 --- a/logic/GameClass/GameObj/Areas/Shadow.cs +++ b/logic/GameClass/GameObj/Areas/Shadow.cs @@ -5,10 +5,10 @@ namespace GameClass.GameObj.Areas; public class Shadow : Immovable { - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => false; public override ShapeType Shape => ShapeType.Square; - public Shadow(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public Shadow(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Shadow) { } } \ No newline at end of file diff --git a/logic/GameClass/GameObj/Areas/Wormhole.cs b/logic/GameClass/GameObj/Areas/Wormhole.cs index 1993f423..a10d291c 100644 --- a/logic/GameClass/GameObj/Areas/Wormhole.cs +++ b/logic/GameClass/GameObj/Areas/Wormhole.cs @@ -7,12 +7,21 @@ namespace GameClass.GameObj.Areas; public class Wormhole : Immovable, IWormhole { + public LongWithVariableRange HP => throw new NotImplementedException(); public List Entrance => throw new NotImplementedException(); public List Content => throw new NotImplementedException(); - public override bool IsRigid => throw new NotImplementedException(); + public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - public Wormhole(XY initPos, GameObjType initType) - : base(initPos, int.MaxValue, initType) + public override bool IgnoreCollideExecutor(IGameObj targetObj) + { + if (HP > GameData.WormholeHP / 2) + { + return true; + } + return false; + } + public Wormhole(XY initPos) + : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Wormhole) { } } \ No newline at end of file diff --git a/logic/GameClass/GameObj/Map.cs b/logic/GameClass/GameObj/Map.cs index 8a4d00f2..7d630291 100644 --- a/logic/GameClass/GameObj/Map.cs +++ b/logic/GameClass/GameObj/Map.cs @@ -42,7 +42,7 @@ public bool IsOutOfBound(IGameObj obj) } public IOutOfBound GetOutOfBound(XY pos) { - return new OutOfBoundBlock(pos); + return new Areas.OutOfBoundBlock(pos); } public Ship? FindShipInID(long ID) { @@ -251,5 +251,50 @@ public void Add(GameObj gameObj) GameObjLockDict[gameObj.Type].ExitWriteLock(); } } + public Map(uint[,] mapResource) + { + gameObjDict = new Dictionary>(); + gameObjLockDict = new Dictionary(); + foreach (GameObjType idx in Enum.GetValues(typeof(GameObjType))) + { + if (idx != GameObjType.Null) + { + gameObjDict.Add(idx, new List()); + gameObjLockDict.Add(idx, new ReaderWriterLockSlim()); + } + } + protoGameMap = new uint[mapResource.GetLength(0), mapResource.GetLength(1)]; + Array.Copy(mapResource, protoGameMap, mapResource.Length); + for (int i = 0; i < GameData.MapRows; ++i) + { + for (int j = 0; j < GameData.MapCols; ++j) + { + switch (mapResource[i, j]) + { + case (uint)PlaceType.Asteroid: + Add(new Areas.Asteroid(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Construction: + Add(new Areas.Construction(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Home: + Add(new Areas.Home(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Resource: + Add(new Areas.Resource(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Ruin: + Add(new Areas.Ruin(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Shadow: + Add(new Areas.Shadow(GameData.GetCellCenterPos(i, j))); + break; + case (uint)PlaceType.Wormhole: + Add(new Areas.Wormhole(GameData.GetCellCenterPos(i, j))); + break; + } + } + } + } } -} +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Team.cs b/logic/GameClass/GameObj/Team.cs index 6d97b0f8..1d6eb645 100644 --- a/logic/GameClass/GameObj/Team.cs +++ b/logic/GameClass/GameObj/Team.cs @@ -13,7 +13,7 @@ public class Team public const long invalidTeamID = long.MaxValue; public const long noneTeamID = long.MinValue; private readonly List shipList; - private Home home = new(); + private Home home; public int Score { get; private set; } = 0; public Ship? GetShip(long shipID) { @@ -46,6 +46,10 @@ public bool AddShip(Ship ship) shipList.Add(ship); return true; } + public void SetHome(Home home) + { + this.home = home; + } public int GetShipNum() { return shipList.Count; diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index f6b6dd11..3961dde5 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -23,5 +23,14 @@ public ShipInitInfo(long teamID, long playerID) public List TeamList => teamList; private readonly Map gameMap; public Map GameMap => gameMap; + public Game(uint[,] mapResource, int numOfTeam) + { + gameMap = new Map(mapResource); + teamList = new List(); + for (int i = 0; i < numOfTeam; i++) + { + teamList.Add(new Team()); + } + } } } diff --git a/logic/Preparation/Utility/EnumType.cs b/logic/Preparation/Utility/EnumType.cs index 83bc113c..53317c03 100644 --- a/logic/Preparation/Utility/EnumType.cs +++ b/logic/Preparation/Utility/EnumType.cs @@ -23,6 +23,16 @@ public enum BulletType Arc = 5, } /// + /// 建筑类型 + /// + public enum ConstructionType + { + Null = 0, + Factory = 1, + Community = 2, + Fort = 3, + } + /// /// 建造器类型 /// public enum ConstructorType @@ -63,6 +73,9 @@ public enum GunType MissileGun = 4, ArcGun = 5, } + /// + /// PlaceType + /// public enum PlaceType { Null = 0, diff --git a/logic/Server/Program.cs b/logic/Server/Program.cs index 70dcd292..eecb8def 100644 --- a/logic/Server/Program.cs +++ b/logic/Server/Program.cs @@ -60,8 +60,8 @@ static int Main(string[] args) Thread.Sleep(50); Console.WriteLine(""); Console.WriteLine("=================== Final Score ===================="); - Console.WriteLine($"Studnet: {server.GetScore()[0]}"); - Console.WriteLine($"Tricker: {server.GetScore()[1]}"); + Console.WriteLine($"Team0: {server.GetScore()[0]}"); + Console.WriteLine($"Team1: {server.GetScore()[1]}"); } catch (Exception ex) { From a546c6e791a6e5447a5423db7091ab287346bc80 Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 28 Oct 2023 19:36:01 +0800 Subject: [PATCH 13/14] feat: :sparkles: add Game related and Move Ships can move now (probably) --- README.md | 12 ++-- docs/README.md | 4 +- docs/temp.url | 2 + logic/GameClass/GameObj/Areas/Construction.cs | 2 +- logic/GameClass/GameObj/Areas/Home.cs | 2 +- logic/GameClass/GameObj/Areas/Wormhole.cs | 10 +--- logic/GameClass/GameObj/{ => Map}/Map.cs | 23 ++++++- .../GameObj/{ => Map}/MapGameTimer.cs | 0 logic/GameClass/GameObj/Ship.cs | 9 +++ logic/GameClass/GameObj/Team.cs | 4 ++ logic/Gaming/ActionManager.cs | 60 +++++++++++++++++++ logic/Gaming/Game.cs | 57 +++++++++++++++++- logic/Gaming/ShipManager.cs | 16 ++--- 13 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 docs/temp.url rename logic/GameClass/GameObj/{ => Map}/Map.cs (94%) rename logic/GameClass/GameObj/{ => Map}/MapGameTimer.cs (100%) create mode 100644 logic/Gaming/ActionManager.cs diff --git a/README.md b/README.md index a68b421c..aaf9643a 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,14 @@ | 子目录 | 说明 | 主要开发组 | | :--------: | :--------------------------------------------------------: | :----------------------------: | | .github | CI,用于选手包同步到服务器上供选手下载 | 运维组 | -| CAPI | 选手接口,生成可执行文件 | 通信组 | -| docs | 比赛的说明文档 | 逻辑组、通信组、运维组、界面组 | +| CAPI | 选手接口,生成可执行文件 | 通信组 | +| docs | 比赛的说明文档 | 逻辑组、通信组、运维组、界面组 | | dependency | 项目依赖文件,如 proto、dll、lib、dockerfile、shell 脚本等 | 逻辑组、通信组、运维组、界面组 | | installer | 下载器,用于选手包的下载与更新,生成可执行文件 | 运维组 | | interface | Unity 界面 | 界面组 | | launcher | 游戏启动器,用于快速启动游戏,生成可执行文件 | 运维组 | -| logic | 游戏逻辑、Server,生成可执行文件 | 通信组、逻辑组 | -| playback | 游戏回放组件,生成类库 | 逻辑组 | +| logic | 游戏逻辑、Server,生成可执行文件 | 通信组、逻辑组 | +| playback | 游戏回放组件,生成类库 | 逻辑组 | | resource | 资源文件目录,用于存储主目录下 README 所用图片 | 端茶倒水 | ### 分支管理 @@ -137,11 +137,11 @@ THUAI7 开发组成员与其他贡献者应当遵循以下流程: 仓库的文档使用 Markdown 语法,具体语法可以参照 [Markdown 语法文档](https://docs.github.com/zh/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)。 -中文文档的书写须严格遵循:[中午技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。 +中文文档的书写须严格遵循:[中文技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。 西文文档的书写须遵循: -+ 在 [中午技术文档规范](https://github.com/ruanyf/document-style-guide) 和 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 中规定的数字和西文的规范 ++ 在 [中文技术文档规范](https://github.com/ruanyf/document-style-guide) 和 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 中规定的数字和西文的规范 + 半角空格的标点符号的使用: + 半角的逗号 `,`、分号 `;`、句号 `.`、叹号 `!`、问号 `?` 等标点,在不位于行尾时,后需加空格,除非其后紧跟全角标点符号。例如:`"Hello, world",是一句学习编程语言常用的句子` + 半角的括号 `()`、`[]`、`{}`、`<>` 等在左半括号前加空格、右半括号后加空格,除非其空格处紧跟全角标点符号或位于行首或行尾。例如:`Tsinghua University (THU) and Peking University (PKU)(我是全角括号)` diff --git a/docs/README.md b/docs/README.md index e5b3ef7b..fdc8101f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,13 +8,13 @@ ## 中文文档 -中文文档的书写须严格遵循:[中午技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。 +中文文档的书写须严格遵循:[中文技术文档规范](https://github.com/ruanyf/document-style-guide)、[中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/),以写出更美观的中文文档。例如:[中文文字与西文文字间空格](https://github.com/ruanyf/document-style-guide/blob/master/docs/text.md#%E5%AD%97%E9%97%B4%E8%B7%9D)、[全角标点符号的正确使用](https://github.com/ruanyf/document-style-guide/blob/master/docs/marks.md),等等。 ## 西文文档 西文文档的书写须遵循: -+ 在 [中午技术文档规范](https://github.com/ruanyf/document-style-guide) 和 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 中规定的数字和西文的规范 ++ 在 [中文技术文档规范](https://github.com/ruanyf/document-style-guide) 和 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 中规定的数字和西文的规范 + 半角空格的标点符号的使用: + 半角的逗号 `,`、分号 `;`、句号 `.`、叹号 `!`、问号 `?` 等标点,在不位于行尾时,后需加空格,除非其后紧跟全角标点符号。例如:`"Hello, world",是一句学习编程语言常用的句子` + 半角的括号 `()`、`[]`、`{}`、`<>` 等在左半括号前加空格、右半括号后加空格,除非其空格处紧跟全角标点符号或位于行首或行尾。例如:`Tsinghua University (THU) and Peking University (PKU)(我是全角括号)` diff --git a/docs/temp.url b/docs/temp.url new file mode 100644 index 00000000..ec3ef483 --- /dev/null +++ b/docs/temp.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://thuai7.panxuc.com/ \ No newline at end of file diff --git a/logic/GameClass/GameObj/Areas/Construction.cs b/logic/GameClass/GameObj/Areas/Construction.cs index c06ffa06..b1bd2653 100644 --- a/logic/GameClass/GameObj/Areas/Construction.cs +++ b/logic/GameClass/GameObj/Areas/Construction.cs @@ -6,7 +6,7 @@ namespace GameClass.GameObj.Areas; public class Construction : Immovable { public LongWithVariableRange HP => throw new NotImplementedException(); - public override bool IsRigid => true; + public override bool IsRigid => constructionType == ConstructionType.Community; public override ShapeType Shape => ShapeType.Square; private ConstructionType constructionType = ConstructionType.Null; public ConstructionType ConstructionType => constructionType; diff --git a/logic/GameClass/GameObj/Areas/Home.cs b/logic/GameClass/GameObj/Areas/Home.cs index 3be1a89d..d32cc1ef 100644 --- a/logic/GameClass/GameObj/Areas/Home.cs +++ b/logic/GameClass/GameObj/Areas/Home.cs @@ -9,7 +9,7 @@ public class Home : Immovable, IHome public AtomicLong TeamID => throw new NotImplementedException(); public LongWithVariableRange HP => throw new NotImplementedException(); public long Score => throw new NotImplementedException(); - public override bool IsRigid => true; + public override bool IsRigid => false; public override ShapeType Shape => ShapeType.Square; public void AddScore(long add) { diff --git a/logic/GameClass/GameObj/Areas/Wormhole.cs b/logic/GameClass/GameObj/Areas/Wormhole.cs index a10d291c..c68a813e 100644 --- a/logic/GameClass/GameObj/Areas/Wormhole.cs +++ b/logic/GameClass/GameObj/Areas/Wormhole.cs @@ -10,16 +10,8 @@ public class Wormhole : Immovable, IWormhole public LongWithVariableRange HP => throw new NotImplementedException(); public List Entrance => throw new NotImplementedException(); public List Content => throw new NotImplementedException(); - public override bool IsRigid => true; + public override bool IsRigid => HP > GameData.WormholeHP / 2; public override ShapeType Shape => ShapeType.Square; - public override bool IgnoreCollideExecutor(IGameObj targetObj) - { - if (HP > GameData.WormholeHP / 2) - { - return true; - } - return false; - } public Wormhole(XY initPos) : base(initPos, GameData.NumOfPosGridPerCell / 2, GameObjType.Wormhole) { diff --git a/logic/GameClass/GameObj/Map.cs b/logic/GameClass/GameObj/Map/Map.cs similarity index 94% rename from logic/GameClass/GameObj/Map.cs rename to logic/GameClass/GameObj/Map/Map.cs index 7d630291..2f4c4595 100644 --- a/logic/GameClass/GameObj/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -52,7 +52,28 @@ public IOutOfBound GetOutOfBound(XY pos) { foreach (Ship s in gameObjDict[GameObjType.Ship]) { - if (s.ShipID == ID) + if (s.ID == ID) + { + ship = s; + break; + } + } + } + finally + { + gameObjLockDict[GameObjType.Ship].ExitReadLock(); + } + return ship; + } + public Ship? FindShipInShipID(long shipID) + { + Ship? ship = null; + gameObjLockDict[GameObjType.Ship].EnterReadLock(); + try + { + foreach (Ship s in gameObjDict[GameObjType.Ship]) + { + if (s.ShipID == shipID) { ship = s; break; diff --git a/logic/GameClass/GameObj/MapGameTimer.cs b/logic/GameClass/GameObj/Map/MapGameTimer.cs similarity index 100% rename from logic/GameClass/GameObj/MapGameTimer.cs rename to logic/GameClass/GameObj/Map/MapGameTimer.cs diff --git a/logic/GameClass/GameObj/Ship.cs b/logic/GameClass/GameObj/Ship.cs index 36d7bf14..4d031c44 100644 --- a/logic/GameClass/GameObj/Ship.cs +++ b/logic/GameClass/GameObj/Ship.cs @@ -159,6 +159,15 @@ public bool TryToRemoveFromGame(ShipStateType shipStateType) } return true; } + public bool Commandable() + { + lock (ActionLock) + { + return (shipState != ShipStateType.Stunned + && shipState != ShipStateType.Swinging + && shipState != ShipStateType.Attacking); + } + } public Ship(XY initPos, int initRadius, ShipType shipType) : base(initPos, initRadius, GameObjType.Ship) { diff --git a/logic/GameClass/GameObj/Team.cs b/logic/GameClass/GameObj/Team.cs index 1d6eb645..d9a0e0fe 100644 --- a/logic/GameClass/GameObj/Team.cs +++ b/logic/GameClass/GameObj/Team.cs @@ -13,6 +13,8 @@ public class Team public const long invalidTeamID = long.MaxValue; public const long noneTeamID = long.MinValue; private readonly List shipList; + private readonly Dictionary birthPointList; + public Dictionary BirthPointList => birthPointList; private Home home; public int Score { get; private set; } = 0; public Ship? GetShip(long shipID) @@ -109,6 +111,8 @@ public static bool TeamExists(long teamID) { return teamID < currentMaxTeamID; } + public void UpdateBirthPoint() + { } public Team() { teamID = currentMaxTeamID++; diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs new file mode 100644 index 00000000..26e4943f --- /dev/null +++ b/logic/Gaming/ActionManager.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using GameClass.GameObj; +using GameEngine; +using Preparation.Utility; +using Protobuf; +using Timothy.FrameRateTask; + +namespace Gaming +{ + public partial class Game + { + private readonly ActionManager actionManager; + private class ActionManager + { + public readonly MoveEngine moveEngine; + public bool MoveShip(Ship shipToMove, int moveTimeInMilliseconds, double moveDirection) + { + if (moveTimeInMilliseconds < 5) + { + return false; + } + long stateNum = shipToMove.SetShipState(RunningStateType.Waiting, ShipStateType.Moving); + if (stateNum == -1) + { + return false; + } + new Thread + ( + () => + { + shipToMove.ThreadNum.WaitOne(); + if (!shipToMove.StartThread(stateNum, RunningStateType.RunningActively)) + { + shipToMove.ThreadNum.Release(); + return; + } + moveEngine.MoveObj(shipToMove, moveTimeInMilliseconds, moveDirection, shipToMove.StateNum); + Thread.Sleep(moveTimeInMilliseconds); + shipToMove.ResetShipState(stateNum); + } + ) + { IsBackground = true }.Start(); + return true; + } + public static bool Stop(Ship ship) + { + lock (ship.ActionLock) + { + if(ship.Commandable()) + { + ship.SetShipState(RunningStateType.Null); + return true; + } + } + return false; + } + } + } +} diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 3961dde5..85ad80c8 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -13,16 +13,71 @@ public struct ShipInitInfo { public long teamID; public long playerID; - public ShipInitInfo(long teamID, long playerID) + public uint birthPoint; + public ShipType shipType; + public ShipInitInfo(long teamID, long playerID, uint birthPoint, ShipType shipType) { this.teamID = teamID; this.playerID = playerID; + this.birthPoint = birthPoint; + this.shipType = shipType; } } private readonly List teamList; public List TeamList => teamList; private readonly Map gameMap; public Map GameMap => gameMap; + public long AddShip(ShipInitInfo shipInitInfo) + { + if (!Team.TeamExists(shipInitInfo.teamID)) + { + return GameObj.invalidID; + } + // 由于BirthPoint实质上是可变且每支队伍不同的,所以暂时把它放到Team里? + XY pos = teamList[(int)shipInitInfo.teamID].BirthPointList[shipInitInfo.birthPoint]; + Ship? newShip = shipManager.AddShip(pos, shipInitInfo.teamID, shipInitInfo.playerID, shipInitInfo.shipType); + if (newShip == null) + { + return GameObj.invalidID; + } + teamList[(int)shipInitInfo.teamID].AddShip(newShip); + return newShip.ShipID; + } + public bool StartGame(int milliSeconds) + { + if (gameMap.Timer.IsGaming) + return false; + // 开始游戏 + new Thread + ( + () => + { + if (!gameMap.Timer.StartGame(milliSeconds)) + return; + + EndGame(); // 游戏结束时要做的事 + } + ) + { IsBackground = true }.Start(); + return true; + } + public void EndGame() + { + } + public bool MoveShip(long shipID, int moveTimeInMilliseconds, double angle) + { + if (!gameMap.Timer.IsGaming) + return false; + Ship? ship = gameMap.FindShipInShipID(shipID); + if(ship!=null) + { + return actionManager.MoveShip(ship, moveTimeInMilliseconds, angle); + } + else + { + return false; + } + } public Game(uint[,] mapResource, int numOfTeam) { gameMap = new Map(mapResource); diff --git a/logic/Gaming/ShipManager.cs b/logic/Gaming/ShipManager.cs index 0bb9c581..77f98b13 100644 --- a/logic/Gaming/ShipManager.cs +++ b/logic/Gaming/ShipManager.cs @@ -11,18 +11,18 @@ public partial class Game private partial class ShipManager { readonly Map gameMap; + public Ship? AddShip(XY pos, long teamID, long shipID, ShipType shipType) + { + Ship newShip = new Ship(pos, GameData.ShipRadius, shipType); + gameMap.Add(newShip); + newShip.TeamID.SetReturnOri(teamID); + newShip.ShipID.SetReturnOri(shipID); + return newShip; + } public ShipManager(Map gameMap) { this.gameMap = gameMap; } } - public Ship? AddShip(XY pos, long teamID, long shipID, ShipType shipType) - { - Ship newShip = new Ship(pos, GameData.ShipRadius, shipType); - gameMap.Add(newShip); - newShip.TeamID.SetReturnOri(teamID); - newShip.ShipID.SetReturnOri(shipID); - return newShip; - } } } From 609baa672a760d3bc427cb0c1889ed3b418dc8f4 Mon Sep 17 00:00:00 2001 From: Panxuc Date: Sat, 28 Oct 2023 22:13:36 +0800 Subject: [PATCH 14/14] test: :white_check_mark: test for CI --- logic/Gaming/ActionManager.cs | 11 +- logic/Gaming/Game.cs | 2 +- logic/Gaming/HealthManager.cs | 13 - logic/Gaming/ModuleManager.cs | 20 +- logic/Server/PlaybackServer.cs | 626 ++++++++++++++++----------------- logic/Server/Program.cs | 134 +++---- 6 files changed, 410 insertions(+), 396 deletions(-) delete mode 100644 logic/Gaming/HealthManager.cs diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index 26e4943f..807a1d0d 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -47,7 +47,7 @@ public static bool Stop(Ship ship) { lock (ship.ActionLock) { - if(ship.Commandable()) + if (ship.Commandable()) { ship.SetShipState(RunningStateType.Null); return true; @@ -55,6 +55,15 @@ public static bool Stop(Ship ship) } return false; } + public bool Produce(Ship ship) + { + return false; + } + public bool Construct(Ship ship) + { + return false; + } + } } } diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 85ad80c8..52ed779e 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -69,7 +69,7 @@ public bool MoveShip(long shipID, int moveTimeInMilliseconds, double angle) if (!gameMap.Timer.IsGaming) return false; Ship? ship = gameMap.FindShipInShipID(shipID); - if(ship!=null) + if (ship != null) { return actionManager.MoveShip(ship, moveTimeInMilliseconds, angle); } diff --git a/logic/Gaming/HealthManager.cs b/logic/Gaming/HealthManager.cs deleted file mode 100644 index 4aa1d614..00000000 --- a/logic/Gaming/HealthManager.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Preparation.Utility; -using GameClass.GameObj; - -namespace Gaming -{ - public partial class Game - { - private readonly HealthManager healthManager; - private partial class HealthManager - { - } - } -} diff --git a/logic/Gaming/ModuleManager.cs b/logic/Gaming/ModuleManager.cs index 4e4e401a..6d0a52fc 100644 --- a/logic/Gaming/ModuleManager.cs +++ b/logic/Gaming/ModuleManager.cs @@ -9,7 +9,25 @@ public partial class Game private partial class ModuleManager { public bool PurchaseProducer(Ship ship, ProducerType producerType, int parameter) - { } + { + return false; + } + public bool PurchaseConstructor(Ship ship, ConstructorType constructorType, int parameter) + { + return false; + } + public bool PurchaseArmor(Ship ship, ArmorType armorType, int parameter) + { + return false; + } + public bool PurchaseShield(Ship ship, ShieldType shieldType, int parameter) + { + return false; + } + public bool PurchaseWeapon(Ship ship, WeaponType weaponType, int parameter) + { + return false; + } } } } diff --git a/logic/Server/PlaybackServer.cs b/logic/Server/PlaybackServer.cs index dc347ce7..58ba64df 100644 --- a/logic/Server/PlaybackServer.cs +++ b/logic/Server/PlaybackServer.cs @@ -1,332 +1,332 @@ -using Gaming; -using Grpc.Core; -using Playback; -using Protobuf; -using System.Collections.Concurrent; -using Timothy.FrameRateTask; +//using Gaming; +//using Grpc.Core; +//using Playback; +//using Protobuf; +//using System.Collections.Concurrent; +//using Timothy.FrameRateTask; -namespace Server -{ - class PlaybackServer : ServerBase - { - protected readonly ArgumentOptions options; - private int[,] teamScore; - private ConcurrentDictionary semaDict = new(); - // private object semaDictLock = new(); - private MessageToClient? currentGameInfo = new(); - private MessageOfObj currentMapMsg = new(); - private uint spectatorMinPlayerID = 2023; - // private List spectatorList = new List(); - public int TeamCount => options.TeamCount; - private object spetatorJoinLock = new(); - protected object spectatorLock = new object(); - protected bool isSpectatorJoin = false; - protected bool IsSpectatorJoin - { - get - { - lock (spectatorLock) - return isSpectatorJoin; - } +//namespace Server +//{ +// class PlaybackServer : ServerBase +// { +// protected readonly ArgumentOptions options; +// private int[,] teamScore; +// private ConcurrentDictionary semaDict = new(); +// // private object semaDictLock = new(); +// private MessageToClient? currentGameInfo = new(); +// private MessageOfObj currentMapMsg = new(); +// private uint spectatorMinPlayerID = 2023; +// // private List spectatorList = new List(); +// public int TeamCount => options.TeamCount; +// private object spetatorJoinLock = new(); +// protected object spectatorLock = new object(); +// protected bool isSpectatorJoin = false; +// protected bool IsSpectatorJoin +// { +// get +// { +// lock (spectatorLock) +// return isSpectatorJoin; +// } - set - { - lock (spectatorLock) - isSpectatorJoin = value; - } - } - private bool IsGaming { get; set; } - private int[] finalScore; - public int[] FinalScore - { - get - { - return finalScore; - } - } - public override int[] GetScore() => FinalScore; - public PlaybackServer(ArgumentOptions options) - { - this.options = options; - IsGaming = true; - teamScore = new int[0, 0]; - finalScore = new int[0]; - } +// set +// { +// lock (spectatorLock) +// isSpectatorJoin = value; +// } +// } +// private bool IsGaming { get; set; } +// private int[] finalScore; +// public int[] FinalScore +// { +// get +// { +// return finalScore; +// } +// } +// public override int[] GetScore() => FinalScore; +// public PlaybackServer(ArgumentOptions options) +// { +// this.options = options; +// IsGaming = true; +// teamScore = new int[0, 0]; +// finalScore = new int[0]; +// } - public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter responseStream, ServerCallContext context) - { - Console.WriteLine($"AddPlayer: {request.PlayerId}"); - if (request.PlayerId >= spectatorMinPlayerID && options.NotAllowSpectator == false) - { - // 观战模式 - lock (spetatorJoinLock) // 具体原因见另一个上锁的地方 - { - if (semaDict.TryAdd(request.PlayerId, (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)))) - { - Console.WriteLine("A new spectator comes to watch this game."); - IsSpectatorJoin = true; - } - else - { - Console.WriteLine($"Duplicated Spectator ID {request.PlayerId}"); - return; - } - } - do - { - semaDict[request.PlayerId].Item1.Wait(); - try - { - if (currentGameInfo != null) - { - await responseStream.WriteAsync(currentGameInfo); - //Console.WriteLine("Send!"); - } - } - catch (InvalidOperationException) - { - if (semaDict.TryRemove(request.PlayerId, out var semas)) - { - try - { - semas.Item1.Release(); - semas.Item2.Release(); - } - catch { } - Console.WriteLine($"The spectator {request.PlayerId} exited"); - return; - } - } - catch (Exception) - { - // Console.WriteLine(ex); - } - finally - { - try - { - semaDict[request.PlayerId].Item2.Release(); - } - catch { } - } - } while (IsGaming); - return; - } - } +// //public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter responseStream, ServerCallContext context) +// //{ +// // Console.WriteLine($"AddPlayer: {request.PlayerId}"); +// // if (request.PlayerId >= spectatorMinPlayerID && options.NotAllowSpectator == false) +// // { +// // // 观战模式 +// // lock (spetatorJoinLock) // 具体原因见另一个上锁的地方 +// // { +// // if (semaDict.TryAdd(request.PlayerId, (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)))) +// // { +// // Console.WriteLine("A new spectator comes to watch this game."); +// // IsSpectatorJoin = true; +// // } +// // else +// // { +// // Console.WriteLine($"Duplicated Spectator ID {request.PlayerId}"); +// // return; +// // } +// // } +// // do +// // { +// // semaDict[request.PlayerId].Item1.Wait(); +// // try +// // { +// // if (currentGameInfo != null) +// // { +// // await responseStream.WriteAsync(currentGameInfo); +// // //Console.WriteLine("Send!"); +// // } +// // } +// // catch (InvalidOperationException) +// // { +// // if (semaDict.TryRemove(request.PlayerId, out var semas)) +// // { +// // try +// // { +// // semas.Item1.Release(); +// // semas.Item2.Release(); +// // } +// // catch { } +// // Console.WriteLine($"The spectator {request.PlayerId} exited"); +// // return; +// // } +// // } +// // catch (Exception) +// // { +// // // Console.WriteLine(ex); +// // } +// // finally +// // { +// // try +// // { +// // semaDict[request.PlayerId].Item2.Release(); +// // } +// // catch { } +// // } +// // } while (IsGaming); +// // return; +// // } +// //} - public void ReportGame(MessageToClient? msg) - { - currentGameInfo = msg; - if (currentGameInfo != null && currentGameInfo.GameState == GameState.GameStart) - { - currentMapMsg = currentGameInfo.ObjMessage[0]; - } +// public void ReportGame(MessageToClient? msg) +// { +// currentGameInfo = msg; +// if (currentGameInfo != null && currentGameInfo.GameState == GameState.GameStart) +// { +// currentMapMsg = currentGameInfo.ObjMessage[0]; +// } - if (currentGameInfo != null && IsSpectatorJoin) - { - currentGameInfo.ObjMessage.Add(currentMapMsg); - IsSpectatorJoin = false; - } +// if (currentGameInfo != null && IsSpectatorJoin) +// { +// currentGameInfo.ObjMessage.Add(currentMapMsg); +// IsSpectatorJoin = false; +// } - foreach (var kvp in semaDict) - { - kvp.Value.Item1.Release(); - } +// foreach (var kvp in semaDict) +// { +// kvp.Value.Item1.Release(); +// } - foreach (var kvp in semaDict) - { - kvp.Value.Item2.Wait(); - } - } +// foreach (var kvp in semaDict) +// { +// kvp.Value.Item2.Wait(); +// } +// } - public override void WaitForEnd() - { - try - { - if (options.ResultOnly) - { - using (MessageReader mr = new MessageReader(options.FileName)) - { - Console.WriteLine("Parsing playback file..."); - teamScore = new int[mr.teamCount, mr.playerCount]; - finalScore = new int[mr.teamCount]; - int infoNo = 0; - object cursorLock = new object(); - var initialTop = Console.CursorTop; - var initialLeft = Console.CursorLeft; - while (true) - { - MessageToClient? msg = null; - for (int i = 0; i < mr.teamCount; ++i) - { - for (int j = 0; j < mr.playerCount; ++j) - { - msg = mr.ReadOne(); - if (msg == null) - { - Console.WriteLine("The game doesn't come to an end because of timing up!"); - IsGaming = false; - goto endParse; - } +// public override void WaitForEnd() +// { +// try +// { +// if (options.ResultOnly) +// { +// using (MessageReader mr = new MessageReader(options.FileName)) +// { +// Console.WriteLine("Parsing playback file..."); +// teamScore = new int[mr.teamCount, mr.playerCount]; +// finalScore = new int[mr.teamCount]; +// int infoNo = 0; +// object cursorLock = new object(); +// var initialTop = Console.CursorTop; +// var initialLeft = Console.CursorLeft; +// while (true) +// { +// MessageToClient? msg = null; +// for (int i = 0; i < mr.teamCount; ++i) +// { +// for (int j = 0; j < mr.playerCount; ++j) +// { +// msg = mr.ReadOne(); +// if (msg == null) +// { +// Console.WriteLine("The game doesn't come to an end because of timing up!"); +// IsGaming = false; +// goto endParse; +// } - lock (cursorLock) - { - var curTop = Console.CursorTop; - var curLeft = Console.CursorLeft; - Console.SetCursorPosition(initialLeft, initialTop); - Console.WriteLine($"Parsing messages... Current message number: {infoNo}"); - Console.SetCursorPosition(curLeft, curTop); - } +// lock (cursorLock) +// { +// var curTop = Console.CursorTop; +// var curLeft = Console.CursorLeft; +// Console.SetCursorPosition(initialLeft, initialTop); +// Console.WriteLine($"Parsing messages... Current message number: {infoNo}"); +// Console.SetCursorPosition(curLeft, curTop); +// } - if (msg != null) - { - //teamScore[i] = msg.TeamScore; - } - } - } +// if (msg != null) +// { +// //teamScore[i] = msg.TeamScore; +// } +// } +// } - ++infoNo; +// ++infoNo; - if (msg == null) - { - Console.WriteLine("No game information in this file!"); - goto endParse; - } - if (msg.GameState == GameState.GameEnd) - { - Console.WriteLine("Game over normally!"); - finalScore[0] = msg.AllMessage.StudentScore; - finalScore[1] = msg.AllMessage.TrickerScore; - goto endParse; - } - } +// if (msg == null) +// { +// Console.WriteLine("No game information in this file!"); +// goto endParse; +// } +// if (msg.GameState == GameState.GameEnd) +// { +// Console.WriteLine("Game over normally!"); +// finalScore[0] = msg.AllMessage.StudentScore; +// finalScore[1] = msg.AllMessage.TrickerScore; +// goto endParse; +// } +// } - endParse: +// endParse: - Console.WriteLine($"Successfully parsed {infoNo} informations!"); - } - } - else - { - long timeInterval = GameServer.SendMessageToClientIntervalInMilliseconds; - if (options.PlaybackSpeed != 1.0) - { - options.PlaybackSpeed = Math.Max(0.25, Math.Min(4.0, options.PlaybackSpeed)); - timeInterval = (int)Math.Round(timeInterval / options.PlaybackSpeed); - } - using (MessageReader mr = new MessageReader(options.FileName)) - { - teamScore = new int[mr.teamCount, mr.playerCount]; - finalScore = new int[mr.teamCount]; - int infoNo = 0; - object cursorLock = new object(); - var msgCurTop = Console.CursorTop; - var msgCurLeft = Console.CursorLeft; - var frt = new FrameRateTaskExecutor - ( - loopCondition: () => true, - loopToDo: () => - { - MessageToClient? msg = null; +// Console.WriteLine($"Successfully parsed {infoNo} informations!"); +// } +// } +// else +// { +// long timeInterval = GameServer.SendMessageToClientIntervalInMilliseconds; +// if (options.PlaybackSpeed != 1.0) +// { +// options.PlaybackSpeed = Math.Max(0.25, Math.Min(4.0, options.PlaybackSpeed)); +// timeInterval = (int)Math.Round(timeInterval / options.PlaybackSpeed); +// } +// using (MessageReader mr = new MessageReader(options.FileName)) +// { +// teamScore = new int[mr.teamCount, mr.playerCount]; +// finalScore = new int[mr.teamCount]; +// int infoNo = 0; +// object cursorLock = new object(); +// var msgCurTop = Console.CursorTop; +// var msgCurLeft = Console.CursorLeft; +// var frt = new FrameRateTaskExecutor +// ( +// loopCondition: () => true, +// loopToDo: () => +// { +// MessageToClient? msg = null; - msg = mr.ReadOne(); - if (msg == null) - { - Console.WriteLine("The game doesn't come to an end because of timing up!"); - IsGaming = false; - ReportGame(msg); - return false; - } - ReportGame(msg); - lock (cursorLock) - { - var curTop = Console.CursorTop; - var curLeft = Console.CursorLeft; - Console.SetCursorPosition(msgCurLeft, msgCurTop); - Console.WriteLine($"Sending messages... Current message number: {infoNo}."); - Console.SetCursorPosition(curLeft, curTop); - } - if (msg != null) - { - foreach (var item in msg.ObjMessage) - { - if (item.StudentMessage != null) - teamScore[0, item.StudentMessage.PlayerId] = item.StudentMessage.Score; - if (item.TrickerMessage != null) - teamScore[1, item.TrickerMessage.PlayerId - options.MaxStudentCount] = item.TrickerMessage.Score; // 这里默认 Tricker 的 PlayerId 从 MaxStudentCount = 4 开始 - } - } +// msg = mr.ReadOne(); +// if (msg == null) +// { +// Console.WriteLine("The game doesn't come to an end because of timing up!"); +// IsGaming = false; +// ReportGame(msg); +// return false; +// } +// ReportGame(msg); +// lock (cursorLock) +// { +// var curTop = Console.CursorTop; +// var curLeft = Console.CursorLeft; +// Console.SetCursorPosition(msgCurLeft, msgCurTop); +// Console.WriteLine($"Sending messages... Current message number: {infoNo}."); +// Console.SetCursorPosition(curLeft, curTop); +// } +// if (msg != null) +// { +// foreach (var item in msg.ObjMessage) +// { +// if (item.StudentMessage != null) +// teamScore[0, item.StudentMessage.PlayerId] = item.StudentMessage.Score; +// if (item.TrickerMessage != null) +// teamScore[1, item.TrickerMessage.PlayerId - options.MaxStudentCount] = item.TrickerMessage.Score; // 这里默认 Tricker 的 PlayerId 从 MaxStudentCount = 4 开始 +// } +// } - ++infoNo; - if (msg == null) - { - Console.WriteLine("No game information in this file!"); - IsGaming = false; - ReportGame(msg); - return false; - } - if (msg.GameState == GameState.GameEnd) - { - Console.WriteLine("Game over normally!"); - IsGaming = false; - finalScore[0] = msg.AllMessage.StudentScore; - finalScore[1] = msg.AllMessage.TrickerScore; - ReportGame(msg); - return false; - } - return true; - }, - timeInterval: timeInterval, - finallyReturn: () => 0 - ) - { AllowTimeExceed = true, MaxTolerantTimeExceedCount = 5 }; +// ++infoNo; +// if (msg == null) +// { +// Console.WriteLine("No game information in this file!"); +// IsGaming = false; +// ReportGame(msg); +// return false; +// } +// if (msg.GameState == GameState.GameEnd) +// { +// Console.WriteLine("Game over normally!"); +// IsGaming = false; +// finalScore[0] = msg.AllMessage.StudentScore; +// finalScore[1] = msg.AllMessage.TrickerScore; +// ReportGame(msg); +// return false; +// } +// return true; +// }, +// timeInterval: timeInterval, +// finallyReturn: () => 0 +// ) +// { AllowTimeExceed = true, MaxTolerantTimeExceedCount = 5 }; - Console.WriteLine("The server is well prepared! Please MAKE SURE that you have opened all the clients to watch the game!"); - Console.WriteLine("If ALL clients have opened, press any key to start."); - Console.ReadKey(); +// Console.WriteLine("The server is well prepared! Please MAKE SURE that you have opened all the clients to watch the game!"); +// Console.WriteLine("If ALL clients have opened, press any key to start."); +// Console.ReadKey(); - new Thread - ( - () => - { - var rateCurTop = Console.CursorTop; - var rateCurLeft = Console.CursorLeft; - lock (cursorLock) - { - rateCurTop = Console.CursorTop; - rateCurLeft = Console.CursorLeft; - Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); - } - while (!frt.Finished) - { - lock (cursorLock) - { - var curTop = Console.CursorTop; - var curLeft = Console.CursorLeft; - Console.SetCursorPosition(rateCurLeft, rateCurTop); - Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); - Console.SetCursorPosition(curLeft, curTop); - } - Thread.Sleep(1000); - } - } - ) - { IsBackground = true }.Start(); +// new Thread +// ( +// () => +// { +// var rateCurTop = Console.CursorTop; +// var rateCurLeft = Console.CursorLeft; +// lock (cursorLock) +// { +// rateCurTop = Console.CursorTop; +// rateCurLeft = Console.CursorLeft; +// Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); +// } +// while (!frt.Finished) +// { +// lock (cursorLock) +// { +// var curTop = Console.CursorTop; +// var curLeft = Console.CursorLeft; +// Console.SetCursorPosition(rateCurLeft, rateCurTop); +// Console.WriteLine($"Send message to clients frame rate: {frt.FrameRate}"); +// Console.SetCursorPosition(curLeft, curTop); +// } +// Thread.Sleep(1000); +// } +// } +// ) +// { IsBackground = true }.Start(); - lock (cursorLock) - { - msgCurLeft = Console.CursorLeft; - msgCurTop = Console.CursorTop; - Console.WriteLine("Sending messages..."); - } - frt.Start(); - } - } - } - finally - { - teamScore ??= new int[0, 0]; - } - } - } -} +// lock (cursorLock) +// { +// msgCurLeft = Console.CursorLeft; +// msgCurTop = Console.CursorTop; +// Console.WriteLine("Sending messages..."); +// } +// frt.Start(); +// } +// } +// } +// finally +// { +// teamScore ??= new int[0, 0]; +// } +// } +// } +//} diff --git a/logic/Server/Program.cs b/logic/Server/Program.cs index eecb8def..71bb2d7d 100644 --- a/logic/Server/Program.cs +++ b/logic/Server/Program.cs @@ -1,74 +1,74 @@ -using CommandLine; -using Grpc.Core; -using Protobuf; +//using CommandLine; +//using Grpc.Core; +//using Protobuf; -namespace Server -{ - public class Program - { - const string welcome = -@" - _____ _ _ _ _ _ ___ _____ - |_ _| | | | | | | / \ |_ _|___ | - | | | |_| | | | |/ _ \ | | / / - | | | _ | |_| / ___ \ | | / / - |_| |_| |_|\___/_/ \_\___|/_/ -"; - static ServerBase CreateServer(ArgumentOptions options) - { - //return options.Playback ? new PlaybackServer(options) : new GameServer(options); - return new PlaybackServer(options); - } +//namespace Server +//{ +// public class Program +// { +// const string welcome = +//@" +// _____ _ _ _ _ _ ___ _____ +// |_ _| | | | | | | / \ |_ _|___ | +// | | | |_| | | | |/ _ \ | | / / +// | | | _ | |_| / ___ \ | | / / +// |_| |_| |_|\___/_/ \_\___|/_/ +//"; +// static ServerBase CreateServer(ArgumentOptions options) +// { +// //return options.Playback ? new PlaybackServer(options) : new GameServer(options); +// return new PlaybackServer(options); +// } - static int Main(string[] args) - { - foreach (var arg in args) - { - Console.Write($"{arg} "); - } - Console.WriteLine(); +// static int Main(string[] args) +// { +// foreach (var arg in args) +// { +// Console.Write($"{arg} "); +// } +// Console.WriteLine(); - ArgumentOptions? options = null; - _ = Parser.Default.ParseArguments(args).WithParsed(o => { options = o; }); - if (options == null) - { - Console.WriteLine("Argument parsing failed!"); - return 1; - } +// ArgumentOptions? options = null; +// _ = Parser.Default.ParseArguments(args).WithParsed(o => { options = o; }); +// if (options == null) +// { +// Console.WriteLine("Argument parsing failed!"); +// return 1; +// } - if (options.StartLockFile == "114514") - { - Console.WriteLine(welcome); - } - Console.WriteLine("Server begins to run: " + options.ServerPort.ToString()); +// if (options.StartLockFile == "114514") +// { +// Console.WriteLine(welcome); +// } +// Console.WriteLine("Server begins to run: " + options.ServerPort.ToString()); - try - { - var server = CreateServer(options); - Grpc.Core.Server rpcServer = new Grpc.Core.Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) - { - Services = { AvailableService.BindService(server) }, - Ports = { new ServerPort(options.ServerIP, options.ServerPort, ServerCredentials.Insecure) } - }; - rpcServer.Start(); +// try +// { +// var server = CreateServer(options); +// Grpc.Core.Server rpcServer = new Grpc.Core.Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) +// { +// Services = { AvailableService.BindService(server) }, +// Ports = { new ServerPort(options.ServerIP, options.ServerPort, ServerCredentials.Insecure) } +// }; +// rpcServer.Start(); - Console.WriteLine("Server begins to listen!"); - server.WaitForEnd(); - Console.WriteLine("Server end!"); - rpcServer.ShutdownAsync().Wait(); +// Console.WriteLine("Server begins to listen!"); +// server.WaitForEnd(); +// Console.WriteLine("Server end!"); +// rpcServer.ShutdownAsync().Wait(); - Thread.Sleep(50); - Console.WriteLine(""); - Console.WriteLine("=================== Final Score ===================="); - Console.WriteLine($"Team0: {server.GetScore()[0]}"); - Console.WriteLine($"Team1: {server.GetScore()[1]}"); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - Console.WriteLine(ex.StackTrace); - } - return 0; - } - } -} +// Thread.Sleep(50); +// Console.WriteLine(""); +// Console.WriteLine("=================== Final Score ===================="); +// Console.WriteLine($"Team0: {server.GetScore()[0]}"); +// Console.WriteLine($"Team1: {server.GetScore()[1]}"); +// } +// catch (Exception ex) +// { +// Console.WriteLine(ex.ToString()); +// Console.WriteLine(ex.StackTrace); +// } +// return 0; +// } +// } +//}