feat: ✨ adjust the CharacterType Klee and TechOtaku
tags/v0.1.0
| @@ -80,7 +80,8 @@ v1.6 | |||||
| - 新造的Robot的PlayerId的n总是尽量小 | - 新造的Robot的PlayerId的n总是尽量小 | ||||
| 1. UseRobot | 1. UseRobot | ||||
| - CD:2s,持续时间:0s | - CD:2s,持续时间:0s | ||||
| - 输入额外参数PlayerID,切换到要使用的角色 | |||||
| - 输入额外参数PlayerID,切换到要使用的角色。 | |||||
| - 切换到其他角色时,自己进入UsingSkill状态。 | |||||
| 修改后: | 修改后: | ||||
| @@ -107,7 +108,7 @@ v1.6 | |||||
| - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | - 被动技能Lucky!(新增):开局获得随机的一个道具(不会是钥匙) | ||||
| - 主动技能SparksNSplash(新增): | - 主动技能SparksNSplash(新增): | ||||
| - CD:45s, 持续时间:10s | - CD:45s, 持续时间:10s | ||||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每50ms向该角色直线移动) | |||||
| - 技能使用瞬间,对于输入的额外参数PlayerID代表的角色,距离最近的本已停止运动的小炸弹开始追踪该角色(每100ms向该角色直线移动)(该角色无血量则失败) | |||||
| - 主动技能 蹦蹦炸弹 JumpyBomb | - 主动技能 蹦蹦炸弹 JumpyBomb | ||||
| - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹 | - 当蹦蹦炸弹因为碰撞而爆炸,向子弹方向上加上0°,45°,90°,135°,180°,225°,270°,315° 发出8个小炸弹 | ||||
| - Idol | - Idol | ||||
| @@ -36,4 +36,10 @@ | |||||
| # 5月15日更新 | # 5月15日更新 | ||||
| - feat:Robot可用 | - 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未更新 | 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(); | UpperLayerOfMap.Children.Clear(); | ||||
| foreach (var data in listOfAll) | foreach (var data in listOfAll) | ||||
| @@ -885,7 +885,7 @@ namespace Client | |||||
| } | } | ||||
| foreach (var data in listOfHuman) | foreach (var data in listOfHuman) | ||||
| { | { | ||||
| if (data.StudentType != StudentType.Robot) | |||||
| if (data.PlayerId < GameData.numOfStudent) | |||||
| StatusBarsOfSurvivor[data.PlayerId].SetValue(data, data.PlayerId); | StatusBarsOfSurvivor[data.PlayerId].SetValue(data, data.PlayerId); | ||||
| if (CanSee(data)) | 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, | PlayerId = playerID, | ||||
| SkillId = 0, | SkillId = 0, | ||||
| SkillParam = 0, | |||||
| }; | }; | ||||
| client.UseSkill(msgB); | client.UseSkill(msgB); | ||||
| break; | break; | ||||
| @@ -197,6 +197,19 @@ namespace GameClass.GameObj | |||||
| return false; | 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 void AddClairaudience(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Clairaudience, () => | ||||
| { }); | { }); | ||||
| public bool HasClairaudience | public bool HasClairaudience | ||||
| @@ -361,7 +361,7 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| lock (actionLock) | lock (actionLock) | ||||
| { | { | ||||
| if (playerState == PlayerStateType.Null && IsMoving) return PlayerStateType.Moving; | |||||
| if (playerState == PlayerStateType.Moving && IsMoving == 1) return PlayerStateType.Moving; | |||||
| return playerState; | return playerState; | ||||
| } | } | ||||
| } | } | ||||
| @@ -423,9 +423,7 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| //只能被SetPlayerState引用 | //只能被SetPlayerState引用 | ||||
| whatInteractingWith = gameObj; | whatInteractingWith = gameObj; | ||||
| if (value != PlayerStateType.Moving) | |||||
| IsMoving = false; | |||||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||||
| playerState = value; | |||||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | ||||
| return ++stateNum; | return ++stateNum; | ||||
| } | } | ||||
| @@ -434,20 +432,19 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| //只能被SetPlayerState引用 | //只能被SetPlayerState引用 | ||||
| whatInteractingWith = gameObj; | whatInteractingWith = gameObj; | ||||
| if (value != PlayerStateType.Moving) | |||||
| IsMoving = false; | |||||
| playerState = (value == PlayerStateType.Moving) ? PlayerStateType.Null : value; | |||||
| playerState = value; | |||||
| //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | //Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString()); | ||||
| return stateNum; | 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) | lock (actionLock) | ||||
| { | { | ||||
| PlayerStateType nowPlayerState = PlayerState; | PlayerStateType nowPlayerState = PlayerState; | ||||
| if (nowPlayerState == value) return -1; | |||||
| if (nowPlayerState == value && value != PlayerStateType.UsingSkill) return -1; | |||||
| switch (nowPlayerState) | switch (nowPlayerState) | ||||
| { | { | ||||
| case PlayerStateType.Escaped: | case PlayerStateType.Escaped: | ||||
| @@ -468,20 +465,21 @@ namespace GameClass.GameObj | |||||
| else return -1; | else return -1; | ||||
| case PlayerStateType.TryingToAttack: | 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); | return ChangePlayerState(value, gameObj); | ||||
| else return -1; | else return -1; | ||||
| case PlayerStateType.Stunned: | case PlayerStateType.Stunned: | ||||
| case PlayerStateType.Charmed: | 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); | return ChangePlayerState(value, gameObj); | ||||
| else return -1; | else return -1; | ||||
| case PlayerStateType.Swinging: | 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 | try | ||||
| { | { | ||||
| @@ -494,7 +492,9 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| else return -1; | else return -1; | ||||
| case PlayerStateType.ClimbingThroughWindows: | 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!; | Window window = (Window)WhatInteractingWith!; | ||||
| try | try | ||||
| @@ -537,6 +537,28 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| ThreadNum.Release(); | 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: | default: | ||||
| return ChangePlayerState(value, gameObj); | return ChangePlayerState(value, gameObj); | ||||
| } | } | ||||
| @@ -548,7 +570,6 @@ namespace GameClass.GameObj | |||||
| lock (actionLock) | lock (actionLock) | ||||
| { | { | ||||
| whatInteractingWith = null; | whatInteractingWith = null; | ||||
| IsMoving = false; | |||||
| playerState = PlayerStateType.Null; | playerState = PlayerStateType.Null; | ||||
| return ++stateNum; | return ++stateNum; | ||||
| } | } | ||||
| @@ -777,6 +798,7 @@ namespace GameClass.GameObj | |||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| public void TryActivatingLIFE() | public void TryActivatingLIFE() | ||||
| { | { | ||||
| if (buffManager.TryActivatingLIFE()) | if (buffManager.TryActivatingLIFE()) | ||||
| @@ -801,6 +823,11 @@ namespace GameClass.GameObj | |||||
| return buffManager.TryUseSpear(); | return buffManager.TryUseSpear(); | ||||
| } | } | ||||
| public bool TryDeleteInvisible() | |||||
| { | |||||
| return buffManager.TryDeleteInvisible(); | |||||
| } | |||||
| public bool TryUseShield() | public bool TryUseShield() | ||||
| { | { | ||||
| if (buffManager.TryUseShield()) | if (buffManager.TryUseShield()) | ||||
| @@ -36,6 +36,35 @@ namespace GameClass.GameObj | |||||
| public abstract ShapeType Shape { get; } | 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 int Radius { get; } | ||||
| public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; | public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; | ||||
| @@ -97,7 +97,7 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| if (whoLockOrOpen != null) | if (whoLockOrOpen != null) | ||||
| { | { | ||||
| if (Environment.TickCount64 - openStartTime >= GameData.degreeOfLockingOrOpeningTheDoor) | |||||
| if ((Environment.TickCount64 - openStartTime) >= GameData.degreeOfLockingOrOpeningTheDoor / whoLockOrOpen.SpeedOfOpeningOrLocking) | |||||
| isOpen = true; | isOpen = true; | ||||
| whoLockOrOpen = null; | whoLockOrOpen = null; | ||||
| } | } | ||||
| @@ -128,7 +128,7 @@ namespace GameClass.GameObj | |||||
| lock (gameObjLock) | lock (gameObjLock) | ||||
| { | { | ||||
| if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | if (lockDegree >= GameData.degreeOfLockingOrOpeningTheDoor) | ||||
| isOpen = true; | |||||
| isOpen = false; | |||||
| whoLockOrOpen = null; | whoLockOrOpen = null; | ||||
| } | } | ||||
| } | } | ||||
| @@ -231,7 +231,10 @@ namespace GameClass.GameObj | |||||
| } | } | ||||
| } | } | ||||
| if (ToDel != null) | if (ToDel != null) | ||||
| { | |||||
| GameObjDict[gameObj.Type].Remove(ToDel); | GameObjDict[gameObj.Type].Remove(ToDel); | ||||
| ToDel.TryToRemove(); | |||||
| } | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| @@ -242,16 +245,19 @@ namespace GameClass.GameObj | |||||
| public bool RemoveJustFromMap(GameObj gameObj) | public bool RemoveJustFromMap(GameObj gameObj) | ||||
| { | { | ||||
| GameObjLockDict[gameObj.Type].EnterWriteLock(); | GameObjLockDict[gameObj.Type].EnterWriteLock(); | ||||
| bool flag; | |||||
| try | try | ||||
| { | { | ||||
| flag = GameObjDict[gameObj.Type].Remove(gameObj); | |||||
| if (GameObjDict[gameObj.Type].Remove(gameObj)) | |||||
| { | |||||
| gameObj.TryToRemove(); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| GameObjLockDict[gameObj.Type].ExitWriteLock(); | GameObjLockDict[gameObj.Type].ExitWriteLock(); | ||||
| } | } | ||||
| return flag; | |||||
| } | } | ||||
| public void Add(GameObj gameObj) | 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 | get | ||||
| { | { | ||||
| @@ -152,7 +141,7 @@ namespace GameClass.GameObj | |||||
| moveReaderWriterLock.EnterReadLock(); | moveReaderWriterLock.EnterReadLock(); | ||||
| try | try | ||||
| { | { | ||||
| return !isMoving && canMove && !isRemoved; | |||||
| return isMoving == 0 && canMove && !isRemoved; | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| @@ -1,5 +1,4 @@ | |||||
| using Google.Protobuf.WellKnownTypes; | |||||
| using Preparation.Interface; | |||||
| using Preparation.Interface; | |||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using System.Threading; | using System.Threading; | ||||
| @@ -1,5 +1,6 @@ | |||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using System.Threading; | |||||
| namespace GameClass.GameObj | namespace GameClass.GameObj | ||||
| { | { | ||||
| @@ -32,9 +33,32 @@ namespace GameClass.GameObj | |||||
| public sealed class CraftingBench : Item | public sealed class CraftingBench : Item | ||||
| { | { | ||||
| public CraftingBench(XY initPos) : | |||||
| public CraftingBench(XY initPos, Character character, int num) : | |||||
| base(initPos) | 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; | public override PropType GetPropType() => PropType.CraftingBench; | ||||
| } | } | ||||
| @@ -102,7 +102,7 @@ namespace GameEngine | |||||
| lock (obj.ActionLock) | lock (obj.ActionLock) | ||||
| { | { | ||||
| if (!obj.IsAvailableForMove) { EndMove(obj); return; } | if (!obj.IsAvailableForMove) { EndMove(obj); return; } | ||||
| obj.IsMoving = true; | |||||
| obj.IsMoving = 1; | |||||
| } | } | ||||
| new Thread | new Thread | ||||
| @@ -139,7 +139,7 @@ namespace GameEngine | |||||
| if (isEnded) | if (isEnded) | ||||
| { | { | ||||
| obj.IsMoving = false; | |||||
| obj.IsMoving = 0; | |||||
| EndMove(obj); | EndMove(obj); | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -184,7 +184,7 @@ namespace GameEngine | |||||
| } | } | ||||
| if (isEnded) | if (isEnded) | ||||
| { | { | ||||
| obj.IsMoving = false; | |||||
| obj.IsMoving = 0; | |||||
| EndMove(obj); | EndMove(obj); | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -224,7 +224,7 @@ namespace GameEngine | |||||
| } | } | ||||
| } while (flag); | } while (flag); | ||||
| } | } | ||||
| obj.IsMoving = false; // 结束移动 | |||||
| obj.IsMoving = 0; // 结束移动 | |||||
| EndMove(obj); | EndMove(obj); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | using System; | ||||
| using System.Numerics; | |||||
| using System.Threading; | using System.Threading; | ||||
| using GameClass.GameObj; | using GameClass.GameObj; | ||||
| using GameEngine; | using GameEngine; | ||||
| @@ -13,26 +14,6 @@ namespace Gaming | |||||
| private readonly ActionManager actionManager; | private readonly ActionManager actionManager; | ||||
| private class 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) | public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection) | ||||
| { | { | ||||
| if (moveTimeInMilliseconds < 5) return false; | if (moveTimeInMilliseconds < 5) return false; | ||||
| @@ -80,7 +61,7 @@ namespace Gaming | |||||
| return true; | return true; | ||||
| } | } | ||||
| public bool Stop(Character player) | |||||
| public static bool Stop(Character player) | |||||
| { | { | ||||
| lock (player.ActionLock) | lock (player.ActionLock) | ||||
| { | { | ||||
| @@ -95,7 +76,7 @@ namespace Gaming | |||||
| public bool Fix(Student player)// 自动检查有无发电机可修 | 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; | return false; | ||||
| Generator? generatorForFix = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator); | Generator? generatorForFix = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator); | ||||
| @@ -449,8 +430,11 @@ namespace Gaming | |||||
| ) | ) | ||||
| .Start(); | .Start(); | ||||
| doorToLock.StopLock(); | doorToLock.StopLock(); | ||||
| if (stateNum == player.StateNum) player.SetPlayerState(); | |||||
| player.ReleaseTool(propType); | player.ReleaseTool(propType); | ||||
| lock (player.ActionLock) | |||||
| { | |||||
| if (stateNum == player.StateNum) player.SetPlayerStateNaturally(); | |||||
| } | |||||
| player.ThreadNum.Release(); | player.ThreadNum.Release(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -500,9 +484,12 @@ namespace Gaming | |||||
| player.ReleaseTool(propType); | player.ReleaseTool(propType); | ||||
| lock (player.ActionLock) | lock (player.ActionLock) | ||||
| { | { | ||||
| if (stateNum == player.StateNum) player.SetPlayerState(); | |||||
| if (stateNum == player.StateNum) | |||||
| { | |||||
| player.SetPlayerStateNaturally(); | |||||
| player.ThreadNum.Release(); | |||||
| } | |||||
| } | } | ||||
| player.ThreadNum.Release(); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -512,7 +499,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (stateNum == player.StateNum) | if (stateNum == player.StateNum) | ||||
| { | { | ||||
| player.SetPlayerState(); | |||||
| player.SetPlayerStateNaturally(); | |||||
| doorToLock.StopOpen(); | doorToLock.StopOpen(); | ||||
| player.ReleaseTool(propType); | player.ReleaseTool(propType); | ||||
| player.ThreadNum.Release(); | player.ThreadNum.Release(); | ||||
| @@ -561,7 +548,37 @@ namespace Gaming | |||||
| gameMap: gameMap, | gameMap: gameMap, | ||||
| OnCollision: (obj, collisionObj, moveVec) => | 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()); | //Preparation.Utility.Debugger.Output(obj, " end move with " + collisionObj.ToString()); | ||||
| //if (collisionObj is Mine) | //if (collisionObj is Mine) | ||||
| //{ | //{ | ||||
| @@ -15,7 +15,7 @@ namespace Gaming | |||||
| private class AttackManager | private class AttackManager | ||||
| { | { | ||||
| readonly Map gameMap; | readonly Map gameMap; | ||||
| readonly MoveEngine moveEngine; | |||||
| public readonly MoveEngine moveEngine; | |||||
| readonly CharacterManager characterManager; | readonly CharacterManager characterManager; | ||||
| public AttackManager(Map gameMap, CharacterManager characterManager) | public AttackManager(Map gameMap, CharacterManager characterManager) | ||||
| @@ -72,7 +72,14 @@ namespace Gaming | |||||
| break; | break; | ||||
| case GameObjType.Door: | case GameObjType.Door: | ||||
| if (bullet.CanBeBombed(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; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| @@ -102,6 +109,17 @@ namespace Gaming | |||||
| else return false; | 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) | private void BulletBomb(Bullet bullet, GameObj? objBeingShot) | ||||
| { | { | ||||
| #if DEBUG | #if DEBUG | ||||
| @@ -137,21 +155,14 @@ namespace Gaming | |||||
| if (bullet.TypeOfBullet == BulletType.BombBomb && objBeingShot != null) | 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>(); | var beAttackedList = new List<IGameObj>(); | ||||
| @@ -383,7 +383,15 @@ namespace Gaming | |||||
| if (parent != null && parent.CharacterType == CharacterType.TechOtaku) | if (parent != null && parent.CharacterType == CharacterType.TechOtaku) | ||||
| { | { | ||||
| ((SummonGolem)(parent.FindActiveSkill(ActiveSkillType.SummonGolem))).DeleteGolem((int)(player.PlayerID - parent.PlayerID) / GameData.numOfPeople - 1); | ((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; | return; | ||||
| } | } | ||||
| @@ -160,7 +160,7 @@ namespace Gaming | |||||
| Character? player = gameMap.FindPlayerToAction(playerID); | Character? player = gameMap.FindPlayerToAction(playerID); | ||||
| if (player != null) | if (player != null) | ||||
| { | { | ||||
| return actionManager.Stop(player); | |||||
| return ActionManager.Stop(player); | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -5,8 +5,6 @@ using Preparation.Utility; | |||||
| using System; | using System; | ||||
| using Timothy.FrameRateTask; | using Timothy.FrameRateTask; | ||||
| using GameEngine; | using GameEngine; | ||||
| using System.Numerics; | |||||
| using System.Reflection; | |||||
| namespace Gaming | namespace Gaming | ||||
| { | { | ||||
| @@ -76,6 +76,7 @@ namespace Gaming | |||||
| } | } | ||||
| }, | }, | ||||
| timeInterval: GameData.checkIntervalWhenShowTime, | timeInterval: GameData.checkIntervalWhenShowTime, | ||||
| maxTotalDuration: skill.DurationTime, | |||||
| finallyReturn: () => 0 | 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) | public static bool JumpyBomb(Character player) | ||||
| @@ -129,6 +152,62 @@ namespace Gaming | |||||
| { player.BulletOfPlayer = player.OriBulletOfPlayer; }); | { 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) | public bool WriteAnswers(Character player) | ||||
| { | { | ||||
| if ((!player.Commandable())) return false; | if ((!player.Commandable())) return false; | ||||
| @@ -151,24 +230,61 @@ namespace Gaming | |||||
| public bool SummonGolem(Character player) | public bool SummonGolem(Character player) | ||||
| { | { | ||||
| ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SummonGolem); | ActiveSkill activeSkill = player.FindActiveSkill(ActiveSkillType.SummonGolem); | ||||
| long num = ((SummonGolem)activeSkill).AddGolem(); | |||||
| int num = ((SummonGolem)activeSkill).BuildGolem(); | |||||
| if (num >= GameData.maxSummonedGolemNum) return false; | 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) | public static bool UseKnife(Character player) | ||||
| @@ -312,7 +428,7 @@ namespace Gaming | |||||
| { | { | ||||
| foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) | foreach (Character character in gameMap.GameObjDict[GameObjType.Character]) | ||||
| { | { | ||||
| if (gameMap.CanSee(player, character)) | |||||
| if (gameMap.CanSee(player, character) && !character.IsGhost()) | |||||
| { | { | ||||
| player.AddScore(GameData.ScoreInspire); | player.AddScore(GameData.ScoreInspire); | ||||
| character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire); | character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire); | ||||
| @@ -336,6 +452,7 @@ namespace Gaming | |||||
| if (activeSkill.TimeUntilActiveSkillAvailable == 0) | if (activeSkill.TimeUntilActiveSkillAvailable == 0) | ||||
| { | { | ||||
| activeSkill.TimeUntilActiveSkillAvailable = activeSkill.SkillCD; | activeSkill.TimeUntilActiveSkillAvailable = activeSkill.SkillCD; | ||||
| new Thread | new Thread | ||||
| (() => | (() => | ||||
| { | { | ||||
| @@ -53,6 +53,10 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束 | |||||
| ) | ) | ||||
| { IsBackground = true }.Start(); | { 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); | return Punish(character); | ||||
| case ActiveSkillType.JumpyBomb: | case ActiveSkillType.JumpyBomb: | ||||
| return JumpyBomb(character); | return JumpyBomb(character); | ||||
| case ActiveSkillType.SparksNSplash: | |||||
| return SparksNSplash(character, parameter); | |||||
| case ActiveSkillType.WriteAnswers: | case ActiveSkillType.WriteAnswers: | ||||
| return WriteAnswers(character); | return WriteAnswers(character); | ||||
| case ActiveSkillType.SummonGolem: | case ActiveSkillType.SummonGolem: | ||||
| return SummonGolem(character); | return SummonGolem(character); | ||||
| case ActiveSkillType.UseRobot: | case ActiveSkillType.UseRobot: | ||||
| return UseRobot(character); | |||||
| return UseRobot(character, parameter); | |||||
| case ActiveSkillType.Rouse: | case ActiveSkillType.Rouse: | ||||
| return Rouse(character); | return Rouse(character); | ||||
| case ActiveSkillType.ShowTime: | case ActiveSkillType.ShowTime: | ||||
| @@ -47,7 +49,7 @@ namespace Gaming | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | |||||
| /*public void UsePassiveSkill(Character character, PassiveSkillType passiveSkillType) | |||||
| { | { | ||||
| if (character.Occupation.ListOfIPassiveSkill.Contains(passiveSkillType)) | if (character.Occupation.ListOfIPassiveSkill.Contains(passiveSkillType)) | ||||
| switch (passiveSkillType) | switch (passiveSkillType) | ||||
| @@ -59,7 +61,7 @@ namespace Gaming | |||||
| return; | return; | ||||
| } | } | ||||
| return; | return; | ||||
| } | |||||
| }*/ | |||||
| public void UseAllPassiveSkill(Character character) | public void UseAllPassiveSkill(Character character) | ||||
| { | { | ||||
| foreach (var passiveSkill in character.Occupation.ListOfIPassiveSkill) | foreach (var passiveSkill in character.Occupation.ListOfIPassiveSkill) | ||||
| @@ -68,6 +70,9 @@ namespace Gaming | |||||
| case PassiveSkillType.Meditate: | case PassiveSkillType.Meditate: | ||||
| Meditate(character); | Meditate(character); | ||||
| break; | break; | ||||
| case PassiveSkillType.Lucky: | |||||
| Lucky(character); | |||||
| break; | |||||
| default: | default: | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ using Preparation.Utility; | |||||
| namespace Preparation.Interface | namespace Preparation.Interface | ||||
| { | { | ||||
| public interface ICharacter : IGameObj | |||||
| public interface ICharacter : IMoveable | |||||
| { | { | ||||
| public long TeamID { get; } | public long TeamID { get; } | ||||
| public int HP { get; set; } | public int HP { get; set; } | ||||
| @@ -13,7 +13,9 @@ namespace Preparation.Interface | |||||
| public PlayerStateType PlayerState { get; } | public PlayerStateType PlayerState { get; } | ||||
| public BulletType BulletOfPlayer { get; set; } | public BulletType BulletOfPlayer { get; set; } | ||||
| public CharacterType CharacterType { get; } | public CharacterType CharacterType { get; } | ||||
| public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType); | |||||
| public int UpdateBulletNum(int time); | public int UpdateBulletNum(int time); | ||||
| public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); | |||||
| public bool IsGhost(); | public bool IsGhost(); | ||||
| } | } | ||||
| @@ -8,7 +8,7 @@ namespace Preparation.Interface | |||||
| { | { | ||||
| object ActionLock { get; } | object ActionLock { get; } | ||||
| public int MoveSpeed { get; } | public int MoveSpeed { get; } | ||||
| public bool IsMoving { get; set; } | |||||
| public int IsMoving { get; set; } | |||||
| public bool IsRemoved { get; } | public bool IsRemoved { get; } | ||||
| public bool IsAvailableForMove { get; } | public bool IsAvailableForMove { get; } | ||||
| public long StateNum { get; } | public long StateNum { get; } | ||||
| @@ -70,8 +70,8 @@ namespace Preparation.Interface | |||||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | 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 = GameData.basicConcealment; | ||||
| public double Concealment => concealment; | public double Concealment => concealment; | ||||
| @@ -169,7 +169,7 @@ namespace Preparation.Interface | |||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.Punish }); | public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.Punish }); | ||||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | ||||
| public const int fixSpeed = 0; | |||||
| public const int fixSpeed = 50; | |||||
| public int FixSpeed => fixSpeed; | public int FixSpeed => fixSpeed; | ||||
| public const int treatSpeed = (int)(GameData.basicTreatSpeed * 0.8); | 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 const double concealment = GameData.basicConcealment * 0.5; | ||||
| public double Concealment => concealment; | 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 AlertnessRadius => alertnessRadius; | ||||
| public int viewRange = GameData.basicStudentViewRange * 9 / 10; | |||||
| public int viewRange = GameData.basicStudentViewRange * 8 / 10; | |||||
| public int ViewRange => viewRange; | public int ViewRange => viewRange; | ||||
| public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | public int speedOfOpeningOrLocking = GameData.basicSpeedOfOpeningOrLocking; | ||||
| public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; | ||||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows / 2; | |||||
| public int speedOfClimbingThroughWindows = GameData.basicStudentSpeedOfClimbingThroughWindows * 1000 / 1222; | |||||
| public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; | ||||
| public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | public int speedOfOpenChest = GameData.basicSpeedOfOpenChest; | ||||
| @@ -32,7 +32,10 @@ namespace Preparation.Interface | |||||
| private int timeUntilActiveSkillAvailable = 0; | private int timeUntilActiveSkillAvailable = 0; | ||||
| public int TimeUntilActiveSkillAvailable | public int TimeUntilActiveSkillAvailable | ||||
| { | { | ||||
| get => Interlocked.CompareExchange(ref timeUntilActiveSkillAvailable, 0, 1); | |||||
| get | |||||
| { | |||||
| return Interlocked.CompareExchange(ref timeUntilActiveSkillAvailable, 0, 1); | |||||
| } | |||||
| set | set | ||||
| { | { | ||||
| if (value < 0) value = 0; | if (value < 0) value = 0; | ||||
| @@ -103,6 +106,12 @@ namespace Preparation.Interface | |||||
| public override int DurationTime => GameData.commonSkillTime * 3 / 10; | 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 class UseKnife : ActiveSkill | ||||
| { | { | ||||
| public override int SkillCD => GameData.commonSkillCD; | public override int SkillCD => GameData.commonSkillCD; | ||||
| @@ -117,8 +126,32 @@ namespace Preparation.Interface | |||||
| private int nowPlayerID; | private int nowPlayerID; | ||||
| public 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 class SummonGolem : ActiveSkill | ||||
| { | { | ||||
| public override int SkillCD => GameData.commonSkillCD * 4 / 3; | 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 | get | ||||
| { | { | ||||
| lock (SkillLock) { return golemIDArray; } | |||||
| lock (SkillLock) { return golemStateArray; } | |||||
| } | } | ||||
| } | } | ||||
| private int nowPtr = 0; | private int nowPtr = 0; | ||||
| @@ -156,14 +190,14 @@ namespace Preparation.Interface | |||||
| lock (SkillLock) { return nowPtr; } | lock (SkillLock) { return nowPtr; } | ||||
| } | } | ||||
| } | } | ||||
| public int AddGolem() | |||||
| public int BuildGolem() | |||||
| { | { | ||||
| lock (SkillLock) | lock (SkillLock) | ||||
| { | { | ||||
| if (nowPtr == GameData.maxSummonedGolemNum) return GameData.maxSummonedGolemNum; | if (nowPtr == GameData.maxSummonedGolemNum) return GameData.maxSummonedGolemNum; | ||||
| int num = nowPtr; | int num = nowPtr; | ||||
| golemIDArray[nowPtr] = true; | |||||
| while ((++nowPtr) < GameData.maxSummonedGolemNum && golemIDArray[nowPtr]) ; | |||||
| golemStateArray[nowPtr] = 1; | |||||
| while ((++nowPtr) < GameData.maxSummonedGolemNum && golemStateArray[nowPtr] != 0) ; | |||||
| return num; | return num; | ||||
| } | } | ||||
| } | } | ||||
| @@ -171,13 +205,20 @@ namespace Preparation.Interface | |||||
| { | { | ||||
| lock (SkillLock) | lock (SkillLock) | ||||
| { | { | ||||
| golemIDArray[num] = false; | |||||
| golemStateArray[num] = 0; | |||||
| if (num < nowPtr) | if (num < nowPtr) | ||||
| { | { | ||||
| nowPtr = num; | nowPtr = num; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| public void AddGolem(int num) | |||||
| { | |||||
| lock (SkillLock) | |||||
| { | |||||
| golemStateArray[num] = 2; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| public class NullSkill : ActiveSkill | public class NullSkill : ActiveSkill | ||||
| @@ -206,6 +247,8 @@ namespace Preparation.Interface | |||||
| return new Punish(); | return new Punish(); | ||||
| case ActiveSkillType.JumpyBomb: | case ActiveSkillType.JumpyBomb: | ||||
| return new JumpyBomb(); | return new JumpyBomb(); | ||||
| case ActiveSkillType.SparksNSplash: | |||||
| return new SparksNSplash(); | |||||
| case ActiveSkillType.WriteAnswers: | case ActiveSkillType.WriteAnswers: | ||||
| return new WriteAnswers(); | return new WriteAnswers(); | ||||
| case ActiveSkillType.SummonGolem: | case ActiveSkillType.SummonGolem: | ||||
| @@ -243,6 +286,8 @@ namespace Preparation.Interface | |||||
| return ActiveSkillType.Punish; | return ActiveSkillType.Punish; | ||||
| case JumpyBomb: | case JumpyBomb: | ||||
| return ActiveSkillType.JumpyBomb; | return ActiveSkillType.JumpyBomb; | ||||
| case SparksNSplash: | |||||
| return ActiveSkillType.SparksNSplash; | |||||
| case WriteAnswers: | case WriteAnswers: | ||||
| return ActiveSkillType.WriteAnswers; | return ActiveSkillType.WriteAnswers; | ||||
| case SummonGolem: | case SummonGolem: | ||||
| @@ -8,6 +8,12 @@ namespace Preparation.Utility | |||||
| { | { | ||||
| #if DEBUG | #if DEBUG | ||||
| Console.WriteLine(current.GetType() + " " + current.ToString() + " " + str); | Console.WriteLine(current.GetType() + " " + current.ToString() + " " + str); | ||||
| #endif | |||||
| } | |||||
| static public void Output(string str) | |||||
| { | |||||
| #if DEBUG | |||||
| Console.WriteLine(str); | |||||
| #endif | #endif | ||||
| } | } | ||||
| } | } | ||||
| @@ -105,11 +105,12 @@ namespace Preparation.Utility | |||||
| Encourage = 12, | Encourage = 12, | ||||
| Inspire = 13, | Inspire = 13, | ||||
| ShowTime = 14, | ShowTime = 14, | ||||
| SparksNSplash = 15, | |||||
| } | } | ||||
| public enum PassiveSkillType | public enum PassiveSkillType | ||||
| { | { | ||||
| Null = 0, | Null = 0, | ||||
| BecomeInvisible = 1, | |||||
| Lucky = 1, | |||||
| BecomeVampire = 2, | BecomeVampire = 2, | ||||
| NuclearWeapon = 3, | NuclearWeapon = 3, | ||||
| SuperFast = 4, | SuperFast = 4, | ||||
| @@ -220,13 +220,14 @@ namespace Preparation.Utility | |||||
| public const int timeOfStudentStunnedWhenHowl = 5500; | public const int timeOfStudentStunnedWhenHowl = 5500; | ||||
| public const int timeOfStunnedWhenJumpyDumpty = 3070; | public const int timeOfStunnedWhenJumpyDumpty = 3070; | ||||
| public const int checkIntervalWhenSparksNSplash = 100; | |||||
| public const double addedTimeOfSpeedWhenInspire = 1.6; | public const double addedTimeOfSpeedWhenInspire = 1.6; | ||||
| public const int timeOfAddingSpeedWhenInspire = 6000; | public const int timeOfAddingSpeedWhenInspire = 6000; | ||||
| public const int addHpWhenEncourage = basicHp / 4; | public const int addHpWhenEncourage = basicHp / 4; | ||||
| public const int checkIntervalWhenShowTime = 200; | |||||
| public const int checkIntervalWhenShowTime = 500; | |||||
| public const int addAddictionPer100msWhenShowTime = 300; | public const int addAddictionPer100msWhenShowTime = 300; | ||||
| public const int maxSummonedGolemNum = 3; | public const int maxSummonedGolemNum = 3; | ||||
| @@ -55,6 +55,8 @@ namespace Preparation.Utility | |||||
| return Protobuf.PropType.ShieldOrSpear; | return Protobuf.PropType.ShieldOrSpear; | ||||
| case Preparation.Utility.PropType.RecoveryFromDizziness: | case Preparation.Utility.PropType.RecoveryFromDizziness: | ||||
| return Protobuf.PropType.RecoveryFromDizziness; | return Protobuf.PropType.RecoveryFromDizziness; | ||||
| case PropType.CraftingBench: | |||||
| return Protobuf.PropType.CraftingBench; | |||||
| case Preparation.Utility.PropType.Key3: | case Preparation.Utility.PropType.Key3: | ||||
| return Protobuf.PropType.Key3; | return Protobuf.PropType.Key3; | ||||
| case Preparation.Utility.PropType.Key5: | case Preparation.Utility.PropType.Key5: | ||||