| @@ -54,10 +54,10 @@ v1.6 | |||||
| - 视野范围由9000降为8000 | - 视野范围由9000降为8000 | ||||
| - 翻窗速度改为1000 | - 翻窗速度改为1000 | ||||
| - 特质: | - 特质: | ||||
| - 扣血则得分50×受到伤害/基本伤害(1500000) | |||||
| - 扣血则得分100×受到伤害/基本伤害(1500000) | |||||
| - 技能惩罚(Punish)改为 | - 技能惩罚(Punish)改为 | ||||
| - CD:45s | - CD:45s | ||||
| - “使用瞬间,在**视野距离/4范围内(不是可视范围)的**翻窗、开锁门、攻击前后摇、**使用技能期间**的捣蛋鬼会被眩晕(3070+**500***已受伤害/基本伤害(1500000))ms” | |||||
| - “使用瞬间,在**视野距离/3范围内(不是可视范围)的**翻窗、开锁门、攻击前后摇、**使用技能期间**的捣蛋鬼会被眩晕(3070+**500***已受伤害/基本伤害(1500000))ms” | |||||
| - 技能喝茶(HaveTea)(新增) | - 技能喝茶(HaveTea)(新增) | ||||
| - CD:90s | - CD:90s | ||||
| - 在有队友受过伤的情况下,使用瞬间,向当前方向瞬移3000(可以穿墙),如果会碰撞则失败。 | - 在有队友受过伤的情况下,使用瞬间,向当前方向瞬移3000(可以穿墙),如果会碰撞则失败。 | ||||
| @@ -38,8 +38,11 @@ | |||||
| - feat:Robot可用 | - feat:Robot可用 | ||||
| - hotfix: 修复了移动相关的bug | - hotfix: 修复了移动相关的bug | ||||
| # 最新更新 | |||||
| # 5月19日13:00更新 | |||||
| - feat:TechOtaku可用 | - feat:TechOtaku可用 | ||||
| - feat:Klee、Idol已调整 | - feat:Klee、Idol已调整 | ||||
| - fix:修复了InSpire会给Tricker加速的问题 | - fix:修复了InSpire会给Tricker加速的问题 | ||||
| - fix:修复了开锁门的bug | |||||
| - fix:修复了开锁门的bug | |||||
| # 最新更新 | |||||
| - docs:更新了 游戏机制与平衡性调整更新草案.pdf | |||||
| @@ -8,20 +8,48 @@ namespace GameClass.GameObj | |||||
| public CommonAttackOfGhost(Character player, XY pos, int radius = GameData.bulletRadius) : | public CommonAttackOfGhost(Character player, XY pos, int radius = GameData.bulletRadius) : | ||||
| base(player, radius, pos) | base(player, radius, pos) | ||||
| { | { | ||||
| ap = GameData.basicApOfGhost; | |||||
| } | } | ||||
| public override double BulletBombRange => 0; | public override double BulletBombRange => 0; | ||||
| public override double AttackDistance => GameData.basicAttackShortRange; | public override double AttackDistance => GameData.basicAttackShortRange; | ||||
| public int ap = GameData.basicApOfGhost; | |||||
| public override int AP | |||||
| public override int Speed => GameData.basicBulletMoveSpeed; | |||||
| public override bool IsRemoteAttack => false; | |||||
| public override int CastTime => (int)AttackDistance * 1000 / Speed; | |||||
| public override int Backswing => GameData.basicBackswing; | |||||
| public override int RecoveryFromHit => GameData.basicRecoveryFromHit; | |||||
| public const int cd = GameData.basicBackswing; | |||||
| public override int CD => cd; | |||||
| public const int maxBulletNum = 1; | |||||
| public override int MaxBulletNum => maxBulletNum; | |||||
| public override bool CanAttack(GameObj target) | |||||
| { | { | ||||
| get => ap; | |||||
| set | |||||
| return false; | |||||
| } | |||||
| public override bool CanBeBombed(GameObjType gameObjType) | |||||
| { | |||||
| switch (gameObjType) | |||||
| { | { | ||||
| lock (gameObjLock) | |||||
| ap = value; | |||||
| case GameObjType.Character: | |||||
| return true; | |||||
| default: | |||||
| return false; | |||||
| } | } | ||||
| } | } | ||||
| public override int Speed => GameData.basicBulletMoveSpeed; | |||||
| public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; | |||||
| } | |||||
| internal sealed class Strike : Bullet | |||||
| { | |||||
| public Strike(Character player, XY pos, int radius = GameData.bulletRadius) : | |||||
| base(player, radius, pos) | |||||
| { | |||||
| ap = GameData.basicApOfGhost * 16 / 15; | |||||
| } | |||||
| public override double BulletBombRange => 0; | |||||
| public override double AttackDistance => GameData.basicAttackShortRange * 20 / 22; | |||||
| public override int Speed => GameData.basicBulletMoveSpeed * 625 / 740; | |||||
| public override bool IsRemoteAttack => false; | public override bool IsRemoteAttack => false; | ||||
| public override int CastTime => (int)AttackDistance * 1000 / Speed; | public override int CastTime => (int)AttackDistance * 1000 / Speed; | ||||
| @@ -41,12 +69,13 @@ namespace GameClass.GameObj | |||||
| switch (gameObjType) | switch (gameObjType) | ||||
| { | { | ||||
| case GameObjType.Character: | case GameObjType.Character: | ||||
| case GameObjType.Generator: | |||||
| return true; | return true; | ||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; | |||||
| public override BulletType TypeOfBullet => BulletType.Strike; | |||||
| } | } | ||||
| internal sealed class FlyingKnife : Bullet | internal sealed class FlyingKnife : Bullet | ||||
| @@ -54,19 +83,11 @@ namespace GameClass.GameObj | |||||
| public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) : | public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) : | ||||
| base(player, radius, pos) | base(player, radius, pos) | ||||
| { | { | ||||
| ap = GameData.basicApOfGhost * 4 / 5; | |||||
| } | } | ||||
| public override double BulletBombRange => 0; | public override double BulletBombRange => 0; | ||||
| public override double AttackDistance => GameData.basicRemoteAttackRange * 13; | public override double AttackDistance => GameData.basicRemoteAttackRange * 13; | ||||
| public int ap = GameData.basicApOfGhost * 4 / 5; | |||||
| public override int AP | |||||
| { | |||||
| get => ap; | |||||
| set | |||||
| { | |||||
| lock (gameObjLock) | |||||
| ap = value; | |||||
| } | |||||
| } | |||||
| public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10; | public override int Speed => GameData.basicBulletMoveSpeed * 25 / 10; | ||||
| public override bool IsRemoteAttack => true; | public override bool IsRemoteAttack => true; | ||||
| @@ -102,19 +123,11 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| public BombBomb(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | public BombBomb(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | ||||
| { | { | ||||
| ap = (int)(GameData.basicApOfGhost * 6.0 / 5); | |||||
| } | } | ||||
| public override double BulletBombRange => GameData.basicBulletBombRange; | public override double BulletBombRange => GameData.basicBulletBombRange; | ||||
| public override double AttackDistance => GameData.basicAttackShortRange; | public override double AttackDistance => GameData.basicAttackShortRange; | ||||
| public int ap = (int)(GameData.basicApOfGhost * 6.0 / 5); | |||||
| public override int AP | |||||
| { | |||||
| get => ap; | |||||
| set | |||||
| { | |||||
| lock (gameObjLock) | |||||
| ap = value; | |||||
| } | |||||
| } | |||||
| public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37); | public override int Speed => (int)(GameData.basicBulletMoveSpeed * 30 / 37); | ||||
| public override bool IsRemoteAttack => false; | public override bool IsRemoteAttack => false; | ||||
| @@ -149,19 +162,11 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| public JumpyDumpty(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | public JumpyDumpty(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) | ||||
| { | { | ||||
| ap = (int)(GameData.basicApOfGhost * 0.6); | |||||
| } | } | ||||
| public override double BulletBombRange => GameData.basicBulletBombRange / 2; | public override double BulletBombRange => GameData.basicBulletBombRange / 2; | ||||
| public override double AttackDistance => GameData.basicAttackShortRange * 2; | public override double AttackDistance => GameData.basicAttackShortRange * 2; | ||||
| public int ap = (int)(GameData.basicApOfGhost * 0.6); | |||||
| public override int AP | |||||
| { | |||||
| get => ap; | |||||
| set | |||||
| { | |||||
| lock (gameObjLock) | |||||
| ap = value; | |||||
| } | |||||
| } | |||||
| public override int Speed => (int)(GameData.basicBulletMoveSpeed * 43 / 37); | public override int Speed => (int)(GameData.basicBulletMoveSpeed * 43 / 37); | ||||
| public override bool IsRemoteAttack => false; | public override bool IsRemoteAttack => false; | ||||
| @@ -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 | ||||
| { | { | ||||
| @@ -10,7 +11,16 @@ namespace GameClass.GameObj | |||||
| /// </summary> | /// </summary> | ||||
| public abstract double BulletBombRange { get; } | public abstract double BulletBombRange { get; } | ||||
| public abstract double AttackDistance { get; } | public abstract double AttackDistance { get; } | ||||
| public abstract int AP { get; set; } | |||||
| protected int ap; | |||||
| public int AP | |||||
| { | |||||
| get => Interlocked.CompareExchange(ref ap, 0, 1); | |||||
| } | |||||
| public void AddAP(int addAp) | |||||
| { | |||||
| Interlocked.Add(ref ap, addAp); | |||||
| } | |||||
| public abstract int Speed { get; } | public abstract int Speed { get; } | ||||
| public abstract bool IsRemoteAttack { get; } | public abstract bool IsRemoteAttack { get; } | ||||
| public abstract int CastTime { get; } | public abstract int CastTime { get; } | ||||
| @@ -106,7 +106,7 @@ namespace GameClass.GameObj | |||||
| ); | ); | ||||
| Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer); | Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer); | ||||
| if (bullet == null) return null; | if (bullet == null) return null; | ||||
| bullet.AP += TryAddAp() ? GameData.ApPropAdd : 0; | |||||
| if (TryAddAp()) bullet.AddAP(GameData.ApPropAdd); | |||||
| facingDirection = new(angle, bullet.AttackDistance); | facingDirection = new(angle, bullet.AttackDistance); | ||||
| return bullet; | return bullet; | ||||
| } | } | ||||
| @@ -361,7 +361,9 @@ namespace GameClass.GameObj | |||||
| { | { | ||||
| lock (actionLock) | lock (actionLock) | ||||
| { | { | ||||
| if (playerState == PlayerStateType.Moving && IsMoving == 1) return PlayerStateType.Moving; | |||||
| if (playerState == PlayerStateType.Moving) | |||||
| if (IsMoving == 1) return PlayerStateType.Moving; | |||||
| else return PlayerStateType.Null; | |||||
| return playerState; | return playerState; | ||||
| } | } | ||||
| } | } | ||||
| @@ -555,7 +555,7 @@ namespace Gaming | |||||
| if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty) | ||||
| { | { | ||||
| if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0) | |||||
| if (CharacterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty) > 0) | |||||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); | player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty)); | ||||
| gameMap.Remove((GameObj)collisionObj); | gameMap.Remove((GameObj)collisionObj); | ||||
| } | } | ||||
| @@ -563,9 +563,9 @@ namespace Gaming | |||||
| case GameObjType.Character: | case GameObjType.Character: | ||||
| if (player.FindActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed == 1 && ((Character)collisionObj).IsGhost()) | if (player.FindActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed == 1 && ((Character)collisionObj).IsGhost()) | ||||
| { | { | ||||
| if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0) | |||||
| if (CharacterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge) > 0) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); | player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge)); | ||||
| characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||||
| CharacterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||||
| } | } | ||||
| break; | break; | ||||
| case GameObjType.Item: | case GameObjType.Item: | ||||
| @@ -240,7 +240,7 @@ namespace Gaming | |||||
| { IsBackground = true }.Start(); | { IsBackground = true }.Start(); | ||||
| } | } | ||||
| public long BeStunned(Character player, int time) | |||||
| public static long BeStunned(Character player, int time) | |||||
| { | { | ||||
| if (player.CharacterType == CharacterType.Robot) return -1; | if (player.CharacterType == CharacterType.Robot) return -1; | ||||
| long threadNum = player.SetPlayerState(PlayerStateType.Stunned); | long threadNum = player.SetPlayerState(PlayerStateType.Stunned); | ||||
| @@ -324,6 +324,11 @@ namespace Gaming | |||||
| #endif | #endif | ||||
| } | } | ||||
| bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); | bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); | ||||
| if (student.CharacterType == CharacterType.Teacher) | |||||
| { | |||||
| student.AddScore(subHp * GameData.factorOfScoreWhenTeacherAttacked / GameData.basicApOfGhost); | |||||
| } | |||||
| bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | bullet.Parent.HP = (int)(bullet.Parent.HP + (bullet.Parent.Vampire * subHp)); | ||||
| } | } | ||||
| if (student.HP <= 0) | if (student.HP <= 0) | ||||
| @@ -310,7 +310,7 @@ namespace Gaming | |||||
| { | { | ||||
| if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange) | if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange) | ||||
| { | { | ||||
| if (characterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0) | |||||
| if (CharacterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0) | |||||
| player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl)); | player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl)); | ||||
| } | } | ||||
| } | } | ||||
| @@ -341,9 +341,9 @@ namespace Gaming | |||||
| || character.PlayerState == PlayerStateType.UsingSkill | || character.PlayerState == PlayerStateType.UsingSkill | ||||
| || character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor | || character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor | ||||
| || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | || character.PlayerState == PlayerStateType.ClimbingThroughWindows) | ||||
| && gameMap.CanSee(player, character)) | |||||
| && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange / 3) | |||||
| { | { | ||||
| if (characterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)) > 0) | |||||
| if (CharacterManager.BeStunned(character, GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP)) > 0) | |||||
| player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))); | player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenPunish + GameData.factorOfTimeStunnedWhenPunish * (player.MaxHp - player.HP))); | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -130,7 +130,7 @@ namespace Preparation.Interface | |||||
| private const int maxHp = GameData.basicHp * 12 / 10; | private const int maxHp = GameData.basicHp * 12 / 10; | ||||
| public int MaxHp => maxHp; | public int MaxHp => maxHp; | ||||
| public BulletType InitBullet => BulletType.CommonAttackOfGhost; | |||||
| public BulletType InitBullet => BulletType.Strike; | |||||
| public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.Howl }); | public List<ActiveSkillType> ListOfIActiveSkill => new(new ActiveSkillType[] { ActiveSkillType.Howl }); | ||||
| public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | public List<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | ||||
| @@ -66,7 +66,7 @@ namespace Preparation.Interface | |||||
| public class Punish : ActiveSkill | public class Punish : ActiveSkill | ||||
| { | { | ||||
| public override int SkillCD => GameData.commonSkillCD; | |||||
| public override int SkillCD => GameData.commonSkillCD * 45 / 30; | |||||
| public override int DurationTime => 0; | public override int DurationTime => 0; | ||||
| } | } | ||||
| @@ -59,7 +59,7 @@ namespace Preparation.Utility | |||||
| CommonAttackOfGhost = 2, | CommonAttackOfGhost = 2, | ||||
| JumpyDumpty = 3, | JumpyDumpty = 3, | ||||
| BombBomb = 4, | BombBomb = 4, | ||||
| // Ram = 7, | |||||
| Strike = 8, | |||||
| } | } | ||||
| public enum PropType // 道具类型 | public enum PropType // 道具类型 | ||||
| {//numOfPropSpecies 见Gamedata | {//numOfPropSpecies 见Gamedata | ||||
| @@ -106,6 +106,7 @@ namespace Preparation.Utility | |||||
| Inspire = 13, | Inspire = 13, | ||||
| ShowTime = 14, | ShowTime = 14, | ||||
| SparksNSplash = 15, | SparksNSplash = 15, | ||||
| HaveTea = 16, | |||||
| } | } | ||||
| public enum PassiveSkillType | public enum PassiveSkillType | ||||
| { | { | ||||
| @@ -205,7 +205,7 @@ namespace Preparation.Utility | |||||
| public const double basicAttackShortRange = 2200; // 基本近程攻击范围 | public const double basicAttackShortRange = 2200; // 基本近程攻击范围 | ||||
| public const double basicBulletBombRange = 2000; // 基本子弹爆炸范围 | public const double basicBulletBombRange = 2000; // 基本子弹爆炸范围 | ||||
| #endregion | #endregion | ||||
| #region 技能相关 | |||||
| #region 技能、特性相关 | |||||
| public const int maxNumOfSkill = 3; | public const int maxNumOfSkill = 3; | ||||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | ||||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | public const int commonSkillTime = 10000; // 普通技能标准持续时间 | ||||
| @@ -214,7 +214,8 @@ namespace Preparation.Utility | |||||
| public const int timeOfStudentStunnedWhenCharge = 2090; | public const int timeOfStudentStunnedWhenCharge = 2090; | ||||
| public const int timeOfGhostStunnedWhenPunish = 3070; | public const int timeOfGhostStunnedWhenPunish = 3070; | ||||
| public const int factorOfTimeStunnedWhenPunish = 300 / basicApOfGhost; | |||||
| public const int factorOfTimeStunnedWhenPunish = 500 / basicApOfGhost; | |||||
| public const int factorOfScoreWhenTeacherAttacked = 100;//每个 | |||||
| public const int timeOfGhostSwingingAfterHowl = 800; | public const int timeOfGhostSwingingAfterHowl = 800; | ||||
| public const int timeOfStudentStunnedWhenHowl = 5500; | public const int timeOfStudentStunnedWhenHowl = 5500; | ||||