diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bd6659c..b40548c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,7 @@ -v1.1.2.2更新日志 +v1.1.2.5更新日志 -* 新增驱逐显示,可自定义驱逐玩家时显示的信息 -* 优化游戏选项中常规选项的页面排版显示 -* 律师的目标如果掉线则律师会变成起诉人 - -问题修复: - -* 炸弹狂、纵火狂技能无法寻到目标 -* 怨灵会被分配到豺狼阵营 -* 非红狼阵营的职业无法使用键盘进入通风口 -* 污点证人在部分情况下无法得分 -* 鹈鹕被击杀时会卡在原地 \ No newline at end of file +* 新增船员职业:牧师 +* 删除附加能力:酒鬼 +* 在下载帽子时,如果配置文件下载失败则自动加载本地配置 +* 修复有玩家复活时会重置其他玩家技能冷却的问题 +* 修复了一些已知问题 \ No newline at end of file diff --git a/README.md b/README.md index 300b27a0..868d3d3f 100644 --- a/README.md +++ b/README.md @@ -468,21 +468,20 @@ | 送葬者 | 巴甫洛夫 | 侦探 | 溅血者 | | 逃逸者 | 巴甫洛夫的狗 | 老兵 | 通讯兵 | | 术士 | 隐身人 | 时间之主 | 破平者 | -| 骗术师 | 纵火犯 | 换票师 | 闪电侠 | +| 骗术师 | 纵火狂 | 换票师 | 闪电侠 | | 赏金猎人 | 月下狼人 | 黑客 | 多线程 | -| 恐怖分子 | 身份窃贼 | 灵媒 | 巨人 | +| 恐怖分子 | 鹈鹕 | 灵媒 | 巨人 | | 勒索者 | 天启 | 传送师 | 小孩 | | 女巫 | 末日预言家 | 追踪者 | Vip | | 忍者 | 魅魔 | 告密者 | 不屈者 | | 悠悠球 | 打工仔 | 卧底 | 反骨 | -| 邪恶的设陷师 | | 保安 | 管道工程师 | -| 肢解者 | | 通灵师 | 酒鬼 | -| 赌徒 | | 设陷师 | 胆小鬼 | -| 掷弹兵 | | 预言家 | 窥视者 | -| | | 情报师 | 雷达 | -| | | 大神官 | 执钮人 | -| | | | 变色龙 | -| | | | 交换师 | +| 邪恶的设陷师 | 身份窃贼 | 保安 | 管道工程师 | +| 肢解者 | | 通灵师 | 胆小鬼 | +| 赌徒 | | 设陷师 | 窥视者 | +| 掷弹兵 | | 预言家 | 雷达 | +| 狼之主 | | 情报师 | 执钮人 | +| | | 大神官 | 变色龙 | +| | | 牧师 | 交换师 | ## 错误报告 diff --git a/README_EN.md b/README_EN.md index 734f1405..b45f8b41 100644 --- a/README_EN.md +++ b/README_EN.md @@ -159,18 +159,17 @@ Even more roles are coming soon. | Warlock | Swooper | Time Master | Tiebreaker | | Trickster | Arsonist | Swapper | Flash | | Bounty Hunter | Werewolf | Hacker | Multitasker | -| Terrorist | Thief | Seer | Giant | +| Terrorist | Pelican | Seer | Giant | | Blackmailer | Juggernaut | Jumper | Mini | | Witch | Doomsayer | Tracker | Vip | | Ninja | Akujo | Snitch | Indomitable | | Yo-Yo | PartTimer | Spy | Slueth | -| Evil Trapper | | SecurityGuard | Cursed | -| Butcher [Beta] | | Medium | Invert | -| Gambler | | Trapper | Blind | -| Grenadier | | Prophet | Watcher | -| | | Info Sleuth | Radar | -| | | | Button Barry | -| | | | Chameleon | +| Evil Trapper | Thief | SecurityGuard | Cursed | +| Butcher [Beta] | | Medium | Blind | +| Gambler | | Trapper | Watcher | +| Grenadier | | Prophet | Radar | +| WolfLord | | Info Sleuth | Button Barry | +| | | Redemptor | Chameleon | | | | | Shifter | ## Credits & Resources diff --git a/Strings.xlsx b/Strings.xlsx index a8197b87..1f082d60 100644 Binary files a/Strings.xlsx and b/Strings.xlsx differ diff --git a/TheOtherRoles/Buttons/Buttons.cs b/TheOtherRoles/Buttons/Buttons.cs index 9e1ecdd5..de63bf6c 100644 --- a/TheOtherRoles/Buttons/Buttons.cs +++ b/TheOtherRoles/Buttons/Buttons.cs @@ -98,6 +98,9 @@ internal static class HudManagerStartPatch public static CustomButton defuseButton; public static CustomButton zoomOutButton; public static CustomButton roleSummaryButton; + public static CustomButton redemptorReviveButton; + public static CustomButton redemptorRevelationButton; + public static CustomButton redemptorPrayerButton; public static Dictionary> deputyHandcuffedButtons; public static PoolablePlayer targetDisplay; @@ -201,7 +204,9 @@ public static void setCustomButtonCooldowns() juggernautKillButton.MaxTimer = Juggernaut.cooldown; swooperKillButton.MaxTimer = Swooper.cooldown; evilTrapperSetTrapButton.MaxTimer = EvilTrapper.cooldown; - + redemptorReviveButton.MaxTimer = 10f; + redemptorRevelationButton.MaxTimer = Redemptor.revelationCooldown; + redemptorPrayerButton.MaxTimer = Redemptor.prayerCooldown; doomsayerButton.MaxTimer = Doomsayer.cooldown; akujoHonmeiButton.MaxTimer = 0f; akujoBackupButton.MaxTimer = 0f; @@ -236,6 +241,9 @@ public static void setCustomButtonCooldowns() securityGuardCamButton.EffectDuration = SecurityGuard.duration; defuseButton.EffectDuration = Terrorist.defuseDuration; terroristButton.EffectDuration = Terrorist.destructionTime + Terrorist.bombActiveAfter; + redemptorRevelationButton.EffectDuration = Redemptor.revelationDuration; + //redemptorPrayerButton.EffectDuration = Redemptor.prayerDuration; + zoomOutButton.MaxTimer = zoomOutButton.Timer = 0f; } @@ -244,11 +252,6 @@ public static void showTargetNameOnButton(PlayerControl target, CustomButton but Helpers.showTargetNameOnButton(target, button, defaultText); } - public static void showTargetNameOnButtonExplicit(PlayerControl target, CustomButton button, string defaultText) - { - Helpers.showTargetNameOnButtonExplicit(target, button, defaultText); - } - public static void resetTimeMasterButton() { timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer; @@ -3454,7 +3457,7 @@ public static void createButtonsPostfix(HudManager __instance) } } }, - buttonText: GetString("SpecterButton") + buttonText: GetString("ReviveButton") ); // Medium button @@ -4085,12 +4088,12 @@ public static void createButtonsPostfix(HudManager __instance) { // Could Use Blackmailer.currentTarget = SetTarget(); - SetPlayerOutline(Medic.currentTarget, Blackmailer.blackmailedColor); + SetPlayerOutline(Blackmailer.currentTarget, Blackmailer.blackmailedColor); - var text = GetString("BlackmailerText"); - if (Blackmailer.blackmailed != null) text = Blackmailer.blackmailed.Data.PlayerName; - //Show target name under button if setting is true - showTargetNameOnButtonExplicit(Blackmailer.currentTarget, blackmailerButton, GetString("BlackmailerText")); + if (Blackmailer.blackmailed == null) + { + showTargetNameOnButton(Blackmailer.currentTarget, blackmailerButton, GetString("BlackmailerText")); + } return Blackmailer.currentTarget != null && PlayerControl.LocalPlayer.CanMove; }, () => { blackmailerButton.Timer = blackmailerButton.MaxTimer; }, @@ -4318,8 +4321,6 @@ public static void createButtonsPostfix(HudManager __instance) trapperChargesText.transform.localScale = Vector3.one * 0.5f; trapperChargesText.transform.localPosition += new Vector3(-0.05f, 0.7f, 0); - - // Yoyo button yoyoButton = new CustomButton( () => @@ -4459,6 +4460,140 @@ public static void createButtonsPostfix(HudManager __instance) "AdminMapText".Translate() ); + redemptorRevelationButton = new CustomButton( + () => + { + Redemptor.Revelating = true; + }, + () => + { + return Redemptor.revelation && Redemptor.Player.IsAlive() && Redemptor.Player == PlayerControl.LocalPlayer; + }, + () => + { + redemptorRevelationButton.PositionOffset = Redemptor.prayer ? ButtonPositions.upperRowLeft : ButtonPositions.upperRowCenter; + return PlayerControl.LocalPlayer.CanMove; + }, + () => + { + Redemptor.Revelating = false; + redemptorRevelationButton.Timer = redemptorRevelationButton.MaxTimer; + redemptorRevelationButton.isEffectActive = false; + redemptorRevelationButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; + }, + Tracker.trackCorpsesButtonSprite, + ButtonPositions.upperRowCenter, + __instance, + secondaryAbilityInput.keyCode, + true, + Redemptor.revelationDuration, + () => + { + Redemptor.Revelating = false; + redemptorRevelationButton.Timer = redemptorRevelationButton.MaxTimer; + }, + buttonText: GetString("RedemptorRevelation") + ); + + redemptorPrayerButton = new CustomButton( + () => + { + var writer = StartRPC(PlayerControl.LocalPlayer, CustomRPC.RedemptorPrayer); + writer.Write(byte.MaxValue); + writer.EndRPC(); + Redemptor.RedemptorPrayer(byte.MaxValue); + }, + () => + { + return Redemptor.prayer && Redemptor.Player.IsAlive() && + Redemptor.Player == PlayerControl.LocalPlayer && Redemptor.RevivedPlayer == null; + }, + () => + { + return Redemptor.target && PlayerControl.LocalPlayer.CanMove; + }, + () => + { + redemptorPrayerButton.Timer = redemptorPrayerButton.MaxTimer; + redemptorPrayerButton.isEffectActive = false; + redemptorPrayerButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; + }, + Redemptor.reviveButton, + ButtonPositions.upperRowCenter, + __instance, + modKillInput.keyCode, + true, + Redemptor.prayerDuration, + () => + { + var writer = StartRPC(PlayerControl.LocalPlayer, CustomRPC.RedemptorPrayer); + writer.Write((byte)0); + writer.EndRPC(); + Redemptor.RedemptorPrayer(0); + + if (Redemptor.target != null) + { + var writer2 = StartRPC(PlayerControl.LocalPlayer, CustomRPC.RedemptorRevive); + writer2.Write(Redemptor.target.PlayerId); + writer2.EndRPC(); + Redemptor.RevivePlayer(Redemptor.target.PlayerId); + + redemptorPrayerButton.Timer = redemptorPrayerButton.MaxTimer; + } + }, + buttonText: GetString("RedemptorPrayer") + ); + + redemptorReviveButton = new CustomButton( + () => + { + if (Redemptor.target == null) return; + + var writer = StartRPC(PlayerControl.LocalPlayer, CustomRPC.UncheckedMurderPlayer); + writer.Write(PlayerControl.LocalPlayer.PlayerId); + writer.Write(PlayerControl.LocalPlayer.PlayerId); + writer.Write(byte.MaxValue); + writer.EndRPC(); + RPCProcedure.uncheckedMurderPlayer(PlayerControl.LocalPlayer.PlayerId, PlayerControl.LocalPlayer.PlayerId, byte.MaxValue); + + _ = new LateTask(() => + { + if (InMeeting) { Message("复活失败", "ReviveTask"); return; } + var writer = StartRPC(PlayerControl.LocalPlayer, CustomRPC.RedemptorRevive); + writer.Write(Redemptor.target.PlayerId); + writer.EndRPC(); + Redemptor.RevivePlayer(Redemptor.target.PlayerId); + }, Redemptor.reviveDuration, "RedemptorRevive"); + }, + () => + { + return Redemptor.Player.IsAlive() && Redemptor.Player == PlayerControl.LocalPlayer; + }, + () => + { + var pos = PlayerControl.LocalPlayer.GetTruePosition(); + var maxDistance = PlayerControl.LocalPlayer.MaxReportDistance * 0.24f; + + var deadBody = Physics2D.OverlapCircleAll(pos, maxDistance, Constants.PlayersOnlyMask) + .Where(collider => collider.CompareTag("DeadBody")) + .Select(collider => collider.GetComponent()) + .FirstOrDefault(db => db != null && playerById(db.ParentId)?.Data?.IsDead == true && + !(playerById(db.ParentId)?.Data?.Disconnected == true)); + + Redemptor.target = playerById(deadBody?.ParentId); + return Redemptor.target && PlayerControl.LocalPlayer.CanMove; + }, + () => + { + redemptorReviveButton.Timer = 10f; + }, + Redemptor.reviveButton, + ButtonPositions.upperRowRight, + __instance, + abilityInput.keyCode, + buttonText: GetString("ReviveButton") + ); + // Set the default (or settings from the previous game) timers / durations when spawning the buttons initialized = true; setCustomButtonCooldowns(); diff --git a/TheOtherRoles/Buttons/CustomButton.cs b/TheOtherRoles/Buttons/CustomButton.cs index 7d6f9d2f..ae72e6b0 100644 --- a/TheOtherRoles/Buttons/CustomButton.cs +++ b/TheOtherRoles/Buttons/CustomButton.cs @@ -131,9 +131,8 @@ public static void MeetingEndedUpdate() } } - public static void ResetAllCooldowns(float Time = -1, PlayerControl target = null) + public static void ResetAllCooldowns(float Time = -1) { - var player = target ?? PlayerControl.LocalPlayer; var time = Time == -1 ? ModOption.KillCooddown : Time; foreach (var t in buttons) { @@ -146,10 +145,10 @@ public static void ResetAllCooldowns(float Time = -1, PlayerControl target = nul } catch (Exception e) { - Error($"NullReferenceException from MeetingEndedUpdate().HasButton(), if theres only one warning its fine\n{e}", "CustomButton"); + Error($"NullReferenceException from ResetAllCooldowns(), if theres only one warning its fine\n{e}", "CustomButton"); } } - player.killTimer = time; + PlayerControl.LocalPlayer.killTimer = time; } public static void resetKillButton(PlayerControl p, float time = -1) @@ -173,7 +172,6 @@ public static void resetKillButton(PlayerControl p, float time = -1) thiefKillButton.Timer = time == -1 ? thiefKillButton.MaxTimer : time; pavlovsdogsKillButton.Timer = time == -1 ? pavlovsdogsKillButton.MaxTimer : time; } - public void setActive(bool isActive) { if (isActive) diff --git a/TheOtherRoles/CustomCosmetics/CustomColors.cs b/TheOtherRoles/CustomCosmetics/CustomColors.cs index a2049075..90e86978 100644 --- a/TheOtherRoles/CustomCosmetics/CustomColors.cs +++ b/TheOtherRoles/CustomCosmetics/CustomColors.cs @@ -126,8 +126,7 @@ public static void Load() [HarmonyPatch] public static class CustomColorPatches { - [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), typeof(StringNames), - typeof(Il2CppReferenceArray))] + [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), typeof(StringNames), typeof(Il2CppReferenceArray))] private class ColorStringPatch { [HarmonyPriority(Priority.Last)] diff --git a/TheOtherRoles/CustomCosmetics/CustomHats/HatsLoader.cs b/TheOtherRoles/CustomCosmetics/CustomHats/HatsLoader.cs index 154d1892..b1f7fe43 100644 --- a/TheOtherRoles/CustomCosmetics/CustomHats/HatsLoader.cs +++ b/TheOtherRoles/CustomCosmetics/CustomHats/HatsLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.IO; using System.Text.Json; @@ -76,6 +76,7 @@ private IEnumerator DownloadHatsConfig(string path) { Error($"下载帽子配置文件时出错: {www.error}"); isSuccessful = false; + LoadLocalHats(); yield break; } @@ -101,6 +102,7 @@ private IEnumerator DownloadHatsConfig(string path) { isSuccessful = false; Error($"未能保存或加载帽子配置文件: {ex.Message}"); + LoadLocalHats(); } finally { diff --git a/TheOtherRoles/Helper/Helpers.cs b/TheOtherRoles/Helper/Helpers.cs index 7ad029ff..9821d16a 100644 --- a/TheOtherRoles/Helper/Helpers.cs +++ b/TheOtherRoles/Helper/Helpers.cs @@ -450,40 +450,27 @@ public static void turnToImpostor(PlayerControl player) if (player2.Data.Role.IsImpostor && PlayerControl.LocalPlayer.Data.Role.IsImpostor) player.cosmetics.nameText.color = Palette.ImpostorRed; } - - public static void showTargetNameOnButton(PlayerControl target, CustomButton button, string defaultText) +#nullable enable + public static void showTargetNameOnButton(PlayerControl? target, CustomButton button, string defaultText) { if (CustomOptionHolder.showButtonTarget.GetBool()) { - // Should the button show the target name option string text; - // set text to default if camo is on if (Camouflager.camouflageTimer >= 0.1f || isCamoComms) text = defaultText; - // set to default if lights are out else if (isLightsActive) text = defaultText; - // set to default if trickster ability is active else if (Trickster.trickster != null && Trickster.lightsOutTimer > 0f) text = defaultText; - // set to morphed player else if (Morphling.morphling != null && Morphling.morphTarget != null && target == Morphling.morphling && Morphling.morphTimer > 0) text = Morphling.morphTarget.Data.PlayerName; else if (target == Ninja.ninja && Ninja.isInvisable) text = defaultText; else if (target == Swooper.swooper && Swooper.isInvisable) text = defaultText; else if (Jackal.jackal.Any(p => p == target) && Jackal.isInvisable) text = defaultText; - //else if (target == PhantomRole.phantomRole) text = defaultText; - else if (target == null) text = defaultText; // Set text to defaultText if no target - else text = target.Data.PlayerName; // Set text to playername - showTargetNameOnButtonExplicit(null, button, text); - } - } + else text = target == null ? defaultText : target.Data.PlayerName; - public static void showTargetNameOnButtonExplicit(PlayerControl target, CustomButton button, string defaultText) - { - var text = defaultText; - if (target == null) text = defaultText; // Set text to defaultText if no target - else text = target.Data.PlayerName; // Set text to playername - button.actionButton.OverrideText(text); - button.showButtonText = true; + button.actionButton.OverrideText(text); + button.showButtonText = true; + } } +#nullable disable public static void AddUnique(this Il2CppSystem.Collections.Generic.List self, T item) where T : IDisconnectHandler { @@ -742,11 +729,13 @@ public static void refreshRoleDescription(PlayerControl player) } } - internal static string getRoleString(RoleInfo roleInfo) + public static void ModRevive(this PlayerControl target) { - if (roleInfo.roleId == RoleId.Invert) - return cs(roleInfo.color, $"{roleInfo.Name}: {roleInfo.ShortDescription} \n(还有 {Invert.meetings} 次会议醒酒)"); + target?.Revive(); + } + internal static string getRoleString(RoleInfo roleInfo) + { return cs(roleInfo.color, $"{roleInfo.Name}: {roleInfo.ShortDescription}"); } @@ -824,15 +813,9 @@ public static GameObject[] GetChildren(this GameObject ParentObject) return ChildObject; } - public static bool shouldShowGhostInfo() - { - return (PlayerControl.LocalPlayer.Data.IsDead && CanSeeRoleInfo) || - AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Ended; - } - public static bool ZoomButtonActive() { - if (!shouldShowGhostInfo() || InMeeting) return false; + if (!ShowGhostInfo || InMeeting) return false; var (playerCompleted, playerTotal) = TasksHandler.taskInfo(PlayerControl.LocalPlayer.Data); var numberOfLeftTasks = playerTotal - playerCompleted; return numberOfLeftTasks <= 0 || !CustomOptionHolder.finishTasksBeforeHauntingOrZoomingOut.GetBool(); @@ -885,7 +868,7 @@ public static bool IsCN() public static string GithubUrl(this string url) { - return IsCN() && !url.Contains("ghp.ci") ? "https://ghp.ci/" + url : url; + return IsCN() && !url.Contains("ghfast.top") ? "https://ghfast.top/" + url : url; } public static void setSemiTransparent(this PoolablePlayer player, bool value, float alpha = 0.25f) @@ -1220,7 +1203,7 @@ public static MurderAttemptResult checkMuderAttempt(PlayerControl killer, Player if (Survivor.Player != null && Survivor.Player.Contains(target) && Survivor.vestActive) { - CustomButton.ResetAllCooldowns(Survivor.vestResetCooldown, killer); + CustomButton.resetKillButton(killer, Survivor.vestResetCooldown); SoundEffectsManager.play("fail"); return MurderAttemptResult.SuppressKill; } diff --git a/TheOtherRoles/Objects/Trap.cs b/TheOtherRoles/Objects/Trap.cs index 49af5758..150b68ad 100644 --- a/TheOtherRoles/Objects/Trap.cs +++ b/TheOtherRoles/Objects/Trap.cs @@ -114,7 +114,7 @@ public static void triggerTrap(byte playerId, byte trapId) t.triggerable = true; // Add trapped Info into Trapper chat - if (Trapper.trapper.IsAlive() && (PlayerControl.LocalPlayer == Trapper.trapper || shouldShowGhostInfo())) + if (Trapper.trapper.IsAlive() && (PlayerControl.LocalPlayer == Trapper.trapper || ShowGhostInfo)) { foreach (var trap in traps) { diff --git a/TheOtherRoles/Options/CustomOptionHolder.cs b/TheOtherRoles/Options/CustomOptionHolder.cs index a540f58f..18dfd975 100644 --- a/TheOtherRoles/Options/CustomOptionHolder.cs +++ b/TheOtherRoles/Options/CustomOptionHolder.cs @@ -279,6 +279,16 @@ public class CustomOptionHolder //public static CustomOption detectiveReportRoleDuration; //public static CustomOption detectiveReportInfoDuration; + public static CustomOption redemptorSpawnRate; + public static CustomOption redemptorRevelation; + public static CustomOption redemptorRevelationCooldown; + public static CustomOption redemptorRevelationDuration; + public static CustomOption redemptorPrayer; + public static CustomOption redemptorPrayerCooldown; + public static CustomOption redemptorPrayerDuration; + public static CustomOption redemptorReviveDuration; + + public static CustomOption timeMasterSpawnRate; public static CustomOption timeMasterCooldown; public static CustomOption timeMasterRewindTime; @@ -573,10 +583,6 @@ public class CustomOptionHolder public static CustomOption modifierVipQuantity; public static CustomOption modifierVipShowColor; - public static CustomOption modifierInvert; - public static CustomOption modifierInvertQuantity; - public static CustomOption modifierInvertDuration; - public static CustomOption modifierChameleon; public static CustomOption modifierChameleonQuantity; public static CustomOption modifierChameleonHoldDuration; @@ -769,8 +775,8 @@ public static void Load() SaboOptions = Create(300, Types.General, cs(Palette.ImpostorRed, "SaboOptions"), false, null, true); disableSabotage = Create(301, Types.General, cs(Palette.ImpostorRed, "disableSabotage"), false, SaboOptions); deadImpsBlockSabotage = Create(302, Types.General, cs(Palette.ImpostorRed, "deadImpsBlockSabotage"), false, SaboOptions); - enableCamoComms = Create(303, Types.General, cs(Palette.ImpostorRed, "enableCamoComms"), false, SaboOptions, true); - IsReactorDurationSetting = Create(310, Types.General, "IsReactorDurationSetting", false, SaboOptions, true); + enableCamoComms = Create(303, Types.General, cs(Palette.ImpostorRed, "enableCamoComms"), false, SaboOptions); + IsReactorDurationSetting = Create(310, Types.General, "IsReactorDurationSetting", false, SaboOptions); SkeldReactorTimeLimit = Create(311, Types.General, "SkeldReactorTimeLimit", 30f, 0f, 30f, 2.5f, IsReactorDurationSetting); SkeldLifeSuppTimeLimit = Create(312, Types.General, "SkeldLifeSuppTimeLimit", 30f, 0f, 30f, 2.5f, IsReactorDurationSetting); MiraLifeSuppTimeLimit = Create(313, Types.General, "MiraLifeSuppTimeLimit", 30f, 0f, 45f, 2.5f, IsReactorDurationSetting); @@ -1143,6 +1149,10 @@ public static void Load() prosecutorDiesOnIncorrectPros = Create(30371, Types.Crewmate, "prosecutorDiesOnIncorrectPros", true, prosecutorSpawnRate); prosecutorCanCallEmergency = Create(30372, Types.Crewmate, "canCallEmergency", true, prosecutorSpawnRate); + veteranSpawnRate = Create(30220, Types.Crewmate, cs(Veteran.color, "Veteran"), rates, null, true); + veteranCooldown = Create(30221, Types.Crewmate, "veteranCooldown", 25f, 5f, 60f, 2.5f, veteranSpawnRate); + veteranAlertDuration = Create(30222, Types.Crewmate, "veteranAlertDuration", 12.5f, 2.5f, 20f, 0.5f, veteranSpawnRate); + engineerSpawnRate = Create(30120, Types.Crewmate, cs(Engineer.color, "Engineer"), rates, null, true); engineerRemoteFix = Create(30121, Types.Crewmate, "engineerRemoteFix", true, engineerSpawnRate); engineerResetFixAfterMeeting = Create(30122, Types.Crewmate, "engineerResetFixAfterMeeting", true, engineerRemoteFix); @@ -1151,13 +1161,16 @@ public static void Load() engineerHighlightForImpostors = Create(30125, Types.Crewmate, "engineerHighlightForImpostors", true, engineerSpawnRate); engineerHighlightForTeamJackal = Create(30126, Types.Crewmate, "engineerHighlightForTeamJackal", true, engineerSpawnRate); - detectiveSpawnRate = Create(30190, Types.Crewmate, cs(Detective.color, "Detective"), rates, null, true); - detectiveAnonymousFootprints = Create(30191, Types.Crewmate, "detectiveAnonymousFootprints", - ["optionOff", "detectiveAnonymousFootprints1", "optionOn"], detectiveSpawnRate); - detectiveFootprintIntervall = Create(30192, Types.Crewmate, "detectiveFootprintIntervall", 0.25f, 0.25f, 10f, 0.25f, detectiveSpawnRate); - detectiveFootprintDuration = Create(30193, Types.Crewmate, "detectiveFootprintDuration", 12.5f, 0.5f, 30f, 0.5f, detectiveSpawnRate); - detectiveReportNameDuration = Create(30194, Types.Crewmate, "detectiveReportNameDuration", 10f, 0f, 60f, 2.5f, detectiveSpawnRate); - detectiveReportColorDuration = Create(30195, Types.Crewmate, "detectiveReportColorDuration", 30f, 0f, 120f, 2.5f, detectiveSpawnRate); + swapperSpawnRate = Create(30230, Types.Crewmate, cs(Swapper.color, "Swapper"), rates, null, true); + swapperCanCallEmergency = Create(30231, Types.Crewmate, "canCallEmergency", true, swapperSpawnRate); + swapperCanFixSabotages = Create(30232, Types.Crewmate, "swapperCanFixSabotages", true, swapperSpawnRate); + swapperCanOnlySwapOthers = Create(30233, Types.Crewmate, "swapperCanOnlySwapOthers", false, swapperSpawnRate); + swapperSwapsNumber = Create(30234, Types.Crewmate, "swapperSwapsNumber", 1f, 0f, 5f, 1f, swapperSpawnRate); + swapperRechargeTasksNumber = Create(30235, Types.Crewmate, "swapperRechargeTasksNumber", 2f, 1f, 10f, 1f, swapperSpawnRate); + + balancerSpawnRate = Create(30330, Types.Crewmate, cs(Balancer.color, "Balancer"), rates, null, true); + balancerCount = Create(30331, Types.Crewmate, "balancerCount", 1, 1, 3, 1, balancerSpawnRate); + balancerVoteTime = Create(30332, Types.Crewmate, "balancerVoteTime", 60, 15, 150, 5, balancerSpawnRate); medicSpawnRate = Create(30200, Types.Crewmate, cs(Medic.color, "Medic"), rates, null, true); medicShowShielded = Create(30201, Types.Crewmate, "medicShowShielded", @@ -1171,6 +1184,23 @@ public static void Load() medicReportNameDuration = Create(30207, Types.Crewmate, "medicReportNameDuration", 5f, 0f, 60f, 2.5f, medicBreakShield); medicReportColorDuration = Create(30208, Types.Crewmate, "medicReportColorDuration", 30f, 0f, 120f, 2.5f, medicBreakShield); + detectiveSpawnRate = Create(30190, Types.Crewmate, cs(Detective.color, "Detective"), rates, null, true); + detectiveAnonymousFootprints = Create(30191, Types.Crewmate, "detectiveAnonymousFootprints", + ["optionOff", "detectiveAnonymousFootprints1", "optionOn"], detectiveSpawnRate); + detectiveFootprintIntervall = Create(30192, Types.Crewmate, "detectiveFootprintIntervall", 0.25f, 0.25f, 10f, 0.25f, detectiveSpawnRate); + detectiveFootprintDuration = Create(30193, Types.Crewmate, "detectiveFootprintDuration", 12.5f, 0.5f, 30f, 0.5f, detectiveSpawnRate); + detectiveReportNameDuration = Create(30194, Types.Crewmate, "detectiveReportNameDuration", 10f, 0f, 60f, 2.5f, detectiveSpawnRate); + detectiveReportColorDuration = Create(30195, Types.Crewmate, "detectiveReportColorDuration", 30f, 0f, 120f, 2.5f, detectiveSpawnRate); + + redemptorSpawnRate = Create(30390, Types.Crewmate, cs(Redemptor.color, "Redemptor"), rates, null, true); + redemptorRevelation = Create(30391, Types.Crewmate, "redemptorRevelation", false, redemptorSpawnRate); + redemptorRevelationCooldown = Create(30392, Types.Crewmate, "redemptorRevelationCooldown", 25f, 10f, 60f, 2.5f, redemptorRevelation); + redemptorRevelationDuration = Create(30393, Types.Crewmate, "redemptorRevelationDuration", 5f, 1f, 15f, 0.5f, redemptorRevelation); + redemptorPrayer = Create(30394, Types.Crewmate, "redemptorPrayer", false, redemptorSpawnRate); + redemptorPrayerCooldown = Create(30395, Types.Crewmate, "redemptorPrayerCooldown", 25f, 10f, 60f, 2.5f, redemptorPrayer); + redemptorPrayerDuration = Create(30396, Types.Crewmate, "redemptorPrayerDuration", 5f, 2f, 15f, 0.5f, redemptorPrayer); + redemptorReviveDuration = Create(30397, Types.Crewmate, "redemptorReviveDuration", 1.5f, 0f, 15f, 0.5f, redemptorSpawnRate); + bodyGuardSpawnRate = Create(30340, Types.Crewmate, cs(BodyGuard.color, "BodyGuard"), rates, null, true); bodyGuardResetTargetAfterMeeting = Create(30341, Types.Crewmate, "bodyGuardResetTargetAfterMeeting", true, bodyGuardSpawnRate); bodyGuardShowShielded = Create(30343, Types.Crewmate, "bodyGuardShowShielded", true, bodyGuardSpawnRate); @@ -1181,17 +1211,6 @@ public static void Load() timeMasterShieldDuration = Create(30213, Types.Crewmate, "timeMasterShieldDuration", 15f, 2.5f, 20f, 0.5f, timeMasterSpawnRate); timeMasterRewindTime = Create(30212, Types.Crewmate, "timeMasterRewindTime", 9f, 1f, 10f, 1f, timeMasterSpawnRate); - veteranSpawnRate = Create(30220, Types.Crewmate, cs(Veteran.color, "Veteran"), rates, null, true); - veteranCooldown = Create(30221, Types.Crewmate, "veteranCooldown", 25f, 5f, 60f, 2.5f, veteranSpawnRate); - veteranAlertDuration = Create(30222, Types.Crewmate, "veteranAlertDuration", 12.5f, 2.5f, 20f, 0.5f, veteranSpawnRate); - - swapperSpawnRate = Create(30230, Types.Crewmate, cs(Swapper.color, "Swapper"), rates, null, true); - swapperCanCallEmergency = Create(30231, Types.Crewmate, "canCallEmergency", true, swapperSpawnRate); - swapperCanFixSabotages = Create(30232, Types.Crewmate, "swapperCanFixSabotages", true, swapperSpawnRate); - swapperCanOnlySwapOthers = Create(30233, Types.Crewmate, "swapperCanOnlySwapOthers", false, swapperSpawnRate); - swapperSwapsNumber = Create(30234, Types.Crewmate, "swapperSwapsNumber", 1f, 0f, 5f, 1f, swapperSpawnRate); - swapperRechargeTasksNumber = Create(30235, Types.Crewmate, "swapperRechargeTasksNumber", 2f, 1f, 10f, 1f, swapperSpawnRate); - seerSpawnRate = Create(30240, Types.Crewmate, cs(Seer.color, "Seer"), rates, null, true); seerMode = Create(30241, Types.Crewmate, "seerMode", ["seerMode1", "seerMode2", "seerMode3"], seerSpawnRate); seerLimitSoulDuration = Create(30242, Types.Crewmate, "seerLimitSoulDuration", false, seerSpawnRate); @@ -1236,6 +1255,20 @@ public static void Load() infoSleuthInfoType = Create(30381, Types.Crewmate, "infoSleuthInfoType", ["infoSleuthInfoType1", "infoSleuthInfoType2", "infoSleuthInfoType3"], infoSleuthSpawnRate); + mediumSpawnRate = Create(30310, Types.Crewmate, cs(Medium.color, "Medium"), rates, null, true); + mediumCooldown = Create(30311, Types.Crewmate, "mediumCooldown", 7.5f, 2.5f, 120f, 2.5f, mediumSpawnRate); + mediumDuration = Create(30312, Types.Crewmate, "mediumDuration", 0.5f, 0f, 15f, 0.5f, mediumSpawnRate); + mediumOneTimeUse = Create(30313, Types.Crewmate, "mediumOneTimeUse", false, mediumSpawnRate); + mediumChanceAdditionalInfo = Create(30314, Types.Crewmate, "mediumChanceAdditionalInfo", rates, mediumSpawnRate); + + trapperSpawnRate = Create(30350, Types.Crewmate, cs(Trapper.color, "Trapper"), rates, null, true); + trapperCooldown = Create(30351, Types.Crewmate, "trapperCooldown", 20f, 5f, 120f, 2.5f, trapperSpawnRate); + trapperMaxCharges = Create(30352, Types.Crewmate, "trapperMaxCharges", 5f, 1f, 15f, 1f, trapperSpawnRate); + trapperRechargeTasksNumber = Create(30353, Types.Crewmate, "trapperRechargeTasksNumber", 2f, 1f, 15f, 1f, trapperSpawnRate); + trapperTrapNeededTriggerToReveal = Create(30354, Types.Crewmate, "trapperTrapNeededTriggerToReveal", 2f, 1f, 10f, 1f, trapperSpawnRate); + trapperInfoType = Create(30356, Types.Crewmate, "trapperInfoType", ["Role", "trapperInfoType2", "Name"], trapperSpawnRate); + trapperTrapDuration = Create(30357, Types.Crewmate, "trapperTrapDuration", 5f, 1f, 15f, 0.5f, trapperSpawnRate); + spySpawnRate = Create(30280, Types.Crewmate, cs(Spy.color, "Spy"), rates, null, true); spyCanDieToSheriff = Create(30281, Types.Crewmate, "spyCanDieToSheriff", false, spySpawnRate); spyImpostorsCanKillAnyone = Create(30282, Types.Crewmate, "spyImpostorsCanKillAnyone", true, spySpawnRate); @@ -1259,30 +1292,12 @@ public static void Load() securityGuardCamRechargeTasksNumber = Create(30307, Types.Crewmate, "securityGuardCamRechargeTasksNumber", 3f, 1f, 10f, 1f, securityGuardSpawnRate); securityGuardNoMove = Create(30308, Types.Crewmate, "securityGuardNoMove", true, securityGuardSpawnRate); - mediumSpawnRate = Create(30310, Types.Crewmate, cs(Medium.color, "Medium"), rates, null, true); - mediumCooldown = Create(30311, Types.Crewmate, "mediumCooldown", 7.5f, 2.5f, 120f, 2.5f, mediumSpawnRate); - mediumDuration = Create(30312, Types.Crewmate, "mediumDuration", 0.5f, 0f, 15f, 0.5f, mediumSpawnRate); - mediumOneTimeUse = Create(30313, Types.Crewmate, "mediumOneTimeUse", false, mediumSpawnRate); - mediumChanceAdditionalInfo = Create(30314, Types.Crewmate, "mediumChanceAdditionalInfo", rates, mediumSpawnRate); - jumperSpawnRate = Create(30320, Types.Crewmate, cs(Jumper.color, "Jumper"), rates, null, true); jumperJumpTime = Create(30321, Types.Crewmate, "jumperJumpTime", 10f, 0f, 60f, 2.5f, jumperSpawnRate); jumperMaxCharges = Create(30325, Types.Crewmate, "jumperMaxCharges", 3, 0, 10, 1, jumperSpawnRate); jumperResetPlaceAfterMeeting = Create(30323, Types.Crewmate, "jumperResetPlaceAfterMeeting", false, jumperSpawnRate); jumperChargesGainOnMeeting = Create(30324, Types.Crewmate, "jumperChargesGainOnMeeting", 2, 0, 10, 1, jumperSpawnRate); - balancerSpawnRate = Create(30330, Types.Crewmate, cs(Balancer.color, "Balancer"), rates, null, true); - balancerCount = Create(30331, Types.Crewmate, "balancerCount", 1, 1, 3, 1, balancerSpawnRate); - balancerVoteTime = Create(30332, Types.Crewmate, "balancerVoteTime", 60, 15, 150, 5, balancerSpawnRate); - - trapperSpawnRate = Create(30350, Types.Crewmate, cs(Trapper.color, "Trapper"), rates, null, true); - trapperCooldown = Create(30351, Types.Crewmate, "trapperCooldown", 20f, 5f, 120f, 2.5f, trapperSpawnRate); - trapperMaxCharges = Create(30352, Types.Crewmate, "trapperMaxCharges", 5f, 1f, 15f, 1f, trapperSpawnRate); - trapperRechargeTasksNumber = Create(30353, Types.Crewmate, "trapperRechargeTasksNumber", 2f, 1f, 15f, 1f, trapperSpawnRate); - trapperTrapNeededTriggerToReveal = Create(30354, Types.Crewmate, "trapperTrapNeededTriggerToReveal", 2f, 1f, 10f, 1f, trapperSpawnRate); - trapperInfoType = Create(30356, Types.Crewmate, "trapperInfoType", ["Role", "trapperInfoType2", "Name"], trapperSpawnRate); - trapperTrapDuration = Create(30357, Types.Crewmate, "trapperTrapDuration", 5f, 1f, 15f, 0.5f, trapperSpawnRate); - //-------------------------- Modifier (40000 - 49999) -------------------------- // modifiersAreHidden = Create(40000, Types.Modifier, cs(Color.yellow, "modifiersAreHidden"), false, null, true); @@ -1380,10 +1395,6 @@ public static void Load() modifierVipQuantity = Create(40311, Types.Modifier, cs(Color.yellow, "modifierVipQuantity"), ratesCount, modifierVip); modifierVipShowColor = Create(40312, Types.Modifier, "modifierVipShowColor", true, modifierVip); - modifierInvert = Create(40320, Types.Modifier, cs(Color.yellow, "Invert"), rates, null, true); - modifierInvertQuantity = Create(40321, Types.Modifier, cs(Color.yellow, "modifierInvertQuantity"), ratesCount, modifierInvert); - modifierInvertDuration = Create(40322, Types.Modifier, "modifierInvertDuration", 2f, 1f, 15f, 1f, modifierInvert); - modifierChameleon = Create(40330, Types.Modifier, cs(Color.yellow, "Chameleon"), rates, null, true); modifierChameleonQuantity = Create(40331, Types.Modifier, cs(Color.yellow, "modifierChameleonQuantity"), ratesCount, modifierChameleon); modifierChameleonHoldDuration = Create(40332, Types.Modifier, "modifierChameleonHoldDuration", 3f, 1f, 10f, 0.5f, modifierChameleon); diff --git a/TheOtherRoles/Patches/ExileControllerPatch.cs b/TheOtherRoles/Patches/ExileControllerPatch.cs index 614564d8..41098d69 100644 --- a/TheOtherRoles/Patches/ExileControllerPatch.cs +++ b/TheOtherRoles/Patches/ExileControllerPatch.cs @@ -535,9 +535,6 @@ static string getTeam(PlayerControl player) } } - // Invert add meeting - if (Invert.meetings > 0) Invert.meetings--; - Chameleon.lastMoved.Clear(); foreach (var trap in Trap.traps) trap.triggerable = false; @@ -594,8 +591,8 @@ public static bool Prefix(AirshipExileController __instance) } } -[HarmonyPatch(typeof(SpawnInMinigame), - nameof(SpawnInMinigame.Close))] // Set position of AntiTp players AFTER they have selected a spawn. +// Set position of AntiTp players AFTER they have selected a spawn. +[HarmonyPatch(typeof(SpawnInMinigame), nameof(SpawnInMinigame.Close))] internal class AirshipSpawnInPatch { private static void Postfix() diff --git a/TheOtherRoles/Patches/IntroPatch.cs b/TheOtherRoles/Patches/IntroPatch.cs index 58593b58..fdef1d9f 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -123,7 +123,6 @@ public static void Prefix(IntroCutscene __instance) } ModOption.firstKillName = ""; - } public static void Postfix(IntroCutscene __instance) diff --git a/TheOtherRoles/Patches/MeetingHudPatch.cs b/TheOtherRoles/Patches/MeetingHudPatch.cs index c4149053..92f23c4d 100644 --- a/TheOtherRoles/Patches/MeetingHudPatch.cs +++ b/TheOtherRoles/Patches/MeetingHudPatch.cs @@ -521,7 +521,7 @@ private class MeetingHudBloopAVoteIconPatch public static bool Prefix(MeetingHud __instance, GameData.PlayerInfo voterPlayer, int index, Transform parent) { var spriteRenderer = Object.Instantiate(__instance.PlayerVotePrefab); - var showVoteColors = !GameManager.Instance.LogicOptions.GetAnonymousVotes() || shouldShowGhostInfo() || + var showVoteColors = !GameManager.Instance.LogicOptions.GetAnonymousVotes() || ShowGhostInfo || (Prosecutor.prosecutor != null && Prosecutor.prosecutor == PlayerControl.LocalPlayer && Prosecutor.canSeeVoteColors && TasksHandler.taskInfo(PlayerControl.LocalPlayer.Data).Item1 >= Prosecutor.tasksNeededToSeeVoteColors) || @@ -793,7 +793,7 @@ public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)] GameDat // Add Portal info into Portalmaker Chat: if (Portalmaker.portalmaker != null && - (PlayerControl.LocalPlayer == Portalmaker.portalmaker || shouldShowGhostInfo()) && + (PlayerControl.LocalPlayer == Portalmaker.portalmaker || ShowGhostInfo) && !Portalmaker.portalmaker.Data.IsDead) if (Portal.teleportedPlayers.Count > 0) { diff --git a/TheOtherRoles/Patches/PlayerControlPatch.cs b/TheOtherRoles/Patches/PlayerControlPatch.cs index 145698ef..7ac86854 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -84,17 +84,17 @@ private static void setBasePlayerOutlines() Medic.shielded != null && ((target == Medic.shielded && !isMorphedMorphling) || (isMorphedMorphling && Morphling.morphTarget == Medic.shielded))) { - hasVisibleShield = Medic.showShielded == 0 || shouldShowGhostInfo() // Everyone or Ghost info + hasVisibleShield = Medic.showShielded == 0 || ShowGhostInfo // Everyone or Ghost info || (Medic.showShielded == 1 && (local == Medic.shielded || local == Medic.medic)) // Shielded + Medic || (Medic.showShielded == 2 && local == Medic.medic); // Medic only // Make shield invisible till after the next meeting if the option is set (the medic can already see the shield) hasVisibleShield = hasVisibleShield && (Medic.meetingAfterShielding || !Medic.showShieldAfterMeeting || - local == Medic.medic || shouldShowGhostInfo()); + local == Medic.medic || ShowGhostInfo); } if (BodyGuard.guarded.IsAlive() && target == BodyGuard.guarded && - (shouldShowGhostInfo() || local == BodyGuard.bodyguard || (local == BodyGuard.guarded && BodyGuard.showShielded))) + (ShowGhostInfo || local == BodyGuard.bodyguard || (local == BodyGuard.guarded && BodyGuard.showShielded))) { hasVisibleShield = true; color = new Color32(205, 150, 100, byte.MaxValue); @@ -500,7 +500,7 @@ private static void trackerUpdate() return; } - if (Tracker.tracked != null && !Tracker.tracker.Data.IsDead) + if (Tracker.tracked != null && Tracker.tracker.IsAlive()) { Tracker.timeUntilUpdate -= Time.fixedDeltaTime; @@ -570,6 +570,77 @@ private static void trackerUpdate() } } + private static void redemptorUpdate() + { + if ((Redemptor.Player == null && Redemptor.RevivedPlayer == null) || Redemptor.arrow == null) return; + + Redemptor.arrow.arrow?.SetActive(false); + var local = PlayerControl.LocalPlayer; + if (Redemptor.Player.IsAlive() && Redemptor.Reviving && local.IsAlive() && local.isKiller()) + { + Redemptor.arrow ??= new Arrow(Redemptor.color); + if (Redemptor.arrow != null) + { + Redemptor.arrow.arrow.SetActive(true); + Redemptor.arrow.Update(Redemptor.Player.transform.position); + } + } + else if (Redemptor.RevivedPlayer.IsAlive() && local.IsAlive() && local.isKiller()) + { + Redemptor.arrow ??= new Arrow(Redemptor.color); + if (Redemptor.arrow != null) + { + Redemptor.arrow.arrow.SetActive(true); + Redemptor.arrow.Update(Redemptor.RevivedPlayer.transform.position); + } + } + else if (local == Redemptor.Player && Redemptor.Revelating) + { + var array = Object.FindObjectsOfType()?.FirstOrDefault(x => !(playerById(x.ParentId)?.Data?.Disconnected == true)); + if (array != null) + { + Redemptor.arrow ??= new Arrow(Redemptor.color); + Redemptor.arrow.arrow.SetActive(true); + Redemptor.arrow.Update(array.transform.position); + } + } + else + { + Redemptor.arrow.arrow?.Destroy(); + } + } + + private static void redemptorTextUpdate() + { + if (Redemptor.Player == null && Redemptor.RevivedPlayer == null) return; + var local = PlayerControl.LocalPlayer; + var enable = (Redemptor.RevivedPlayer.IsAlive() || Redemptor.Reviving) && ((local.IsAlive() && local.isKiller()) || ShowGhostInfo); + if (enable) + { + if (Redemptor.text == null) + { + Redemptor.text = Object.Instantiate(FastDestroyableSingleton.Instance.KillButton.cooldownTimerText, FastDestroyableSingleton.Instance.transform); + Redemptor.text.enableWordWrapping = false; + Redemptor.text.transform.localScale = Vector3.one * 0.75f; + Redemptor.text.transform.localPosition += new Vector3(0f, 1.8f, -69f); + Redemptor.text.gameObject.SetActive(true); + } + else if (Redemptor.Reviving && Redemptor.Player.IsAlive()) + { + Redemptor.text.text = $"牧师正在祈祷!"; + } + else if (Redemptor.RevivedPlayer.IsAlive()) + { + Redemptor.text.text = $"有玩家已被复活!"; + } + else + { + Redemptor.text?.Destroy(); + } + } + else if (Redemptor.text != null) Redemptor.text.Destroy(); + } + private static void MiniSizeUpdate(PlayerControl p) { if (Mini.mini == null) return; @@ -1412,6 +1483,9 @@ public static void Postfix(PlayerControl __instance) engineerUpdate(); // Tracker trackerUpdate(); + // Redemptor + redemptorUpdate(); + redemptorTextUpdate(); // Pavlovsdogs pavlovsownerUpdate(); // Check for deputy promotion on Sheriff disconnect @@ -1551,7 +1625,11 @@ internal class PlayerControlRevivePatch { public static void Postfix(PlayerControl __instance) { - if (PlayerControl.LocalPlayer == __instance) CanSeeRoleInfo = false; + if (PlayerControl.LocalPlayer == __instance) + { + CustomButton.ResetAllCooldowns(-1); + CanSeeRoleInfo = false; + } if (__instance == Specter.Player) Specter.Player.clearAllTasks(); @@ -1568,13 +1646,12 @@ public static void Postfix(PlayerControl __instance) Akujo.otherLover(__instance)?.Revive(); } - CustomButton.ResetAllCooldowns(-1, __instance); - DeadBody[] array = Object.FindObjectsOfType(); for (var i = 0; i < array.Length; i++) { if (GameData.Instance.GetPlayerById(array[i].ParentId).PlayerId == __instance.PlayerId) { + __instance.NetTransform.RpcSnapTo(array[i].transform.position); Object.Destroy(array[i].gameObject); break; } @@ -1832,7 +1909,7 @@ public static void HandleMurderPostfix(PlayerControl __instance, PlayerControl t // Seer show flash and add dead player position if (Seer.seer != null && - (PlayerControl.LocalPlayer == Seer.seer || shouldShowGhostInfo()) && + (PlayerControl.LocalPlayer == Seer.seer || ShowGhostInfo) && !Seer.seer.Data.IsDead && Seer.seer != target && Seer.mode <= 1) showFlash(new Color(42f / 255f, 187f / 255f, 245f / 255f), message: GetString("seerShowInfoText")); Seer.deadBodyPositions?.Add(target.transform.position); diff --git a/TheOtherRoles/Patches/PlayerPhysicsPatch.cs b/TheOtherRoles/Patches/PlayerPhysicsPatch.cs index 7eef03d2..869b93b9 100644 --- a/TheOtherRoles/Patches/PlayerPhysicsPatch.cs +++ b/TheOtherRoles/Patches/PlayerPhysicsPatch.cs @@ -9,8 +9,6 @@ public static void Postfix(PlayerPhysics __instance) { if (InGame && __instance && __instance.AmOwner && PlayerControl.LocalPlayer.IsAlive() && __instance.myPlayer.CanMove) { - if (Invert.invert.Any(x => x.PlayerId == PlayerControl.LocalPlayer.PlayerId) && Invert.meetings > 0) - __instance.body.velocity *= -1; if (Flash.flash != null && Flash.flash.Any(x => x.PlayerId == PlayerControl.LocalPlayer.PlayerId)) __instance.body.velocity *= Flash.speed; if (Giant.giant != null && Giant.giant == PlayerControl.LocalPlayer && !MushroomSabotageActive && !isCamoComms && Camouflager.camouflageTimer <= 0f) diff --git a/TheOtherRoles/Patches/RoleAssignmentPatch.cs b/TheOtherRoles/Patches/RoleAssignmentPatch.cs index dea178c5..01ee79d6 100644 --- a/TheOtherRoles/Patches/RoleAssignmentPatch.cs +++ b/TheOtherRoles/Patches/RoleAssignmentPatch.cs @@ -191,6 +191,7 @@ public static RoleAssignmentData getRoleAssignmentData() crewSettings.Add((byte)RoleId.Snitch, CustomOptionHolder.snitchSpawnRate.GetSelection()); crewSettings.Add((byte)RoleId.Medium, CustomOptionHolder.mediumSpawnRate.GetSelection()); crewSettings.Add((byte)RoleId.Prophet, CustomOptionHolder.prophetSpawnRate.GetSelection()); + crewSettings.Add((byte)RoleId.Redemptor, CustomOptionHolder.redemptorSpawnRate.GetSelection()); if (!isGuesserGamemode) crewSettings.Add((byte)RoleId.Vigilante, CustomOptionHolder.guesserSpawnRate.GetSelection()); crewSettings.Add((byte)RoleId.Trapper, CustomOptionHolder.trapperSpawnRate.GetSelection()); @@ -536,7 +537,6 @@ private static void assignModifiers() RoleId.Multitasker, RoleId.ButtonBarry, RoleId.Vip, - RoleId.Invert, RoleId.Indomitable, RoleId.Tunneler, RoleId.Slueth, @@ -1043,10 +1043,6 @@ private static int getSelectionForRoleId(RoleId roleId, bool multiplyQuantity = selection = CustomOptionHolder.modifierVip.GetSelection(); if (multiplyQuantity) selection *= CustomOptionHolder.modifierVipQuantity.GetQuantity(); break; - case RoleId.Invert: - selection = CustomOptionHolder.modifierInvert.GetSelection(); - if (multiplyQuantity) selection *= CustomOptionHolder.modifierInvertQuantity.GetQuantity(); - break; case RoleId.Chameleon: selection = CustomOptionHolder.modifierChameleon.GetSelection(); if (multiplyQuantity) selection *= CustomOptionHolder.modifierChameleonQuantity.GetQuantity(); diff --git a/TheOtherRoles/Patches/UpdatePatch.cs b/TheOtherRoles/Patches/UpdatePatch.cs index c92a93ca..20c7b55b 100644 --- a/TheOtherRoles/Patches/UpdatePatch.cs +++ b/TheOtherRoles/Patches/UpdatePatch.cs @@ -132,7 +132,7 @@ private static void setNameColors() } if (Grenadier.Player != null && ((localPlayer.isImpostor() && Grenadier.indicatorsMode) - || localPlayer == Grenadier.Player || shouldShowGhostInfo())) + || localPlayer == Grenadier.Player || ShowGhostInfo)) { foreach (var p in Grenadier.controls) { @@ -273,7 +273,7 @@ private static void setNameTags() } // Parttimer - if (PartTimer.partTimer != null && PartTimer.target != null && (local == PartTimer.partTimer || local == PartTimer.target || shouldShowGhostInfo())) + if (PartTimer.partTimer != null && PartTimer.target != null && (local == PartTimer.partTimer || local == PartTimer.target || ShowGhostInfo)) { var suffix = cs(PartTimer.color, " ★"); PartTimer.partTimer.cosmetics.nameText.text += suffix; @@ -286,7 +286,7 @@ private static void setNameTags() } var localIsArsonist = Arsonist.arsonist != null && Arsonist.dousedPlayers != null && Arsonist.arsonist == local; - var localIsDead = Arsonist.arsonist != null && Arsonist.dousedPlayers != null && shouldShowGhostInfo(); + var localIsDead = Arsonist.arsonist != null && Arsonist.dousedPlayers != null && ShowGhostInfo; if (localIsArsonist || localIsDead) { var suffix = cs(Arsonist.color, " ♨"); diff --git a/TheOtherRoles/RPC.cs b/TheOtherRoles/RPC.cs index 18178eae..1b7c8c02 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -127,6 +127,8 @@ public enum CustomRPC WitnessSetTarget, WolfLordkilled, PelicanKill, + RedemptorRevive, + RedemptorPrayer, TrapperKill, PlaceTrap, @@ -466,6 +468,9 @@ public static void setRole(byte roleId, byte playerId) case RoleId.Gambler: Gambler.gambler = player; break; + case RoleId.Redemptor: + Redemptor.Player = player; + break; } } if (AmongUsClient.Instance.AmHost && Helpers.roleCanUseVents(player) && !player.Data.Role.IsImpostor) @@ -557,9 +562,6 @@ public static void setModifier(byte modifierId, byte playerId, byte flag) case RoleId.Vip: Vip.vip.Add(player); break; - case RoleId.Invert: - Invert.invert.Add(player); - break; case RoleId.Indomitable: Indomitable.indomitable = player; break; @@ -838,7 +840,7 @@ public static void shieldedMurderAttempt(byte blank) !Medic.showShieldAfterMeeting); // Dont show attempt, if shield is not shown yet var isMedicAndShow = Medic.medic == PlayerControl.LocalPlayer && Medic.showAttemptToMedic; - if (isShieldedAndShow || isMedicAndShow || shouldShowGhostInfo()) + if (isShieldedAndShow || isMedicAndShow || ShowGhostInfo) showFlash(Palette.ImpostorRed, 1.5f, GetString("medicShowAttemptText")); } @@ -1125,6 +1127,7 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == Hacker.hacker) Hacker.clearAndReload(); if (player == BodyGuard.bodyguard) BodyGuard.clearAndReload(); if (player == Balancer.balancer) Balancer.clearAndReload(); + if (player == Redemptor.Player) Redemptor.ClearAndReload(); if (player == Tracker.tracker) Tracker.clearAndReload(); if (player == Snitch.snitch) Snitch.clearAndReload(); if (player == Swapper.swapper) Swapper.clearAndReload(); @@ -1213,7 +1216,6 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) Flash.flash.RemoveAll(x => x.PlayerId == player.PlayerId); Multitasker.multitasker.RemoveAll(x => x.PlayerId == player.PlayerId); Vip.vip.RemoveAll(x => x.PlayerId == player.PlayerId); - Invert.invert.RemoveAll(x => x.PlayerId == player.PlayerId); Chameleon.chameleon.RemoveAll(x => x.PlayerId == player.PlayerId); if (player == Lovers.lover1 || player == Lovers.lover2) Lovers.clearAndReload(); // The whole Lover couple is being erased if (player == Specoality.specoality) Specoality.clearAndReload(); @@ -1931,7 +1933,7 @@ public static void receiveGhostInfo(byte senderId, MessageReader reader) break; case GhostInfoTypes.GhostChat: string chat = reader.ReadString(); - if (shouldShowGhostInfo()) FastDestroyableSingleton.Instance.Chat.AddChat(sender, chat); + if (ShowGhostInfo) FastDestroyableSingleton.Instance.Chat.AddChat(sender, chat); break; case GhostInfoTypes.BlankUsed: Pursuer.blankedList.Remove(sender); @@ -2474,6 +2476,14 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) RPCProcedure.balancerBalance(reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); break; + case CustomRPC.RedemptorRevive: + Redemptor.RevivePlayer(reader.ReadByte()); + break; + + case CustomRPC.RedemptorPrayer: + Redemptor.RedemptorPrayer(reader.ReadByte()); + break; + case CustomRPC.RevivePlayer: RPCProcedure.RevivePlayer(reader.ReadByte()); break; diff --git a/TheOtherRoles/Resources/Revive.png b/TheOtherRoles/Resources/Revive.png new file mode 100644 index 00000000..9f6dede4 Binary files /dev/null and b/TheOtherRoles/Resources/Revive.png differ diff --git a/TheOtherRoles/Resources/stringData.json b/TheOtherRoles/Resources/stringData.json index 0a7edb2f..0e053bd8 100644 --- a/TheOtherRoles/Resources/stringData.json +++ b/TheOtherRoles/Resources/stringData.json @@ -2217,6 +2217,27 @@ "0": "Trap Duration", "13": "陷阱定身时间" }, + "redemptorRevelation": { + "13": "启示" + }, + "redemptorRevelationCooldown": { + "13": "启示冷却" + }, + "redemptorRevelationDuration": { + "13": "箭头持续时间" + }, + "redemptorPrayer": { + "13": "祈祷" + }, + "redemptorPrayerCooldown": { + "13": "祈祷冷却" + }, + "redemptorPrayerDuration": { + "13": "祈祷持续时间" + }, + "redemptorReviveDuration": { + "13": "普通复活持续时间" + }, "modifiersAreHidden": { "0": "Hide After Death Modifiers", "13": "隐藏死亡触发的附加能力" @@ -3132,10 +3153,18 @@ "0": "FLASH", "13": "闪光弹" }, - "SpecterButton": { + "ReviveButton": { "0": "Revive", "13": "复活" }, + "RedemptorRevelation": { + "0": "Revelation", + "13": " 启示" + }, + "RedemptorPrayer": { + "0": "Prayer", + "13": "祈祷" + }, "GuessserGMInfo": { "0": " (Guesser)", "13": " (赌怪)" @@ -3168,6 +3197,12 @@ "0": "WolfLord", "13": "狼之主" }, + "WolfLordIntroDesc": { + "13": "恶者亦可称王" + }, + "WolfLordShortDesc": { + "13": "在会议中强制击杀一人" + }, "Morphling": { "0": "Morphling", "13": "化形者" @@ -3868,6 +3903,16 @@ "BalancerShortDesc": { "13": "审判他人罪孽的轻重" }, + "Redemptor": { + "0": "Redemptor", + "13": "牧师" + }, + "RedemptorIntroDesc": { + "13": "为亡灵祈祷吧" + }, + "RedemptorShortDesc": { + "13": "给予重生的机会" + }, "Disperser": { "0": "Disperser", "13": "分散者" @@ -4270,6 +4315,9 @@ "GamblerFullDesc": { "13": "每次击杀,下一次击杀冷却会随机变化。\n注:赌徒无法获得绝境者" }, + "GrenadierFullDesc": { + "13": "可以使用技能闪瞎附近的非红狼玩家,被闪到的玩家名称会变黑。\n队友按照房间设置也可以得知哪些玩家被影响。" + }, "SurvivorFullDesc": { "13": "存活到游戏结束即可跟随其它阵营胜利\n以房间设置为准,幸存者可能拥有防弹衣和空包弹技能。\n防弹衣在开启期间,无法被击杀,杀手击杀时会进入较短的冷却。\n空包弹可以让目标下一次无法正常击杀。" }, @@ -4414,6 +4462,9 @@ "BalancerFullDesc": { "13": "可以在会议中选择两位玩家挂上天平,所有玩家只能在这两位玩家中投票。\n\n在特殊会议中,如果平票则同时驱逐两位玩家。\n\n如果其中一位玩家死亡(例如被赌死),则直接跳过投票环节直接驱逐另一位被挂上天平的玩家。可以在会议中选择两位玩家挂上天平,所有玩家只能在这两位玩家中投票。\n\n在特殊会议中,如果平票则同时驱逐两位玩家。\n\n如果其中一位玩家死亡(例如被赌死),则直接跳过投票环节直接驱逐另一位被挂上天平的玩家。" }, + "RedemptorFullDesc": { + "13": "牧师可以寻找尸体并复活该玩家,且复活时会向所有坏人通知复活位置。\n该玩家复活后,所有坏人会有箭头指向该玩家。\n\n牧师拥有三个技能(房主可以禁用前两个技能):\n启示:使用后会随机指向场上的一个尸体,没有尸体则没有箭头。\n\n祈祷:需要在尸体旁使用,祈祷需要持续一定的时间,且会向所有坏人\n通知牧师的位置,祈祷完成后会将该尸体所属的玩家复活。\n如果祈祷被打断则复活失败!每回合只能使用一次。\n\n殉道:需要在尸体旁使用:使用技能后会自杀,技能持续时间(一般很短)\n结束后完成后会将该尸体所属的玩家复活。\n复活过程中如果进入会议则复活失败!\n" + }, "LoverFullDesc": { "13": "执子之手,与子共生。\n恋人如果有任何一方被击杀,另一方也会一起死亡。\n当恋人中有一方并非船员时并且恋人存活,\n则不会计算该玩家的任务进度。\n\n剩余3名玩家时并且两名玩家为恋人时,\n恋人将直接独自获胜。" }, diff --git a/TheOtherRoles/Roles/Crewmate/Jumper.cs b/TheOtherRoles/Roles/Crewmate/Jumper.cs index dc0b58c4..50736220 100644 --- a/TheOtherRoles/Roles/Crewmate/Jumper.cs +++ b/TheOtherRoles/Roles/Crewmate/Jumper.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; namespace TheOtherRoles.Roles.Crewmate; diff --git a/TheOtherRoles/Roles/Crewmate/Medic.cs b/TheOtherRoles/Roles/Crewmate/Medic.cs index 31f6a85c..39828320 100644 --- a/TheOtherRoles/Roles/Crewmate/Medic.cs +++ b/TheOtherRoles/Roles/Crewmate/Medic.cs @@ -41,7 +41,7 @@ public static bool shieldVisible(PlayerControl target) if (shielded != null && ((target == shielded && !isMorphedMorphling) || (isMorphedMorphling && Morphling.morphTarget == shielded))) { // Everyone or Ghost info - hasVisibleShield = showShielded == 0 || shouldShowGhostInfo() + hasVisibleShield = showShielded == 0 || ShowGhostInfo || (showShielded == 1 && (PlayerControl.LocalPlayer == shielded || PlayerControl.LocalPlayer == medic)) // Shielded + Medic || (showShielded == 2 && PlayerControl.LocalPlayer == medic); @@ -50,7 +50,7 @@ public static bool shieldVisible(PlayerControl target) hasVisibleShield = hasVisibleShield && (meetingAfterShielding || !showShieldAfterMeeting || PlayerControl.LocalPlayer == medic - || shouldShowGhostInfo()); + || ShowGhostInfo); } return hasVisibleShield; } diff --git a/TheOtherRoles/Roles/Crewmate/Redemptor.cs b/TheOtherRoles/Roles/Crewmate/Redemptor.cs new file mode 100644 index 00000000..3fcf70ef --- /dev/null +++ b/TheOtherRoles/Roles/Crewmate/Redemptor.cs @@ -0,0 +1,85 @@ +using Reactor.Utilities.Extensions; +using TheOtherRoles.Objects; +using TMPro; +using UnityEngine; + +namespace TheOtherRoles.Roles.Crewmate; +public class Redemptor +{ + public static PlayerControl Player; + public static Color color = new Color32(255, 216, 70, byte.MaxValue); + public static PlayerControl target; + public static Arrow arrow; + public static bool Revelating; + public static bool Reviving; + public static PlayerControl RevivedPlayer; + + public static bool revelation; + public static float revelationCooldown; + public static float revelationDuration; + public static bool prayer; + public static float prayerCooldown; + public static float prayerDuration; + public static float reviveDuration; + + public static Sprite reviveButton = new ResourceSprite("Revive.png"); + public static TextMeshPro text; + + public static void RevivePlayer(byte targetId) + { + var player = playerById(targetId); + player?.Revive(); + RevivedPlayer = player; + target = null; + } + + /// + /// off = 0, on > 0 + /// + public static void RedemptorPrayer(byte status) + { + Reviving = status != 0; + Message($"{Reviving}"); + } + + public static void ClearAndReload() + { + Player = null; + target = null; + RevivedPlayer = null; + arrow?.arrow?.Destroy(); + Reviving = false; + Revelating = false; + if (text != null) Object.Destroy(text); + text = null; + revelation = CustomOptionHolder.redemptorRevelation.GetBool(); + revelationCooldown = CustomOptionHolder.redemptorRevelationCooldown.GetFloat(); + revelationDuration = CustomOptionHolder.redemptorRevelationDuration.GetFloat(); + prayer = CustomOptionHolder.redemptorPrayer.GetBool(); + prayerCooldown = CustomOptionHolder.redemptorPrayerCooldown.GetFloat(); + prayerDuration = CustomOptionHolder.redemptorPrayerDuration.GetFloat(); + reviveDuration = CustomOptionHolder.redemptorReviveDuration.GetFloat(); + } + + [HarmonyPatch] + public static class Redemptor_Patch + { + [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start)), HarmonyPostfix] + public static void MeetingStartPatch() + { + if (Player == null) return; + if (Reviving) + { + Reviving = false; + target = null; + RevivedPlayer = null; + } + if (Revelating) + { + target = null; + Revelating = false; + } + RevivedPlayer = null; + } + } +} diff --git a/TheOtherRoles/Roles/Ghost/Specter.cs b/TheOtherRoles/Roles/Ghost/Specter.cs index 4366cc21..07c190d8 100644 --- a/TheOtherRoles/Roles/Ghost/Specter.cs +++ b/TheOtherRoles/Roles/Ghost/Specter.cs @@ -255,55 +255,55 @@ public static void TakeRole(byte targetId) if (Sheriff.formerDeputy == target) Sheriff.formerDeputy = local; break; case RoleId.Deputy: - if (Amnisiac.resetRole) Sheriff.Reload(); + if (resetRole) Sheriff.Reload(); Sheriff.Deputy = local; break; case RoleId.BodyGuard: - if (Amnisiac.resetRole) BodyGuard.clearAndReload(); + if (resetRole) BodyGuard.clearAndReload(); BodyGuard.bodyguard = local; break; case RoleId.Jumper: - if (Amnisiac.resetRole) Jumper.clearAndReload(); + if (resetRole) Jumper.clearAndReload(); Jumper.jumper = local; break; case RoleId.Detective: - if (Amnisiac.resetRole) Detective.clearAndReload(); + if (resetRole) Detective.clearAndReload(); Detective.detective = local; break; case RoleId.TimeMaster: - if (Amnisiac.resetRole) TimeMaster.clearAndReload(); + if (resetRole) TimeMaster.clearAndReload(); TimeMaster.timeMaster = local; break; case RoleId.Veteran: - if (Amnisiac.resetRole) Veteran.clearAndReload(); + if (resetRole) Veteran.clearAndReload(); Veteran.veteran = local; break; case RoleId.Medic: - if (Amnisiac.resetRole) Medic.clearAndReload(); + if (resetRole) Medic.clearAndReload(); Medic.medic = local; break; case RoleId.Swapper: - if (Amnisiac.resetRole) Swapper.clearAndReload(); + if (resetRole) Swapper.clearAndReload(); Swapper.swapper = local; break; case RoleId.Seer: - if (Amnisiac.resetRole) Seer.clearAndReload(); + if (resetRole) Seer.clearAndReload(); Seer.seer = local; break; case RoleId.Hacker: - if (Amnisiac.resetRole) Hacker.clearAndReload(); + if (resetRole) Hacker.clearAndReload(); Hacker.hacker = local; break; case RoleId.Tracker: - if (Amnisiac.resetRole) Tracker.clearAndReload(); + if (resetRole) Tracker.clearAndReload(); Tracker.tracker = local; break; case RoleId.Snitch: - if (Amnisiac.resetRole) Snitch.clearAndReload(); + if (resetRole) Snitch.clearAndReload(); Snitch.snitch = local; break; case RoleId.Prophet: - if (Amnisiac.resetRole) Prophet.clearAndReload(); + if (resetRole) Prophet.clearAndReload(); Prophet.prophet = local; break; case RoleId.InfoSleuth: @@ -311,25 +311,29 @@ public static void TakeRole(byte targetId) InfoSleuth.infoSleuth = local; break; case RoleId.Spy: - if (Amnisiac.resetRole) Spy.clearAndReload(); + if (resetRole) Spy.clearAndReload(); Spy.spy = local; break; case RoleId.SecurityGuard: - if (Amnisiac.resetRole) SecurityGuard.clearAndReload(); + if (resetRole) SecurityGuard.clearAndReload(); SecurityGuard.securityGuard = local; break; case RoleId.Medium: - if (Amnisiac.resetRole) Medium.clearAndReload(); + if (resetRole) Medium.clearAndReload(); Medium.medium = local; break; case RoleId.Trapper: - if (Amnisiac.resetRole) Trapper.clearAndReload(); + if (resetRole) Trapper.clearAndReload(); Trapper.trapper = local; break; case RoleId.Balancer: - if (Amnisiac.resetRole) Balancer.clearAndReload(); + if (resetRole) Balancer.clearAndReload(); Balancer.balancer = local; break; + case RoleId.Redemptor: + if (resetRole) Redemptor.ClearAndReload(); + Redemptor.Player = local; + break; } } diff --git a/TheOtherRoles/Roles/Impostor/Mimic.cs b/TheOtherRoles/Roles/Impostor/Mimic.cs index 4b8c3581..81fb6903 100644 --- a/TheOtherRoles/Roles/Impostor/Mimic.cs +++ b/TheOtherRoles/Roles/Impostor/Mimic.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using TheOtherRoles.Buttons; using UnityEngine; @@ -173,6 +173,13 @@ public static void MimicRole(byte targetId) prophetButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; hasMimic = true; break; + case RoleId.Redemptor: + if (Amnisiac.resetRole) Redemptor.ClearAndReload(); + Redemptor.Player = mimic; + redemptorReviveButton.PositionOffset = CustomButton.ButtonPositions.lowerRowFarLeft; + redemptorPrayerButton.PositionOffset = CustomButton.ButtonPositions.upperRowFarLeft; + hasMimic = true; + break; } } diff --git a/TheOtherRoles/Roles/Modifier/Invert.cs b/TheOtherRoles/Roles/Modifier/Invert.cs deleted file mode 100644 index 41a8477f..00000000 --- a/TheOtherRoles/Roles/Modifier/Invert.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace TheOtherRoles.Roles.Modifier; - -public static class Invert -{ - public static List invert = new(); - public static int meetings = 3; - - public static void clearAndReload() - { - invert.Clear(); - meetings = (int)CustomOptionHolder.modifierInvertDuration.GetFloat(); - } -} diff --git a/TheOtherRoles/Roles/Modifier/Shifter.cs b/TheOtherRoles/Roles/Modifier/Shifter.cs index b2f9316b..acbf7127 100644 --- a/TheOtherRoles/Roles/Modifier/Shifter.cs +++ b/TheOtherRoles/Roles/Modifier/Shifter.cs @@ -169,6 +169,11 @@ public static void shiftRole(PlayerControl player1, PlayerControl player2, bool if (repeat) shiftRole(player2, player1, false); Prosecutor.prosecutor = player1; } + else if (Redemptor.Player != null && Redemptor.Player == player2) + { + if (repeat) shiftRole(player2, player1, false); + Redemptor.Player = player1; + } else if (Amnisiac.Player != null && Amnisiac.Player.Any(x => x.PlayerId == player2.PlayerId)) { if (repeat) shiftRole(player2, player1, false); diff --git a/TheOtherRoles/Roles/Neutral/Amnisiac.cs b/TheOtherRoles/Roles/Neutral/Amnisiac.cs index 07b95af6..3fa83973 100644 --- a/TheOtherRoles/Roles/Neutral/Amnisiac.cs +++ b/TheOtherRoles/Roles/Neutral/Amnisiac.cs @@ -378,6 +378,10 @@ public static void TakeRole(byte targetId, byte playerId) if (resetRole) Balancer.clearAndReload(); Balancer.balancer = local; break; + case RoleId.Redemptor: + if (resetRole) Redemptor.ClearAndReload(); + Redemptor.Player = local; + break; } Player.RemoveAll(x => x.PlayerId == local.PlayerId); foreach (var arrow in localArrows) diff --git a/TheOtherRoles/Roles/RoleHelpers.cs b/TheOtherRoles/Roles/RoleHelpers.cs index c7ea0ccb..6df5b40c 100644 --- a/TheOtherRoles/Roles/RoleHelpers.cs +++ b/TheOtherRoles/Roles/RoleHelpers.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using InnerNet; using MonoMod.Utils; using TheOtherRoles.Utilities; @@ -84,6 +85,7 @@ public enum RoleId Medium, Trapper, Balancer, + Redemptor, // Modifier --- Lover, @@ -108,7 +110,6 @@ public enum RoleId Indomitable, Slueth, Cursed, - Invert, Blind, Watcher, Radar, @@ -136,6 +137,9 @@ public static bool CanSeeRoleInfo } } + public static bool ShowGhostInfo => + (PlayerControl.LocalPlayer.Data.IsDead && CanSeeRoleInfo) || AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Ended; + public static Dictionary blockedRolePairings = new(); public static Dictionary> GhostRoles = new(); public static List GhostPlayer = new(); @@ -198,6 +202,7 @@ public static void ResetRoleSelection() { RoleId.Trapper, CustomOptionHolder.trapperSpawnRate.GetSelection() }, { RoleId.Veteran, CustomOptionHolder.veteranSpawnRate.GetSelection() }, { RoleId.Vigilante, CustomOptionHolder.guesserSpawnRate.GetSelection() }, + { RoleId.Redemptor, CustomOptionHolder.redemptorSpawnRate.GetSelection() }, { RoleId.WolfLord, CustomOptionHolder.wolfLordSpawnRate.GetSelection() }, { RoleId.Blackmailer, CustomOptionHolder.blackmailerSpawnRate.GetSelection() }, @@ -260,7 +265,6 @@ public static void ResetRoleSelection() { RoleId.Flash, CustomOptionHolder.modifierFlash.GetSelection() }, { RoleId.Giant, CustomOptionHolder.modifierGiant.GetSelection() }, { RoleId.Indomitable, CustomOptionHolder.modifierIndomitable.GetSelection() }, - { RoleId.Invert, CustomOptionHolder.modifierInvert.GetSelection() }, { RoleId.LastImpostor, CustomOptionHolder.modifierLastImpostor.GetSelection() }, { RoleId.Mini, CustomOptionHolder.modifierMini.GetSelection() }, { RoleId.Multitasker, CustomOptionHolder.modifierMultitasker.GetSelection() }, @@ -360,6 +364,7 @@ public static void clearAndReloadRoles() Witness.ClearAndReload(); WolfLord.ClearAndReload(); Pelican.clearAndReload(); + Redemptor.ClearAndReload(); // Modifier Assassin.clearAndReload(); @@ -383,7 +388,6 @@ public static void clearAndReloadRoles() Slueth.clearAndReload(); Cursed.clearAndReload(); Vip.clearAndReload(); - Invert.clearAndReload(); Chameleon.clearAndReload(); ButtonBarry.clearAndReload(); LastImpostor.clearAndReload(); @@ -404,9 +408,9 @@ public static void clearAndReloadRoles() [HarmonyPatch(typeof(RoleManager), nameof(RoleManager.AssignRoleOnDeath))] public static class AssignRoleOnDeathPatch { - public static bool Prefix([HarmonyArgument(0)] PlayerControl player) + public static bool Prefix([HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] bool specialRolesAllowed) { - if (player.IsAlive() || player == null) return false; + if (player.IsAlive() || player == null || !specialRolesAllowed) return false; return true; } diff --git a/TheOtherRoles/Roles/RoleInfo.cs b/TheOtherRoles/Roles/RoleInfo.cs index 0485225b..45073d94 100644 --- a/TheOtherRoles/Roles/RoleInfo.cs +++ b/TheOtherRoles/Roles/RoleInfo.cs @@ -94,6 +94,7 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleType public static RoleInfo medium = new("Medium", Medium.color, RoleId.Medium, RoleType.Crewmate); public static RoleInfo trapper = new("Trapper", Trapper.color, RoleId.Trapper, RoleType.Crewmate); public static RoleInfo balancer = new("Balancer", Balancer.color, RoleId.Balancer, RoleType.Crewmate); + public static RoleInfo redemptor = new("Redemptor", Redemptor.color, RoleId.Redemptor, RoleType.Crewmate); // Modifier public static RoleInfo assassin = new("Assassin", Assassin.color, RoleId.Assassin, RoleType.Modifier); @@ -118,7 +119,6 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleType public static RoleInfo indomitable = new("Indomitable", Color.yellow, RoleId.Indomitable, RoleType.Modifier); public static RoleInfo slueth = new("Slueth", Color.yellow, RoleId.Slueth, RoleType.Modifier, true); public static RoleInfo cursed = new("Cursed", Color.yellow, RoleId.Cursed, RoleType.Modifier, true); - public static RoleInfo invert = new("Invert", Color.yellow, RoleId.Invert, RoleType.Modifier); public static RoleInfo blind = new("Blind", Color.yellow, RoleId.Blind, RoleType.Modifier); public static RoleInfo watcher = new("Watcher", Color.yellow, RoleId.Watcher, RoleType.Modifier, true); public static RoleInfo radar = new("Radar", Color.yellow, RoleId.Radar, RoleType.Modifier, true); @@ -206,6 +206,7 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleType medium, trapper, balancer, + redemptor, lover, assassin, @@ -229,7 +230,6 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleType indomitable, slueth, cursed, - invert, blind, watcher, radar, @@ -281,7 +281,6 @@ public static List getRoleInfoForPlayer(PlayerControl p, bool showModi if (p == Poucher.poucher && Poucher.spawnModifier) infos.Add(poucherModifier); if (p == Giant.giant) infos.Add(giant); if (p == Vortox.Player) infos.Add(vortox); - if (Invert.invert.Any(x => x.PlayerId == p.PlayerId)) infos.Add(invert); if (Chameleon.chameleon.Any(x => x.PlayerId == p.PlayerId)) infos.Add(chameleon); if (p == Shifter.shifter) infos.Add(shifter); if (p == LastImpostor.lastImpostor) infos.Add(lastImpostor); @@ -355,6 +354,7 @@ public static List getRoleInfoForPlayer(PlayerControl p, bool showModi if (p == Akujo.akujo) infos.Add(akujo); if (p == Jackal.Sidekick) infos.Add(sidekick); if (p == Pavlovsdogs.pavlovsowner) infos.Add(pavlovsowner); + if (p == Redemptor.Player) infos.Add(redemptor); if (Jackal.jackal.Any(x => x != null && x.PlayerId == p.PlayerId)) infos.Add(jackal); if (Amnisiac.Player.Any(x => x.PlayerId == p.PlayerId)) infos.Add(amnisiac); if (Pavlovsdogs.pavlovsdogs.Any(x => x.PlayerId == p.PlayerId)) infos.Add(pavlovsdogs); @@ -406,14 +406,14 @@ public static string GetRolesString(PlayerControl p, bool useColors, bool showMo if (showGhostInfo && p != null) { - if (p == Shifter.shifter && (PlayerControl.LocalPlayer == Shifter.shifter || shouldShowGhostInfo()) && Shifter.futureShift != null) + if (p == Shifter.shifter && (PlayerControl.LocalPlayer == Shifter.shifter || ShowGhostInfo) && Shifter.futureShift != null) roleName += cs(Color.yellow, " ← " + Shifter.futureShift.Data.PlayerName); - if (p == Vulture.vulture && (PlayerControl.LocalPlayer == Vulture.vulture || shouldShowGhostInfo())) + if (p == Vulture.vulture && (PlayerControl.LocalPlayer == Vulture.vulture || ShowGhostInfo)) roleName += cs(Vulture.color, string.Format("roleInfoRemaining".Translate(), Vulture.vultureNumberToWin - Vulture.eatenBodies)); - if (p == Witness.Player && (PlayerControl.LocalPlayer == Witness.Player || shouldShowGhostInfo())) + if (p == Witness.Player && (PlayerControl.LocalPlayer == Witness.Player || ShowGhostInfo)) roleName += cs(Witness.color, string.Format("roleInfoRemaining".Translate(), Witness.exileToWin - Witness.exiledCount)); - if (shouldShowGhostInfo()) + if (ShowGhostInfo) { if (Eraser.futureErased.Contains(p)) roleName = cs(Color.gray, "(被抹除) ") + roleName; diff --git a/TheOtherRoles/TheOtherRoles.csproj b/TheOtherRoles/TheOtherRoles.csproj index bbcdd760..50f48d1a 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -1,7 +1,7 @@  net6.0 - 1.1.2.3 + 1.1.2.5 TheOtherUs mxyx-club latest