mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
some improvements
This commit is contained in:
1 parent
e994ca0fc3
commit
899d40530e
18 files changed
+315
-173
No files matched your search
@@ -16,7 +16,7 @@ public static class AutoHaunt
|
||||
|
||||
return Options.CurrentGameMode switch
|
||||
{
|
||||
CustomGameMode.Standard => validPCs.OrderByDescending(x => x.GetCustomRole() is CustomRoles.Workaholic or CustomRoles.Snitch && x.GetTaskState().RemainingTasksCount <= 2).ThenByDescending(x => x.Is(CustomRoleTypes.Coven)).ThenByDescending(x => x.IsNeutralKiller()).ThenByDescending(x => x.IsImpostor()).ThenByDescending(x => x.GetCustomRole().IsNeutral()).FirstOrDefault(),
|
||||
CustomGameMode.Standard => validPCs.OrderByDescending(x => x.GetCustomRole() is CustomRoles.Workaholic or CustomRoles.Snitch && x.GetTaskState().RemainingTasksCount <= 2).ThenByDescending(x => x.IsNeutralKiller()).ThenByDescending(x => x.IsImpostor()).ThenByDescending(x => x.Is(CustomRoleTypes.Coven)).ThenByDescending(x => x.GetCustomRole().IsNeutral()).FirstOrDefault(),
|
||||
CustomGameMode.SoloPVP => validPCs.Where(x => x.SoloAlive()).MinBy(x => SoloPVP.GetRankFromScore(x.PlayerId)),
|
||||
CustomGameMode.FFA => validPCs.MaxBy(x => FreeForAll.KillCount.GetValueOrDefault(x.PlayerId, 0)),
|
||||
CustomGameMode.StopAndGo => validPCs.MaxBy(x => x.GetTaskState().CompletedTasksCount),
|
||||
|
||||
@@ -161,7 +161,7 @@ public static class Camouflage
|
||||
|
||||
public static void RpcSetSkin(PlayerControl target, bool forceRevert = false, bool revertToDefault = false, bool gameEnd = false, bool revive = false, bool notCommsOrCamo = false, CustomRpcSender sender = null)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost || (!Options.CommsCamouflage.GetBool() && !Camouflager.On && !revive && !notCommsOrCamo) || target == null || (BlockCamouflage && !forceRevert && !revertToDefault && !gameEnd && !revive && !notCommsOrCamo)) return;
|
||||
if (!AmongUsClient.Instance.AmHost || (!Options.CommsCamouflage.GetBool() && !Camouflager.On && !revive && !notCommsOrCamo) || !target || (BlockCamouflage && !forceRevert && !revertToDefault && !gameEnd && !revive && !notCommsOrCamo)) return;
|
||||
|
||||
Logger.Info($"New outfit for {target.GetNameWithRole()}", "Camouflage.RpcSetSkin");
|
||||
|
||||
@@ -202,7 +202,7 @@ public static class Camouflage
|
||||
{
|
||||
var killer = target.GetRealKiller();
|
||||
|
||||
if (Options.AnonymousBodies.GetBool() || target.Is(CustomRoles.Hidden) || (killer != null && killer.Is(CustomRoles.Concealer)))
|
||||
if (Options.AnonymousBodies.GetBool() || target.Is(CustomRoles.Hidden) || (killer && killer.Is(CustomRoles.Concealer)))
|
||||
newOutfit = new NetworkedPlayerInfo.PlayerOutfit().Set(Translator.GetString("Dead"), 15, "", "", "", "", "");
|
||||
else if (Options.RemovePetsAtDeadPlayers.GetBool())
|
||||
newOutfit.PetId = string.Empty;
|
||||
|
||||
@@ -2126,7 +2126,7 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
public string GetRoleInfo(bool infoLong = false)
|
||||
{
|
||||
CustomRoles role = player.GetCustomRole();
|
||||
CustomRoles role = player.AmOwner && Main.GM.Value ? CustomRoles.GM : player.GetCustomRole();
|
||||
if (role is CustomRoles.Crewmate or CustomRoles.Impostor) infoLong = false;
|
||||
|
||||
string info = (role.IsVanilla() ? "Blurb" : "Info") + (infoLong ? "Long" : string.Empty);
|
||||
|
||||
@@ -79,21 +79,6 @@ public static class CollectionExtensions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random element from a collection
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection</param>
|
||||
/// <typeparam name="T">The type of the collection</typeparam>
|
||||
/// <returns>
|
||||
/// A random element from the collection, or the default value of <typeparamref name="T" /> if the collection is
|
||||
/// empty
|
||||
/// </returns>
|
||||
public static T RandomElement<T>(this IReadOnlyList<T> collection)
|
||||
{
|
||||
if (collection.Count == 0) return default(T);
|
||||
return collection[IRandom.Instance.Next(collection.Count)];
|
||||
}
|
||||
|
||||
/// <param name="collection">The collection</param>
|
||||
/// <typeparam name="T">The type of the collection</typeparam>
|
||||
extension<T>(IEnumerable<T> collection)
|
||||
@@ -241,6 +226,220 @@ public static class CollectionExtensions
|
||||
element = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes the specified number of random elements from the collection to a List, and yields its elements for enumeration.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of random elements to pick.</param>
|
||||
/// <returns>A new collection with the specified number of random elements from the original collection.</returns>
|
||||
public IEnumerable<T> TakeRandom(int count)
|
||||
{
|
||||
if (collection == null || count <= 0)
|
||||
yield break;
|
||||
|
||||
var reservoir = new List<T>(count);
|
||||
int i = 0;
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
if (i < count)
|
||||
{
|
||||
reservoir.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
int j = IRandom.Instance.Next(i + 1);
|
||||
if (j < count)
|
||||
reservoir[j] = item;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// yield instead of returning list to avoid extra allocation
|
||||
foreach (var item in reservoir)
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes the specified number of random elements from the collection, and adds them to a new List.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of random elements to pick.</param>
|
||||
/// <returns>A new List with the specified number of random elements from the original collection.</returns>
|
||||
public List<T> TakeRandomToList(int count)
|
||||
{
|
||||
if (collection == null || count <= 0)
|
||||
return [];
|
||||
|
||||
var reservoir = new List<T>(count);
|
||||
int i = 0;
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
if (i < count)
|
||||
{
|
||||
reservoir.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
int j = IRandom.Instance.Next(i + 1);
|
||||
if (j < count)
|
||||
reservoir[j] = item;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return reservoir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a collection into a specified number of parts
|
||||
/// </summary>
|
||||
/// <param name="parts">The number of parts to partition the collection into</param>
|
||||
/// <returns>A collection of collections, each containing a part of the original collection</returns>
|
||||
public IEnumerable<IEnumerable<T>> Partition(int parts)
|
||||
{
|
||||
List<T> list = collection.ToList();
|
||||
int length = list.Count;
|
||||
if (parts <= 0 || length == 0) yield break;
|
||||
|
||||
if (parts > length) parts = length;
|
||||
|
||||
int size = length / parts;
|
||||
int remainder = length % parts;
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < parts; i++)
|
||||
{
|
||||
int partSize = size + (i < remainder ? 1 : 0);
|
||||
yield return list.Skip(index).Take(partSize);
|
||||
index += partSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an element from a collection
|
||||
/// </summary>
|
||||
/// <param name="element">The element to remove</param>
|
||||
/// <returns>
|
||||
/// A collection containing all elements of <paramref name="collection" /> except for <paramref name="element" />
|
||||
/// </returns>
|
||||
public IEnumerable<T> Without(T element)
|
||||
{
|
||||
return collection.Where(x => !x.Equals(element));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles all elements in a collection randomly
|
||||
/// </summary>
|
||||
/// <returns>A new, shuffled collection as a <see cref="List{T}" /></returns>
|
||||
public List<T> Shuffle()
|
||||
{
|
||||
if (collection is not List<T> list)
|
||||
list = collection.ToList();
|
||||
|
||||
int n = list.Count;
|
||||
var r = IRandom.Instance;
|
||||
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = r.Next(n + 1);
|
||||
(list[n], list[k]) = (list[k], list[n]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="collection">The collection</param>
|
||||
/// <typeparam name="T">The type of the collection</typeparam>
|
||||
extension<T>(IReadOnlyList<T> collection)
|
||||
{
|
||||
/// <summary>
|
||||
/// Partitions a list into a specified number of parts
|
||||
/// </summary>
|
||||
/// <param name="parts">The number of parts to partition the list into</param>
|
||||
/// <returns>A list of lists, each containing a part of the original list</returns>
|
||||
public IEnumerable<IEnumerable<T>> Partition(int parts)
|
||||
{
|
||||
int length = collection.Count;
|
||||
if (parts <= 0 || length == 0) yield break;
|
||||
|
||||
if (parts > length) parts = length;
|
||||
|
||||
int size = length / parts;
|
||||
int remainder = length % parts;
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < parts; i++)
|
||||
{
|
||||
int partSize = size + (i < remainder ? 1 : 0);
|
||||
yield return collection.Skip(index).Take(partSize);
|
||||
index += partSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes the specified number of random elements from the collection.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of random elements to pick.</param>
|
||||
/// <returns>A new collection with the specified number of random elements from the original collection.</returns>
|
||||
public IEnumerable<T> TakeRandom(int count)
|
||||
{
|
||||
if (collection == null || collection.Count == 0 || count <= 0)
|
||||
yield break;
|
||||
|
||||
int n = collection.Count;
|
||||
|
||||
// If asking for >= all elements, just return everything
|
||||
if (count >= n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
yield return collection[i];
|
||||
yield break;
|
||||
}
|
||||
|
||||
// If k is small relative to n → pick unique indices via HashSet
|
||||
// If k is large → invert selection (pick excluded indices instead)
|
||||
if (count <= n / 2)
|
||||
{
|
||||
var chosen = new HashSet<int>();
|
||||
while (chosen.Count < count)
|
||||
{
|
||||
int idx = IRandom.Instance.Next(n);
|
||||
if (chosen.Add(idx))
|
||||
yield return collection[idx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// More efficient to exclude (n - count) items
|
||||
int excludeCount = n - count;
|
||||
var excluded = new HashSet<int>();
|
||||
|
||||
while (excluded.Count < excludeCount)
|
||||
excluded.Add(IRandom.Instance.Next(n));
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (!excluded.Contains(i))
|
||||
yield return collection[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random element from a collection
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A random element from the collection, or the default value of <typeparamref name="T" /> if the collection is
|
||||
/// empty
|
||||
/// </returns>
|
||||
public T RandomElement()
|
||||
{
|
||||
if (collection.Count == 0) return default(T);
|
||||
return collection[IRandom.Instance.Next(collection.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,7 +472,7 @@ public static class CollectionExtensions
|
||||
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
}
|
||||
|
||||
|
||||
#region ToValidPlayers
|
||||
|
||||
/// <summary>
|
||||
@@ -295,25 +494,11 @@ public static class CollectionExtensions
|
||||
{
|
||||
return playerIds.ConvertAll(Utils.GetPlayer).FindAll(x => x);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Without
|
||||
|
||||
/// <summary>
|
||||
/// Removes an element from a collection
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection to remove the element from</param>
|
||||
/// <param name="element">The element to remove</param>
|
||||
/// <typeparam name="T">The type of the elements in the collection</typeparam>
|
||||
/// <returns>
|
||||
/// A collection containing all elements of <paramref name="collection" /> except for <paramref name="element" />
|
||||
/// </returns>
|
||||
public static IEnumerable<T> Without<T>(this IEnumerable<T> collection, T element)
|
||||
{
|
||||
return collection.Where(x => !x.Equals(element));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an element from a collection
|
||||
/// </summary>
|
||||
@@ -339,35 +524,11 @@ public static class CollectionExtensions
|
||||
{
|
||||
return collection.Where(x => x != element);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Shuffle
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles all elements in a collection randomly
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the collection</typeparam>
|
||||
/// <param name="collection">The collection to be shuffled</param>
|
||||
/// <returns>A new, shuffled collection as a <see cref="List{T}" /></returns>
|
||||
public static List<T> Shuffle<T>(this IEnumerable<T> collection)
|
||||
{
|
||||
if (collection is not List<T> list)
|
||||
list = collection.ToList();
|
||||
|
||||
int n = list.Count;
|
||||
var r = IRandom.Instance;
|
||||
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = r.Next(n + 1);
|
||||
(list[n], list[k]) = (list[k], list[n]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles all elements in a collection randomly
|
||||
/// </summary>
|
||||
@@ -410,63 +571,27 @@ public static class CollectionExtensions
|
||||
return collection;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Partition
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a collection into a specified number of parts
|
||||
/// Shuffles all elements in an IList randomly
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection to partition</param>
|
||||
/// <param name="parts">The number of parts to partition the collection into</param>
|
||||
/// <typeparam name="T">The type of the elements in the collection</typeparam>
|
||||
/// <returns>A collection of collections, each containing a part of the original collection</returns>
|
||||
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> collection, int parts)
|
||||
/// <param name="collection">The IList to be shuffled</param>
|
||||
/// <typeparam name="T">The type of the IList</typeparam>
|
||||
/// <returns>The same IList with its elements shuffled</returns>
|
||||
public static IList<T> Shuffle<T>(this IList<T> collection)
|
||||
{
|
||||
List<T> list = collection.ToList();
|
||||
int length = list.Count;
|
||||
if (parts <= 0 || length == 0) yield break;
|
||||
int n = collection.Count;
|
||||
var r = IRandom.Instance;
|
||||
|
||||
if (parts > length) parts = length;
|
||||
|
||||
int size = length / parts;
|
||||
int remainder = length % parts;
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < parts; i++)
|
||||
while (n > 1)
|
||||
{
|
||||
int partSize = size + (i < remainder ? 1 : 0);
|
||||
yield return list.Skip(index).Take(partSize);
|
||||
index += partSize;
|
||||
n--;
|
||||
int k = r.Next(n + 1);
|
||||
(collection[n], collection[k]) = (collection[k], collection[n]);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a list into a specified number of parts
|
||||
/// </summary>
|
||||
/// <param name="collection">The list to partition</param>
|
||||
/// <param name="parts">The number of parts to partition the list into</param>
|
||||
/// <typeparam name="T">The type of the elements in the list</typeparam>
|
||||
/// <returns>A list of lists, each containing a part of the original list</returns>
|
||||
public static IEnumerable<IEnumerable<T>> Partition<T>(this IReadOnlyList<T> collection, int parts)
|
||||
{
|
||||
int length = collection.Count;
|
||||
if (parts <= 0 || length == 0) yield break;
|
||||
|
||||
if (parts > length) parts = length;
|
||||
|
||||
int size = length / parts;
|
||||
int remainder = length % parts;
|
||||
var index = 0;
|
||||
|
||||
for (var i = 0; i < parts; i++)
|
||||
{
|
||||
int partSize = size + (i < remainder ? 1 : 0);
|
||||
yield return collection.Skip(index).Take(partSize);
|
||||
index += partSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,8 @@ public class PlayerState(byte playerId)
|
||||
|
||||
if (!role.Is(Team.Impostor) && !(role == CustomRoles.Traitor && Traitor.CanGetImpostorOnlyAddons.GetBool()))
|
||||
SubRoles.ToArray().DoIf(x => x.IsImpOnlyAddon(), RemoveSubRole);
|
||||
|
||||
if (role == CustomRoles.GM) return;
|
||||
|
||||
if (role is CustomRoles.Sidekick or CustomRoles.Necromancer or CustomRoles.Deathknight or CustomRoles.Renegade)
|
||||
SubRoles.ToArray().DoIf(StartGameHostPatch.BasisChangingAddons.ContainsKey, RemoveSubRole);
|
||||
|
||||
+1
-1
@@ -4065,7 +4065,7 @@ public static class Utils
|
||||
Logger.Exception(ex, "AfterPlayerDeathTasks");
|
||||
}
|
||||
|
||||
if (!target || (Main.DiedThisRound.Contains(target.PlayerId) && IsRevivingRoleAlive()) || Options.CurrentGameMode != CustomGameMode.Standard) return;
|
||||
if (!target || (Main.DiedThisRound.Contains(target.PlayerId) && IsRevivingRoleAlive() && !GameStates.IsMeeting) || Options.CurrentGameMode != CustomGameMode.Standard) return;
|
||||
|
||||
if (targetRealKiller)
|
||||
target.Notify($"<#ffffff>{string.Format(GetString("DeathCommand"), targetRealKiller.PlayerId.ColoredPlayerName(), (targetRealKiller.Is(CustomRoles.Bloodlust) ? $"{CustomRoles.Bloodlust.ToColoredString()} " : string.Empty) + targetRealKiller.GetCustomRole().ToColoredString())}</color>", 10f);
|
||||
|
||||
@@ -1453,7 +1453,7 @@ internal static class ChatCommands
|
||||
{
|
||||
var suitableRoles = hnsRoles.FindAll(x => x.Interface.Team == team);
|
||||
if (suitableRoles.Count == 0) continue;
|
||||
allPlayerIds.Shuffle().Where(x => !DraftRoles.ContainsKey(x)).Take(num).Do(x => DraftRoles[x] = [suitableRoles.RandomElement().Role]);
|
||||
allPlayerIds.Where(x => !DraftRoles.ContainsKey(x)).TakeRandom(num).Do(x => DraftRoles[x] = [suitableRoles.RandomElement().Role]);
|
||||
}
|
||||
|
||||
var hiderRoles = hnsRoles.FindAll(x => x.Interface.Team == Team.Crewmate);
|
||||
@@ -1488,10 +1488,10 @@ internal static class ChatCommands
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<CustomRoles> impRoles = allRoles.Where(x => x.IsImpostor()).Shuffle().Take(Options.FactionMinMaxSettings[Team.Impostor].MaxSetting.GetInt());
|
||||
IEnumerable<CustomRoles> nkRoles = allRoles.Where(x => x.IsNK()).Shuffle().Take(Math.Min(Options.FactionMinMaxSettings[Team.Neutral].MaxSetting.GetInt(), Options.RoleSubCategoryLimits[RoleOptionType.Neutral_Killing][2].GetInt()));
|
||||
IEnumerable<CustomRoles> nnkRoles = allRoles.Where(x => x.IsNonNK()).Shuffle().Take(Math.Min(Options.FactionMinMaxSettings[Team.Neutral].MaxSetting.GetInt() - Options.RoleSubCategoryLimits[RoleOptionType.Neutral_Killing][2].GetInt(), Options.MaxNNKs.GetInt()));
|
||||
IEnumerable<CustomRoles> covenRoles = allRoles.Where(x => x.IsCoven()).Shuffle().Take(Options.FactionMinMaxSettings[Team.Coven].MaxSetting.GetInt());
|
||||
IEnumerable<CustomRoles> impRoles = allRoles.Where(x => x.IsImpostor()).TakeRandom(Options.FactionMinMaxSettings[Team.Impostor].MaxSetting.GetInt());
|
||||
IEnumerable<CustomRoles> nkRoles = allRoles.Where(x => x.IsNK()).TakeRandom(Math.Min(Options.FactionMinMaxSettings[Team.Neutral].MaxSetting.GetInt(), Options.RoleSubCategoryLimits[RoleOptionType.Neutral_Killing][2].GetInt()));
|
||||
IEnumerable<CustomRoles> nnkRoles = allRoles.Where(x => x.IsNonNK()).TakeRandom(Math.Min(Options.FactionMinMaxSettings[Team.Neutral].MaxSetting.GetInt() - Options.RoleSubCategoryLimits[RoleOptionType.Neutral_Killing][2].GetInt(), Options.MaxNNKs.GetInt()));
|
||||
IEnumerable<CustomRoles> covenRoles = allRoles.Where(x => x.IsCoven()).TakeRandom(Options.FactionMinMaxSettings[Team.Coven].MaxSetting.GetInt());
|
||||
|
||||
allRoles.RemoveAll(x => x.IsImpostor());
|
||||
allRoles.RemoveAll(x => x.IsNK());
|
||||
@@ -3428,6 +3428,7 @@ internal static class ChatCommands
|
||||
if (text.StartsWith('/') && !player.IsModdedClient() && (!GameStates.IsMeeting || MeetingHud.Instance.state is not MeetingHud.VoteStates.Results and not MeetingHud.VoteStates.Proceeding))
|
||||
{
|
||||
Utils.CheckServerCommand(ref text, out bool spamRequired);
|
||||
if (!spamRequired) canceled = true;
|
||||
string[] args = text.Split(' ');
|
||||
|
||||
foreach (Command command in Command.AllCommands)
|
||||
@@ -3445,7 +3446,7 @@ internal static class ChatCommands
|
||||
|
||||
if (command.AlwaysHidden && spamRequired) Utils.SendMessage("\n", player.PlayerId, GetString("NoSpamAnymoreUseCmd"));
|
||||
command.Action(player, text, args);
|
||||
if (command.IsCanceled) canceled = command.AlwaysHidden || !Options.HostSeesCommandsEnteredByOthers.GetBool();
|
||||
if (command.IsCanceled) canceled |= command.AlwaysHidden || !Options.HostSeesCommandsEnteredByOthers.GetBool();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -456,14 +456,25 @@ public static class InGameRoleInfoMenu
|
||||
MainInfo.DestroyTranslator();
|
||||
MainInfo.transform.localPosition = new(-2.3f, 0.8f, 4f);
|
||||
MainInfo.GetComponent<RectTransform>().sizeDelta = new(4.5f, 10f);
|
||||
|
||||
MainInfoTMP.alignment = TextAlignmentOptions.Left;
|
||||
MainInfoTMP.fontSize = MainInfoTMP.fontSizeMax = MainInfoTMP.fontSizeMin = 1.75f;
|
||||
MainInfoTMP.enableAutoSizing = true;
|
||||
MainInfoTMP.fontSizeMax = 1.75f;
|
||||
MainInfoTMP.fontSizeMin = 0.75f;
|
||||
MainInfoTMP.enableWordWrapping = true;
|
||||
MainInfoTMP.overflowMode = TextOverflowModes.Overflow;
|
||||
|
||||
AddonsInfo = Object.Instantiate(MainInfo, MainInfo.transform.parent);
|
||||
AddonsInfo.name = "Addons Info";
|
||||
AddonsInfo.DestroyTranslator();
|
||||
AddonsInfo.transform.SetLocalX(2.3f);
|
||||
AddonsInfo.transform.localScale = new(0.7f, 0.7f, 0.7f);
|
||||
|
||||
AddonsInfoTMP.enableAutoSizing = true;
|
||||
AddonsInfoTMP.fontSizeMax = 1.75f;
|
||||
AddonsInfoTMP.fontSizeMin = 0.6f;
|
||||
AddonsInfoTMP.enableWordWrapping = true;
|
||||
AddonsInfoTMP.overflowMode = TextOverflowModes.Overflow;
|
||||
}
|
||||
|
||||
public static void SetRoleInfoRef(PlayerControl player)
|
||||
@@ -477,26 +488,20 @@ public static class InGameRoleInfoMenu
|
||||
StringBuilder titleSb = new();
|
||||
StringBuilder settings = new();
|
||||
StringBuilder addons = new();
|
||||
settings.Append("<size=75%>");
|
||||
titleSb.Append($"{role.ToColoredString()} {Utils.GetRoleMode(role)}");
|
||||
sb.Append("<size=90%>");
|
||||
sb.Append(player.GetRoleInfo(true).TrimStart());
|
||||
if (Options.CustomRoleSpawnChances.TryGetValue(role, out StringOptionItem opt)) Utils.ShowChildrenSettings(opt, ref settings, f1: true, disableColor: false);
|
||||
|
||||
settings.Append("</size>");
|
||||
if (settings.Length > 0) addons.Append($"{settings}\n\n");
|
||||
|
||||
if (role.PetActivatedAbility()) sb.Append($"<size=80%>{GetString("SupportsPetMessage")}</size>");
|
||||
if (role.PetActivatedAbility()) sb.Append($"{GetString("SupportsPetMessage")}");
|
||||
|
||||
string searchStr = GetString(role.ToString());
|
||||
sb.Replace(searchStr, role.ToColoredString());
|
||||
sb.Replace(searchStr.ToLower(), role.ToColoredString());
|
||||
sb.Append("</size>");
|
||||
List<CustomRoles> subRoles = Main.PlayerStates[player.PlayerId].SubRoles;
|
||||
if (subRoles.Count > 0) addons.Append(GetString("AddonListTitle"));
|
||||
|
||||
addons.Append("<size=75%>");
|
||||
|
||||
subRoles.ForEach(subRole =>
|
||||
{
|
||||
addons.Append($"\n\n{subRole.ToColoredString()} {Utils.GetRoleMode(subRole)} {GetString($"{subRole}InfoLong").FixRoleName(subRole)}");
|
||||
@@ -505,9 +510,8 @@ public static class InGameRoleInfoMenu
|
||||
addons.Replace(searchSubStr.ToLower(), subRole.ToColoredString());
|
||||
});
|
||||
|
||||
addons.Append("</size>");
|
||||
if (role.UsesPetInsteadOfKill()) sb.Append($"\n\n<size=85%>{GetString("UsesPetInsteadOfKillNotice")}</size>");
|
||||
if (player.UsesMeetingShapeshift()) sb.Append($"\n\n<size=85%>{GetString("UsesMeetingShapeshiftNotice")}</size>");
|
||||
if (role.UsesPetInsteadOfKill()) sb.Append($"\n\n{GetString("UsesPetInsteadOfKillNotice")}");
|
||||
if (player.UsesMeetingShapeshift()) sb.Append($"\n\n{GetString("UsesMeetingShapeshiftNotice")}");
|
||||
|
||||
sb.Insert(0, $"{titleSb}\n");
|
||||
|
||||
|
||||
+10
-10
@@ -509,9 +509,9 @@ public static class ButtonCooldownPatch
|
||||
|
||||
if (__instance.isCoolingDown)
|
||||
{
|
||||
__instance.cooldownTimerText.text = timer < 10f
|
||||
__instance.cooldownTimerText.text = timer <= 9.9f
|
||||
? timer.ToString("0.0", NumberFormatInfo.CurrentInfo)
|
||||
: ((int)timer).ToString();
|
||||
: timer >= 11 ? ((int)timer).ToString() : "10";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1021,23 +1021,17 @@ internal static class TaskPanelBehaviourPatch
|
||||
panel.openPosition = Vector3.Lerp(panel.openPosition, targetOpen, t);
|
||||
|
||||
PlayerControl player = PlayerControl.LocalPlayer;
|
||||
|
||||
string roleInfo = player.GetRoleInfo();
|
||||
|
||||
var roleWithInfoBuilder = new StringBuilder();
|
||||
roleWithInfoBuilder.Append("<b>");
|
||||
roleWithInfoBuilder.Append(role.ToColoredString());
|
||||
roleWithInfoBuilder.Append(":</b>\r\n");
|
||||
roleWithInfoBuilder.Append(roleInfo);
|
||||
|
||||
if (Options.CurrentGameMode != CustomGameMode.Standard)
|
||||
{
|
||||
string[] splitted = roleInfo.Split(' ');
|
||||
|
||||
roleWithInfoBuilder.Clear();
|
||||
roleWithInfoBuilder.Append("<b>");
|
||||
roleWithInfoBuilder.Append(GetString(Options.CurrentGameMode.ToString()));
|
||||
roleWithInfoBuilder.Append(":</b>\r\n");
|
||||
|
||||
if (splitted.Length <= 3)
|
||||
{
|
||||
roleWithInfoBuilder.Append(roleInfo);
|
||||
@@ -1053,7 +1047,6 @@ internal static class TaskPanelBehaviourPatch
|
||||
{
|
||||
string[] split = roleInfo.Split(' ');
|
||||
int half = split.Length / 2;
|
||||
roleWithInfoBuilder.Clear();
|
||||
roleWithInfoBuilder.Append("<b>");
|
||||
roleWithInfoBuilder.Append(role.ToColoredString());
|
||||
roleWithInfoBuilder.Append(":</b>\r\n");
|
||||
@@ -1061,6 +1054,13 @@ internal static class TaskPanelBehaviourPatch
|
||||
roleWithInfoBuilder.Append("\r\n");
|
||||
roleWithInfoBuilder.Append(string.Join(' ', split[half..]));
|
||||
}
|
||||
else
|
||||
{
|
||||
roleWithInfoBuilder.Append("<b>");
|
||||
roleWithInfoBuilder.Append(role.ToColoredString());
|
||||
roleWithInfoBuilder.Append(":</b>\r\n");
|
||||
roleWithInfoBuilder.Append(roleInfo);
|
||||
}
|
||||
|
||||
StringBuilder finalTextBuilder = new StringBuilder(Utils.ColorString(player.GetRoleColor(), roleWithInfoBuilder.ToString()));
|
||||
|
||||
|
||||
@@ -799,7 +799,7 @@ internal static class StartGameHostPatch
|
||||
roleList.ExceptWith(otherList);
|
||||
}
|
||||
|
||||
BasisChangingAddons[addon] = roleList.Shuffle().Take(addon.GetCount()).ToList();
|
||||
BasisChangingAddons[addon] = roleList.TakeRandomToList(addon.GetCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1571,7 +1571,7 @@ internal static class StartGameHostPatch
|
||||
foreach ((byte id, RoleTypes roleTypes) in OverriddenTeamRevealScreen)
|
||||
{
|
||||
PlayerControl pc = id.GetPlayer();
|
||||
if (pc == null || !pc.IsAlive()) continue;
|
||||
if (!pc || !pc.IsAlive()) continue;
|
||||
|
||||
int targetClientId = pc.OwnerId;
|
||||
if (targetClientId == -1) continue;
|
||||
|
||||
@@ -2637,7 +2637,7 @@
|
||||
"EndermanSecondsBeforeTP": "Seconds Before Teleporting",
|
||||
"Evolver": "Evolver",
|
||||
"EvolverInfo": "Select an Upgrade After Each Meeting",
|
||||
"EvolverInfoLong": "(Neutrals):\\nAs the Evolver, you can choose an upgrade after each meeting. Pet to cycle between the available upgrades. The same upgrade can appear multiple times. Once the timer to pick ends, your upgrade will be applied.\\n\\n<size=70%><b><u>Upgrades:</u></b>\\n- Decrease Kill Cooldown (by 2.5s)\\n- Gain Impostor Vision (unaffected by lights sabotage)\\n- Increase Vision (by 0.4x)\\n- Increase Speed (by 0.25x)\\n- Increase Kill Distance (Short → Medium → Long)\\n- Gain Venting Ability (and 1 use)\\n- Increase Venting Use Limit (by 3)\\n- Gain Sabotage Ability (and 1 use)\\n- Increase Sabotage Use Limit (by 2)\\n- Gain Shield (cannot be killed in the current round)</size>\\n\\n<u><b>Remarks:</b></u>\\nIf you can vent, the number of vent uses you have is displayed next to your role name like this: <b>(V: 1)</b> - Same with the sabotage use limit: <b>(S: 2)</b>",
|
||||
"EvolverInfoLong": "(Neutrals):\\nAs the Evolver, you can choose an upgrade after each meeting. Pet to cycle between the available upgrades. The same upgrade can appear multiple times. Once the timer to pick ends, your upgrade will be applied.\\n\\n<size=70%><b><u>Upgrades:</u></b>\\n- Decrease Kill Cooldown (by 5s)\\n- Gain Impostor Vision (unaffected by lights sabotage)\\n- Increase Vision (by 0.6x)\\n- Increase Speed (by 0.5x)\\n- Increase Kill Distance (Short → Medium → Long)\\n- Gain Venting Ability (and 3 uses)\\n- Increase Venting Use Limit (by 10)\\n- Gain Sabotage Ability (and 2 uses)\\n- Increase Sabotage Use Limit (by 5)\\n- Gain Shield (cannot be killed in the current round)</size>\\n\\n<u><b>Remarks:</b></u>\\nIf you can vent, the number of vent uses you have is displayed next to your role name like this: <b>(V: 1)</b> - Same with the sabotage use limit: <b>(S: 2)</b>",
|
||||
"EvolverProgress.Sabotage": " (S: {0})",
|
||||
"EvolverProgress.Vent": " (V: {0})",
|
||||
"EvolverSuffix": "<size=85%>Selected upgrade will be applied in <#00ffa5>{0}s</color>\\nSelected upgrade: <#00ffa5>{1}</color> (Pet to cycle)\\n<size=70%>Available upgrades: <#00ffa5>{2}</color></size></size>",
|
||||
@@ -2690,7 +2690,7 @@
|
||||
"GaslighterWinCondition.LastStanding": "<#ffab1b>Last one standing</color>",
|
||||
"Glitch": "The Glitch",
|
||||
"GlitchInfo": "Hack and kill everyone",
|
||||
"GlitchInfoLong": "(Neutrals):\\nAs the Glitch, your goal is to kill everyone on the ship. You can hack players to prevent them from killing, venting, sabotaging, and reporting dead bodies for a limited time. You can also disguise as a random player for a limited time. Additionally, your cooldowns go down while in vents.",
|
||||
"GlitchInfoLong": "(Neutrals):\\nAs the Glitch, your goal is to kill everyone on the ship. You can hack players to prevent them from killing, venting, sabotaging, and reporting dead bodies for a limited time by single clicking on them with your kill button. Double click with it to kill them normally. You can also disguise by shapeshifting for a limited time. Additionally, your cooldowns go down while in vents.",
|
||||
"GlitchCanVote": "Can vote",
|
||||
"God": "God",
|
||||
"GodInfo": "Everything is under your control",
|
||||
|
||||
@@ -71,11 +71,11 @@ public class Locator : RoleBase, IHideAndSeekRole
|
||||
void OnElapsed()
|
||||
{
|
||||
PlayerControl pc = Utils.GetPlayerById(playerId);
|
||||
if (pc == null || !pc.IsAlive()) return;
|
||||
if (!pc || !pc.IsAlive()) return;
|
||||
|
||||
PlayerControl target = CustomHnS.PlayerRoles.Where(x => x.Value.Interface.Team != Team.Impostor).Select(x => Utils.GetPlayerById(x.Key)).Where(x => x != null && x.IsAlive()).Shuffle().FirstOrDefault();
|
||||
PlayerControl target = CustomHnS.PlayerRoles.Where(x => x.Value.Interface.Team != Team.Impostor).Select(x => Utils.GetPlayerById(x.Key)).Where(x => x && x.IsAlive()).RandomElement();
|
||||
|
||||
if (target != null)
|
||||
if (target)
|
||||
{
|
||||
TargetId = target.PlayerId;
|
||||
TargetArrow.Add(pc.PlayerId, target.PlayerId);
|
||||
|
||||
@@ -7,9 +7,12 @@ internal class Kidnapper : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
public static OptionItem KCD;
|
||||
public static OptionItem SSCD;
|
||||
private static int Id => 643300;
|
||||
private static OptionItem KCD;
|
||||
private static OptionItem SSCD;
|
||||
private static OptionItem AbilityUseLimit;
|
||||
private static OptionItem AbilityUseGainWithEachKill;
|
||||
|
||||
private const int Id = 643300;
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
@@ -23,6 +26,14 @@ internal class Kidnapper : RoleBase
|
||||
KCD = new FloatOptionItem(Id + 3, "KillCooldown", new(0f, 180f, 0.5f), 30f, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Kidnapper])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
|
||||
AbilityUseLimit = new IntegerOptionItem(Id + 4, "AbilityUseLimit", new(0, 20, 1), 1, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Kidnapper])
|
||||
.SetValueFormat(OptionFormat.Times);
|
||||
|
||||
AbilityUseGainWithEachKill = new FloatOptionItem(Id + 5, "AbilityUseGainWithEachKill", new(0f, 5f, 0.1f), 0.5f, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Kidnapper])
|
||||
.SetValueFormat(OptionFormat.Times);
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
@@ -40,8 +51,12 @@ internal class Kidnapper : RoleBase
|
||||
{
|
||||
if (!kidnapper || !target || !shapeshifting) return true;
|
||||
|
||||
if (kidnapper.GetAbilityUseLimit() < 1f) return false;
|
||||
|
||||
if (!target.TP(kidnapper))
|
||||
kidnapper.Notify(Utils.ColorString(Color.yellow, Translator.GetString("TargetCannotBeTeleported")));
|
||||
else
|
||||
kidnapper.RpcRemoveAbilityUse();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -54,5 +69,6 @@ internal class Kidnapper : RoleBase
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class Centralizer : RoleBase
|
||||
if (MarkedPosition.HasValue)
|
||||
{
|
||||
if (pc.GetAbilityUseLimit() < 1) return;
|
||||
Main.EnumerateAlivePlayerControls().Shuffle().Take(NumPlayersTeleported.GetInt()).MassTP(MarkedPosition.Value);
|
||||
Main.EnumerateAlivePlayerControls().TakeRandom(NumPlayersTeleported.GetInt()).MassTP(MarkedPosition.Value);
|
||||
MarkedPosition = null;
|
||||
pc.RpcRemoveAbilityUse();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Hazel;
|
||||
using static EHR.Translator;
|
||||
|
||||
namespace EHR.Roles;
|
||||
@@ -149,12 +148,12 @@ internal class EvilEraser : RoleBase
|
||||
PlayerToErase.ForEach(id =>
|
||||
{
|
||||
PlayerControl pc = Utils.GetPlayerById(id);
|
||||
if (pc == null || !pc.IsAlive() || pc.Is(CustomRoles.Bloodlust)) return;
|
||||
if (!pc || !pc.IsAlive() || pc.Is(CustomRoles.Bloodlust)) return;
|
||||
|
||||
CustomRoles erasedRole = pc.IsImpostor() ? CustomRoles.ImpostorEHR : pc.IsCrewmate() ? CustomRoles.CrewmateEHR : pc.Is(Team.Coven) ? CustomRoles.CovenMember : CustomRoles.Amnesiac;
|
||||
pc.RpcSetCustomRole(erasedRole);
|
||||
pc.RpcChangeRoleBasis(erasedRole);
|
||||
pc.Notify(GetString("LostRoleByEraser"));
|
||||
pc.Notify(GetString("LostRoleByEvilEraser"));
|
||||
Logger.Info($"{pc.GetNameWithRole().RemoveHtmlTags()} lost their role", "Eraser");
|
||||
ErasedPlayers.Add(id);
|
||||
});
|
||||
|
||||
@@ -117,7 +117,7 @@ public class Evolver : RoleBase
|
||||
{
|
||||
if (!EvolverPC.IsAlive()) return;
|
||||
|
||||
Upgrades = Enum.GetValues<Upgrade>().Except(GetBannedUpgradeList()).Shuffle().GetRange(0, 3);
|
||||
Upgrades = Enum.GetValues<Upgrade>().Except(GetBannedUpgradeList()).TakeRandomToList(3);
|
||||
SelectedUpgradeIndex = 0;
|
||||
ChooseTimer = 15;
|
||||
|
||||
@@ -130,15 +130,10 @@ public class Evolver : RoleBase
|
||||
{
|
||||
var banned = new List<Upgrade>();
|
||||
if (Stats.KillCooldown <= Limits.MinKillCooldown) banned.Add(Upgrade.DecreaseKillCooldown);
|
||||
|
||||
if (Stats.ImpostorVision) banned.Add(Upgrade.GainImpostorVision);
|
||||
|
||||
if (Stats.Vision >= Limits.MaxVision) banned.Add(Upgrade.IncreaseVision);
|
||||
|
||||
if (Stats.Speed >= Limits.MaxSpeed) banned.Add(Upgrade.IncreaseSpeed);
|
||||
|
||||
if (Stats.KillDistance >= Limits.MaxKillDistance) banned.Add(Upgrade.IncreaseKillDistance);
|
||||
|
||||
banned.Add(Stats.CanVent ? Upgrade.GainVent : Upgrade.IncreaseVentUseLimit);
|
||||
banned.Add(Stats.CanSabotage ? Upgrade.GainSabotage : Upgrade.IncreaseSabotageUseLimit);
|
||||
return banned;
|
||||
@@ -165,33 +160,33 @@ public class Evolver : RoleBase
|
||||
switch (Upgrades[SelectedUpgradeIndex])
|
||||
{
|
||||
case Upgrade.DecreaseKillCooldown:
|
||||
Stats.KillCooldown -= 2.5f;
|
||||
Stats.KillCooldown -= 5f;
|
||||
break;
|
||||
case Upgrade.GainImpostorVision:
|
||||
Stats.ImpostorVision = true;
|
||||
break;
|
||||
case Upgrade.IncreaseVision:
|
||||
Stats.Vision += 0.4f;
|
||||
Stats.Vision += 0.6f;
|
||||
break;
|
||||
case Upgrade.IncreaseSpeed:
|
||||
Stats.Speed += 0.25f;
|
||||
Stats.Speed += 0.5f;
|
||||
break;
|
||||
case Upgrade.IncreaseKillDistance:
|
||||
Stats.KillDistance++;
|
||||
break;
|
||||
case Upgrade.GainVent:
|
||||
Stats.CanVent = true;
|
||||
Stats.VentUseLimit = 1;
|
||||
Stats.VentUseLimit = 3;
|
||||
break;
|
||||
case Upgrade.IncreaseVentUseLimit:
|
||||
Stats.VentUseLimit += 3;
|
||||
Stats.VentUseLimit += 10;
|
||||
break;
|
||||
case Upgrade.GainSabotage:
|
||||
Stats.CanSabotage = true;
|
||||
Stats.SabotageUseLimit = 1;
|
||||
Stats.SabotageUseLimit = 2;
|
||||
break;
|
||||
case Upgrade.IncreaseSabotageUseLimit:
|
||||
Stats.SabotageUseLimit += 2;
|
||||
Stats.SabotageUseLimit += 5;
|
||||
break;
|
||||
case Upgrade.GainShield:
|
||||
Stats.Shielded = true;
|
||||
|
||||
@@ -244,16 +244,16 @@ internal class QuizMaster : RoleBase
|
||||
(IEnumerable<string> WrongAnswers, string CorrectAnswer) answers = index switch
|
||||
{
|
||||
1 when abc => (Enum.GetValues<Team>().Skip(1).Where(x => x != Data.LastReportedPlayer.Player.GetTeam()).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{Data.LastReportedPlayer.Player.GetTeam()}")),
|
||||
1 => (AllColors.Without(Data.LastReportedPlayer.ColorString).Shuffle().Take(2), Data.LastReportedPlayer.ColorString),
|
||||
1 => (AllColors.Without(Data.LastReportedPlayer.ColorString).TakeRandom(2), Data.LastReportedPlayer.ColorString),
|
||||
2 when abc => (new[] { PlayerState.DeathReason.Suicide, PlayerState.DeathReason.Kill, PlayerState.DeathReason.etc }.Without(Main.PlayerStates[Data.LastReportedPlayer.Player!.PlayerId].deathReason).Select(x => Translator.GetString($"DeathReason.{x}")), Translator.GetString($"DeathReason.{Main.PlayerStates[Data.LastReportedPlayer.Player!.PlayerId].deathReason}")),
|
||||
2 => (GetTwoRandomNames(Data.LastPlayerPressedButtonName), Data.LastPlayerPressedButtonName),
|
||||
3 when abc => (["Town Of Us", "Town Of Host Enhanced"], "Endless Host Roles"),
|
||||
3 => (AllSabotages.Take(2).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{Data.LastSabotage}")),
|
||||
4 when abc => (["tukasa0001", "0xDrMoe"], "Gurge44"),
|
||||
4 => (CustomRoleSelector.RoleResult.Values.Without(Main.LastVotedPlayerInfo!.Object.GetCustomRole()).Shuffle().Take(2).Select(x => x.ToColoredString()), Main.LastVotedPlayerInfo!.Object.GetCustomRole().ToColoredString()),
|
||||
4 => (CustomRoleSelector.RoleResult.Values.Without(Main.LastVotedPlayerInfo!.Object.GetCustomRole()).TakeRandom(2).Select(x => x.ToColoredString()), Main.LastVotedPlayerInfo!.Object.GetCustomRole().ToColoredString()),
|
||||
5 when abc => (Enum.GetValues<Team>().Skip(1).Where(x => x != randomRole.GetTeam()).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{randomRole.GetTeam()}")),
|
||||
5 => (GetTwoRandomNames(Data.LastReporterName), Data.LastReporterName),
|
||||
6 when abc => (Enum.GetValues<CustomRoleTypes>().Where(x => x != randomRole.GetCustomRoleTypes()).Shuffle().Take(2).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{randomRole.GetCustomRoleTypes()}")),
|
||||
6 when abc => (Enum.GetValues<CustomRoleTypes>().Where(x => x != randomRole.GetCustomRoleTypes()).TakeRandom(2).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{randomRole.GetCustomRoleTypes()}")),
|
||||
6 => (GetTwoRandomNumbers(Data.NumPlayersVotedLastMeeting, 0, 15), Data.NumPlayersVotedLastMeeting.ToString()),
|
||||
7 => (GetTwoRandomNumbers(Data.NumMeetings, Math.Max(Data.NumMeetings - 3, 0), Data.NumMeetings + 3), Data.NumMeetings.ToString()),
|
||||
8 => (GetTwoRandomNames(Data.FirstReportedBodyPlayerName), Data.FirstReportedBodyPlayerName),
|
||||
@@ -262,7 +262,7 @@ internal class QuizMaster : RoleBase
|
||||
11 => (GetTwoRandomNumbers(Data.NumPlayersDeadFirstRound, Math.Max(Data.NumPlayersDeadFirstRound - 3, 0), Data.NumPlayersDeadFirstRound + 3), Data.NumPlayersDeadFirstRound.ToString()),
|
||||
12 => (GetTwoRandomNumbers(Data.NumSabotages, Math.Max(Data.NumSabotages - 3, 0), Data.NumSabotages + 3), Data.NumSabotages.ToString()),
|
||||
13 => (GetTwoRandomNumbers(GameData.Instance.CompletedTasks, 0, GameData.Instance.TotalTasks), GameData.Instance.CompletedTasks.ToString()),
|
||||
14 => (Enum.GetValues<RoleTypes>().Without(RoleTypes.CrewmateGhost).Without(RoleTypes.ImpostorGhost).Where(x => x != randomRole.GetRoleTypes()).Shuffle().Take(2).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{randomRole.GetRoleTypes()}")),
|
||||
14 => (Enum.GetValues<RoleTypes>().Without(RoleTypes.CrewmateGhost).Without(RoleTypes.ImpostorGhost).Where(x => x != randomRole.GetRoleTypes()).TakeRandom(2).Select(x => Translator.GetString($"{x}")), Translator.GetString($"{randomRole.GetRoleTypes()}")),
|
||||
|
||||
_ => ([], string.Empty)
|
||||
};
|
||||
@@ -272,7 +272,7 @@ internal class QuizMaster : RoleBase
|
||||
|
||||
return new(title, allAnswersList.ToArray(), correctIndex);
|
||||
|
||||
IEnumerable<string> GetTwoRandomNames(string except) => Main.EnumeratePlayerControls().Select(x => x?.GetRealName()).Without(except).Shuffle().TakeLast(2);
|
||||
IEnumerable<string> GetTwoRandomNames(string except) => Main.EnumeratePlayerControls().Select(x => x?.GetRealName()).Without(except).TakeRandom(2);
|
||||
|
||||
IEnumerable<string> GetTwoRandomNumbers(params int[] nums) => IRandom.SequenceUnique(3, nums[1], nums[2] + 1).Without(nums[0]).Take(2).Select(x => x.ToString());
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Thanos : RoleBase
|
||||
CollectedStones = [];
|
||||
ActiveStone = null;
|
||||
MindStoneUsed = false;
|
||||
PlayersWithStones = Main.EnumerateAlivePlayerControls().Shuffle().Take(Enum.GetValues<Stone>().Length).Select(x => x.PlayerId).ToList();
|
||||
PlayersWithStones = Main.EnumerateAlivePlayerControls().Select(x => x.PlayerId).TakeRandomToList(Enum.GetValues<Stone>().Length);
|
||||
ThanosId = playerId;
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user