Browse Source

Merge pull request #138 from shangfengh/new

chore: add place of GameObj
tags/0.1.0
TCL GitHub 2 years ago
parent
commit
46742a7284
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 418 additions and 241 deletions
  1. +2
    -0
      logic/GameClass/GameObj/Bullet/BombedBullet.cs
  2. +12
    -12
      logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs
  3. +8
    -7
      logic/GameClass/GameObj/Bullet/Bullet.cs
  4. +4
    -5
      logic/GameClass/GameObj/Character/Character.Skill.cs
  5. +61
    -28
      logic/GameClass/GameObj/Character/Character.cs
  6. +10
    -84
      logic/GameClass/GameObj/GameObj.cs
  7. +1
    -0
      logic/GameClass/GameObj/Map/Doorway.cs
  8. +1
    -0
      logic/GameClass/GameObj/Map/EmergencyExit.cs
  9. +1
    -0
      logic/GameClass/GameObj/Map/Generator.cs
  10. +7
    -6
      logic/GameClass/GameObj/Map/Map.cs
  11. +1
    -0
      logic/GameClass/GameObj/Map/Wall.cs
  12. +103
    -0
      logic/GameClass/GameObj/Moveable.cs
  13. +1
    -1
      logic/GameClass/GameObj/ObjOfCharacter.cs
  14. +1
    -0
      logic/GameClass/GameObj/OutOfBoundBlock.cs
  15. +1
    -0
      logic/GameClass/GameObj/PickedProp.cs
  16. +8
    -13
      logic/GameClass/GameObj/Prop.cs
  17. +32
    -12
      logic/GameEngine/MoveEngine.cs
  18. +3
    -2
      logic/Gaming/ActionManager.cs
  19. +10
    -8
      logic/Gaming/AttackManager.cs
  20. +97
    -35
      logic/Gaming/Game.cs
  21. +10
    -9
      logic/Gaming/PropManager.cs
  22. +2
    -2
      logic/Preparation/Interface/IGameObj.cs
  23. +2
    -0
      logic/Preparation/Interface/IMap.cs
  24. +4
    -2
      logic/Preparation/Interface/IMoveable.cs
  25. +16
    -1
      logic/Preparation/Interface/IOccupation.cs
  26. +7
    -0
      logic/Preparation/Utility/GameData.cs
  27. +1
    -1
      logic/Server/GameServer.cs
  28. +12
    -13
      logic/规则Logic.md

+ 2
- 0
logic/GameClass/GameObj/Bullet/BombedBullet.cs View File

@@ -9,9 +9,11 @@ namespace GameClass.GameObj
public override bool IsRigid => false; public override bool IsRigid => false;
public long MappingID { get; } public long MappingID { get; }
public Bullet bulletHasBombed; public Bullet bulletHasBombed;

public BombedBullet(Bullet bullet) : public BombedBullet(Bullet bullet) :
base(bullet.Position, bullet.Radius, GameObjType.BombedBullet) base(bullet.Position, bullet.Radius, GameObjType.BombedBullet)
{ {
this.place = bullet.Place;
this.bulletHasBombed = bullet; this.bulletHasBombed = bullet;
this.MappingID = bullet.ID; this.MappingID = bullet.ID;
this.FacingDirection = bullet.FacingDirection; this.FacingDirection = bullet.FacingDirection;


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

@@ -6,8 +6,8 @@ namespace GameClass.GameObj
{ {
internal sealed class CommonAttackOfGhost : Bullet internal sealed class CommonAttackOfGhost : Bullet
{ {
public CommonAttackOfGhost(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public CommonAttackOfGhost(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
@@ -28,8 +28,8 @@ namespace GameClass.GameObj
} }
internal sealed class FlyingKnife : Bullet internal sealed class FlyingKnife : Bullet
{ {
public FlyingKnife(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public FlyingKnife(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => 0; public override double BulletBombRange => 0;
@@ -51,8 +51,8 @@ namespace GameClass.GameObj


internal sealed class AtomBomb : Bullet internal sealed class AtomBomb : Bullet
{ {
public AtomBomb(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public AtomBomb(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 7; public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 7;
@@ -75,8 +75,8 @@ namespace GameClass.GameObj


internal sealed class OrdinaryBullet : Bullet // 1倍攻击范围,1倍攻击力,一倍速 internal sealed class OrdinaryBullet : Bullet // 1倍攻击范围,1倍攻击力,一倍速
{ {
public OrdinaryBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public OrdinaryBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 6 * 5; public override double BulletBombRange => GameData.basicBulletBombRange / 6 * 5;
@@ -98,8 +98,8 @@ namespace GameClass.GameObj


internal sealed class FastBullet : Bullet // 1倍攻击范围,0.2倍攻击力,2倍速 internal sealed class FastBullet : Bullet // 1倍攻击范围,0.2倍攻击力,2倍速
{ {
public FastBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public FastBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 4 * 2; public override double BulletBombRange => GameData.basicBulletBombRange / 4 * 2;
@@ -122,8 +122,8 @@ namespace GameClass.GameObj


internal sealed class LineBullet : Bullet // 直线爆炸,宽度1格,长度为2倍爆炸范围 internal sealed class LineBullet : Bullet // 直线爆炸,宽度1格,长度为2倍爆炸范围
{ {
public LineBullet(Character player, int radius = GameData.bulletRadius) :
base(player, radius)
public LineBullet(Character player, PlaceType placeType, XY pos, int radius = GameData.bulletRadius) :
base(player, radius, placeType, pos)
{ {
} }
public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 4; public override double BulletBombRange => GameData.basicBulletBombRange / 3 * 4;


+ 8
- 7
logic/GameClass/GameObj/Bullet/Bullet.cs View File

@@ -37,9 +37,10 @@ namespace GameClass.GameObj
return true; return true;
return false; return false;
} }
public Bullet(Character player, int radius) :
base(player.Position, radius, GameObjType.Bullet)
public Bullet(Character player, int radius, PlaceType placeType, XY Position) :
base(Position, radius, GameObjType.Bullet)
{ {
this.place = placeType;
this.CanMove = true; this.CanMove = true;
this.moveSpeed = this.Speed; this.moveSpeed = this.Speed;
this.hasSpear = player.HasSpear; this.hasSpear = player.HasSpear;
@@ -52,22 +53,22 @@ namespace GameClass.GameObj


public static class BulletFactory public static class BulletFactory
{ {
public static Bullet? GetBullet(Character character)
public static Bullet? GetBullet(Character character, PlaceType place, XY pos)
{ {
Bullet? newBullet = null; Bullet? newBullet = null;
switch (character.BulletOfPlayer) switch (character.BulletOfPlayer)
{ {
case BulletType.AtomBomb: case BulletType.AtomBomb:
newBullet = new AtomBomb(character);
newBullet = new AtomBomb(character, place, pos);
break; break;
case BulletType.LineBullet: case BulletType.LineBullet:
newBullet = new LineBullet(character);
newBullet = new LineBullet(character, place, pos);
break; break;
case BulletType.FastBullet: case BulletType.FastBullet:
newBullet = new FastBullet(character);
newBullet = new FastBullet(character, place, pos);
break; break;
case BulletType.OrdinaryBullet: case BulletType.OrdinaryBullet:
newBullet = new OrdinaryBullet(character);
newBullet = new OrdinaryBullet(character, place, pos);
break; break;
default: default:
break; break;


logic/GameClass/GameObj/Character/Character.SkillManager.cs → logic/GameClass/GameObj/Character/Character.Skill.cs View File

@@ -53,16 +53,13 @@ namespace GameClass.GameObj


public bool IsGhost() public bool IsGhost()
{ {
return this.CharacterType switch
{
CharacterType.Assassin => true,
_ => false,
};
return GameData.IsGhost(CharacterType);
} }


protected Character(XY initPos, int initRadius, CharacterType characterType) : protected Character(XY initPos, int initRadius, CharacterType characterType) :
base(initPos, initRadius, GameObjType.Character) base(initPos, initRadius, GameObjType.Character)
{ {
this.place = PlaceType.Null;
this.CanMove = true; this.CanMove = true;
this.score = 0; this.score = 0;
this.propInventory = null; this.propInventory = null;
@@ -88,6 +85,8 @@ namespace GameClass.GameObj
this.bulletNum = maxBulletNum; this.bulletNum = maxBulletNum;
this.bulletOfPlayer = Occupation.InitBullet; this.bulletOfPlayer = Occupation.InitBullet;
this.OriBulletOfPlayer = Occupation.InitBullet; this.OriBulletOfPlayer = Occupation.InitBullet;
this.concealment = Occupation.Concealment;
this.alertnessRadius = Occupation.AlertnessRadius;
this.characterType = characterType; this.characterType = characterType;


foreach (var activeSkill in this.Occupation.ListOfIActiveSkill) foreach (var activeSkill in this.Occupation.ListOfIActiveSkill)

+ 61
- 28
logic/GameClass/GameObj/Character/Character.cs View File

@@ -8,7 +8,7 @@ using System.Threading;


namespace GameClass.GameObj namespace GameClass.GameObj
{ {
public partial class Character : GameObj, ICharacter // 负责人LHR摆烂终了
public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了
{ {
private readonly object beAttackedLock = new(); private readonly object beAttackedLock = new();


@@ -175,24 +175,56 @@ namespace GameClass.GameObj
} }
} }


private Dictionary<BgmType, double> bgmDictionary = new();
public Dictionary<BgmType, double> BgmDictionary
{
get => bgmDictionary;
set
{
lock (gameObjLock)
{
bgmDictionary = value;
}
}
}

private int alertnessRadius;
public int AlertnessRadius
{
get => alertnessRadius;
set
{
lock (gameObjLock)
{
alertnessRadius = value;
}
}
}

private double concealment;
public double Concealment
{
get => concealment;
set
{
lock (gameObjLock)
{
concealment = value;
}
}
}

/// <summary> /// <summary>
/// 进行一次远程攻击
/// 进行一次攻击
/// </summary> /// </summary>
/// <param name="posOffset">子弹初始位置偏差值</param>
/// <returns>攻击操作发出的子弹</returns> /// <returns>攻击操作发出的子弹</returns>
public Bullet? RemoteAttack(XY posOffset)
public Bullet? Attack(XY pos, PlaceType place)
{ {
if (TrySubBulletNum()) if (TrySubBulletNum())
return ProduceOneBullet(this.Position + posOffset);
return BulletFactory.GetBullet(this, place, pos);
else else
return null; return null;
} }
protected Bullet? ProduceOneBullet(XY initPos)
{
var newBullet = BulletFactory.GetBullet(this);
newBullet?.SetPosition(initPos);
return newBullet;
}


/// <summary> /// <summary>
/// 尝试将子弹数量减1 /// 尝试将子弹数量减1
@@ -452,24 +484,24 @@ namespace GameClass.GameObj
} }
} }
#endregion #endregion
public override void Reset() // 要加锁吗?
{
lock (gameObjLock)
{
// _ = AddDeathCount();
base.Reset();
this.MoveSpeed = OrgMoveSpeed;
HP = MaxHp;
PropInventory = null;
BulletOfPlayer = OriBulletOfPlayer;
lock (gameObjLock)
bulletNum = maxBulletNum;
/* public override void Reset() // 要加锁吗?
{
lock (gameObjLock)
{
// _ = AddDeathCount();
base.Reset();
this.MoveSpeed = OrgMoveSpeed;
HP = MaxHp;
PropInventory = null;
BulletOfPlayer = OriBulletOfPlayer;
lock (gameObjLock)
bulletNum = maxBulletNum;


buffManager.ClearAll();
IsInvisible = false;
this.Vampire = this.OriVampire;
}
}
buffManager.ClearAll();
IsInvisible = false;
this.Vampire = this.OriVampire;
}
}*/
public void Die(PlayerStateType playerStateType) public void Die(PlayerStateType playerStateType)
{ {
lock (gameObjLock) lock (gameObjLock)
@@ -478,6 +510,7 @@ namespace GameClass.GameObj
CanMove = false; CanMove = false;
IsResetting = true; IsResetting = true;
Position = GameData.PosWhoDie; Position = GameData.PosWhoDie;
place = PlaceType.Grass;
} }
} }




+ 10
- 84
logic/GameClass/GameObj/GameObj.cs View File

@@ -7,13 +7,10 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR /// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR
/// </summary> /// </summary>
public abstract class GameObj : IMoveable
public abstract class GameObj : IGameObj
{ {
protected readonly object gameObjLock = new(); protected readonly object gameObjLock = new();
/// <summary>
/// 可移动物体专用锁
/// </summary>
public object MoveLock => gameObjLock;
public object GameLock => gameObjLock;


protected readonly XY birthPos; protected readonly XY birthPos;


@@ -38,7 +35,9 @@ namespace GameClass.GameObj
} }
} }
} }
public abstract bool IsRigid { get; }

protected PlaceType place;
public PlaceType Place { get => place; }


private XY facingDirection = new(1, 0); private XY facingDirection = new(1, 0);
public XY FacingDirection public XY FacingDirection
@@ -50,6 +49,9 @@ namespace GameClass.GameObj
facingDirection = value; facingDirection = value;
} }
} }

public abstract bool IsRigid { get; }

public abstract ShapeType Shape { get; } public abstract ShapeType Shape { get; }


private bool canMove; private bool canMove;
@@ -65,19 +67,6 @@ namespace GameClass.GameObj
} }
} }


private bool isMoving;
public bool IsMoving
{
get => isMoving;
set
{
lock (gameObjLock)
{
isMoving = value;
}
}
}

private bool isResetting; private bool isResetting;
public bool IsResetting public bool IsResetting
{ {
@@ -90,77 +79,14 @@ namespace GameClass.GameObj
} }
} }
} }
public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令
public int Radius { get; }


protected int moveSpeed;
/// <summary>
/// 移动速度
/// </summary>
public int MoveSpeed
{
get => moveSpeed;
set
{
lock (gameObjLock)
{
moveSpeed = value;
}
}
}
/// <summary>
/// 原初移动速度
/// </summary>
public int OrgMoveSpeed { get; protected set; }
public int Radius { get; }


// 移动,改变坐标
public long Move(XY moveVec)
{
lock (gameObjLock)
{
FacingDirection = moveVec;
this.Position += moveVec;
}
return (long)(moveVec * moveVec);
}
/// <summary>
/// 设置位置
/// </summary>
/// <param name="newpos">新位置</param>
public void SetPosition(XY newpos)
{
Position = newpos;
}
/// <summary>
/// 设置移动速度
/// </summary>
/// <param name="newMoveSpeed">新速度</param>
public void SetMoveSpeed(int newMoveSpeed)
{
MoveSpeed = newMoveSpeed;
}
/// <summary>
/// 复活时数据重置
/// </summary>
public virtual void Reset()
{
lock (gameObjLock)
{
facingDirection = new XY(1, 0);
isMoving = false;
canMove = false;
isResetting = true;
this.position = birthPos;
}
}
/// <summary>
/// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable
/// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现
/// </summary> /// </summary>
/// <returns> 依具体类及该方法参数而定,默认为false </returns> /// <returns> 依具体类及该方法参数而定,默认为false </returns>
protected virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; protected virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false;


bool IMoveable.IgnoreCollide(IGameObj targetObj) => IgnoreCollideExecutor(targetObj);
bool IGameObj.IgnoreCollide(IGameObj targetObj) => IgnoreCollideExecutor(targetObj);
public GameObj(XY initPos, int initRadius, GameObjType initType) public GameObj(XY initPos, int initRadius, GameObjType initType)
{ {
this.Position = this.birthPos = initPos; this.Position = this.birthPos = initPos;


+ 1
- 0
logic/GameClass/GameObj/Map/Doorway.cs View File

@@ -11,6 +11,7 @@ namespace GameClass.GameObj
public Doorway(XY initPos) : public Doorway(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway)
{ {
this.place = PlaceType.Doorway;
this.CanMove = false; this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;


+ 1
- 0
logic/GameClass/GameObj/Map/EmergencyExit.cs View File

@@ -11,6 +11,7 @@ namespace GameClass.GameObj
public EmergencyExit(XY initPos) : public EmergencyExit(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit)
{ {
this.place = PlaceType.EmergencyExit;
this.CanMove = false; this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;


+ 1
- 0
logic/GameClass/GameObj/Map/Generator.cs View File

@@ -10,6 +10,7 @@ namespace GameClass.GameObj
public Generator(XY initPos) : public Generator(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator)
{ {
this.place = PlaceType.Generator;
this.CanMove = false; this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;


+ 7
- 6
logic/GameClass/GameObj/Map/Map.cs View File

@@ -18,12 +18,13 @@ namespace GameClass.GameObj
private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict; private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict;
public Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict => gameObjLockDict; public Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict => gameObjLockDict;


public readonly uint[,] ProtoGameMap;
public PlaceType GetPlaceType(GameObj obj)
public readonly uint[,] protoGameMap;
public uint[,] ProtoGameMap => protoGameMap;
public PlaceType GetPlaceType(IGameObj obj)
{ {
try try
{ {
return (PlaceType)ProtoGameMap[obj.Position.x / GameData.numOfPosGridPerCell, obj.Position.y / GameData.numOfPosGridPerCell];
return (PlaceType)protoGameMap[obj.Position.x / GameData.numOfPosGridPerCell, obj.Position.y / GameData.numOfPosGridPerCell];
} }
catch catch
{ {
@@ -35,7 +36,7 @@ namespace GameClass.GameObj
{ {
try try
{ {
return (PlaceType)ProtoGameMap[pos.x / GameData.numOfPosGridPerCell, pos.y / GameData.numOfPosGridPerCell];
return (PlaceType)protoGameMap[pos.x / GameData.numOfPosGridPerCell, pos.y / GameData.numOfPosGridPerCell];
} }
catch catch
{ {
@@ -120,8 +121,8 @@ namespace GameClass.GameObj
} }
} }


ProtoGameMap = new uint[mapResource.GetLength(0), mapResource.GetLength(1)];
Array.Copy(mapResource, ProtoGameMap, mapResource.Length);
protoGameMap = new uint[mapResource.GetLength(0), mapResource.GetLength(1)];
Array.Copy(mapResource, protoGameMap, mapResource.Length);


birthPointList = new Dictionary<uint, XY>(GameData.numOfBirthPoint); birthPointList = new Dictionary<uint, XY>(GameData.numOfBirthPoint);




+ 1
- 0
logic/GameClass/GameObj/Map/Wall.cs View File

@@ -10,6 +10,7 @@ namespace GameClass.GameObj
public Wall(XY initPos) : public Wall(XY initPos) :
base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall) base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall)
{ {
this.place = PlaceType.Wall;
this.CanMove = false; this.CanMove = false;
} }
public override bool IsRigid => true; public override bool IsRigid => true;


+ 103
- 0
logic/GameClass/GameObj/Moveable.cs View File

@@ -0,0 +1,103 @@
using Preparation.Interface;
using Preparation.Utility;
using System.Threading;

namespace GameClass.GameObj
{
/// <summary>
/// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR
/// </summary>
public abstract class Moveable : GameObj, IMoveable
{
protected readonly object moveObjLock = new();
public object MoveLock => moveObjLock;

private bool isMoving;
public bool IsMoving
{
get => isMoving;
set
{
lock (gameObjLock)
{
isMoving = value;
}
}
}

public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令

protected int moveSpeed;
/// <summary>
/// 移动速度
/// </summary>
public int MoveSpeed
{
get => moveSpeed;
set
{
lock (gameObjLock)
{
moveSpeed = value;
}
}
}
/// <summary>
/// 原初移动速度
/// </summary>
public int OrgMoveSpeed { get; protected set; }

// 移动,改变坐标
public long MovingSetPos(XY moveVec, PlaceType place)
{
lock (gameObjLock)
{
FacingDirection = moveVec;
this.Position += moveVec;
this.place = place;
}
return moveVec * moveVec;
}

public void ReSetPos(XY pos, PlaceType place)
{
lock (gameObjLock)
{
this.Position = pos;
this.place = place;
}
}

/// <summary>
/// 设置移动速度
/// </summary>
/// <param name="newMoveSpeed">新速度</param>
public void SetMoveSpeed(int newMoveSpeed)
{
MoveSpeed = newMoveSpeed;
}
/* /// <summary>
/// 复活时数据重置
/// </summary>
public virtual void Reset(PlaceType place)
{
lock (gameObjLock)
{
this.FacingDirection = new XY(1, 0);
isMoving = false;
CanMove = false;
IsResetting = true;
this.Position = birthPos;
this.Place= place;
}
}*/
/// <summary>
/// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable
/// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现
/// </summary>
/// <returns> 依具体类及该方法参数而定,默认为false </returns>
public Moveable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType)
{
}
}
}

+ 1
- 1
logic/GameClass/GameObj/ObjOfCharacter.cs View File

@@ -6,7 +6,7 @@ namespace GameClass.GameObj
/// <summary> /// <summary>
/// 所有物,具有主人(Parent)(特定玩家)属性的对象 /// 所有物,具有主人(Parent)(特定玩家)属性的对象
/// </summary> /// </summary>
public abstract class ObjOfCharacter : GameObj, IObjOfCharacter
public abstract class ObjOfCharacter : Moveable, IObjOfCharacter
{ {
private ICharacter? parent = null; // 主人 private ICharacter? parent = null; // 主人
public ICharacter? Parent public ICharacter? Parent


+ 1
- 0
logic/GameClass/GameObj/OutOfBoundBlock.cs View File

@@ -11,6 +11,7 @@ namespace GameClass.GameObj
public OutOfBoundBlock(XY initPos) : public OutOfBoundBlock(XY initPos) :
base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock)
{ {
this.place = PlaceType.Wall;
this.CanMove = false; this.CanMove = false;
} }




+ 1
- 0
logic/GameClass/GameObj/PickedProp.cs View File

@@ -17,6 +17,7 @@ namespace GameClass.GameObj
public PickedProp(Prop prop) : public PickedProp(Prop prop) :
base(prop.Position, prop.Radius, GameObjType.PickedProp) base(prop.Position, prop.Radius, GameObjType.PickedProp)
{ {
this.place = prop.Place;
this.PropHasPicked = prop; this.PropHasPicked = prop;
this.MappingID = prop.ID; this.MappingID = prop.ID;
} }


+ 8
- 13
logic/GameClass/GameObj/Prop.cs View File

@@ -21,16 +21,13 @@ namespace GameClass.GameObj


public abstract PropType GetPropType(); public abstract PropType GetPropType();


public Prop(XY initPos, int radius = GameData.PropRadius) :
public Prop(XY initPos, PlaceType place, int radius = GameData.PropRadius) :
base(initPos, radius, GameObjType.Prop) base(initPos, radius, GameObjType.Prop)
{ {
this.place = place;
this.CanMove = false; this.CanMove = false;
this.moveSpeed = GameData.PropMoveSpeed; this.moveSpeed = GameData.PropMoveSpeed;
} }
public void SetNewPos(XY pos)
{
this.Position = pos;
}
} }




@@ -48,8 +45,8 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public sealed class AddSpeed : Prop public sealed class AddSpeed : Prop
{ {
public AddSpeed(XY initPos) :
base(initPos)
public AddSpeed(XY initPos, PlaceType placeType) :
base(initPos, placeType)
{ {
} }
public override PropType GetPropType() => PropType.addSpeed; public override PropType GetPropType() => PropType.addSpeed;
@@ -59,8 +56,8 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public sealed class AddLIFE : Prop public sealed class AddLIFE : Prop
{ {
public AddLIFE(XY initPos) :
base(initPos)
public AddLIFE(XY initPos, PlaceType placeType) :
base(initPos, placeType)
{ {
} }
public override PropType GetPropType() => PropType.addLIFE; public override PropType GetPropType() => PropType.addLIFE;
@@ -70,8 +67,7 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public sealed class Shield : Prop public sealed class Shield : Prop
{ {
public Shield(XY initPos) :
base(initPos)
public Shield(XY initPos, PlaceType placeType) : base(initPos, placeType)
{ {
} }
public override PropType GetPropType() => PropType.Shield; public override PropType GetPropType() => PropType.Shield;
@@ -81,8 +77,7 @@ namespace GameClass.GameObj
/// </summary> /// </summary>
public sealed class Spear : Prop public sealed class Spear : Prop
{ {
public Spear(XY initPos) :
base(initPos)
public Spear(XY initPos, PlaceType placeType) : base(initPos, placeType)
{ {
} }
public override PropType GetPropType() => PropType.Spear; public override PropType GetPropType() => PropType.Spear;


+ 32
- 12
logic/GameEngine/MoveEngine.cs View File

@@ -20,6 +20,18 @@ namespace GameEngine


private readonly ITimer gameTimer; private readonly ITimer gameTimer;
private readonly Action<IMoveable> EndMove; private readonly Action<IMoveable> EndMove;
public readonly uint[,] ProtoGameMap;
public PlaceType GetPlaceType(XY Position)
{
try
{
return (PlaceType)ProtoGameMap[Position.x / GameData.numOfPosGridPerCell, Position.y / GameData.numOfPosGridPerCell];
}
catch
{
return PlaceType.Null;
}
}
private readonly CollisionChecker collisionChecker; private readonly CollisionChecker collisionChecker;
private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision; private readonly Func<IMoveable, IGameObj, XY, AfterCollision> OnCollision;
/// <summary> /// <summary>
@@ -34,6 +46,7 @@ namespace GameEngine
Action<IMoveable> EndMove Action<IMoveable> EndMove
) )
{ {
this.ProtoGameMap = gameMap.ProtoGameMap;
this.gameTimer = gameMap.Timer; this.gameTimer = gameMap.Timer;
this.EndMove = EndMove; this.EndMove = EndMove;
this.OnCollision = OnCollision; this.OnCollision = OnCollision;
@@ -50,9 +63,11 @@ namespace GameEngine


/*由于四周是墙,所以人物永远不可能与越界方块碰撞*/ /*由于四周是墙,所以人物永远不可能与越界方块碰撞*/
XY nextPos = obj.Position + moveVec; XY nextPos = obj.Position + moveVec;
double maxLen = collisionChecker.FindMax(obj, nextPos, moveVec);
maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);
_ = obj.Move(new XY(moveVec.Angle(), maxLen));
//double maxLen
_ = collisionChecker.FindMax(obj, nextPos, moveVec);
//maxLen = Math.Min(maxLen, obj.MoveSpeed / GameData.numOfStepPerSecond);

obj.MovingSetPos(moveVec, GetPlaceType(nextPos));
} }


public void MoveObj(IMoveable obj, int moveTime, double direction) public void MoveObj(IMoveable obj, int moveTime, double direction)
@@ -69,7 +84,8 @@ namespace GameEngine
obj.IsMoving = true; obj.IsMoving = true;


double moveVecLength = 0.0; double moveVecLength = 0.0;
double deltaLen = moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength))); // 转向,并用deltaLen存储行走的误差
XY res = new XY(direction, moveVecLength);
double deltaLen = moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res))); // 转向,并用deltaLen存储行走的误差
IGameObj? collisionObj = null; IGameObj? collisionObj = null;
bool isDestroyed = false; bool isDestroyed = false;
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
@@ -77,17 +93,18 @@ namespace GameEngine
() => () =>
{ {
moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond; moveVecLength = obj.MoveSpeed / GameData.numOfStepPerSecond;
XY res = new XY(direction, moveVecLength);


// 越界情况处理:如果越界,则与越界方块碰撞 // 越界情况处理:如果越界,则与越界方块碰撞
bool flag; // 循环标志 bool flag; // 循环标志
do do
{ {
flag = false; flag = false;
collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength));
collisionObj = collisionChecker.CheckCollision(obj, res);
if (collisionObj == null) if (collisionObj == null)
break; break;


switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength)))
switch (OnCollision(obj, collisionObj, res))
{ {
case AfterCollision.ContinueCheck: case AfterCollision.ContinueCheck:
flag = true; flag = true;
@@ -97,13 +114,14 @@ namespace GameEngine
isDestroyed = true; isDestroyed = true;
return false; return false;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
MoveMax(obj, new XY(direction, moveVecLength));
MoveMax(obj, res);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength);
break; break;
} }
} while (flag); } while (flag);


deltaLen += moveVecLength - Math.Sqrt(obj.Move(new XY(direction, moveVecLength)));
deltaLen += moveVecLength - Math.Sqrt(obj.MovingSetPos(res, GetPlaceType(obj.Position + res)));


return true; return true;
}, },
@@ -118,13 +136,14 @@ namespace GameEngine
if (!isDestroyed) if (!isDestroyed)
{ {
moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell; moveVecLength = deltaLen + leftTime * obj.MoveSpeed / GameData.numOfPosGridPerCell;
if ((collisionObj = collisionChecker.CheckCollision(obj, new XY(direction, moveVecLength))) == null)
XY res = new XY(direction, moveVecLength);
if ((collisionObj = collisionChecker.CheckCollision(obj, res)) == null)
{ {
obj.Move(new XY(direction, moveVecLength));
obj.MovingSetPos(res, GetPlaceType(obj.Position + res));
} }
else else
{ {
switch (OnCollision(obj, collisionObj, new XY(direction, moveVecLength)))
switch (OnCollision(obj, collisionObj, res))
{ {
case AfterCollision.ContinueCheck: case AfterCollision.ContinueCheck:
flag = true; flag = true;
@@ -134,8 +153,9 @@ namespace GameEngine
isDestroyed = true; isDestroyed = true;
break; break;
case AfterCollision.MoveMax: case AfterCollision.MoveMax:
MoveMax(obj, new XY(direction, moveVecLength));
MoveMax(obj, res);
moveVecLength = 0; moveVecLength = 0;
res = new XY(direction, moveVecLength);
break; break;
} }
} }


+ 3
- 2
logic/Gaming/ActionManager.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using GameClass.GameObj; using GameClass.GameObj;
@@ -38,7 +39,7 @@ namespace Gaming


public bool Fix(Student player)// 自动检查有无发电机可修 public bool Fix(Student player)// 自动检查有无发电机可修
{ {
if (player.PlayerState != PlayerStateType.Null || player.IsGhost())
if (player.IsGhost() || (player.PlayerState != PlayerStateType.Null && player.PlayerState != PlayerStateType.IsMoving))
return false; return false;
Generator? generatorForFix = null; Generator? generatorForFix = null;


@@ -72,7 +73,7 @@ namespace Gaming
loopCondition: () => player.PlayerState == PlayerStateType.IsFixing && gameMap.Timer.IsGaming && generatorForFix.DegreeOfFRepair < GameData.degreeOfFixedGenerator && GameData.ApproachToInteract(player.Position, generatorForFix.Position), loopCondition: () => player.PlayerState == PlayerStateType.IsFixing && gameMap.Timer.IsGaming && generatorForFix.DegreeOfFRepair < GameData.degreeOfFixedGenerator && GameData.ApproachToInteract(player.Position, generatorForFix.Position),
loopToDo: () => loopToDo: () =>
{ {
return !generatorForFix.Repair(player.FixSpeed * GameData.frameDuration);
generatorForFix.Repair(player.FixSpeed * GameData.frameDuration);
}, },
timeInterval: GameData.frameDuration, timeInterval: GameData.frameDuration,
finallyReturn: () => 0 finallyReturn: () => 0


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

@@ -38,7 +38,7 @@ namespace Gaming
); );
} }


public void BeAddictedToGame(Student player)
private void BeAddictedToGame(Student player)
{ {
new Thread new Thread
(() => (() =>
@@ -68,11 +68,10 @@ namespace Gaming
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
} }


public void Die(Character player)
private void Die(Character player)
{ {


player.CanMove = false;
player.IsResetting = true;
player.Die(PlayerStateType.IsDeceased);
// gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); // gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock();
// try // try
//{ //{
@@ -87,7 +86,8 @@ namespace Gaming
if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地
{ {
dropProp = player.PropInventory; dropProp = player.PropInventory;
dropProp.SetNewPos(GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell));
XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell);
dropProp.ReSetPos(res, gameMap.GetPlaceType(res));
} }
gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock();
try try
@@ -298,12 +298,14 @@ namespace Gaming
if (player.PlayerState != PlayerStateType.Null || player.PlayerState != PlayerStateType.IsMoving) if (player.PlayerState != PlayerStateType.Null || player.PlayerState != PlayerStateType.IsMoving)
return false; return false;


Bullet? bullet = player.RemoteAttack(
new XY // 子弹紧贴人物生成。
XY res = new XY // 子弹紧贴人物生成。
( (
(int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle)), (int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle)),
(int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle)) (int)((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle))
)
);

Bullet? bullet = player.Attack(
res, gameMap.GetPlaceType(res)
); );
if (bullet.CastTime > 0) if (bullet.CastTime > 0)
{ {


+ 97
- 35
logic/Gaming/Game.cs View File

@@ -5,6 +5,7 @@ using Preparation.Utility;
using Timothy.FrameRateTask; using Timothy.FrameRateTask;
using Preparation.Interface; using Preparation.Interface;
using GameClass.GameObj; using GameClass.GameObj;
using System.Numerics;


namespace Gaming namespace Gaming
{ {
@@ -41,21 +42,14 @@ namespace Gaming
// Console.WriteLine($"x,y: {pos.x},{pos.y}"); // Console.WriteLine($"x,y: {pos.x},{pos.y}");


Character newPlayer = (GameData.IsGhost(playerInitInfo.characterType)) ? new Ghost(pos, GameData.characterRadius, playerInitInfo.characterType) : new Student(pos, GameData.characterRadius, playerInitInfo.characterType); Character newPlayer = (GameData.IsGhost(playerInitInfo.characterType)) ? new Ghost(pos, GameData.characterRadius, playerInitInfo.characterType) : new Student(pos, GameData.characterRadius, playerInitInfo.characterType);
gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock();
try
{
gameMap.GameObjDict[GameObjType.Character].Add(newPlayer);
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitWriteLock();
}
gameMap.Add(newPlayer);

// Console.WriteLine($"GameObjDict[GameObjType.Character] length:{gameMap.GameObjDict[GameObjType.Character].Count}"); // Console.WriteLine($"GameObjDict[GameObjType.Character] length:{gameMap.GameObjDict[GameObjType.Character].Count}");
teamList[(int)playerInitInfo.teamID].AddPlayer(newPlayer); teamList[(int)playerInitInfo.teamID].AddPlayer(newPlayer);
newPlayer.TeamID = playerInitInfo.teamID; newPlayer.TeamID = playerInitInfo.teamID;
newPlayer.PlayerID = playerInitInfo.playerID; newPlayer.PlayerID = playerInitInfo.playerID;
new Thread //人物装弹
#region 人物装弹
new Thread
( (
() => () =>
{ {
@@ -63,19 +57,16 @@ namespace Gaming
Thread.Sleep(newPlayer.CD); Thread.Sleep(newPlayer.CD);
long lastTime = Environment.TickCount64; long lastTime = Environment.TickCount64;
new FrameRateTaskExecutor<int>( new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming,
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopToDo: () => loopToDo: () =>
{ {
if (!newPlayer.IsResetting)
long nowTime = Environment.TickCount64;
if (newPlayer.BulletNum == newPlayer.MaxBulletNum)
lastTime = nowTime;
if (nowTime - lastTime >= newPlayer.CD)
{ {
long nowTime = Environment.TickCount64;
if (newPlayer.BulletNum == newPlayer.MaxBulletNum)
lastTime = nowTime;
if (nowTime - lastTime >= newPlayer.CD)
{
_ = newPlayer.TryAddBulletNum();
lastTime = nowTime;
}
_ = newPlayer.TryAddBulletNum();
lastTime = nowTime;
} }
}, },
timeInterval: GameData.checkInterval, timeInterval: GameData.checkInterval,
@@ -93,6 +84,91 @@ namespace Gaming
} }
) )
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
#endregion
#region BGM更新
new Thread
(
() =>
{
while (!gameMap.Timer.IsGaming)
Thread.Sleep((int)GameData.checkInterval);
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsResetting,
loopToDo: () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
if (newPlayer.IsGhost())
{
double bgmVolume = 0;
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
if (!person.IsGhost() && XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
{
if ((double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position) > bgmVolume)
bgmVolume = newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position);
}
}
if (bgmVolume > 0)
newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume);
}
else
{
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
if (person.IsGhost())
{
if (XY.Distance(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.BgmDictionary.Add(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.Distance(newPlayer.Position, person.Position));
break;
}
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}

gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock();
try
{
double bgmVolume = 0;
foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator])
{
if (XY.Distance(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius)
{
if ((double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position) > bgmVolume)
bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfFRepair / GameData.degreeOfFixedGenerator / XY.Distance(newPlayer.Position, generator.Position);
}
}
if (bgmVolume > 0)
newPlayer.BgmDictionary.Add(BgmType.StudentIsApproaching, bgmVolume);
}


finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
},
timeInterval: GameData.checkInterval,
finallyReturn: () => 0
)
{
AllowTimeExceed = true/*,
MaxTolerantTimeExceedCount = 5,
TimeExceedAction = exceedTooMuch =>
{
if (exceedTooMuch) Console.WriteLine("The computer runs too slow that it cannot check the color below the player in time!");
}*/
}
.Start();
}
)
{ IsBackground = true }.Start();
#endregion


return newPlayer.ID; return newPlayer.ID;
} }
@@ -100,20 +176,6 @@ namespace Gaming
{ {
if (gameMap.Timer.IsGaming) if (gameMap.Timer.IsGaming)
return false; return false;
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{
player.CanMove = true;

player.AddShield(GameData.shieldTimeAtBirth);
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}


propManager.StartProducing(); propManager.StartProducing();




+ 10
- 9
logic/Gaming/PropManager.cs View File

@@ -110,7 +110,8 @@ namespace Gaming
if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地 if (player.PropInventory != null) // 若角色原来有道具,则原始道具掉落在原地
{ {
dropProp = player.PropInventory; dropProp = player.PropInventory;
dropProp.SetNewPos(GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell));
XY res = GameData.GetCellCenterPos(player.Position.x / GameData.numOfPosGridPerCell, player.Position.y / GameData.numOfPosGridPerCell);
dropProp.ReSetPos(res, gameMap.GetPlaceType(res));
} }
player.PropInventory = pickProp; player.PropInventory = pickProp;
gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock();
@@ -151,7 +152,7 @@ namespace Gaming
return; return;


prop.CanMove = true; prop.CanMove = true;
prop.SetNewPos(player.Position);
prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position));
gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock(); gameMap.GameObjLockDict[GameObjType.Prop].EnterWriteLock();
try try
{ {
@@ -187,16 +188,16 @@ namespace Gaming
switch (r.Next(0, 4)) switch (r.Next(0, 4))
{ {
case 0: case 0:
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddLIFE(randPos));
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddLIFE(randPos, gameMap.GetPlaceType(randPos)));
break; break;
case 1: case 1:
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddSpeed(randPos));
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new AddSpeed(randPos, gameMap.GetPlaceType(randPos)));
break; break;
case 2: case 2:
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Shield(randPos));
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Shield(randPos, gameMap.GetPlaceType(randPos)));
break; break;
case 3: case 3:
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Spear(randPos));
gameMap.GameObjDict[GameObjType.Prop].Add((Preparation.Interface.IGameObj)new Spear(randPos, gameMap.GetPlaceType(randPos)));
break; break;
default: default:
break; break;
@@ -230,11 +231,11 @@ namespace Gaming
} }
); );
availableCellForGenerateProp = new List<XY>(); availableCellForGenerateProp = new List<XY>();
for (int i = 0; i < gameMap.ProtoGameMap.GetLength(0); i++)
for (int i = 0; i < gameMap.protoGameMap.GetLength(0); i++)
{ {
for (int j = 0; j < gameMap.ProtoGameMap.GetLength(1); j++)
for (int j = 0; j < gameMap.protoGameMap.GetLength(1); j++)
{ {
if (gameMap.ProtoGameMap[i, j] == (int)PlaceType.Null)
if (gameMap.protoGameMap[i, j] == (int)PlaceType.Null)
{ {
availableCellForGenerateProp.Add(GameData.GetCellCenterPos(i, j)); availableCellForGenerateProp.Add(GameData.GetCellCenterPos(i, j));
} }


+ 2
- 2
logic/Preparation/Interface/IGameObj.cs View File

@@ -7,13 +7,13 @@ namespace Preparation.Interface
public GameObjType Type { get; } public GameObjType Type { get; }
public long ID { get; } public long ID { get; }
public XY Position { get; } // if Square, Pos equals the center public XY Position { get; } // if Square, Pos equals the center
public PlaceType Place { get; }
public XY FacingDirection { get; } public XY FacingDirection { get; }
public bool IsRigid { get; } public bool IsRigid { get; }
public ShapeType Shape { get; } public ShapeType Shape { get; }
public bool CanMove { get; set; } public bool CanMove { get; set; }
public bool IsMoving { get; set; }
public bool IsResetting { get; set; } // reviving public bool IsResetting { get; set; } // reviving
public bool IsAvailable { get; }
public int Radius { get; } // if Square, Radius equals half length of one side public int Radius { get; } // if Square, Radius equals half length of one side
protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现
} }
} }

+ 2
- 0
logic/Preparation/Interface/IMap.cs View File

@@ -12,6 +12,8 @@ namespace Preparation.Interface
Dictionary<GameObjType, IList<IGameObj>> GameObjDict { get; } Dictionary<GameObjType, IList<IGameObj>> GameObjDict { get; }
Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict { get; } Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict { get; }


public uint[,] ProtoGameMap { get; }
public PlaceType GetPlaceType(IGameObj obj);
public bool IsOutOfBound(IGameObj obj); public bool IsOutOfBound(IGameObj obj);
public IOutOfBound GetOutOfBound(XY pos); // 返回新建的一个OutOfBound对象 public IOutOfBound GetOutOfBound(XY pos); // 返回新建的一个OutOfBound对象
} }


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

@@ -7,8 +7,10 @@ namespace Preparation.Interface
{ {
object MoveLock { get; } object MoveLock { get; }
public int MoveSpeed { get; } public int MoveSpeed { get; }
public long Move(XY moveVec);
protected bool IgnoreCollide(IGameObj targetObj); // 忽略碰撞,在具体类中实现
public bool IsMoving { get; set; }
public bool IsAvailable { get; }
public long MovingSetPos(XY moveVec, PlaceType place);
public void ReSetPos(XY pos, PlaceType place);
public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞
{ {
if (targetObj == null) if (targetObj == null)


+ 16
- 1
logic/Preparation/Interface/IOccupation.cs View File

@@ -12,6 +12,8 @@ namespace Preparation.Interface
public int MaxBulletNum { get; } public int MaxBulletNum { get; }
public List<ActiveSkillType> ListOfIActiveSkill { get; } public List<ActiveSkillType> ListOfIActiveSkill { get; }
public List<PassiveSkillType> ListOfIPassiveSkill { get; } public List<PassiveSkillType> ListOfIPassiveSkill { get; }
public double Concealment { get; }
public int AlertnessRadius { get; }
} }


public interface IGhost : IOccupation public interface IGhost : IOccupation
@@ -41,6 +43,12 @@ namespace Preparation.Interface


public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BecomeInvisible, ActiveSkillType.UseKnife }); public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BecomeInvisible, ActiveSkillType.UseKnife });
public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { });

public double concealment = GameData.basicConcealment * 1.5;
public double Concealment => concealment;

public int alertnessRadius = (int)(GameData.basicAlertnessRadius * 1.3);
public int AlertnessRadius => alertnessRadius;
} }
public class Athlete : IStudent public class Athlete : IStudent
{ {
@@ -61,6 +69,13 @@ namespace Preparation.Interface
public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge }); public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.BeginToCharge });
public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { });


public int FixSpeed => GameData.basicFixSpeed / 10 * 6;
public const int fixSpeed = GameData.basicFixSpeed / 10 * 6;
public int FixSpeed => fixSpeed;

public const double concealment = GameData.basicConcealment * 0.9;
public double Concealment => concealment;

public const int alertnessRadius = (int)(GameData.basicAlertnessRadius * 0.9);
public int AlertnessRadius => alertnessRadius;
} }
} }

+ 7
- 0
logic/Preparation/Utility/GameData.cs View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Threading;


namespace Preparation.Utility namespace Preparation.Utility
{ {
@@ -75,12 +76,15 @@ namespace Preparation.Utility
public const int basicMoveSpeed = 3800; // 基本移动速度,单位:s-1 public const int basicMoveSpeed = 3800; // 基本移动速度,单位:s-1
public const int basicBulletMoveSpeed = 5400; // 基本子弹移动速度,单位:s-1 public const int basicBulletMoveSpeed = 5400; // 基本子弹移动速度,单位:s-1
public const int characterMaxSpeed = 12000; // 最大速度 public const int characterMaxSpeed = 12000; // 最大速度
public const double basicConcealment = 1.0;
public const int basicAlertnessRadius = 30700;
public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分 public const int addScoreWhenKillOneLevelPlayer = 30; // 击杀一级角色获得的加分
public const int commonSkillCD = 30000; // 普通技能标准冷却时间 public const int commonSkillCD = 30000; // 普通技能标准冷却时间
public const int commonSkillTime = 10000; // 普通技能标准持续时间 public const int commonSkillTime = 10000; // 普通技能标准持续时间
public const int bulletRadius = 200; // 默认子弹半径 public const int bulletRadius = 200; // 默认子弹半径
public const int reviveTime = 30000; // 复活时间 public const int reviveTime = 30000; // 复活时间
public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间 public const int shieldTimeAtBirth = 3000; // 复活时的护盾时间

public static XY PosWhoDie = new XY(1, 1); public static XY PosWhoDie = new XY(1, 1);


public static bool IsGhost(CharacterType characterType) public static bool IsGhost(CharacterType characterType)
@@ -101,9 +105,12 @@ namespace Preparation.Utility
public const long GemProduceTime = 10000; public const long GemProduceTime = 10000;
public const long PropProduceTime = 10000; public const long PropProduceTime = 10000;
public const int PropDuration = 10000; public const int PropDuration = 10000;
#endregion


#region 物体相关
public const int degreeOfFixedGenerator = 10300000; public const int degreeOfFixedGenerator = 10300000;
#endregion #endregion

#region 游戏帧相关 #region 游戏帧相关
public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长 public const long checkInterval = 50; // 检查位置标志、补充子弹的帧时长
#endregion #endregion


+ 1
- 1
logic/Server/GameServer.cs View File

@@ -292,7 +292,7 @@ namespace Server


public override Task<MoveRes> Move(MoveMsg request, ServerCallContext context) public override Task<MoveRes> Move(MoveMsg request, ServerCallContext context)
{ {
Console.WriteLine($"Move ID: {request.PlayerId}, TimeInMilliseconds: {request.TimeInMilliseconds}");
Console.WriteLine($"SetPos ID: {request.PlayerId}, TimeInMilliseconds: {request.TimeInMilliseconds}");
var gameID = communicationToGameID[PlayerTypeToTeamID(request.PlayerType), request.PlayerId]; var gameID = communicationToGameID[PlayerTypeToTeamID(request.PlayerType), request.PlayerId];
game.MovePlayer(gameID, (int)request.TimeInMilliseconds, request.Angle); game.MovePlayer(gameID, (int)request.TimeInMilliseconds, request.Angle);
// 之后game.MovePlayer可能改为bool类型 // 之后game.MovePlayer可能改为bool类型


+ 12
- 13
logic/规则Logic.md View File

@@ -1,7 +1,7 @@
# 规则Logic # 规则Logic


## 说明 ## 说明
- 版本V1.003
- 版本V2.0
- 该规则直接服务于Sever,并非选手版本 - 该规则直接服务于Sever,并非选手版本
- *斜体表示Logic底层尚未(完全)实现* - *斜体表示Logic底层尚未(完全)实现*
- []表示待决定 - []表示待决定
@@ -23,13 +23,11 @@
- 底层实现中的属性,不代表界面全部都需要展示,也可能需要额外展示信息 - 底层实现中的属性,不代表界面全部都需要展示,也可能需要额外展示信息
- 只展示外部需要的属性,部分属性被省略 - 只展示外部需要的属性,部分属性被省略


### Bgm
- *double bgmVolume*
- *BgmType bgmType*
对于枚举类BgmType
1. *不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离)*
2. *期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量2.越大。bgmVolume=(警戒半径/二者距离)*
3. *修理电机的声音: 警戒半径内有电机正在被修理时,全员收到;bgmVolume=(警戒半径/二者距离)*电机修理程度/10300000*
### BgmType
- 枚举类BgmType
1. 不详的感觉:监管者进入(求生者的警戒半径/监管者的隐蔽度)时,求生者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/二者距离)
2. 期待搞事的感觉:求生者进入(监管者的警戒半径/求生者的隐蔽度)时,监管者收到;监管者距离求生者越近,Bgm音量越大。bgmVolume=(警戒半径/可被发觉的最近的求生者距离)
3. 修理电机的声音: 警戒半径内有电机正在被修理时收到;bgmVolume=(警戒半径*电机修理程度/二者距离)/10300000
~~~csharp ~~~csharp
public enum BgmType public enum BgmType
{ {
@@ -58,6 +56,7 @@


### 物体 ### 物体
- 位置 - 位置
- 位置地形
- ID - ID
- 类型 - 类型
- 面向角度 - 面向角度
@@ -95,20 +94,20 @@
IsClimbingThroughWindows = 15, IsClimbingThroughWindows = 15,
} }
~~~ ~~~
- *Bgm(数组)*
- *Bgm(字典)*
- 得分 - 得分
- ~~回血率/原始回血率~~ - ~~回血率/原始回血率~~
- 当前子弹类型 - 当前子弹类型
- 原始子弹类型 - 原始子弹类型
- 持有道具*(最多三个)(数组)*
- 持有道具 *(最多三个)(列表)*
- 是否隐身 - 是否隐身
- 队伍ID - 队伍ID
- 玩家ID - 玩家ID
- 当前Buff - 当前Buff
- 职业类型 - 职业类型
- 拥有的被动技能(数组)
- 拥有的主动技能(数组)
- 各个主动技能CD(数组)
- 拥有的被动技能(列表)
- 拥有的主动技能(列表)
- 各个主动技能CD(字典)
- *警戒半径* - *警戒半径*
- *double 隐蔽度* - *double 隐蔽度*
- *翻墙速度* - *翻墙速度*


Loading…
Cancel
Save