| @@ -844,7 +844,7 @@ namespace Client | |||
| } | |||
| foreach (var obj in listOfButcher) | |||
| { | |||
| if (!isDataFixed[obj.PlayerId]) | |||
| if (obj.PlayerId < GameData.numOfStudent && !isDataFixed[obj.PlayerId]) | |||
| { | |||
| IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); | |||
| int j = 0; | |||
| @@ -8,7 +8,7 @@ namespace GameClass.GameObj | |||
| public CommonAttackOfGhost(Character player, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, pos) | |||
| { | |||
| AP.Set(GameData.basicApOfGhost); | |||
| AP.SetReturnOri(GameData.basicApOfGhost); | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| public override double AttackDistance => GameData.basicAttackShortRange; | |||
| @@ -45,7 +45,7 @@ namespace GameClass.GameObj | |||
| public Strike(Character player, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, pos) | |||
| { | |||
| AP.Set(GameData.basicApOfGhost * 16 / 15); | |||
| AP.SetReturnOri(GameData.basicApOfGhost * 16 / 15); | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| public override double AttackDistance => GameData.basicAttackShortRange * 20 / 22; | |||
| @@ -83,7 +83,7 @@ namespace GameClass.GameObj | |||
| public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, pos) | |||
| { | |||
| AP.Set(GameData.basicApOfGhost * 4 / 5); | |||
| AP.SetReturnOri(GameData.basicApOfGhost * 4 / 5); | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| public override double AttackDistance => GameData.basicRemoteAttackRange * 13; | |||
| @@ -123,7 +123,7 @@ namespace GameClass.GameObj | |||
| { | |||
| public BombBomb(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | |||
| { | |||
| AP.Set((int)(GameData.basicApOfGhost * 6.0 / 5)); | |||
| AP.SetReturnOri((int)(GameData.basicApOfGhost * 6.0 / 5)); | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange; | |||
| public override double AttackDistance => GameData.basicAttackShortRange; | |||
| @@ -163,7 +163,7 @@ namespace GameClass.GameObj | |||
| { | |||
| public JumpyDumpty(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | |||
| { | |||
| AP.Set((int)(GameData.basicApOfGhost * 0.6)); | |||
| AP.SetReturnOri((int)(GameData.basicApOfGhost * 0.6)); | |||
| } | |||
| public override double BulletBombRange => GameData.basicBulletBombRange / 2; | |||
| public override double AttackDistance => GameData.basicAttackShortRange * 18 / 22; | |||
| @@ -11,7 +11,8 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public abstract double BulletBombRange { get; } | |||
| public abstract double AttackDistance { get; } | |||
| public AtomicInt AP { get; } | |||
| private AtomicInt ap = new(0); | |||
| public AtomicInt AP { get => ap; } | |||
| public abstract int Speed { get; } | |||
| public abstract bool IsRemoteAttack { get; } | |||
| public abstract int CastTime { get; } | |||
| @@ -43,8 +44,8 @@ namespace GameClass.GameObj | |||
| public Bullet(Character player, int radius, XY Position) : | |||
| base(Position, radius, GameObjType.Bullet) | |||
| { | |||
| this.CanMove.Set(true); | |||
| this.MoveSpeed.Set(this.Speed); | |||
| this.CanMove.SetReturnOri(true); | |||
| this.MoveSpeed.SetReturnOri(this.Speed); | |||
| this.hasSpear = player.TryUseSpear(); | |||
| this.Parent = player; | |||
| } | |||
| @@ -31,12 +31,12 @@ namespace GameClass.GameObj | |||
| protected Character(XY initPos, int initRadius, CharacterType characterType) : | |||
| base(initPos, initRadius, GameObjType.Character) | |||
| { | |||
| this.CanMove.Set(true); | |||
| this.CanMove.SetReturnOri(true); | |||
| this.score = 0; | |||
| this.buffManager = new BuffManager(); | |||
| this.occupation = OccupationFactory.FindIOccupation(characterType); | |||
| this.MaxHp = this.hp = Occupation.MaxHp; | |||
| this.MoveSpeed.Set(this.orgMoveSpeed = Occupation.MoveSpeed); | |||
| this.HP = new(Occupation.MaxHp); | |||
| this.MoveSpeed.SetReturnOri(this.orgMoveSpeed = Occupation.MoveSpeed); | |||
| this.BulletOfPlayer = this.OriBulletOfPlayer = Occupation.InitBullet; | |||
| this.concealment = Occupation.Concealment; | |||
| this.alertnessRadius = Occupation.AlertnessRadius; | |||
| @@ -10,20 +10,7 @@ namespace GameClass.GameObj | |||
| { | |||
| #region 装弹、攻击相关的基本属性及方法 | |||
| private readonly object attackLock = new(); | |||
| /// <summary> | |||
| /// 装弹冷却 | |||
| /// </summary> | |||
| protected int cd; | |||
| public int CD | |||
| { | |||
| get | |||
| { | |||
| lock (attackLock) | |||
| { | |||
| return cd; | |||
| } | |||
| } | |||
| } | |||
| public IntNumUpdateByCD BulletNum { get; } = new IntNumUpdateByCD(); | |||
| private int orgCD; | |||
| public int OrgCD | |||
| { | |||
| @@ -50,56 +37,26 @@ namespace GameClass.GameObj | |||
| lock (attackLock) | |||
| { | |||
| bulletOfPlayer = value; | |||
| cd = orgCD = (BulletFactory.BulletCD(value)); | |||
| Debugger.Output(this, string.Format("'s CD has been set to: {0}.", cd)); | |||
| maxBulletNum = bulletNum = (BulletFactory.BulletNum(value)); | |||
| orgCD = (BulletFactory.BulletCD(value)); | |||
| BulletNum.SetCD(orgCD); | |||
| Debugger.Output(this, string.Format("'s CD has been set to: {0}.", orgCD)); | |||
| BulletNum.SetPositiveMaxNumAndNum(BulletFactory.BulletNum(value)); | |||
| } | |||
| } | |||
| } | |||
| protected int maxBulletNum; | |||
| public int MaxBulletNum | |||
| { | |||
| get | |||
| { | |||
| lock (attackLock) | |||
| { | |||
| return maxBulletNum; | |||
| } | |||
| } | |||
| } | |||
| private int bulletNum; | |||
| private int updateTimeOfBulletNum = 0; | |||
| public int UpdateBulletNum(int time)//通过该函数获取真正的bulletNum | |||
| { | |||
| lock (attackLock) | |||
| { | |||
| if (bulletNum < maxBulletNum && time - updateTimeOfBulletNum >= cd) | |||
| { | |||
| int add = Math.Min(maxBulletNum - bulletNum, (time - updateTimeOfBulletNum) / cd); | |||
| updateTimeOfBulletNum += add * cd; | |||
| return (bulletNum += add); | |||
| } | |||
| return bulletNum; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 进行一次攻击 | |||
| /// </summary> | |||
| /// <returns>攻击操作发出的子弹</returns> | |||
| public Bullet? Attack(double angle, int time) | |||
| public Bullet? Attack(double angle) | |||
| { | |||
| lock (attackLock) | |||
| { | |||
| if (bulletOfPlayer == BulletType.Null) | |||
| return null; | |||
| if (UpdateBulletNum(time) > 0) | |||
| if (BulletNum.TrySub(1) == 1) | |||
| { | |||
| if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time; | |||
| --bulletNum; | |||
| XY res = Position + new XY // 子弹紧贴人物生成。 | |||
| ( | |||
| (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), | |||
| @@ -115,31 +72,6 @@ namespace GameClass.GameObj | |||
| return null; | |||
| } | |||
| } | |||
| /* | |||
| /// <summary> | |||
| /// 攻击被反弹,反弹伤害不会再被反弹 | |||
| /// </summary> | |||
| /// <param name="subHP"></param> | |||
| /// <param name="hasSpear"></param> | |||
| /// <param name="bouncer">反弹伤害者</param> | |||
| /// <returns>是否因反弹伤害而死</returns> | |||
| private bool BeBounced(int subHP, bool hasSpear, Character? bouncer) | |||
| { | |||
| lock (beAttackedLock) | |||
| { | |||
| if (hp <= 0) | |||
| return false; | |||
| if (!(bouncer?.TeamID == this.TeamID)) | |||
| { | |||
| if (hasSpear || !HasShield) | |||
| _ = SubHp(subHP); | |||
| if (hp <= 0) | |||
| TryActivatingLIFE(); | |||
| } | |||
| return hp <= 0; | |||
| } | |||
| }*/ | |||
| #endregion | |||
| #region 感知相关的基本属性及方法 | |||
| private readonly object bgmLock = new(); | |||
| @@ -187,114 +119,7 @@ namespace GameClass.GameObj | |||
| } | |||
| #endregion | |||
| #region 血量相关的基本属性及方法 | |||
| private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); | |||
| public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; | |||
| private long maxHp; | |||
| public long MaxHp | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return maxHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| protected set | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| maxHp = value; | |||
| if (hp > maxHp) hp = maxHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| } | |||
| // 最大血量 | |||
| protected long hp; | |||
| public long HP | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return hp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| public long SetHP(long value) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| if (value > 0) | |||
| { | |||
| return hp = value <= maxHp ? value : maxHp; | |||
| } | |||
| else | |||
| return hp = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 尝试减血 | |||
| /// </summary> | |||
| /// <param name="sub">减血量</param> | |||
| public long SubHp(long sub) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| long previousHp = hp; | |||
| if (hp <= sub) | |||
| { | |||
| hp = 0; | |||
| return hp; | |||
| } | |||
| else | |||
| { | |||
| hp -= sub; | |||
| return sub; | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| public long AddHP(long add) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| long previousHp = hp; | |||
| return (hp = (hp + add > maxHp) ? maxHp : hp + add) - previousHp; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| public LongWithVariableRange HP { get; } | |||
| private readonly object vampireLock = new(); | |||
| public object VampireLock => vampire; | |||
| @@ -328,55 +153,42 @@ namespace GameClass.GameObj | |||
| { | |||
| get | |||
| { | |||
| HPReadWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| lock (treatLock) | |||
| return degreeOfTreatment; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| public void SetDegreeOfTreatment0() | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| lock (treatLock) | |||
| degreeOfTreatment = 0; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| public bool AddDegreeOfTreatment(int value, Student whoTreatYou) | |||
| { | |||
| HPReadWriterLock.EnterWriteLock(); | |||
| try | |||
| lock (treatLock) | |||
| { | |||
| if (value >= maxHp - hp) | |||
| degreeOfTreatment += value; | |||
| long addV = HP.TryAddAll(degreeOfTreatment); | |||
| if (addV == 0) | |||
| { | |||
| degreeOfTreatment = 0; | |||
| return false; | |||
| } | |||
| if (addV > 0) | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(maxHp - hp)); | |||
| hp = maxHp; | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(addV)); | |||
| degreeOfTreatment = 0; | |||
| return true; | |||
| } | |||
| if (value >= GameData.basicTreatmentDegree) | |||
| if (degreeOfTreatment >= GameData.basicTreatmentDegree) | |||
| { | |||
| whoTreatYou.AddScore(GameData.StudentScoreTreat(GameData.basicTreatmentDegree)); | |||
| hp += GameData.basicTreatmentDegree; | |||
| HP.AddPositiveV(GameData.basicTreatmentDegree); | |||
| degreeOfTreatment = 0; | |||
| return true; | |||
| } | |||
| degreeOfTreatment = value; | |||
| return false; | |||
| } | |||
| finally | |||
| { | |||
| HPReadWriterLock.ExitWriteLock(); | |||
| } | |||
| return false; | |||
| } | |||
| #endregion | |||
| #region 查询状态相关的基本属性与方法 | |||
| @@ -542,7 +354,7 @@ namespace GameClass.GameObj | |||
| case PlayerStateType.OpeningTheChest: | |||
| if (value == PlayerStateType.Rescued) return -1; | |||
| ((Chest)lastObj!).StopOpen(); | |||
| ((Chest)lastObj!).OpenProgress.Set0(); | |||
| return ChangePlayerState(runningState, value, gameObj); | |||
| case PlayerStateType.OpeningTheDoorway: | |||
| if (value == PlayerStateType.Rescued) return -1; | |||
| @@ -570,7 +382,7 @@ namespace GameClass.GameObj | |||
| else | |||
| { | |||
| if (value != PlayerStateType.UsingSkill) | |||
| ((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)playerID; | |||
| ((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)PlayerID; | |||
| return ChangePlayerState(runningState, value, gameObj); | |||
| } | |||
| } | |||
| @@ -647,7 +459,7 @@ namespace GameClass.GameObj | |||
| { | |||
| if (SetPlayerState(RunningStateType.RunningForcibly, playerStateType) == -1) return false; | |||
| TryToRemove(); | |||
| CanMove.Set(false); | |||
| CanMove.SetReturnOri(false); | |||
| position = GameData.PosWhoDie; | |||
| } | |||
| return true; | |||
| @@ -673,18 +485,8 @@ namespace GameClass.GameObj | |||
| /// <summary> | |||
| /// 角色所属队伍ID | |||
| /// </summary> | |||
| private long teamID = long.MaxValue; | |||
| public long TeamID | |||
| { | |||
| get => Interlocked.Read(ref teamID); | |||
| set => Interlocked.Exchange(ref teamID, value); | |||
| } | |||
| private long playerID = long.MaxValue; | |||
| public long PlayerID | |||
| { | |||
| get => Interlocked.Read(ref playerID); | |||
| set => Interlocked.Exchange(ref playerID, value); | |||
| } | |||
| public AtomicLong TeamID { get; } = new AtomicLong(long.MaxValue); | |||
| public AtomicLong PlayerID { get; } = new AtomicLong(long.MaxValue); | |||
| #region 道具和buff相关属性、方法 | |||
| private readonly object inventoryLock = new(); | |||
| @@ -802,7 +604,7 @@ namespace GameClass.GameObj | |||
| } | |||
| public void AddMoveSpeed(int buffTime, double add = 1.0) => buffManager.AddMoveSpeed(add, buffTime, newVal => | |||
| { MoveSpeed.Set(newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed); }, | |||
| { MoveSpeed.SetReturnOri(newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed); }, | |||
| OrgMoveSpeed); | |||
| public bool HasFasterSpeed => buffManager.HasFasterSpeed; | |||
| @@ -866,7 +668,7 @@ namespace GameClass.GameObj | |||
| if (buffManager.TryActivatingLIFE()) | |||
| { | |||
| AddScore(GameData.ScorePropRemainHp); | |||
| hp = GameData.RemainHpWhenAddLife; | |||
| HP.SetPositiveV(GameData.RemainHpWhenAddLife); | |||
| } | |||
| } | |||
| @@ -30,7 +30,8 @@ namespace GameClass.GameObj | |||
| public abstract ShapeType Shape { get; } | |||
| public AtomicBool IsRemoved { get; } = new AtomicBool(false); | |||
| private AtomicBool isRemoved = new(false); | |||
| public AtomicBool IsRemoved { get => isRemoved; } | |||
| public virtual bool TryToRemove() | |||
| { | |||
| return IsRemoved.TrySet(true); | |||
| @@ -1,13 +1,12 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| namespace GameClass.GameObj | |||
| { | |||
| /// <summary> | |||
| /// 箱子 | |||
| /// </summary> | |||
| public class Chest : Immovable, IChest | |||
| public class Chest : Immovable | |||
| { | |||
| public Chest(XY initPos) : | |||
| base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) | |||
| @@ -19,38 +18,7 @@ namespace GameClass.GameObj | |||
| private readonly Gadget[] propInChest = new Gadget[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; | |||
| public Gadget[] PropInChest => propInChest; | |||
| private long openStartTime = 0; | |||
| public long OpenStartTime | |||
| { | |||
| get | |||
| { | |||
| lock (gameObjLock) return openStartTime; | |||
| } | |||
| } | |||
| private Character? whoOpen = null; | |||
| public Character? WhoOpen | |||
| { | |||
| get | |||
| { | |||
| lock (gameObjLock) return whoOpen; | |||
| } | |||
| } | |||
| public bool Open(Character character) | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| if (whoOpen != null) return false; | |||
| openStartTime = Environment.TickCount64; | |||
| whoOpen = character; | |||
| } | |||
| return true; | |||
| } | |||
| public void StopOpen() | |||
| { | |||
| lock (gameObjLock) | |||
| { | |||
| whoOpen = null; | |||
| } | |||
| } | |||
| private LongProgressByTime openProgress = new LongProgressByTime(); | |||
| public LongProgressByTime OpenProgress { get => openProgress; } | |||
| } | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -47,7 +46,8 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public AtomicInt LockDegree { get; } = new AtomicInt(0); | |||
| private AtomicInt lockDegree = new AtomicInt(0); | |||
| public AtomicInt LockDegree { get => lockDegree; } | |||
| private long openStartTime = 0; | |||
| public long OpenStartTime | |||
| @@ -98,7 +98,7 @@ namespace GameClass.GameObj | |||
| { | |||
| if (!isOpen) return false; | |||
| if (whoLockOrOpen != null) return false; | |||
| LockDegree.Set(0); | |||
| LockDegree.SetReturnOri(0); | |||
| whoLockOrOpen = character; | |||
| return true; | |||
| } | |||
| @@ -26,7 +26,7 @@ namespace GameClass.GameObj | |||
| { | |||
| Random r = new Random(Environment.TickCount); | |||
| EmergencyExit emergencyExit = (EmergencyExit)(GameObjDict[GameObjType.EmergencyExit][r.Next(0, GameObjDict[GameObjType.EmergencyExit].Count)]); | |||
| emergencyExit.CanOpen.Set(true); | |||
| emergencyExit.CanOpen.SetReturnOri(true); | |||
| Preparation.Utility.Debugger.Output(emergencyExit, emergencyExit.Position.ToString()); | |||
| } | |||
| finally | |||
| @@ -41,7 +41,7 @@ namespace GameClass.GameObj | |||
| try | |||
| { | |||
| foreach (Doorway doorway in GameObjDict[GameObjType.Doorway]) | |||
| doorway.PowerSupply.Set(true); | |||
| doorway.PowerSupply.SetReturnOri(true); | |||
| } | |||
| finally | |||
| { | |||
| @@ -228,7 +228,6 @@ namespace GameClass.GameObj | |||
| { | |||
| if (person.CharacterType == CharacterType.TechOtaku) | |||
| { | |||
| Debugger.Output(person, person.PlayerID.ToString()); | |||
| foreach (Character character in gameObjDict[GameObjType.Character]) | |||
| { | |||
| if (((UseRobot)person.FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID == character.PlayerID) | |||
| @@ -94,14 +94,16 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| public AtomicBool CanMove { get; } | |||
| private AtomicBool canMove = new(false); | |||
| public AtomicBool CanMove { get => canMove; } | |||
| public bool IsAvailableForMove => !IsMoving && CanMove && !IsRemoved; // 是否能接收移动指令 | |||
| /// <summary> | |||
| /// 移动速度 | |||
| /// </summary> | |||
| public AtomicInt MoveSpeed { get; } | |||
| private AtomicInt moveSpeed = new(0); | |||
| public AtomicInt MoveSpeed { get => moveSpeed; } | |||
| /// <summary> | |||
| /// 原初移动速度 | |||
| /// </summary> | |||
| @@ -24,8 +24,8 @@ namespace GameClass.GameObj | |||
| public Gadget(XY initPos, int radius = GameData.propRadius) : | |||
| base(initPos, radius, GameObjType.Gadget) | |||
| { | |||
| this.CanMove.Set(false); | |||
| this.MoveSpeed.Set(GameData.propMoveSpeed); | |||
| this.CanMove.SetReturnOri(false); | |||
| this.MoveSpeed.SetReturnOri(GameData.propMoveSpeed); | |||
| } | |||
| } | |||
| public abstract class Tool : Gadget | |||
| @@ -17,8 +17,8 @@ namespace GameClass.GameObj | |||
| public Item(XY initPos, int radius = GameData.propRadius) : | |||
| base(initPos, radius, GameObjType.Item) | |||
| { | |||
| this.CanMove.Set(false); | |||
| this.MoveSpeed.Set(0); | |||
| this.CanMove.SetReturnOri(false); | |||
| this.MoveSpeed.SetReturnOri(0); | |||
| } | |||
| } | |||
| @@ -102,7 +102,7 @@ namespace GameEngine | |||
| lock (obj.ActionLock) | |||
| { | |||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | |||
| obj.IsMoving.Set(true); | |||
| obj.IsMoving.SetReturnOri(true); | |||
| } | |||
| new Thread | |||
| @@ -139,7 +139,7 @@ namespace GameEngine | |||
| if (isEnded) | |||
| { | |||
| obj.IsMoving.Set(false); | |||
| obj.IsMoving.SetReturnOri(false); | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| @@ -184,7 +184,7 @@ namespace GameEngine | |||
| } | |||
| if (isEnded) | |||
| { | |||
| obj.IsMoving.Set(false); | |||
| obj.IsMoving.SetReturnOri(false); | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| @@ -224,7 +224,7 @@ namespace GameEngine | |||
| } | |||
| } while (flag); | |||
| } | |||
| obj.IsMoving.Set(false); // 结束移动 | |||
| obj.IsMoving.SetReturnOri(false); // 结束移动 | |||
| EndMove(obj); | |||
| } | |||
| } | |||
| @@ -199,7 +199,7 @@ namespace Gaming | |||
| } | |||
| else if (!GameData.ApproachToInteract(playerTreated.Position, player.Position)) return false; | |||
| if (playerTreated.HP == playerTreated.MaxHp) return false; | |||
| if (playerTreated.HP == playerTreated.HP.GetMaxV()) return false; | |||
| long stateNumTreated = playerTreated.SetPlayerState(RunningStateType.Waiting, PlayerStateType.Treated); | |||
| if (stateNumTreated == -1) return false; | |||
| @@ -321,7 +321,7 @@ namespace Gaming | |||
| if (playerRescued.AddTimeOfRescue(GameData.checkInterval)) | |||
| { | |||
| playerRescued.SetPlayerStateNaturally(); | |||
| playerRescued.SetHP(playerRescued.MaxHp / 2); | |||
| playerRescued.HP.SetPositiveV(playerRescued.HP.GetMaxV() / 2); | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| return false; | |||
| } | |||
| @@ -369,7 +369,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| else | |||
| if (!chestToOpen.Open(player)) | |||
| if (!chestToOpen.OpenProgress.Start((long)(GameData.degreeOfOpenedChest / player.SpeedOfOpenChest))) | |||
| { | |||
| player.ThreadNum.Release(); | |||
| player.SetPlayerStateNaturally(); | |||
| @@ -450,12 +450,12 @@ namespace Gaming | |||
| player.ReSetPos(windowToPlayer + windowForClimb.Position); | |||
| } | |||
| player.MoveSpeed.Set(player.SpeedOfClimbingThroughWindows); | |||
| player.MoveSpeed.SetReturnOri(player.SpeedOfClimbingThroughWindows); | |||
| moveEngine.MoveObj(player, (int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2), (-1 * windowToPlayer).Angle(), stateNum); | |||
| Thread.Sleep((int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2)); | |||
| player.MoveSpeed.Set(player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed)); | |||
| player.MoveSpeed.SetReturnOri(player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed)); | |||
| lock (player.ActionLock) | |||
| { | |||
| @@ -33,7 +33,7 @@ namespace Gaming | |||
| Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); | |||
| if (obj.CanMove && ((Bullet)obj).TypeOfBullet != BulletType.JumpyDumpty) | |||
| BulletBomb((Bullet)obj, null); | |||
| obj.CanMove.Set(false); | |||
| obj.CanMove.SetReturnOri(false); | |||
| } | |||
| ); | |||
| this.characterManager = characterManager; | |||
| @@ -89,7 +89,7 @@ namespace Gaming | |||
| { | |||
| if (gameMap.Remove(bullet)) | |||
| { | |||
| bullet.CanMove.Set(false); | |||
| bullet.CanMove.SetReturnOri(false); | |||
| if (bullet.BulletBombRange > 0) | |||
| { | |||
| BombedBullet bombedBullet = new(bullet); | |||
| @@ -203,7 +203,7 @@ namespace Gaming | |||
| { // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange | |||
| if (!player.Commandable()) return false; | |||
| Bullet? bullet = player.Attack(angle, gameMap.Timer.nowTime()); | |||
| Bullet? bullet = player.Attack(angle); | |||
| if (bullet != null) | |||
| { | |||
| @@ -57,41 +57,8 @@ namespace Gaming | |||
| } | |||
| gameMap.Add(newPlayer); | |||
| newPlayer.TeamID = teamID; | |||
| newPlayer.PlayerID = playerID; | |||
| /* #region 人物装弹 | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| while (!gameMap.Timer.IsGaming) | |||
| Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval)); | |||
| long lastTime = Environment.TickCount64; | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved, | |||
| loopToDo: () => | |||
| { | |||
| long nowTime = Environment.TickCount64; | |||
| if (newPlayer.BulletNum == newPlayer.MaxBulletNum) | |||
| lastTime = nowTime; | |||
| else if (nowTime - lastTime >= newPlayer.CD) | |||
| { | |||
| _ = newPlayer.TryAddBulletNum(); | |||
| lastTime = nowTime; | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkInterval, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| { | |||
| AllowTimeExceed = true, | |||
| } | |||
| .Start(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| #endregion | |||
| */ | |||
| newPlayer.TeamID.SetReturnOri(teamID); | |||
| newPlayer.PlayerID.SetReturnOri(playerID); | |||
| #region BGM,牵制得分更新 | |||
| new Thread | |||
| ( | |||
| @@ -311,7 +278,7 @@ namespace Gaming | |||
| if (student.CharacterType == CharacterType.StraightAStudent) | |||
| { | |||
| ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation.Set(0); | |||
| ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation.SetReturnOri(0); | |||
| } | |||
| student.SetDegreeOfTreatment0(); | |||
| @@ -322,10 +289,10 @@ namespace Gaming | |||
| { | |||
| if (bullet.HasSpear) | |||
| { | |||
| long subHp = student.SubHp(bullet.AP); | |||
| long subHp = student.HP.SubPositiveV(bullet.AP); | |||
| Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); | |||
| bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp) + GameData.ScorePropUseSpear); | |||
| bullet.Parent.AddHP((long)bullet.Parent.Vampire * subHp); | |||
| bullet.Parent.HP.AddPositiveV((long)bullet.Parent.Vampire * subHp); | |||
| } | |||
| else return; | |||
| } | |||
| @@ -334,12 +301,12 @@ namespace Gaming | |||
| long subHp; | |||
| if (bullet.HasSpear) | |||
| { | |||
| subHp = student.SubHp(bullet.AP + GameData.ApSpearAdd); | |||
| subHp = student.HP.SubPositiveV(bullet.AP + GameData.ApSpearAdd); | |||
| Debugger.Output(this, "is being shot with Spear! Now his hp is" + student.HP.ToString()); | |||
| } | |||
| else | |||
| { | |||
| subHp = student.SubHp(bullet.AP); | |||
| subHp = student.HP.SubPositiveV(bullet.AP); | |||
| Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); | |||
| } | |||
| bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); | |||
| @@ -348,7 +315,7 @@ namespace Gaming | |||
| student.AddScore(subHp * GameData.factorOfScoreWhenTeacherAttacked / GameData.basicApOfGhost / FactorTeacher); | |||
| } | |||
| bullet.Parent.AddHP((long)(bullet.Parent.Vampire * subHp)); | |||
| bullet.Parent.HP.AddPositiveV((long)(bullet.Parent.Vampire * subHp)); | |||
| } | |||
| if (student.HP <= 0) | |||
| student.TryActivatingLIFE(); // 如果有复活甲 | |||
| @@ -84,7 +84,7 @@ namespace Gaming | |||
| else | |||
| { | |||
| #if DEBUG | |||
| Console.WriteLine($"PlayerID:{playerID} player does not exists!"); | |||
| Console.WriteLine($"playerID:{playerID} player does not exists!"); | |||
| #endif | |||
| return false; | |||
| @@ -326,7 +326,7 @@ namespace Gaming | |||
| { | |||
| foreach (Character player in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| player.CanMove.Set(false); | |||
| player.CanMove.SetReturnOri(false); | |||
| } | |||
| } | |||
| gameMap.GameObjDict[keyValuePair.Key].Clear(); | |||
| @@ -44,9 +44,9 @@ namespace Gaming | |||
| break; | |||
| case PropType.AddHpOrAp: | |||
| if (!player.IsGhost()) | |||
| if (player.HP < player.MaxHp) | |||
| if (player.HP < player.HP.GetMaxV()) | |||
| { | |||
| player.AddHP(GameData.basicTreatmentDegree / 2); | |||
| player.HP.AddPositiveV(GameData.basicTreatmentDegree / 2); | |||
| player.AddScore(GameData.ScorePropAddHp); | |||
| } | |||
| else player.AddAp(GameData.PropDuration); | |||
| @@ -196,7 +196,7 @@ namespace Gaming | |||
| } | |||
| if (homingMissile != null) | |||
| { | |||
| homingMissile.CanMove.Set(true); | |||
| homingMissile.CanMove.SetReturnOri(true); | |||
| attackManager.moveEngine.MoveObj(homingMissile, GameData.checkIntervalWhenSparksNSplash - 1, (whoAttacked.Position - homingMissile.Position).Angle(), ++homingMissile.StateNum); | |||
| } | |||
| }, | |||
| @@ -227,7 +227,7 @@ namespace Gaming | |||
| if (generator.Repair(((WriteAnswers)activeSkill).DegreeOfMeditation, player)) | |||
| gameMap.AddNumOfRepairedGenerators(); | |||
| Debugger.Output(player, "uses WriteAnswers in" + generator.ToString() + "with " + (((WriteAnswers)activeSkill).DegreeOfMeditation).ToString()); | |||
| ((WriteAnswers)activeSkill).DegreeOfMeditation.Set(0); | |||
| ((WriteAnswers)activeSkill).DegreeOfMeditation.SetReturnOri(0); | |||
| } | |||
| }, | |||
| () => | |||
| @@ -349,7 +349,7 @@ namespace Gaming | |||
| || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | |||
| && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange / 3) | |||
| { | |||
| int stunTime = (GameData.timeOfGhostStunnedWhenPunish + (int)((GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP) / GameData.basicApOfGhost))) / characterManager.FactorTeacher; | |||
| int stunTime = (GameData.timeOfGhostStunnedWhenPunish + (int)((GameData.factorOfTimeStunnedWhenPunish * (player.HP.GetMaxV() - player.HP) / GameData.basicApOfGhost))) / characterManager.FactorTeacher; | |||
| if (CharacterManager.BeStunned(character, stunTime) > 0) | |||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(stunTime) / characterManager.FactorTeacher); | |||
| break; | |||
| @@ -421,7 +421,7 @@ namespace Gaming | |||
| if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character)) | |||
| { | |||
| character.SetPlayerStateNaturally(); | |||
| character.SetHP(GameData.RemainHpWhenAddLife); | |||
| character.HP.SetPositiveV(GameData.RemainHpWhenAddLife); | |||
| ((Student)character).SetTimeOfRescue(0); | |||
| player.AddScore(GameData.StudentScoreRescue); | |||
| break; | |||
| @@ -449,10 +449,10 @@ namespace Gaming | |||
| { | |||
| foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if ((character.HP < character.MaxHp) && gameMap.CanSee(player, character)) | |||
| if ((character.HP < character.HP.GetMaxV()) && gameMap.CanSee(player, character)) | |||
| { | |||
| player.AddScore(GameData.StudentScoreTreat(GameData.addHpWhenEncourage)); | |||
| character.AddHP(GameData.addHpWhenEncourage); | |||
| character.HP.AddPositiveV(GameData.addHpWhenEncourage); | |||
| ((Student)character).SetDegreeOfTreatment0(); | |||
| break; | |||
| } | |||
| @@ -26,7 +26,7 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束 | |||
| () => | |||
| { | |||
| if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation.Add(learningDegree * GameData.frameDuration); | |||
| else activeSkill.DegreeOfMeditation.Set(0); | |||
| else activeSkill.DegreeOfMeditation.SetReturnOri(0); | |||
| //Debugger.Output(player, "with " + (((WriteAnswers)activeSkill).DegreeOfMeditation).ToString()); | |||
| }, | |||
| timeInterval: GameData.frameDuration, | |||
| @@ -1,13 +1,11 @@ | |||
| using System; | |||
| using Preparation.Utility; | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface ICharacter : IMoveable | |||
| { | |||
| public long TeamID { get; } | |||
| public long HP { get; } | |||
| public long AddHP(long add); | |||
| public AtomicLong TeamID { get; } | |||
| public LongWithVariableRange HP { get; } | |||
| public long Score { get; } | |||
| public void AddScore(long add); | |||
| public double Vampire { get; } | |||
| @@ -15,7 +13,7 @@ namespace Preparation.Interface | |||
| public BulletType BulletOfPlayer { get; set; } | |||
| public CharacterType CharacterType { get; } | |||
| public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType); | |||
| public int UpdateBulletNum(int time); | |||
| public IntNumUpdateByCD BulletNum { get; } | |||
| public long SetPlayerState(RunningStateType running, PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); | |||
| public bool ResetPlayerState(long state, RunningStateType running = RunningStateType.Null, PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); | |||
| @@ -1,9 +0,0 @@ | |||
| using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface IChest : IGameObj | |||
| { | |||
| public void StopOpen(); | |||
| } | |||
| } | |||
| @@ -174,7 +174,8 @@ namespace Preparation.Interface | |||
| public override int SkillCD => GameData.commonSkillCD; | |||
| public override int DurationTime => 0; | |||
| public AtomicInt DegreeOfMeditation { get; } = new(0); | |||
| private AtomicInt degreeOfMeditation = new(0); | |||
| public AtomicInt DegreeOfMeditation { get => degreeOfMeditation; } | |||
| } | |||
| public class SummonGolem : ActiveSkill | |||
| @@ -5,7 +5,7 @@ namespace Preparation.Utility | |||
| { | |||
| //理论上结构体最好不可变,这里采用了可变结构。 | |||
| //其对应属性不应当有set访问器,避免不安全的=赋值 | |||
| public struct AtomicInt | |||
| public class AtomicInt | |||
| { | |||
| private int v; | |||
| public AtomicInt(int x) | |||
| @@ -15,20 +15,39 @@ namespace Preparation.Utility | |||
| public override string ToString() => Interlocked.CompareExchange(ref v, -1, -1).ToString(); | |||
| public int Get() => Interlocked.CompareExchange(ref v, -1, -1); | |||
| public static implicit operator int(AtomicInt aint) => Interlocked.CompareExchange(ref aint.v, -1, -1); | |||
| public int Set(int value) => Interlocked.Exchange(ref v, value); | |||
| /// <returns>返回操作前的值</returns> | |||
| public int SetReturnOri(int value) => Interlocked.Exchange(ref v, value); | |||
| public int Add(int x) => Interlocked.Add(ref v, x); | |||
| public int Sub(int x) => Interlocked.Add(ref v, -x); | |||
| public int Inc() => Interlocked.Increment(ref v); | |||
| public int Dec() => Interlocked.Decrement(ref v); | |||
| public void CompareExchange(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); | |||
| /// <returns>返回操作前的值</returns> | |||
| public int CompareExReturnOri(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); | |||
| } | |||
| public struct AtomicBool | |||
| public class AtomicLong | |||
| { | |||
| private long v; | |||
| public AtomicLong(long x) | |||
| { | |||
| v = x; | |||
| } | |||
| public override string ToString() => Interlocked.Read(ref v).ToString(); | |||
| public long Get() => Interlocked.Read(ref v); | |||
| public static implicit operator long(AtomicLong aint) => Interlocked.Read(ref aint.v); | |||
| /// <returns>返回操作前的值</returns> | |||
| public long SetReturnOri(long value) => Interlocked.Exchange(ref v, value); | |||
| public long Add(long x) => Interlocked.Add(ref v, x); | |||
| public long Sub(long x) => Interlocked.Add(ref v, -x); | |||
| public long Inc() => Interlocked.Increment(ref v); | |||
| public long Dec() => Interlocked.Decrement(ref v); | |||
| /// <returns>返回操作前的值</returns> | |||
| public long CompareExReturnOri(long newV, long compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); | |||
| } | |||
| public class AtomicBool | |||
| { | |||
| private int v;//v==0为false,v!=0(v==1或v==-1)为true | |||
| private int v;//v==0为false,v==1为true | |||
| public AtomicBool(bool x) | |||
| { | |||
| v = x ? 1 : 0; | |||
| @@ -36,50 +55,113 @@ namespace Preparation.Utility | |||
| public override string ToString() => (Interlocked.CompareExchange(ref v, -2, -2) == 0) ? "false" : "true"; | |||
| public bool Get() => (Interlocked.CompareExchange(ref v, -1, -1) != 0); | |||
| public static implicit operator bool(AtomicBool abool) => (Interlocked.CompareExchange(ref abool.v, -1, -1) != 0); | |||
| public bool Set(bool value) => (Interlocked.Exchange(ref v, value ? 1 : 0) != 0); | |||
| /// <returns>返回操作前的值</returns> | |||
| public bool SetReturnOri(bool value) => (Interlocked.Exchange(ref v, value ? 1 : 0) != 0); | |||
| /// <returns>赋值前的值是否与将赋予的值不相同</returns> | |||
| public bool TrySet(bool value) | |||
| { | |||
| return (Interlocked.CompareExchange(ref v, value ? 1 : 0, value ? 0 : 1) ^ (value ? 1 : 0)) != 0; | |||
| } | |||
| public bool Invert() => Interlocked.Add(ref v, -1) != 0; | |||
| public bool And(bool x) => Interlocked.And(ref v, x ? 1 : 0) != 0; | |||
| public bool Or(bool x) => Interlocked.Or(ref v, x ? 1 : 0) != 0; | |||
| } | |||
| public struct IntProgressContinuously | |||
| /// <summary> | |||
| /// 根据时间推算Start后完成多少进度的进度条(long)。 | |||
| /// 只允许Start时修改needTime(请确保大于0); | |||
| /// 支持TrySet0使未完成的进度条终止清零;支持Set0使进度条强制终止清零; | |||
| /// 通过原子操作实现。 | |||
| /// </summary> | |||
| public class LongProgressByTime | |||
| { | |||
| private long endT = long.MaxValue; | |||
| private long needT; | |||
| public IntProgressContinuously(long needTime) | |||
| public LongProgressByTime(long needTime) | |||
| { | |||
| if (needTime <= 0) Debugger.Output("Bug:LongProgressByTime.needTime (" + needTime.ToString() + ") is less than 0."); | |||
| this.needT = needTime; | |||
| } | |||
| public LongProgressByTime() | |||
| { | |||
| this.needT = 0; | |||
| } | |||
| public long GetEndTime() => Interlocked.CompareExchange(ref endT, -2, -2); | |||
| public long GetNeedTime() => Interlocked.CompareExchange(ref needT, -2, -2); | |||
| public override string ToString() => "EndTime:" + Interlocked.CompareExchange(ref endT, -2, -2).ToString() + " ms, NeedTime:" + Interlocked.CompareExchange(ref needT, -2, -2).ToString() + " ms"; | |||
| public bool IsFinished() | |||
| { | |||
| return Interlocked.CompareExchange(ref endT, -2, -2) <= Environment.TickCount64; | |||
| } | |||
| public bool IsOpened() => Interlocked.Read(ref endT) != long.MaxValue; | |||
| /// <summary> | |||
| /// GetProgress<0则表明未开始 | |||
| /// </summary> | |||
| public long GetProgress() | |||
| { | |||
| long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; | |||
| if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); | |||
| return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; | |||
| } | |||
| public long GetNonNegativeProgress() | |||
| { | |||
| long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; | |||
| if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); | |||
| long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; | |||
| return progress < 0 ? 0 : progress; | |||
| } | |||
| /// <summary> | |||
| /// GetProgress<0则表明未开始 | |||
| /// </summary> | |||
| public long GetProgress(long time) | |||
| { | |||
| long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - time; | |||
| if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); | |||
| return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; | |||
| } | |||
| public long GetNonNegativeProgress(long time) | |||
| { | |||
| long cutime = Interlocked.Read(ref endT) - time; | |||
| if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); | |||
| long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; | |||
| return progress < 0 ? 0 : progress; | |||
| } | |||
| /// <summary> | |||
| /// <0则表明未开始 | |||
| /// </summary> | |||
| public static implicit operator long(LongProgressByTime pLong) => pLong.GetProgress(); | |||
| /// <summary> | |||
| /// GetProgressDouble<0则表明未开始 | |||
| /// </summary> | |||
| public double GetProgressDouble() | |||
| { | |||
| long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; | |||
| if (cutime <= 0) return 1; | |||
| return 1.0 - ((double)cutime / Interlocked.CompareExchange(ref needT, -2, -2)); | |||
| long needTime = Interlocked.CompareExchange(ref needT, -2, -2); | |||
| if (needTime == 0) return 0; | |||
| return 1.0 - ((double)cutime / needTime); | |||
| } | |||
| public double GetNonNegativeProgressDouble(long time) | |||
| { | |||
| long cutime = Interlocked.Read(ref endT) - time; | |||
| if (cutime <= 0) return 1; | |||
| long needTime = Interlocked.CompareExchange(ref needT, -2, -2); | |||
| if (needTime <= cutime) return 0; | |||
| return 1.0 - ((double)cutime / needTime); | |||
| } | |||
| public bool Start(long needTime) | |||
| { | |||
| if (needTime <= 0) | |||
| { | |||
| Debugger.Output("Warning:Start LongProgressByTime with the needTime (" + needTime.ToString() + ") which is less than 0."); | |||
| return false; | |||
| } | |||
| //规定只有Start可以修改needT,且需要先访问endTime,从而避免锁(某种程度上endTime可以认为是needTime的锁) | |||
| if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; | |||
| if (needTime <= 2) Debugger.Output("Warning:the field of IntProgressContinuously is " + needTime.ToString() + ",which is too small."); | |||
| Interlocked.Exchange(ref this.needT, needTime); | |||
| if (needTime <= 2) Debugger.Output("Warning:the field of LongProgressByTime is " + needTime.ToString() + ",which is too small."); | |||
| Interlocked.Exchange(ref needT, needTime); | |||
| return true; | |||
| } | |||
| public bool Start() | |||
| @@ -88,12 +170,513 @@ namespace Preparation.Utility | |||
| if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; | |||
| return true; | |||
| } | |||
| /// <summary> | |||
| /// 使进度条强制终止清零 | |||
| /// </summary> | |||
| public void Set0() => Interlocked.Exchange(ref endT, long.MaxValue); | |||
| public void TryStop() | |||
| /// <summary> | |||
| /// 使未完成的进度条终止清零 | |||
| /// </summary> | |||
| public bool TrySet0() | |||
| { | |||
| if (Environment.TickCount64 < Interlocked.CompareExchange(ref endT, -2, -2)) | |||
| { | |||
| Interlocked.Exchange(ref endT, long.MaxValue); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| //增加其他新的写操作可能导致不安全 | |||
| } | |||
| /* | |||
| /// <summary> | |||
| /// 记录(不是根据时间)完成多少进度的进度条(long)。 | |||
| /// </summary> | |||
| public struct IntProgressByAdding | |||
| { | |||
| private int completedProgress = -1; | |||
| private int requiredProgress; | |||
| public IntProgressByAdding(int completedProgress, int requiredProgress) | |||
| { | |||
| this.completedProgress = completedProgress; | |||
| this.requiredProgress = requiredProgress; | |||
| } | |||
| public IntProgressByAdding(int requiredProgress) | |||
| { | |||
| this.requiredProgress = requiredProgress; | |||
| } | |||
| public IntProgressByAdding() | |||
| { | |||
| this.requiredProgress=int.MaxValue; | |||
| } | |||
| } | |||
| */ | |||
| /// <summary> | |||
| /// 一个保证在[0,maxValue]的可变int,支持可变的maxValue(请确保大于0) | |||
| /// </summary> | |||
| public class IntWithVariableRange | |||
| { | |||
| private int v; | |||
| private int maxV; | |||
| private readonly object vLock = new(); | |||
| public IntWithVariableRange(int value, int maxValue) | |||
| { | |||
| if (maxValue < 0) | |||
| { | |||
| Debugger.Output("Warning:Try to set IntWithVariableRange.maxValue to " + maxValue.ToString() + "."); | |||
| maxValue = 0; | |||
| } | |||
| v = value < maxValue ? value : maxValue; | |||
| this.maxV = maxValue; | |||
| } | |||
| /// <summary> | |||
| /// 默认使Value=maxValue | |||
| /// </summary> | |||
| public IntWithVariableRange(int maxValue) | |||
| { | |||
| if (maxValue < 0) | |||
| { | |||
| Debugger.Output("Warning:Try to set IntWithVariableRange.maxValue to " + maxValue.ToString() + "."); | |||
| maxValue = 0; | |||
| } | |||
| v = this.maxV = maxValue; | |||
| } | |||
| public IntWithVariableRange() | |||
| { | |||
| v = this.maxV = int.MaxValue; | |||
| } | |||
| public override string ToString() | |||
| { | |||
| lock (vLock) | |||
| { | |||
| return "value:" + v.ToString() + " ,maxValue:" + maxV.ToString(); | |||
| } | |||
| } | |||
| public int GetValue() { lock (vLock) return v; } | |||
| public static implicit operator int(IntWithVariableRange aint) => aint.GetValue(); | |||
| public int GetMaxV() { lock (vLock) return maxV; } | |||
| /// <summary> | |||
| /// 若maxValue<=0则maxValue设为0并返回False | |||
| /// </summary> | |||
| public bool SetMaxV(int maxValue) | |||
| { | |||
| if (maxValue < 0) maxValue = 0; | |||
| lock (vLock) | |||
| { | |||
| maxV = maxValue; | |||
| if (v > maxValue) v = maxValue; | |||
| } | |||
| return maxValue > 0; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该maxValue>=0 | |||
| /// </summary> | |||
| public void SetPositiveMaxV(int maxValue) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| maxV = maxValue; | |||
| if (v > maxValue) v = maxValue; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该value>=0 | |||
| /// </summary> | |||
| public int SetPositiveV(int value) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| return v = (value > maxV) ? maxV : value; | |||
| } | |||
| } | |||
| public int SetV(int value) | |||
| { | |||
| if (value < 0) value = 0; | |||
| lock (vLock) | |||
| { | |||
| return v = (value > maxV) ? maxV : value; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 返回实际改变量 | |||
| /// </summary> | |||
| public int AddV(int addV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| int previousV = v; | |||
| v += addV; | |||
| if (v < 0) v = 0; | |||
| if (v > maxV) v = maxV; | |||
| return v - previousV; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该增加值大于0,返回实际改变量 | |||
| /// </summary> | |||
| public int AddPositiveV(int addPositiveV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| addPositiveV = Math.Min(addPositiveV, maxV - v); | |||
| v += addPositiveV; | |||
| } | |||
| return addPositiveV; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该减少值大于0,返回实际改变量 | |||
| /// </summary> | |||
| public int SubPositiveV(int subPositiveV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| subPositiveV = Math.Min(subPositiveV, v); | |||
| v -= subPositiveV; | |||
| } | |||
| return subPositiveV; | |||
| } | |||
| /// <summary> | |||
| /// 试图加到满,如果无法加到maxValue则不加并返回-1 | |||
| /// </summary> | |||
| public int TryAddAll(int addV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| if (maxV - v <= addV) | |||
| { | |||
| addV = maxV - v; | |||
| v = maxV; | |||
| return addV; | |||
| } | |||
| return 0; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 一个保证在[0,maxValue]的可变long,支持可变的maxValue(请确保大于0) | |||
| /// </summary> | |||
| public class LongWithVariableRange | |||
| { | |||
| private long v; | |||
| private long maxV; | |||
| private readonly object vLock = new(); | |||
| public LongWithVariableRange(long value, long maxValue) | |||
| { | |||
| if (maxValue < 0) | |||
| { | |||
| Debugger.Output("Warning:Try to set SafaValues.LongWithVariableRange.maxValue to " + maxValue.ToString() + "."); | |||
| maxValue = 0; | |||
| } | |||
| v = value < maxValue ? value : maxValue; | |||
| this.maxV = maxValue; | |||
| } | |||
| /// <summary> | |||
| /// 默认使Value=maxValue | |||
| /// </summary> | |||
| public LongWithVariableRange(long maxValue) | |||
| { | |||
| if (maxValue < 0) | |||
| { | |||
| Debugger.Output("Warning:Try to set SafaValues.LongWithVariableRange.maxValue to " + maxValue.ToString() + "."); | |||
| maxValue = 0; | |||
| } | |||
| v = this.maxV = maxValue; | |||
| } | |||
| public LongWithVariableRange() | |||
| { | |||
| v = this.maxV = long.MaxValue; | |||
| } | |||
| public override string ToString() | |||
| { | |||
| lock (vLock) | |||
| { | |||
| return "value:" + v.ToString() + " ,maxValue:" + maxV.ToString(); | |||
| } | |||
| } | |||
| public long GetValue() { lock (vLock) return v; } | |||
| public static implicit operator long(LongWithVariableRange aint) => aint.GetValue(); | |||
| public long GetMaxV() { lock (vLock) return maxV; } | |||
| /// <summary> | |||
| /// 若maxValue<=0则maxValue设为0并返回False | |||
| /// </summary> | |||
| public bool SetMaxV(long maxValue) | |||
| { | |||
| if (maxValue < 0) maxValue = 0; | |||
| lock (vLock) | |||
| { | |||
| maxV = maxValue; | |||
| if (v > maxValue) v = maxValue; | |||
| } | |||
| return maxValue > 0; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该maxValue>=0 | |||
| /// </summary> | |||
| public void SetPositiveMaxV(long maxValue) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| maxV = maxValue; | |||
| if (v > maxValue) v = maxValue; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该value>=0 | |||
| /// </summary> | |||
| public long SetPositiveV(long value) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| return v = (value > maxV) ? maxV : value; | |||
| } | |||
| } | |||
| public long SetV(long value) | |||
| { | |||
| if (value < 0) value = 0; | |||
| lock (vLock) | |||
| { | |||
| return v = (value > maxV) ? maxV : value; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 返回实际改变量 | |||
| /// </summary> | |||
| public long AddV(long addV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| long previousV = v; | |||
| v += addV; | |||
| if (v < 0) v = 0; | |||
| if (v > maxV) v = maxV; | |||
| return v - previousV; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该增加值大于0,返回实际改变量 | |||
| /// </summary> | |||
| public long AddPositiveV(long addPositiveV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| addPositiveV = Math.Min(addPositiveV, maxV - v); | |||
| v += addPositiveV; | |||
| } | |||
| return addPositiveV; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该减少值大于0,返回实际改变量 | |||
| /// </summary> | |||
| public long SubPositiveV(long subPositiveV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| subPositiveV = Math.Min(subPositiveV, v); | |||
| v -= subPositiveV; | |||
| } | |||
| return subPositiveV; | |||
| } | |||
| /// <summary> | |||
| /// 试图加到满,如果无法加到maxValue则不加并返回-1 | |||
| /// </summary> | |||
| public long TryAddAll(long addV) | |||
| { | |||
| lock (vLock) | |||
| { | |||
| if (maxV - v <= addV) | |||
| { | |||
| addV = maxV - v; | |||
| v = maxV; | |||
| return addV; | |||
| } | |||
| return -1; | |||
| } | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 一个保证在[0,maxNum],每CDms自动更新的可变int,支持可变的CD、maxNum(请确保大于0) | |||
| /// </summary> | |||
| public class IntNumUpdateByCD | |||
| { | |||
| private int num; | |||
| private int maxNum; | |||
| private int cd; | |||
| private long updateTime = 0; | |||
| private readonly object numLock = new(); | |||
| public IntNumUpdateByCD(int num, int maxNum, int cd) | |||
| { | |||
| if (num < 0) Debugger.Output("Bug:IntNumUpdateByCD.num (" + num.ToString() + ") is less than 0."); | |||
| if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateByCD.maxNum (" + maxNum.ToString() + ") is less than 0."); | |||
| if (cd <= 0) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 0."); | |||
| this.num = (num < maxNum) ? num : maxNum; | |||
| this.maxNum = maxNum; | |||
| this.cd = cd; | |||
| this.updateTime = Environment.TickCount64; | |||
| } | |||
| /// <summary> | |||
| /// 默认使num=maxNum | |||
| /// </summary> | |||
| public IntNumUpdateByCD(int maxNum, int cd) | |||
| { | |||
| if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateByCD.maxNum (" + maxNum.ToString() + ") is less than 0."); | |||
| if (cd <= 0) Debugger.Output("Bug:IntNumUpdateByCD.cd (" + cd.ToString() + ") is less than 0."); | |||
| this.num = this.maxNum = maxNum; | |||
| this.cd = cd; | |||
| } | |||
| public IntNumUpdateByCD() | |||
| { | |||
| this.num = this.maxNum = 0; | |||
| this.cd = int.MaxValue; | |||
| } | |||
| public int GetMaxNum() { lock (numLock) return maxNum; } | |||
| public int GetCD() { lock (numLock) return cd; } | |||
| public int GetNum(long time) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| if (num < maxNum && time - updateTime >= cd) | |||
| { | |||
| int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); | |||
| updateTime += add * cd; | |||
| return (num += add); | |||
| } | |||
| return num; | |||
| } | |||
| } | |||
| public static implicit operator int(IntNumUpdateByCD aint) => aint.GetNum(Environment.TickCount64); | |||
| /// <summary> | |||
| /// 应当保证该subV>=0 | |||
| /// </summary> | |||
| public int TrySub(int subV) | |||
| { | |||
| if (subV < 0) Debugger.Output("Bug:IntNumUpdateByCD Try to sub " + subV.ToString() + ", which is less than 0."); | |||
| long time = Environment.TickCount64; | |||
| lock (numLock) | |||
| { | |||
| if (num < maxNum && time - updateTime >= cd) | |||
| { | |||
| int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); | |||
| updateTime += add * cd; | |||
| num += add; | |||
| } | |||
| if (num == maxNum) updateTime = time; | |||
| num -= subV = Math.Min(subV, num); | |||
| } | |||
| return subV; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该addV>=0 | |||
| /// </summary> | |||
| public void TryAdd(int addV) | |||
| { | |||
| if (addV < 0) Debugger.Output("Bug:IntNumUpdateByCD Try to add " + addV.ToString() + ", which is less than 0."); | |||
| lock (numLock) | |||
| { | |||
| num += Math.Min(addV, maxNum - num); | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 若maxNum<=0则maxNum及Num设为0并返回False | |||
| /// </summary> | |||
| public bool SetMaxNumAndNum(int maxNum) | |||
| { | |||
| if (maxNum < 0) maxNum = 0; | |||
| lock (numLock) | |||
| { | |||
| this.num = this.maxNum = maxNum; | |||
| } | |||
| return maxNum > 0; | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该maxnum>=0 | |||
| /// </summary> | |||
| public void SetPositiveMaxNumAndNum(int maxNum) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| this.num = this.maxNum = maxNum; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该maxnum>=0 | |||
| /// </summary> | |||
| public void SetPositiveMaxNum(int maxNum) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| if ((this.maxNum = maxNum) < num) | |||
| num = maxNum; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 若maxNum<=0则maxNum及Num设为0并返回False | |||
| /// </summary> | |||
| public bool SetMaxNum(int maxNum) | |||
| { | |||
| if (maxNum < 0) maxNum = 0; | |||
| lock (numLock) | |||
| { | |||
| if ((this.maxNum = maxNum) < num) | |||
| num = maxNum; | |||
| } | |||
| return maxNum > 0; | |||
| } | |||
| /// <summary> | |||
| /// 若num<0则num设为0并返回False | |||
| /// </summary> | |||
| public bool SetNum(int num) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| if (num < 0) | |||
| { | |||
| this.num = 0; | |||
| updateTime = Environment.TickCount64; | |||
| return false; | |||
| } | |||
| if (num < maxNum) | |||
| { | |||
| if (this.num == maxNum) updateTime = Environment.TickCount64; | |||
| this.num = num; | |||
| } | |||
| else this.num = maxNum; | |||
| return true; | |||
| } | |||
| } | |||
| /// <summary> | |||
| /// 应当保证该num>=0 | |||
| /// </summary> | |||
| public void SetPositiveNum(int num) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| if (num < maxNum) | |||
| { | |||
| if (this.num == maxNum) updateTime = Environment.TickCount64; | |||
| this.num = num; | |||
| } | |||
| else this.num = maxNum; | |||
| } | |||
| } | |||
| public void SetCD(int cd) | |||
| { | |||
| lock (numLock) | |||
| { | |||
| if (cd <= 0) Debugger.Output("Bug:SetReturnOri IntNumUpdateByCD.cd to " + cd.ToString() + "."); | |||
| this.cd = cd; | |||
| } | |||
| } | |||
| //增加新的写操作可能导致不安全 | |||
| } | |||
| } | |||
| } | |||
| @@ -269,17 +269,15 @@ namespace Server | |||
| } | |||
| private static MessageOfObj Chest(Chest chest, long time) | |||
| { | |||
| MessageOfObj msg = new() | |||
| return new() | |||
| { | |||
| ChestMessage = new() | |||
| { | |||
| X = chest.Position.x, | |||
| Y = chest.Position.y | |||
| Y = chest.Position.y, | |||
| Progress = (int)(chest.OpenProgress.GetNonNegativeProgressDouble(time) * GameData.degreeOfOpenedChest) | |||
| } | |||
| }; | |||
| int progress = (chest.WhoOpen != null) ? (((int)(time - chest.OpenStartTime)) * chest.WhoOpen.SpeedOfOpenChest) : 0; | |||
| msg.ChestMessage.Progress = (progress > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : progress; | |||
| return msg; | |||
| } | |||
| } | |||
| } | |||