Browse Source

Merge pull request #526 from shangfengh/new

fix: 🐛 fix the initialization about JumpyDumpty and fix the AttackDistance
tags/v0.1.0
DragonAura GitHub 2 years ago
parent
commit
f8c20e0341
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 244 additions and 167 deletions
  1. +1
    -1
      docs/CAPI接口(cpp).md
  2. +0
    -1
      docs/CAPI接口(python).md
  3. +3
    -2
      docs/GameRules.md
  4. +13
    -3
      docs/版本更新说明.md
  5. +6
    -6
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  6. +1
    -1
      logic/GameClass/GameObj/Bullet/Bullet.Student.cs
  7. +3
    -3
      logic/GameClass/GameObj/Bullet/Bullet.cs
  8. +0
    -2
      logic/GameClass/GameObj/Character/Character.Skill.cs
  9. +66
    -42
      logic/GameClass/GameObj/Character/Character.cs
  10. +44
    -16
      logic/GameClass/GameObj/Moveable.cs
  11. +15
    -18
      logic/GameEngine/MoveEngine.cs
  12. +21
    -22
      logic/Gaming/ActionManager.cs
  13. +30
    -8
      logic/Gaming/AttackManager.cs
  14. +29
    -30
      logic/Gaming/CharacterManager .cs
  15. +3
    -3
      logic/Gaming/PropManager.cs
  16. +4
    -4
      logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs
  17. +1
    -1
      logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs
  18. +0
    -1
      logic/Preparation/Interface/ICharacter.cs
  19. +4
    -3
      logic/Preparation/Interface/IMoveable.cs

+ 1
- 1
docs/CAPI接口(cpp).md View File

@@ -17,7 +17,7 @@
#### 人物 #### 人物
- `std::future<bool> EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动 - `std::future<bool> EndAllAction()`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令 - 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次


#### 攻击 #### 攻击
- `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向 - `std::future<bool> Attack(double angleInRadian)`:`angleInRadian`为攻击方向


+ 0
- 1
docs/CAPI接口(python).md View File

@@ -22,7 +22,6 @@


- `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动 - `def EndAllAction(self) -> Future[bool]`:可以使不处在不可行动状态中的玩家终止当前行动
- 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令 - 在指令仍在进行时,重复发出同一类型的交互指令和移动指令是无效的,你需要先发出 Stop 指令终止进行的指令
- 实际上唤醒或勉励不同的人是有效的
- EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次 - EndAllAction() 及 Move 指令调用数总和一帧内不超过 10 次


#### 攻击 #### 攻击


+ 3
- 2
docs/GameRules.md View File

@@ -89,13 +89,14 @@ $$
8. 翻窗 Climbing 8. 翻窗 Climbing


### 攻击 ### 攻击
- 攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)*2
- 攻击类型CommonAttackOfTricker攻击未写完的作业,会造成对应攻击力的损坏 - 攻击类型CommonAttackOfTricker攻击未写完的作业,会造成对应攻击力的损坏
- 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s - 捣蛋鬼攻击交互状态或前后摇的学生,将使学生眩晕4.3s


| 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty | | 攻击(子弹)类型 |搞蛋鬼的一般攻击CommonAttackOfTricker| 飞刀FlyingKnife | 蹦蹦炸弹BombBomb | 小炸弹JumpyDumpty |
| :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- | | :------------ | :--------------------- | :--------------------- | :--------------------- | :--------------------- |
| 子弹爆炸范围 | 0 | 0 | 2000 | 1000 |
| 子弹攻击距离 | 2200 | 78000 | 2200 | 4400 |
| 爆炸范围 | 0 | 0 | 2000 | 1000 |
| 攻击距离 | 2200 | 78000 | 2200 | 4400 |
| 攻击力 | 1500000 | 1200000 | 1800000 | 900000 | | 攻击力 | 1500000 | 1200000 | 1800000 | 900000 |
| 移动速度/s | 7400 | 18500 | 6000 | 8600 | | 移动速度/s | 7400 | 18500 | 6000 | 8600 |
| 前摇(ms) | 297 | 400 | 366 | - | | 前摇(ms) | 297 | 400 | 366 | - |


+ 13
- 3
docs/版本更新说明.md View File

@@ -11,11 +11,21 @@


# 5月6日12点更新 # 5月6日12点更新
- hotfix: 修复了突然的bug(物件锁的相关问题) - hotfix: 修复了突然的bug(物件锁的相关问题)
- rule:增加了每帧最多50条主动指令的限制
- rule增加了每帧最多50条主动指令的限制


# 5月8日更新 # 5月8日更新
- feat:增加了可选地图功能
- feat增加了可选地图功能
- **脚本RunServer(ForDebug).cmd/sh现在支持可选地图功能,但想选择地图,选手需要自行参照使用文档修改命令行或在云盘下载脚本** - **脚本RunServer(ForDebug).cmd/sh现在支持可选地图功能,但想选择地图,选手需要自行参照使用文档修改命令行或在云盘下载脚本**


# 5月9日19:30更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- change:更改了地图的文件路径

# 最新更新 # 最新更新
- docs:更新了 游戏机制与平衡性调整更新草案.pdf
- fix:修复JumpyDumpty的初始位置错误的问题
- fix:修正和重新说明攻击距离
- **攻击距离是指攻击(子弹)的移动距离,也就是说理论上最远被攻击的学生的中心与捣蛋鬼的中心=学生的半径+捣蛋鬼的半径+攻击距离+子弹半径(200)×2**
- hotfix:修复小炸弹初始化类型错误的问题
- remove:去除了“实际上唤醒或勉励不同的人是有效的”
- **重复发出同一类型的交互指令和移动指令是无效的**
- feat&fix:修复并**将`SendMessage`改为`SendTextMessage`与`SendBinaryMessage`**

+ 6
- 6
logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs View File

@@ -10,7 +10,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => GameData.basicAttackShortRange;
public override double AttackDistance => GameData.basicAttackShortRange;
public int ap = GameData.basicApOfGhost; public int ap = GameData.basicApOfGhost;
public override int AP public override int AP
{ {
@@ -24,7 +24,7 @@ namespace GameClass.GameObj
public override int Speed => GameData.basicBulletMoveSpeed; public override int Speed => GameData.basicBulletMoveSpeed;
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


public override int CastTime => (int)BulletAttackRange * 1000 / Speed;
public override int CastTime => (int)AttackDistance * 1000 / Speed;
public override int Backswing => GameData.basicBackswing; public override int Backswing => GameData.basicBackswing;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicBackswing; public const int cd = GameData.basicBackswing;
@@ -57,7 +57,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => GameData.basicRemoteAttackRange * 13;
public override double AttackDistance => GameData.basicRemoteAttackRange * 13;
public int ap = GameData.basicApOfGhost * 4 / 5; public int ap = GameData.basicApOfGhost * 4 / 5;
public override int AP public override int AP
{ {
@@ -104,7 +104,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange; public override double BulletBombRange => GameData.basicBulletBombRange;
public override double BulletAttackRange => GameData.basicAttackShortRange;
public override double AttackDistance => GameData.basicAttackShortRange;
public int ap = (int)(GameData.basicApOfGhost * 6.0 / 5); public int ap = (int)(GameData.basicApOfGhost * 6.0 / 5);
public override int AP public override int AP
{ {
@@ -118,7 +118,7 @@ namespace GameClass.GameObj
public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37); public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37);
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


public override int CastTime => (int)(BulletAttackRange * 1000 / Speed);
public override int CastTime => (int)(AttackDistance * 1000 / Speed);
public override int Backswing => GameData.basicRecoveryFromHit; public override int Backswing => GameData.basicRecoveryFromHit;
public override int RecoveryFromHit => GameData.basicRecoveryFromHit; public override int RecoveryFromHit => GameData.basicRecoveryFromHit;
public const int cd = GameData.basicCD; public const int cd = GameData.basicCD;
@@ -149,7 +149,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 2; public override double BulletBombRange => GameData.basicBulletBombRange / 2;
public override double BulletAttackRange => GameData.basicAttackShortRange * 2;
public override double AttackDistance => GameData.basicAttackShortRange * 2;
public int ap = (int)(GameData.basicApOfGhost * 0.6); public int ap = (int)(GameData.basicApOfGhost * 0.6);
public override int AP public override int AP
{ {


+ 1
- 1
logic/GameClass/GameObj/Bullet/Bullet.Student.cs View File

@@ -9,7 +9,7 @@ namespace GameClass.GameObj
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
public override double BulletAttackRange => 0;
public override double AttackDistance => 0;
public override int AP => 7220; public override int AP => 7220;
public override int Speed => 0; public override int Speed => 0;
public override bool IsRemoteAttack => false; public override bool IsRemoteAttack => false;


+ 3
- 3
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -10,7 +10,7 @@ namespace GameClass.GameObj
/// //攻击力 /// //攻击力
/// </summary> /// </summary>
public abstract double BulletBombRange { get; } public abstract double BulletBombRange { get; }
public abstract double BulletAttackRange { get; }
public abstract double AttackDistance { get; }
public abstract int AP { get; set; } public abstract int AP { get; set; }
public abstract int Speed { get; } public abstract int Speed { get; }
public abstract bool IsRemoteAttack { get; } public abstract bool IsRemoteAttack { get; }
@@ -56,9 +56,9 @@ namespace GameClass.GameObj


public static class BulletFactory public static class BulletFactory
{ {
public static Bullet? GetBullet(Character character, XY pos)
public static Bullet? GetBullet(Character character, XY pos, BulletType bulletType)
{ {
switch (character.BulletOfPlayer)
switch (bulletType)
{ {
case BulletType.FlyingKnife: case BulletType.FlyingKnife:
return new FlyingKnife(character, pos); return new FlyingKnife(character, pos);


+ 0
- 2
logic/GameClass/GameObj/Character/Character.Skill.cs View File

@@ -1,8 +1,6 @@
using Preparation.Utility; using Preparation.Utility;
using Preparation.Interface; using Preparation.Interface;
using System.Collections.Generic; using System.Collections.Generic;
using System;
using System.Numerics;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {


+ 66
- 42
logic/GameClass/GameObj/Character/Character.cs View File

@@ -8,9 +8,6 @@ namespace GameClass.GameObj
public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了
{ {
#region 装弹、攻击相关的基本属性及方法 #region 装弹、攻击相关的基本属性及方法
private readonly object attackLock = new();
public object AttackLock => attackLock;

/// <summary> /// <summary>
/// 装弹冷却 /// 装弹冷却
/// </summary> /// </summary>
@@ -19,7 +16,7 @@ namespace GameClass.GameObj
{ {
get get
{ {
lock (attackLock)
lock (actionLock)
{ {
return cd; return cd;
} }
@@ -33,14 +30,14 @@ namespace GameClass.GameObj
{ {
get get
{ {
lock (attackLock)
lock (actionLock)
{ {
return bulletOfPlayer; return bulletOfPlayer;
} }
} }
set set
{ {
lock (attackLock)
lock (actionLock)
{ {
bulletOfPlayer = value; bulletOfPlayer = value;
cd = OrgCD = (BulletFactory.BulletCD(value)); cd = OrgCD = (BulletFactory.BulletCD(value));
@@ -55,7 +52,7 @@ namespace GameClass.GameObj
{ {
get get
{ {
lock (attackLock)
lock (actionLock)
{ {
return maxBulletNum; return maxBulletNum;
} }
@@ -66,7 +63,7 @@ namespace GameClass.GameObj


public int UpdateBulletNum(int time) public int UpdateBulletNum(int time)
{ {
lock (attackLock)
lock (actionLock)
{ {
if (bulletNum < maxBulletNum) if (bulletNum < maxBulletNum)
{ {
@@ -84,7 +81,7 @@ namespace GameClass.GameObj
/// <returns>攻击操作发出的子弹</returns> /// <returns>攻击操作发出的子弹</returns>
public Bullet? Attack(double angle, int time) public Bullet? Attack(double angle, int time)
{ {
lock (attackLock)
lock (actionLock)
{ {
if (bulletOfPlayer == BulletType.Null) if (bulletOfPlayer == BulletType.Null)
return null; return null;
@@ -92,14 +89,16 @@ namespace GameClass.GameObj
{ {
if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time; if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time;
--bulletNum; --bulletNum;

XY res = Position + new XY // 子弹紧贴人物生成。 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);
Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer);
if (bullet == null) return null; if (bullet == null) return null;
facingDirection = new(angle, bullet.BulletAttackRange);
bullet.AP += TryAddAp() ? GameData.ApPropAdd : 0;
facingDirection = new(angle, bullet.AttackDistance);
return bullet; return bullet;
} }
else else
@@ -305,63 +304,88 @@ namespace GameClass.GameObj
{ {
get get
{ {
if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving;
return playerState;
lock (actionLock)
{
if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving;
return playerState;
}
} }
} }


public bool NoHp() => (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued);
public bool Commandable() => (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack
&& playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned);
public bool InteractingWithMapWithoutMoving() => (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest);
public bool NullOrMoving() => (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
public bool CanBeAwed() => !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued
|| playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned
|| playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);

public bool NoHp()
{
lock (actionLock)
return (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued);
}
public bool Commandable()
{
lock (actionLock)
{
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack
&& playerState != PlayerStateType.ClimbingThroughWindows && playerState != PlayerStateType.Stunned);
}
}
public bool InteractingWithMapWithoutMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.LockingOrOpeningTheDoor || playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest);
}
}
public bool NullOrMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
}
public bool CanBeAwed()
{
lock (actionLock)
return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued
|| playerState == PlayerStateType.Treated || playerState == PlayerStateType.Stunned
|| playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
private GameObj? whatInteractingWith = null; private GameObj? whatInteractingWith = null;
public GameObj? WhatInteractingWith => whatInteractingWith; public GameObj? WhatInteractingWith => whatInteractingWith;


private long threadNum = 0;
public long ThreadNum => threadNum;

public void ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
public long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{ {
lock (moveObjLock)
lock (actionLock)
{ {
++threadNum;
whatInteractingWith = gameObj; whatInteractingWith = gameObj;
if (value != PlayerStateType.Moving) if (value != PlayerStateType.Moving)
IsMoving = false; IsMoving = false;
playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return ++stateNum;
} }
} }


public void ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
public long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{ {
lock (moveObjLock)
lock (actionLock)
{ {
whatInteractingWith = gameObj; whatInteractingWith = gameObj;
if (value != PlayerStateType.Moving) if (value != PlayerStateType.Moving)
IsMoving = false; IsMoving = false;
playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return stateNum;
} }
} }


public void SetPlayerStateNaturally()
public long SetPlayerStateNaturally()
{ {
lock (moveObjLock)
lock (actionLock)
{ {
++threadNum;
whatInteractingWith = null; whatInteractingWith = null;
IsMoving = false; IsMoving = false;
playerState = PlayerStateType.Null; playerState = PlayerStateType.Null;
return ++stateNum;
} }
} }


@@ -370,11 +394,11 @@ namespace GameClass.GameObj
MoveReaderWriterLock.EnterWriteLock(); MoveReaderWriterLock.EnterWriteLock();
try try
{ {
lock (moveObjLock)
lock (actionLock)
{ {
playerState = playerStateType; playerState = playerStateType;
canMove = false; canMove = false;
isResetting = true;
isRemoved = true;
position = GameData.PosWhoDie; position = GameData.PosWhoDie;
} }
} }
@@ -623,7 +647,7 @@ namespace GameClass.GameObj
public override ShapeType Shape => ShapeType.Circle; public override ShapeType Shape => ShapeType.Circle;
public override bool IgnoreCollideExecutor(IGameObj targetObj) public override bool IgnoreCollideExecutor(IGameObj targetObj)
{ {
if (IsResetting)
if (IsRemoved)
return true; return true;
if (targetObj.Type == GameObjType.Prop) if (targetObj.Type == GameObjType.Prop)
{ {


+ 44
- 16
logic/GameClass/GameObj/Moveable.cs View File

@@ -6,17 +6,30 @@ namespace GameClass.GameObj
{ {
public abstract class Moveable : GameObj, IMoveable 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(); private readonly ReaderWriterLockSlim moveReaderWriterLock = new();
public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock;
//规定moveReaderWriterLock>moveObjLock
protected long stateNum = 0;
public long StateNum
{
get
{
lock (actionLock)
return stateNum;
}
set
{
lock (actionLock) stateNum = value;
}
}
//规定moveReaderWriterLock>actionLock


public override XY Position public override XY Position
{ {
get get
{ {
lock (moveObjLock)
lock (actionLock)
return position; return position;
} }
} }
@@ -25,7 +38,7 @@ namespace GameClass.GameObj
{ {
get get
{ {
lock (moveObjLock)
lock (actionLock)
return facingDirection; return facingDirection;
} }
} }
@@ -35,12 +48,12 @@ namespace GameClass.GameObj
{ {
get get
{ {
lock (moveObjLock)
lock (actionLock)
return isMoving; return isMoving;
} }
set set
{ {
lock (moveObjLock)
lock (actionLock)
{ {
isMoving = value; isMoving = value;
} }
@@ -51,7 +64,7 @@ namespace GameClass.GameObj
public long MovingSetPos(XY moveVec) public long MovingSetPos(XY moveVec)
{ {
if (moveVec.x != 0 || moveVec.y != 0) if (moveVec.x != 0 || moveVec.y != 0)
lock (moveObjLock)
lock (actionLock)
{ {
facingDirection = moveVec; facingDirection = moveVec;
this.position += moveVec; this.position += moveVec;
@@ -61,7 +74,7 @@ namespace GameClass.GameObj


public void ReSetPos(XY position) public void ReSetPos(XY position)
{ {
lock (moveObjLock)
lock (actionLock)
{ {
this.position = position; this.position = position;
} }
@@ -88,7 +101,7 @@ namespace GameClass.GameObj
moveReaderWriterLock.EnterWriteLock(); moveReaderWriterLock.EnterWriteLock();
try try
{ {
lock (moveObjLock)
lock (actionLock)
{ {
canMove = value; canMove = value;
} }
@@ -99,15 +112,15 @@ namespace GameClass.GameObj
} }
} }


protected bool isResetting;
public bool IsResetting
protected bool isRemoved;
public bool IsRemoved
{ {
get get
{ {
moveReaderWriterLock.EnterReadLock(); moveReaderWriterLock.EnterReadLock();
try try
{ {
return isResetting;
return isRemoved;
} }
finally finally
{ {
@@ -116,7 +129,22 @@ namespace GameClass.GameObj
} }
} }


public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令
public bool IsAvailableForMove // 是否能接收移动指令
{
get
{
moveReaderWriterLock.EnterReadLock();
try
{
lock (actionLock)
return !isMoving && canMove && !isRemoved;
}
finally
{
moveReaderWriterLock.ExitReadLock();
}
}
}


protected int moveSpeed; protected int moveSpeed;
/// <summary> /// <summary>
@@ -141,7 +169,7 @@ namespace GameClass.GameObj
moveReaderWriterLock.EnterWriteLock(); moveReaderWriterLock.EnterWriteLock();
try try
{ {
lock (moveObjLock)
lock (actionLock)
{ {
moveSpeed = value; moveSpeed = value;
} }
@@ -167,7 +195,7 @@ namespace GameClass.GameObj
this.FacingDirection = new XY(1, 0); this.FacingDirection = new XY(1, 0);
isMoving = false; isMoving = false;
CanMove = false; CanMove = false;
IsResetting = true;
IsRemoved = true;
this.Position = birthPos; this.Position = birthPos;
this.Place= place; this.Place= place;
} }


+ 15
- 18
logic/GameEngine/MoveEngine.cs View File

@@ -73,24 +73,21 @@ namespace GameEngine
obj.MovingSetPos(new XY(moveVec, maxLen)); obj.MovingSetPos(new XY(moveVec, maxLen));
} }


public void MoveObj(IMoveable obj, int moveTime, double direction)
public void MoveObj(IMoveable obj, int moveTime, double direction, long threadNum)
{ {
if (obj.IsMoving) // 已经移动的物体不能再移动
return;
if (!obj.IsAvailable || !gameTimer.IsGaming)
return;
long threadNum = (obj.Type == GameObjType.Character) ? ((ICharacter)obj).ThreadNum : 0;//对人特殊处理
if (!gameTimer.IsGaming) return;
lock (obj.ActionLock)
{
if (!obj.IsAvailableForMove) return;
obj.IsMoving = true;
}
new Thread new Thread
( (
() => () =>
{ {
lock (obj.MoveLock)
obj.IsMoving = true;

double moveVecLength = 0.0; double moveVecLength = 0.0;
XY res = new(direction, moveVecLength); XY res = new(direction, moveVecLength);
double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); // 转向,并用deltaLen存储行走的误差
double deltaLen = 0; // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null; IGameObj? collisionObj = null;
bool isDestroyed = false; bool isDestroyed = false;


@@ -119,14 +116,14 @@ namespace GameEngine
if (!isDestroyed) if (!isDestroyed)
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
() => gameTimer.IsGaming && obj.CanMove && !obj.IsResetting && obj.IsMoving,
() => gameTimer.IsGaming && obj.CanMove && !obj.IsRemoved && obj.IsMoving,
() => () =>
{ {
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);


//对人特殊处理 //对人特殊处理
if (threadNum > 0 && ((ICharacter)obj).ThreadNum != threadNum) return false;
if (threadNum > 0 && obj.StateNum != threadNum) return false;


// 越界情况处理:如果越界,则与越界方块碰撞 // 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志 bool flag; // 循环标志
@@ -147,7 +144,7 @@ namespace GameEngine
isDestroyed = true; isDestroyed = true;
return false; return false;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
if (threadNum == 0 || obj.StateNum == threadNum)
MoveMax(obj, res); MoveMax(obj, res);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);
@@ -155,7 +152,7 @@ namespace GameEngine
} }
} while (flag); } while (flag);


if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
if (threadNum == 0 || obj.StateNum == threadNum)
deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res)); deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res));


return true; return true;
@@ -174,7 +171,7 @@ namespace GameEngine
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null) if ((collisionObj = collisionChecker.CheckCollisionWhenMoving(obj, res)) == null)
{ {
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
if (threadNum == 0 || obj.StateNum == threadNum)
obj.MovingSetPos(res); obj.MovingSetPos(res);
} }
else else
@@ -189,7 +186,7 @@ namespace GameEngine
isDestroyed = true; isDestroyed = true;
break; break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
if (threadNum == 0 || ((ICharacter)obj).ThreadNum == threadNum)
if (threadNum == 0 || obj.StateNum == threadNum)
MoveMax(obj, res); MoveMax(obj, res);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength); res = new XY(direction, moveVecLength);
@@ -202,7 +199,7 @@ namespace GameEngine
{ {
Thread.Sleep(leftTime); // 多移动的在这里补回来 Thread.Sleep(leftTime); // 多移动的在这里补回来
} }
lock (obj.MoveLock)
lock (obj.ActionLock)
obj.IsMoving = false; // 结束移动 obj.IsMoving = false; // 结束移动
EndMove(obj); EndMove(obj);
return 0; return 0;


+ 21
- 22
logic/Gaming/ActionManager.cs View File

@@ -21,14 +21,14 @@ namespace Gaming
{ {
if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty)
{ {
if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty))
if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty));
gameMap.Remove((GameObj)collisionObj); gameMap.Remove((GameObj)collisionObj);
} }
} }
if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost())
{ {
if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge))
if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge));
characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge);
} }
@@ -37,9 +37,8 @@ namespace Gaming
{ {
if (moveTimeInMilliseconds < 5) return false; if (moveTimeInMilliseconds < 5) return false;
if (!playerToMove.Commandable()) return false; if (!playerToMove.Commandable()) return false;
if (playerToMove.IsMoving) return false;
characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection,
characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving));
return true; return true;
} }


@@ -47,7 +46,7 @@ namespace Gaming
{ {
if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false; if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false;
characterManager.BeStunned(playerToMove, moveTimeInMilliseconds); characterManager.BeStunned(playerToMove, moveTimeInMilliseconds);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection, playerToMove.StateNum);
return true; return true;
} }


@@ -72,14 +71,14 @@ namespace Gaming


++generatorForFix.NumOfFixing; ++generatorForFix.NumOfFixing;
characterManager.SetPlayerState(player, PlayerStateType.Fixing); characterManager.SetPlayerState(player, PlayerStateType.Fixing);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;
new Thread new Thread
( (
() => () =>
{ {
Thread.Sleep(GameData.frameDuration); Thread.Sleep(GameData.frameDuration);
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.ThreadNum,
loopCondition: () => gameMap.Timer.IsGaming && threadNum == player.StateNum,
loopToDo: () => loopToDo: () =>
{ {
if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player)) if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player))
@@ -173,10 +172,10 @@ namespace Gaming
{ {
characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated); characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated);
characterManager.SetPlayerState(player, PlayerStateType.Treating); characterManager.SetPlayerState(player, PlayerStateType.Treating);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;


new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player)) if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player))
@@ -187,7 +186,7 @@ namespace Gaming
) )
.Start(); .Start();


if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player);
if (threadNum == player.StateNum) characterManager.SetPlayerState(player);
else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated); else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated);
} }
) )
@@ -205,14 +204,14 @@ namespace Gaming
return false; return false;
characterManager.SetPlayerState(player, PlayerStateType.Rescuing); characterManager.SetPlayerState(player, PlayerStateType.Rescuing);
characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued); characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;


new Thread new Thread
( (
() => () =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
playerRescued.TimeOfRescue += GameData.frameDuration; playerRescued.TimeOfRescue += GameData.frameDuration;
@@ -234,7 +233,7 @@ namespace Gaming
else else
characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted); characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted);
} }
if (threadNum == player.ThreadNum) characterManager.SetPlayerState(player);
if (threadNum == player.StateNum) characterManager.SetPlayerState(player);
playerRescued.TimeOfRescue = 0; playerRescued.TimeOfRescue = 0;
} }
) )
@@ -303,14 +302,14 @@ namespace Gaming
// gameMap.Add(addWall); // gameMap.Add(addWall);


characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows); characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;
windowForClimb.WhoIsClimbing = player; windowForClimb.WhoIsClimbing = player;
new Thread new Thread
( (
() => () =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => { }, loopToDo: () => { },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0, finallyReturn: () => 0,
@@ -326,10 +325,10 @@ namespace Gaming
player.ReSetPos(windowToPlayer + windowForClimb.Position); player.ReSetPos(windowToPlayer + windowForClimb.Position);
player.MoveSpeed = player.SpeedOfClimbingThroughWindows; player.MoveSpeed = player.SpeedOfClimbingThroughWindows;


moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle());
moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle(), threadNum);


new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
}, },
@@ -343,7 +342,7 @@ namespace Gaming
player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed);
windowForClimb.WhoIsClimbing = null; windowForClimb.WhoIsClimbing = null;
// gameMap.Remove(addWall); // gameMap.Remove(addWall);
if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
{ {
characterManager.SetPlayerState(player); characterManager.SetPlayerState(player);
} }
@@ -386,13 +385,13 @@ namespace Gaming
if (!flag) return false; if (!flag) return false;


characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor); characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;
new Thread new Thread
( (
() => () =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
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: () => loopToDo: () =>
{ {
flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null); flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null);
@@ -407,7 +406,7 @@ namespace Gaming
{ {
doorToLock.IsOpen = (!doorToLock.IsOpen); doorToLock.IsOpen = (!doorToLock.IsOpen);
} }
if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
characterManager.SetPlayerState(player); characterManager.SetPlayerState(player);
doorToLock.OpenOrLockDegree = 0; doorToLock.OpenOrLockDegree = 0;
} }


+ 30
- 8
logic/Gaming/AttackManager.cs View File

@@ -39,6 +39,17 @@ namespace Gaming
this.characterManager = characterManager; 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, bulletType);
if (bullet == null) return;
Debugger.Output(bullet, "Attack in " + pos.ToString());
gameMap.Add(bullet);
moveEngine.MoveObj(bullet, (int)(bullet.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
}

private void BombObj(Bullet bullet, GameObj objBeingShot) private void BombObj(Bullet bullet, GameObj objBeingShot)
{ {
#if DEBUG #if DEBUG
@@ -122,9 +133,21 @@ namespace Gaming


if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) 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<IGameObj>(); var beAttackedList = new List<IGameObj>();
@@ -171,19 +194,18 @@ namespace Gaming
if (bullet != null) if (bullet != null)
{ {
Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); Debugger.Output(bullet, "Attack in " + bullet.Position.ToString());
bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0;
gameMap.Add(bullet); 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.AttackDistance * 1000 / bullet.MoveSpeed), angle, ++bullet.StateNum); // 这里时间参数除出来的单位要是ms
if (bullet.CastTime > 0) if (bullet.CastTime > 0)
{ {
characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack); characterManager.SetPlayerState(player, PlayerStateType.TryingToAttack);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;


new Thread new Thread
(() => (() =>
{ {
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => threadNum == player.ThreadNum && gameMap.Timer.IsGaming,
loopCondition: () => threadNum == player.StateNum && gameMap.Timer.IsGaming,
loopToDo: () => loopToDo: () =>
{ {
}, },
@@ -195,7 +217,7 @@ namespace Gaming


if (gameMap.Timer.IsGaming) if (gameMap.Timer.IsGaming)
{ {
if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
{ {
characterManager.SetPlayerState(player); characterManager.SetPlayerState(player);
} }


+ 29
- 30
logic/Gaming/CharacterManager .cs View File

@@ -1,5 +1,4 @@
using System;
using System.Threading;
using System.Threading;
using GameClass.GameObj; using GameClass.GameObj;
using Preparation.Utility; using Preparation.Utility;
using Preparation.Interface; using Preparation.Interface;
@@ -18,37 +17,37 @@ namespace Gaming
this.gameMap = gameMap; this.gameMap = gameMap;
} }


public void SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
public long SetPlayerState(Character player, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{ {
lock (player.MoveLock)
lock (player.ActionLock)
{ {
switch (player.PlayerState)
PlayerStateType nowPlayerState = player.PlayerState;
if (nowPlayerState == value) return -1;
switch (nowPlayerState)
{ {
case PlayerStateType.OpeningTheChest: case PlayerStateType.OpeningTheChest:
if (player.NoHp()) return -1;
((Chest)player.WhatInteractingWith!).StopOpen(); ((Chest)player.WhatInteractingWith!).StopOpen();
player.ChangePlayerState(value, gameObj);
break;
return player.ChangePlayerState(value, gameObj);
case PlayerStateType.OpeningTheDoorway: case PlayerStateType.OpeningTheDoorway:
if (player.NoHp()) return -1;
Doorway doorway = (Doorway)player.WhatInteractingWith!; Doorway doorway = (Doorway)player.WhatInteractingWith!;
doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime; doorway.OpenDegree += gameMap.Timer.nowTime() - doorway.OpenStartTime;
doorway.OpenStartTime = 0; doorway.OpenStartTime = 0;
player.ChangePlayerState(value, gameObj);
break;
return player.ChangePlayerState(value, gameObj);
case PlayerStateType.Addicted: case PlayerStateType.Addicted:
if (value == PlayerStateType.Rescued) if (value == PlayerStateType.Rescued)
player.ChangePlayerStateInOneThread(value, gameObj);
return player.ChangePlayerStateInOneThread(value, gameObj);
else else
player.ChangePlayerState(value, gameObj);
break;
return player.ChangePlayerState(value, gameObj);
case PlayerStateType.Rescued: case PlayerStateType.Rescued:
if (value == PlayerStateType.Addicted) if (value == PlayerStateType.Addicted)
player.ChangePlayerStateInOneThread(value, gameObj);
return player.ChangePlayerStateInOneThread(value, gameObj);
else else
player.ChangePlayerState(value, gameObj);
break;
return player.ChangePlayerState(value, gameObj);
default: default:
player.ChangePlayerState(value, gameObj);
break;
if (player.NoHp()) return -1;
return player.ChangePlayerState(value, gameObj);
} }
} }
} }
@@ -75,7 +74,7 @@ namespace Gaming
Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval)); Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval));
long lastTime = Environment.TickCount64; long lastTime = Environment.TickCount64;
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved,
loopToDo: () => loopToDo: () =>
{ {
long nowTime = Environment.TickCount64; long nowTime = Environment.TickCount64;
@@ -133,7 +132,7 @@ namespace Gaming
} }
} }
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved,
loopToDo: () => loopToDo: () =>
{ {
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock(); gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
@@ -246,7 +245,7 @@ namespace Gaming
} }
} }
SetPlayerState(player, PlayerStateType.Addicted); SetPlayerState(player, PlayerStateType.Addicted);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;
new Thread new Thread
(() => (() =>
{ {
@@ -254,7 +253,7 @@ namespace Gaming
Debugger.Output(player, " is addicted "); Debugger.Output(player, " is addicted ");
#endif #endif
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
() => 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; player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0;
@@ -276,28 +275,28 @@ namespace Gaming
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
} }


public bool BeStunned(Character player, int time)
public long BeStunned(Character player, int time)
{ {
if (player.PlayerState == PlayerStateType.Stunned || player.NoHp() || player.CharacterType == CharacterType.Robot) return false;
if (player.CharacterType == CharacterType.Robot) return -1;
long threadNum = SetPlayerState(player, PlayerStateType.Stunned);
if (threadNum == -1) return -1;
new Thread new Thread
(() => (() =>
{ {
SetPlayerState(player, PlayerStateType.Stunned);
long threadNum = player.ThreadNum;
Thread.Sleep(time); Thread.Sleep(time);
if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
SetPlayerState(player); SetPlayerState(player);
} }
) )
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
return true;
return threadNum;
} }


public bool TryBeAwed(Student character, Bullet bullet) public bool TryBeAwed(Student character, Bullet bullet)
{ {
if (character.CanBeAwed()) if (character.CanBeAwed())
{ {
if (BeStunned(character, GameData.basicStunnedTimeOfStudent))
if (BeStunned(character, GameData.basicStunnedTimeOfStudent) > 0)
bullet.Parent!.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.basicStunnedTimeOfStudent)); bullet.Parent!.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.basicStunnedTimeOfStudent));
return true; return true;
} }
@@ -373,14 +372,14 @@ namespace Gaming
if (time <= 0) return false; if (time <= 0) return false;
if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false; if (player.PlayerState == PlayerStateType.Swinging || (!player.Commandable() && player.PlayerState != PlayerStateType.TryingToAttack)) return false;
SetPlayerState(player, PlayerStateType.Swinging); SetPlayerState(player, PlayerStateType.Swinging);
long threadNum = player.ThreadNum;
long threadNum = player.StateNum;


new Thread new Thread
(() => (() =>
{ {
Thread.Sleep(time); Thread.Sleep(time);


if (threadNum == player.ThreadNum)
if (threadNum == player.StateNum)
{ {
SetPlayerState(player); SetPlayerState(player);
} }


+ 3
- 3
logic/Gaming/PropManager.cs View File

@@ -21,7 +21,7 @@ namespace Gaming


public void UseProp(Character player, PropType propType) public void UseProp(Character player, PropType propType)
{ {
if (player.IsResetting || player.CharacterType == CharacterType.Robot)
if (player.IsRemoved || player.CharacterType == CharacterType.Robot)
return; return;
Prop prop = player.UseProp(propType); Prop prop = player.UseProp(propType);
switch (prop.GetPropType()) switch (prop.GetPropType())
@@ -73,7 +73,7 @@ namespace Gaming
/// <returns></returns> /// <returns></returns>
public bool PickProp(Character player, PropType propType = PropType.Null) public bool PickProp(Character player, PropType propType = PropType.Null)
{ {
if (player.IsResetting)
if (player.IsRemoved)
return false; return false;
int indexing = player.IndexingOfAddProp(); int indexing = player.IndexingOfAddProp();
if (indexing == GameData.maxNumOfPropInPropInventory) if (indexing == GameData.maxNumOfPropInPropInventory)
@@ -118,7 +118,7 @@ namespace Gaming


public void ThrowProp(Character player, PropType propType) public void ThrowProp(Character player, PropType propType)
{ {
if (!gameMap.Timer.IsGaming || player.IsResetting)
if (!gameMap.Timer.IsGaming || player.IsRemoved)
return; return;
Prop prop = player.UseProp(propType); Prop prop = player.UseProp(propType);
if (prop.GetPropType() == PropType.Null) if (prop.GetPropType() == PropType.Null)


+ 4
- 4
logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs View File

@@ -188,7 +188,7 @@ namespace Gaming
{ {
if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange) if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange)
{ {
if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl))
if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl)); player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl));
} }
} }
@@ -219,7 +219,7 @@ namespace Gaming
|| character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows) || character.PlayerState == PlayerStateType.UsingSkill || character.PlayerState == PlayerStateType.LockingOrOpeningTheDoor || character.PlayerState == PlayerStateType.ClimbingThroughWindows)
&& gameMap.CanSee(player, character)) && gameMap.CanSee(player, character))
{ {
if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)))
if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))); player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)));
break; break;
} }
@@ -335,7 +335,7 @@ namespace Gaming
startSkill(); startSkill();
activeSkill.IsBeingUsed = true; activeSkill.IsBeingUsed = true;
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
() => !player.IsResetting,
() => !player.IsRemoved,
() => () =>
{ {
player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration);
@@ -355,7 +355,7 @@ namespace Gaming
Debugger.Output(player, "return to normal."); Debugger.Output(player, "return to normal.");


new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsResetting,
loopCondition: () => player.TimeUntilActiveSkillAvailable[activeSkillType] > 0 && !player.IsRemoved,
loopToDo: () => loopToDo: () =>
{ {
player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration); player.AddTimeUntilActiveSkillAvailable(activeSkillType, -(int)GameData.frameDuration);


+ 1
- 1
logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs View File

@@ -22,7 +22,7 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束
{ {
new FrameRateTaskExecutor<int> new FrameRateTaskExecutor<int>
( (
() => gameMap.Timer.IsGaming && !player.IsResetting,
() => gameMap.Timer.IsGaming && !player.IsRemoved,
() => () =>
{ {
if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration; if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration;


+ 0
- 1
logic/Preparation/Interface/ICharacter.cs View File

@@ -14,7 +14,6 @@ namespace Preparation.Interface
public BulletType BulletOfPlayer { get; set; } public BulletType BulletOfPlayer { get; set; }
public CharacterType CharacterType { get; } public CharacterType CharacterType { get; }
public int UpdateBulletNum(int time); public int UpdateBulletNum(int time);
public long ThreadNum { get; }


public bool IsGhost(); public bool IsGhost();
} }


+ 4
- 3
logic/Preparation/Interface/IMoveable.cs View File

@@ -5,11 +5,12 @@ namespace Preparation.Interface
{ {
public interface IMoveable : IGameObj public interface IMoveable : IGameObj
{ {
object MoveLock { get; }
object ActionLock { get; }
public int MoveSpeed { get; } public int MoveSpeed { get; }
public bool IsMoving { get; set; } public bool IsMoving { get; set; }
public bool IsResetting { get; } // reviving
public bool IsAvailable { get; }
public bool IsRemoved { get; }
public bool IsAvailableForMove { get; }
public long StateNum { get; }
public long MovingSetPos(XY moveVec); public long MovingSetPos(XY moveVec);
public void ReSetCanMove(bool value); public void ReSetCanMove(bool value);
public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞


Loading…
Cancel
Save