| @@ -0,0 +1,77 @@ | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.GameData | |||||
| { | |||||
| public static class GameData | |||||
| { | |||||
| #region 基本常数与常方法 | |||||
| public const int numOfPosGridPerCell = 1000; // 每格的【坐标单位】数 | |||||
| public const int numOfStepPerSecond = 20; // 每秒行走的步数 | |||||
| public const int lengthOfMap = 50000; // 地图长度 | |||||
| public const int rows = 50; // 行数 | |||||
| public const int cols = 50; // 列数 | |||||
| public const long gameDuration = 600000; // 游戏时长600000ms = 10min | |||||
| public const long frameDuration = 50; // 每帧时长 | |||||
| public const int MinSpeed = 1; // 最小速度 | |||||
| public const int MaxSpeed = int.MaxValue; // 最大速度 | |||||
| public static XYPosition GetCellCenterPos(int x, int y) // 求格子的中心坐标 | |||||
| { | |||||
| XYPosition ret = new((x * numOfPosGridPerCell) + (numOfPosGridPerCell / 2), (y * numOfPosGridPerCell) + (numOfPosGridPerCell / 2)); | |||||
| return ret; | |||||
| } | |||||
| public static int PosGridToCellX(XYPosition pos) // 求坐标所在的格子的x坐标 | |||||
| { | |||||
| return pos.x / numOfPosGridPerCell; | |||||
| } | |||||
| public static int PosGridToCellY(XYPosition pos) // 求坐标所在的格子的y坐标 | |||||
| { | |||||
| return pos.y / numOfPosGridPerCell; | |||||
| } | |||||
| public static bool IsInTheSameCell(XYPosition pos1, XYPosition pos2) | |||||
| { | |||||
| return PosGridToCellX(pos1) == PosGridToCellX(pos2) && PosGridToCellY(pos1) == PosGridToCellY(pos2); | |||||
| } | |||||
| #endregion | |||||
| #region 角色相关 | |||||
| /// <summary> | |||||
| /// 玩家相关 | |||||
| /// </summary> | |||||
| public const int characterRadius = numOfPosGridPerCell / 2; // 人物半径 | |||||
| public const int basicAp = 3000; // 初始攻击力 | |||||
| public const int basicHp = 6000; // 初始血量 | |||||
| public const int basicCD = 3000; // 初始子弹冷却 | |||||
| public const int basicBulletNum = 3; // 基本初始子弹量 | |||||
| public const int MinAP = 0; // 最小攻击力 | |||||
| public const int MaxAP = int.MaxValue; // 最大攻击力 | |||||
| public const double basicAttackRange = 9000; // 基本攻击范围 | |||||
| public const double basicBulletBombRange = 3000; // 基本子弹爆炸范围 | |||||
| public const int basicMoveSpeed = 3000; // 基本移动速度,单位:s-1 | |||||
| public const int basicBulletMoveSpeed = 3000; // 基本子弹移动速度,单位:s-1 | |||||
| public const int characterMaxSpeed = 12000; // 最大速度 | |||||
| public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 | |||||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||||
| public const int bulletRadius = 200; // 默认子弹半径 | |||||
| public const int reviveTime = 30000; // 复活时间 | |||||
| public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间 | |||||
| public const int gemToScore = 4; // 一个宝石的标准加分 | |||||
| /// <summary> | |||||
| /// 道具相关 | |||||
| /// </summary> | |||||
| public const int MinPropTypeNum = 1; | |||||
| public const int MaxPropTypeNum = 10; | |||||
| public const int PropRadius = numOfPosGridPerCell / 2; | |||||
| public const int PropMoveSpeed = 3000; | |||||
| public const int PropMaxMoveDistance = 15 * numOfPosGridPerCell; | |||||
| public const int MaxGemSize = 5; // 随机生成的宝石最大size | |||||
| public const long GemProduceTime = 10000; | |||||
| public const long PropProduceTime = 10000; | |||||
| public const int PropDuration = 10000; | |||||
| #endregion | |||||
| #region 游戏帧相关 | |||||
| public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| using System; | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface ICharacter : IGameObj | |||||
| { | |||||
| public long TeamID { get; } | |||||
| public int HP { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface IGameObj | |||||
| { | |||||
| public GameObjType Type { get; set; } | |||||
| public long ID { get; } | |||||
| public XYPosition Position { get; } // if Square, Pos equals the center | |||||
| public double FacingDirection { get; } | |||||
| public bool IsRigid { get; } | |||||
| public ShapeType Shape { get; } | |||||
| public bool CanMove { get; set; } | |||||
| public bool IsMoving { get; set; } | |||||
| public bool IsResetting { get; set; } // reviving | |||||
| public bool IsAvailable { get; } | |||||
| public int Radius { get; } // if Square, Radius equals half length of one side | |||||
| public PlaceType Place { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Threading; | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface IMap | |||||
| { | |||||
| ITimer Timer { get; } | |||||
| // the two dicts must have same keys | |||||
| Dictionary<GameObjIdx, IList<IGameObj>> GameObjDict { get; } | |||||
| Dictionary<GameObjIdx, ReaderWriterLockSlim> GameObjLockDict { get; } | |||||
| public bool IsOutOfBound(IGameObj obj); | |||||
| public IOutOfBound GetOutOfBound(XYPosition pos); // 返回新建的一个OutOfBound对象 | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| using System; | |||||
| using Preparation.Utility; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface IMoveable : IGameObj | |||||
| { | |||||
| object MoveLock { get; } | |||||
| public int MoveSpeed { get; } | |||||
| public long Move(Vector moveVec); | |||||
| protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现 | |||||
| public bool WillCollideWith(IGameObj? targetObj, XYPosition nextPos) // 检查下一位置是否会和目标物碰撞 | |||||
| { | |||||
| if (targetObj == null) | |||||
| return false; | |||||
| // 会移动的只有子弹和人物,都是Circle | |||||
| if (!targetObj.IsRigid || targetObj.ID == ID) | |||||
| return false; | |||||
| if (IgnoreCollide(targetObj)) | |||||
| return false; | |||||
| if (targetObj.Shape == ShapeType.Circle) | |||||
| { | |||||
| return XYPosition.Distance(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; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| using System; | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface IObjOfCharacter : IGameObj | |||||
| { | |||||
| ICharacter? Parent { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| | |||||
| namespace Preparation.Interface | |||||
| { | |||||
| public interface ITimer | |||||
| { | |||||
| bool IsGaming { get; } | |||||
| public bool StartGame(int timeInMilliseconds); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,14 @@ | |||||
| 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 | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,96 @@ | |||||
| | |||||
| namespace Preparation.Utility | |||||
| { | |||||
| /// <summary> | |||||
| /// 存放所有用到的枚举类型 | |||||
| /// </summary> | |||||
| public enum GameObjType | |||||
| { | |||||
| Null = 0, | |||||
| Character = 1, | |||||
| Prop = 2, | |||||
| PickedProp = 3, | |||||
| Bullet = 4, | |||||
| BombedBullet = 5, | |||||
| Wall = 6, | |||||
| Grass = 7, | |||||
| Generator = 8, // 发电机 | |||||
| BirthPoint = 9, | |||||
| Exit = 10, | |||||
| EmergencyExit = 11, | |||||
| OutOfBoundBlock = 12, // 范围外 | |||||
| } | |||||
| public enum ShapeType | |||||
| { | |||||
| Null = 0, | |||||
| Circle = 1, // 子弹和人物为圆形,格子为方形 | |||||
| Square = 2 | |||||
| } | |||||
| public enum PlaceType // 位置标志,包括陆地(一般默认为陆地,如墙体等),草丛。游戏中每一帧都要刷新各个物体的该属性 | |||||
| { | |||||
| Null = 0, | |||||
| Land = 1, | |||||
| Grass1 = 2, | |||||
| Grass2 = 3, | |||||
| Grass3 = 4, | |||||
| Grass4 = 5, | |||||
| Grass5 = 6, | |||||
| } | |||||
| public enum BulletType // 子弹类型 | |||||
| { | |||||
| Null = 0, | |||||
| OrdinaryBullet = 1, // 普通子弹 | |||||
| AtomBomb = 2, // 原子弹 | |||||
| FastBullet = 3, // 快速子弹 | |||||
| LineBullet = 4 // 直线子弹 | |||||
| } | |||||
| public enum PropType // 道具类型 | |||||
| { | |||||
| Null = 0, | |||||
| addSpeed = 1, | |||||
| addLIFE = 2, | |||||
| Shield = 3, | |||||
| Spear = 4, | |||||
| Gem = 5, // 新增:宝石 | |||||
| } | |||||
| public enum PassiveSkillType // 被动技能 | |||||
| { | |||||
| Null = 0, | |||||
| RecoverAfterBattle = 1, | |||||
| SpeedUpWhenLeavingGrass = 2, | |||||
| Vampire = 3, | |||||
| PSkill3 = 4, | |||||
| PSkill4 = 5, | |||||
| PSkill5 = 6 | |||||
| } | |||||
| public enum ActiveSkillType // 主动技能 | |||||
| { | |||||
| Null = 0, | |||||
| BecomeVampire = 1, | |||||
| BecomeAssassin = 2, | |||||
| NuclearWeapon = 3, | |||||
| SuperFast = 4, | |||||
| ASkill4 = 5, | |||||
| ASkill5 = 6 | |||||
| } | |||||
| public enum BuffType // buff | |||||
| { | |||||
| Null = 0, | |||||
| AddSpeed = 1, | |||||
| AddLIFE = 2, | |||||
| Shield = 3, | |||||
| Spear = 4 | |||||
| } | |||||
| public enum GameObjIdx | |||||
| { | |||||
| None = 0, | |||||
| Player = 1, | |||||
| Bullet = 2, | |||||
| Prop = 3, | |||||
| Gem = 4, | |||||
| Map = 5, | |||||
| BombedBullet = 6, | |||||
| PickedProp = 7 | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| using System; | |||||
| namespace Preparation.Utility | |||||
| { | |||||
| public static class Tools | |||||
| { | |||||
| public static double CorrectAngle(double angle) // 将幅角转化为主值0~2pi | |||||
| { | |||||
| if (double.IsNaN(angle) || double.IsInfinity(angle)) | |||||
| { | |||||
| return 0.0; | |||||
| } | |||||
| while (angle < 0) | |||||
| angle += 2 * Math.PI; | |||||
| while (angle >= 2 * Math.PI) | |||||
| angle -= 2 * Math.PI; | |||||
| return angle; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,57 @@ | |||||
| using System; | |||||
| namespace Preparation.Utility | |||||
| { | |||||
| public struct Vector | |||||
| { | |||||
| public double angle; | |||||
| public double length; | |||||
| public static XYPosition VectorToXY(Vector v) | |||||
| { | |||||
| return new XYPosition((int)(v.length * Math.Cos(v.angle)), (int)(v.length * Math.Sin(v.angle))); | |||||
| } | |||||
| public Vector2 ToVector2() | |||||
| { | |||||
| return new Vector2((int)(this.length * Math.Cos(this.angle)), (int)(this.length * Math.Sin(this.angle))); | |||||
| } | |||||
| public static Vector XYToVector(double x, double y) | |||||
| { | |||||
| return new Vector(Math.Atan2(y, x), Math.Sqrt((x * x) + (y * y))); | |||||
| } | |||||
| public Vector(double angle, double length) | |||||
| { | |||||
| if (length < 0) | |||||
| { | |||||
| angle += Math.PI; | |||||
| length = -length; | |||||
| } | |||||
| this.angle = Tools.CorrectAngle(angle); | |||||
| this.length = length; | |||||
| } | |||||
| } | |||||
| public struct Vector2 | |||||
| { | |||||
| public double x; | |||||
| public double y; | |||||
| public Vector2(double x, double y) | |||||
| { | |||||
| this.x = x; | |||||
| this.y = y; | |||||
| } | |||||
| public static double operator*(Vector2 v1, Vector2 v2) | |||||
| { | |||||
| return (v1.x * v2.x) + (v1.y * v2.y); | |||||
| } | |||||
| public static Vector2 operator +(Vector2 v1, Vector2 v2) | |||||
| { | |||||
| return new Vector2(v1.x + v2.x, v1.y + v2.y); | |||||
| } | |||||
| public static Vector2 operator -(Vector2 v1, Vector2 v2) | |||||
| { | |||||
| return new Vector2(v1.x - v2.x, v1.y - v2.y); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,49 @@ | |||||
| using System; | |||||
| namespace Preparation.Utility | |||||
| { | |||||
| public struct XYPosition | |||||
| { | |||||
| public int x; | |||||
| public int y; | |||||
| public XYPosition(int x, int y) | |||||
| { | |||||
| this.x = x; | |||||
| this.y = y; | |||||
| } | |||||
| public override string ToString() | |||||
| { | |||||
| return "(" + x.ToString() + "," + y.ToString() + ")"; | |||||
| } | |||||
| public static XYPosition operator +(XYPosition p1, XYPosition p2) | |||||
| { | |||||
| return new XYPosition(p1.x + p2.x, p1.y + p2.y); | |||||
| } | |||||
| public static XYPosition operator -(XYPosition p1, XYPosition p2) | |||||
| { | |||||
| return new XYPosition(p1.x - p2.x, p1.y - p2.y); | |||||
| } | |||||
| public static double Distance(XYPosition p1, XYPosition p2) | |||||
| { | |||||
| return Math.Sqrt(((long)(p1.x - p2.x) * (p1.x - p2.x)) + ((long)(p1.y - p2.y) * (p1.y - p2.y))); | |||||
| } | |||||
| /*public static XYPosition[] GetSquareRange(uint edgeLen) // 从THUAI4的BULLET.CS移植而来,不知还有用否 | |||||
| { | |||||
| XYPosition[] range = new XYPosition[edgeLen * edgeLen]; | |||||
| int offset = (int)(edgeLen >> 1); | |||||
| for (int i = 0; i < (int)edgeLen; ++i) | |||||
| { | |||||
| for (int j = 0; j < (int)edgeLen; ++j) | |||||
| { | |||||
| range[i * edgeLen + j].x = i - offset; | |||||
| range[i * edgeLen + j].y = j - offset; | |||||
| } | |||||
| } | |||||
| return range; | |||||
| }*/ | |||||
| public Vector2 ToVector2() | |||||
| { | |||||
| return new Vector2(this.x, this.y); | |||||
| } | |||||
| } | |||||
| } | |||||