| @@ -54,10 +54,10 @@ v1.6 | |||
| - 视野范围由9000降为8000 | |||
| - 翻窗速度改为1000 | |||
| - 特质: | |||
| - 扣血则得分50×受到伤害/基本伤害(1500000) | |||
| - 扣血则得分100×受到伤害/基本伤害(1500000) | |||
| - 技能惩罚(Punish)改为 | |||
| - CD:45s | |||
| - “使用瞬间,在**视野距离/4范围内(不是可视范围)的**翻窗、开锁门、攻击前后摇、**使用技能期间**的捣蛋鬼会被眩晕(3070+**500***已受伤害/基本伤害(1500000))ms” | |||
| - “使用瞬间,在**视野距离/3范围内(不是可视范围)的**翻窗、开锁门、攻击前后摇、**使用技能期间**的捣蛋鬼会被眩晕(3070+**500***已受伤害/基本伤害(1500000))ms” | |||
| - 技能喝茶(HaveTea)(新增) | |||
| - CD:90s | |||
| - 在有队友受过伤的情况下,使用瞬间,向当前方向瞬移3000(可以穿墙),如果会碰撞则失败。 | |||
| @@ -38,8 +38,11 @@ | |||
| - feat:Robot可用 | |||
| - hotfix: 修复了移动相关的bug | |||
| # 最新更新 | |||
| # 5月19日13:00更新 | |||
| - feat:TechOtaku可用 | |||
| - feat:Klee、Idol已调整 | |||
| - 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) : | |||
| base(player, radius, pos) | |||
| { | |||
| ap = GameData.basicApOfGhost; | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| 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 int CastTime => (int)AttackDistance * 1000 / Speed; | |||
| @@ -41,12 +69,13 @@ namespace GameClass.GameObj | |||
| switch (gameObjType) | |||
| { | |||
| case GameObjType.Character: | |||
| case GameObjType.Generator: | |||
| return true; | |||
| default: | |||
| return false; | |||
| } | |||
| } | |||
| public override BulletType TypeOfBullet => BulletType.CommonAttackOfGhost; | |||
| public override BulletType TypeOfBullet => BulletType.Strike; | |||
| } | |||
| internal sealed class FlyingKnife : Bullet | |||
| @@ -54,19 +83,11 @@ namespace GameClass.GameObj | |||
| public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) : | |||
| base(player, radius, pos) | |||
| { | |||
| ap = GameData.basicApOfGhost * 4 / 5; | |||
| } | |||
| public override double BulletBombRange => 0; | |||
| 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 bool IsRemoteAttack => true; | |||
| @@ -102,19 +123,11 @@ namespace GameClass.GameObj | |||
| { | |||
| 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 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 bool IsRemoteAttack => false; | |||
| @@ -149,19 +162,11 @@ namespace GameClass.GameObj | |||
| { | |||
| 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 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 bool IsRemoteAttack => false; | |||
| @@ -1,5 +1,6 @@ | |||
| using Preparation.Interface; | |||
| using Preparation.Utility; | |||
| using System.Threading; | |||
| namespace GameClass.GameObj | |||
| { | |||
| @@ -10,7 +11,16 @@ namespace GameClass.GameObj | |||
| /// </summary> | |||
| public abstract double BulletBombRange { 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 bool IsRemoteAttack { get; } | |||
| public abstract int CastTime { get; } | |||
| @@ -106,7 +106,7 @@ namespace GameClass.GameObj | |||
| ); | |||
| Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer); | |||
| if (bullet == null) return null; | |||
| bullet.AP += TryAddAp() ? GameData.ApPropAdd : 0; | |||
| if (TryAddAp()) bullet.AddAP(GameData.ApPropAdd); | |||
| facingDirection = new(angle, bullet.AttackDistance); | |||
| return bullet; | |||
| } | |||
| @@ -361,7 +361,9 @@ namespace GameClass.GameObj | |||
| { | |||
| 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; | |||
| } | |||
| } | |||
| @@ -555,7 +555,7 @@ namespace Gaming | |||
| 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)); | |||
| gameMap.Remove((GameObj)collisionObj); | |||
| } | |||
| @@ -563,9 +563,9 @@ namespace Gaming | |||
| case GameObjType.Character: | |||
| 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)); | |||
| characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||
| CharacterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge); | |||
| } | |||
| break; | |||
| case GameObjType.Item: | |||
| @@ -240,7 +240,7 @@ namespace Gaming | |||
| { 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; | |||
| long threadNum = player.SetPlayerState(PlayerStateType.Stunned); | |||
| @@ -324,6 +324,11 @@ namespace Gaming | |||
| #endif | |||
| } | |||
| 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)); | |||
| } | |||
| if (student.HP <= 0) | |||
| @@ -310,7 +310,7 @@ namespace Gaming | |||
| { | |||
| 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)); | |||
| } | |||
| } | |||
| @@ -341,9 +341,9 @@ namespace Gaming | |||
| || character.PlayerState == PlayerStateType.UsingSkill | |||
| || character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor | |||
| || 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))); | |||
| break; | |||
| } | |||
| @@ -130,7 +130,7 @@ namespace Preparation.Interface | |||
| private const int maxHp = GameData.basicHp * 12 / 10; | |||
| 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<PassiveSkillType> ListOfIPassiveSkill => new(new PassiveSkillType[] { }); | |||
| @@ -66,7 +66,7 @@ namespace Preparation.Interface | |||
| public class Punish : ActiveSkill | |||
| { | |||
| public override int SkillCD => GameData.commonSkillCD; | |||
| public override int SkillCD => GameData.commonSkillCD * 45 / 30; | |||
| public override int DurationTime => 0; | |||
| } | |||
| @@ -59,7 +59,7 @@ namespace Preparation.Utility | |||
| CommonAttackOfGhost = 2, | |||
| JumpyDumpty = 3, | |||
| BombBomb = 4, | |||
| // Ram = 7, | |||
| Strike = 8, | |||
| } | |||
| public enum PropType // 道具类型 | |||
| {//numOfPropSpecies 见Gamedata | |||
| @@ -106,6 +106,7 @@ namespace Preparation.Utility | |||
| Inspire = 13, | |||
| ShowTime = 14, | |||
| SparksNSplash = 15, | |||
| HaveTea = 16, | |||
| } | |||
| public enum PassiveSkillType | |||
| { | |||
| @@ -205,7 +205,7 @@ namespace Preparation.Utility | |||
| public const double basicAttackShortRange = 2200; // 基本近程攻击范围 | |||
| public const double basicBulletBombRange = 2000; // 基本子弹爆炸范围 | |||
| #endregion | |||
| #region 技能相关 | |||
| #region 技能、特性相关 | |||
| public const int maxNumOfSkill = 3; | |||
| public const int commonSkillCD = 30000; // 普通技能标准冷却时间 | |||
| public const int commonSkillTime = 10000; // 普通技能标准持续时间 | |||
| @@ -214,7 +214,8 @@ namespace Preparation.Utility | |||
| public const int timeOfStudentStunnedWhenCharge = 2090; | |||
| 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 timeOfStudentStunnedWhenHowl = 5500; | |||