Merge remote-tracking branch 'origin/main'

This commit is contained in:
Gurge44 committed 2026-08-12 10:07:39 +02:00
commit d68798092f
22 files changed
+281 -15

No files matched your search

+4
View File
@@ -121,6 +121,7 @@ public enum CustomRoles
Trapster,
Trickster,
Twister,
Tyrant,
Undertaker,
Vampire,
Venerer,
@@ -538,6 +539,7 @@ public enum CustomRoles
Blocked,
Bloodlust,
Bloodmoon, // Ghost role
Bluetooth,
Busy,
Charmed,
Circumvent,
@@ -552,6 +554,7 @@ public enum CustomRoles
Damocles,
Deadlined,
DeadlyQuota,
Degraded,
Disco,
Diseased,
Disregarded,
@@ -605,6 +608,7 @@ public enum CustomRoles
Sleep,
Phantasm, // Ghost role
Physicist,
Priority,
Rascal,
Reach,
Rookie,
+4
View File
@@ -121,6 +121,7 @@ public enum CustomRoles
Trapster,
Trickster,
Twister,
Tyrant,
Undertaker,
Vampire,
Venerer,
@@ -538,6 +539,7 @@ public enum CustomRoles
Blocked,
Bloodlust,
Bloodmoon, // Ghost role
Bluetooth,
Busy,
Charmed,
Circumvent,
@@ -552,6 +554,7 @@ public enum CustomRoles
Damocles,
Deadlined,
DeadlyQuota,
Degraded,
Disco,
Diseased,
Disregarded,
@@ -605,6 +608,7 @@ public enum CustomRoles
Sleep,
Phantasm, // Ghost role
Physicist,
Priority,
Rascal,
Reach,
Rookie,
+3
View File
@@ -782,6 +782,9 @@ public class Main : BasePlugin
{ CustomRoles.Shy, "#9582f5" },
{ CustomRoles.Blocked, "#B7A627" },
{ CustomRoles.Aide, "#ff1919" },
{ CustomRoles.Bluetooth, "#ff1919" },
{ CustomRoles.Priority, CovenColor },
{ CustomRoles.Degraded,"#b14a0e" },
{ CustomRoles.Anchor, "#6B4CE4" },
{ CustomRoles.Fragile, "#debe66" },
{ CustomRoles.Absorber, "#29f5a0" },
+4
View File
@@ -217,6 +217,7 @@ internal static class CustomRolesHelper
CustomRoles.Psychic => CustomRoles.Crewmate,
CustomRoles.LazyGuy => CustomRoles.Crewmate,
CustomRoles.Twister => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Tyrant => CustomRoles.Impostor,
CustomRoles.SuperStar => CustomRoles.Crewmate,
CustomRoles.Survivor => CustomRoles.Engineer,
CustomRoles.Anonymous => CustomRoles.Shapeshifter,
@@ -856,6 +857,7 @@ internal static class CustomRolesHelper
CustomRoles.Devourer or
CustomRoles.Camouflager or
CustomRoles.Twister or
CustomRoles.Tyrant or
CustomRoles.Lurker or
CustomRoles.Fakeshifter or
CustomRoles.Perplexer;
@@ -1282,6 +1284,7 @@ internal static class CustomRolesHelper
CustomRoles.Swapster => RoleOptionType.Impostor_Support,
CustomRoles.TimeThief => RoleOptionType.Impostor_Support,
CustomRoles.Twister => RoleOptionType.Impostor_Support,
CustomRoles.Tyrant => RoleOptionType.Impostor_Support,
CustomRoles.Ventriloquist => RoleOptionType.Impostor_Support,
CustomRoles.Vindicator => RoleOptionType.Impostor_Support,
CustomRoles.YinYanger => RoleOptionType.Impostor_Support,
@@ -1932,6 +1935,7 @@ internal static class CustomRolesHelper
CustomRoles.Rascal when !pc.IsCrewmate() => false,
CustomRoles.LazyGuy when pc.GetCustomRole().IsAdditionRole() => false,
CustomRoles.Stealer when pc.Is(CustomRoles.Vindicator) => false,
CustomRoles.Priority when Main.PlayerStates[pc.PlayerId].Role is not CovenBase coven || coven.CanNeverGetNecronomicon => false,
CustomRoles.Bloodlust when !pc.GetCustomRole().IsCrewmate() || pc.GetCustomRole().IsTaskBasedCrewmate() || pc.GetCustomRole() is CustomRoles.Medic => false,
CustomRoles.Mare when pc.GetCustomRole() is CustomRoles.Inhibitor or CustomRoles.Saboteur or CustomRoles.Swift or CustomRoles.Nemesis or CustomRoles.Sniper or CustomRoles.Fireworker or CustomRoles.Swooper or CustomRoles.Vampire => false,
CustomRoles.Torch when pc.GetCustomRole() is CustomRoles.Lighter or CustomRoles.Ignitor or CustomRoles.Investigator or CustomRoles.Eclipse or CustomRoles.Decryptor => false,
-1
View File
@@ -39,7 +39,6 @@ public class PlayerState(byte playerId)
Quantization,
Overtired,
Ashamed,
PissedOff,
Dismembered,
LossOfHead,
Trialed,
+1
View File
@@ -143,6 +143,7 @@ public static class NameColorManager
{
CustomRoles.Transporter when target.IsImpostor() => "ffffff",
CustomRoles.Silencer when Silencer.ForSilencer.Contains(target.PlayerId) => "000000",
CustomRoles.Tyrant when Tyrant.IsDegraded(target) => "000000",
CustomRoles.PlagueBearer when PlagueBearer.IsPlagued(seer.PlayerId, target.PlayerId) => "000000",
CustomRoles.Executioner when Executioner.Target.TryGetValue(seer.PlayerId, out byte exeTarget) && exeTarget == target.PlayerId => "000000",
CustomRoles.Lawyer when Lawyer.Target.TryGetValue(seer.PlayerId, out byte lawyerTarget) && lawyerTarget == target.PlayerId => "000000",
+9 -1
View File
@@ -352,7 +352,7 @@ public static class Utils
foreach (PlayerControl seer in Main.CachedAllPlayerControls())
{
if (seer.Is(CustomRoles.GM) || seer.Is(CustomRoles.Seer))
if (seer.Is(CustomRoles.GM) || seer.Is(CustomRoles.Seer) || (seer.Is(CustomRoles.Bluetooth) && target.IsImpostor()))
{
seer.KillFlash();
continue;
@@ -4289,6 +4289,14 @@ public static class Utils
addons.ForEach(state.RemoveSubRole);
}
break;
case CustomRoles.Tyrant:
foreach (byte id in Tyrant.Degraded[target.PlayerId])
{
if (!Main.PlayerStates.TryGetValue(id, out var state)) continue;
state.RemoveSubRole(CustomRoles.Degraded);
}
Tyrant.Degraded[target.PlayerId] = [];
break;
case CustomRoles.Swapper when disconnect:
Swapper.SwapTargets = (byte.MaxValue, byte.MaxValue);
+1
View File
@@ -10,6 +10,7 @@ using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using TMPro;
using UnityEngine;
using Priority = HarmonyLib.Priority;
// ReSharper disable PossibleLossOfFraction
+1
View File
@@ -12,6 +12,7 @@ using HarmonyLib;
using TMPro;
using UnityEngine;
using static EHR.Translator;
using Priority = HarmonyLib.Priority;
namespace EHR.Patches;
+1
View File
@@ -3,6 +3,7 @@ using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using TMPro;
using UnityEngine;
using Priority = HarmonyLib.Priority;
namespace EHR;
+6
View File
@@ -11,6 +11,7 @@ using Hazel;
using TMPro;
using UnityEngine;
using static EHR.Translator;
using Priority = HarmonyLib.Priority;
namespace EHR.Patches;
@@ -182,6 +183,7 @@ internal static class CheckForEndVotingPatch
bool canVote = !(CheckRole(ps.TargetPlayerId, CustomRoles.Glitch) && !Glitch.CanVote.GetBool());
if (CheckRole(ps.TargetPlayerId, CustomRoles.Shifter) && !Shifter.CanVote.GetBool()) canVote = false;
if (ps.VotedFor.GetPlayer() && CheckRole(ps.VotedFor, CustomRoles.Zombie)) canVote = false;
if (CheckRole(ps.TargetPlayerId, CustomRoles.Degraded)) canVote = false;
if (Poache.PoachedPlayers != null && Poache.PoachedPlayers.Contains(ps.TargetPlayerId)) canVote = false;
if (Silencer.ForSilencer.Contains(ps.TargetPlayerId) && Main.AllAlivePlayerControlsCount > Silencer.MaxPlayersAliveForSilencedToVote.GetInt()) canVote = false;
@@ -199,6 +201,9 @@ internal static class CheckForEndVotingPatch
case Mayor mayor when !Mayor.MayorHideVote.GetBool():
Loop.Times(Mayor.MayorAdditionalVote.GetInt() + mayor.TaskVotes, _ => AddVote());
break;
case Survivor when Main.AllAlivePlayerControlsCount <= Survivor.ThirdAbility.GetInt():
Loop.Times(Survivor.AdditionalVote.GetInt(), _ => AddVote());
break;
}
if (canVote) AddVote();
@@ -645,6 +650,7 @@ internal static class ExtendedMeetingHud
if (Silencer.ForSilencer.Contains(ps.TargetPlayerId) && Main.AllAlivePlayerControlsCount > Silencer.MaxPlayersAliveForSilencedToVote.GetInt()) voteNum = 0;
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Knighted)) voteNum += 1;
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Degraded)) voteNum -= 1;
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Schizophrenic) && Options.DualVotes.GetBool()) voteNum += voteNum;
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Stealer)) voteNum += (int)(Main.EnumeratePlayerControls().Count(x => x.GetRealKiller()?.PlayerId == ps.TargetPlayerId) * Options.VotesPerKill.GetFloat());
+1
View File
@@ -17,6 +17,7 @@ using static EHR.Modules.CustomRoleSelector;
using static EHR.Translator;
using DateTime = Il2CppSystem.DateTime;
using Exception = System.Exception;
using Priority = HarmonyLib.Priority;
namespace EHR;
+1 -1
View File
@@ -559,7 +559,7 @@ internal static class CheckMurderPatch
killer.Kill(player);
Crusader.ForCrusade.Remove(target.PlayerId);
target.RpcGuardAndKill(killer);
Main.PlayerStates[player.PlayerId].deathReason = PlayerState.DeathReason.PissedOff;
Main.PlayerStates[player.PlayerId].deathReason = PlayerState.DeathReason.Destroyed;
return false;
}
}
+19 -4
View File
@@ -123,6 +123,7 @@
"Prefix.Blocked": "Blocked",
"Prefix.Bloodlust": "Bloodlust",
"Prefix.Bloodmoon": "Bloodmoon",
"Prefix.Bluetooth": "Bluetooth",
"Prefix.Busy": "Busy",
"Prefix.Circumvent": "Circumvent",
"Prefix.Cleansed": "Cleansed",
@@ -135,6 +136,7 @@
"Prefix.Damocles": "Damocles",
"Prefix.Deadlined": "Deadlined",
"Prefix.DeadlyQuota": "Deadly Quota",
"Prefix.Degraded": "Degraded",
"Prefix.Disco": "Disco",
"Prefix.Diseased": "Diseased",
"Prefix.Disregarded": "Disregarded",
@@ -182,6 +184,7 @@
"Prefix.Onbound": "Onbound",
"Prefix.Phantasm": "Phantasm",
"Prefix.Physicist": "Physicist",
"Prefix.Priority": "Priority",
"Prefix.Rascal": "Rascal",
"Prefix.Reach": "Reach",
"Prefix.Reroll": "Reroll",
@@ -2291,6 +2294,10 @@
"TwisterInfoLong": "(Impostors):\\nAs the Twister, you can use vanishing/petting to swap the position of all players randomly.\\n\\nThe Twister itself will not swap places with anyone, and players who are in vents cannot be teleported.",
"TwisterButtonText": "Twist",
"TwisterCooldown": "Twist Cooldown",
"Tyrant": "Tyrant",
"TyrantInfo": "Degrade players",
"TyrantInfoLong": "(Impostors):\\nAs the Tyrant, you can mark a player with your Kill button, and when a meeting is called, the player will get the Degraded addon, which subtracts 1 from their total amount of votes. The Tyrant sees all Degraded players in black names. When the Tyrant dies, all players lose the Degraded addon. Double-click to kill normally.",
"TyrantButtonText": "Degrade",
"Undertaker": "Undertaker",
"UndertakerInfo": "Take bodies wherever you want to",
"UndertakerInfoLong": "(Impostors):\\nAs the Undertaker, you can mark your target using your kill button, then vanish/pet to kill them. However, the person you assassinate will teleport to your location when you shapeshift, so their body will be wherever you are! Use this well to hide bodies!",
@@ -3616,6 +3623,9 @@
"Bloodlust": "Bloodlust",
"BloodlustInfo": "You can kill",
"BloodlustInfoLong": "(Add-ons):\\nYou can kill. You are a neutral killer while keeping your role's ability (as much as host-only limitations allow).\\nIf your role has an ability to be used through the kill button, you can single-click to use your ability and double-click the kill button to kill.",
"Bluetooth": "Bluetooth",
"BluetoothInfo": "You know when your teammate dies",
"BluetoothInfoLong": "(Add-ons):\\nWhen your teammate dies, you will see a red flash paired with a Reactor Sabotage alarm sound.",
"Busy": "Busy",
"BusyInfo": "You have more tasks",
"BusyInfoLong": "(Add-ons):\\nYou have more tasks.",
@@ -3687,6 +3697,9 @@
"DeadlyQuota": "Deadly Quota",
"DeadlyQuotaInfo": "You don't win if you didn't kill enough",
"DeadlyQuotaInfoLong": "(Add-ons):\\nDeadly Quota is an add-on that only impostors can get. The holder of this add-on will need to get a specified number of kills (or more) to be able to win. If their win condition triggers before getting a required amount of kills, the player loses even if they were supposed to win.",
"Degraded": "Degraded",
"DegradedInfo": "You lost one of your votes",
"DegradedInfoLong": "(Add-ons):\\nDegraded players were degraded by the Tyrant. When you vote, your vote count will be subtracted by 1. If the Tyrant who degraded you dies, you lose this add-on.",
"Disco": "Disco",
"DiscoInfo": "You continuously change colors",
"DiscoInfoLong": "(Add-ons):\\nYou change your skin color every few seconds.",
@@ -3861,6 +3874,9 @@
"Physicist": "Physicist",
"PhysicistInfo": "Grants Portable Vitals Access",
"PhysicistInfoLong": "(Add-ons):\\nGrants portable vitals access for crewmate Roles.",
"Priority": "Priority",
"PriorityInfo": "Gets Necronomicon First",
"PriorityInfoLong": "(Add-ons):\\nCan only be assigned to Coven roles who can receive Necronomicon. As the Priority, you will always receive the Necronomicon first, even if there is a Coven Leader. If multiple Priorities are present, one will be picked at random to get the Necronomicon first.",
"Rascal": "Rascal",
"RascalInfo": "You appear evil in some cases",
"RascalInfoLong": "(Add-ons):\\nAs the Rascal, you can die to the Sheriff, and Snitch can find you if Snitch can find madmates.\\n\\nOnly assigned to Crewmates, cannot be assigned by the Merchant.",
@@ -3891,7 +3907,7 @@
"SchizophrenicInfoLong": "(Add-ons):\\nNeutrals and Madmates can't become Schizophrenic. The Schizophrenic will be considered as two players in the game. Having at least one player as Schizophrenic won't end the game if there are only them and the Impostor left.",
"Seer": "Seer",
"SeerInfo": "You see when players die",
"SeerInfoLong": "(Add-ons):\\nWhen a player is killed, you will see a red flash paired with a Reactor Sabotage alarm sound..",
"SeerInfoLong": "(Add-ons):\\nWhen a player is killed, you will see a red flash paired with a Reactor Sabotage alarm sound.",
"Shy": "Shy",
"ShyInfo": "You don't hear whispers",
"ShyInfoLong": "(Add-ons):\\nAs the Shy, you can't hear whispers. You can still send them, but you won't be able to hear them.",
@@ -9035,7 +9051,7 @@
"AFKSuffix": "<#ffa500>(AFK)</color>",
"AFKTooMany": "AFK detection was disabled for this round since there are too many AFK players",
"AFKWarning": "<#ffff00><size=90%>Please move to avoid being marked as AFK</color> <#ffffff>({0})</size></color>",
"MenuTitle.CTA": "★ Custom Team Assignment ★",
"MenuTitle.CTA": "★ Custom Team Assignment ★",
"TabGroup.TaskSettings": "Game Modifiers",
"UsePets": "Use Pet button instead of Vent/Shapeshift",
"PetToAssign": "Pet to Assign to Everyone",
@@ -9778,7 +9794,6 @@
"DeathReason.OutOfOxygen": "Out Of Oxygen",
"DeathReason.Overtired": "Overtired",
"DeathReason.Patrolled": "Patrolled",
"DeathReason.PissedOff": "Destroyed",
"DeathReason.Poison": "Poisoned",
"DeathReason.Quantization": "Quantization",
"DeathReason.RNG": "RNG",
@@ -10582,7 +10597,7 @@
"RejoinRequiredDueToVanillaSwitch": "You've decided to switch to vanilla mode, so it was required to disconnect you from the current game, or it would've caused issues. Please rejoin the game.",
"ReportBaitAtAllCost": "Report <#00f7ff>Bait</color> at All Cost",
"ReportButtonText": "Report",
"RequiresPetIndicator": " <#ffab1b>PET</color>",
"RequiresPetIndicator": " <#ffab1b>PET</color>",
"RevivedByAltruist": "<#ffff00><size=150%>You've been revived by the <color=#300000>Altruist</color>!</size></color>",
"RevivedByOccultist": "<#ffff00><size=150%>You've been revived by the <color=#ff1919>Occultist</color>!</size>\\nYou are now on the <color=#ff1919>Impostor</color> team.</color>",
"RevivedByTimeMaster": "<size=150%><#ffff00>You were revived during the rewind!</color></size>",
+60 -3
View File
@@ -24,7 +24,9 @@
"Server": "<#00ffa5>Регион:</color>: ",
"VersionMismatch": "У {0} установлена другая версия мода",
"ModMismatch": "У {0} установлен другой мод (несовместимый с EHR)",
"PrivacyPolicy": "<size=200%>Вы согласились с Политикой конфиденциальности и Кодексом поведения, доступными на официальном GitHub репозитории EHR?</size>\\nВы должны сделать это, чтобы продолжить.",
"EHRConsentPopUp": "<size=2.0>Добро пожаловать в Endless Host Roles!</size>\\n\\n<size=1.3>Играя с модом, вы соглашаетесь, что некоторые игровые и технические данные могут собираться для взаимодействия с модом.\\n\\n> <link=\"https://github.com/Gurge44/EndlessHostRoles#privacy-policy\">Политика Конфиденциальности</link> - Указывает собираемую информацию и её использование.\\n\\n> <link=\"https://github.com/Gurge44/EndlessHostRoles/blob/main/CODE_OF_CONDUCT.md\">Пользовательское соглашение</link> - Указывает нормы поведения и наказания за нарушение правил.\\n\\nНажмите \"Принять\", чтобы продолжить.</size>",
"Reject": "Отклонить",
"Accept": "Принять",
"Yes": "Да",
"No": "Нет",
"None": "Нет",
@@ -121,6 +123,7 @@
"Prefix.Blocked": "Блокированный",
"Prefix.Bloodlust": "Кровожадный",
"Prefix.Bloodmoon": "Кроволуние",
"Prefix.Bluetooth": "Блютуз",
"Prefix.Busy": "Занятой",
"Prefix.Circumvent": "Ограниченный",
"Prefix.Cleansed": "Очищенный",
@@ -133,6 +136,7 @@
"Prefix.Damocles": "Дамоклов",
"Prefix.Deadlined": "Опаздывающий",
"Prefix.DeadlyQuota": "Убийственная Квота",
"Prefix.Degraded": "Заткнутый",
"Prefix.Disco": "Диско",
"Prefix.Diseased": "Больной",
"Prefix.Disregarded": "Проигнорированный",
@@ -147,6 +151,7 @@
"Prefix.Facilitator": "Фасилитатор",
"Prefix.Finder": "Ищущий",
"Prefix.Flash": "Флеш",
"Prefix.Focused": "Фокусированный",
"Prefix.Fool": "Глупый",
"Prefix.Fragile": "Хрупкий",
"Prefix.GA": "Ангел-Хранитель",
@@ -179,6 +184,7 @@
"Prefix.Onbound": "Непобедимый",
"Prefix.Phantasm": "Фантазм",
"Prefix.Physicist": "Аспирант",
"Prefix.Priority": "Приоритетный",
"Prefix.Rascal": "Поддельный",
"Prefix.Reach": "Длиннолапый",
"Prefix.Reroll": "Замена",
@@ -402,6 +408,16 @@
"BodyguardKillsKiller": "Телохранитель убивает убийцу",
"BodyguardProtectRadius": "Радиус защиты",
"BodyguardProtected": "Телохранитель защитил вашу цель и умер вместо неё",
"Bouncer": "Вышибала",
"BouncerInfo": "Вышибайте игроков из комнат",
"BouncerInfoLong": "(Член Экипажа):\\nКак Вышибала, вы можете погладить питомца, чтобы отметить комнату, в которой вы находитесь. Это вышибет всех игроков из комнаты и не позволит им входить в неё некоторое время.",
"Bouncer.AbortBouncingIfLeftRoom": "Прекратить действие способности после выхода из комнаты",
"Bouncer.WhoGetsBounced": "Кого вышибает из комнаты",
"Bouncer.WhoGetsBouncedOptions.Everyone": "Всех",
"Bouncer.WhoGetsBouncedOptions.Impostors": "<#ff1919>Предателей</color>",
"Bouncer.WhoGetsBouncedOptions.ImpNKNPNE": "<#ff1919>Всех злых</color>",
"Bouncer.Suffix": "<#00ffa5>Вышибание</color> <b>{0}</b>",
"Bouncer.Suffix.RemainingSeconds": " <#00ffff>({0}с осталось)</color>",
"CameraMan": "Камераман",
"CameraManInfo": "Телепортируйтесь в комнату охраны",
"CameraManInfoLong": "(Член Экипажа):\\nКамераман может прыгнуть в люк/погладить питомца, чтобы мгновенно телепортироваться в комнату охраны.\\nВ зависимости от настроек, если Камераман выйдет из охраны, его телепортирует туда, откуда он телепортировался в охрану.",
@@ -1943,6 +1959,10 @@
"GangsterRecruitLimit": "Лимит рекрутирования",
"GangsterRecruitmentFailure": "Этот игрок не может быть рекрутирован",
"GangsterSuccessfullyRecruited": "Вы успешно рекрутировали игрока",
"Gatekeeper": "Привратник",
"GatekeeperInfo": "Отслеживайте сколько игроков в комнатах",
"GatekeeperInfoLong": "(Предателей):\\nКак Привратник, вы можете пет/морф/исчезновение, чтобы отметить комнату, в которой вы находитесь. Теперь вы будете видеть кол-во игроков в этой комнате. Трупы не считаются. В зависимости от настроек, вы можете отмечать несколько комнат.",
"Gatekeeper.MaxRoomsMarkedAtOnce": "Макс. кол-во комнат за раз",
"Generator": "Генератор",
"GeneratorInfo": "Генерируйте способности",
"GeneratorInfoLong": "(Предатель):\\nКак Генератор, каждую секунду вы генерируете заряды. Убивать/Прыгать в люк/Морфиться/Саботировать можно только при достаточном кол-ве зарядов. Вы также теряете заряды во время морфа.",
@@ -2274,6 +2294,10 @@
"TwisterInfoLong": "(Предатель):\\nКак Твистер, если вы морфнитесь/погладите питомца, все игроки поменяются местами.\\n\\nВы сами, игроки в люке и в других подобных состояниях не будут телепортированы.",
"TwisterButtonText": "Твист",
"TwisterCooldown": "Откат твиста",
"Tyrant": "Тиран",
"TyrantInfo": "Затыкайте игроков",
"TyrantInfoLong": "(Предатель):\\nКак Тиран, вы можете заткнуть игрока кнопкой убийства. Тогда во время следующего собрания игрок получит атрибут Заткнутый, из-за чего у него будет на 1 голос меньше. Тиран видит заткнутых игроков чёрными именами. После смерти все заткнутые теряют этот атрибут. Обычное убийство - 2 нажатия на кнопку убийства.",
"TyrantButtonText": "Заткнутый",
"Undertaker": "Андертейкер",
"UndertakerInfo": "Телепортируйте трупы туда, куда хотите",
"UndertakerInfoLong": "(Предатель):\\nКак Андертейкер, вы можете пометить свою цель, используя кнопку убийства, а затем исчезнуть/погладить питомца, чтобы убить её. Труп убитого вами игрока телепортируется туда, где вы исчезли/погладили питомца.",
@@ -3599,6 +3623,9 @@
"Bloodlust": "Кровожадный",
"BloodlustInfo": "Вы - нейтральный убийца",
"BloodlustInfoLong": "(Атрибут):\\nКак Кровожадный, вы - член экипажа, ставший убийцей. Вы сохраняете способность своей роли. Некоторые роли членов экипажа не могут стать Кровожадным в принципе.\\nЕсли у вашей роли была способность, которая использовалась с помощью кнопки убийства, то одиночное нажатие активирует эту способность, а двойное - убивает.",
"Bluetooth": "Блютуз",
"BluetoothInfo": "Если ваш тиммейт умрёт, вы увидите вспышку",
"BluetoothInfoLong": "(Атрибут):\\nКогда ваш тиммейт умрёт, вы увидите красную вспышку и звук саботажа реактора на мгновение.",
"Busy": "Занятой",
"BusyInfo": "У вас больше тасков",
"BusyInfoLong": "(Атрибут):\\nУ вас больше заданий.",
@@ -3670,6 +3697,9 @@
"DeadlyQuota": "Убийственная Квота",
"DeadlyQuotaInfo": "Пока не убьёте достаточно, не выиграете",
"DeadlyQuotaInfoLong": "(Атрибут Предателей):\\nУбийственная Квота требует от своего носителя убить определённое количество игроков (или больше), чтобы быть способным выиграть. Если убийств будет недостаточно, носитель атрибута проиграет даже тогда, когда должен был выиграть.",
"Degraded": "Заткнутый",
"DegradedInfo": "У вас на 1 голос меньше",
"DegradedInfoLong": "(Атрибут):\\nТе, кого заткнул Тиран, получают атрибут Заткнутый. У Заткнутых на 1 голос меньше на собрании. Если Тиран умрёт, Заткнутые потеряют этот атрибут.",
"Disco": "Диско",
"DiscoInfo": "Вы постоянно меняете цвета",
"DiscoInfoLong": "(Атрибут):\\nВаш скин меняет цвет каждые несколько секунд. Цвета не меняются во время камуфляжа/морфа.",
@@ -3721,6 +3751,9 @@
"FlashInfo": "СКО-О-Р-О-О-С-Т-Ь!!!",
"FlashInfoLong": "(Атрибут):\\nВы быстрее других игроков.",
"FlashSpeed": "Скорость <color=#ff8400>Флеша</color>",
"Focused": "Фокусированный",
"FocusedInfo": "На вас не действуют эффекты от убийства",
"FocusedInfoLong": "(Атрибут):\\nКак Сфокусированный, если вы убьёте, у вас иммунитет к эффектам, накладываемым Капканом, Запутанным, Проклятием и др.).",
"Fool": "Глупый",
"FoolInfo": "Вы не можете чинить саботажи света и связи",
"FoolInfoLong": "(Атрибут):\\nВы не можете чинить саботажи света и связи.",
@@ -3841,6 +3874,9 @@
"Physicist": "Аспирант",
"PhysicistInfo": "Вы можете смотреть пульсы",
"PhysicistInfoLong": "(Атрибут Членов Экипажа):\\nДаёт доступ к пульсам игрокам, чья роль основана на ванильном Члене Экипажа.",
"Priority": "Приоритетный",
"PriorityInfo": "Получите Некрономикон первым",
"PriorityInfoLong": "(Атрибут Ковенов):\\nКак Приоритетный, вы будете первым Ковеном, который получит Некрономикон, даже если есть Лидер Ковенов. Если Приоритетных несколько, будет выбран случайный из них.",
"Rascal": "Поддельный",
"RascalInfo": "Другие видят вас как злого",
"RascalInfoLong": "(Атрибут Членов Экипажа):\\nИграя с данным атрибутом, для других вы выглядите как Безумец. Шериф может вас убить, Стукач видит вас как Безумца и т.д.",
@@ -4434,6 +4470,7 @@
"HNS.MaxNeutrals": "<#0000ff>Макс.</color> кол-во <#ffab1b>Нейтралов</color>",
"HNS.MinNeutrals": "<#00ffff>Мин.</color> кол-во <#ffab1b>Нейтралов</color>",
"HNS.PlayersSeeRoles": "Игроки видят роли друг друга",
"HNS.ShiftAndSeek": "Прятки с маскировкой",
"HNS.TaskCount": "Таски:",
"HideAndSeek_MaxGameTime": "Макс. длительность игры",
"TimeDecreaseOnCommonTaskComplete": "Общий таск уменьшает время на",
@@ -4441,6 +4478,7 @@
"TimeDecreaseOnOtherTaskComplete": "Не попадающий ни под одну из категорий таск уменьшает время на",
"TimeDecreaseOnShortTaskComplete": "Короткий таск уменьшает время на",
"TimeDecreaseOnSituationalTaskComplete": "Ситуативный таск уменьшает время на",
"HNSSeekers": "<#ff1919>Искатели</color>: {0}",
"SeekerCanVent": "Искатель может прыгать в люки",
"SeekerSpeed": "Скорость Искателя",
"SeekerVision": "Зрение Искателя",
@@ -4472,6 +4510,7 @@
"FoxInfoLong": "(Нейтрал):\\nЛис победит с любой выигравшей командой (включая Искателей), если просто доживёт до конца игры.",
"FoxSpeed": "<#ffffff>Скорость <#00ff00>Лиса<#ffffff>",
"FoxVision": "<#ffffff>Зрение <#00ff00>Лиса<#ffffff>",
"FoxHasVitals": "У Лиса есть пульсы",
"Hider": "Прячущийся",
"HiderInfo": "ВЫЖИВИТЕ И ДЕЛАЙТЕ ТАСКИ",
"HiderInfoLong": "(Прячущийся):\\nЦель Прячущийся - выжить до конца и спастись от Искателей. Выполняя таски, Прячущийся уменьшает время игры.",
@@ -4516,6 +4555,20 @@
"VenterInfoLong": "(Искатель):\\nВентер может использовать люки.",
"VenterSpeed": "Скорость Вентера",
"VenterVision": "Зрение Вентера",
"Disguiser": "Искатель-Маскировщик",
"DisguiserInfo": "Маскируйтесь!",
"DisguiserInfoLong": "(Искатель):\\nКак Искатель-Маскировщик, вы можете маскироваться под других игроков. В зависимости от настроек, вы можете убивать только тех, под кого маскируетесь.",
"DisguiserSpeed": "Скорость Искателя-Маскировщика",
"DisguiserVision": "Зрение Искателя-Маскировщика",
"DisguiserCanOnlyKillShapeshiftTarget": "Может убивать только того, под кого маскируется",
"DisguiserPenaltyForWrongKillAttempt": "Наказание за попытку убить не того",
"DisguiserPFWKA.BlockKill": "Ничего",
"DisguiserPFWKA.Suicide": "Суицид",
"DisguiserPFWKA.ResetKillCooldown": "Сброс отката",
"DisguiserToleranceBeforeSuicide": "Кол-во ошибок перед суицидом",
"DisguiserResetKillCooldownTo": "Сбросить откат до",
"DisguiserShapeshiftCooldown": "Откат маскировки",
"DisguiserShapeshiftDuration": "Длительность маскировки",
"HotPotato": "Горячая Картошка",
"ModeHotPotato": "Режим: Горячая Картошка",
"ModeDescribe.HotPotato": "В режиме ''Горячая Картошка'' один из игроков всегда держит горячую картошку, и если этот игрок пройдёт мимо другого, то он передаст эту картошку другому. Каждые X секунд (X - время, зависящее от настроек) игрок с картошкой умирает. Игрок с картошкой не может передать её предыдущему владельцу (за исключением того момента, когда на карте остаётся всего 2 игрока). Последний в живых побеждает!",
@@ -8631,10 +8684,11 @@
"Arabic": "Арабский",
"Slovak": "Словацкий",
"Romanian": "Румынский",
"Serbian": "Сербский",
"LatinSerbian": "Сербский (Лат.)",
"Vietnamese": "Вьетнамский",
"Belarusian": "Белорусский",
"Turkish": "Турецкий",
"CyrillicSerbian": "Сербский (Кир.)",
"KickLowLevelPlayer": "Кикнуть игроков, чей уровень ниже",
"KickBecauseLowLevel": "<b>{0}</b> был кикнут из-за слишком низкого уровня",
"KickMobilePlayer": "Кикнуть игроков с телефонов",
@@ -8939,6 +8993,7 @@
"WhenSkipVoteIgnoreNoDeadBody": "Выключить, если никто не умер",
"WhenSkipVoteIgnoreEmergency": "Выключить на срочных собрания",
"MenuTitle.GameCommands": "Игровые Команды",
"EnableAnagramCommand": "Включить команду /anagram",
"AnagramLanguage": "Язык игры Анаграммы",
"AnagramLang.Auto": "Авто (язык в настройках игры)",
"AnagramLang.English": "Английский",
@@ -9475,6 +9530,7 @@
"CommandDescription.SetRole": "Установить роль или атрибут игроку на следующую игру",
"CommandDescription.Spectate": "Стать Спектатором на следующую игру. Вы должны просто включить режим Спектатора в настройках, как Хост.",
"CommandDescription.Start": "Начать игру",
"CommandDescription.StartNow": "Начинает игру мгновенно, без обратного отсчёта",
"CommandDescription.Summon": "Вызвать игрока убить за вас, будучи Вызывателем",
"CommandDescription.Swap": "Свапнуть голоса как Сваппер",
"CommandDescription.TimeLimit": "Отобразить оставшееся время до конца игры (когда включён лимит времени на игру)",
@@ -9588,6 +9644,7 @@
"CommandForms.SetRole": "setrole,setaddon",
"CommandForms.Spectate": "spectate,спектатор,наблюдать",
"CommandForms.Start": "start,старт,начать",
"CommandForms.StartNow": "startnow,начатьсразу,начатьсейчас",
"CommandForms.Summon": "summon",
"CommandForms.Swap": "sw,swap,st,свап",
"CommandForms.TimeLimit": "til,timelimit,тил",
@@ -9737,7 +9794,6 @@
"DeathReason.OutOfOxygen": "Задохнулся",
"DeathReason.Overtired": "Переработал",
"DeathReason.Patrolled": "Дозор",
"DeathReason.PissedOff": "Разозлён",
"DeathReason.Poison": "Отравлен",
"DeathReason.Quantization": "Квантование",
"DeathReason.RNG": "ГСЧ",
@@ -10343,6 +10399,7 @@
"AlliesKnowCrewpostor": "<color=#ff1919>Предатели</color> знают <color=#ff1919>Экипателя</color>",
"AmOblivious": "<color=#424242>Игнорирующий</color> не может репортить трупы",
"Anagram": "Зашифрованное слово - <b>{0}</b>! Отгадайте изначальное! (Подсказка: первая буква - <b>{1}</b>!)",
"AnagramDisabled": "<#ff0000>X</color> Команда /anagram выключена.",
"AnalyzeCD": "Откат анализа",
"AnalyzeDur": "Длительность анализа",
"AnalyzerCD": "Откат анализа",
+11
View File
@@ -0,0 +1,11 @@
namespace EHR.Roles;
public class Priority : IAddon
{
public AddonTypes Type => AddonTypes.Helpful;
public void SetupCustomOption()
{
Options.SetupAdtRoleOptions(659550, CustomRoles.Priority, canSetNum: true);
}
}
@@ -0,0 +1,11 @@
namespace EHR.Roles;
public class Bluetooth : IAddon
{
public AddonTypes Type => AddonTypes.ImpOnly;
public void SetupCustomOption()
{
Options.SetupAdtRoleOptions(659500, CustomRoles.Bluetooth, canSetNum: true);
}
}
+10 -2
View File
@@ -17,14 +17,22 @@ public abstract class CovenBase : RoleBase
public bool HasNecronomicon { get; set; }
public bool CanNeverGetNecronomicon => NecronomiconReceivePriority == NecronomiconReceivePriorities.Never;
public virtual void OnReceiveNecronomicon() { }
private static void GiveNecronomicon()
{
Dictionary<byte, PlayerState> psDict = Main.PlayerStates.Where(x => x.Value.Role is CovenBase { HasNecronomicon: false } coven && coven.NecronomiconReceivePriority != NecronomiconReceivePriorities.Never).ToDictionary(x => x.Key, x => x.Value);
Dictionary<byte, PlayerState> psDict = Main.PlayerStates
.Where(x => x.Value.Role is CovenBase { HasNecronomicon: false } coven && !coven.CanNeverGetNecronomicon)
.ToDictionary(x => x.Key, x => x.Value);
if (psDict.Count == 0) return;
KeyValuePair<byte, PlayerState> receiver = psDict.Shuffle().OrderByDescending(x => !x.Value.IsDead).ThenBy(x => ((CovenBase)x.Value.Role).NecronomiconReceivePriority).First();
KeyValuePair<byte, PlayerState> receiver = psDict.Shuffle()
.OrderByDescending(x => !x.Value.IsDead)
.ThenByDescending(x => x.Value.SubRoles.Contains(CustomRoles.Priority))
.ThenBy(x => ((CovenBase)x.Value.Role).NecronomiconReceivePriority)
.First();
var covenRole = (CovenBase)receiver.Value.Role;
covenRole.HasNecronomicon = true;
+2 -1
View File
@@ -83,7 +83,8 @@ public class Goddess : CovenBase
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Stoned;
if (killer.PlayerId != target.PlayerId)
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Stoned;
}
public void ReceiveRPC(MessageReader reader)
+2 -1
View File
@@ -139,7 +139,8 @@ public class Sheriff : RoleBase
{
killer.RpcRemoveAbilityUse();
Logger.Info($"{killer.GetNameWithRole()} : Number of kills left: {killer.GetAbilityUseLimit()}", "Sheriff");
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Shot;
if (killer.PlayerId != target.PlayerId)
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Shot;
if (killer.AmOwner && Utils.TimeStamp - IntroCutsceneDestroyPatch.IntroDestroyTS < 25)
Achievements.Type.ItsGamblingTime.Complete();
+129
View File
@@ -0,0 +1,129 @@
using System.Collections.Generic;
using System.Linq;
using EHR.Modules;
using Hazel;
namespace EHR.Roles;
public class Tyrant : RoleBase
{
public static bool On;
public static Dictionary<byte, HashSet<byte>> Degraded = [];
private List<byte> ScheduledAssigns = [];
private byte TyrantId;
private static OptionItem KillCooldown;
private static OptionItem AbilityUseLimit;
public static OptionItem AbilityUseGainWithEachKill;
public override bool IsEnable => On;
public override void SetupCustomOption()
{
StartSetup(659450)
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(1, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityUseLimit, 3f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityUseGainWithEachKill, 0.5f, new FloatValueRule(0f, 5f, 0.25f), OptionFormat.Times);
}
public override void Init()
{
On = false;
Degraded = [];
}
public override void Add(byte playerId)
{
On = true;
Degraded[playerId] = [];
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
TyrantId = playerId;
}
public override void SetKillCooldown(byte id)
{
Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
}
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (killer.GetAbilityUseLimit() < 1f || IsDegraded(target)) return true;
return killer.CheckDoubleTrigger(target, () =>
{
ScheduledAssigns.Add(target.PlayerId);
Degraded[killer.PlayerId].Add(target.PlayerId);
killer.RpcRemoveAbilityUse();
killer.ResetKillCooldown();
killer.SetKillCooldown();
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, 1, target.PlayerId);
});
}
public override void OnReportDeadBody()
{
var tyrant = TyrantId.GetPlayer();
if (!tyrant || !tyrant.IsAlive()) return;
foreach (byte id in ScheduledAssigns)
{
var pc = id.GetPlayer();
if (!pc) continue;
pc.RpcSetCustomRole(CustomRoles.Degraded);
}
ScheduledAssigns.Clear();
}
// this method is used in case target's Degraded addon was removed, e.g. Cleansed
public override void AfterMeetingTasks()
{
List<byte> removeList = [];
foreach (byte id in Degraded[TyrantId])
{
var pc = id.GetPlayer();
if (!pc || !pc.IsAlive())
continue;
if (!pc.Is(CustomRoles.Degraded))
removeList.Add(id);
}
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncRoleData, SendOption.Reliable);
writer.Write(TyrantId);
writer.WritePacked(2);
writer.Write(removeList.Count);
foreach (byte id in removeList)
{
Degraded[TyrantId].Remove(id);
writer.Write(id);
}
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
public void ReceiveRPC(MessageReader reader)
{
switch (reader.ReadPackedInt32())
{
case 1:
Degraded[TyrantId].Add(reader.ReadByte());
break;
case 2:
int length = reader.ReadInt32();
for (var i = 0; i < length; i++) Degraded[TyrantId].Remove(reader.ReadByte());
break;
}
}
public static bool IsDegraded(PlayerControl target)
{
return Degraded.Values.Any(x => x.Contains(target.PlayerId)) || target.Is(CustomRoles.Degraded);
}
public override void SetButtonTexts(HudManager hud, byte id)
{
hud.KillButton?.OverrideText(Translator.GetString("TyrantButtonText"));
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ internal class Provocateur : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.PissedOff;
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Destroyed;
killer.Kill(target);
Provoked.TryAdd(killer.PlayerId, target.PlayerId);
return false;