| @@ -277,7 +277,12 @@ $$ | |||
| - 主动技能 | |||
| 0. 惩罚 Punish | |||
| - CD:45s | |||
| - “使用瞬间,在视野距离/3范围内(不是可视范围)的翻窗、开锁门、攻击前后摇、使用技能期间的捣蛋鬼会被眩晕(3070+$\frac{500×已受伤害}{基本伤害(1500000)×2^{开局老师数量-1}}$)ms” | |||
| - 使用瞬间,在视野距离/3范围内(不是可视范围)的翻窗、开锁门、攻击前后摇、使用技能期间的捣蛋鬼会被眩晕 | |||
| $$ | |||
| (3070+\frac{500×已受伤害}{基本伤害(1500000)×2^{开局老师数量-1}})ms | |||
| $$ | |||
| - 其眩晕得分为正常眩晕得分/2^开局老师数量-1^ | |||
| 1. 喝茶 HaveTea | |||
| - CD:90s | |||
| @@ -8,7 +8,8 @@ namespace GameClass.GameObj | |||
| public override ShapeType Shape => ShapeType.Circle; | |||
| public override bool IsRigid => false; | |||
| public long MappingID { get; } | |||
| public Bullet bulletHasBombed; | |||
| public readonly Bullet bulletHasBombed; | |||
| public readonly XY facingDirection; | |||
| public BombedBullet(Bullet bullet) : | |||
| base(bullet.Position, bullet.Radius, GameObjType.BombedBullet) | |||
| @@ -36,7 +36,7 @@ namespace GameClass.GameObj | |||
| this.buffManager = new BuffManager(); | |||
| this.occupation = OccupationFactory.FindIOccupation(characterType); | |||
| this.MaxHp = this.hp = Occupation.MaxHp; | |||
| this.MoveSpeed = this.OrgMoveSpeed = Occupation.MoveSpeed; | |||
| this.MoveSpeed = this.orgMoveSpeed = Occupation.MoveSpeed; | |||
| this.BulletOfPlayer = this.OriBulletOfPlayer = Occupation.InitBullet; | |||
| this.concealment = Occupation.Concealment; | |||
| this.alertnessRadius = Occupation.AlertnessRadius; | |||
| @@ -50,9 +50,6 @@ namespace GameClass.GameObj | |||
| { | |||
| this.ActiveSkillDictionary.Add(activeSkill, SkillFactory.FindActiveSkill(activeSkill)); | |||
| } | |||
| // UsePassiveSkill(); //这一过程放到gamestart时进行 | |||
| Debugger.Output(this, "constructed!"); | |||
| } | |||
| } | |||
| @@ -32,83 +32,60 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| protected readonly int orgFixSpeed; | |||
| private readonly object treatLock = new(); | |||
| protected int treatSpeed = GameData.basicTreatSpeed; | |||
| public int TreatSpeed | |||
| { | |||
| get => treatSpeed; | |||
| get | |||
| { | |||
| lock (treatLock) | |||
| return treatSpeed; | |||
| } | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| lock (treatLock) | |||
| { | |||
| treatSpeed = value; | |||
| } | |||
| } | |||
| } | |||
| public int OrgTreatSpeed { get; protected set; } | |||
| protected readonly int orgTreatSpeed; | |||
| public int MaxGamingAddiction { get; protected set; } | |||
| private int gamingAddiction; | |||
| public int GamingAddiction | |||
| private readonly object addictionLock = new(); | |||
| private int maxGamingAddiction; | |||
| public int MaxGamingAddiction | |||
| { | |||
| get => gamingAddiction; | |||
| set | |||
| get | |||
| { | |||
| if (value > 0) | |||
| lock (gameObjLock) | |||
| gamingAddiction = value <= MaxGamingAddiction ? value : MaxGamingAddiction; | |||
| else | |||
| lock (gameObjLock) | |||
| gamingAddiction = 0; | |||
| lock (addictionLock) | |||
| return maxGamingAddiction; | |||
| } | |||
| } | |||
| private int selfHealingTimes = 1;//剩余的自愈次数 | |||
| public int SelfHealingTimes | |||
| { | |||
| get => selfHealingTimes; | |||
| set | |||
| protected set | |||
| { | |||
| lock (gameObjLock) | |||
| selfHealingTimes = (value > 0) ? value : 0; | |||
| } | |||
| } | |||
| private int degreeOfTreatment = 0; | |||
| public int DegreeOfTreatment | |||
| { | |||
| get => degreeOfTreatment; | |||
| private set | |||
| { | |||
| degreeOfTreatment = value; | |||
| lock (addictionLock) | |||
| { | |||
| if (value < gamingAddiction) gamingAddiction = value; | |||
| maxGamingAddiction = value; | |||
| } | |||
| } | |||
| } | |||
| public void SetDegreeOfTreatment0() | |||
| { | |||
| DegreeOfTreatment = 0; | |||
| } | |||
| public bool SetDegreeOfTreatment(int value, Student whoTreatYou) | |||
| private int gamingAddiction; | |||
| public int GamingAddiction | |||
| { | |||
| if (value <= 0) { degreeOfTreatment = 0; return false; } | |||
| if (value >= MaxHp - HP) | |||
| get | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(MaxHp - HP)); | |||
| HP = MaxHp; | |||
| degreeOfTreatment = 0; | |||
| return true; | |||
| lock (addictionLock) | |||
| return gamingAddiction; | |||
| } | |||
| if (value >= GameData.basicTreatmentDegree) | |||
| set | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(GameData.basicTreatmentDegree)); | |||
| HP += GameData.basicTreatmentDegree; | |||
| DegreeOfTreatment = 0; | |||
| return true; | |||
| if (value > 0) | |||
| lock (addictionLock) | |||
| gamingAddiction = value <= maxGamingAddiction ? value : maxGamingAddiction; | |||
| else | |||
| lock (addictionLock) | |||
| gamingAddiction = 0; | |||
| } | |||
| DegreeOfTreatment = value; | |||
| return false; | |||
| } | |||
| public bool AddDegreeOfTreatment(int value, Student student) | |||
| { | |||
| return SetDegreeOfTreatment(value + degreeOfTreatment, student); | |||
| } | |||
| private int timeOfRescue = 0; | |||
| @@ -128,19 +105,24 @@ namespace GameClass.GameObj | |||
| public Student(XY initPos, int initRadius, CharacterType characterType) : base(initPos, initRadius, characterType) | |||
| { | |||
| this.orgFixSpeed = this.fixSpeed = ((IStudentType)Occupation).FixSpeed; | |||
| this.TreatSpeed = this.OrgTreatSpeed = ((IStudentType)Occupation).TreatSpeed; | |||
| this.TreatSpeed = this.orgTreatSpeed = ((IStudentType)Occupation).TreatSpeed; | |||
| this.MaxGamingAddiction = ((IStudentType)Occupation).MaxGamingAddiction; | |||
| } | |||
| } | |||
| public class Golem : Student, IGolem | |||
| { | |||
| private readonly object parentLock = new(); | |||
| private Character? parent; // 主人 | |||
| public Character? Parent | |||
| { | |||
| get => parent; | |||
| get | |||
| { | |||
| lock (parentLock) | |||
| return parent; | |||
| } | |||
| set | |||
| { | |||
| lock (gameObjLock) | |||
| lock (parentLock) | |||
| { | |||
| parent = value; | |||
| } | |||
| @@ -8,15 +8,8 @@ namespace GameClass.GameObj | |||
| { | |||
| public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了 | |||
| { | |||
| private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; | |||
| private readonly object vampireLock = new(); | |||
| public object VampireLock => vampire; | |||
| private readonly object inventoryLock = new(); | |||
| public object InventoryLock => inventoryLock; | |||
| #region 装弹、攻击相关的基本属性及方法 | |||
| private readonly object attackLock = new(); | |||
| /// <summary> | |||
| /// 装弹冷却 | |||
| /// </summary> | |||
| @@ -25,7 +18,7 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| return cd; | |||
| } | |||
| @@ -36,7 +29,7 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| return orgCD; | |||
| } | |||
| } | |||
| @@ -47,14 +40,14 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| return bulletOfPlayer; | |||
| } | |||
| } | |||
| set | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| bulletOfPlayer = value; | |||
| cd = orgCD = (BulletFactory.BulletCD(value)); | |||
| @@ -69,7 +62,7 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| return maxBulletNum; | |||
| } | |||
| @@ -78,17 +71,17 @@ namespace GameClass.GameObj | |||
| private int bulletNum; | |||
| private int updateTimeOfBulletNum = 0; | |||
| public int UpdateBulletNum(int time) | |||
| public int UpdateBulletNum(int time)//通过该函数获取真正的bulletNum | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| if (bulletNum < maxBulletNum) | |||
| if (bulletNum < maxBulletNum && time - updateTimeOfBulletNum >= cd) | |||
| { | |||
| int add = Math.Min(maxBulletNum - bulletNum, (time - updateTimeOfBulletNum) / cd); | |||
| updateTimeOfBulletNum += add * cd; | |||
| return (bulletNum += add); | |||
| } | |||
| return maxBulletNum; | |||
| return bulletNum; | |||
| } | |||
| } | |||
| @@ -98,7 +91,7 @@ namespace GameClass.GameObj | |||
| /// <returns>攻击操作发出的子弹</returns> | |||
| public Bullet? Attack(double angle, int time) | |||
| { | |||
| lock (actionLock) | |||
| lock (attackLock) | |||
| { | |||
| if (bulletOfPlayer == BulletType.Null) | |||
| return null; | |||
| @@ -115,7 +108,7 @@ namespace GameClass.GameObj | |||
| Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer); | |||
| if (bullet == null) return null; | |||
| if (TryAddAp()) bullet.AddAP(GameData.ApPropAdd); | |||
| facingDirection = new(angle, bullet.AttackDistance); | |||
| FacingDirection = new(angle, bullet.AttackDistance); | |||
| return bullet; | |||
| } | |||
| else | |||
| @@ -140,7 +133,7 @@ namespace GameClass.GameObj | |||
| if (!(bouncer?.TeamID == this.TeamID)) | |||
| { | |||
| if (hasSpear || !HasShield) | |||
| _ = TrySubHp(subHP); | |||
| _ = SubHp(subHP); | |||
| if (hp <= 0) | |||
| TryActivatingLIFE(); | |||
| } | |||
| @@ -194,6 +187,9 @@ namespace GameClass.GameObj | |||
| } | |||
| #endregion | |||
| #region 血量相关的基本属性及方法 | |||
| private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; | |||
| private long maxHp; | |||
| public long MaxHp | |||
| { | |||
| @@ -222,7 +218,8 @@ namespace GameClass.GameObj | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } // 最大血量 | |||
| } | |||
| // 最大血量 | |||
| protected long hp; | |||
| public long HP | |||
| { | |||
| @@ -238,22 +235,23 @@ namespace GameClass.GameObj | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| set | |||
| } | |||
| public long SetHP(long value) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (value > 0) | |||
| { | |||
| hp = value <= maxHp ? value : maxHp; | |||
| } | |||
| else | |||
| hp = 0; | |||
| } | |||
| finally | |||
| if (value > 0) | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| return hp = value <= maxHp ? value : maxHp; | |||
| } | |||
| else | |||
| return hp = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| @@ -261,7 +259,7 @@ namespace GameClass.GameObj | |||
| /// 尝试减血 | |||
| /// </summary> | |||
| /// <param name="sub">减血量</param> | |||
| public long TrySubHp(long sub) | |||
| public long SubHp(long sub) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| @@ -284,6 +282,23 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public long AddHP(long add) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| long previousHp = hp; | |||
| return (hp = (hp + add > maxHp) ? maxHp : hp + add) - previousHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| private readonly object vampireLock = new(); | |||
| public object VampireLock => vampire; | |||
| private double vampire = 0; // 回血率:0-1之间 | |||
| public double Vampire | |||
| { | |||
| @@ -306,8 +321,65 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public double OriVampire { get; protected set; } | |||
| private readonly object treatLock = new(); | |||
| private int degreeOfTreatment = 0; | |||
| public int DegreeOfTreatment | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return degreeOfTreatment; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| public void SetDegreeOfTreatment0() | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| degreeOfTreatment = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| public bool AddDegreeOfTreatment(int value, Student whoTreatYou) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (value >= maxHp - hp) | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(maxHp - hp)); | |||
| hp = maxHp; | |||
| degreeOfTreatment = 0; | |||
| return true; | |||
| } | |||
| if (value >= GameData.basicTreatmentDegree) | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(GameData.basicTreatmentDegree)); | |||
| hp += GameData.basicTreatmentDegree; | |||
| degreeOfTreatment = 0; | |||
| return true; | |||
| } | |||
| degreeOfTreatment = value; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| return false; | |||
| } | |||
| #endregion | |||
| #region 状态相关的基本属性与方法 | |||
| #region 查询状态相关的基本属性与方法 | |||
| private PlayerStateType playerState = PlayerStateType.Null; | |||
| public PlayerStateType PlayerState | |||
| { | |||
| @@ -371,6 +443,8 @@ namespace GameClass.GameObj | |||
| || playerState == PlayerStateType.Stunned || playerState == PlayerStateType.Charmed | |||
| || playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving); | |||
| } | |||
| #endregion | |||
| #region 更改状态相关的属性和方法 | |||
| private GameObj? whatInteractingWith = null; | |||
| public GameObj? WhatInteractingWith | |||
| { | |||
| @@ -383,19 +457,6 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public bool StartThread(long stateNum, RunningStateType runningState) | |||
| { | |||
| lock (ActionLock) | |||
| { | |||
| if (this.StateNum == stateNum) | |||
| { | |||
| this.runningState = runningState; | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| private long ChangePlayerState(RunningStateType running, PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| @@ -492,13 +553,7 @@ namespace GameClass.GameObj | |||
| if (value == PlayerStateType.Rescued) return -1; | |||
| Door door = (Door)lastObj!; | |||
| door.StopOpen(); | |||
| ReleaseTool(door.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| } | |||
| ); | |||
| ReleaseTool(door.KeyType); | |||
| return ChangePlayerState(runningState, value, gameObj); | |||
| case PlayerStateType.UsingSkill: | |||
| { | |||
| @@ -573,6 +628,19 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public bool StartThread(long stateNum, RunningStateType runningState) | |||
| { | |||
| lock (ActionLock) | |||
| { | |||
| if (this.StateNum == stateNum) | |||
| { | |||
| this.runningState = runningState; | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| public bool TryToRemoveFromGame(PlayerStateType playerStateType) | |||
| { | |||
| lock (actionLock) | |||
| @@ -619,6 +687,9 @@ namespace GameClass.GameObj | |||
| } | |||
| #region 道具和buff相关属性、方法 | |||
| private readonly object inventoryLock = new(); | |||
| public object InventoryLock => inventoryLock; | |||
| private Gadget[] propInventory = new Gadget[GameData.maxNumOfPropInPropInventory] | |||
| {new NullProp(), new NullProp(),new NullProp() }; | |||
| public Gadget[] PropInventory | |||
| @@ -639,7 +710,7 @@ namespace GameClass.GameObj | |||
| /// 使用物品栏中的道具 | |||
| /// </summary> | |||
| /// <returns>被使用的道具</returns> | |||
| public Gadget UseProp(int indexing) | |||
| public Gadget ConsumeProp(int indexing) | |||
| { | |||
| if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory) | |||
| return new NullProp(); | |||
| @@ -652,7 +723,7 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public Gadget UseProp(PropType propType) | |||
| public Gadget ConsumeProp(PropType propType) | |||
| { | |||
| if (propType == PropType.Null) | |||
| { | |||
| @@ -687,7 +758,7 @@ namespace GameClass.GameObj | |||
| return new NullProp(); | |||
| } | |||
| public bool UseTool(PropType propType) | |||
| public bool UseTool(PropType propType)//占用道具,使其不能重复使用和被消耗 | |||
| { | |||
| lock (inventoryLock) | |||
| { | |||
| @@ -26,9 +26,6 @@ namespace GameClass.GameObj | |||
| protected XY position; | |||
| public abstract XY Position { get; } | |||
| protected XY facingDirection = new(1, 0); | |||
| public abstract XY FacingDirection { get; } | |||
| public abstract bool CanMove { get; } | |||
| public abstract bool IsRigid { get; } | |||
| @@ -7,8 +7,6 @@ namespace GameClass.GameObj | |||
| public override XY Position => position; | |||
| public override XY FacingDirection => facingDirection; | |||
| public override bool CanMove => false; | |||
| public Immovable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) | |||
| @@ -13,23 +13,16 @@ namespace GameClass.GameObj | |||
| public Door(XY initPos, PlaceType placeType) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Door) | |||
| { | |||
| switch (placeType) | |||
| keyType = placeType switch | |||
| { | |||
| case PlaceType.Door3: | |||
| doorNum = 3; | |||
| break; | |||
| case PlaceType.Door5: | |||
| doorNum = 5; | |||
| break; | |||
| case PlaceType.Door6: | |||
| default: | |||
| doorNum = 6; | |||
| break; | |||
| } | |||
| PlaceType.Door3 => PropType.Key3, | |||
| PlaceType.Door5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }; | |||
| } | |||
| private readonly int doorNum; | |||
| public int DoorNum => doorNum; | |||
| private readonly PropType keyType; | |||
| public PropType KeyType => keyType; | |||
| public override bool IsRigid => !isOpen; | |||
| public override ShapeType Shape => ShapeType.Square; | |||
| @@ -57,17 +50,11 @@ namespace GameClass.GameObj | |||
| private int lockDegree = 0; | |||
| public int LockDegree | |||
| { | |||
| get | |||
| { | |||
| lock (gameObjLock) | |||
| return lockDegree; | |||
| } | |||
| set | |||
| { | |||
| value = (value > GameData.degreeOfLockingOrOpeningTheDoor) ? GameData.degreeOfLockingOrOpeningTheDoor : value; | |||
| lock (gameObjLock) | |||
| lockDegree = value; | |||
| } | |||
| get => Interlocked.CompareExchange(ref lockDegree, -1, -1); | |||
| } | |||
| public int AddLockDegree(int add) | |||
| { | |||
| return Interlocked.Add(ref lockDegree, add); | |||
| } | |||
| private long openStartTime = 0; | |||
| @@ -119,7 +106,7 @@ namespace GameClass.GameObj | |||
| { | |||
| if (!isOpen) return false; | |||
| if (whoLockOrOpen != null) return false; | |||
| lockDegree = 0; | |||
| Interlocked.Exchange(ref lockDegree, 0); | |||
| whoLockOrOpen = character; | |||
| return true; | |||
| } | |||
| @@ -128,7 +115,7 @@ namespace GameClass.GameObj | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| if (LockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| isOpen = false; | |||
| whoLockOrOpen = null; | |||
| } | |||
| @@ -157,12 +144,7 @@ namespace GameClass.GameObj | |||
| { | |||
| if (character.PlayerState == PlayerStateType.OpeningTheDoor) | |||
| { | |||
| character.ReleaseTool(DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }); | |||
| character.ReleaseTool(KeyType); | |||
| character.SetPlayerStateNaturally(); | |||
| } | |||
| else if (character.PlayerState == PlayerStateType.LockingTheDoor) | |||
| @@ -1,5 +1,4 @@ | |||
| using Google.Protobuf.WellKnownTypes; | |||
| using Preparation.Interface; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| @@ -8,7 +8,7 @@ namespace GameClass.GameObj | |||
| { | |||
| protected readonly object actionLock = new(); | |||
| public object ActionLock => actionLock; | |||
| //player.actionLock>其他.actionLock | |||
| //player.actionLock>其他.actionLock/其他Lock,应当避免两个player的actionlock互锁 | |||
| private readonly ReaderWriterLockSlim moveReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; | |||
| //规定moveReaderWriterLock<actionLock | |||
| @@ -52,13 +52,19 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public override XY FacingDirection | |||
| protected XY facingDirection = new(1, 0); | |||
| public XY FacingDirection | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| return facingDirection; | |||
| } | |||
| set | |||
| { | |||
| lock (actionLock) | |||
| facingDirection = value; | |||
| } | |||
| } | |||
| private int isMoving = 0; | |||
| @@ -127,7 +133,8 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| /// </summary> | |||
| public int OrgMoveSpeed { get; protected set; } | |||
| protected int orgMoveSpeed; | |||
| public int OrgMoveSpeed => orgMoveSpeed; | |||
| /* /// <summary> | |||
| /// 复活时数据重置 | |||
| @@ -321,7 +321,7 @@ namespace Gaming | |||
| if (playerRescued.AddTimeOfRescue(GameData.checkInterval)) | |||
| { | |||
| playerRescued.SetPlayerStateNaturally(); | |||
| playerRescued.HP = playerRescued.MaxHp / 2; | |||
| playerRescued.SetHP(playerRescued.MaxHp / 2); | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| return false; | |||
| } | |||
| @@ -479,19 +479,12 @@ namespace Gaming | |||
| Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| if (doorToLock == null) return false; | |||
| PropType propType = doorToLock.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }; | |||
| if (!player.UseTool(propType)) return false; | |||
| if (!player.UseTool(doorToLock.KeyType)) return false; | |||
| long stateNum = player.SetPlayerState(RunningStateType.Waiting, PlayerStateType.LockingTheDoor, doorToLock); | |||
| if (stateNum == -1) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToLock.KeyType); | |||
| return false; | |||
| } | |||
| @@ -502,25 +495,26 @@ namespace Gaming | |||
| player.ThreadNum.WaitOne(); | |||
| if (!player.StartThread(stateNum, RunningStateType.RunningActively)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToLock.KeyType); | |||
| player.ThreadNum.Release(); | |||
| return; | |||
| } | |||
| if (!doorToLock.TryLock(player)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToLock.KeyType); | |||
| player.ResetPlayerState(stateNum); | |||
| player.ThreadNum.Release(); | |||
| return; | |||
| } | |||
| Thread.Sleep(GameData.checkInterval); | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => stateNum == player.StateNum && gameMap.Timer.IsGaming && doorToLock.LockDegree < GameData.degreeOfLockingOrOpeningTheDoor, | |||
| loopCondition: () => stateNum == player.StateNum && gameMap.Timer.IsGaming, | |||
| loopToDo: () => | |||
| { | |||
| if ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) != null) | |||
| return false; | |||
| doorToLock.LockDegree += GameData.checkInterval * player.SpeedOfOpeningOrLocking; | |||
| if (doorToLock.AddLockDegree(GameData.checkInterval * player.SpeedOfOpeningOrLocking) >= GameData.basicSpeedOfOpeningOrLocking) | |||
| return false; | |||
| return true; | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| @@ -528,7 +522,7 @@ namespace Gaming | |||
| ) | |||
| .Start(); | |||
| doorToLock.StopLock(); | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToLock.KeyType); | |||
| player.ThreadNum.Release(); | |||
| player.ResetPlayerState(stateNum); | |||
| } | |||
| @@ -541,22 +535,15 @@ namespace Gaming | |||
| public bool OpenDoor(Character player) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot) return false; | |||
| Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| if (doorToLock == null) return false; | |||
| PropType propType = doorToLock.DoorNum switch | |||
| { | |||
| 3 => PropType.Key3, | |||
| 5 => PropType.Key5, | |||
| _ => PropType.Key6, | |||
| }; | |||
| Door? doorToOpen = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door); | |||
| if (doorToOpen == null) return false; | |||
| if (!player.UseTool(propType)) return false; | |||
| if (!player.UseTool(doorToOpen.KeyType)) return false; | |||
| long stateNum = player.SetPlayerState(RunningStateType.Waiting, PlayerStateType.OpeningTheDoor, doorToLock); | |||
| long stateNum = player.SetPlayerState(RunningStateType.Waiting, PlayerStateType.OpeningTheDoor, doorToOpen); | |||
| if (stateNum == -1) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToOpen.KeyType); | |||
| return false; | |||
| } | |||
| @@ -567,13 +554,13 @@ namespace Gaming | |||
| player.ThreadNum.WaitOne(); | |||
| if (!player.StartThread(stateNum, RunningStateType.RunningSleepily)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToOpen.KeyType); | |||
| player.ThreadNum.Release(); | |||
| return; | |||
| } | |||
| if (!doorToLock.TryOpen(player)) | |||
| if (!doorToOpen.TryOpen(player)) | |||
| { | |||
| player.ReleaseTool(propType); | |||
| player.ReleaseTool(doorToOpen.KeyType); | |||
| if (player.ResetPlayerState(stateNum)) | |||
| player.ThreadNum.Release(); | |||
| return; | |||
| @@ -582,8 +569,8 @@ namespace Gaming | |||
| if (player.ResetPlayerState(stateNum)) | |||
| { | |||
| doorToLock.StopOpen(); | |||
| player.ReleaseTool(propType); | |||
| doorToOpen.StopOpen(); | |||
| player.ReleaseTool(doorToOpen.KeyType); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| @@ -326,12 +326,12 @@ namespace Gaming | |||
| { | |||
| if (bullet.HasSpear) | |||
| { | |||
| long subHp = student.TrySubHp(bullet.AP); | |||
| long subHp = student.SubHp(bullet.AP); | |||
| #if DEBUG | |||
| Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); | |||
| #endif | |||
| bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp) + GameData.ScorePropUseSpear); | |||
| bullet.Parent.HP = (long)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | |||
| bullet.Parent.AddHP((long)bullet.Parent.Vampire * subHp); | |||
| } | |||
| else return; | |||
| } | |||
| @@ -340,14 +340,14 @@ namespace Gaming | |||
| long subHp; | |||
| if (bullet.HasSpear) | |||
| { | |||
| subHp = student.TrySubHp(bullet.AP + GameData.ApSpearAdd); | |||
| subHp = student.SubHp(bullet.AP + GameData.ApSpearAdd); | |||
| #if DEBUG | |||
| Debugger.Output(this, "is being shot with Spear! Now his hp is" + student.HP.ToString()); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| subHp = student.TrySubHp(bullet.AP); | |||
| subHp = student.SubHp(bullet.AP); | |||
| #if DEBUG | |||
| Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); | |||
| #endif | |||
| @@ -358,7 +358,7 @@ namespace Gaming | |||
| student.AddScore(subHp * GameData.factorOfScoreWhenTeacherAttacked / GameData.basicApOfGhost / FactorTeacher); | |||
| } | |||
| bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | |||
| bullet.Parent.AddHP((long)(bullet.Parent.Vampire * subHp)); | |||
| } | |||
| if (student.HP <= 0) | |||
| student.TryActivatingLIFE(); // 如果有复活甲 | |||
| @@ -393,7 +393,7 @@ namespace Gaming | |||
| for (int i = 0; i < GameData.maxNumOfPropInPropInventory; i++) | |||
| { | |||
| Gadget? prop = player.UseProp(i); | |||
| Gadget? prop = player.ConsumeProp(i); | |||
| if (prop != null) | |||
| { | |||
| prop.ReSetPos(player.Position); | |||
| @@ -238,7 +238,7 @@ namespace Gaming | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| propManager.UseProp(player, propType); | |||
| propManager.ConsumeProp(player, propType); | |||
| } | |||
| } | |||
| public void ThrowProp(long playerID, PropType propType = PropType.Null) | |||
| @@ -17,11 +17,11 @@ namespace Gaming | |||
| private readonly CharacterManager characterManager; | |||
| private readonly List<XY> availableCellForGenerateProp; | |||
| public void UseProp(Character player, PropType propType) | |||
| public void ConsumeProp(Character player, PropType propType) | |||
| { | |||
| if (player.CharacterType == CharacterType.Robot || player.IsRemoved) | |||
| return; | |||
| Gadget prop = player.UseProp(propType); | |||
| Gadget prop = player.ConsumeProp(propType); | |||
| switch (prop.GetPropType()) | |||
| { | |||
| case PropType.ShieldOrSpear: | |||
| @@ -46,7 +46,7 @@ namespace Gaming | |||
| if (!player.IsGhost()) | |||
| if (player.HP < player.MaxHp) | |||
| { | |||
| player.HP += GameData.basicTreatmentDegree / 2; | |||
| player.AddHP(GameData.basicTreatmentDegree / 2); | |||
| player.AddScore(GameData.ScorePropAddHp); | |||
| } | |||
| else player.AddAp(GameData.PropDuration); | |||
| @@ -117,7 +117,7 @@ namespace Gaming | |||
| { | |||
| if (!gameMap.Timer.IsGaming || player.IsRemoved) | |||
| return; | |||
| Gadget prop = player.UseProp(propType); | |||
| Gadget prop = player.ConsumeProp(propType); | |||
| if (prop.GetPropType() == PropType.Null) | |||
| return; | |||
| @@ -421,7 +421,7 @@ namespace Gaming | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| character.SetPlayerStateNaturally(); | |||
| character.HP = GameData.RemainHpWhenAddLife; | |||
| character.SetHP(GameData.RemainHpWhenAddLife); | |||
| ((Student)character).SetTimeOfRescue(0); | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| break; | |||
| @@ -452,7 +452,7 @@ namespace Gaming | |||
| if ((character.HP < character.MaxHp) && gameMap.CanSee(player, character)) | |||
| { | |||
| player.AddScore(GameData.StudentScoreTreat(GameData.addHpWhenEncourage)); | |||
| character.HP += GameData.addHpWhenEncourage; | |||
| character.AddHP(GameData.addHpWhenEncourage); | |||
| ((Student)character).SetDegreeOfTreatment0(); | |||
| break; | |||
| } | |||
| @@ -6,7 +6,8 @@ namespace Preparation.Interface | |||
| public interface ICharacter : IMoveable | |||
| { | |||
| public long TeamID { get; } | |||
| public long HP { get; set; } | |||
| public long HP { get; } | |||
| public long AddHP(long add); | |||
| public long Score { get; } | |||
| public void AddScore(long add); | |||
| public double Vampire { get; } | |||
| @@ -7,7 +7,6 @@ namespace Preparation.Interface | |||
| public GameObjType Type { get; } | |||
| public long ID { get; } | |||
| public XY Position { get; } // if Square, Pos equals the center | |||
| public XY FacingDirection { get; } | |||
| public bool IsRigid { get; } | |||
| public ShapeType Shape { get; } | |||
| public bool CanMove { get; } | |||
| @@ -6,6 +6,7 @@ namespace Preparation.Interface | |||
| { | |||
| public interface IMoveable : IGameObj | |||
| { | |||
| public XY FacingDirection { get; set; } | |||
| object ActionLock { get; } | |||
| public int MoveSpeed { get; } | |||
| public bool IsMoving { get; set; } | |||
| @@ -203,7 +203,7 @@ namespace Server | |||
| Type = Transformation.ToBulletType(bombedBullet.bulletHasBombed.TypeOfBullet), | |||
| X = bombedBullet.bulletHasBombed.Position.x, | |||
| Y = bombedBullet.bulletHasBombed.Position.y, | |||
| FacingDirection = bombedBullet.FacingDirection.Angle(), | |||
| FacingDirection = bombedBullet.facingDirection.Angle(), | |||
| MappingId = bombedBullet.MappingID, | |||
| BombRange = bombedBullet.bulletHasBombed.BulletBombRange | |||
| } | |||
| @@ -305,7 +305,7 @@ namespace Server | |||
| public override Task<BoolRes> UseProp(PropMsg request, ServerCallContext context) | |||
| { | |||
| #if DEBUG | |||
| Console.WriteLine($"UseProp ID: {request.PlayerId}"); | |||
| Console.WriteLine($"ConsumeProp ID: {request.PlayerId}"); | |||
| #endif | |||
| BoolRes boolRes = new(); | |||
| if (request.PlayerId >= spectatorMinPlayerID) | |||