diff --git a/docs/版本更新说明.md b/docs/版本更新说明.md index 6869194..e36dadf 100644 --- a/docs/版本更新说明.md +++ b/docs/版本更新说明.md @@ -17,5 +17,9 @@ - feat:增加了可选地图功能 - **脚本RunServer(ForDebug).cmd/sh现在支持可选地图功能,但想选择地图,选手需要自行参照使用文档修改命令行或在云盘下载脚本** +# 5月9日更新 +- docs:更新了 游戏机制与平衡性调整更新草案.pdf +- change:更改了地图的文件路径 + # 最新更新 -- docs:更新了 游戏机制与平衡性调整更新草案.pdf \ No newline at end of file +- \ No newline at end of file diff --git a/logic/GameClass/GameObj/Character/Character.Skill.cs b/logic/GameClass/GameObj/Character/Character.Skill.cs index 0ae23db..b9b152b 100644 --- a/logic/GameClass/GameObj/Character/Character.Skill.cs +++ b/logic/GameClass/GameObj/Character/Character.Skill.cs @@ -1,8 +1,6 @@ using Preparation.Utility; using Preparation.Interface; using System.Collections.Generic; -using System; -using System.Numerics; namespace GameClass.GameObj { diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs index 15c2e78..5ece5e3 100644 --- a/logic/GameClass/GameObj/Character/Character.cs +++ b/logic/GameClass/GameObj/Character/Character.cs @@ -2,15 +2,14 @@ using Preparation.Utility; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; namespace GameClass.GameObj { public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 { #region 装弹、攻击相关的基本属性及方法 - private readonly object attackLock = new(); - public object AttackLock => attackLock; - /// /// 装弹冷却 /// @@ -19,7 +18,7 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return cd; } @@ -33,14 +32,14 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return bulletOfPlayer; } } set { - lock (attackLock) + lock (actionLock) { bulletOfPlayer = value; cd = OrgCD = (BulletFactory.BulletCD(value)); @@ -55,7 +54,7 @@ namespace GameClass.GameObj { get { - lock (attackLock) + lock (actionLock) { return maxBulletNum; } @@ -66,7 +65,7 @@ namespace GameClass.GameObj public int UpdateBulletNum(int time) { - lock (attackLock) + lock (actionLock) { if (bulletNum < maxBulletNum) { @@ -84,7 +83,7 @@ namespace GameClass.GameObj /// 攻击操作发出的子弹 public Bullet? Attack(double angle, int time) { - lock (attackLock) + lock (actionLock) { if (bulletOfPlayer == BulletType.Null) return null; @@ -92,10 +91,11 @@ namespace GameClass.GameObj { if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time; --bulletNum; + XY res = Position + new XY // 子弹紧贴人物生成。 ( - (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1), - (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1) + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) ); Bullet? bullet = BulletFactory.GetBullet(this, res); if (bullet == null) return null; @@ -326,14 +326,11 @@ namespace GameClass.GameObj private GameObj? whatInteractingWith = null; public GameObj? WhatInteractingWith => whatInteractingWith; - private long threadNum = 0; - public long ThreadNum => threadNum; - public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (moveObjLock) + lock (actionLock) { - ++threadNum; + stateNum= Interlocked.Increment(ref stateNum); whatInteractingWith = gameObj; if (value != PlayerStateType.Moving) IsMoving = false; @@ -344,7 +341,7 @@ namespace GameClass.GameObj public void ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (moveObjLock) + lock (actionLock) { whatInteractingWith = gameObj; if (value != PlayerStateType.Moving) @@ -356,9 +353,9 @@ namespace GameClass.GameObj public void SetPlayerStateNaturally() { - lock (moveObjLock) + lock (actionLock) { - ++threadNum; + stateNum= Interlocked.Increment(ref stateNum); whatInteractingWith = null; IsMoving = false; playerState = PlayerStateType.Null; @@ -370,7 +367,7 @@ namespace GameClass.GameObj MoveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { playerState = playerStateType; canMove = false; diff --git a/logic/GameClass/GameObj/Moveable.cs b/logic/GameClass/GameObj/Moveable.cs index 10351dc..72e3927 100644 --- a/logic/GameClass/GameObj/Moveable.cs +++ b/logic/GameClass/GameObj/Moveable.cs @@ -6,17 +6,22 @@ namespace GameClass.GameObj { public abstract class Moveable : GameObj, IMoveable { - protected readonly object moveObjLock = new(); - public object MoveLock => moveObjLock; + protected readonly object actionLock = new(); + public object ActionLock => actionLock; private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; - //规定moveReaderWriterLock>moveObjLock + protected long stateNum = 0; + public long StateNum + { + get=>Interlocked.Read(ref stateNum); + } + //规定moveReaderWriterLock>actionLock public override XY Position { get { - lock (moveObjLock) + lock (actionLock) return position; } } @@ -25,7 +30,7 @@ namespace GameClass.GameObj { get { - lock (moveObjLock) + lock (actionLock) return facingDirection; } } @@ -35,12 +40,12 @@ namespace GameClass.GameObj { get { - lock (moveObjLock) + lock (actionLock) return isMoving; } set { - lock (moveObjLock) + lock (actionLock) { isMoving = value; } @@ -51,7 +56,7 @@ namespace GameClass.GameObj public long MovingSetPos(XY moveVec) { if (moveVec.x != 0 || moveVec.y != 0) - lock (moveObjLock) + lock (actionLock) { facingDirection = moveVec; this.position += moveVec; @@ -61,7 +66,7 @@ namespace GameClass.GameObj public void ReSetPos(XY position) { - lock (moveObjLock) + lock (actionLock) { this.position = position; } @@ -88,7 +93,7 @@ namespace GameClass.GameObj moveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { canMove = value; } @@ -141,7 +146,7 @@ namespace GameClass.GameObj moveReaderWriterLock.EnterWriteLock(); try { - lock (moveObjLock) + lock (actionLock) { moveSpeed = value; } diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs index e3c758e..ff847d8 100644 --- a/logic/GameEngine/MoveEngine.cs +++ b/logic/GameEngine/MoveEngine.cs @@ -80,12 +80,12 @@ namespace GameEngine if (!obj.IsAvailable || !gameTimer.IsGaming) return; - long threadNum = (obj.Type == GameObjType.Character) ? ((ICharacter)obj).ThreadNum : 0;//对人特殊处理 + long threadNum = (obj.Type == GameObjType.Character) ? ((ICharacter)obj).StateNum : 0;//对人特殊处理 new Thread ( () => { - lock (obj.MoveLock) + lock (obj.ActionLock) obj.IsMoving = true; double moveVecLength = 0.0; @@ -126,7 +126,7 @@ namespace GameEngine res = new XY(direction, moveVecLength); //对人特殊处理 - if (threadNum > 0 && ((ICharacter)obj).ThreadNum != threadNum) return false; + if (threadNum > 0 && ((ICharacter)obj).StateNum != threadNum) return false; // 越界情况处理:如果越界,则与越界方块碰撞 bool flag; // 循环标志 @@ -147,7 +147,7 @@ namespace GameEngine isDestroyed = true; return false; case AfterCollision.MoveMax: - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || ((ICharacter)obj).StateNum == threadNum) MoveMax(obj, res); moveVecLength = 0; res = new XY(direction, moveVecLength); @@ -155,7 +155,7 @@ namespace GameEngine } } while (flag); - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || ((ICharacter)obj).StateNum == threadNum) deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); return true; @@ -174,7 +174,7 @@ namespace GameEngine res = new XY(direction, moveVecLength); if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) { - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || ((ICharacter)obj).StateNum == threadNum) obj.MovingSetPos(res); } else @@ -189,7 +189,7 @@ namespace GameEngine isDestroyed = true; break; case AfterCollision.MoveMax: - if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum) + if (threadNum == 0 || ((ICharacter)obj).StateNum == threadNum) MoveMax(obj, res); moveVecLength = 0; res = new XY(direction, moveVecLength); @@ -202,7 +202,7 @@ namespace GameEngine { Thread.Sleep(leftTime); // 多移动的在这里补回来 } - lock (obj.MoveLock) + lock (obj.ActionLock) obj.IsMoving = false; // 结束移动 EndMove(obj); return 0; diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index 5f0f1db..09c8aba 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -72,14 +72,14 @@ namespace Gaming ++generatorForFix.NumOfFixing; characterManager.SetPlayerState(player, PlayerStateType.Fixing); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { Thread.Sleep(GameData.frameDuration); new FrameRateTaskExecutor( - loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.ThreadNum, + loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.StateNum, loopToDo: () => { if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) @@ -173,10 +173,10 @@ namespace Gaming { characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); characterManager.SetPlayerState(player, PlayerStateType.Treating); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new FrameRateTaskExecutor( - loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) @@ -187,7 +187,7 @@ namespace Gaming ) .Start(); - if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player); + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); } ) @@ -205,14 +205,14 @@ namespace Gaming return false; characterManager.SetPlayerState(player, PlayerStateType.Rescuing); characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { playerRescued.TimeOfRescue += GameData.frameDuration; @@ -234,7 +234,7 @@ namespace Gaming else characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); } - if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player); + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); playerRescued.TimeOfRescue = 0; } ) @@ -303,14 +303,14 @@ namespace Gaming // gameMap.Add(addWall); characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; windowForClimb.WhoIsClimbing = player; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, timeInterval: GameData.frameDuration, finallyReturn: () => 0, @@ -329,7 +329,7 @@ namespace Gaming moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle()); new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, @@ -343,7 +343,7 @@ namespace Gaming player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); windowForClimb.WhoIsClimbing = null; // gameMap.Remove(addWall); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { characterManager.SetPlayerState(player); } @@ -386,13 +386,13 @@ namespace Gaming if (!flag) return false; characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread ( () => { new FrameRateTaskExecutor( - loopCondition: () => flag && threadNum == player.ThreadNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, + loopCondition: () => flag && threadNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor, loopToDo: () => { flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); @@ -407,7 +407,7 @@ namespace Gaming { doorToLock.IsOpen = (!doorToLock.IsOpen); } - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) characterManager.SetPlayerState(player); doorToLock.OpenOrLockDegree = 0; } diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index e7f3eca..3710e94 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -39,6 +39,17 @@ namespace Gaming this.characterManager = characterManager; } + public void ProduceBulletNaturally(BulletType bulletType, Character player, double angle,XY pos) + { + // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange + if (bulletType == BulletType.Null)return; + Bullet? bullet = BulletFactory.GetBullet(player, pos); + if (bullet == null) return; + Debugger.Output(bullet, "Attack in " + pos.ToString()); + gameMap.Add(bullet); + moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(bulletType)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms + } + private void BombObj(Bullet bullet, GameObj objBeingShot) { #if DEBUG @@ -122,9 +133,21 @@ namespace Gaming if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) { - bullet.Parent!.BulletOfPlayer = BulletType.JumpyDumpty; - Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI / 2.0); - Attack((Character)bullet.Parent, bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0); + double angle = bullet.FacingDirection.Angle() + Math.PI / 2.0; + XY pos = bullet.Position + new XY // 子弹紧贴人物生成。 + ( + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) + ); + ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent, angle, pos); + + angle = bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0; + pos = bullet.Position + new XY // 子弹紧贴人物生成。 + ( + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), + (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) + ); + ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent, angle, pos); } var beAttackedList = new List(); @@ -173,17 +196,17 @@ namespace Gaming Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0; gameMap.Add(bullet); - moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms + moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(bullet.TypeOfBullet)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms if (bullet.CastTime > 0) { characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { new FrameRateTaskExecutor( - loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming, + loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming, loopToDo: () => { }, @@ -195,7 +218,7 @@ namespace Gaming if (gameMap.Timer.IsGaming) { - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { characterManager.SetPlayerState(player); } diff --git a/logic/Gaming/CharacterManager .cs b/logic/Gaming/CharacterManager .cs index bb49b8c..ca29584 100644 --- a/logic/Gaming/CharacterManager .cs +++ b/logic/Gaming/CharacterManager .cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using GameClass.GameObj; using Preparation.Utility; using Preparation.Interface; @@ -20,7 +19,7 @@ namespace Gaming public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) { - lock (player.MoveLock) + lock (player.ActionLock) { switch (player.PlayerState) { @@ -246,7 +245,7 @@ namespace Gaming } } SetPlayerState(player, PlayerStateType.Addicted); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { @@ -254,7 +253,7 @@ namespace Gaming Debugger.Output(player, " is addicted "); #endif new FrameRateTaskExecutor( - () => threadNum == player.ThreadNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming, + () => threadNum == player.StateNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming, () => { player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0; @@ -283,9 +282,9 @@ namespace Gaming (() => { SetPlayerState(player, PlayerStateType.Stunned); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; Thread.Sleep(time); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) SetPlayerState(player); } ) @@ -373,14 +372,14 @@ namespace Gaming if (time <= 0) return false; if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; SetPlayerState(player, PlayerStateType.Swinging); - long threadNum = player.ThreadNum; + long threadNum = player.StateNum; new Thread (() => { Thread.Sleep(time); - if (threadNum == player.ThreadNum) + if (threadNum == player.StateNum) { SetPlayerState(player); } diff --git a/logic/Preparation/Interface/ICharacter.cs b/logic/Preparation/Interface/ICharacter.cs index 3af523d..f7daade 100644 --- a/logic/Preparation/Interface/ICharacter.cs +++ b/logic/Preparation/Interface/ICharacter.cs @@ -14,7 +14,7 @@ namespace Preparation.Interface public BulletType BulletOfPlayer { get; set; } public CharacterType CharacterType { get; } public int UpdateBulletNum(int time); - public long ThreadNum { get; } + public long StateNum { get; } public bool IsGhost(); } diff --git a/logic/Preparation/Interface/IMoveable.cs b/logic/Preparation/Interface/IMoveable.cs index a6c74e4..2110f5a 100644 --- a/logic/Preparation/Interface/IMoveable.cs +++ b/logic/Preparation/Interface/IMoveable.cs @@ -5,7 +5,7 @@ namespace Preparation.Interface { public interface IMoveable : IGameObj { - object MoveLock { get; } + object ActionLock { get; } public int MoveSpeed { get; } public bool IsMoving { get; set; } public bool IsResetting { get; } // reviving