mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2013dc25f | ||
|
|
cc8244f487 | ||
|
|
0dc6fc2164 | ||
|
|
1e57d4e171 | ||
|
|
9abed63369 | ||
|
|
2713b69f86 |
No files matched your search
@@ -88,15 +88,12 @@ internal class CustomRoleSelector
|
||||
{
|
||||
object cr = list[i1];
|
||||
CustomRoles role = (CustomRoles)Enum.Parse(typeof(CustomRoles), cr.ToString());
|
||||
if (role.IsVanilla() || role.IsAdditionRole()) continue;
|
||||
if (!Main.UseVersionProtocol.Value && !role.IsAbleToHostPublic()) continue;
|
||||
if (role.IsVanilla() || role.IsAdditionRole() || (CustomRolesHelper.OnlySpawnsWithPetsRoleList.Contains(role) && !Options.UsePets.GetBool()) || (!Main.UseVersionProtocol.Value && !role.IsAbleToHostPublic())) continue;
|
||||
switch (role)
|
||||
{
|
||||
case CustomRoles.DarkHide when (MapNames)Main.NormalOptions.MapId == MapNames.Fungle:
|
||||
case CustomRoles.Pelican when roleList.Contains(CustomRoles.Duellist):
|
||||
case CustomRoles.Duellist when roleList.Contains(CustomRoles.Pelican):
|
||||
case CustomRoles.Tunneler when !Options.UsePets.GetBool():
|
||||
case CustomRoles.Tornado when !Options.UsePets.GetBool():
|
||||
case CustomRoles.GM:
|
||||
case CustomRoles.NotAssigned:
|
||||
continue;
|
||||
|
||||
@@ -15,7 +15,7 @@ public class DevUser(string code = "", string color = "null", string tag = "null
|
||||
public string UpName { get; set; } = upName;
|
||||
|
||||
public bool HasTag() => Tag != "null";
|
||||
public string GetTag() => Color == "null" ? $"<size=1>{Tag}</size>\r\n" : $"<color={Color}><size=1>{(Tag == "#Dev" ? Translator.GetString("Developer") : Tag)}</size></color>\r\n";
|
||||
public string GetTag() => Color == "null" ? $"<size=1.4>{Tag}</size>\r\n" : $"<color={Color}><size=1.4>{(Tag == "#Dev" ? Translator.GetString("Developer") : Tag)}</size></color>\r\n";
|
||||
}
|
||||
|
||||
public static class DevManager
|
||||
|
||||
@@ -213,7 +213,10 @@ static class ExtendedPlayerControl
|
||||
Main.PlayerStates[pc.PlayerId].deathReason = deathReason;
|
||||
Main.PlayerStates[pc.PlayerId].SetDead();
|
||||
if (realKiller != null) pc.SetRealKiller(realKiller);
|
||||
|
||||
Medic.IsDead(pc);
|
||||
if (realKiller != null && realKiller.Is(CustomRoles.Damocles)) Damocles.OnMurder();
|
||||
|
||||
pc.Kill(pc);
|
||||
}
|
||||
|
||||
@@ -624,8 +627,8 @@ static class ExtendedPlayerControl
|
||||
CustomRoles.YinYanger => pc.IsAlive(),
|
||||
CustomRoles.Librarian => pc.IsAlive() && Librarian.CanUseKillButton(pc),
|
||||
CustomRoles.Cantankerous => pc.IsAlive() && Cantankerous.CanUseKillButton(pc.PlayerId),
|
||||
CustomRoles.Inhibitor => !Utils.IsActive(SystemTypes.Electrical) && !Utils.IsActive(SystemTypes.Comms) && !Utils.IsActive(SystemTypes.MushroomMixupSabotage) && !Utils.IsActive(SystemTypes.Laboratory) && !Utils.IsActive(SystemTypes.LifeSupp) && !Utils.IsActive(SystemTypes.Reactor) && !Utils.IsActive(SystemTypes.HeliSabotage),
|
||||
CustomRoles.Saboteur => Utils.IsActive(SystemTypes.Electrical) || Utils.IsActive(SystemTypes.Comms) || Utils.IsActive(SystemTypes.MushroomMixupSabotage) || Utils.IsActive(SystemTypes.Laboratory) || Utils.IsActive(SystemTypes.LifeSupp) || Utils.IsActive(SystemTypes.Reactor) || Utils.IsActive(SystemTypes.HeliSabotage),
|
||||
CustomRoles.Inhibitor => !IsActive(SystemTypes.Electrical) && !IsActive(SystemTypes.Comms) && !IsActive(SystemTypes.MushroomMixupSabotage) && !IsActive(SystemTypes.Laboratory) && !IsActive(SystemTypes.LifeSupp) && !IsActive(SystemTypes.Reactor) && !IsActive(SystemTypes.HeliSabotage),
|
||||
CustomRoles.Saboteur => IsActive(SystemTypes.Electrical) || IsActive(SystemTypes.Comms) || IsActive(SystemTypes.MushroomMixupSabotage) || IsActive(SystemTypes.Laboratory) || IsActive(SystemTypes.LifeSupp) || IsActive(SystemTypes.Reactor) || IsActive(SystemTypes.HeliSabotage),
|
||||
CustomRoles.Sniper => Sniper.CanUseKillButton(pc),
|
||||
CustomRoles.Sheriff => Sheriff.CanUseKillButton(pc.PlayerId),
|
||||
CustomRoles.Crusader => Crusader.CanUseKillButton(pc.PlayerId),
|
||||
@@ -1209,7 +1212,6 @@ static class ExtendedPlayerControl
|
||||
case CustomRoles.Medusa:
|
||||
Medusa.SetKillCooldown(player.PlayerId);
|
||||
break;
|
||||
|
||||
case CustomRoles.Cleaner:
|
||||
Main.AllPlayerKillCooldown[player.PlayerId] = Options.CleanerKillCooldown.GetFloat();
|
||||
break;
|
||||
|
||||
+128
-36
@@ -1,4 +1,3 @@
|
||||
using Csv;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using System;
|
||||
@@ -7,6 +6,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TOHE;
|
||||
@@ -17,55 +17,105 @@ public static class Translator
|
||||
public const string LANGUAGE_FOLDER_NAME = "Language";
|
||||
public static void Init()
|
||||
{
|
||||
Logger.Info("Loading language file...", "Translator");
|
||||
Logger.Info("Loading Custom Translations...", "Translator");
|
||||
LoadLangs();
|
||||
Logger.Info("Language file loaded successfully", "Translator");
|
||||
Logger.Info("Loaded Custom Translations", "Translator");
|
||||
}
|
||||
public static void LoadLangs()
|
||||
{
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
var stream = assembly.GetManifestResourceStream("TOHE.Resources.String.csv");
|
||||
translateMaps = [];
|
||||
try
|
||||
{
|
||||
// Get the directory containing the JSON files (e.g., TOHE.Resources.Lang)
|
||||
string jsonDirectory = "TOHE.Resources.Lang";
|
||||
// Get the assembly containing the resources
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
string[] jsonFileNames = GetJsonFileNames(assembly, jsonDirectory);
|
||||
|
||||
var options = new CsvOptions()
|
||||
{
|
||||
HeaderMode = HeaderMode.HeaderPresent,
|
||||
AllowNewLineInEnclosedFieldValues = false,
|
||||
};
|
||||
foreach (var line in CsvReader.ReadFromStream(stream, options))
|
||||
{
|
||||
if (line.Values[0][0] == '#') continue;
|
||||
try
|
||||
translateMaps = [];
|
||||
|
||||
if (jsonFileNames.Length == 0)
|
||||
{
|
||||
Dictionary<int, string> dic = [];
|
||||
for (int i = 1; i < line.ColumnCount; i++)
|
||||
Logger.Warn("Json Translation files does not exist.", "Translator");
|
||||
return;
|
||||
}
|
||||
foreach (string jsonFileName in jsonFileNames)
|
||||
{
|
||||
// Read the JSON file content
|
||||
using Stream resourceStream = assembly.GetManifestResourceStream(jsonFileName);
|
||||
if (resourceStream != null)
|
||||
{
|
||||
int id = int.Parse(line.Headers[i]);
|
||||
dic[id] = line.Values[i].Replace("\\n", "\n").Replace("\\r", "\r");
|
||||
using StreamReader reader = new(resourceStream);
|
||||
string jsonContent = reader.ReadToEnd();
|
||||
|
||||
// Deserialize the JSON into a dictionary
|
||||
Dictionary<string, string> jsonDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonContent);
|
||||
if (jsonDictionary.TryGetValue("LanguageID", out string languageIdObj) && int.TryParse(languageIdObj, out int languageId))
|
||||
{
|
||||
// Remove the "LanguageID" entry
|
||||
jsonDictionary.Remove("LanguageID");
|
||||
|
||||
// Handle the rest of the data and merge it into the resulting translation map
|
||||
MergeJsonIntoTranslationMap(translateMaps, languageId, jsonDictionary);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Logger.Warn(jsonDictionary["HostText"], "Translator");
|
||||
Logger.Warn($"Invalid JSON format in {jsonFileName}: Missing or invalid 'LanguageID' field.", "Translator");
|
||||
}
|
||||
}
|
||||
if (!translateMaps.TryAdd(line.Values[0], dic))
|
||||
Logger.Warn($"Duplicate in CSV file to be translated: line {line.Index} => \"{line.Values[0]}\"", "Translator");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
// Convert the resulting translation map to JSON
|
||||
string mergedJson = JsonSerializer.Serialize(translateMaps, new JsonSerializerOptions
|
||||
{
|
||||
Logger.Warn($"Translation file error: line {line.Index} => \"{line.Values[0]}\"", "Translator");
|
||||
Logger.Warn(ex.ToString(), "Translator");
|
||||
}
|
||||
WriteIndented = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Error: {ex}", "Translator");
|
||||
}
|
||||
|
||||
// カスタム翻訳ファイルの読み込み
|
||||
// Loading custom translation files
|
||||
if (!Directory.Exists(LANGUAGE_FOLDER_NAME)) Directory.CreateDirectory(LANGUAGE_FOLDER_NAME);
|
||||
|
||||
// 翻訳テンプレートの作成
|
||||
// Creating a translation template
|
||||
CreateTemplateFile();
|
||||
System.Collections.IList list = Enum.GetValues(typeof(SupportedLangs));
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
foreach (var lang in EnumHelper.GetAllValues<SupportedLangs>())
|
||||
{
|
||||
object lang = list[i];
|
||||
if (File.Exists(@$"./{LANGUAGE_FOLDER_NAME}/{lang}.dat"))
|
||||
LoadCustomTranslation($"{lang}.dat", (SupportedLangs)lang);
|
||||
{
|
||||
UpdateCustomTranslation($"{lang}.dat", lang);
|
||||
LoadCustomTranslation($"{lang}.dat", lang);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void MergeJsonIntoTranslationMap(Dictionary<string, Dictionary<int, string>> translationMaps, int languageId, Dictionary<string, string> jsonDictionary)
|
||||
{
|
||||
foreach (var kvp in jsonDictionary)
|
||||
{
|
||||
string textString = kvp.Key;
|
||||
if (kvp.Value is string translation)
|
||||
{
|
||||
|
||||
// If the textString is not already in the translation map, add it
|
||||
if (!translationMaps.ContainsKey(textString))
|
||||
{
|
||||
translationMaps[textString] = [];
|
||||
}
|
||||
|
||||
// Add or update the translation for the current id and textString
|
||||
translationMaps[textString][languageId] = translation.Replace("\\n", "\n").Replace("\\r", "\r");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get a list of JSON file names in a directory
|
||||
static string[] GetJsonFileNames(System.Reflection.Assembly assembly, string directoryName)
|
||||
{
|
||||
string[] resourceNames = assembly.GetManifestResourceNames();
|
||||
return resourceNames.Where(resourceName => resourceName.StartsWith(directoryName) && resourceName.EndsWith(".json")).ToArray();
|
||||
}
|
||||
|
||||
public static string GetString(string s, Dictionary<string, string> replacementDic = null, bool console = false)
|
||||
{
|
||||
@@ -93,8 +143,8 @@ public static class Translator
|
||||
}
|
||||
if (!translateMaps.ContainsKey(str)) //translateMapsにない場合、StringNamesにあれば取得する
|
||||
{
|
||||
var stringNames = Enum.GetValues(typeof(StringNames)).Cast<StringNames>().Where(x => x.ToString() == str);
|
||||
if (stringNames != null && stringNames.Any())
|
||||
var stringNames = EnumHelper.GetAllValues<StringNames>().Where(x => x.ToString() == str).ToArray();
|
||||
if (stringNames != null && stringNames.Length > 0)
|
||||
res = GetString(stringNames.FirstOrDefault());
|
||||
}
|
||||
}
|
||||
@@ -132,12 +182,54 @@ public static class Translator
|
||||
return SupportedLangs.English;
|
||||
}
|
||||
}
|
||||
static void UpdateCustomTranslation(string filename, SupportedLangs lang)
|
||||
{
|
||||
string path = @$"./{LANGUAGE_FOLDER_NAME}/{filename}";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Logger.Info("Updating Custom Translations", "UpdateCustomTranslation");
|
||||
try
|
||||
{
|
||||
List<string> textStrings = [];
|
||||
using (StreamReader reader = new(path, Encoding.GetEncoding("UTF-8")))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
// Split the line by ':' to get the first part
|
||||
string[] parts = line.Split(':');
|
||||
|
||||
// Check if there is at least one part before ':'
|
||||
if (parts.Length >= 1)
|
||||
{
|
||||
// Trim any leading or trailing spaces and add it to the list
|
||||
string textString = parts[0].Trim();
|
||||
textStrings.Add(textString);
|
||||
}
|
||||
}
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
foreach (var templateString in translateMaps.Keys)
|
||||
{
|
||||
if (!textStrings.Contains(templateString)) sb.Append($"{templateString}:\n");
|
||||
}
|
||||
using FileStream fileStream = new(path, FileMode.Append, FileAccess.Write);
|
||||
using StreamWriter writer = new(fileStream);
|
||||
writer.WriteLine(sb.ToString());
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("An error occurred: " + e.Message, "Translator");
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void LoadCustomTranslation(string filename, SupportedLangs lang)
|
||||
{
|
||||
string path = @$"./{LANGUAGE_FOLDER_NAME}/{filename}";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Logger.Info($"Loading custom translation file:{filename}", "LoadCustomTranslation");
|
||||
Logger.Info($"Loading Custom Translation File:{filename}", "LoadCustomTranslation");
|
||||
using StreamReader sr = new(path, Encoding.GetEncoding("UTF-8"));
|
||||
string text;
|
||||
string[] tmp = [];
|
||||
@@ -152,14 +244,14 @@ public static class Translator
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Logger.Warn($"Invalid key:{tmp[0]}", "LoadCustomTranslation");
|
||||
Logger.Warn($"Invalid Key:{tmp[0]}", "LoadCustomTranslation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"Custom translation file not found:{filename}", "LoadCustomTranslation");
|
||||
Logger.Error($"Custom Translation File Not Found:{filename}", "LoadCustomTranslation");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-31
@@ -383,32 +383,8 @@ public static class Utils
|
||||
|
||||
if (Options.NameDisplayAddons.GetBool() && !pure && self)
|
||||
{
|
||||
if (Options.AddBracketsToAddons.GetBool())
|
||||
{
|
||||
if (Options.ImpEgoistVisibalToAllies.GetBool())
|
||||
{
|
||||
foreach (var subRole in targetSubRoles.Where(x => x is not CustomRoles.LastImpostor and not CustomRoles.Madmate and not CustomRoles.Charmed and not CustomRoles.Recruit and not CustomRoles.Admired and not CustomRoles.Soulless and not CustomRoles.Lovers and not CustomRoles.Infected and not CustomRoles.Contagious))
|
||||
RoleText = ColorString(GetRoleColor(subRole), GetString("PrefixB." + subRole.ToString())) + RoleText;
|
||||
}
|
||||
if (!Options.ImpEgoistVisibalToAllies.GetBool())
|
||||
{
|
||||
foreach (var subRole in targetSubRoles.Where(x => x is not CustomRoles.LastImpostor and not CustomRoles.Madmate and not CustomRoles.Charmed and not CustomRoles.Recruit and not CustomRoles.Admired and not CustomRoles.Soulless and not CustomRoles.Lovers and not CustomRoles.Infected and not CustomRoles.Contagious))
|
||||
RoleText = ColorString(GetRoleColor(subRole), GetString("PrefixB." + subRole.ToString())) + RoleText;
|
||||
}
|
||||
}
|
||||
else if (!Options.AddBracketsToAddons.GetBool())
|
||||
{
|
||||
if (Options.ImpEgoistVisibalToAllies.GetBool())
|
||||
{
|
||||
foreach (var subRole in targetSubRoles.Where(x => x is not CustomRoles.LastImpostor and not CustomRoles.Madmate and not CustomRoles.Charmed and not CustomRoles.Recruit and not CustomRoles.Admired and not CustomRoles.Soulless and not CustomRoles.Lovers and not CustomRoles.Infected and not CustomRoles.Contagious))
|
||||
RoleText = ColorString(GetRoleColor(subRole), GetString("Prefix." + subRole.ToString())) + RoleText;
|
||||
}
|
||||
if (!Options.ImpEgoistVisibalToAllies.GetBool())
|
||||
{
|
||||
foreach (var subRole in targetSubRoles.Where(x => x is not CustomRoles.LastImpostor and not CustomRoles.Madmate and not CustomRoles.Charmed and not CustomRoles.Recruit and not CustomRoles.Admired and not CustomRoles.Soulless and not CustomRoles.Lovers and not CustomRoles.Infected and not CustomRoles.Contagious))
|
||||
RoleText = ColorString(GetRoleColor(subRole), GetString("Prefix." + subRole.ToString())) + RoleText;
|
||||
}
|
||||
}
|
||||
foreach (var subRole in targetSubRoles.Where(x => x is not CustomRoles.LastImpostor and not CustomRoles.Madmate and not CustomRoles.Charmed and not CustomRoles.Recruit and not CustomRoles.Admired and not CustomRoles.Soulless and not CustomRoles.Lovers and not CustomRoles.Infected and not CustomRoles.Contagious))
|
||||
RoleText = ColorString(GetRoleColor(subRole), (Options.AddBracketsToAddons.GetBool() ? "<#ffffff>(</color>" : string.Empty) + GetString("Prefix." + subRole.ToString())) + (Options.AddBracketsToAddons.GetBool() ? "<#ffffff>)</color>" : string.Empty) + RoleText;
|
||||
}
|
||||
|
||||
if (targetSubRoles.Contains(CustomRoles.Madmate))
|
||||
@@ -1882,12 +1858,13 @@ public static class Utils
|
||||
}
|
||||
|
||||
DevUser devUser = player.FriendCode.GetDevUser();
|
||||
if (!name.Contains('\r') && devUser.HasTag())
|
||||
bool isMod = ChatCommands.IsPlayerModerator(player.FriendCode);
|
||||
if (!name.Contains('\r') && (devUser.HasTag() || isMod))
|
||||
{
|
||||
string tag = devUser.GetTag();
|
||||
if (player.AmOwner || player.IsModClient())
|
||||
name = tag + name;
|
||||
else name = tag.Replace("\r\n", " - ") + name;
|
||||
name = tag + (isMod ? ("<size=1.4>" + GetString("ModeratorTag") + "\r\n</size>") : string.Empty) + name;
|
||||
else name = tag.Replace("\r\n", " - ") + (isMod ? ("<size=1.4>" + GetString("ModeratorTag") + " - </size>") : string.Empty) + name;
|
||||
}
|
||||
|
||||
if (player.AmOwner)
|
||||
@@ -1963,6 +1940,8 @@ public static class Utils
|
||||
PlayerControl[] seerList = SpecifySeer != null ? ([SpecifySeer]) : Main.AllPlayerControls;
|
||||
PlayerControl[] targetList = SpecifyTarget != null ? ([SpecifyTarget]) : Main.AllPlayerControls;
|
||||
|
||||
Logger.Info($" Seers: {string.Join(", ", seerList.ToList())}; Targets: {string.Join(", ", targetList.ToList())}", "NR");
|
||||
|
||||
//seer: Players who can see changes made here
|
||||
//target: Players subject to changes that seer can see
|
||||
foreach (PlayerControl seer in seerList)
|
||||
@@ -2215,7 +2194,6 @@ public static class Utils
|
||||
if (target.PlayerId == seer.PlayerId) continue;
|
||||
Logger.Info("NotifyRoles-Loop2-" + target.GetNameWithRole().RemoveHtmlTags() + ":START", "NotifyRoles");
|
||||
|
||||
|
||||
if ((IsActive(SystemTypes.MushroomMixupSabotage) || MushroomMixup) && target.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && Main.ResetCamPlayerList.Contains(seer.PlayerId))
|
||||
{
|
||||
seer.RpcSetNamePrivate("<size=0%>", true, force: NoCache);
|
||||
@@ -2491,6 +2469,7 @@ public static class Utils
|
||||
|
||||
TargetSuffix.Append(PlagueDoctor.GetLowerTextOthers(seer, target));
|
||||
TargetSuffix.Append(Stealth.GetSuffix(seer, target));
|
||||
TargetSuffix.Append(Bubble.GetEncasedPlayerSuffix(seer, target));
|
||||
|
||||
if (target.Is(CustomRoles.Librarian)) TargetSuffix.Append(Librarian.GetNameTextForSuffix(target.PlayerId));
|
||||
|
||||
@@ -2899,7 +2878,7 @@ public static class Utils
|
||||
neutralnum++;
|
||||
}
|
||||
}
|
||||
return (notify ? "<#777777>" : string.Empty) +
|
||||
return (notify ? "<#777777>" : string.Empty) +
|
||||
(impnum == 1 ? GetString("RemainingText.Prefix.SingleImp") : GetString("RemainingText.Prefix.PluralImp")) +
|
||||
(notify ? " " : "\n") +
|
||||
(notify ? "<#ffffff>" : "<b>") +
|
||||
|
||||
@@ -20,12 +20,12 @@ namespace TOHE;
|
||||
internal class ChatCommands
|
||||
{
|
||||
// Function to check if a player is a moderator
|
||||
private static bool IsPlayerModerator(string friendCode)
|
||||
public static bool IsPlayerModerator(string friendCode)
|
||||
{
|
||||
if (friendCode == "" || friendCode == string.Empty) return false;
|
||||
if (friendCode == "" || friendCode == string.Empty || !Options.ApplyModeratorList.GetBool()) return false;
|
||||
var friendCodesFilePath = @"./TOHE_DATA/Moderators.txt";
|
||||
var friendCodes = File.ReadAllLines(friendCodesFilePath);
|
||||
return friendCodes.Any(code => code.Contains(friendCode));
|
||||
return friendCodes.Any(code => code.Contains(friendCode, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public static List<string> ChatHistory = [];
|
||||
@@ -322,6 +322,7 @@ internal class ChatCommands
|
||||
Utils.SendMessage(args.Skip(1).Join(delimiter: " "), title: $"<color=#ff0000>{GetString("MessageFromTheHost")}</color>");
|
||||
break;
|
||||
|
||||
case "/ban":
|
||||
case "/kick":
|
||||
canceled = true;
|
||||
// Check if the kick command is enabled in the settings
|
||||
@@ -366,7 +367,7 @@ internal class ChatCommands
|
||||
}
|
||||
|
||||
// Kick the specified player
|
||||
AmongUsClient.Instance.KickPlayer(kickedPlayer.GetClientId(), true);
|
||||
AmongUsClient.Instance.KickPlayer(kickedPlayer.GetClientId(), args[0] == "/ban");
|
||||
string kickedPlayerName = kickedPlayer.GetRealName();
|
||||
string textToSend = $"{kickedPlayerName} {GetString("KickCommandKicked")}";
|
||||
if (GameStates.IsInGame)
|
||||
@@ -818,7 +819,8 @@ internal class ChatCommands
|
||||
if (isUp) return;
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
_ = sb.Append($"{devMark}<b>{roleName}</b>{Utils.GetRoleMode(rl)}{GetString($"{rl}InfoLong")}");
|
||||
var title = $"{devMark}<{Main.roleColors[rl]}>{roleName}</color> {Utils.GetRoleMode(rl)}";
|
||||
_ = sb.Append($"{GetString($"{rl}InfoLong")}");
|
||||
var settings = new StringBuilder();
|
||||
if (Options.CustomRoleSpawnChances.ContainsKey(rl))
|
||||
{
|
||||
@@ -829,7 +831,7 @@ internal class ChatCommands
|
||||
_ = sb.Clear().Append(txt);
|
||||
}
|
||||
Utils.SendMessage(text: "\n", sendTo: playerId, title: settings.ToString());
|
||||
Utils.SendMessage(text: sb.ToString(), sendTo: playerId);
|
||||
Utils.SendMessage(text: sb.ToString(), sendTo: playerId, title: title);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -999,6 +1001,7 @@ internal class ChatCommands
|
||||
|
||||
Utils.SendMessage(msgText, player.PlayerId);
|
||||
break;
|
||||
case "/ban":
|
||||
case "/kick":
|
||||
// Check if the kick command is enabled in the settings
|
||||
if (Options.ApplyModeratorList.GetValue() == 0)
|
||||
@@ -1042,7 +1045,7 @@ internal class ChatCommands
|
||||
}
|
||||
|
||||
// Kick the specified player
|
||||
AmongUsClient.Instance.KickPlayer(kickedPlayer.GetClientId(), true);
|
||||
AmongUsClient.Instance.KickPlayer(kickedPlayer.GetClientId(), args[0] == "/ban");
|
||||
string kickedPlayerName = kickedPlayer.GetRealName();
|
||||
string textToSend = $"{kickedPlayerName} {GetString("KickCommandKicked")}";
|
||||
if (GameStates.IsInGame)
|
||||
|
||||
@@ -96,6 +96,12 @@ class ExternalRpcPetPatch
|
||||
Pelican.IsEaten(pc.PlayerId))
|
||||
return;
|
||||
|
||||
if (Mastermind.ManipulatedPlayers.ContainsKey(pc.PlayerId))
|
||||
{
|
||||
var killTarget = SelectKillButtonTarget(pc);
|
||||
if (killTarget != null) Mastermind.ForceKillForManipulatedPlayer(pc, killTarget);
|
||||
}
|
||||
|
||||
if (pc.HasAbilityCD()) return;
|
||||
|
||||
PlayerControl[] AllAlivePlayers = Main.AllAlivePlayerControls;
|
||||
|
||||
@@ -214,15 +214,14 @@ class CheckMurderPatch
|
||||
//非自杀场景下才会触发
|
||||
switch (killer.GetCustomRole())
|
||||
{
|
||||
//==========内鬼阵营==========//
|
||||
case CustomRoles.BountyHunter: //必须在击杀发生前处理
|
||||
case CustomRoles.BountyHunter:
|
||||
BountyHunter.OnCheckMurder(killer, target);
|
||||
break;
|
||||
case CustomRoles.Reckless:
|
||||
Reckless.OnCheckMurder(killer);
|
||||
break;
|
||||
case CustomRoles.YinYanger:
|
||||
if (YinYanger.OnCheckMurder(killer, target)) return false;
|
||||
if (!YinYanger.OnCheckMurder(killer, target)) return false;
|
||||
break;
|
||||
case CustomRoles.Magician:
|
||||
Magician.OnCheckMurder(killer);
|
||||
@@ -1395,7 +1394,7 @@ class ShapeshiftPatch
|
||||
isSSneeded = false;
|
||||
break;
|
||||
case CustomRoles.Librarian:
|
||||
if (shapeshifting) isSSneeded = Librarian.OnShapeshift(shapeshifter);
|
||||
isSSneeded = Librarian.OnShapeshift(shapeshifter, shapeshifting);
|
||||
break;
|
||||
case CustomRoles.Warlock:
|
||||
if (Main.CursedPlayers[shapeshifter.PlayerId] != null)//呪われた人がいるか確認
|
||||
@@ -1947,6 +1946,7 @@ class ReportDeadBodyPatch
|
||||
{
|
||||
Stressed.OnReport(player);
|
||||
}
|
||||
Stressed.OnMeetingStart();
|
||||
|
||||
if (Options.InhibitorCDAfterMeetings.GetFloat() != Options.InhibitorCD.GetFloat() || Options.SaboteurCD.GetFloat() != Options.SaboteurCDAfterMeetings.GetFloat())
|
||||
{
|
||||
@@ -2137,9 +2137,6 @@ class FixedUpdatePatch
|
||||
case CustomRoles.BountyHunter:
|
||||
BountyHunter.FixedUpdate(player);
|
||||
break;
|
||||
case CustomRoles.Tornado when !lowLoad:
|
||||
Tornado.OnFixedUpdate(player);
|
||||
break;
|
||||
case CustomRoles.Glitch when !lowLoad:
|
||||
Glitch.UpdateHackCooldown(player);
|
||||
break;
|
||||
@@ -2173,9 +2170,6 @@ class FixedUpdatePatch
|
||||
case CustomRoles.Bubble when !lowLoad:
|
||||
Bubble.OnFixedUpdate();
|
||||
break;
|
||||
case CustomRoles.Sprayer when !lowLoad:
|
||||
Sprayer.OnFixedUpdate();
|
||||
break;
|
||||
case CustomRoles.Sentinel when !lowLoad:
|
||||
Sentinel.OnFixedUpdate();
|
||||
break;
|
||||
@@ -2218,6 +2212,15 @@ class FixedUpdatePatch
|
||||
PlagueBearer.playerIdList.Remove(playerId);
|
||||
}
|
||||
|
||||
if (GameStates.IsInTask && player != null && player.IsAlive())
|
||||
{
|
||||
Druid.OnCheckPlayerPosition(player);
|
||||
Sentinel.OnCheckPlayerPosition(player);
|
||||
Tornado.OnCheckPlayerPosition(player);
|
||||
BallLightning.OnCheckPlayerPosition(player);
|
||||
Sprayer.OnCheckPlayerPosition(player);
|
||||
}
|
||||
|
||||
if (!lowLoad && Main.PlayerStates.TryGetValue(playerId, out var playerState) && GameStates.IsInTask)
|
||||
{
|
||||
if (playerState.SubRoles.Contains(CustomRoles.Damocles))
|
||||
@@ -2532,10 +2535,6 @@ class FixedUpdatePatch
|
||||
Pelican.OnFixedUpdate();
|
||||
break;
|
||||
|
||||
case CustomRoles.BallLightning:
|
||||
BallLightning.OnFixedUpdate();
|
||||
break;
|
||||
|
||||
case CustomRoles.Sapper:
|
||||
Sapper.OnFixedUpdate(player);
|
||||
break;
|
||||
@@ -2936,6 +2935,7 @@ class FixedUpdatePatch
|
||||
if (PlagueDoctor.IsEnable) Suffix.Append(PlagueDoctor.GetLowerTextOthers(seer, target));
|
||||
if (Stealth.IsEnable) Suffix.Append(Stealth.GetSuffix(seer, target));
|
||||
if (Tracker.IsEnable) Suffix.Append(Tracker.GetTrackerArrow(seer, target));
|
||||
if (Bubble.IsEnable) Suffix.Append(Bubble.GetEncasedPlayerSuffix(seer, target));
|
||||
|
||||
if (Deathpact.IsEnable)
|
||||
{
|
||||
@@ -3034,6 +3034,10 @@ class FixedUpdatePatch
|
||||
target.cosmetics.nameText.text = changeTo;
|
||||
|
||||
float offset = 0.2f;
|
||||
if (NameNotifyManager.Notice.TryGetValue(target.PlayerId, out var notify) && notify.TEXT.Contains('\n'))
|
||||
{
|
||||
offset += 0.1f;
|
||||
}
|
||||
if (Suffix.ToString() != string.Empty)
|
||||
{
|
||||
// If the name is on two lines, the job title text needs to be moved up.
|
||||
@@ -3041,6 +3045,10 @@ class FixedUpdatePatch
|
||||
offset += 0.15f;
|
||||
target.cosmetics.nameText.text += "\r\n" + Suffix.ToString();
|
||||
}
|
||||
if (!seer.IsAlive())
|
||||
{
|
||||
offset += 0.1f;
|
||||
}
|
||||
if (isProgressTextLong)
|
||||
{
|
||||
offset += 0.3f;
|
||||
|
||||
@@ -190,7 +190,6 @@ class RepairSystemPatch
|
||||
if (player.Is(CustomRoles.Damocles) && Damocles.countRepairSabotage) Damocles.OnRepairSabotage();
|
||||
if (player.Is(CustomRoles.Stressed) && Stressed.countRepairSabotage) Stressed.OnRepairSabotage(player);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -103,10 +103,10 @@ namespace TOHE.Roles.AddOns.Crewmate
|
||||
|
||||
public static void Update(PlayerControl pc)
|
||||
{
|
||||
if (!LastUpdates.TryGetValue(pc.PlayerId, out var x) || x >= GetTimeStamp() || !Timers.ContainsKey(pc.PlayerId) || !IsEnable || !GameStates.IsInTask || !pc.IsAlive() || !pc.Is(CustomRoles.Stressed)) return;
|
||||
if (!LastUpdates.TryGetValue(pc.PlayerId, out var x) || x >= GetTimeStamp() || !Timers.ContainsKey(pc.PlayerId) || !IsEnable || !GameStates.IsInTask || !pc.Is(CustomRoles.Stressed)) return;
|
||||
LastUpdates[pc.PlayerId] = GetTimeStamp();
|
||||
|
||||
if (pc.GetPlayerTaskState().IsTaskFinished)
|
||||
if (pc.GetPlayerTaskState().IsTaskFinished || !pc.IsAlive())
|
||||
{
|
||||
Main.PlayerStates[pc.PlayerId].RemoveSubRole(CustomRoles.Stressed);
|
||||
Timers.Remove(pc.PlayerId);
|
||||
@@ -121,7 +121,7 @@ namespace TOHE.Roles.AddOns.Crewmate
|
||||
pc.Suicide();
|
||||
}
|
||||
|
||||
if (pc.IsModClient() && pc.PlayerId != 0) SendRPC(pc.PlayerId, Timers[pc.PlayerId], LastUpdates[pc.PlayerId]);
|
||||
if (pc.IsNonHostModClient()) SendRPC(pc.PlayerId, Timers[pc.PlayerId], LastUpdates[pc.PlayerId]);
|
||||
if (!pc.IsModClient()) NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,6 @@ namespace TOHE.Roles.AddOns.Crewmate
|
||||
public static void AfterMeetingTasks()
|
||||
{
|
||||
if (!IsEnable) return;
|
||||
AdjustTime(TimeAfterMeeting + 9);
|
||||
countRepairSabotage = true;
|
||||
}
|
||||
|
||||
@@ -188,6 +187,12 @@ namespace TOHE.Roles.AddOns.Crewmate
|
||||
Timers[pc.PlayerId] += TimeAfterReport;
|
||||
}
|
||||
|
||||
public static void OnMeetingStart()
|
||||
{
|
||||
if (!IsEnable) return;
|
||||
AdjustTime(TimeAfterMeeting + 9);
|
||||
}
|
||||
|
||||
public static string GetProgressText(byte playerId) => Timers.TryGetValue(playerId, out var x) ? string.Format(GetString("DamoclesTimeLeft"), x) : string.Empty;
|
||||
|
||||
private static void AdjustTime(int change)
|
||||
|
||||
+30
-37
@@ -169,52 +169,45 @@ namespace TOHE.Roles.Crewmate
|
||||
SendRPCSyncAbilityUse(pc.PlayerId);
|
||||
}
|
||||
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask || Triggers.Count <= 0 || playerIdList.Contains(pc.PlayerId)) return;
|
||||
|
||||
foreach (var triggers in Triggers.ToArray()) // Check for all Druids' traps - Most likely just 1 loop
|
||||
{
|
||||
foreach (var trigger in triggers.Value.Where(trigger => Vector2.Distance(trigger.Key, pc.Pos()) <= 1.5f)) // Check for all traps of the current Druid - Most likely 0-3 loops
|
||||
{
|
||||
GetPlayerById(triggers.Key).Notify(string.Format(GetString("DruidTriggerTriggered"), GetFormattedRoomName(trigger.Value), GetFormattedVectorText(trigger.Key)));
|
||||
Triggers[triggers.Key].Remove(trigger.Key);
|
||||
SendRPCRemoveTrigger(triggers.Key, trigger.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnFixedUpdate()
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask || Triggers.Count == 0 && TriggerDelays.Count == 0) return;
|
||||
if (!IsEnable || !GameStates.IsInTask || TriggerDelays.Count <= 0) return;
|
||||
|
||||
long now = GetTimeStamp();
|
||||
|
||||
if (TriggerDelays.Count > 0)
|
||||
foreach (var x in TriggerDelays.ToArray())
|
||||
{
|
||||
foreach (var x in TriggerDelays.ToArray())
|
||||
var id = x.Key;
|
||||
var pc = GetPlayerById(id);
|
||||
if (pc == null) continue;
|
||||
|
||||
if (x.Value + TriggerPlaceDelay.GetInt() < now)
|
||||
{
|
||||
var id = x.Key;
|
||||
var pc = GetPlayerById(id);
|
||||
if (pc == null) continue;
|
||||
|
||||
if (x.Value + TriggerPlaceDelay.GetInt() < now)
|
||||
{
|
||||
TriggerDelays.Remove(id);
|
||||
var (LOCATION, ROOM_NAME) = pc.GetPositionInfo();
|
||||
if (!Triggers.ContainsKey(id)) Triggers.Add(id, []);
|
||||
Triggers[id].TryAdd(LOCATION, ROOM_NAME);
|
||||
SendRPCAddTrigger(id, LOCATION, ROOM_NAME);
|
||||
continue;
|
||||
}
|
||||
|
||||
var timeLeft = TriggerPlaceDelay.GetInt() - (now - x.Value);
|
||||
if (lastUpdate < now) pc.Notify(string.Format(GetString("DruidTimeLeft"), timeLeft, 2f));
|
||||
TriggerDelays.Remove(id);
|
||||
var (LOCATION, ROOM_NAME) = pc.GetPositionInfo();
|
||||
if (!Triggers.ContainsKey(id)) Triggers.Add(id, []);
|
||||
Triggers[id].TryAdd(LOCATION, ROOM_NAME);
|
||||
SendRPCAddTrigger(id, LOCATION, ROOM_NAME);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Triggers.Count > 0)
|
||||
{
|
||||
foreach (var pc in Main.AllAlivePlayerControls.Where(pc => !playerIdList.Contains(pc.PlayerId)).ToArray()) // Check for all alive players except Druids - Worst case scenario it's 14 loops
|
||||
{
|
||||
foreach (var triggers in Triggers.ToArray()) // Check for all Druids' traps - Most likely just 1 loop
|
||||
{
|
||||
foreach (var trigger in triggers.Value.ToArray()) // Check for all traps of the current Druid - Most likely 0-3 loops
|
||||
{
|
||||
if (Vector2.Distance(trigger.Key, pc.Pos()) <= 1.5f)
|
||||
{
|
||||
GetPlayerById(triggers.Key).Notify(string.Format(GetString("DruidTriggerTriggered"), GetFormattedRoomName(trigger.Value), GetFormattedVectorText(trigger.Key)));
|
||||
Triggers[triggers.Key].Remove(trigger.Key);
|
||||
SendRPCRemoveTrigger(triggers.Key, trigger.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var timeLeft = TriggerPlaceDelay.GetInt() - (now - x.Value);
|
||||
if (lastUpdate < now) pc.Notify(string.Format(GetString("DruidTimeLeft"), timeLeft, 2f));
|
||||
}
|
||||
|
||||
lastUpdate = now;
|
||||
|
||||
@@ -24,6 +24,7 @@ public static class Medic
|
||||
public static OptionItem GuesserIgnoreShield;
|
||||
private static OptionItem AmountOfShields;
|
||||
public static OptionItem UsePet;
|
||||
public static OptionItem CD;
|
||||
|
||||
public static readonly string[] MedicWhoCanSeeProtectName =
|
||||
[
|
||||
@@ -67,6 +68,9 @@ public static class Medic
|
||||
AmountOfShields = IntegerOptionItem.Create(Id + 34, "MedicAmountOfShields", new(1, 14, 1), 1, TabGroup.CrewmateRoles, false)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Medic]);
|
||||
UsePet = Options.CreatePetUseSetting(Id + 36, CustomRoles.Medic);
|
||||
CD = FloatOptionItem.Create(Id + 38, "AbilityCooldown", new(0f, 180f, 2.5f), 7.5f, TabGroup.CrewmateRoles, false)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Medic])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
}
|
||||
public static void Init()
|
||||
{
|
||||
@@ -124,7 +128,7 @@ public static class Medic
|
||||
public static bool CanUseKillButton(byte playerId)
|
||||
=> !Main.PlayerStates[playerId].IsDead
|
||||
&& (ProtectLimit.TryGetValue(playerId, out var x) ? x : 1) >= 1;
|
||||
public static void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = CanUseKillButton(id) ? 5f : 300f;
|
||||
public static void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = CanUseKillButton(id) ? CD.GetFloat() : 300f;
|
||||
public static string GetSkillLimit(byte playerId) => Utils.ColorString(CanUseKillButton(playerId) ? Utils.GetRoleColor(CustomRoles.Medic).ShadeColor(0.25f) : Color.gray, ProtectLimit.TryGetValue(playerId, out var protectLimit) ? $"({protectLimit})" : "Invalid");
|
||||
public static bool InProtect(byte id) => ProtectList.Contains(id) && Main.PlayerStates.TryGetValue(id, out var ps) && !ps.IsDead;
|
||||
public static void OnCheckMurderFormedicaler(PlayerControl killer, PlayerControl target)
|
||||
|
||||
+20
-12
@@ -49,22 +49,22 @@ namespace TOHE.Roles.Crewmate
|
||||
FinishPatrolling();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
var killers = NearbyKillers;
|
||||
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
bool nowInRange = killers.Any(x => x.PlayerId == pc.PlayerId);
|
||||
bool wasInRange = LastNearbyKillers.Contains(pc.PlayerId);
|
||||
bool nowInRange = killers.Any(x => x.PlayerId == pc.PlayerId);
|
||||
bool wasInRange = LastNearbyKillers.Contains(pc.PlayerId);
|
||||
|
||||
if (wasInRange && !nowInRange)
|
||||
{
|
||||
pc.Notify(GetString("KillerEscapedFromSentinel"));
|
||||
}
|
||||
if (nowInRange)
|
||||
{
|
||||
pc.Notify(string.Format(GetString("KillerNotifyPatrol"), PatrolDuration - (now - PatrolStartTimeStamp)));
|
||||
}
|
||||
if (wasInRange && !nowInRange)
|
||||
{
|
||||
pc.Notify(GetString("KillerEscapedFromSentinel"));
|
||||
}
|
||||
if (nowInRange)
|
||||
{
|
||||
pc.Notify(string.Format(GetString("KillerNotifyPatrol"), PatrolDuration - (GetTimeStamp() - PatrolStartTimeStamp)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,5 +171,13 @@ namespace TOHE.Roles.Crewmate
|
||||
state.Update();
|
||||
}
|
||||
}
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable) return;
|
||||
foreach (PatrollingState state in PatrolStates)
|
||||
{
|
||||
state.OnCheckPlayerPosition(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ namespace TOHE.Roles.Crewmate
|
||||
Tornados.Add(info, now);
|
||||
SendRPCAddTornado(info.LOCATION, info.ROOM_NAME, now);
|
||||
}
|
||||
public static void OnFixedUpdate(PlayerControl tornadoPc)
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask || Tornados.Count == 0) return;
|
||||
|
||||
@@ -114,10 +114,8 @@ namespace TOHE.Roles.Crewmate
|
||||
var tornadoRange = TornadoRange.GetFloat();
|
||||
var tornadoDuration = TornadoDuration.GetInt();
|
||||
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
foreach (var tornadoPc in playerIdList.Select(x => GetPlayerById(x)).Where(x => x.PlayerId != pc.PlayerId).ToArray())
|
||||
{
|
||||
if (playerIdList.Contains(pc.PlayerId)) continue;
|
||||
|
||||
foreach (var tornado in Tornados)
|
||||
{
|
||||
if (Vector2.Distance(tornado.Key.LOCATION, pc.Pos()) <= tornadoRange)
|
||||
@@ -139,11 +137,11 @@ namespace TOHE.Roles.Crewmate
|
||||
SendRPCRemoveTornado(tornado.Key.LOCATION, tornado.Key.ROOM_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tornadoPc == null || LastNotify >= now || !tornadoPc.Is(CustomRoles.Tornado) || tornadoPc.HasAbilityCD()) return;
|
||||
NotifyRoles(SpecifySeer: tornadoPc, SpecifyTarget: tornadoPc);
|
||||
LastNotify = now;
|
||||
if (tornadoPc == null || LastNotify >= now || !tornadoPc.Is(CustomRoles.Tornado) || tornadoPc.HasAbilityCD()) return;
|
||||
NotifyRoles(SpecifySeer: tornadoPc, SpecifyTarget: tornadoPc);
|
||||
LastNotify = now;
|
||||
}
|
||||
}
|
||||
public static string GetSuffixText(byte playerId, bool isHUD = false) => string.Join(isHUD ? "\n" : ", ", Tornados.Select(x => $"Tornado {GetFormattedRoomName(x.Key.ROOM_NAME)} {GetFormattedVectorText(x.Key.LOCATION)} ({(int)(TornadoDuration.GetInt() - (GetTimeStamp() - x.Value) + 1)}s)"));
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public static class BallLightning
|
||||
RealKiller.TryAdd(killer.PlayerId, target);
|
||||
StartConvertCountDown(target, killer);
|
||||
}
|
||||
public static void OnFixedUpdate()
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask) return;
|
||||
List<byte> deList = [];
|
||||
@@ -115,7 +115,7 @@ public static class BallLightning
|
||||
deList.Add(gs.PlayerId);
|
||||
continue;
|
||||
}
|
||||
foreach (var pc in Main.AllAlivePlayerControls.Where(x => x.PlayerId != gs.PlayerId && x.IsAlive() && !x.Is(CustomRoles.BallLightning) && !IsGhost(x) && !Pelican.IsEaten(x.PlayerId)).ToArray())
|
||||
if (pc.PlayerId != gs.PlayerId && pc.IsAlive() && !pc.Is(CustomRoles.BallLightning) && !IsGhost(pc) && !Pelican.IsEaten(pc.PlayerId))
|
||||
{
|
||||
var pos = gs.transform.position;
|
||||
var dis = Vector2.Distance(pos, pc.Pos());
|
||||
|
||||
+19
-16
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using MS.Internal.Xml.XPath;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TOHE.Roles.Crewmate;
|
||||
@@ -33,8 +34,8 @@ namespace TOHE.Roles.Impostor
|
||||
public static OptionItem IgnorePestilence;
|
||||
public static OptionItem PositiveEffectChance;
|
||||
|
||||
public static byte EffectID = byte.MaxValue;
|
||||
public static bool isPositiveEffect;
|
||||
public static Dictionary<byte, byte> EffectID = [];
|
||||
public static Dictionary<byte, bool> isPositiveEffect = [];
|
||||
|
||||
public static Dictionary<byte, long> waitingDelayedKills = [];
|
||||
public static Dictionary<byte, long> isShielded = [];
|
||||
@@ -43,7 +44,7 @@ namespace TOHE.Roles.Impostor
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
SetupSingleRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Gambler, 1);
|
||||
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Gambler);
|
||||
KillCooldown = FloatOptionItem.Create(Id + 10, "KillCooldown", new(0f, 60f, 2.5f), 25f, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Gambler])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
KillDelay = IntegerOptionItem.Create(Id + 11, "GamblerKillDelay", new(0, 10, 1), 3, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Gambler])
|
||||
@@ -74,17 +75,18 @@ namespace TOHE.Roles.Impostor
|
||||
IgnoreVeteranAlert = BooleanOptionItem.Create(Id + 25, "GamblerIgnoreVeteranAlert", false, TabGroup.ImpostorRoles, false).SetParent(WhatToIgnore);
|
||||
IgnorePestilence = BooleanOptionItem.Create(Id + 26, "GamblerIgnorePestilence", false, TabGroup.ImpostorRoles, false).SetParent(WhatToIgnore);
|
||||
PositiveEffectChance = IntegerOptionItem.Create(Id + 27, "GamblerPositiveEffectChance", new(0, 100, 5), 70, TabGroup.ImpostorRoles, false)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Gambler])
|
||||
.SetValueFormat(OptionFormat.Percent);
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Gambler])
|
||||
.SetValueFormat(OptionFormat.Percent);
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
playerIdList = [];
|
||||
EffectID = byte.MaxValue;
|
||||
EffectID = [];
|
||||
waitingDelayedKills = [];
|
||||
isSpeedChange = [];
|
||||
isVisionChange = [];
|
||||
isPositiveEffect = [];
|
||||
}
|
||||
|
||||
public static void Add(byte playerId)
|
||||
@@ -99,17 +101,17 @@ namespace TOHE.Roles.Impostor
|
||||
if (killer == null) return false;
|
||||
if (target == null) return false;
|
||||
|
||||
if (EffectID != byte.MaxValue)
|
||||
if (EffectID.TryGetValue(killer.PlayerId, out var id) && id != byte.MaxValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var rd = IRandom.Instance;
|
||||
isPositiveEffect = rd.Next(1, 101) <= PositiveEffectChance.GetInt();
|
||||
if (isPositiveEffect)
|
||||
isPositiveEffect[killer.PlayerId] = rd.Next(1, 101) <= PositiveEffectChance.GetInt();
|
||||
if (isPositiveEffect[killer.PlayerId])
|
||||
{
|
||||
EffectID = (byte)rd.Next(1, 8);
|
||||
switch (EffectID)
|
||||
EffectID[killer.PlayerId] = (byte)rd.Next(1, 8);
|
||||
switch (EffectID[killer.PlayerId])
|
||||
{
|
||||
case 1: // Delayed kill
|
||||
killer.Notify(string.Format(GetString("GamblerGet.DelayedKill"), KillDelay.GetInt()));
|
||||
@@ -176,8 +178,8 @@ namespace TOHE.Roles.Impostor
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectID = (byte)rd.Next(1, 5);
|
||||
switch (EffectID)
|
||||
EffectID[killer.PlayerId] = (byte)rd.Next(1, 5);
|
||||
switch (EffectID[killer.PlayerId])
|
||||
{
|
||||
case 1: // BSR
|
||||
var delay = Math.Max(0.15f, BSRDelay.GetFloat());
|
||||
@@ -211,7 +213,7 @@ namespace TOHE.Roles.Impostor
|
||||
}
|
||||
}
|
||||
|
||||
EffectID = byte.MaxValue;
|
||||
EffectID[killer.PlayerId] = byte.MaxValue;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -260,7 +262,8 @@ namespace TOHE.Roles.Impostor
|
||||
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
EffectID = byte.MaxValue;
|
||||
EffectID.Clear();
|
||||
isPositiveEffect.Clear();
|
||||
foreach (var playerId in waitingDelayedKills.Keys.ToArray())
|
||||
{
|
||||
var pc = GetPlayerById(playerId);
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace TOHE.Roles.Impostor
|
||||
|
||||
if (librarian == null) return true;
|
||||
|
||||
reporter.SetRealKiller(librarian);
|
||||
if (librarian.RpcCheckAndMurder(reporter))
|
||||
{
|
||||
sssh.Add(librarian.PlayerId);
|
||||
@@ -143,7 +144,7 @@ namespace TOHE.Roles.Impostor
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool OnShapeshift(PlayerControl pc)
|
||||
public static bool OnShapeshift(PlayerControl pc, bool shapeshifting)
|
||||
{
|
||||
if (!IsEnable) return false;
|
||||
if (pc == null) return false;
|
||||
@@ -153,7 +154,7 @@ namespace TOHE.Roles.Impostor
|
||||
isInSilencingMode[id] = (!isInSilencingMode[id].SILENCING, GetTimeStamp());
|
||||
SendRPC(id, isInSilencingMode[id].SILENCING);
|
||||
|
||||
return ShowSSAnimation.GetBool();
|
||||
return !shapeshifting || ShowSSAnimation.GetBool();
|
||||
}
|
||||
|
||||
public static void OnFixedUpdate()
|
||||
@@ -179,13 +180,9 @@ namespace TOHE.Roles.Impostor
|
||||
sssh.Clear();
|
||||
}
|
||||
|
||||
public static string GetNameTextForSuffix(byte playerId)
|
||||
{
|
||||
if (!IsEnable) return string.Empty;
|
||||
return isInSilencingMode.TryGetValue(playerId, out var x) && x.SILENCING && sssh.Contains(playerId) && GameStates.IsInTask
|
||||
public static string GetNameTextForSuffix(byte playerId) => IsEnable && isInSilencingMode.TryGetValue(playerId, out var x) && x.SILENCING && sssh.Contains(playerId) && GameStates.IsInTask
|
||||
? GetString("LibrarianNameText")
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
public static string GetSelfSuffixAndHUDText(byte playerId)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static TOHE.Options;
|
||||
using static TOHE.Translator;
|
||||
using static TOHE.Utils;
|
||||
@@ -59,7 +60,7 @@ namespace TOHE.Roles.Impostor
|
||||
return killer.CheckDoubleTrigger(target, () =>
|
||||
{
|
||||
killer.SetKillCooldown(time: ManipulateCD);
|
||||
if (target.HasKillButton() || target.GetPlayerTaskState().hasTasks)
|
||||
if (target.HasKillButton() || target.GetPlayerTaskState().hasTasks || UsePets.GetBool())
|
||||
{
|
||||
ManipulateDelays.TryAdd(target.PlayerId, GetTimeStamp());
|
||||
NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
@@ -85,13 +86,13 @@ namespace TOHE.Roles.Impostor
|
||||
ManipulateDelays.Remove(x.Key);
|
||||
ManipulatedPlayers.TryAdd(x.Key, GetTimeStamp());
|
||||
|
||||
if (!pc.GetPlayerTaskState().hasTasks)
|
||||
if (!pc.GetPlayerTaskState().hasTasks || UsePets.GetBool())
|
||||
{
|
||||
TempKCDs.TryAdd(pc.PlayerId, Main.KillTimers[pc.PlayerId]);
|
||||
pc.SetKillCooldown(time: 1f);
|
||||
}
|
||||
|
||||
NotifyRoles(SpecifySeer: Mastermind_, SpecifyTarget: Mastermind_);
|
||||
NotifyRoles(SpecifySeer: Mastermind_, SpecifyTarget: pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +116,7 @@ namespace TOHE.Roles.Impostor
|
||||
|
||||
var time = TimeLimit.GetInt() - (GetTimeStamp() - x.Value);
|
||||
|
||||
player.Notify(string.Format(GetString(player.GetPlayerTaskState().hasTasks ? "ManipulateTaskNotify" : "ManipulateNotify"), time), 1.1f);
|
||||
player.Notify(string.Format(GetString(UsePets.GetBool() ? "ManipulatePetNotify" : player.GetPlayerTaskState().hasTasks ? "ManipulateTaskNotify" : "ManipulateNotify"), time), 1.1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +158,9 @@ namespace TOHE.Roles.Impostor
|
||||
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
killer.SetKillCooldown(time: TempKCDs.TryGetValue(killer.PlayerId, out var cd) ? cd : 0f + Main.AllPlayerKillCooldown[killer.PlayerId]);
|
||||
var kcd = TempKCDs.TryGetValue(killer.PlayerId, out var cd) ? cd : Main.AllPlayerKillCooldown.TryGetValue(killer.PlayerId, out var baseKCD) ? baseKCD : DefaultKillCooldown;
|
||||
killer.SetKillCooldown(time: kcd);
|
||||
if (killer.GetCustomRole().PetActivatedAbility()) killer.AddAbilityCD((int)Math.Round(kcd));
|
||||
TempKCDs.Remove(killer.PlayerId);
|
||||
}, 0.1f, "Set KCD for Manipulated Kill");
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace TOHE.Roles.Impostor
|
||||
public static bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!IsEnable) return false;
|
||||
if (killer == null || target == null) return false;
|
||||
if (killer == null || target == null || !killer.Is(CustomRoles.YinYanger)) return false;
|
||||
|
||||
if (YinYangedPlayers.Count == 2)
|
||||
{
|
||||
|
||||
+37
-12
@@ -1,7 +1,9 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using static TOHE.Options;
|
||||
using static TOHE.Utils;
|
||||
|
||||
@@ -92,29 +94,52 @@ namespace TOHE.Roles.Neutral
|
||||
if (!IsEnable || !GameStates.IsInTask || EncasedPlayers.Count == 0) return;
|
||||
|
||||
long now = GetTimeStamp();
|
||||
foreach (var id in EncasedPlayers.Where(item => item.Value + ExplodeDelay.GetInt() < now).Select(item => item.Key).ToArray())
|
||||
foreach (var kvp in EncasedPlayers)
|
||||
{
|
||||
var players = GetPlayersInRadius(ExplosionRadius.GetFloat(), GetPlayerById(id).Pos());
|
||||
foreach (var pc in players)
|
||||
var id = kvp.Key;
|
||||
var encasedPc = GetPlayerById(id);
|
||||
|
||||
if (kvp.Value + ExplodeDelay.GetInt() < now)
|
||||
{
|
||||
if (pc == null) continue;
|
||||
if (pc.PlayerId == BubbleId)
|
||||
if (!encasedPc.IsAlive())
|
||||
{
|
||||
if (BubbleDiesIfInRange.GetBool()) _ = new LateTask(() => { if (GameStates.IsInTask) pc.Suicide(); }, 0.2f, log: false);
|
||||
else continue;
|
||||
EncasedPlayers.Remove(id);
|
||||
SendRPC(id, remove: true);
|
||||
continue;
|
||||
}
|
||||
pc.Suicide(realKiller: Bubble_);
|
||||
var players = GetPlayersInRadius(ExplosionRadius.GetFloat(), encasedPc.Pos());
|
||||
foreach (var pc in players)
|
||||
{
|
||||
if (pc == null) continue;
|
||||
if (pc.PlayerId == BubbleId)
|
||||
{
|
||||
if (BubbleDiesIfInRange.GetBool()) _ = new LateTask(() => { if (GameStates.IsInTask) pc.Suicide(PlayerState.DeathReason.Bombed); }, 0.5f, log: false);
|
||||
continue;
|
||||
}
|
||||
pc.Suicide(PlayerState.DeathReason.Bombed, Bubble_);
|
||||
}
|
||||
EncasedPlayers.Remove(id);
|
||||
SendRPC(id, remove: true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kvp.Value + NotifyDelay.GetInt() < now)
|
||||
{
|
||||
Main.AllAlivePlayerControls.Where(x => Vector2.Distance(x.Pos(), encasedPc.Pos()) < 5f).Do(x => NotifyRoles(SpecifySeer: x, SpecifyTarget: encasedPc));
|
||||
}
|
||||
EncasedPlayers.Remove(id);
|
||||
SendRPC(id, remove: true);
|
||||
}
|
||||
}
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
if (IsEnable) return;
|
||||
foreach (var pc in EncasedPlayers.Keys.Select(x => GetPlayerById(x)).Where(x => x != null && x.IsAlive())) pc.Suicide(realKiller: Bubble_);
|
||||
if (!IsEnable) return;
|
||||
foreach (var pc in EncasedPlayers.Keys.Select(x => GetPlayerById(x)).Where(x => x != null && x.IsAlive())) pc.Suicide(PlayerState.DeathReason.Bombed, Bubble_);
|
||||
EncasedPlayers.Clear();
|
||||
SendRPC(clear: true);
|
||||
}
|
||||
public static string GetEncasedPlayerSuffix(PlayerControl seer, PlayerControl target)
|
||||
{
|
||||
if (!IsEnable || target == null || !EncasedPlayers.TryGetValue(target.PlayerId, out var ts) || (ts + NotifyDelay.GetInt() >= GetTimeStamp())) return string.Empty;
|
||||
return ColorString(GetRoleColor(CustomRoles.Bubble), $"{ExplodeDelay.GetInt() - (GetTimeStamp() - ts) + 1}s");
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-30
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using static TOHE.Options;
|
||||
using static TOHE.Translator;
|
||||
using static TOHE.Utils;
|
||||
|
||||
namespace TOHE.Roles.Neutral
|
||||
@@ -93,56 +94,56 @@ namespace TOHE.Roles.Neutral
|
||||
public static void ReceiveRPC(MessageReader reader) => UseLimit = reader.ReadInt32();
|
||||
public static void PlaceTrap()
|
||||
{
|
||||
if (!IsEnable || Sprayer_.HasAbilityCD() || UseLimit <= 0) return;
|
||||
if (!IsEnable || UseLimit <= 0 || Sprayer_.HasAbilityCD()) return;
|
||||
|
||||
Traps.Add(Sprayer_.Pos());
|
||||
UseLimit--;
|
||||
SendRPC();
|
||||
|
||||
if (UseLimit > 0) Sprayer_.AddAbilityCD(CD.GetInt());
|
||||
|
||||
Sprayer_.Notify(GetString("SprayerNotify"));
|
||||
}
|
||||
public static void OnFixedUpdate()
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask || Traps.Count == 0) return;
|
||||
|
||||
long now = GetTimeStamp();
|
||||
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
if (pc.PlayerId == SprayerId) return;
|
||||
|
||||
if (!LastUpdate.ContainsKey(pc.PlayerId)) LastUpdate.Add(pc.PlayerId, now);
|
||||
if (LastUpdate[pc.PlayerId] + 3 > now) return;
|
||||
LastUpdate[pc.PlayerId] = now;
|
||||
|
||||
foreach (var trap in Traps.ToArray())
|
||||
{
|
||||
if (pc.PlayerId == SprayerId) continue;
|
||||
|
||||
if (!LastUpdate.ContainsKey(pc.PlayerId)) LastUpdate.Add(pc.PlayerId, now);
|
||||
if (LastUpdate[pc.PlayerId] + 3 > now) continue;
|
||||
LastUpdate[pc.PlayerId] = now;
|
||||
|
||||
foreach (var trap in Traps.ToArray())
|
||||
if (Vector2.Distance(pc.Pos(), trap) <= 2f)
|
||||
{
|
||||
if (Vector2.Distance(pc.Pos(), trap) <= 2f)
|
||||
byte playerId = pc.PlayerId;
|
||||
var tempSpeed = Main.AllPlayerSpeed[playerId];
|
||||
Main.AllPlayerSpeed[playerId] = LoweredSpeed.GetFloat();
|
||||
LowerVisionList.Add(playerId);
|
||||
TrappedCount[playerId]++;
|
||||
if (TrappedCount[playerId] > MaxTrappedTimes.GetInt())
|
||||
{
|
||||
byte playerId = pc.PlayerId;
|
||||
var tempSpeed = Main.AllPlayerSpeed[playerId];
|
||||
Main.AllPlayerSpeed[playerId] = LoweredSpeed.GetFloat();
|
||||
LowerVisionList.Add(playerId);
|
||||
TrappedCount[playerId]++;
|
||||
if (TrappedCount[playerId] > MaxTrappedTimes.GetInt())
|
||||
pc.Suicide(realKiller: Sprayer_);
|
||||
TrappedCount.Remove(playerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.MarkDirtySettings();
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
pc.Suicide(realKiller: Sprayer_);
|
||||
TrappedCount.Remove(playerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.MarkDirtySettings();
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Main.AllPlayerSpeed[playerId] = tempSpeed;
|
||||
LowerVisionList.Remove(playerId);
|
||||
if (GameStates.IsInTask) pc.MarkDirtySettings();
|
||||
}, EffectDuration.GetFloat(), "Sprayer Revert Effects");
|
||||
}
|
||||
Main.AllPlayerSpeed[playerId] = tempSpeed;
|
||||
LowerVisionList.Remove(playerId);
|
||||
if (GameStates.IsInTask) pc.MarkDirtySettings();
|
||||
}, EffectDuration.GetFloat(), "Sprayer Revert Effects");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
Traps.Clear();
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2023.11.28" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.682" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="Samboy063.Cpp2IL.Core" Version="2022.1.0-development.898" />
|
||||
<PackageReference Include="Samboy063.Cpp2IL.Core" Version="2022.1.0-pre-release.13" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -33,8 +33,8 @@ public class Main : BasePlugin
|
||||
public static ConfigEntry<string> DebugKeyInput { get; private set; }
|
||||
public static readonly string MainMenuText = " ";
|
||||
public const string PluginGuid = "com.gurge44.toheplus";
|
||||
public const string PluginVersion = "2.2.2";
|
||||
public const string PluginDisplayVersion = "2.2.2";
|
||||
public const string PluginVersion = "2.2.3";
|
||||
public const string PluginDisplayVersion = "2.2.3";
|
||||
public static readonly string SupportedAUVersion = "2023.10.24";
|
||||
public const int PluginCreate = 3;
|
||||
public const bool Canary = false;
|
||||
@@ -653,84 +653,80 @@ public enum CustomRoles
|
||||
|
||||
//Impostors
|
||||
|
||||
BountyHunter,
|
||||
FireWorks,
|
||||
Mafia,
|
||||
Godfather,
|
||||
SerialKiller,
|
||||
//ShapeMaster,
|
||||
Wildling,
|
||||
EvilGuesser,
|
||||
Minimalism,
|
||||
Zombie,
|
||||
Sniper,
|
||||
Vampire,
|
||||
Witch,
|
||||
Warlock,
|
||||
Assassin,
|
||||
Undertaker,
|
||||
Vindicator,
|
||||
Hacker,
|
||||
Miner,
|
||||
Escapee,
|
||||
Inhibitor,
|
||||
Librarian,
|
||||
Chronomancer,
|
||||
Nullifier,
|
||||
Stealth,
|
||||
Penguin,
|
||||
Sapper,
|
||||
Mastermind,
|
||||
Hitman,
|
||||
RiftMaker,
|
||||
Gambler,
|
||||
Puppeteer,
|
||||
TimeThief,
|
||||
EvilTracker,
|
||||
Hacker, // Anonymous
|
||||
AntiAdminer,
|
||||
Consort,
|
||||
Sans,
|
||||
Bomber,
|
||||
Nuker,
|
||||
BoobyTrap,
|
||||
Scavenger,
|
||||
Capitalism,
|
||||
Gangster,
|
||||
Cleaner,
|
||||
BallLightning,
|
||||
Greedier,
|
||||
CursedWolf,
|
||||
ImperiusCurse,
|
||||
QuickShooter,
|
||||
Eraser,
|
||||
OverKiller,
|
||||
Hangman,
|
||||
Sans, // Arrogance
|
||||
Bard,
|
||||
Mafioso,
|
||||
Trickster,
|
||||
Swooper,
|
||||
Cantankerous,
|
||||
Duellist,
|
||||
YinYanger,
|
||||
Crewpostor,
|
||||
Parasite,
|
||||
Disperser,
|
||||
Blackmailer,
|
||||
Bomber,
|
||||
BountyHunter,
|
||||
OverKiller, // Butcher
|
||||
Camouflager,
|
||||
Saboteur,
|
||||
Capitalism,
|
||||
Cantankerous,
|
||||
Chronomancer,
|
||||
EvilDiviner, // Consigliere
|
||||
Consort,
|
||||
Councillor,
|
||||
Dazzler,
|
||||
Crewpostor,
|
||||
CursedWolf,
|
||||
Deathpact,
|
||||
Devourer,
|
||||
EvilDiviner,
|
||||
Morphling,
|
||||
Twister,
|
||||
Blackmailer,
|
||||
Disperser,
|
||||
Duellist,
|
||||
Dazzler,
|
||||
Escapee,
|
||||
Eraser,
|
||||
EvilGuesser,
|
||||
EvilTracker,
|
||||
FireWorks,
|
||||
Gambler,
|
||||
Gangster,
|
||||
Godfather,
|
||||
Greedier,
|
||||
Hangman,
|
||||
Hitman,
|
||||
Inhibitor,
|
||||
Minimalism, // Killing Machine
|
||||
BallLightning, // Lightning
|
||||
Librarian,
|
||||
Lurker,
|
||||
Convict,
|
||||
Visionary,
|
||||
Mafia,
|
||||
Mafioso,
|
||||
Mastermind,
|
||||
SerialKiller, // Mercenary
|
||||
Miner,
|
||||
Morphling,
|
||||
Assassin, // Ninja
|
||||
Nuker,
|
||||
Nullifier,
|
||||
Parasite,
|
||||
Penguin,
|
||||
Puppeteer,
|
||||
QuickShooter,
|
||||
Refugee,
|
||||
RiftMaker,
|
||||
Saboteur,
|
||||
Sapper,
|
||||
Scavenger,
|
||||
Sniper,
|
||||
ImperiusCurse, // Soul Catcher
|
||||
Swooper,
|
||||
Stealth,
|
||||
TimeThief,
|
||||
BoobyTrap, // Trapster
|
||||
Trickster,
|
||||
Twister,
|
||||
Underdog,
|
||||
// Flashbang,
|
||||
Undertaker,
|
||||
Vampire,
|
||||
Vindicator,
|
||||
Visionary,
|
||||
Warlock,
|
||||
Wildling,
|
||||
Witch,
|
||||
YinYanger,
|
||||
Zombie,
|
||||
|
||||
//Crewmates (Vanilla)
|
||||
Engineer,
|
||||
@@ -744,178 +740,169 @@ public enum CustomRoles
|
||||
|
||||
// Crewmate
|
||||
|
||||
Luckey,
|
||||
Needy,
|
||||
SuperStar,
|
||||
CyberStar,
|
||||
Demolitionist,
|
||||
Ventguard,
|
||||
Express,
|
||||
NiceEraser,
|
||||
TaskManager,
|
||||
Shiftguard,
|
||||
Mole,
|
||||
Sentinel,
|
||||
Electric,
|
||||
Philantropist,
|
||||
Tornado,
|
||||
Insight,
|
||||
Tunneler,
|
||||
Detour,
|
||||
Gaulois,
|
||||
Druid,
|
||||
Drainer,
|
||||
CameraMan,
|
||||
NiceHacker,
|
||||
Spy,
|
||||
Ricochet,
|
||||
Doormaster,
|
||||
Tether,
|
||||
Admirer,
|
||||
Addict,
|
||||
Aid,
|
||||
Benefactor,
|
||||
GuessManager,
|
||||
Escort,
|
||||
DonutDelivery,
|
||||
Alchemist,
|
||||
Altruist,
|
||||
Analyzer,
|
||||
Mayor,
|
||||
Jailor,
|
||||
Paranoia,
|
||||
Psychic,
|
||||
SabotageMaster,
|
||||
Sheriff,
|
||||
Snitch,
|
||||
Enigma,
|
||||
Marshall,
|
||||
SpeedBooster,
|
||||
Lookout,
|
||||
Autocrat,
|
||||
Benefactor,
|
||||
Bodyguard,
|
||||
CameraMan,
|
||||
CyberStar, // Celebrity
|
||||
Chameleon,
|
||||
Cleaner,
|
||||
Cleanser,
|
||||
CopyCat,
|
||||
Bloodhound, // Coroner
|
||||
Crusader,
|
||||
Demolitionist,
|
||||
Deputy,
|
||||
Detective,
|
||||
Detour,
|
||||
Dictator,
|
||||
Doctor,
|
||||
Detective,
|
||||
SwordsMan,
|
||||
NiceGuesser,
|
||||
Transporter,
|
||||
NiceSwapper,
|
||||
Altruist,
|
||||
Transmitter,
|
||||
Autocrat,
|
||||
Nightmare,
|
||||
TimeManager,
|
||||
Veteran,
|
||||
Bodyguard,
|
||||
//Counterfeiter,
|
||||
Witness,
|
||||
Grenadier,
|
||||
Lighter,
|
||||
SecurityGuard,
|
||||
Medic,
|
||||
Divinator,
|
||||
Glitch,
|
||||
Judge,
|
||||
Mortician,
|
||||
Mediumshiper,
|
||||
Observer,
|
||||
DonutDelivery,
|
||||
Doormaster,
|
||||
DovesOfNeace,
|
||||
Monarch,
|
||||
CopyCat,
|
||||
Drainer,
|
||||
Druid,
|
||||
Electric,
|
||||
Enigma,
|
||||
Escort,
|
||||
Express,
|
||||
Farseer,
|
||||
Bloodhound,
|
||||
Tracker,
|
||||
Merchant,
|
||||
//Retributionist,
|
||||
Deputy,
|
||||
Divinator, // Fortune Teller
|
||||
Gaulois,
|
||||
Glitch,
|
||||
Grenadier,
|
||||
GuessManager,
|
||||
Guardian,
|
||||
Addict,
|
||||
Alchemist,
|
||||
Tracefinder,
|
||||
Ignitor,
|
||||
Oracle,
|
||||
Spiritualist,
|
||||
Chameleon,
|
||||
ParityCop,
|
||||
Admirer,
|
||||
TimeMaster,
|
||||
Insight,
|
||||
ParityCop, // Inspector
|
||||
Jailor,
|
||||
Judge,
|
||||
Needy, // Lazy Guy
|
||||
Lighter,
|
||||
Lookout,
|
||||
Luckey,
|
||||
Marshall,
|
||||
Mayor,
|
||||
SabotageMaster, // Mechanic
|
||||
Medic,
|
||||
Mediumshiper,
|
||||
Merchant,
|
||||
Monitor,
|
||||
Crusader,
|
||||
Cleanser,
|
||||
//Reverie,
|
||||
Mole,
|
||||
Monarch,
|
||||
Mortician,
|
||||
NiceEraser,
|
||||
NiceGuesser,
|
||||
NiceHacker,
|
||||
NiceSwapper,
|
||||
Nightmare,
|
||||
Observer,
|
||||
Oracle,
|
||||
Paranoia,
|
||||
Philantropist,
|
||||
Psychic,
|
||||
Ricochet,
|
||||
Sentinel,
|
||||
SecurityGuard,
|
||||
Sheriff,
|
||||
Shiftguard,
|
||||
Snitch,
|
||||
Spiritualist,
|
||||
Speedrunner,
|
||||
SpeedBooster,
|
||||
Spy,
|
||||
SuperStar,
|
||||
TaskManager,
|
||||
Tether,
|
||||
TimeManager,
|
||||
TimeMaster,
|
||||
Tornado,
|
||||
Tracker,
|
||||
Transmitter,
|
||||
Transporter,
|
||||
Tracefinder,
|
||||
Tunneler,
|
||||
Ventguard,
|
||||
Veteran,
|
||||
SwordsMan, // Vigilante
|
||||
Witness,
|
||||
|
||||
//Neutrals
|
||||
|
||||
Arsonist,
|
||||
HexMaster,
|
||||
Jester,
|
||||
God,
|
||||
Opportunist,
|
||||
Mario,
|
||||
Terrorist,
|
||||
Executioner,
|
||||
Bandit,
|
||||
Lawyer,
|
||||
Jackal,
|
||||
Poisoner,
|
||||
//NWitch,
|
||||
Innocent,
|
||||
Pelican,
|
||||
Revolutionist,
|
||||
NSerialKiller,
|
||||
Enderman,
|
||||
Mycologist,
|
||||
Bubble,
|
||||
Hookshot,
|
||||
Sprayer,
|
||||
PlagueDoctor,
|
||||
Postman,
|
||||
Reckless,
|
||||
Magician,
|
||||
WeaponMaster,
|
||||
Eclipse,
|
||||
Pyromaniac,
|
||||
Vengeance,
|
||||
HeadHunter,
|
||||
Imitator,
|
||||
Werewolf,
|
||||
Juggernaut,
|
||||
Infectious,
|
||||
FFF,
|
||||
Konan,
|
||||
Gamer,
|
||||
DarkHide,
|
||||
Workaholic,
|
||||
Speedrunner,
|
||||
Collector,
|
||||
Agitater,
|
||||
Provocateur,
|
||||
Sunnyboy,
|
||||
BloodKnight,
|
||||
Wraith,
|
||||
Totocalcio,
|
||||
Romantic,
|
||||
VengefulRomantic,
|
||||
RuthlessRomantic,
|
||||
Succubus,
|
||||
Virus,
|
||||
Pursuer,
|
||||
Phantom,
|
||||
Jinx,
|
||||
Maverick,
|
||||
//CursedSoul,
|
||||
//Pirate,
|
||||
Ritualist,
|
||||
Doppelganger,
|
||||
Pickpocket,
|
||||
Traitor,
|
||||
Vulture,
|
||||
PlagueBearer,
|
||||
Pestilence,
|
||||
Medusa,
|
||||
Sidekick,
|
||||
//Baker,
|
||||
//Famine,
|
||||
Spiritcaller,
|
||||
Amnesiac,
|
||||
Arsonist,
|
||||
Bandit,
|
||||
BloodKnight,
|
||||
Bubble,
|
||||
Collector,
|
||||
Gamer, // Demon
|
||||
Doppelganger,
|
||||
Doomsayer,
|
||||
//Masochist,
|
||||
// Flux,
|
||||
Eclipse,
|
||||
Enderman,
|
||||
Executioner,
|
||||
Totocalcio, // Follower
|
||||
God,
|
||||
FFF, // Hater
|
||||
HeadHunter,
|
||||
HexMaster,
|
||||
Hookshot,
|
||||
Imitator,
|
||||
Infectious,
|
||||
Innocent,
|
||||
Jackal,
|
||||
Jester,
|
||||
Jinx,
|
||||
Juggernaut,
|
||||
Konan,
|
||||
Lawyer,
|
||||
Magician,
|
||||
Mario,
|
||||
Maverick,
|
||||
Medusa,
|
||||
Mycologist,
|
||||
Opportunist,
|
||||
Pelican,
|
||||
Pestilence,
|
||||
Phantom,
|
||||
Pickpocket,
|
||||
PlagueBearer,
|
||||
PlagueDoctor,
|
||||
Poisoner,
|
||||
Postman,
|
||||
Provocateur,
|
||||
Pursuer,
|
||||
Pyromaniac,
|
||||
Reckless,
|
||||
Revolutionist,
|
||||
Ritualist,
|
||||
Romantic,
|
||||
RuthlessRomantic,
|
||||
NSerialKiller, // Serial Killer
|
||||
Sidekick,
|
||||
Spiritcaller,
|
||||
Sprayer,
|
||||
DarkHide, // Stalker
|
||||
Succubus,
|
||||
Sunnyboy,
|
||||
Terrorist,
|
||||
Traitor,
|
||||
Vengeance,
|
||||
VengefulRomantic,
|
||||
Virus,
|
||||
Vulture,
|
||||
Wraith,
|
||||
Werewolf,
|
||||
WeaponMaster,
|
||||
Workaholic,
|
||||
|
||||
//SoloKombat
|
||||
KB_Normal,
|
||||
@@ -926,66 +913,67 @@ public enum CustomRoles
|
||||
//GM
|
||||
GM,
|
||||
|
||||
// ????
|
||||
Convict,
|
||||
|
||||
// Sub-role after 500
|
||||
NotAssigned = 500,
|
||||
LastImpostor,
|
||||
Lovers,
|
||||
Ntr,
|
||||
Madmate,
|
||||
Watcher,
|
||||
Flashman,
|
||||
Torch,
|
||||
Seer,
|
||||
Brakar,
|
||||
Oblivious,
|
||||
Bewilder,
|
||||
Sunglasses,
|
||||
Workhorse,
|
||||
Fool,
|
||||
Avanger,
|
||||
Youtuber,
|
||||
Egoist,
|
||||
TicketsStealer,
|
||||
DualPersonality,
|
||||
Mimic,
|
||||
Guesser,
|
||||
Necroview,
|
||||
Reach,
|
||||
Charmed,
|
||||
Bait,
|
||||
Trapper,
|
||||
Infected,
|
||||
Onbound,
|
||||
Knighted,
|
||||
Contagious,
|
||||
Cleansed,
|
||||
Unreportable,
|
||||
DeadlyQuota,
|
||||
Damocles,
|
||||
Stressed,
|
||||
Rogue,
|
||||
Lucky,
|
||||
Unlucky,
|
||||
DoubleShot,
|
||||
// Reflective,
|
||||
Rascal,
|
||||
Soulless,
|
||||
Gravestone,
|
||||
Lazy,
|
||||
Autopsy,
|
||||
Loyal,
|
||||
EvilSpirit,
|
||||
Recruit,
|
||||
Admired,
|
||||
Glow,
|
||||
Diseased,
|
||||
Antidote,
|
||||
Swift,
|
||||
Autopsy,
|
||||
Avanger,
|
||||
Bait,
|
||||
Trapper, // Beartrap
|
||||
Bewilder,
|
||||
Charmed,
|
||||
Cleansed,
|
||||
Contagious,
|
||||
Damocles,
|
||||
DeadlyQuota,
|
||||
Diseased,
|
||||
Unreportable, // Disregarded
|
||||
DoubleShot,
|
||||
Egoist,
|
||||
EvilSpirit,
|
||||
Flashman,
|
||||
Fool,
|
||||
Ghoul,
|
||||
Glow,
|
||||
Gravestone,
|
||||
Guesser,
|
||||
Infected,
|
||||
Knighted,
|
||||
LastImpostor,
|
||||
Lazy,
|
||||
Lovers,
|
||||
Loyal,
|
||||
Lucky,
|
||||
Madmate,
|
||||
Mare,
|
||||
Mimic,
|
||||
Necroview,
|
||||
Ntr, // Neptune
|
||||
Nimble,
|
||||
Oblivious,
|
||||
Onbound,
|
||||
Physicist,
|
||||
// QuickFix
|
||||
Rascal,
|
||||
Reach,
|
||||
Recruit,
|
||||
Rogue,
|
||||
DualPersonality, // Schizophrenic
|
||||
Seer,
|
||||
Soulless,
|
||||
TicketsStealer, // Stealer
|
||||
Stressed,
|
||||
Swift,
|
||||
Sunglasses,
|
||||
Brakar, // Tiebreaker
|
||||
Torch,
|
||||
Unlucky,
|
||||
Watcher,
|
||||
Workhorse,
|
||||
Youtuber
|
||||
}
|
||||
//WinData
|
||||
public enum CustomWinner
|
||||
|
||||
Reference in new issue
Block a user