feat: ✨ adjust the CharacterType Klee and TechOtaku
tags/v0.1.0
| @@ -80,7 +80,8 @@ v1.6 | |||
| - 新造的Robot的PlayerId的n总是尽量小 | |||
| 1. UseRobot | |||
| - CD:2s,持续时间:0s | |||
| - 输入额外参数PlayerID,切换到要使用的角色 | |||
| - 输入额外参数PlayerID,切换到要使用的角色。 | |||
| - 切换到其他角色时,自己进入UsingSkill状态。 | |||
| 修改后: | |||
| @@ -107,7 +108,7 @@ v1.6 | |||
| - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | |||
| - 主动技能SparksNSplash(新增): | |||
| - CD:45s, 持续时间:10s | |||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动) | |||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每100ms向该角色直线移动)(该角色无血量则失败) | |||
| - 主动技能 蹦蹦炸弹 JumpyBomb | |||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹 | |||
| - Idol | |||
| @@ -36,4 +36,10 @@ | |||
| # 5月15日更新 | |||
| - feat:Robot可用 | |||
| - hotfix: 修复了移动相关的bug | |||
| - hotfix: 修复了移动相关的bug | |||
| # 最新更新 | |||
| - feat:TechOtaku可用 | |||
| - feat:Klee、Idol已调整 | |||
| - fix:修复了InSpire会给Tricker加速的问题 | |||
| - fix:修复了开锁门的bug | |||
| @@ -816,63 +816,63 @@ namespace Client | |||
| private void Refresh(object? sender, EventArgs e) //log未更新 | |||
| { | |||
| lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行 | |||
| try | |||
| { | |||
| // Bonus(); | |||
| if (WindowState == WindowState.Maximized) | |||
| MaxButton.Content = "❐"; | |||
| else | |||
| MaxButton.Content = "🗖"; | |||
| foreach (var obj in listOfHuman) | |||
| lock (drawPicLock) // 加锁是必要的,画图操作和接收信息操作不能同时进行 | |||
| { | |||
| if (!isDataFixed[obj.PlayerId] && obj.PlayerId < GameData.numOfStudent && obj.StudentType != StudentType.Robot) | |||
| // Bonus(); | |||
| if (WindowState == WindowState.Maximized) | |||
| MaxButton.Content = "❐"; | |||
| else | |||
| MaxButton.Content = "🗖"; | |||
| foreach (var obj in listOfHuman) | |||
| { | |||
| IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType)); | |||
| totalLife[obj.PlayerId] = occupation.MaxHp; | |||
| totalDeath[obj.PlayerId] = occupation.MaxGamingAddiction; | |||
| int i = 0; | |||
| foreach (var skill in occupation.ListOfIActiveSkill) | |||
| if (obj.PlayerId < GameData.numOfStudent && !isDataFixed[obj.PlayerId]) | |||
| { | |||
| var iActiveSkill = SkillFactory.FindActiveSkill(skill); | |||
| coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD; | |||
| ++i; | |||
| IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType)); | |||
| totalLife[obj.PlayerId] = occupation.MaxHp; | |||
| totalDeath[obj.PlayerId] = occupation.MaxGamingAddiction; | |||
| int i = 0; | |||
| foreach (var skill in occupation.ListOfIActiveSkill) | |||
| { | |||
| var iActiveSkill = SkillFactory.FindActiveSkill(skill); | |||
| coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD; | |||
| ++i; | |||
| } | |||
| isDataFixed[obj.PlayerId] = true; | |||
| } | |||
| isDataFixed[obj.PlayerId] = true; | |||
| } | |||
| } | |||
| foreach (var obj in listOfButcher) | |||
| { | |||
| if (!isDataFixed[obj.PlayerId]) | |||
| foreach (var obj in listOfButcher) | |||
| { | |||
| IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); | |||
| int j = 0; | |||
| foreach (var skill in occupation1.ListOfIActiveSkill) | |||
| if (!isDataFixed[obj.PlayerId]) | |||
| { | |||
| var iActiveSkill = SkillFactory.FindActiveSkill(skill); | |||
| coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; | |||
| ++j; | |||
| IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); | |||
| int j = 0; | |||
| foreach (var skill in occupation1.ListOfIActiveSkill) | |||
| { | |||
| var iActiveSkill = SkillFactory.FindActiveSkill(skill); | |||
| coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; | |||
| ++j; | |||
| } | |||
| isDataFixed[obj.PlayerId] = true; | |||
| } | |||
| isDataFixed[obj.PlayerId] = true; | |||
| } | |||
| } | |||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||
| { | |||
| StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime); | |||
| } | |||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||
| { | |||
| StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime); | |||
| } | |||
| StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime); | |||
| StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime); | |||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||
| { | |||
| StatusBarsOfSurvivor[i].SetFontSize(12 * unitFontsize); | |||
| } | |||
| for (int i = 0; i < GameData.numOfStudent; i++) | |||
| { | |||
| StatusBarsOfSurvivor[i].SetFontSize(12 * unitFontsize); | |||
| } | |||
| StatusBarsOfHunter.SetFontSize(12 * unitFontsize); | |||
| StatusBarsOfCircumstance.SetFontSize(12 * unitFontsize); | |||
| if (!isClientStocked) | |||
| { | |||
| try | |||
| StatusBarsOfHunter.SetFontSize(12 * unitFontsize); | |||
| StatusBarsOfCircumstance.SetFontSize(12 * unitFontsize); | |||
| if (!isClientStocked) | |||
| { | |||
| UpperLayerOfMap.Children.Clear(); | |||
| foreach (var data in listOfAll) | |||
| @@ -885,7 +885,7 @@ namespace Client | |||
| } | |||
| foreach (var data in listOfHuman) | |||
| { | |||
| if (data.StudentType != StudentType.Robot) | |||
| if (data.PlayerId < GameData.numOfStudent) | |||
| StatusBarsOfSurvivor[data.PlayerId].SetValue(data, data.PlayerId); | |||
| if (CanSee(data)) | |||
| { | |||
| @@ -1136,15 +1136,15 @@ namespace Client | |||
| } | |||
| } | |||
| } | |||
| catch (Exception exc) | |||
| { | |||
| ErrorDisplayer error = new("Error: " + exc.ToString()); | |||
| error.Show(); | |||
| isClientStocked = true; | |||
| PorC.Content = "▶"; | |||
| } | |||
| counter++; | |||
| } | |||
| counter++; | |||
| } | |||
| catch (Exception exc) | |||
| { | |||
| ErrorDisplayer error = new("Error: " + exc.ToString()); | |||
| error.Show(); | |||
| isClientStocked = true; | |||
| PorC.Content = "▶"; | |||
| } | |||
| } | |||
| @@ -1309,6 +1309,7 @@ namespace Client | |||
| { | |||
| PlayerId = playerID, | |||
| SkillId = 0, | |||
| SkillParam = 0, | |||
| }; | |||
| client.UseSkill(msgB); | |||
| break; | |||
| @@ -197,6 +197,19 @@ namespace GameClass.GameObj | |||
| return false; | |||
| } | |||
| public bool TryDeleteInvisible() | |||
| { | |||
| if (HasInvisible) | |||
| { | |||
| lock (buffListLock[(int)BuffType.Invisible]) | |||
| { | |||
| buffList[(int)BuffType.Invisible].RemoveFirst(); | |||
| } | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| public void AddClairaudience(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Clairaudience, () => | |||
| { }); | |||
| public bool HasClairaudience | |||
| @@ -361,7 +361,7 @@ namespace GameClass.GameObj | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; | |||
| if (playerState == PlayerStateType.Moving && IsMoving == 1) return PlayerStateType.Moving; | |||
| return playerState; | |||
| } | |||
| } | |||
| @@ -423,9 +423,7 @@ namespace GameClass.GameObj | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| playerState = value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return ++stateNum; | |||
| } | |||
| @@ -434,20 +432,19 @@ namespace GameClass.GameObj | |||
| { | |||
| //只能被SetPlayerState引用 | |||
| whatInteractingWith = gameObj; | |||
| if (value != PlayerStateType.Moving) | |||
| IsMoving = false; | |||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||
| playerState = value; | |||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | |||
| return stateNum; | |||
| } | |||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null) | |||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null) | |||
| { | |||
| GameObj? gameObj = (GameObj?)obj; | |||
| lock (actionLock) | |||
| { | |||
| PlayerStateType nowPlayerState = PlayerState; | |||
| if (nowPlayerState == value) return -1; | |||
| if (nowPlayerState == value && value != PlayerStateType.UsingSkill) return -1; | |||
| switch (nowPlayerState) | |||
| { | |||
| case PlayerStateType.Escaped: | |||
| @@ -468,20 +465,21 @@ namespace GameClass.GameObj | |||
| else return -1; | |||
| case PlayerStateType.TryingToAttack: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| if (value == PlayerStateType.Addicted || value == PlayerStateType.Swinging | |||
| || value == PlayerStateType.Deceased || value == PlayerStateType.Stunned | |||
| || value == PlayerStateType.Charmed || value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Stunned: | |||
| case PlayerStateType.Charmed: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor | |||
| && value != PlayerStateType.Swinging) | |||
| if (value == PlayerStateType.Addicted || value == PlayerStateType.Deceased | |||
| || value == PlayerStateType.Null) | |||
| return ChangePlayerState(value, gameObj); | |||
| else return -1; | |||
| case PlayerStateType.Swinging: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.ClimbingThroughWindows | |||
| && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| if (value == PlayerStateType.Addicted | |||
| || value == PlayerStateType.Deceased || value == PlayerStateType.Stunned | |||
| || value == PlayerStateType.Charmed || value == PlayerStateType.Null) | |||
| { | |||
| try | |||
| { | |||
| @@ -494,7 +492,9 @@ namespace GameClass.GameObj | |||
| } | |||
| else return -1; | |||
| case PlayerStateType.ClimbingThroughWindows: | |||
| if (value != PlayerStateType.Moving && value != PlayerStateType.LockingTheDoor && value != PlayerStateType.OpeningTheDoor) | |||
| if (value == PlayerStateType.Addicted | |||
| || value == PlayerStateType.Deceased || value == PlayerStateType.Stunned | |||
| || value == PlayerStateType.Charmed || value == PlayerStateType.Null) | |||
| { | |||
| Window window = (Window)WhatInteractingWith!; | |||
| try | |||
| @@ -537,6 +537,28 @@ namespace GameClass.GameObj | |||
| { | |||
| ThreadNum.Release(); | |||
| } | |||
| case PlayerStateType.UsingSkill: | |||
| if (CharacterType == CharacterType.TechOtaku) | |||
| { | |||
| if (typeof(CraftingBench).IsInstanceOfType(whatInteractingWith)) | |||
| { | |||
| try | |||
| { | |||
| ((CraftingBench)whatInteractingWith!).StopSkill(); | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| finally | |||
| { | |||
| ThreadNum.Release(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (value != PlayerStateType.UsingSkill) | |||
| ((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)playerID; | |||
| } | |||
| } | |||
| return ChangePlayerState(value, gameObj); | |||
| default: | |||
| return ChangePlayerState(value, gameObj); | |||
| } | |||
| @@ -548,7 +570,6 @@ namespace GameClass.GameObj | |||
| lock (actionLock) | |||
| { | |||
| whatInteractingWith = null; | |||
| IsMoving = false; | |||
| playerState = PlayerStateType.Null; | |||
| return ++stateNum; | |||
| } | |||
| @@ -777,6 +798,7 @@ namespace GameClass.GameObj | |||
| return false; | |||
| } | |||
| } | |||
| public void TryActivatingLIFE() | |||
| { | |||
| if (buffManager.TryActivatingLIFE()) | |||
| @@ -801,6 +823,11 @@ namespace GameClass.GameObj | |||
| return buffManager.TryUseSpear(); | |||
| } | |||
| public bool TryDeleteInvisible() | |||
| { | |||
| return buffManager.TryDeleteInvisible(); | |||
| } | |||
| public bool TryUseShield() | |||
| { | |||
| if (buffManager.TryUseShield()) | |||
| @@ -36,6 +36,35 @@ namespace GameClass.GameObj | |||
| public abstract ShapeType Shape { get; } | |||
| protected bool isRemoved = false; | |||
| public virtual bool IsRemoved | |||
| { | |||
| get | |||
| { | |||
| gameObjReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return isRemoved; | |||
| } | |||
| finally | |||
| { | |||
| gameObjReaderWriterLock.ExitReadLock(); | |||
| } | |||
| } | |||
| } | |||
| public virtual void TryToRemove() | |||
| { | |||
| gameObjReaderWriterLock.EnterWriteLock(); | |||
| try | |||
| { | |||
| isRemoved = true; | |||
| } | |||
| finally | |||
| { | |||
| gameObjReaderWriterLock.ExitWriteLock(); | |||
| } | |||
| } | |||
| public int Radius { get; } | |||
| public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; | |||
| @@ -97,7 +97,7 @@ namespace GameClass.GameObj | |||
| { | |||
| if (whoLockOrOpen != null) | |||
| { | |||
| if (Environment.TickCount64 - openStartTime >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| if ((Environment.TickCount64 - openStartTime) >= GameData.degreeOfLockingOrOpeningTheDoor / whoLockOrOpen.SpeedOfOpeningOrLocking) | |||
| isOpen = true; | |||
| whoLockOrOpen = null; | |||
| } | |||
| @@ -128,7 +128,7 @@ namespace GameClass.GameObj | |||
| lock (gameObjLock) | |||
| { | |||
| if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | |||
| isOpen = true; | |||
| isOpen = false; | |||
| whoLockOrOpen = null; | |||
| } | |||
| } | |||
| @@ -231,7 +231,10 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| if (ToDel != null) | |||
| { | |||
| GameObjDict[gameObj.Type].Remove(ToDel); | |||
| ToDel.TryToRemove(); | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| @@ -242,16 +245,19 @@ namespace GameClass.GameObj | |||
| public bool RemoveJustFromMap(GameObj gameObj) | |||
| { | |||
| GameObjLockDict[gameObj.Type].EnterWriteLock(); | |||
| bool flag; | |||
| try | |||
| { | |||
| flag = GameObjDict[gameObj.Type].Remove(gameObj); | |||
| if (GameObjDict[gameObj.Type].Remove(gameObj)) | |||
| { | |||
| gameObj.TryToRemove(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| finally | |||
| { | |||
| GameObjLockDict[gameObj.Type].ExitWriteLock(); | |||
| } | |||
| return flag; | |||
| } | |||
| public void Add(GameObj gameObj) | |||
| { | |||
| @@ -47,21 +47,11 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| private bool isMoving; | |||
| public bool IsMoving | |||
| private int isMoving = 0; | |||
| public int IsMoving | |||
| { | |||
| get | |||
| { | |||
| lock (actionLock) | |||
| return isMoving; | |||
| } | |||
| set | |||
| { | |||
| lock (actionLock) | |||
| { | |||
| isMoving = value; | |||
| } | |||
| } | |||
| get => Interlocked.CompareExchange(ref isMoving, 0, 1); | |||
| set => Interlocked.Exchange(ref isMoving, value); | |||
| } | |||
| // 移动,改变坐标 | |||
| @@ -126,8 +116,7 @@ namespace GameClass.GameObj | |||
| } | |||
| } | |||
| protected bool isRemoved; | |||
| public bool IsRemoved | |||
| public override bool IsRemoved | |||
| { | |||
| get | |||
| { | |||
| @@ -152,7 +141,7 @@ namespace GameClass.GameObj | |||
| moveReaderWriterLock.EnterReadLock(); | |||
| try | |||
| { | |||
| return !isMoving && canMove && !isRemoved; | |||
| return isMoving == 0 && canMove && !isRemoved; | |||
| } | |||
| finally | |||
| { | |||
| @@ -1,5 +1,4 @@ | |||
| using Google.Protobuf.WellKnownTypes; | |||
| using Preparation.Interface; | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Threading; | |||
| @@ -1,5 +1,6 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -32,9 +33,32 @@ namespace GameClass.GameObj | |||
| public sealed class CraftingBench : Item | |||
| { | |||
| public CraftingBench(XY initPos) : | |||
| public CraftingBench(XY initPos, Character character, int num) : | |||
| base(initPos) | |||
| { | |||
| Parent = character; | |||
| this.num = num; | |||
| } | |||
| private readonly int num; | |||
| private long parentStateNum; | |||
| public long ParentStateNum | |||
| { | |||
| get => Interlocked.Read(ref parentStateNum); | |||
| set => Interlocked.Exchange(ref parentStateNum, value); | |||
| } | |||
| public void StopSkill() | |||
| { | |||
| ((SummonGolem)Parent!.FindActiveSkill(ActiveSkillType.SummonGolem)).DeleteGolem((int)num); | |||
| } | |||
| public void TryStopSkill() | |||
| { | |||
| lock (Parent!.ActionLock) | |||
| { | |||
| if (Parent!.StateNum == parentStateNum) | |||
| { | |||
| Parent!.SetPlayerState(); | |||
| } | |||
| } | |||
| } | |||
| public override PropType GetPropType() => PropType.CraftingBench; | |||
| } | |||
| @@ -102,7 +102,7 @@ namespace GameEngine | |||
| lock (obj.ActionLock) | |||
| { | |||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | |||
| obj.IsMoving = true; | |||
| obj.IsMoving = 1; | |||
| } | |||
| new Thread | |||
| @@ -139,7 +139,7 @@ namespace GameEngine | |||
| if (isEnded) | |||
| { | |||
| obj.IsMoving = false; | |||
| obj.IsMoving = 0; | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| @@ -184,7 +184,7 @@ namespace GameEngine | |||
| } | |||
| if (isEnded) | |||
| { | |||
| obj.IsMoving = false; | |||
| obj.IsMoving = 0; | |||
| EndMove(obj); | |||
| return; | |||
| } | |||
| @@ -224,7 +224,7 @@ namespace GameEngine | |||
| } | |||
| } while (flag); | |||
| } | |||
| obj.IsMoving = false; // 结束移动 | |||
| obj.IsMoving = 0; // 结束移动 | |||
| EndMove(obj); | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using System.Numerics; | |||
| using System.Threading; | |||
| using GameClass.GameObj; | |||
| using GameEngine; | |||
| @@ -13,26 +14,6 @@ namespace Gaming | |||
| private readonly ActionManager actionManager; | |||
| private class ActionManager | |||
| { | |||
| // 人物移动 | |||
| private void SkillWhenColliding(Character player, IGameObj collisionObj) | |||
| { | |||
| if (collisionObj.Type == GameObjType.Bullet) | |||
| { | |||
| if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | |||
| { | |||
| if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0) | |||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); | |||
| gameMap.Remove((GameObj)collisionObj); | |||
| } | |||
| } | |||
| if (player.FindActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed == 1 && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost()) | |||
| { | |||
| if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0) | |||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); | |||
| characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||
| } | |||
| } | |||
| public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | |||
| { | |||
| if (moveTimeInMilliseconds < 5) return false; | |||
| @@ -80,7 +61,7 @@ namespace Gaming | |||
| return true; | |||
| } | |||
| public bool Stop(Character player) | |||
| public static bool Stop(Character player) | |||
| { | |||
| lock (player.ActionLock) | |||
| { | |||
| @@ -95,7 +76,7 @@ namespace Gaming | |||
| public bool Fix(Student player)// 自动检查有无发电机可修 | |||
| { | |||
| if (player.CharacterType == CharacterType.Teacher || (!player.Commandable()) || player.PlayerState == PlayerStateType.Fixing) | |||
| if ((!player.Commandable()) || player.PlayerState == PlayerStateType.Fixing) | |||
| return false; | |||
| Generator? generatorForFix = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator); | |||
| @@ -449,8 +430,11 @@ namespace Gaming | |||
| ) | |||
| .Start(); | |||
| doorToLock.StopLock(); | |||
| if (stateNum == player.StateNum) player.SetPlayerState(); | |||
| player.ReleaseTool(propType); | |||
| lock (player.ActionLock) | |||
| { | |||
| if (stateNum == player.StateNum) player.SetPlayerStateNaturally(); | |||
| } | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| @@ -500,9 +484,12 @@ namespace Gaming | |||
| player.ReleaseTool(propType); | |||
| lock (player.ActionLock) | |||
| { | |||
| if (stateNum == player.StateNum) player.SetPlayerState(); | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.SetPlayerStateNaturally(); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| player.ThreadNum.Release(); | |||
| } | |||
| else | |||
| { | |||
| @@ -512,7 +499,7 @@ namespace Gaming | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| player.SetPlayerState(); | |||
| player.SetPlayerStateNaturally(); | |||
| doorToLock.StopOpen(); | |||
| player.ReleaseTool(propType); | |||
| player.ThreadNum.Release(); | |||
| @@ -561,7 +548,37 @@ namespace Gaming | |||
| gameMap: gameMap, | |||
| OnCollision: (obj, collisionObj, moveVec) => | |||
| { | |||
| SkillWhenColliding((Character)obj, collisionObj); | |||
| Character player = (Character)obj; | |||
| switch (collisionObj.Type) | |||
| { | |||
| case GameObjType.Bullet: | |||
| if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | |||
| { | |||
| if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0) | |||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); | |||
| gameMap.Remove((GameObj)collisionObj); | |||
| } | |||
| break; | |||
| case GameObjType.Character: | |||
| if (player.FindActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed == 1 && ((Character)collisionObj).IsGhost()) | |||
| { | |||
| if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0) | |||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); | |||
| characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||
| } | |||
| break; | |||
| case GameObjType.Item: | |||
| if (((Item)collisionObj).GetPropType() == PropType.CraftingBench) | |||
| { | |||
| ((CraftingBench)collisionObj).TryStopSkill(); | |||
| gameMap.Remove((Item)collisionObj); | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| //Preparation.Utility.Debugger.Output(obj, " end move with " + collisionObj.ToString()); | |||
| //if (collisionObj is Mine) | |||
| //{ | |||
| @@ -15,7 +15,7 @@ namespace Gaming | |||
| private class AttackManager | |||
| { | |||
| readonly Map gameMap; | |||
| readonly MoveEngine moveEngine; | |||
| public readonly MoveEngine moveEngine; | |||
| readonly CharacterManager characterManager; | |||
| public AttackManager(Map gameMap, CharacterManager characterManager) | |||
| @@ -72,7 +72,14 @@ namespace Gaming | |||
| break; | |||
| case GameObjType.Door: | |||
| if (bullet.CanBeBombed(GameObjType.Door)) | |||
| ; | |||
| ((Door)objBeingShot).ForceToOpen(); | |||
| break; | |||
| case GameObjType.Item: | |||
| if (((Item)objBeingShot).GetPropType() == PropType.CraftingBench) | |||
| { | |||
| ((CraftingBench)objBeingShot).TryStopSkill(); | |||
| gameMap.Remove(objBeingShot); | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| @@ -102,6 +109,17 @@ namespace Gaming | |||
| else return false; | |||
| } | |||
| private void ProduceBombBomb(Bullet bullet, double angle) | |||
| { | |||
| angle += bullet.FacingDirection.Angle(); | |||
| XY pos = bullet.Position + new XY | |||
| ( | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) | |||
| ); | |||
| ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos); | |||
| } | |||
| private void BulletBomb(Bullet bullet, GameObj? objBeingShot) | |||
| { | |||
| #if DEBUG | |||
| @@ -137,21 +155,14 @@ namespace Gaming | |||
| if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) | |||
| { | |||
| double angle = bullet.FacingDirection.Angle() + Math.PI / 2.0; | |||
| XY pos = bullet.Position + new XY // 子弹紧贴人物生成。 | |||
| ( | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) | |||
| ); | |||
| ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos); | |||
| angle = bullet.FacingDirection.Angle() + Math.PI * 3.0 / 2.0; | |||
| pos = bullet.Position + new XY // 子弹紧贴人物生成。 | |||
| ( | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), | |||
| (int)(Math.Abs((bullet.Radius + BulletFactory.BulletRadius(BulletType.JumpyDumpty)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle)) | |||
| ); | |||
| ProduceBulletNaturally(BulletType.JumpyDumpty, (Character)bullet.Parent!, angle, pos); | |||
| ProduceBombBomb(bullet, 0); | |||
| ProduceBombBomb(bullet, Math.PI / 4); | |||
| ProduceBombBomb(bullet, Math.PI / 2); | |||
| ProduceBombBomb(bullet, Math.PI * 3 / 4); | |||
| ProduceBombBomb(bullet, Math.PI); | |||
| ProduceBombBomb(bullet, Math.PI * 5 / 4); | |||
| ProduceBombBomb(bullet, Math.PI * 3 / 2); | |||
| ProduceBombBomb(bullet, Math.PI * 7 / 4); | |||
| } | |||
| var beAttackedList = new List<IGameObj>(); | |||
| @@ -383,7 +383,15 @@ namespace Gaming | |||
| if (parent != null && parent.CharacterType == CharacterType.TechOtaku) | |||
| { | |||
| ((SummonGolem)(parent.FindActiveSkill(ActiveSkillType.SummonGolem))).DeleteGolem((int)(player.PlayerID - parent.PlayerID) / GameData.numOfPeople - 1); | |||
| //player.FindActiveSkill(ActiveSkillType.UseRobot).IsBeingUsed = false; | |||
| UseRobot useRobot = (UseRobot)parent.FindActiveSkill(ActiveSkillType.UseRobot); | |||
| if (useRobot.TryResetNowPlayerID((int)player.PlayerID)) | |||
| { | |||
| lock (parent.ActionLock) | |||
| { | |||
| if (parent.PlayerState == PlayerStateType.UsingSkill) | |||
| parent.SetPlayerState(); | |||
| } | |||
| } | |||
| } | |||
| return; | |||
| } | |||
| @@ -160,7 +160,7 @@ namespace Gaming | |||
| Character? player = gameMap.FindPlayerToAction(playerID); | |||
| if (player != null) | |||
| { | |||
| return actionManager.Stop(player); | |||
| return ActionManager.Stop(player); | |||
| } | |||
| return false; | |||
| } | |||
| @@ -5,8 +5,6 @@ using Preparation.Utility; | |||
| using System; | |||
| using Timothy.FrameRateTask; | |||
| using GameEngine; | |||
| using System.Numerics; | |||
| using System.Reflection; | |||
| namespace Gaming | |||
| { | |||
| @@ -76,6 +76,7 @@ namespace Gaming | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkIntervalWhenShowTime, | |||
| maxTotalDuration: skill.DurationTime, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| @@ -104,17 +105,39 @@ namespace Gaming | |||
| { }); | |||
| } | |||
| public bool UseRobot(Character player) | |||
| public bool UseRobot(Character player, int robotID) | |||
| { | |||
| /* | |||
| IGolem? golem = (IGolem?)(((SummonGolem)player.FindActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned); | |||
| if ((!player.Commandable()) || ((SummonGolem)player.FindActiveSkill(ActiveSkillType.SummonGolem)).GolemSummoned == null) return false; | |||
| Debugger.Output(player, "use robot!"); | |||
| IActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.UseRobot); | |||
| activeSkill.IsBeingUsed = (activeSkill.IsBeingUsed) ? false : true; | |||
| if (activeSkill.IsBeingUsed) player.SetPlayerState(PlayerStateType.UsingSkill); | |||
| else player.SetPlayerState();*/ | |||
| return true; | |||
| if ((robotID - player.PlayerID) % GameData.numOfPeople != 0) return false; | |||
| if ((robotID - (int)player.PlayerID) / GameData.numOfPeople < 0 || (robotID - (int)player.PlayerID) / GameData.numOfPeople > GameData.maxSummonedGolemNum) return false; | |||
| UseRobot activeSkill = (UseRobot)player.FindActiveSkill(ActiveSkillType.UseRobot); | |||
| lock (activeSkill.ActiveSkillUseLock) | |||
| { | |||
| if (robotID == player.PlayerID) | |||
| { | |||
| lock (player.ActionLock) | |||
| { | |||
| if (player.PlayerState == PlayerStateType.UsingSkill && player.WhatInteractingWith == null) | |||
| player.SetPlayerStateNaturally(); | |||
| activeSkill.NowPlayerID = robotID; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| SummonGolem summonGolemSkill = (SummonGolem)player.FindActiveSkill(ActiveSkillType.SummonGolem); | |||
| if (summonGolemSkill.GolemStateArray[(robotID - (int)player.PlayerID) / GameData.numOfPeople - 1] == 2) | |||
| { | |||
| activeSkill.NowPlayerID = robotID; | |||
| } | |||
| else return false; | |||
| long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill); | |||
| if (stateNum == -1) | |||
| { | |||
| activeSkill.NowPlayerID = (int)player.PlayerID; | |||
| return false; | |||
| } | |||
| } | |||
| return ActiveSkillEffect(activeSkill, player, () => { }, () => { }); | |||
| } | |||
| } | |||
| public static bool JumpyBomb(Character player) | |||
| @@ -129,6 +152,62 @@ namespace Gaming | |||
| { player.BulletOfPlayer = player.OriBulletOfPlayer; }); | |||
| } | |||
| public bool SparksNSplash(Character player, int AttackID) | |||
| { | |||
| Character? whoAttacked = gameMap.FindPlayer(AttackID); | |||
| if (whoAttacked == null || whoAttacked.NoHp()) return false; | |||
| ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SparksNSplash); | |||
| return ActiveSkillEffect(activeSkill, player, () => | |||
| { | |||
| new Thread | |||
| ( | |||
| () => | |||
| { | |||
| Bullet? homingMissile = null; | |||
| double dis; | |||
| new FrameRateTaskExecutor<int>( | |||
| loopCondition: () => gameMap.Timer.IsGaming && !whoAttacked.NoHp(), | |||
| loopToDo: () => | |||
| { | |||
| dis = ((homingMissile == null || homingMissile.IsRemoved) ? double.MaxValue : XY.DistanceFloor3(homingMissile.Position, whoAttacked.Position)); | |||
| gameMap.GameObjLockDict[GameObjType.Bullet].EnterReadLock(); | |||
| try | |||
| { | |||
| foreach (Bullet bullet in gameMap.GameObjDict[GameObjType.Bullet]) | |||
| { | |||
| if (!bullet.CanMove && XY.DistanceFloor3(bullet.Position, whoAttacked.Position) < dis && bullet.TypeOfBullet == BulletType.JumpyDumpty) | |||
| { | |||
| homingMissile = bullet; | |||
| dis = XY.DistanceFloor3(bullet.Position, whoAttacked.Position); | |||
| } | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| gameMap.GameObjLockDict[GameObjType.Bullet].ExitReadLock(); | |||
| } | |||
| if (homingMissile != null) | |||
| { | |||
| homingMissile.ReSetCanMove(true); | |||
| attackManager.moveEngine.MoveObj(homingMissile, GameData.checkIntervalWhenSparksNSplash - 1, (whoAttacked.Position - homingMissile.Position).Angle(), ++homingMissile.StateNum); | |||
| } | |||
| }, | |||
| timeInterval: GameData.checkIntervalWhenSparksNSplash, | |||
| maxTotalDuration: activeSkill.DurationTime, | |||
| finallyReturn: () => 0 | |||
| ) | |||
| .Start(); | |||
| } | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| Debugger.Output(player, "uses sparks n splash!"); | |||
| }, | |||
| () => | |||
| { }); | |||
| } | |||
| public bool WriteAnswers(Character player) | |||
| { | |||
| if ((!player.Commandable())) return false; | |||
| @@ -151,24 +230,61 @@ namespace Gaming | |||
| public bool SummonGolem(Character player) | |||
| { | |||
| ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SummonGolem); | |||
| long num = ((SummonGolem)activeSkill).AddGolem(); | |||
| int num = ((SummonGolem)activeSkill).BuildGolem(); | |||
| if (num >= GameData.maxSummonedGolemNum) return false; | |||
| num = (num + 1) * GameData.numOfPeople + player.PlayerID; | |||
| /* if ((!player.Commandable())) return false; | |||
| XY res = player.Position + new XY(player.FacingDirection, player.Radius * 2); | |||
| if (actionManager.moveEngine.CheckCollision(player, res) != null) | |||
| return false; | |||
| Golem? golem = (Golem?)characterManager.AddPlayer(res, player.TeamID, player.PlayerID + GameData.numOfPeople, CharacterType.Robot, player); | |||
| if (golem == null) return false; | |||
| ((SummonGolem)activeSkill).GolemSummoned = golem; | |||
| */ | |||
| return ActiveSkillEffect(activeSkill, player, () => | |||
| XY res = player.Position + new XY(player.FacingDirection, player.Radius * 2); | |||
| lock (activeSkill.ActiveSkillUseLock) | |||
| { | |||
| }, | |||
| () => | |||
| { }); | |||
| CraftingBench craftingBench = new(res, player, num); | |||
| long stateNum = player.SetPlayerState(PlayerStateType.UsingSkill, craftingBench); | |||
| if (stateNum == -1) | |||
| { | |||
| ((SummonGolem)activeSkill).DeleteGolem(num); | |||
| return false; | |||
| } | |||
| player.ThreadNum.WaitOne(); | |||
| if (stateNum != player.StateNum) | |||
| { | |||
| ((SummonGolem)activeSkill).DeleteGolem(num); | |||
| player.ThreadNum.Release(); | |||
| return false; | |||
| } | |||
| if (actionManager.moveEngine.CheckCollision(craftingBench, res) != null) | |||
| { | |||
| ((SummonGolem)activeSkill).DeleteGolem(num); | |||
| player.ThreadNum.Release(); | |||
| return false; | |||
| } | |||
| craftingBench.ParentStateNum = stateNum; | |||
| gameMap.Add(craftingBench); | |||
| /* | |||
| */ | |||
| return ActiveSkillEffect(activeSkill, player, () => | |||
| { | |||
| }, | |||
| () => | |||
| { | |||
| lock (player.ActionLock) | |||
| { | |||
| if (stateNum == player.StateNum) | |||
| { | |||
| gameMap.RemoveJustFromMap(craftingBench); | |||
| Golem? golem = (Golem?)characterManager.AddPlayer(res, player.TeamID, (num + 1) * GameData.numOfPeople + player.PlayerID, CharacterType.Robot, player); | |||
| if (golem == null) | |||
| { | |||
| ((SummonGolem)activeSkill).AddGolem(num); | |||
| } | |||
| player.SetPlayerStateNaturally(); | |||
| player.ThreadNum.Release(); | |||
| } | |||
| } | |||
| } | |||
| ); | |||
| } | |||
| } | |||
| public static bool UseKnife(Character player) | |||
| @@ -312,7 +428,7 @@ namespace Gaming | |||
| { | |||
| foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) | |||
| { | |||
| if (gameMap.CanSee(player, character)) | |||
| if (gameMap.CanSee(player, character) && !character.IsGhost()) | |||
| { | |||
| player.AddScore(GameData.ScoreInspire); | |||
| character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire); | |||
| @@ -336,6 +452,7 @@ namespace Gaming | |||
| if (activeSkill.TimeUntilActiveSkillAvailable == 0) | |||
| { | |||
| activeSkill.TimeUntilActiveSkillAvailable = activeSkill.SkillCD; | |||
| new Thread | |||
| (() => | |||
| { | |||
| @@ -53,6 +53,10 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束 | |||
| ) | |||
| { IsBackground = true }.Start(); | |||
| } | |||
| public void Lucky(Character player) | |||
| { | |||
| player.PropInventory[0] = PropFactory.GetConsumables((PropType)((4 * Environment.TickCount) % 5 + 4), new XY(0, 0)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -32,12 +32,14 @@ namespace Gaming | |||
| return Punish(character); | |||
| case ActiveSkillType.JumpyBomb: | |||
| return JumpyBomb(character); | |||
| case ActiveSkillType.SparksNSplash: | |||
| return SparksNSplash(character, parameter); | |||
| case ActiveSkillType.WriteAnswers: | |||
| return WriteAnswers(character); | |||
| case ActiveSkillType.SummonGolem: | |||
| return SummonGolem(character); | |||
| case ActiveSkillType.UseRobot: | |||
| return UseRobot(character); | |||
| return UseRobot(character, parameter); | |||
| case ActiveSkillType.Rouse: | |||
| return Rouse(character); | |||
| case ActiveSkillType.ShowTime: | |||
| @@ -47,7 +49,7 @@ namespace Gaming | |||
| } | |||
| return false; | |||
| } | |||
| public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | |||
| /*public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | |||
| { | |||
| if (character.Occupation.ListOfIPassiveSkill.Contains(passiveSkillType)) | |||
| switch (passiveSkillType) | |||
| @@ -59,7 +61,7 @@ namespace Gaming | |||
| return; | |||
| } | |||
| return; | |||
| } | |||
| }*/ | |||
| public void UseAllPassiveSkill(Character character) | |||
| { | |||
| foreach (var passiveSkill in character.Occupation.ListOfIPassiveSkill) | |||
| @@ -68,6 +70,9 @@ namespace Gaming | |||
| case PassiveSkillType.Meditate: | |||
| Meditate(character); | |||
| break; | |||
| case PassiveSkillType.Lucky: | |||
| Lucky(character); | |||
| break; | |||
| default: | |||
| return; | |||
| } | |||
| @@ -3,7 +3,7 @@ using Preparation.Utility; | |||
| namespace Preparation.Interface | |||
| { | |||
| public interface ICharacter : IGameObj | |||
| public interface ICharacter : IMoveable | |||
| { | |||
| public long TeamID { get; } | |||
| public int HP { get; set; } | |||
| @@ -13,7 +13,9 @@ namespace Preparation.Interface | |||
| public PlayerStateType PlayerState { get; } | |||
| public BulletType BulletOfPlayer { get; set; } | |||
| public CharacterType CharacterType { get; } | |||
| public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType); | |||
| public int UpdateBulletNum(int time); | |||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); | |||
| public bool IsGhost(); | |||
| } | |||
| @@ -8,7 +8,7 @@ namespace Preparation.Interface | |||
| { | |||
| object ActionLock { get; } | |||
| public int MoveSpeed { get; } | |||
| public bool IsMoving { get; set; } | |||
| public int IsMoving { get; set; } | |||
| public bool IsRemoved { get; } | |||
| public bool IsAvailableForMove { get; } | |||
| public long StateNum { get; } | |||
| @@ -70,8 +70,8 @@ namespace Preparation.Interface | |||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.JumpyBomb }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.JumpyBomb, ActiveSkillType.SparksNSplash }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { PassiveSkillType.Lucky }); | |||
| public double concealment = GameData.basicConcealment; | |||
| public double Concealment => concealment; | |||
| @@ -169,7 +169,7 @@ namespace Preparation.Interface | |||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.Punish }); | |||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| public const int fixSpeed = 0; | |||
| public const int fixSpeed = 50; | |||
| public int FixSpeed => fixSpeed; | |||
| public const int treatSpeed = (int)(GameData.basicTreatSpeed * 0.8); | |||
| @@ -178,16 +178,16 @@ namespace Preparation.Interface | |||
| public const double concealment = GameData.basicConcealment * 0.5; | |||
| public double Concealment => concealment; | |||
| public const int alertnessRadius = GameData.basicStudentAlertnessRadius / 2; | |||
| public const int alertnessRadius = GameData.basicStudentAlertnessRadius * 2 / 3; | |||
| public int AlertnessRadius => alertnessRadius; | |||
| public int viewRange = GameData.basicStudentViewRange * 9 / 10; | |||
| public int viewRange = GameData.basicStudentViewRange * 8 / 10; | |||
| public int ViewRange => viewRange; | |||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | |||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | |||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows / 2; | |||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows * 1000 / 1222; | |||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | |||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | |||
| @@ -32,7 +32,10 @@ namespace Preparation.Interface | |||
| private int timeUntilActiveSkillAvailable = 0; | |||
| public int TimeUntilActiveSkillAvailable | |||
| { | |||
| get => Interlocked.CompareExchange(ref timeUntilActiveSkillAvailable, 0, 1); | |||
| get | |||
| { | |||
| return Interlocked.CompareExchange(ref timeUntilActiveSkillAvailable, 0, 1); | |||
| } | |||
| set | |||
| { | |||
| if (value < 0) value = 0; | |||
| @@ -103,6 +106,12 @@ namespace Preparation.Interface | |||
| public override int DurationTime => GameData.commonSkillTime * 3 / 10; | |||
| } | |||
| public class SparksNSplash : ActiveSkill | |||
| { | |||
| public override int SkillCD => GameData.commonSkillCD * 45 / 30; | |||
| public override int DurationTime => GameData.commonSkillTime; | |||
| } | |||
| public class UseKnife : ActiveSkill | |||
| { | |||
| public override int SkillCD => GameData.commonSkillCD; | |||
| @@ -117,8 +126,32 @@ namespace Preparation.Interface | |||
| private int nowPlayerID; | |||
| public int NowPlayerID | |||
| { | |||
| get => Interlocked.CompareExchange(ref nowPlayerID, 0, 1); | |||
| set => Interlocked.Exchange(ref nowPlayerID, value); | |||
| get | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| return nowPlayerID; | |||
| } | |||
| } | |||
| set | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| nowPlayerID = value; | |||
| } | |||
| } | |||
| } | |||
| public bool TryResetNowPlayerID(int tryPlayerID) | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| if (nowPlayerID == tryPlayerID) | |||
| { | |||
| nowPlayerID %= GameData.numOfPeople; | |||
| return true; | |||
| } | |||
| else return false; | |||
| } | |||
| } | |||
| } | |||
| @@ -138,14 +171,15 @@ namespace Preparation.Interface | |||
| public class SummonGolem : ActiveSkill | |||
| { | |||
| public override int SkillCD => GameData.commonSkillCD * 4 / 3; | |||
| public override int DurationTime => 6; | |||
| public override int DurationTime => 6000; | |||
| private bool[] golemIDArray = new bool[GameData.maxSummonedGolemNum] { false, false, false }; | |||
| public bool[] GolemIDArray | |||
| private int[] golemStateArray = new int[GameData.maxSummonedGolemNum] { 0, 0, 0 }; | |||
| //0未建造,1建造中,2已建造 | |||
| public int[] GolemStateArray | |||
| { | |||
| get | |||
| { | |||
| lock (SkillLock) { return golemIDArray; } | |||
| lock (SkillLock) { return golemStateArray; } | |||
| } | |||
| } | |||
| private int nowPtr = 0; | |||
| @@ -156,14 +190,14 @@ namespace Preparation.Interface | |||
| lock (SkillLock) { return nowPtr; } | |||
| } | |||
| } | |||
| public int AddGolem() | |||
| public int BuildGolem() | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| if (nowPtr == GameData.maxSummonedGolemNum) return GameData.maxSummonedGolemNum; | |||
| int num = nowPtr; | |||
| golemIDArray[nowPtr] = true; | |||
| while ((++nowPtr) < GameData.maxSummonedGolemNum && golemIDArray[nowPtr]) ; | |||
| golemStateArray[nowPtr] = 1; | |||
| while ((++nowPtr) < GameData.maxSummonedGolemNum && golemStateArray[nowPtr] != 0) ; | |||
| return num; | |||
| } | |||
| } | |||
| @@ -171,13 +205,20 @@ namespace Preparation.Interface | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| golemIDArray[num] = false; | |||
| golemStateArray[num] = 0; | |||
| if (num < nowPtr) | |||
| { | |||
| nowPtr = num; | |||
| } | |||
| } | |||
| } | |||
| public void AddGolem(int num) | |||
| { | |||
| lock (SkillLock) | |||
| { | |||
| golemStateArray[num] = 2; | |||
| } | |||
| } | |||
| } | |||
| public class NullSkill : ActiveSkill | |||
| @@ -206,6 +247,8 @@ namespace Preparation.Interface | |||
| return new Punish(); | |||
| case ActiveSkillType.JumpyBomb: | |||
| return new JumpyBomb(); | |||
| case ActiveSkillType.SparksNSplash: | |||
| return new SparksNSplash(); | |||
| case ActiveSkillType.WriteAnswers: | |||
| return new WriteAnswers(); | |||
| case ActiveSkillType.SummonGolem: | |||
| @@ -243,6 +286,8 @@ namespace Preparation.Interface | |||
| return ActiveSkillType.Punish; | |||
| case JumpyBomb: | |||
| return ActiveSkillType.JumpyBomb; | |||
| case SparksNSplash: | |||
| return ActiveSkillType.SparksNSplash; | |||
| case WriteAnswers: | |||
| return ActiveSkillType.WriteAnswers; | |||
| case SummonGolem: | |||
| @@ -8,6 +8,12 @@ namespace Preparation.Utility | |||
| { | |||
| #if DEBUG | |||
| Console.WriteLine(current.GetType() + " " + current.ToString() + " " + str); | |||
| #endif | |||
| } | |||
| static public void Output(string str) | |||
| { | |||
| #if DEBUG | |||
| Console.WriteLine(str); | |||
| #endif | |||
| } | |||
| } | |||
| @@ -105,11 +105,12 @@ namespace Preparation.Utility | |||
| Encourage = 12, | |||
| Inspire = 13, | |||
| ShowTime = 14, | |||
| SparksNSplash = 15, | |||
| } | |||
| public enum PassiveSkillType | |||
| { | |||
| Null = 0, | |||
| BecomeInvisible = 1, | |||
| Lucky = 1, | |||
| BecomeVampire = 2, | |||
| NuclearWeapon = 3, | |||
| SuperFast = 4, | |||
| @@ -220,13 +220,14 @@ namespace Preparation.Utility | |||
| public const int timeOfStudentStunnedWhenHowl = 5500; | |||
| public const int timeOfStunnedWhenJumpyDumpty = 3070; | |||
| public const int checkIntervalWhenSparksNSplash = 100; | |||
| public const double addedTimeOfSpeedWhenInspire = 1.6; | |||
| public const int timeOfAddingSpeedWhenInspire = 6000; | |||
| public const int addHpWhenEncourage = basicHp / 4; | |||
| public const int checkIntervalWhenShowTime = 200; | |||
| public const int checkIntervalWhenShowTime = 500; | |||
| public const int addAddictionPer100msWhenShowTime = 300; | |||
| public const int maxSummonedGolemNum = 3; | |||
| @@ -55,6 +55,8 @@ namespace Preparation.Utility | |||
| return Protobuf.PropType.ShieldOrSpear; | |||
| case Preparation.Utility.PropType.RecoveryFromDizziness: | |||
| return Protobuf.PropType.RecoveryFromDizziness; | |||
| case PropType.CraftingBench: | |||
| return Protobuf.PropType.CraftingBench; | |||
| case Preparation.Utility.PropType.Key3: | |||
| return Protobuf.PropType.Key3; | |||
| case Preparation.Utility.PropType.Key5: | |||