Compare commits

...
18 Commits
Author SHA1 Message Date
Gurge44 c8ef3f0a9f Fixes & Cat becomes Amnesiac if copied role is single 2024-04-06 22:30:24 +02:00
Gurge44 28d448a93a Fix duplicate IDs & missing strings 2024-04-06 20:46:56 +02:00
Gurge44 8614486bb7 Code Cleanup 2024-04-06 20:38:36 +02:00
Gurge44 0ca51a468b bruh 2024-04-06 20:35:04 +02:00
Gurge44 b614ec7dfa Some settings 2024-04-06 20:34:47 +02:00
Gurge44 8df2c8fa81 Add Schrödinger's Cat & Cherokious 2024-04-06 20:14:06 +02:00
Gurge44 21b4a1ead8 Merge branch 'main' of https://github.com/Gurge44/TOHE_PLUS 2024-04-06 19:07:13 +02:00
Gurge44 6f1bc6a415 fix FFA win condition (again) 2024-04-06 19:07:08 +02:00
PHGaming121 339fabc04b bro put analyst on role name, but forgot to but analyst on info and infolong 💀 (#51)
* Update en_US.json

* Update pt_BR.json
2024-04-06 18:43:40 +02:00
Gurge44 026ee4d322 Add Generator & Sentry 2024-04-06 14:50:25 +02:00
Gurge44 91e2748156 Fix FFA, Fix Moderator tags, New timer font for S&G 2024-04-05 19:04:05 +02:00
Gurge44 dbb5c39dea Minor fixes 2024-04-03 21:25:25 +02:00
Gurge44 a13cbd9f88 Merge branch 'main' of https://github.com/Gurge44/TOHE_PLUS 2024-04-03 20:24:12 +02:00
Gurge44 333dfbee6f Teamed FFA 2024-04-03 20:24:03 +02:00
PHGaming121 0431d18127 make adventurer things translatable + a little fix 💀 (#50)
* Update Adventurer.cs

* Update en_US.json

* also, heres a little fix lol
2024-04-03 12:06:55 +02:00
Gurge44 2db423411f Merge branch 'main' of https://github.com/Gurge44/TOHE_PLUS 2024-04-02 19:58:39 +02:00
Gurge44 3d120a5924 New role Adventurer & Bug Fixes 2024-04-02 19:58:34 +02:00
Gurge44 f91f0cd09c Translation (#48)
* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Portuguese, Brazilian)
2024-04-02 15:51:54 +02:00
53 changed files with 1544 additions and 394 deletions

No files matched your search

+19 -14
View File
@@ -3,10 +3,10 @@ namespace EHR;
public enum CustomRoles
{
//Default
// Default
Crewmate = 0,
//Impostors (Vanilla)
// Impostors (Vanilla)
Impostor,
Shapeshifter,
@@ -14,7 +14,7 @@ public enum CustomRoles
ImpostorEHR,
ShapeshifterEHR,
//Impostors
// Impostors
Hacker, // Anonymous
AntiAdminer,
@@ -49,6 +49,7 @@ public enum CustomRoles
Freezer,
Gambler,
Gangster,
Generator,
Godfather,
Greedier,
Hangman,
@@ -100,7 +101,7 @@ public enum CustomRoles
YinYanger,
Zombie,
//Crewmates (Vanilla)
// Crewmates (Vanilla)
Engineer,
GuardianAngel,
Scientist,
@@ -111,13 +112,14 @@ public enum CustomRoles
GuardianAngelEHR,
ScientistEHR,
// Crewmate
// Crewmates
Addict,
Adventurer,
Aid,
Alchemist,
Altruist,
Analyzer,
Analyst,
Autocrat,
Beacon,
Benefactor,
@@ -186,8 +188,9 @@ public enum CustomRoles
Rabbit,
Randomizer,
Ricochet,
Sentinel,
SecurityGuard,
Sentinel,
Sentry,
Sheriff,
Shiftguard,
Snitch,
@@ -211,7 +214,7 @@ public enum CustomRoles
SwordsMan, // Vigilante
Witness,
//Neutrals
// Neutrals
Agitater,
Amnesiac,
@@ -219,6 +222,7 @@ public enum CustomRoles
Bandit,
BloodKnight,
Bubble,
Cherokious,
Collector,
Deathknight,
Gamer, // Demon
@@ -267,6 +271,7 @@ public enum CustomRoles
Ritualist,
Romantic,
RuthlessRomantic,
SchrodingersCat,
NSerialKiller, // Serial Killer
Sidekick,
SoulHunter,
@@ -287,25 +292,25 @@ public enum CustomRoles
WeaponMaster,
Workaholic,
//SoloKombat
// Solo Kombat
KB_Normal,
//FFA
// FFA
Killer,
//MoveAndStop
// Move And Stop
Tasker,
//HotPotato
// Hot Potato
Potato,
//H&S
// H&S
Hider,
Seeker,
Fox,
Troll,
//GM
// GM
GM,
// ????
+19 -14
View File
@@ -3,10 +3,10 @@ namespace EHR;
public enum CustomRoles
{
//Default
// Default
Crewmate = 0,
//Impostors (Vanilla)
// Impostors (Vanilla)
Impostor,
Shapeshifter,
@@ -14,7 +14,7 @@ public enum CustomRoles
ImpostorEHR,
ShapeshifterEHR,
//Impostors
// Impostors
Hacker, // Anonymous
AntiAdminer,
@@ -49,6 +49,7 @@ public enum CustomRoles
Freezer,
Gambler,
Gangster,
Generator,
Godfather,
Greedier,
Hangman,
@@ -100,7 +101,7 @@ public enum CustomRoles
YinYanger,
Zombie,
//Crewmates (Vanilla)
// Crewmates (Vanilla)
Engineer,
GuardianAngel,
Scientist,
@@ -111,13 +112,14 @@ public enum CustomRoles
GuardianAngelEHR,
ScientistEHR,
// Crewmate
// Crewmates
Addict,
Adventurer,
Aid,
Alchemist,
Altruist,
Analyzer,
Analyst,
Autocrat,
Beacon,
Benefactor,
@@ -186,8 +188,9 @@ public enum CustomRoles
Rabbit,
Randomizer,
Ricochet,
Sentinel,
SecurityGuard,
Sentinel,
Sentry,
Sheriff,
Shiftguard,
Snitch,
@@ -211,7 +214,7 @@ public enum CustomRoles
SwordsMan, // Vigilante
Witness,
//Neutrals
// Neutrals
Agitater,
Amnesiac,
@@ -219,6 +222,7 @@ public enum CustomRoles
Bandit,
BloodKnight,
Bubble,
Cherokious,
Collector,
Deathknight,
Gamer, // Demon
@@ -267,6 +271,7 @@ public enum CustomRoles
Ritualist,
Romantic,
RuthlessRomantic,
SchrodingersCat,
NSerialKiller, // Serial Killer
Sidekick,
SoulHunter,
@@ -287,25 +292,25 @@ public enum CustomRoles
WeaponMaster,
Workaholic,
//SoloKombat
// Solo Kombat
KB_Normal,
//FFA
// FFA
Killer,
//MoveAndStop
// Move And Stop
Tasker,
//HotPotato
// Hot Potato
Potato,
//H&S
// H&S
Hider,
Seeker,
Fox,
Troll,
//GM
// GM
GM,
// ????
+2
View File
@@ -15,8 +15,10 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NE/@EntryIndexedValue">NE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NK/@EntryIndexedValue">NK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NNK/@EntryIndexedValue">NNK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RNG/@EntryIndexedValue">RNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SK/@EntryIndexedValue">SK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TMP/@EntryIndexedValue">TMP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EHR/@EntryIndexedValue">EHR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=VN/@EntryIndexedValue">VN</s:String></wpf:ResourceDictionary>
+142 -91
View File
@@ -22,6 +22,20 @@ internal static class FFAManager
public static string LatestChatMessage = string.Empty;
public static Dictionary<byte, int> PlayerTeams = [];
public static readonly Dictionary<int, string> TeamColors = new()
{
{ 0, "#00ffff" },
{ 1, "#ffff00" },
{ 2, "#ff00ff" },
{ 3, "#ff0000" },
{ 4, "#00ff00" },
{ 5, "#0000ff" },
{ 6, "#ffffff" },
{ 7, "#000000" }
};
public static OptionItem FFAGameTime;
public static OptionItem FFAKcd;
public static OptionItem FFALowerVision;
@@ -36,6 +50,8 @@ internal static class FFAManager
public static OptionItem FFAEnableRandomTwists;
public static OptionItem FFAShieldIsOneTimeUse;
public static OptionItem FFAChatDuringGame;
public static OptionItem FFATeamMode;
public static OptionItem FFATeamNumber;
public static void SetupCustomOption()
{
@@ -84,6 +100,14 @@ internal static class FFAManager
FFAChatDuringGame = BooleanOptionItem.Create(67_223_014, "FFA_ChatDuringGame", false, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.FFA)
.SetColor(new Color32(0, 255, 165, byte.MaxValue));
FFATeamMode = BooleanOptionItem.Create(67_223_015, "FFA_TeamMode", false, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.FFA)
.SetHeader(true)
.SetColor(new Color32(0, 255, 165, byte.MaxValue));
FFATeamNumber = IntegerOptionItem.Create(67_223_016, "FFA_TeamNumber", new(2, 7, 1), 2, TabGroup.GameSettings, false)
.SetParent(FFATeamMode)
.SetGameMode(CustomGameMode.FFA)
.SetColor(new Color32(0, 255, 165, byte.MaxValue));
}
public static void Init()
@@ -99,10 +123,32 @@ internal static class FFAManager
KillCount = [];
RoundTime = FFAGameTime.GetInt() + 8;
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
PlayerTeams = [];
var allPlayers = Main.AllAlivePlayerControls;
foreach (PlayerControl pc in allPlayers)
{
KillCount[pc.PlayerId] = 0;
}
if (FFATeamMode.GetBool())
{
var teamNum = FFATeamNumber.GetInt();
var playerNum = allPlayers.Length;
int memberNum = (teamNum > 5 && playerNum == 15) || playerNum % teamNum == 0 ? playerNum / teamNum : playerNum / teamNum + 1;
var teamMembers = allPlayers.Select(x => x.PlayerId).Shuffle(IRandom.Instance).Chunk(memberNum).ToList();
for (int i = 0; i < teamMembers.Count; i++)
{
foreach (var id in teamMembers[i])
{
PlayerTeams.Add(id, i);
}
}
_ = new LateTask(() => { Utils.NotifyRoles(NoCache: true, ForceLoop: true); }, 10f, log: false);
}
}
private static void SendRPCSyncFFAPlayer(byte playerId)
@@ -146,15 +192,6 @@ internal static class FFAManager
}
}
public static string GetDisplayScore(byte playerId)
{
int rank = GetRankOfScore(playerId);
string score = KillCount.TryGetValue(playerId, out var s) ? $"{s}" : "Invalid";
string text = string.Format(GetString("KBDisplayScore"), rank.ToString(), score);
Color color = Utils.GetRoleColor(CustomRoles.Killer);
return Utils.ColorString(color, text);
}
public static int GetRankOfScore(byte playerId)
{
try
@@ -184,6 +221,12 @@ internal static class FFAManager
return;
}
if (FFATeamMode.GetBool() && PlayerTeams[killer.PlayerId] == PlayerTeams[target.PlayerId])
{
Logger.Info("Killer and Target are in the same team, attack blocked", "FFA");
return;
}
var totalalive = Main.AllAlivePlayerControls.Length;
if (FFAShieldedList.TryGetValue(target.PlayerId, out var dur))
{
@@ -206,7 +249,7 @@ internal static class FFAManager
if (totalalive == 3)
{
PlayerControl otherPC = null;
foreach (var pc in Main.AllAlivePlayerControls.Where(a => a.PlayerId != killer.PlayerId && a.PlayerId != target.PlayerId && a.IsAlive()).ToArray())
foreach (var pc in Main.AllAlivePlayerControls.Where(a => a.PlayerId != killer.PlayerId && a.PlayerId != target.PlayerId && a.IsAlive()))
{
TargetArrow.Add(killer.PlayerId, pc.PlayerId);
TargetArrow.Add(pc.PlayerId, killer.PlayerId);
@@ -222,90 +265,99 @@ internal static class FFAManager
bool mark = false;
var nowKCD = Main.AllPlayerKillCooldown[killer.PlayerId];
byte EffectType;
if (Main.NormalOptions.MapId != 4) EffectType = (byte)HashRandom.Next(0, 10);
else EffectType = (byte)HashRandom.Next(4, 10);
if (EffectType <= 7) // Buff
if (Main.NormalOptions.MapId != 4) EffectType = (byte)IRandom.Instance.Next(0, 10);
else EffectType = (byte)IRandom.Instance.Next(4, 10);
switch (EffectType)
{
byte EffectID = (byte)HashRandom.Next(0, 3);
if (Main.NormalOptions.MapId == 4) EffectID = 2;
switch (EffectID)
// Buff
case <= 7:
{
case 0:
FFAShieldedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
killer.Notify(GetString("FFA-Event-GetShield"), FFAShieldDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
case 1:
if (FFAIncreasedSpeedList.ContainsKey(killer.PlayerId))
{
FFAIncreasedSpeedList.Remove(killer.PlayerId);
FFAIncreasedSpeedList.Add(killer.PlayerId, Utils.TimeStamp);
}
else
{
FFAIncreasedSpeedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]);
Main.AllPlayerSpeed[killer.PlayerId] = FFAIncreasedSpeed.GetFloat();
}
byte EffectID = (byte)IRandom.Instance.Next(0, 3);
if (Main.NormalOptions.MapId == 4) EffectID = 2;
switch (EffectID)
{
case 0:
FFAShieldedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
killer.Notify(GetString("FFA-Event-GetShield"), FFAShieldDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
case 1:
if (FFAIncreasedSpeedList.ContainsKey(killer.PlayerId))
{
FFAIncreasedSpeedList.Remove(killer.PlayerId);
FFAIncreasedSpeedList.Add(killer.PlayerId, Utils.TimeStamp);
}
else
{
FFAIncreasedSpeedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]);
Main.AllPlayerSpeed[killer.PlayerId] = FFAIncreasedSpeed.GetFloat();
}
killer.Notify(GetString("FFA-Event-GetIncreasedSpeed"), FFAModifiedSpeedDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
mark = true;
break;
case 2:
Main.AllPlayerKillCooldown[killer.PlayerId] = Math.Clamp(FFAKcd.GetFloat() - 3f, 1f, 60f);
killer.Notify(GetString("FFA-Event-GetLowKCD"));
sync = true;
break;
default:
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
killer.Notify(GetString("FFA-Event-GetIncreasedSpeed"), FFAModifiedSpeedDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
mark = true;
break;
case 2:
Main.AllPlayerKillCooldown[killer.PlayerId] = Math.Clamp(FFAKcd.GetFloat() - 3f, 1f, 60f);
killer.Notify(GetString("FFA-Event-GetLowKCD"));
sync = true;
break;
default:
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
}
break;
}
}
else if (EffectType == 8) // De-Buff
{
byte EffectID = (byte)HashRandom.Next(0, 3);
if (Main.NormalOptions.MapId == 4) EffectID = 1;
switch (EffectID)
// De-Buff
case 8:
{
case 0:
if (FFADecreasedSpeedList.ContainsKey(killer.PlayerId))
{
FFADecreasedSpeedList.Remove(killer.PlayerId);
FFADecreasedSpeedList.Add(killer.PlayerId, Utils.TimeStamp);
}
else
{
FFADecreasedSpeedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]);
Main.AllPlayerSpeed[killer.PlayerId] = FFADecreasedSpeed.GetFloat();
}
byte EffectID = (byte)IRandom.Instance.Next(0, 3);
if (Main.NormalOptions.MapId == 4) EffectID = 1;
switch (EffectID)
{
case 0:
if (FFADecreasedSpeedList.ContainsKey(killer.PlayerId))
{
FFADecreasedSpeedList.Remove(killer.PlayerId);
FFADecreasedSpeedList.Add(killer.PlayerId, Utils.TimeStamp);
}
else
{
FFADecreasedSpeedList.TryAdd(killer.PlayerId, Utils.TimeStamp);
originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]);
Main.AllPlayerSpeed[killer.PlayerId] = FFADecreasedSpeed.GetFloat();
}
killer.Notify(GetString("FFA-Event-GetDecreasedSpeed"), FFAModifiedSpeedDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
mark = true;
break;
case 1:
Main.AllPlayerKillCooldown[killer.PlayerId] = Math.Clamp(FFAKcd.GetFloat() + 3f, 1f, 60f);
killer.Notify(GetString("FFA-Event-GetHighKCD"));
sync = true;
break;
case 2:
FFALowerVisionList.TryAdd(killer.PlayerId, Utils.TimeStamp);
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
killer.Notify(GetString("FFA-Event-GetLowVision"));
mark = true;
break;
default:
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
killer.Notify(GetString("FFA-Event-GetDecreasedSpeed"), FFAModifiedSpeedDuration.GetFloat());
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
mark = true;
break;
case 1:
Main.AllPlayerKillCooldown[killer.PlayerId] = Math.Clamp(FFAKcd.GetFloat() + 3f, 1f, 60f);
killer.Notify(GetString("FFA-Event-GetHighKCD"));
sync = true;
break;
case 2:
FFALowerVisionList.TryAdd(killer.PlayerId, Utils.TimeStamp);
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
killer.Notify(GetString("FFA-Event-GetLowVision"));
mark = true;
break;
default:
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
}
break;
}
}
else // Mixed
{
_ = new LateTask(() => { killer.TPtoRndVent(); }, 0.5f, "FFA-Event-TP");
killer.Notify(GetString("FFA-Event-GetTP"));
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
// Mixed
default:
_ = new LateTask(() => { killer.TPtoRndVent(); }, 0.5f, "FFA-Event-TP");
killer.Notify(GetString("FFA-Event-GetTP"));
Main.AllPlayerKillCooldown[killer.PlayerId] = FFAKcd.GetFloat();
break;
}
if (sync || Math.Abs(nowKCD - Main.AllPlayerKillCooldown[killer.PlayerId]) > 0.1f)
@@ -353,7 +405,7 @@ internal static class FFAManager
LatestChatMessage = string.Format(GetString("FFAChatMessageNotify"), playerName, message);
foreach (var pc in Main.AllAlivePlayerControls)
{
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
pc.Notify(LatestChatMessage);
}
}
@@ -384,8 +436,7 @@ internal static class FFAManager
var rd = IRandom.Instance;
byte FFAdoTPdecider = (byte)rd.Next(0, 100);
bool FFAdoTP = false;
if (FFAdoTPdecider == 0) FFAdoTP = true;
bool FFAdoTP = FFAdoTPdecider == 0;
if (FFAEnableRandomTwists.GetBool() && FFAdoTP)
{
+12 -4
View File
@@ -62,7 +62,15 @@ public class Counter(int totalGreenTime, int totalRedTime, long startTimeStamp,
public string ColoredTimerString
{
get => IsYellow ? Utils.ColorString(Color.clear, "00") : Utils.ColorString(IsRed ? Color.red : Color.green, Timer < 10 ? $"0{Timer}" : Timer.ToString());
get
{
string result = IsYellow || (Timer == TotalGreenTime && !IsRed && !IsYellow) || (Timer == TotalRedTime && IsRed) ? Utils.ColorString(Color.clear, "00") : Utils.ColorString(IsRed ? Color.red : Color.green, Timer < 10 ? $"0{Timer}" : Timer.ToString());
if (Timer is <= 19 and >= 10 && !IsYellow) result = $" {result}";
if (Timer % 10 == 1 && !IsYellow) result = result.Insert(result.Length - 9, " ");
return $"<font=\"DIGITAL-7 SDF\"><size=130%>{result}</size></font>";
}
}
public string ColoredArrow
@@ -139,10 +147,10 @@ class MoveAndStopPlayerData(Counter leftCounter, Counter middleCounter, Counter
var middleTimer = MiddleCounter.ColoredTimerString;
var rightTimer = RightCounter.ColoredTimerString;
var arrowRow = $"{LeftCounter.ColoredArrow.PadRightV2(4)} {MiddleCounter.ColoredArrow.PadRightV2(4)} {RightCounter.ColoredArrow.PadRightV2(4)}";
var counterRow = $"{leftTimer.PadRightV2(4)} {middleTimer.PadRightV2(4)} {rightTimer.PadRightV2(4)}";
var arrowRow = $"{LeftCounter.ColoredArrow} {MiddleCounter.ColoredArrow} {RightCounter.ColoredArrow}";
var counterRow = $"{leftTimer} {middleTimer} {rightTimer}";
return $"{arrowRow}\n{counterRow}";
return $"{counterRow}\n{arrowRow}";
}
public void UpdateCounters()
+27 -7
View File
@@ -3,6 +3,7 @@ using EHR.Roles.Crewmate;
using EHR.Roles.Impostor;
using EHR.Roles.Neutral;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using UnityEngine;
@@ -98,6 +99,7 @@ internal static class CustomRolesHelper
CustomRoles.Dictator => CustomRoles.Crewmate,
CustomRoles.Inhibitor => CustomRoles.Impostor,
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
CustomRoles.Generator => CustomRoles.Shapeshifter,
CustomRoles.Blackmailer => CustomRoles.Impostor,
CustomRoles.Commander => CustomRoles.Shapeshifter,
CustomRoles.Freezer => CustomRoles.Shapeshifter,
@@ -148,6 +150,7 @@ internal static class CustomRolesHelper
CustomRoles.Express => CustomRoles.Crewmate,
CustomRoles.NiceEraser => CustomRoles.Crewmate,
CustomRoles.TaskManager => CustomRoles.Crewmate,
CustomRoles.Adventurer => CustomRoles.Engineer,
CustomRoles.Randomizer => CustomRoles.Crewmate,
CustomRoles.Beacon => CustomRoles.Crewmate,
CustomRoles.Rabbit => CustomRoles.Crewmate,
@@ -168,6 +171,7 @@ internal static class CustomRolesHelper
CustomRoles.Altruist => CustomRoles.Crewmate,
CustomRoles.Transmitter => CustomRoles.Crewmate,
CustomRoles.Autocrat => CustomRoles.Crewmate,
CustomRoles.Sentry => CustomRoles.Crewmate,
CustomRoles.Perceiver => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Convener => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Mathematician => CustomRoles.Crewmate,
@@ -181,7 +185,7 @@ internal static class CustomRolesHelper
CustomRoles.Escort => UsePets && Escort.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor,
CustomRoles.DonutDelivery => UsePets && DonutDelivery.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor,
CustomRoles.Gaulois => UsePets && Gaulois.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor,
CustomRoles.Analyzer => UsePets && Analyzer.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor,
CustomRoles.Analyst => UsePets && Analyst.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor,
CustomRoles.Escapee => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.NiceGuesser => CustomRoles.Crewmate,
CustomRoles.EvilGuesser => CustomRoles.Impostor,
@@ -228,6 +232,7 @@ internal static class CustomRolesHelper
CustomRoles.Bard => CustomRoles.Impostor,
CustomRoles.Swooper => CustomRoles.Impostor,
CustomRoles.Crewpostor => CustomRoles.Engineer,
CustomRoles.Cherokious => CustomRoles.Engineer,
CustomRoles.Observer => CustomRoles.Crewmate,
CustomRoles.DovesOfNeace => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Disperser => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
@@ -310,7 +315,7 @@ internal static class CustomRolesHelper
CustomRoles.Escort => UsePets && Escort.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.DonutDelivery => UsePets && DonutDelivery.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Gaulois => UsePets && Gaulois.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Analyzer => UsePets && Analyzer.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Analyst => UsePets && Analyst.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Witness => UsePets && Options.WitnessUsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Pursuer => RoleTypes.Impostor,
CustomRoles.Revolutionist => RoleTypes.Impostor,
@@ -337,6 +342,7 @@ internal static class CustomRolesHelper
CustomRoles.Sprayer => RoleTypes.Impostor,
CustomRoles.PlagueDoctor => RoleTypes.Impostor,
CustomRoles.Postman => RoleTypes.Impostor,
CustomRoles.SchrodingersCat => RoleTypes.Impostor,
CustomRoles.Impartial => RoleTypes.Impostor,
CustomRoles.Predator => RoleTypes.Impostor,
CustomRoles.Reckless => RoleTypes.Impostor,
@@ -375,6 +381,7 @@ internal static class CustomRolesHelper
public static bool IsNonNK(this CustomRoles role) => role is
CustomRoles.Jester or
CustomRoles.Postman or
CustomRoles.SchrodingersCat or
CustomRoles.Impartial or
CustomRoles.Predator or
CustomRoles.SoulHunter or
@@ -411,6 +418,7 @@ internal static class CustomRolesHelper
CustomRoles.Succubus or
CustomRoles.Gamer or
CustomRoles.Crewpostor or
CustomRoles.Cherokious or
CustomRoles.Necromancer or
CustomRoles.Agitater or
CustomRoles.Wraith or
@@ -475,6 +483,7 @@ internal static class CustomRolesHelper
CustomRoles.Amnesiac or
CustomRoles.God or
CustomRoles.Postman or
CustomRoles.SchrodingersCat or
CustomRoles.Predator or
CustomRoles.Pursuer or
CustomRoles.FFF or
@@ -531,6 +540,7 @@ internal static class CustomRolesHelper
CustomRoles.Underdog or
CustomRoles.Inhibitor or
CustomRoles.Kidnapper or
CustomRoles.Generator or
CustomRoles.Blackmailer or
CustomRoles.Commander or
CustomRoles.Freezer or
@@ -629,7 +639,7 @@ internal static class CustomRolesHelper
CustomRoles.Escort or
CustomRoles.DonutDelivery or
CustomRoles.Gaulois or
CustomRoles.Analyzer or
CustomRoles.Analyst or
CustomRoles.Witness or
CustomRoles.Monarch or
CustomRoles.Jailor or
@@ -652,7 +662,7 @@ internal static class CustomRolesHelper
CustomRoles.Aid when Aid.UsePet.GetBool() => true,
CustomRoles.Escort when Escort.UsePet.GetBool() => true,
CustomRoles.DonutDelivery when DonutDelivery.UsePet.GetBool() => true,
CustomRoles.Analyzer when Analyzer.UsePet.GetBool() => true,
CustomRoles.Analyst when Analyst.UsePet.GetBool() => true,
CustomRoles.Jailor when Jailor.UsePet.GetBool() => true,
CustomRoles.Sheriff when Sheriff.UsePet.GetBool() => true,
CustomRoles.SwordsMan when SwordsMan.UsePet.GetBool() => true,
@@ -671,14 +681,17 @@ internal static class CustomRolesHelper
_ => false,
};
public static readonly CustomRoles[] OnlySpawnsWithPetsRoleList =
public static readonly List<CustomRoles> OnlySpawnsWithPetsRoleList =
[
CustomRoles.Tunneler,
CustomRoles.Tornado,
CustomRoles.Swiftclaw,
CustomRoles.Adventurer,
CustomRoles.Sentry,
CustomRoles.Cherokious
];
public static bool OnlySpawnsWithPets(this CustomRoles role) => OnlySpawnsWithPetsRoleList.Any(x => x.ToString() == role.ToString());
public static bool OnlySpawnsWithPets(this CustomRoles role) => OnlySpawnsWithPetsRoleList.Contains(role);
public static bool NeedUpdateOnLights(this CustomRoles role) => (!role.UsesPetInsteadOfKill()) && (role.GetDYRole() != RoleTypes.GuardianAngel || role is
CustomRoles.Convict or
@@ -734,6 +747,10 @@ internal static class CustomRolesHelper
CustomRoles.Pickpocket or
CustomRoles.TicketsStealer;
public static bool ForceCancelShapeshift(this CustomRoles role) => role is
CustomRoles.Hangman or
CustomRoles.Generator;
public static bool CheckAddonConflictV2(CustomRoles addon, CustomRoles mainRole) => addon.IsAdditionRole() && mainRole is not CustomRoles.GuardianAngelEHR and not CustomRoles.God and not CustomRoles.GM && addon switch
{
CustomRoles.Autopsy when mainRole is CustomRoles.Doctor or CustomRoles.Tracefinder or CustomRoles.Scientist or CustomRoles.ScientistEHR or CustomRoles.Sunnyboy => false,
@@ -1168,8 +1185,10 @@ internal static class CustomRolesHelper
CustomRoles.Refugee => CountTypes.Impostor,
CustomRoles.Glitch => CountTypes.Glitch,
CustomRoles.Spiritcaller => CountTypes.Spiritcaller,
CustomRoles.Cherokious => CountTypes.Cherokious,
CustomRoles.DarkHide => DarkHide.SnatchesWin.GetBool() ? CountTypes.Crew : CountTypes.DarkHide,
CustomRoles.Arsonist => !Options.ArsonistCanIgniteAnytime.GetBool() ? CountTypes.Crew : CountTypes.Arsonist,
CustomRoles.SchrodingersCat => SchrodingersCat.WinsWithCrewIfNotAttacked.GetBool() ? CountTypes.Crew : CountTypes.OutOfGame,
_ => role.Is(Team.Impostor) ? CountTypes.Impostor : CountTypes.Crew
};
@@ -1318,5 +1337,6 @@ public enum CountTypes
Pestilence,
PlagueBearer,
Glitch,
Arsonist
Arsonist,
Cherokious
}
+24 -14
View File
@@ -86,23 +86,33 @@ namespace EHR.Modules
{
if (CustomTeams.Count == 0 || CustomTeamPlayerIds.Count == 0) return false;
CustomTeamPlayerIds.Values.Do(x => x.RemoveWhere(p =>
try
{
var pc = Utils.GetPlayerById(p);
return pc == null || !pc.IsAlive() || pc.Data.Disconnected;
}));
var team = CustomTeamPlayerIds.Keys.First();
if (CustomTeamPlayerIds.Count == 1 && Main.AllAlivePlayerControls.All(x =>
CustomTeamPlayerIds.Do(x => x.Value.RemoveWhere(p =>
{
var customTeam = GetCustomTeam(x.PlayerId);
return customTeam != null && customTeam.Equals(team);
}))
var pc = Utils.GetPlayerById(p);
return pc == null || pc.Data.Disconnected;
}));
var aliveTeamPlayers = CustomTeamPlayerIds.ToDictionary(x => x.Key, x => x.Value);
aliveTeamPlayers.Do(x => x.Value.RemoveWhere(p => !Utils.GetPlayerById(p).IsAlive()));
var team = aliveTeamPlayers.Keys.First();
if (aliveTeamPlayers.Count == 1 && Main.AllAlivePlayerControls.All(x =>
{
var customTeam = GetCustomTeam(x.PlayerId);
return customTeam != null && customTeam.Equals(team);
}))
{
WinnerTeam = team;
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.CustomTeam);
CustomWinnerHolder.WinnerIds = aliveTeamPlayers.Values.First();
return true;
}
}
catch
{
WinnerTeam = team;
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.CustomTeam);
CustomWinnerHolder.WinnerIds = CustomTeamPlayerIds.Values.First();
return true;
return false;
}
return false;
+1 -1
View File
@@ -25,7 +25,7 @@ public static class DevManager
public static void Init()
{
// Dev
DevUserList.Add(new(code: "actorour#0029", color: "#ffc0cb", tag: "EHR/TONX Developer", isUp: true, isDev: true, deBug: true, colorCmd: true, upName: "KARPED1EM"));
DevUserList.Add(new(code: "actorour#0029", color: "#ffc0cb", tag: "TOHE/TONX Developer", isUp: true, isDev: true, deBug: true, colorCmd: true, upName: "KARPED1EM"));
DevUserList.Add(new(code: "pinklaze#1776", color: "#30548e", tag: "#Dev", isUp: true, isDev: true, deBug: true, colorCmd: false, upName: "NCSIMON"));
DevUserList.Add(new(code: "keepchirpy#6354", color: "#1FF3C6", tag: "Переводчик", isUp: true, isDev: false, deBug: false, colorCmd: true, upName: "TommyXL")); //Tommy-XL
DevUserList.Add(new(code: "taskunsold#2701", color: "null", tag: "<color=#426798>Tem</color><color=#f6e509>mie</color>", isUp: false, isDev: true, deBug: false, colorCmd: false, upName: null)); //Tem
+13 -7
View File
@@ -323,15 +323,21 @@ internal class EAC
if (amount is not 64 and not 65) goto YesCheat;
break;
case SystemTypes.Comms:
if (amount == 0)
switch (amount)
{
if (Mapid is 1 or 5) goto YesCheat;
case 0:
{
if (Mapid is 1 or 5) goto YesCheat;
break;
}
case 64 or 65 or 32 or 33 or 16 or 17:
{
if (Mapid is not (1 or 5)) goto YesCheat;
break;
}
default:
goto YesCheat;
}
else if (amount is 64 or 65 or 32 or 33 or 16 or 17)
{
if (!(Mapid is 1 or 5)) goto YesCheat;
}
else goto YesCheat;
break;
case SystemTypes.Electrical:
+12 -11
View File
@@ -256,22 +256,23 @@ static class ExtendedPlayerControl
public static float GetAbilityUseLimit(this PlayerControl pc) => Main.AbilityUseLimit.GetValueOrDefault(pc.PlayerId, float.NaN);
public static float GetAbilityUseLimit(this byte playerId) => Main.AbilityUseLimit.GetValueOrDefault(playerId, float.NaN);
public static void RpcRemoveAbilityUse(this PlayerControl pc)
public static void RpcRemoveAbilityUse(this PlayerControl pc, bool log = true)
{
float current = pc.GetAbilityUseLimit();
if (float.IsNaN(current) || current <= 0f) return;
pc.SetAbilityUseLimit(current - 1);
pc.SetAbilityUseLimit(current - 1, log: log);
}
public static void RpcIncreaseAbilityUseLimitBy(this PlayerControl pc, float get)
public static void RpcIncreaseAbilityUseLimitBy(this PlayerControl pc, float get, bool log = true)
{
float current = pc.GetAbilityUseLimit();
if (float.IsNaN(current)) return;
pc.SetAbilityUseLimit(current + get);
pc.SetAbilityUseLimit(current + get, log: log);
}
public static void SetAbilityUseLimit(this PlayerControl pc, float limit, bool rpc = true) => pc.PlayerId.SetAbilityUseLimit(limit, rpc);
public static void SetAbilityUseLimit(this PlayerControl pc, float limit, bool rpc = true, bool log = true) => pc.PlayerId.SetAbilityUseLimit(limit, rpc, log);
public static void SetAbilityUseLimit(this byte playerId, float limit, bool rpc = true)
public static void SetAbilityUseLimit(this byte playerId, float limit, bool rpc = true, bool log = true)
{
if (float.IsNaN(limit) || limit is < 0f or > 100f || (Main.AbilityUseLimit.TryGetValue(playerId, out var beforeLimit) && Math.Abs(beforeLimit - limit) < 0.01f)) return;
@@ -287,7 +288,7 @@ static class ExtendedPlayerControl
var pc = GetPlayerById(playerId);
NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
Logger.Info($" {pc.GetNameWithRole()} => {Math.Round(limit, 1)}", "SetAbilityUseLimit");
if (log) Logger.Info($" {pc.GetNameWithRole()} => {Math.Round(limit, 1)}", "SetAbilityUseLimit");
}
public static void Suicide(this PlayerControl pc, PlayerState.DeathReason deathReason = PlayerState.DeathReason.Suicide, PlayerControl realKiller = null)
@@ -631,13 +632,13 @@ static class ExtendedPlayerControl
return pc.GetCustomRole() switch
{
//SoloKombat
// SoloKombat
CustomRoles.KB_Normal => true,
//FFA
// FFA
CustomRoles.Killer => true,
//Move And Stop
// Move And Stop
CustomRoles.Tasker => false,
//Hot Potato
// Hot Potato
CustomRoles.Potato => false,
_ => Main.PlayerStates.TryGetValue(pc.PlayerId, out var state) && state.Role.CanUseImpostorVentButton(pc),
@@ -16,41 +16,59 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
{
public static void SetDirty(byte playerId)
{
foreach (var sender in AllSenders.OfType<PlayerGameOptionsSender>().Where(sender => sender.player.PlayerId == playerId))
foreach (GameOptionsSender allSender in AllSenders)
{
sender.SetDirty();
if (allSender is PlayerGameOptionsSender sender && sender.player.PlayerId == playerId)
{
sender.SetDirty();
}
}
}
public static void SetDirtyToAll()
{
foreach (var sender in AllSenders.OfType<PlayerGameOptionsSender>())
foreach (GameOptionsSender allSender in AllSenders)
{
sender.SetDirty();
if (allSender is PlayerGameOptionsSender sender)
{
sender.SetDirty();
}
}
}
// For lights call/fix
public static void SetDirtyToAllV2()
{
foreach (var sender in AllSenders.OfType<PlayerGameOptionsSender>().Where(sender => !sender.IsDirty && sender.player.IsAlive() && (sender.player.GetCustomRole().NeedUpdateOnLights() || sender.player.Is(CustomRoles.Torch) || sender.player.Is(CustomRoles.Mare))))
foreach (GameOptionsSender allSender in AllSenders)
{
sender.SetDirty();
if (allSender is PlayerGameOptionsSender { IsDirty: false } sender && sender.player.IsAlive() && (sender.player.GetCustomRole().NeedUpdateOnLights() || sender.player.Is(CustomRoles.Torch) || sender.player.Is(CustomRoles.Mare)))
{
sender.SetDirty();
}
}
}
// For Grenadier blidning/restoring
public static void SetDirtyToAllV3()
{
foreach (var sender in AllSenders.OfType<PlayerGameOptionsSender>().Where(sender => !sender.IsDirty && sender.player.IsAlive() && ((Grenadier.GrenadierBlinding.Count > 0 && (sender.player.GetCustomRole().IsImpostor() || (sender.player.GetCustomRole().IsNeutral() && Options.GrenadierCanAffectNeutral.GetBool()))) || (Grenadier.MadGrenadierBlinding.Count > 0 && !sender.player.GetCustomRole().IsImpostorTeam() && !sender.player.Is(CustomRoles.Madmate)))))
foreach (GameOptionsSender allSender in AllSenders)
{
sender.SetDirty();
if (allSender is PlayerGameOptionsSender { IsDirty: false } sender && sender.player.IsAlive() && ((Grenadier.GrenadierBlinding.Count > 0 && (sender.player.GetCustomRole().IsImpostor() || (sender.player.GetCustomRole().IsNeutral() && Options.GrenadierCanAffectNeutral.GetBool()))) || (Grenadier.MadGrenadierBlinding.Count > 0 && !sender.player.GetCustomRole().IsImpostorTeam() && !sender.player.Is(CustomRoles.Madmate))))
{
sender.SetDirty();
}
}
}
// For players with kill buttons
public static void SetDirtyToAllV4()
{
foreach (var sender in AllSenders.OfType<PlayerGameOptionsSender>().Where(sender => !sender.IsDirty && sender.player.IsAlive() && sender.player.CanUseKillButton()).ToArray())
foreach (GameOptionsSender allSender in AllSenders)
{
sender.SetDirty();
if (allSender is PlayerGameOptionsSender { IsDirty: false } sender && sender.player.IsAlive() && sender.player.CanUseKillButton())
{
sender.SetDirty();
}
}
}
@@ -324,6 +342,13 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0);
}
if (player.IsCrewmate() && Main.PlayerStates.Values.Any(s => s.Role is Adventurer { IsEnable: true } av && av.ActiveWeapons.Contains(Adventurer.Weapon.Lantern)))
{
opt.SetVision(true);
opt.SetFloat(FloatOptionNames.CrewLightMod, 1.5f);
opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.5f);
}
if (Changeling.ChangedRole.TryGetValue(player.PlayerId, out var changed) && changed && player.GetRoleTypes() != RoleTypes.Shapeshifter)
{
AURoleOptions.ShapeshifterCooldown = 300f;
+13 -20
View File
@@ -106,24 +106,18 @@ public static class GuessManager
return true;
}
if (!pc.Is(CustomRoles.NiceGuesser))
if (!pc.Is(CustomRoles.NiceGuesser) && pc.IsCrewmate() && !Options.CrewmatesCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.Judge) && !pc.Is(CustomRoles.NiceSwapper))
{
if (pc.IsCrewmate() && !Options.CrewmatesCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.Judge) && !pc.Is(CustomRoles.NiceSwapper))
{
if (!isUI) Utils.SendMessage(GetString("GuessNotAllowed"), pc.PlayerId);
else pc.ShowPopUp(GetString("GuessNotAllowed"));
return true;
}
if (!isUI) Utils.SendMessage(GetString("GuessNotAllowed"), pc.PlayerId);
else pc.ShowPopUp(GetString("GuessNotAllowed"));
return true;
}
if (!pc.Is(CustomRoles.EvilGuesser))
if (!pc.Is(CustomRoles.EvilGuesser) && pc.GetCustomRole().IsImpostor() && !Options.ImpostorsCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.Councillor))
{
if (pc.GetCustomRole().IsImpostor() && !Options.ImpostorsCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.Councillor))
{
if (!isUI) Utils.SendMessage(GetString("GuessNotAllowed"), pc.PlayerId);
else pc.ShowPopUp(GetString("GuessNotAllowed"));
return true;
}
if (!isUI) Utils.SendMessage(GetString("GuessNotAllowed"), pc.PlayerId);
else pc.ShowPopUp(GetString("GuessNotAllowed"));
return true;
}
if (pc.IsNeutralKiller() && !Options.NeutralKillersCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser))
@@ -147,12 +141,11 @@ public static class GuessManager
return true;
case 2:
{
if (
(pc.Is(CustomRoles.NiceGuesser) && Options.GGTryHideMsg.GetBool()) ||
if ((pc.Is(CustomRoles.NiceGuesser) && Options.GGTryHideMsg.GetBool()) ||
(pc.Is(CustomRoles.EvilGuesser) && Options.EGTryHideMsg.GetBool()) ||
(pc.Is(CustomRoles.Doomsayer) && Doomsayer.DoomsayerTryHideMsg.GetBool()) ||
(pc.Is(CustomRoles.Guesser) && Options.GTryHideMsg.GetBool()) || (Options.GuesserMode.GetBool() && Options.HideGuesserCommands.GetBool())
) /*TryHideMsg();*/ ChatManager.SendPreviousMessagesToAll();
(pc.Is(CustomRoles.Guesser) && Options.GTryHideMsg.GetBool()) || (Options.GuesserMode.GetBool() && Options.HideGuesserCommands.GetBool()))
ChatManager.SendPreviousMessagesToAll();
else if (pc.AmOwner && !isUI) Utils.SendMessage(originMsg, 255, pc.GetRealName());
if (!MsgToPlayerAndRole(msg, out byte targetId, out CustomRoles role, out string error))
@@ -960,7 +953,7 @@ public static class GuessManager
CustomRoleTypes.Impostor => new(255, 25, 25, byte.MaxValue),
CustomRoleTypes.Neutral => new(255, 171, 27, byte.MaxValue),
CustomRoleTypes.Addon => new Color32(255, 154, 206, byte.MaxValue),
_ => throw new NotImplementedException(),
_ => throw new NotImplementedException()
};
Logger.Info(Teamlabel.color.ToString(), ((CustomRoleTypes)index).ToString());
Teamlabel.text = GetString("Type" + ((CustomRoleTypes)index));
@@ -1026,7 +1019,7 @@ public static class GuessManager
Pagelabel.transform.localScale *= 1.6f;
Pagelabel.autoSizeTextContainer = true;
if (!IsNext && Page <= 1) Pagebutton.GetComponent<SpriteRenderer>().color = new(1, 1, 1, 0.1f);
Pagebutton.GetComponent<PassiveButton>().OnClick.AddListener((Action)(() => ClickEvent()));
Pagebutton.GetComponent<PassiveButton>().OnClick.AddListener((Action)(ClickEvent));
PageButtons.Add(Pagebutton.GetComponent<SpriteRenderer>());
return;
+1 -6
View File
@@ -83,12 +83,7 @@ static class LocateArrow
/// <returns></returns>
public static string GetArrows(PlayerControl seer)
{
var arrows = string.Empty;
foreach (var arrowInfo in LocateArrows.Keys.Where(ai => ai.From == seer.PlayerId))
{
arrows += LocateArrows[arrowInfo];
}
return arrows;
return LocateArrows.Keys.Where(ai => ai.From == seer.PlayerId).Aggregate(string.Empty, (current, arrowInfo) => current + LocateArrows[arrowInfo]);
}
/// <summary>
/// Check target arrow every FixedUpdate
+4 -1
View File
@@ -39,6 +39,9 @@ public static class NameColorManager
switch (Options.CurrentGameMode)
{
case CustomGameMode.FFA when FFAManager.FFATeamMode.GetBool():
color = FFAManager.TeamColors[FFAManager.PlayerTeams[target.PlayerId]];
return true;
case CustomGameMode.MoveAndStop:
color = "#ffffff";
return true;
@@ -68,7 +71,7 @@ public static class NameColorManager
}
// Custom Teams
if (CustomTeamManager.AreInSameCustomTeam(seer.PlayerId, target.PlayerId)) color = Main.RoleColors[target.GetCustomRole()];
if (CustomTeamManager.AreInSameCustomTeam(seer.PlayerId, target.PlayerId) && Options.CTAPlayersCanSeeEachOthersRoles.GetBool()) color = Main.RoleColors[target.GetCustomRole()];
// Add-ons
if (seer.Is(CustomRoles.Rogue) && target.Is(CustomRoles.Rogue) && Options.RogueKnowEachOther.GetBool()) color = Main.RoleColors[CustomRoles.Rogue];
+49 -20
View File
@@ -217,6 +217,7 @@ public static class Options
public static OptionItem DisableShieldAnimations;
public static OptionItem DisableCrackedGlass;
public static OptionItem DisableShapeshiftAnimations;
public static OptionItem DisableAllShapeshiftAnimations;
public static OptionItem DisableKillAnimationOnGuess;
public static OptionItem DisableVanillaRoles;
public static OptionItem SabotageCooldownControl;
@@ -870,6 +871,9 @@ public static class Options
public static OptionItem EndWhenPlayerBug;
public static OptionItem RemovePetsAtDeadPlayers;
public static OptionItem CTAPlayersCanWinWithOriginalTeam;
public static OptionItem CTAPlayersCanSeeEachOthersRoles;
public static OptionItem UsePets;
public static OptionItem PetToAssignToEveryone;
public static OptionItem UseVoteCancelling;
@@ -929,7 +933,6 @@ public static class Options
public static OptionItem MinWaitAutoStart;
public static OptionItem MaxWaitAutoStart;
public static OptionItem PlayerAutoStart;
//public static OptionItem SidekickCountMode;
public static OptionItem DumpLogAfterGameEnd;
@@ -1185,21 +1188,21 @@ public static class Options
var tab = roleClasses.Key.GetTabFromOptionType();
var categorySuffix = roleClasses.Key switch
{
RoleOptionType.Neutral_Killing => "NK",
RoleOptionType.Neutral_NonKilling => "NNK",
_ => string.Empty
};
TextOptionItem.Create(titleId, $"ROT.Basic{categorySuffix}", tab)
.SetGameMode(CustomGameMode.Standard)
.SetColor(Color.gray)
.SetHeader(true);
titleId += 10;
var key = roleClasses.Key.GetSimpleRoleOptionType();
if (simpleRoleClasses.TryGetValue(key, out ISettingHolder[] value))
if (simpleRoleClasses.TryGetValue(key, out ISettingHolder[] value) && value.Length > 0)
{
var categorySuffix = roleClasses.Key switch
{
RoleOptionType.Neutral_Killing => "NK",
RoleOptionType.Neutral_NonKilling => "NNK",
_ => string.Empty
};
TextOptionItem.Create(titleId, $"ROT.Basic{categorySuffix}", tab)
.SetGameMode(CustomGameMode.Standard)
.SetColor(Color.gray)
.SetHeader(true);
titleId += 10;
foreach (ISettingHolder holder in value)
{
holder.SetupCustomOption();
@@ -1619,6 +1622,10 @@ public static class Options
DisableShapeshiftAnimations = BooleanOptionItem.Create(22604, "DisableShapeshiftAnimations", true, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(255, 153, 153, byte.MaxValue));
DisableAllShapeshiftAnimations = BooleanOptionItem.Create(22605, "DisableAllShapeshiftAnimations", false, TabGroup.GameSettings, false)
.SetParent(DisableShapeshiftAnimations)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(255, 153, 153, byte.MaxValue));
DisableVanillaRoles = BooleanOptionItem.Create(22600, "DisableVanillaRoles", true, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(255, 153, 153, byte.MaxValue));
@@ -1751,7 +1758,7 @@ public static class Options
.SetColor(new Color32(255, 153, 153, byte.MaxValue));
//Disable Short Tasks
// Disable Short Tasks
DisableShortTasks = BooleanOptionItem.Create(23000, "DisableShortTasks", false, TabGroup.TaskSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
@@ -1871,7 +1878,7 @@ public static class Options
LoadingPercentage = 84;
//Disable Common Tasks
// Disable Common Tasks
DisableCommonTasks = BooleanOptionItem.Create(23100, "DisableCommonTasks", false, TabGroup.TaskSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(239, 89, 175, byte.MaxValue));
@@ -1914,7 +1921,7 @@ public static class Options
LoadingPercentage = 85;
//Disable Long Tasks
// Disable Long Tasks
DisableLongTasks = BooleanOptionItem.Create(23150, "DisableLongTasks", false, TabGroup.TaskSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(239, 89, 175, byte.MaxValue));
@@ -2003,7 +2010,7 @@ public static class Options
LoadingPercentage = 89;
//Disable Divert Power, Weather Nodes etc. situational Tasks
// Disable Divert Power, Weather Nodes etc. situational Tasks
DisableOtherTasks = BooleanOptionItem.Create(23200, "DisableOtherTasks", false, TabGroup.TaskSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(239, 89, 175, byte.MaxValue));
@@ -2075,13 +2082,28 @@ public static class Options
MainLoadingText = "Building game settings";
TextOptionItem.Create(100027, "MenuTitle.Meeting", TabGroup.GameSettings)
TextOptionItem.Create(100027, "MenuTitle.CTA", TabGroup.GameSettings)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(215, 227, 84, byte.MaxValue));
CTAPlayersCanWinWithOriginalTeam = BooleanOptionItem.Create(23550, "CTA.PlayersCanWinWithOriginalTeam", false, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(215, 227, 84, byte.MaxValue));
CTAPlayersCanSeeEachOthersRoles = BooleanOptionItem.Create(23551, "CTA.PlayersCanSeeEachOthersRoles", true, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetColor(new Color32(215, 227, 84, byte.MaxValue));
TextOptionItem.Create(100037, "MenuTitle.Meeting", TabGroup.GameSettings)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(147, 241, 240, byte.MaxValue));
SyncButtonMode = BooleanOptionItem.Create(23300, "SyncButtonMode", false, TabGroup.GameSettings, false)
.SetHeader(true)
.SetColor(new Color32(147, 241, 240, byte.MaxValue))
.SetHeader(true)
.SetGameMode(CustomGameMode.Standard);
SyncedButtonCount = IntegerOptionItem.Create(23310, "SyncedButtonCount", new(0, 100, 1), 10, TabGroup.GameSettings, false)
.SetParent(SyncButtonMode)
@@ -2145,9 +2167,11 @@ public static class Options
TextOptionItem.Create(100028, "MenuTitle.Other", TabGroup.GameSettings)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(193, 255, 209, byte.MaxValue));
LadderDeath = BooleanOptionItem.Create(23800, "LadderDeath", false, TabGroup.GameSettings, false)
.SetHeader(true)
.SetColor(new Color32(193, 255, 209, byte.MaxValue));
LadderDeathChance = StringOptionItem.Create(23810, "LadderDeathChance", Rates[1..], 0, TabGroup.GameSettings, false)
.SetParent(LadderDeath);
@@ -2181,6 +2205,7 @@ public static class Options
TextOptionItem.Create(100029, "MenuTitle.Ghost", TabGroup.GameSettings)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(217, 218, 255, byte.MaxValue));
LoadingPercentage = 99;
@@ -2188,6 +2213,7 @@ public static class Options
GhostCanSeeOtherRoles = BooleanOptionItem.Create(24300, "GhostCanSeeOtherRoles", true, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
.SetHeader(true)
.SetColor(new Color32(217, 218, 255, byte.MaxValue));
GhostCanSeeOtherVotes = BooleanOptionItem.Create(24400, "GhostCanSeeOtherVotes", true, TabGroup.GameSettings, false)
.SetGameMode(CustomGameMode.Standard)
@@ -2244,6 +2270,8 @@ public static class Options
CustomRoleCounts.Add(role, countOption);
}
public static HashSet<CustomRoles> SingleRoles = [];
public static void SetupSingleRoleOptions(int id, TabGroup tab, CustomRoles role, int count = 1, CustomGameMode customGameMode = CustomGameMode.Standard, bool zeroOne = false, bool hideMaxSetting = true)
{
var spawnOption = StringOptionItem.Create(id, role.ToString(), zeroOne ? RatesZeroOne : Rates, 0, tab, false).SetColor(Utils.GetRoleColor(role))
@@ -2257,6 +2285,7 @@ public static class Options
CustomRoleSpawnChances.Add(role, spawnOption);
CustomRoleCounts.Add(role, countOption);
SingleRoles.Add(role);
}
public static OptionItem CreateCDSetting(int id, TabGroup tab, CustomRoles role, bool isKCD = false) =>
+61 -32
View File
@@ -42,9 +42,9 @@ other: ∟ ⌠ ⌡ ╬ ╨ ▓ ▒ ░ « » █ ▄ ▌▀▐│ ┤ ╡ ╢
public static class Utils
{
private static readonly DateTime timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static long GetTimeStamp(DateTime? dateTime = null) => (long)((dateTime ?? DateTime.Now).ToUniversalTime() - timeStampStartTime).TotalSeconds;
public static long TimeStamp => GetTimeStamp();
private static readonly DateTime TimeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static long GetTimeStamp(DateTime? dateTime = null) => (long)((dateTime ?? DateTime.Now).ToUniversalTime() - TimeStampStartTime).TotalSeconds;
public static long TimeStamp => (long)(DateTime.Now.ToUniversalTime() - TimeStampStartTime).TotalSeconds;
public static void ErrorEnd(string text)
{
@@ -528,6 +528,7 @@ public static class Utils
case CustomRoles.Doppelganger:
case CustomRoles.PlagueDoctor:
case CustomRoles.Postman:
case CustomRoles.SchrodingersCat:
case CustomRoles.Impartial:
case CustomRoles.Predator:
case CustomRoles.Reckless:
@@ -586,7 +587,7 @@ public static class Utils
case CustomRoles.Escort when !Options.UsePets.GetBool() || !Escort.UsePet.GetBool():
case CustomRoles.DonutDelivery when !Options.UsePets.GetBool() || !DonutDelivery.UsePet.GetBool():
case CustomRoles.Gaulois when !Options.UsePets.GetBool() || !Gaulois.UsePet.GetBool():
case CustomRoles.Analyzer when !Options.UsePets.GetBool() || !Analyzer.UsePet.GetBool():
case CustomRoles.Analyst when !Options.UsePets.GetBool() || !Analyst.UsePet.GetBool():
case CustomRoles.Witness when !Options.UsePets.GetBool() || !Options.WitnessUsePet.GetBool():
case CustomRoles.Pursuer:
case CustomRoles.Spiritcaller:
@@ -606,6 +607,7 @@ public static class Utils
if (ForRecompute)
hasTasks = false;
break;
case CustomRoles.Cherokious:
case CustomRoles.Crewpostor:
if (ForRecompute && !p.IsDead)
hasTasks = false;
@@ -703,7 +705,8 @@ public static class Utils
__instance.Is(CustomRoleTypes.Impostor) && PlayerControl.LocalPlayer.Is(CustomRoles.Crewpostor) && Options.AlliesKnowCrewpostor.GetBool() ||
__instance.Is(CustomRoleTypes.Impostor) && PlayerControl.LocalPlayer.Is(CustomRoleTypes.Impostor) && Options.ImpKnowAlliesRole.GetBool() ||
__instance.Is(CustomRoleTypes.Impostor) && PlayerControl.LocalPlayer.Is(CustomRoles.Madmate) && Options.MadmateKnowWhosImp.GetBool() ||
CustomTeamManager.AreInSameCustomTeam(__instance.PlayerId, PlayerControl.LocalPlayer.PlayerId) ||
CustomTeamManager.AreInSameCustomTeam(__instance.PlayerId, PlayerControl.LocalPlayer.PlayerId) && Options.CTAPlayersCanSeeEachOthersRoles.GetBool() ||
Adventurer.KnowRole(PlayerControl.LocalPlayer, __instance) ||
Totocalcio.KnowRole(PlayerControl.LocalPlayer, __instance) ||
Romantic.KnowRole(PlayerControl.LocalPlayer, __instance) ||
Lawyer.KnowRole(PlayerControl.LocalPlayer, __instance) ||
@@ -716,7 +719,6 @@ public static class Utils
PlayerControl.LocalPlayer.IsRevealedPlayer(__instance) ||
PlayerControl.LocalPlayer.Is(CustomRoles.God) ||
PlayerControl.LocalPlayer.Is(CustomRoles.GM) ||
Totocalcio.KnowRole(PlayerControl.LocalPlayer, __instance) ||
Lawyer.KnowRole(PlayerControl.LocalPlayer, __instance) ||
EvilDiviner.IsShowTargetRole(PlayerControl.LocalPlayer, __instance) ||
Executioner.KnowRole(PlayerControl.LocalPlayer, __instance) ||
@@ -805,8 +807,15 @@ public static class Utils
NonCompleteColor = Workhorse.RoleColor;
var NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
if (Main.PlayerStates.TryGetValue(playerId, out var ps) && ps.MainRole == CustomRoles.Crewpostor)
NormalColor = Color.red;
if (Main.PlayerStates.TryGetValue(playerId, out var ps))
{
NormalColor = ps.MainRole switch
{
CustomRoles.Crewpostor => Color.red,
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
_ => NormalColor
};
}
Color TextColor = comms ? Color.gray : NormalColor;
string Completed = comms ? "?" : $"{taskState.CompletedTasksCount}";
@@ -1587,7 +1596,7 @@ public static class Utils
public static void ApplySuffix(PlayerControl player)
{
if (!AmongUsClient.Instance.AmHost || player == null) return;
if (!(player.AmOwner || player.FriendCode.GetDevUser().HasTag())) return;
if (!player.AmOwner && !player.FriendCode.GetDevUser().HasTag() && !ChatCommands.IsPlayerModerator(player.FriendCode)) return;
string name = Main.AllPlayerNames.TryGetValue(player.PlayerId, out var n) ? n : string.Empty;
if (Main.NickName != string.Empty && player.AmOwner) name = Main.NickName;
if (name == string.Empty) return;
@@ -1620,6 +1629,7 @@ public static class Utils
if (devUser.HasTag() || isMod)
{
string tag = devUser.GetTag();
if (tag == "null") tag = string.Empty;
if (player.AmOwner || player.IsModClient())
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;
@@ -1786,6 +1796,8 @@ public static class Utils
if (Options.CurrentGameMode != CustomGameMode.Standard) goto GameMode;
SelfSuffix.Append(Adventurer.GetSuffixAndHUDText(seer, isForMeeting: isForMeeting));
if (!isForMeeting)
{
if (Options.UsePets.GetBool() && Main.AbilityCD.TryGetValue(seer.PlayerId, out var time) && !seer.IsModClient())
@@ -1799,6 +1811,7 @@ public static class Utils
SelfSuffix.Append(Deathpact.GetDeathpactPlayerArrow(seer));
SelfSuffix.Append(Commander.GetSuffixText(seer, seer));
SelfSuffix.Append(AntiAdminer.GetSuffixText(seer));
SelfSuffix.Append(Roles.Impostor.Sentry.GetSuffix(seer));
switch (seer.GetCustomRole())
{
@@ -1913,7 +1926,6 @@ public static class Utils
{
case CustomGameMode.FFA:
SelfSuffix.Append(FFAManager.GetPlayerArrow(seer));
if (FFAManager.FFAChatDuringGame.GetBool()) SelfSuffix.Append(SelfSuffix.Length > 0 && FFAManager.LatestChatMessage != string.Empty ? "\n" : string.Empty).Append(FFAManager.LatestChatMessage);
break;
case CustomGameMode.SoloKombat:
SelfSuffix.Append(SoloKombatManager.GetDisplayHealth(seer));
@@ -2028,7 +2040,7 @@ public static class Utils
seer.RpcSetNamePrivate(SelfName, true, force: NoCache);
// Run the second loop only when necessary, such as when seer is dead
if (seer.Data.IsDead || !seer.IsAlive() || NoCache || CamouflageIsForMeeting || MushroomMixup || IsActive(SystemTypes.MushroomMixupSabotage) || ForceLoop || seerList.Length == 1 || targetList.Length == 1)
if (GameStates.IsLobby || seer.Data.IsDead || !seer.IsAlive() || NoCache || CamouflageIsForMeeting || MushroomMixup || IsActive(SystemTypes.MushroomMixupSabotage) || ForceLoop || seerList.Length == 1 || targetList.Length == 1)
{
foreach (PlayerControl target in targetList)
{
@@ -2104,10 +2116,10 @@ public static class Utils
}
break;
case CustomRoles.Analyzer:
if ((Main.PlayerStates[seer.PlayerId].Role as Analyzer).CurrentTarget.ID == target.PlayerId)
case CustomRoles.Analyst:
if ((Main.PlayerStates[seer.PlayerId].Role as Analyst).CurrentTarget.ID == target.PlayerId)
{
TargetMark.Append($"<color={GetRoleColorCode(CustomRoles.Analyzer)}>○</color>");
TargetMark.Append($"<color={GetRoleColorCode(CustomRoles.Analyst)}>○</color>");
}
break;
@@ -2137,7 +2149,8 @@ public static class Utils
(target.Is(CustomRoles.Mayor) && Mayor.MayorRevealWhenDoneTasks.GetBool() && target.GetTaskState().IsTaskFinished) ||
(seer.Is(CustomRoleTypes.Crewmate) && target.Is(CustomRoles.Marshall) && target.GetTaskState().IsTaskFinished) ||
(Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Vote && Options.SeeEjectedRolesInMeeting.GetBool()) ||
CustomTeamManager.AreInSameCustomTeam(seer.PlayerId, target.PlayerId) ||
CustomTeamManager.AreInSameCustomTeam(seer.PlayerId, target.PlayerId) && Options.CTAPlayersCanSeeEachOthersRoles.GetBool() ||
Adventurer.KnowRole(seer, target) ||
Totocalcio.KnowRole(seer, target) ||
Romantic.KnowRole(seer, target) ||
Lawyer.KnowRole(seer, target) ||
@@ -2389,6 +2402,7 @@ public static class Utils
CustomRoles.Tornado => Tornado.TornadoCooldown.GetInt(),
CustomRoles.Sentinel => Sentinel.PatrolCooldown.GetInt(),
CustomRoles.Druid => Druid.VentCooldown.GetInt(),
CustomRoles.Sentry => Roles.Impostor.Sentry.ShowInfoCooldown.GetInt(),
CustomRoles.Sniper => Options.DefaultShapeshiftCooldown.GetInt(),
CustomRoles.Assassin => Assassin.AssassinateCooldownOpt.GetInt(),
CustomRoles.Undertaker => Undertaker.UndertakerAssassinateCooldown.GetInt(),
@@ -2403,6 +2417,7 @@ public static class Utils
CustomRoles.Warlock => Warlock.IsCursed ? -1 : (int)Options.DefaultKillCooldown,
CustomRoles.Swiftclaw => Swiftclaw.DashCD.GetInt() + (includeDuration ? Swiftclaw.DashDuration.GetInt() : 0),
CustomRoles.Tiger => Tiger.EnrageCooldown.GetInt() + (includeDuration ? Tiger.EnrageDuration.GetInt() : 0),
CustomRoles.Cherokious => Cherokious.KillCooldown.GetInt(),
_ => -1,
};
if (CD == -1) return;
@@ -2412,25 +2427,30 @@ public static class Utils
public static void AfterMeetingTasks()
{
foreach (var pc in Main.AllAlivePlayerControls)
foreach (var pc in Main.AllPlayerControls)
{
pc.AddKillTimerToDict();
if (pc.Is(CustomRoles.Truant))
if (pc.IsAlive())
{
float beforeSpeed = Main.AllPlayerSpeed[pc.PlayerId];
Main.AllPlayerSpeed[pc.PlayerId] = Main.MinSpeed;
pc.MarkDirtySettings();
_ = new LateTask(() =>
{
Main.AllPlayerSpeed[pc.PlayerId] = beforeSpeed;
pc.MarkDirtySettings();
}, Options.TruantWaitingTime.GetFloat(), $"Truant Waiting: {pc.GetNameWithRole()}");
pc.AddKillTimerToDict();
if (pc.Is(CustomRoles.Truant))
{
float beforeSpeed = Main.AllPlayerSpeed[pc.PlayerId];
Main.AllPlayerSpeed[pc.PlayerId] = Main.MinSpeed;
pc.MarkDirtySettings();
_ = new LateTask(() =>
{
Main.AllPlayerSpeed[pc.PlayerId] = beforeSpeed;
pc.MarkDirtySettings();
}, Options.TruantWaitingTime.GetFloat(), $"Truant Waiting: {pc.GetNameWithRole()}");
}
if (Options.UsePets.GetBool()) pc.AddAbilityCD(includeDuration: false);
Main.PlayerStates[pc.PlayerId].Role.AfterMeetingTasks();
}
if (Options.UsePets.GetBool()) pc.AddAbilityCD(includeDuration: false);
Main.PlayerStates[pc.PlayerId].Role.AfterMeetingTasks();
if (pc.Is(CustomRoles.Specter) || pc.Is(CustomRoles.Haunter)) pc.RpcResetAbilityCooldown();
}
CopyCat.ResetRole();
@@ -2555,6 +2575,8 @@ public static class Utils
Tracefinder.OnPlayerDead(target);
Vulture.OnPlayerDead(target);
Tracker.OnPlayerDeath(target);
Adventurer.OnAnyoneDead(target);
Roles.Impostor.Sentry.OnAnyoneMurder(target);
FixedUpdatePatch.LoversSuicide(target.PlayerId, onMeeting);
}
@@ -2682,8 +2704,15 @@ public static class Utils
NonCompleteColor = Workhorse.RoleColor;
var NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
if (Main.PlayerStates.TryGetValue(id, out var ps) && ps.MainRole == CustomRoles.Crewpostor)
NormalColor = Color.red;
if (Main.PlayerStates.TryGetValue(id, out var ps))
{
NormalColor = ps.MainRole switch
{
CustomRoles.Crewpostor => Color.red,
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
_ => NormalColor
};
}
Color TextColor = NormalColor;
string Completed = $"{taskState.CompletedTasksCount}";
+5 -6
View File
@@ -297,7 +297,7 @@ internal class ChatCommands
case "ban":
if (GetRoleByName(args[2], out CustomRoles mainRole) && GetRoleByName(args[3], out CustomRoles addOn))
{
if (mainRole.IsAdditionRole() || !addOn.IsAdditionRole()) break;
if (mainRole.IsAdditionRole() || !addOn.IsAdditionRole() || addOn == CustomRoles.Lovers) break;
if (args[1] == "add") Main.AlwaysSpawnTogetherCombos[mainRole] = addOn;
else Main.NeverSpawnTogetherCombos[mainRole] = addOn;
Utils.SendMessage(string.Format(args[1] == "add" ? GetString("ComboAdd") : GetString("ComboBan"), GetString(mainRole.ToString()), GetString(addOn.ToString())), localPlayerId);
@@ -353,7 +353,7 @@ internal class ChatCommands
settings.Append("</size>");
var txt = sb.ToString();
_ = sb.Clear().Append(txt.RemoveHtmlTags());
if (role.PetActivatedAbility()) sb.Append("<size=50%>" + GetString("SupportsPetMessage").RemoveHtmlTags() + "</size>");
if (role.PetActivatedAbility() && sb.Length < 1000) sb.Append("<size=50%>" + GetString("SupportsPetMessage").RemoveHtmlTags() + "</size>");
sb.Append("<size=70%>");
foreach (CustomRoles subRole in Main.PlayerStates[localPlayerId].SubRoles.ToArray())
{
@@ -939,7 +939,7 @@ internal class ChatCommands
_ = sb.Clear().Append(txt);
}
if (rl.PetActivatedAbility()) sb.Append($"<size=50%>{GetString("SupportsPetMessage")}</size>");
if (rl.PetActivatedAbility() && sb.Length < 1000) sb.Append($"<size=50%>{GetString("SupportsPetMessage")}</size>");
Utils.SendMessage(text: "\n", sendTo: playerId, title: settings.ToString());
Utils.SendMessage(text: sb.ToString(), sendTo: playerId, title: title);
return;
@@ -968,8 +968,7 @@ internal class ChatCommands
if (GuessManager.GuesserMsg(player, text))
{
if (GuessManager.GuesserMsg(player, text) ||
Judge.TrialMsg(player, text) ||
if (Judge.TrialMsg(player, text) ||
NiceSwapper.SwapMsg(player, text) ||
ParityCop.ParityCheckMsg(player, text) ||
Councillor.MurderMsg(player, text))
@@ -1045,7 +1044,7 @@ internal class ChatCommands
settings.Append("</size>");
var txt = sb.ToString();
_ = sb.Clear().Append(txt.RemoveHtmlTags());
if (role.PetActivatedAbility()) sb.Append("<size=50%>" + GetString("SupportsPetMessage").RemoveHtmlTags() + "</size>");
if (role.PetActivatedAbility() && sb.Length < 1000) sb.Append("<size=50%>" + GetString("SupportsPetMessage").RemoveHtmlTags() + "</size>");
sb.Append("<size=70%>");
foreach (CustomRoles subRole in Main.PlayerStates[player.PlayerId].SubRoles.ToArray())
{
+28 -7
View File
@@ -183,6 +183,10 @@ class GameEndChecker
WinnerIds.Add(pc.PlayerId);
AdditionalWinnerTeams.Add(AdditionalWinners.SoulHunter);
break;
case CustomRoles.SchrodingersCat when SchrodingersCat.WinsWithCrewIfNotAttacked.GetBool():
WinnerIds.Add(pc.PlayerId);
AdditionalWinnerTeams.Add(AdditionalWinners.SchrodingersCat);
break;
}
}
@@ -218,7 +222,7 @@ class GameEndChecker
.Do(p => WinnerIds.Add(p.PlayerId));
}
if (WinnerTeam != CustomWinner.CustomTeam)
if (WinnerTeam != CustomWinner.CustomTeam && !Options.CTAPlayersCanWinWithOriginalTeam.GetBool())
{
WinnerIds.RemoveWhere(x => CustomTeamManager.GetCustomTeam(x) != null);
}
@@ -282,7 +286,7 @@ class GameEndChecker
continue;
}
bool canWin = WinnerIds.Contains(pc.PlayerId) || WinnerRoles.Contains(pc.GetCustomRole());
bool canWin = WinnerIds.Contains(pc.PlayerId) || WinnerRoles.Contains(pc.GetCustomRole()) || (winner == CustomWinner.Bloodlust && pc.Is(CustomRoles.Bloodlust));
bool isCrewmateWin = reason.Equals(GameOverReason.HumansByVote) || reason.Equals(GameOverReason.HumansByTask);
SetGhostRole(ToGhostImpostor: canWin ^ isCrewmateWin); // XOR
@@ -515,21 +519,38 @@ class GameEndChecker
Logger.Warn($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "FFA");
WinnerIds =
[
winnerId
];
WinnerIds = [winnerId];
Main.DoBlockNameChange = true;
return true;
}
if (FFAManager.FFATeamMode.GetBool())
{
var teams = FFAManager.PlayerTeams.GroupBy(x => x.Value, x => x.Key).Select(x => x.Where(p =>
{
var pc = GetPlayerById(p);
return pc != null && !pc.Data.Disconnected;
}).ToHashSet()).Where(x => x.Count > 0);
foreach (var team in teams)
{
if (Main.AllAlivePlayerControls.All(x => team.Contains(x.PlayerId)))
{
WinnerIds = team;
Main.DoBlockNameChange = true;
return true;
}
}
}
switch (Main.AllAlivePlayerControls.Length)
{
case 1:
{
var winner = Main.AllAlivePlayerControls.First();
var winner = Main.AllAlivePlayerControls[0];
Logger.Info($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "FFA");
+3 -4
View File
@@ -203,7 +203,7 @@ class HudManagerPatch
case CustomRoles.Pelican:
__instance.KillButton?.OverrideText(GetString("PelicanButtonText"));
break;
case CustomRoles.Analyzer:
case CustomRoles.Analyst:
usedButton?.OverrideText(GetString("AnalyzerKillButtonText"));
break;
case CustomRoles.Pursuer:
@@ -377,6 +377,7 @@ class HudManagerPatch
CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Chameleon => Swooper.GetHudText(player),
CustomRoles.HeadHunter => HeadHunter.GetHudText(player),
CustomRoles.Alchemist => Alchemist.GetHudText(player),
CustomRoles.Adventurer => Adventurer.GetSuffixAndHUDText(player, hud: true),
CustomRoles.Werewolf => Werewolf.GetHudText(player),
CustomRoles.Glitch => Glitch.GetHudText(player),
CustomRoles.NiceHacker => NiceHacker.GetHudText(player),
@@ -568,8 +569,6 @@ class SetHudActivePatch
case CustomRoles.SwordsMan:
case CustomRoles.Deputy:
case CustomRoles.Monarch:
//case CustomRoles.NWitch:
//case CustomRoles.Reverie:
case CustomRoles.Pelican:
case CustomRoles.FFF:
case CustomRoles.Medic:
@@ -773,7 +772,7 @@ class TaskPanelBehaviourPatch
List<(int, byte)> list3 = [];
foreach (var id in Main.PlayerStates.Keys) list3.Add((MoveAndStopManager.GetRankOfScore(id), id));
list3.Sort();
list3 = [.. list3.OrderBy(x => Utils.GetPlayerById(x.Item2).IsAlive())];
list3 = [.. list3.OrderBy(x => !Utils.GetPlayerById(x.Item2).IsAlive())];
foreach (var id in list3.Where(x => SummaryText3.ContainsKey(x.Item2)).ToArray())
{
bool alive = Utils.GetPlayerById(id.Item2).IsAlive();
+1 -1
View File
@@ -238,7 +238,7 @@ public static class HudSpritePatch
else
newPetButton = CustomButton.Get("Mine");
break;
case CustomRoles.Analyzer:
case CustomRoles.Analyst:
case CustomRoles.Witness:
newKillButton = CustomButton.Get("Examine");
break;
+23 -1
View File
@@ -182,6 +182,7 @@ class BeginCrewmatePatch
var team = CustomTeamManager.GetCustomTeam(PlayerControl.LocalPlayer.PlayerId);
if (team != null)
{
teamToDisplay = new();
foreach (var pc in Main.AllPlayerControls)
{
if (CustomTeamManager.AreInSameCustomTeam(pc.PlayerId, PlayerControl.LocalPlayer.PlayerId))
@@ -192,6 +193,18 @@ class BeginCrewmatePatch
}
}
if (Options.CurrentGameMode == CustomGameMode.FFA && FFAManager.FFATeamMode.GetBool() && FFAManager.PlayerTeams.TryGetValue(PlayerControl.LocalPlayer.PlayerId, out var ffaTeam))
{
teamToDisplay = new();
foreach (var pc in Main.AllPlayerControls)
{
if (FFAManager.PlayerTeams.TryGetValue(pc.PlayerId, out var team) && team == ffaTeam)
{
teamToDisplay.Add(pc);
}
}
}
return true;
}
@@ -441,33 +454,42 @@ class BeginCrewmatePatch
break;
}
case CustomGameMode.FFA:
{
__instance.TeamTitle.text = GetString("Killer");
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(0, 255, 255, byte.MaxValue);
var color = FFAManager.PlayerTeams.TryGetValue(PlayerControl.LocalPlayer.PlayerId, out var team) && ColorUtility.TryParseHtmlString(FFAManager.TeamColors[team], out var teamColor) ? teamColor : new(0, 255, 255, byte.MaxValue);
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = color;
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Shapeshifter);
__instance.ImpostorText.gameObject.SetActive(true);
__instance.ImpostorText.text = GetString("KillerInfo");
break;
}
case CustomGameMode.MoveAndStop:
{
__instance.TeamTitle.text = GetString("MoveAndStop");
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(0, 255, 160, byte.MaxValue);
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Shapeshifter);
__instance.ImpostorText.gameObject.SetActive(true);
__instance.ImpostorText.text = GetString("TaskerInfo");
break;
}
case CustomGameMode.HotPotato:
{
__instance.TeamTitle.text = GetString("HotPotato");
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(232, 205, 70, byte.MaxValue);
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Shapeshifter);
__instance.ImpostorText.gameObject.SetActive(true);
__instance.ImpostorText.text = GetString("PotatoInfo");
break;
}
case CustomGameMode.HideAndSeek:
{
__instance.TeamTitle.text = GetString("HideAndSeek");
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(52, 94, 235, byte.MaxValue);
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Impostor);
__instance.ImpostorText.gameObject.SetActive(true);
__instance.ImpostorText.text = GetString("SubText.HideAndSeek");
break;
}
}
if (Input.GetKey(KeyCode.RightShift))
+29 -11
View File
@@ -121,7 +121,7 @@ class CheckForEndVotingPatch
}
}
if (__instance.playerStates.TakeWhile(_ => !shouldSkip).Any(ps => !(Main.PlayerStates[ps.TargetPlayerId].IsDead || ps.DidVote)))
if (!shouldSkip && !__instance.playerStates.All(ps => Main.PlayerStates[ps.TargetPlayerId].IsDead || ps.DidVote || Utils.GetPlayerById(ps.TargetPlayerId).Data.Disconnected))
{
return false;
}
@@ -132,7 +132,7 @@ class CheckForEndVotingPatch
foreach (var ps in __instance.playerStates)
{
if (ps == null) continue;
voteLog.Info($"{ps.TargetPlayerId,-2}{$"({Utils.GetVoteName(ps.TargetPlayerId)})".PadRightV2(40)}:{ps.VotedFor,-3}{$"({Utils.GetVoteName(ps.VotedFor)})"}");
voteLog.Info($"{ps.TargetPlayerId,-2}{$"({Utils.GetVoteName(ps.TargetPlayerId)})".PadRightV2(40)}:{ps.VotedFor,-3}({Utils.GetVoteName(ps.VotedFor)})");
var voter = Utils.GetPlayerById(ps.TargetPlayerId);
if (voter == null || voter.Data == null || voter.Data.Disconnected) continue;
if (Options.VoteMode.GetBool())
@@ -195,9 +195,6 @@ class CheckForEndVotingPatch
VotedForId = ps.VotedFor
});
Blackmailer.OnCheckForEndVoting();
NiceSwapper.OnCheckForEndVoting();
if (CheckRole(ps.TargetPlayerId, CustomRoles.Mayor) && !Mayor.MayorHideVote.GetBool()) //Mayorの投票数
{
for (var i2 = 0; i2 < Mayor.MayorAdditionalVote.GetFloat(); i2++)
@@ -221,8 +218,20 @@ class CheckForEndVotingPatch
});
}
}
if (Main.PlayerStates[ps.TargetPlayerId].Role is Adventurer { IsEnable: true } av && av.ActiveWeapons.Contains(Adventurer.Weapon.Proxy))
{
statesList.Add(new()
{
VoterId = ps.TargetPlayerId,
VotedForId = ps.VotedFor
});
}
}
Blackmailer.OnCheckForEndVoting();
NiceSwapper.OnCheckForEndVoting();
states = [.. statesList];
var VotingData = __instance.CustomCalculateVotes();
@@ -308,13 +317,13 @@ class CheckForEndVotingPatch
exiledPlayer?.Object.SetRealKiller(null);
//RPC
// RPC
if (AntiBlackout.OverrideExiledPlayer)
{
__instance.RpcVotingComplete(states.ToArray(), null, true);
ExileControllerWrapUpPatch.AntiBlackout_LastExiled = exiledPlayer;
}
else __instance.RpcVotingComplete(states.ToArray(), exiledPlayer, tie); //通常処理
else __instance.RpcVotingComplete(states.ToArray(), exiledPlayer, tie);
CheckForDeathOnExile(PlayerState.DeathReason.Vote, exileId);
@@ -387,7 +396,10 @@ class CheckForEndVotingPatch
if (Options.ShowTeamNextToRoleNameOnEject.GetBool())
{
name += " (";
if (player.GetCustomRole().IsImpostor() || player.Is(CustomRoles.Madmate))
var team = CustomTeamManager.GetCustomTeam(player.PlayerId);
if (team != null)
name += Utils.ColorString(team.RoleRevealScreenBackgroundColor == "*" || !ColorUtility.TryParseHtmlString(team.RoleRevealScreenBackgroundColor, out var color) ? Color.yellow : color, team.RoleRevealScreenTitle == "*" ? team.TeamName : team.RoleRevealScreenTitle);
else if (player.GetCustomRole().IsImpostor() || player.Is(CustomRoles.Madmate))
name += Utils.ColorString(new(255, 25, 25, byte.MaxValue), GetString("TeamImpostor"));
else if (player.GetCustomRole().IsNeutral() || player.Is(CustomRoles.Charmed))
name += Utils.ColorString(new(255, 171, 27, byte.MaxValue), GetString("TeamNeutral"));
@@ -584,6 +596,11 @@ static class ExtendedMeetingHud
) VoteNum += VoteNum;
}
if (Main.PlayerStates[ps.TargetPlayerId].Role is Adventurer { IsEnable: true } av && av.ActiveWeapons.Contains(Adventurer.Weapon.Proxy))
{
VoteNum++;
}
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.TicketsStealer))
{
var ps1 = ps;
@@ -654,7 +671,7 @@ class MeetingHudStartPatch
AddMsg(string.Format(GetString("WorkaholicAdviceAlive"), string.Join(separator, workaholicAliveList)), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Workaholic), GetString("WorkaholicAliveTitle")));
}
//Bait Notify
// Bait Notify
if (MeetingStates.FirstMeeting && CustomRoles.Bait.RoleExist() && Options.BaitNotification.GetBool())
{
foreach (var pc in Main.AllAlivePlayerControls.Where(x => x.Is(CustomRoles.Bait)).ToArray())
@@ -779,7 +796,8 @@ class MeetingHudStartPatch
(pc.Is(CustomRoles.Mayor) && Mayor.MayorRevealWhenDoneTasks.GetBool() && pc.GetTaskState().IsTaskFinished) ||
(pc.Is(CustomRoles.Marshall) && PlayerControl.LocalPlayer.Is(CustomRoleTypes.Crewmate) && pc.GetTaskState().IsTaskFinished) ||
(Main.PlayerStates[pc.PlayerId].deathReason == PlayerState.DeathReason.Vote && Options.SeeEjectedRolesInMeeting.GetBool()) ||
CustomTeamManager.AreInSameCustomTeam(pc.PlayerId, PlayerControl.LocalPlayer.PlayerId) ||
CustomTeamManager.AreInSameCustomTeam(pc.PlayerId, PlayerControl.LocalPlayer.PlayerId) && Options.CTAPlayersCanSeeEachOthersRoles.GetBool() ||
Adventurer.KnowRole(PlayerControl.LocalPlayer, pc) ||
Totocalcio.KnowRole(PlayerControl.LocalPlayer, pc) ||
Romantic.KnowRole(PlayerControl.LocalPlayer, pc) ||
EvilDiviner.IsShowTargetRole(PlayerControl.LocalPlayer, pc) ||
@@ -834,7 +852,7 @@ class MeetingHudStartPatch
if (AmongUsClient.Instance.AmHost)
{
_ = _ = new LateTask(() =>
_ = new LateTask(() =>
{
foreach (PlayerControl pc in Main.AllPlayerControls)
{
+1 -1
View File
@@ -231,7 +231,7 @@ class SetEverythingUpPatch
{
var winnerId = CustomWinnerHolder.WinnerIds.FirstOrDefault();
__instance.BackgroundBar.material.color = new Color32(0, 255, 255, 255);
WinnerText.text = Main.AllPlayerNames[winnerId] + " wins!";
WinnerText.text = FFAManager.FFATeamMode.GetBool() ? string.Empty : Main.AllPlayerNames[winnerId] + " wins!";
WinnerText.color = Main.PlayerColors[winnerId];
goto EndOfText;
}
+59 -40
View File
@@ -330,6 +330,8 @@ class CheckMurderPatch
if (Penguin.IsVictim(killer)) return false;
if (!Adventurer.OnAnyoneCheckMurder(target)) return false;
if (killer.Is(CustomRoles.Refugee) && target.Is(CustomRoleTypes.Impostor)) return false;
if (SoulHunter.IsSoulHunterTarget(killer.PlayerId) && target.Is(CustomRoles.SoulHunter))
@@ -504,11 +506,11 @@ class MurderPlayerPatch
if (Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.etc)
{
//If the cause of death is not specified, it is determined as a normal kill.
// If the cause of death is not specified, it is determined as a normal kill.
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Kill;
}
//Let’s see if Youtuber was stabbed first
// Let’s see if Youtuber was stabbed first
if (Main.FirstDied == byte.MaxValue && target.Is(CustomRoles.Youtuber))
{
CustomSoundsManager.RPCPlayCustomSoundAll("Congrats");
@@ -516,7 +518,7 @@ class MurderPlayerPatch
CustomWinnerHolder.WinnerIds.Add(target.PlayerId);
}
//Record the first blow
// Record the first blow
if (Main.FirstDied == byte.MaxValue)
Main.FirstDied = target.PlayerId;
@@ -528,9 +530,6 @@ class MurderPlayerPatch
if (target.Is(CustomRoles.Stained))
Stained.OnDeath(target, killer);
//if ((Romantic.BetPlayer.TryGetValue(target.PlayerId, out var RomanticPartner) && target.PlayerId == RomanticPartner) && target.PlayerId != killer.PlayerId)
// VengefulRomantic.PartnerKiller.Add(killer.PlayerId, 1);
Witness.AllKillers.Remove(killer.PlayerId);
Witness.AllKillers.Add(killer.PlayerId, TimeStamp);
@@ -660,7 +659,7 @@ class ShapeshiftPatch
if (Main.CheckShapeshift.TryGetValue(shapeshifter.PlayerId, out var last) && last == shapeshifting)
{
// Dunno how you would get here but ok
// Don't know how you would get here but ok
return true;
}
@@ -683,8 +682,20 @@ class ShapeshiftPatch
isSSneeded = false;
}
bool forceCancel = shapeshifter.GetCustomRole().ForceCancelShapeshift();
if (shapeshifter.Is(CustomRoles.Camouflager) && !shapeshifting) Camouflager.Reset();
if (Changeling.ChangedRole.TryGetValue(shapeshifter.PlayerId, out var changed) && changed && shapeshifter.GetRoleTypes() != RoleTypes.Shapeshifter) isSSneeded = false;
if (Changeling.ChangedRole.TryGetValue(shapeshifter.PlayerId, out var changed) && changed && shapeshifter.GetRoleTypes() != RoleTypes.Shapeshifter)
{
forceCancel = true;
isSSneeded = false;
}
bool shouldCancel = Options.DisableShapeshiftAnimations.GetBool();
bool shouldAlwaysCancel = shouldCancel && Options.DisableAllShapeshiftAnimations.GetBool();
isSSneeded &= !shouldAlwaysCancel;
forceCancel |= shouldAlwaysCancel;
// Forced rewriting in case the name cannot be corrected due to the timing of canceling the transformation being off.
if (!shapeshifting && !shapeshifter.Is(CustomRoles.Glitch) && isSSneeded)
@@ -704,7 +715,7 @@ class ShapeshiftPatch
NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: shapeshifter);
}
return isSSneeded || !Options.DisableShapeshiftAnimations.GetBool() || !shapeshifting;
return isSSneeded || (!shouldCancel && !forceCancel) || (!shapeshifting && !shouldAlwaysCancel);
}
// Tasks that should run when someone performs a shapeshift (with the egg animation) should be here.
@@ -720,10 +731,29 @@ class ShapeshiftPatch
{
pc.Notify(shapeshifting ? GetString("ShiftguardNotifySS") : "ShiftguardNotifyUnshift");
}
switch (Main.PlayerStates[pc.PlayerId].Role)
{
case Adventurer av:
Adventurer.OnAnyoneShapeshiftLoop(av, __instance);
break;
case Roles.Impostor.Sentry st:
st.OnAnyoneShapeshiftLoop(__instance);
break;
}
}
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcShapeshift))]
class RpcShapeshiftPatch
{
public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target)
{
Main.CheckShapeshift[__instance.PlayerId] = __instance.PlayerId != target.PlayerId;
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.ReportDeadBody))]
class ReportDeadBodyPatch
{
@@ -1261,6 +1291,8 @@ class FixedUpdatePatch
var seer = PlayerControl.LocalPlayer;
var target = __instance;
bool self = seer.PlayerId == target.PlayerId;
Mark.Clear();
Suffix.Clear();
@@ -1320,8 +1352,6 @@ class FixedUpdatePatch
break;
}
bool self = seer.PlayerId == target.PlayerId;
switch (seer.GetCustomRole())
{
case CustomRoles.Lookout:
@@ -1370,10 +1400,10 @@ class FixedUpdatePatch
}
break;
case CustomRoles.Analyzer:
if ((Main.PlayerStates[seer.PlayerId].Role as Analyzer).CurrentTarget.ID == target.PlayerId)
case CustomRoles.Analyst:
if ((Main.PlayerStates[seer.PlayerId].Role as Analyst).CurrentTarget.ID == target.PlayerId)
{
Mark.Append($"<color={GetRoleColorCode(CustomRoles.Analyzer)}>○</color>");
Mark.Append($"<color={GetRoleColorCode(CustomRoles.Analyst)}>○</color>");
}
break;
@@ -1486,9 +1516,12 @@ class FixedUpdatePatch
Suffix.Append(Deathpact.GetDeathpactPlayerArrow(seer, target));
Suffix.Append(Deathpact.GetDeathpactMark(seer, target));
if (self) Suffix.Append(AntiAdminer.GetSuffixText(seer));
if (seer.Is(CustomRoles.Asthmatic) && self) Suffix.Append(Asthmatic.GetSuffixText(seer.PlayerId));
if (self)
{
Suffix.Append(AntiAdminer.GetSuffixText(seer));
Suffix.Append(Roles.Impostor.Sentry.GetSuffix(seer));
if (seer.Is(CustomRoles.Asthmatic)) Suffix.Append(Asthmatic.GetSuffixText(seer.PlayerId));
}
if (target.Is(CustomRoles.Librarian)) Suffix.Append(Librarian.GetNameTextForSuffix(target.PlayerId));
@@ -1498,7 +1531,6 @@ class FixedUpdatePatch
{
case CustomGameMode.FFA:
Suffix.Append(FFAManager.GetPlayerArrow(seer, target));
if (self && FFAManager.FFAChatDuringGame.GetBool()) Suffix.Append((Suffix.Length > 0 && FFAManager.LatestChatMessage != string.Empty ? "\n" : string.Empty) + FFAManager.LatestChatMessage);
break;
case CustomGameMode.MoveAndStop when self:
Suffix.Append(MoveAndStopManager.GetSuffixText(seer));
@@ -1554,20 +1586,9 @@ class FixedUpdatePatch
offset += 0.15f;
}
if (!seer.IsAlive())
{
offset += 0.1f;
}
if (isProgressTextLong)
{
offset += 0.3f;
}
if (Options.CurrentGameMode == CustomGameMode.MoveAndStop)
{
offset += 0.2f;
}
if (!seer.IsAlive()) offset += 0.1f;
if (isProgressTextLong) offset += 0.3f;
if (Options.CurrentGameMode == CustomGameMode.MoveAndStop) offset += 0.2f;
RoleText.transform.SetLocalY(offset);
}
@@ -1688,14 +1709,15 @@ class EnterVentPatch
{
Logger.Info($" {pc.GetNameWithRole()}, Vent ID: {__instance.Id} ({__instance.name})", "EnterVent");
if (pc.GetRoleTypes() != RoleTypes.Engineer && !Main.PlayerStates[pc.PlayerId].Role.CanUseImpostorVentButton(pc))
if (pc.GetRoleTypes() != RoleTypes.Engineer && !Main.PlayerStates[pc.PlayerId].Role.CanUseImpostorVentButton(pc) && Options.CurrentGameMode == CustomGameMode.Standard)
{
pc.MyPhysics?.RpcBootFromVent(__instance.Id);
return;
}
Drainer.OnAnyoneEnterVent(pc, __instance);
Analyzer.OnAnyoneEnterVent(pc);
Analyst.OnAnyoneEnterVent(pc);
Roles.Impostor.Sentry.OnAnyoneEnterVent(pc);
switch (pc.GetCustomRole())
{
@@ -1803,10 +1825,7 @@ class CoEnterVentPatch
if (Ventguard.BlockedVents.Contains(id))
{
var pc = __instance.myPlayer;
if (Options.VentguardBlockDoesNotAffectCrew.GetBool() && pc.IsCrewmate())
{
}
else
if (!Options.VentguardBlockDoesNotAffectCrew.GetBool() || !pc.IsCrewmate())
{
_ = new LateTask(() =>
{
@@ -1827,7 +1846,7 @@ class CoEnterVentPatch
Circumvent.OnCoEnterVent(__instance, id);
}
if (__instance.myPlayer.GetCustomRole().GetDYRole() == RoleTypes.Impostor && !Main.PlayerStates[__instance.myPlayer.PlayerId].Role.CanUseImpostorVentButton(__instance.myPlayer))
if (__instance.myPlayer.GetCustomRole().GetDYRole() == RoleTypes.Impostor && !Main.PlayerStates[__instance.myPlayer.PlayerId].Role.CanUseImpostorVentButton(__instance.myPlayer) && Options.CurrentGameMode == CustomGameMode.Standard)
{
_ = new LateTask(() => { __instance.RpcBootFromVent(id); }, 0.5f);
}
@@ -1835,7 +1854,7 @@ class CoEnterVentPatch
if (((__instance.myPlayer.Data.Role.Role != RoleTypes.Engineer && !__instance.myPlayer.CanUseImpostorVentButton()) ||
(__instance.myPlayer.Is(CustomRoles.Mayor) && Mayor.MayorUsedButtonCount.TryGetValue(__instance.myPlayer.PlayerId, out var count) && count >= Mayor.MayorNumOfUseButton.GetInt()) ||
(__instance.myPlayer.Is(CustomRoles.Paranoia) && Paranoia.ParaUsedButtonCount.TryGetValue(__instance.myPlayer.PlayerId, out var count2) && count2 >= Options.ParanoiaNumOfUseButton.GetInt()))
&& !__instance.myPlayer.Is(CustomRoles.Nimble) && !__instance.myPlayer.Is(CustomRoles.Bloodlust))
&& !__instance.myPlayer.Is(CustomRoles.Nimble) && !__instance.myPlayer.Is(CustomRoles.Bloodlust) && Options.CurrentGameMode == CustomGameMode.Standard)
{
try
{
+1 -1
View File
@@ -261,7 +261,7 @@ public static class SabotageSystemTypeRepairDamagePatch
}
if (player.Is(CustomRoleTypes.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false;
if (player.Is(CustomRoleTypes.Impostor) && (player.IsAlive() || !Options.DeadImpCantSabotage.GetBool()) && !player.Is(CustomRoles.Minimalism) && !player.Is(CustomRoles.Mafioso)) return true;
if (player.Is(CustomRoleTypes.Impostor) && (player.IsAlive() || !Options.DeadImpCantSabotage.GetBool()) && player.GetCustomRole() is not CustomRoles.Minimalism and not CustomRoles.Mafioso and not CustomRoles.Generator) return true;
return player.GetCustomRole() switch
{
CustomRoles.Jackal when Jackal.CanSabotage.GetBool() => true,
+12 -5
View File
@@ -71,7 +71,7 @@ class RepairSystemPatch
if (RepairSender.enabled && AmongUsClient.Instance.NetworkMode != NetworkModes.OnlineGame)
Logger.SendInGame("SystemType: " + systemType + ", PlayerName: " + player.GetNameWithRole().RemoveHtmlTags() + ", amount: " + amount);
if (!AmongUsClient.Instance.AmHost) return true; //Execute the following only on the host
if (!AmongUsClient.Instance.AmHost) return true; // Execute the following only on the host
IsComms = PlayerControl.LocalPlayer.myTasks.ToArray().Any(x => x.TaskType == TaskTypes.FixComms);
@@ -79,7 +79,7 @@ class RepairSystemPatch
if (Options.DisableSabotage.GetBool() && systemType == SystemTypes.Sabotage) return false;
//Note: "SystemTypes.Laboratory" сauses bugs in the Host, it is better not to use
// Note: "SystemTypes.Laboratory" сauses bugs in the Host, it is better not to use
if (player.Is(CustomRoles.Fool) &&
(systemType is
SystemTypes.Comms or
@@ -168,20 +168,27 @@ class RepairSystemPatch
var SwitchSystem = ShipStatus.Instance?.Systems?[SystemTypes.Electrical]?.Cast<SwitchSystem>();
if (SwitchSystem is { IsActive: true })
{
switch (player.GetCustomRole())
switch (Main.PlayerStates[player.PlayerId].Role)
{
case CustomRoles.SabotageMaster:
case SabotageMaster:
Logger.Info($"{player.GetNameWithRole().RemoveHtmlTags()} instant-fix-lights", "SwitchSystem");
SabotageMaster.SwitchSystemRepair(player.PlayerId, SwitchSystem, amount);
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
break;
case CustomRoles.Alchemist when Main.PlayerStates[player.PlayerId].Role is Alchemist { FixNextSabo: true } am:
case Alchemist { FixNextSabo: true } am:
Logger.Info($"{player.GetNameWithRole().RemoveHtmlTags()} instant-fix-lights", "SwitchSystem");
SwitchSystem.ActualSwitches = 0;
SwitchSystem.ExpectedSwitches = 0;
am.FixNextSabo = false;
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
break;
case Adventurer av:
Logger.Info($"{player.GetNameWithRole().RemoveHtmlTags()} instant-fix-lights", "SwitchSystem");
SwitchSystem.ActualSwitches = 0;
SwitchSystem.ExpectedSwitches = 0;
av.OnLightsFix();
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
break;
}
if (player.Is(CustomRoles.Damocles) && Damocles.countRepairSabotage) Damocles.OnRepairSabotage(player.PlayerId);
+1 -1
View File
@@ -596,7 +596,7 @@ internal class SelectRolesPatch
GameOptionsSender.AllSenders.Add(new PlayerGameOptionsSender(pc));
}
// Added players with unclassified roles to the list of players who require ResetCam.
// Add players with unclassified roles to the list of players who require ResetCam.
Main.ResetCamPlayerList.AddRange(Main.AllPlayerControls.Where(p => p.GetCustomRole() is CustomRoles.Arsonist or CustomRoles.Revolutionist or CustomRoles.Sidekick or CustomRoles.KB_Normal or CustomRoles.Killer or CustomRoles.Tasker or CustomRoles.Potato or CustomRoles.Seeker or CustomRoles.Hider or CustomRoles.Fox or CustomRoles.Troll or CustomRoles.Innocent || (p.Is(CustomRoles.Witness) && (!Options.UsePets.GetBool() || Options.WitnessUsePet.GetBool()))).Select(p => p.PlayerId));
Utils.CountAlivePlayers(true);
Utils.SyncAllSettings();
+1 -1
View File
@@ -134,7 +134,7 @@
- Tunneler (Crewmate)
- Detour (Crewmate)
- Gaulois (Crewmate)
- Analyzer (Crewmate)
- Analyst (Crewmate)
- Electric (Crewmate)
- Mole (Crewmate)
- Tornado (Crewmate)
+86 -15
View File
@@ -240,7 +240,7 @@
"Infectious": "Infectious",
"Virus": "Virus",
"Pursuer": "Pursuer",
"Phantom": "Phantom",
"Phantom": "Specter",
"Pirate": "Pirate",
"Jinx": "Jinx",
"Maverick": "Maverick",
@@ -761,7 +761,7 @@
"InfectiousInfoLong": "(Neutrals):\\nAs the Infectious, your job is to infect as many players as you can.\\n\\nIf you infect all the killers, you then can simply outnumber the crew and win the game.",
"VirusInfoLong": "(Neutrals):\\nThe virus' goal is to eliminate other players. When a crewmate is killed, their corpse is infected, and the reporting crewmate joins the virus team or dies if the virus isn't voted out. The Virus team wins if they outnumber the Crewmate team.",
"PursuerInfoLong": "(Neutrals):\\nAs the Pursuer, you can use your ability on someone to make them shoot a blank when they try to kill (= reset their kill cooldown).\\n\\nTo win, just survive to the end of the game.",
"PhantomInfoLong": "(Neutrals):\\nAs the Phantom, your job is to get killed and finish your tasks.\\nYou can do your tasks while alive.\\nYou cannot win if you're alive.\\nIf you get killed, you win with the winning team if your tasks are completed.",
"PhantomInfoLong": "(Neutrals):\\nAs the Specter, your job is to get killed and finish your tasks.\\nYou can do your tasks while alive.\\nYou cannot win if you're alive.\\nIf you get killed, you win with the winning team if your tasks are completed.",
"PirateInfoLong": "(Neutrals):\\nAs the Pirate, use your kill button to select a target every round. You will duel with your target when in a meeting. If both the Pirate and the target choose the same number Pirate wins.\\nAdditionally, if Pirate wins the duel, or Target doesn't duel Target will suicide.\\n\\nDueling command:- /duel X (where X can be 0, 1 or 2)\\n\\nYou win after winning a certain number of duels set by the host.",
"JinxInfoLong": "(Neutrals):\\nAs the Jinx, whenever you are attacked, you jinx them, resulting in them dying to a jinx.\\nThis has limited uses.\\n\\nKill off everyone to win.",
"MaverickInfoLong": "(Neutrals):\\nAs the Maverick, you can kill much like a Serial Killer.\\n\\nHowever, you win with the winning team rather than alone.\\nIf you die, you lose.",
@@ -1091,6 +1091,9 @@
"DisableZiplineForImps": "Disable Zipline for <color=#ff1919>Impostors</color>",
"DisableZiplineForNeutrals": "Disable Zipline for <color=#ffab1b>Neutrals</color>",
"DisableShapeshiftAnimations": "Disable Shapeshift Animations",
"DisableAllShapeshiftAnimations": "Disable All Shapeshift Animations",
"CTA.PlayersCanWinWithOriginalTeam": "Custom Teamed Players Can Win With Their Original Team",
"CTA.PlayersCanSeeEachOthersRoles": "Custom Teamed Players Can See Each Other's Roles",
"AirshipVariableElectrical": "Variable Electrical (Airship)",
"ApplyDenyNameList": "Apply DenyName List",
"KickPlayerFriendCodeNotExist": "Kick players who are not logged in",
@@ -1128,6 +1131,7 @@
"MenuTitle.TaskSettings": "★ Task Management ★",
"MenuTitle.Guessers": "★ Guesser Mode ★",
"MenuTitle.GuesserModeRoles": "★ Roles and Add-ons for Guesser Mode ★",
"MenuTitle.CTA": "★ Custom Team Assignment ★",
"ShieldPersonDiedFirst": "Shield the person who got killed first in the last game",
"JackalWinWithSidekick": "<color=#00b4eb>Jackal</color> can win with <color=#00b4eb>Sidekick</color>'s team",
"SidekickKnowOtherSidekick": "<color=#00b4eb>Sidekicks</color> know each other",
@@ -1946,7 +1950,7 @@
"InfectiousBittenPlayer": "You successfully infected a player",
"GuessNotAllowed": "Sorry, your role does not have access to guessing.",
"GuessOnbound": "This player has the Onbound add-on, so your guess on them was canceled.",
"GuessPhantom": "You can't guess a Phantom, that allows them to win!",
"GuessPhantom": "You can't guess a Specter, that allows them to win!",
"DovesOfNeaceOnGuard": "Ability used, {0} uses remain",
"DovesOfNeaceMaxUsage": "Ability use limit reached",
"DovesOfNeaceSkillNotify": "Pacifist reset your kill cooldown",
@@ -2615,6 +2619,24 @@
"Transmitter": "Transmitter",
"TransmitterInfo": "TP to nearest player by doing a task",
"TransmitterInfoLong": "(Crewmates):\\nAs the Transmitter, when you complete a task, you teleport to the nearest player.",
"Sentry": "Sentry",
"SentryInfo": "Monitor a Room",
"SentryInfoLong": "(Crewmates):\\nAs the Sentry, you can monitor a room by petting your pet. When you pet your pet in the future, you get information from that room, including:<size=80%>\\n- The players currently in that room\\n- Who is dead in that room (if any)\\n- The players that used a vent in that room\\n- Whether someone shapeshifted in that room</size>\\n\\nDepending on the settings, players may know if a room is monitored, and you may not be able to view this info during Comms Sabotages.",
"Sentry.ShowInfoDuration": "Info is Visible for",
"Sentry.PlayersKnowAboutCamera": "Players Know if the Room is Monitored",
"Sentry.Suffix.MonitoredRoom": "<size=80%>This room is monitored by the {0}</size>",
"Sentry.Suffix.Self": "<size=70%>Monitoring: <#00ffa5>{0}</color></size>",
"Sentry.Notify.Info": "<u>Info about your monitored room:</u>\\nPlayers: {0}\\nDead players: {1}\\nPlayers that used a vent: {2}\\nShapeshifters: {3}",
"Sentry.Notify.Info.NoData": "<#ff0000>none</color>",
"SchrodingersCat": "Schrödinger's Cat",
"SchrodingersCatInfo": "Get Attacked to Get a Role",
"SchrodingersCatInfoLong": "(Neutrals):\\nAs Schrödinger's Cat, if you get attacked, you turn into the same role as your killer.\\nIf you don't get attacked, you lose, or you can win with crewmates, depending on the settings.",
"SchrodingersCat.WinsWithCrewIfNotAttacked": "Wins with crewmates if not attacked",
"SchrodingersCat.Notify.KillerRecruited": "You have recruited the {1}! {0} now has the same role as you.",
"SchrodingersCat.Notify.RecruitedByKiller": "You have been recruited by the {1}! You now have the same role as {0}.",
"Cherokious": "Cherokious",
"CherokiousInfo": "Do tasks to get Bullets to Kill",
"CherokiousInfoLong": "(Neutrals):\\nAs the Cherokious, you can get bullets by completing tasks. You can kill by petting your pet near someone. You can only kill if you have bullets.",
"Autocrat": "Autocrat",
"AutocratInfo": "TP nearest player to you by doing a task",
"AutocratInfoLong": "(Crewmates):\\nAs the Autocrat, when you complete a task, you teleport the nearest player to you.",
@@ -2829,9 +2851,9 @@
"StealthDarkened": "Darkened: {0}",
"StealthExcludeImpostors": "Ignore Impostors when Blinding",
"StealthDarkenDuration": "Blinding Duration",
"Analyzer": "Analyzer",
"AnalyzerInfo": "Analyze Players to Get Information",
"AnalyzerInfoLong": "(Crewmates):\\nAs the Analyzer, you may use your kill button on someone to analyze them. You will have to stand next to your target for a few seconds to complete the analysis. When the analysis is complete, you will see how many players they've killed so far, how many times they've vented so far, and their role basis.\\n\\nNote: If the host's settings don't allow you to see an information, the result will show 0 or nothing!\\nNote: None of these factors determine whether someone is evil or not! Think twice before judging too fast!",
"Analyst": "Analyst",
"AnalystInfo": "Analyze Players to Get Information",
"AnalystInfoLong": "(Crewmates):\\nAs the Analyst, you may use your kill button on someone to analyze them. You will have to stand next to your target for a few seconds to complete the analysis. When the analysis is complete, you will see how many players they've killed so far, how many times they've vented so far, and their role basis.\\n\\nNote: If the host's settings don't allow you to see an information, the result will show 0 or nothing!\\nNote: None of these factors determine whether someone is evil or not! Think twice before judging too fast!",
"AnalyzerCD": "Analyze Cooldown",
"AnalyzerDur": "Analyze Time",
"AnalyzerResult": "\\n\\nShowing analyzation result for <color=#00ffa5>{0}</color>:\\nKills: <color=#ff1919>{1}</color>, Vents: <color=#ffa500>{2}</color>, Role Basis: <color=#00ffff>{3}</color>",
@@ -3106,6 +3128,40 @@
"CandleLighterEndVision": "End Vision (Smallest Vision)",
"CandleLighterTimeMoveMeeting": "Time Moves During Meetings",
"CandleLighterTasksFinishedVision": "Vision after completing all tasks",
"AprilFoolsMode": "April Fools Mode",
"SilenceMode": "Silencing Method",
"Adventurer": "Adventurer",
"AdventurerInfo": "Collect Resources to Perform Actions",
"AdventurerInfoLong": "(Crewmates):\\nAs the Adventurer, you can collect resources listed below and craft them into weapons if you have the required amount of materials for it.<size=70%>\\n\\n<u><b>Resources (and how to obtain them):</b></u>\\n- ❖ (Complete a Task)\\n- Θ (Players Leave This Behind<sup>*</sup>)\\n- ♳ (Dead Bodies<sup>*</sup>)\\n- ⁂ (Shapeshift Skins<sup>*</sup>)\\n- \u2600 (Fix Lights)\\n- ○ (Found in Groups<sup>*</sup>)\\n<size=50%><sup>*</sup> When this resource is available somewhere, you get an arrow pointing towards that location. (Except dead bodies.) Go there to collect it.</size>\\n\\n<u><b>Weapons (and their ingredients):</b></u>\\n- Gun (1 ♳ + 1 ❖) (Kill someone (pet near them))\\n- Shield (3 ❖) (Gives someone a 1-time shield)\\n- Lantern (1 \u2600 + 1 ❖) (Increases crewmates' vision for some time)\\n- Portal (2 Θ) (Swaps you with a random player)\\n- Wrench (1 \u2600 + 2 ❖) (Clears the current sabotage)\\n- Proxy (2 ○ + 1 ❖) (Gives you +1 vote in the next meeting)\\n- Prediction (1 ♳ + 2 ⁂ + 1 ❖) (Reveals the nearest player's role)\\n- RNG (2 Θ + 2 ❖) (Makes a random weapon)</size>",
"AdventurerWeaponDescription.Gun": "Kill the next player you go near",
"AdventurerWeaponDescription.Shield": "Gives someone a 1-time shield",
"AdventurerWeaponDescription.Lantern": "Increases crewmates' vision for some time",
"AdventurerWeaponDescription.Portal": "Swaps you with a random player",
"AdventurerWeaponDescription.Wrench": "Clears the current sabotage",
"AdventurerWeaponDescription.Proxy": "Gives you +1 vote in the next meeting",
"AdventurerWeaponDescription.Prediction": "Reveals the nearest player's role",
"AdventurerWeaponDescription.RNG": "Makes a random weapon",
"AdventurerIncreasedVisionDuration": "Increased Vision Duration",
"AdventurerWeaponEnabled.Gun": "Gun Enabled",
"AdventurerWeaponEnabled.Shield": "Healthy Dose Enabled",
"AdventurerWeaponEnabled.Lantern": "Lantern Enabled",
"AdventurerWeaponEnabled.Portal": "Portal Enabled",
"AdventurerWeaponEnabled.Wrench": "Wrench Enabled",
"AdventurerWeaponEnabled.Proxy": "Proxy Enabled",
"AdventurerWeaponEnabled.Prediction": "Prediction Enabled",
"AdventurerWeaponEnabled.RNG": "RNG Enabled",
"AdventurerVentToEnterCrafting": "Vent to Enter Crafting Menu",
"AdventurerIngredientsDisplay": "{0}: {1}\\nVent to craft this weapon / Pet to cycle to next weapon",
"AdventurerWeaponCrafted": "Crafted Weapon: {0}",
"AdventurerFound": "Found Resource: {0}",
"AdventurerGun.Gun": "Gun",
"AdventurerGun.Shield": "Shield",
"AdventurerGun.Lantern": "Lantern",
"AdventurerGun.Portal": "Portal",
"AdventurerGun.Wrench": "Wrench",
"AdventurerGun.Proxy": "Proxy",
"AdventurerGun.Prediction": "Prediction",
"AdventurerGun.RNG": "RNG",
"Imitator": "Imitator",
"ImitatorInfo": "Your kill cooldown shifts",
"ImitatorInfoLong": "(Neutral):\\nAs the Imitator, your kill cooldown is different after every even numbered kill and every odd numbered kill of yours. You have a separate cooldown after meetings. Your first kill after every meeting will set your cooldown to the Even-Numbered Kill Cooldown, your second kill will set it to the Odd-Numbered Kill Cooldown, and the process repeats until a meeting is called again.",
@@ -3203,6 +3259,8 @@
"FFA_LowerVision": "Lowered Vision",
"FFA_ModifiedVisionDuration": "Lowered Vision Duration",
"FFA_EnableRandomTwists": "Enable Random Swaps from time to time",
"FFA_TeamMode": "Enable Team Mode",
"FFA_TeamNumber": "Number of Teams",
"FFA-Event-GetShield": "You have a temporary shield!",
"FFA-Event-GetIncreasedSpeed": "You have a temporary speed boost!",
"FFA-Event-GetLowKCD": "You got a lower kill cooldown!",
@@ -3283,6 +3341,19 @@
"TrollInfoLong": "GET KILLED TO WIN",
"TrollVision": "Troll Vision",
"TrollSpeed": "Troll Speed",
"Generator": "Generator",
"GeneratorInfo": "Power up the ship",
"GeneratorInfoLong": "(Impostors):\\nAs the Generator, you generate charges every second. You can only kill/vent/shapeshift/sabotage if you have enough charges to do so. You also lose charges while shapeshifted.",
"Generator.ChargesGainedEverySecond": "Charges Gained Every Second",
"Generator.StartingCharges": "Starting Charges",
"Generator.ChargesLostWhileShiftedPerSecond": "Charges Lost While Shifted Per Second",
"Generator.MaxChargesStored": "Maximum Charges Stored",
"Generator.Kill.Cost": "Charges Needed to Kill",
"Generator.Vent.Cost": "Charges Needed to Vent",
"Generator.Sabotage.Cost": "Charges Needed to Sabotage",
"Generator.Shapeshift.Cost": "Charges Needed to Shapeshift",
"Generator.Notify.RevertShapeshift": "You ran out of charges, so shapeshifting was reverted",
"Generator.Notify.NotEnoughCharges": "You don't have enough charges ({0}) to do that",
"GNoCommandInfo": "<b>This Command can only be used when in lobby or after you die.</b>\\n\\ntype <b>/gno X</b> to play guess a number. X can be a number between 0 and 99 (both included). \\n\\nYou get maximum of <color=#ff0000>7 tries</color> to guess the number.\\n\\n Example:- <b>/gno 10</b>",
"GNoLost": "Oh, you were so close! Just one more guess, and you might have deciphered the Da Vinci code! By the way, <color=#ff0000>the secret number was... {0}!</color> But hey, you were only off by a few billion possibilities. Better luck next time, Sherlock! ",
"GNoLow": "Oh, you're really nailing this! It's so low, I almost need a shovel to dig it up!\\nYou have <color=#ff0000>{0} guesses</color> left!",
@@ -3301,8 +3372,8 @@
"CommanderInfo": "Command your team",
"CommanderInfoLong": "(Impostors):\\nAs the Commander, you can switch between 6 modes by venting/petting your pet. You can execute the selected action by shapeshifting.<size=70%>\\n\\n<b><u>Mark:</u></b> Marks your shapeshift target with a unique color visible to all impostors. Use this to command them to kill that player!\\n\\n<b><u>Whistle:</u></b> This tells all or 1 specific<sup>*</sup> impostor(s) to come to you and gives them (each) an arrow pointing towards you.\\n\\n<b><u>Kill Anyone:</u></b> Notifies all or 1 specific<sup>*</sup> impostor(s) to kill anyone.\\n\\n<b><u>Leave Player Alive:</u></b> Shapeshifting into a crewmate marks that player with a unique color and text telling other impostors to not kill that player. Shapeshifting into an impostor tells that impostor to not kill anyone.\\n\\n<b><u>Don't Sabotage:</u></b> Notifies all impostors to not call any sabotage.\\n\\n<b><u>Use Ability:</u></b> Notifies all or 1 specific<sup>*</sup> impostor(s) to use their ability.\\n\\n<size=60%><sup>*</sup> If you shapeshift into an impostor, the action executes specifically and only on them.</size></size>",
"CommanderNotify": "The Commander wants you to meet with them",
"CommanderTarget": "<size=1.7>Commander's target</color>",
"CommanderDontKill": "<size=1.7>Don't kill this player</color>",
"CommanderTarget": "<size=1.7>Commander's target</size>",
"CommanderDontKill": "<size=1.7>Don't kill this player</size>",
"CommanderWhistling": "Whistling",
"CommanderKillAnyoneNotify": "Commander wants you to kill anyone",
"CommanderDontSabotageNotify": "Commander wants you to not sabotage",
@@ -3421,11 +3492,11 @@
"WardenInfoLong": "(Crewmates):\\nAs the Warden, you can protect someone (optimally a crewmate) to notify them about being in danger and to give them a temporary speed boost.",
"WardenNotify": "The Warden warns you that you're in danger!",
"Prefix.Warden": "Warden",
"Specter": "Specter",
"Specter": "Phantom",
"SpecterInfo": "Finish Tasks to Win!",
"SpecterInfoLong": "(Crewmates):\\nAs the Specter, you must finish all of your tasks to win! Depending on a setting, you may also end the game and win alone if you do so!",
"SpecterInfoLong": "(Crewmates):\\nAs the Phantom, you must finish all of your tasks to win! Depending on a setting, you may also end the game and win alone if you do so!",
"SnatchWin": "Snatches Win (Win With Winning Team if Off)",
"Prefix.Specter": "Specter",
"Prefix.Specter": "Phantom",
"Haunter": "Haunter",
"HaunterInfo": "You're the Impostors' Worst Nightmare",
"HaunterInfoLong": "(Neutrals):\\nAs the Haunter, when you complete all of your tasks, a random living impostor's name will show in red to everyone in meetings. When you have 1 task left, all impostors will get an arrow pointing towards you, and if any of them come close to you, you lose this role.",
@@ -3802,7 +3873,7 @@
"WinnerRoleText.Infectious": "Infectious Wins!",
"WinnerRoleText.Rogue": "Mavericks Win!",
"WinnerRoleText.Virus": "Virus Wins!",
"WinnerRoleText.Phantom": "Phantom Wins!",
"WinnerRoleText.Phantom": "Specter Wins!",
"WinnerRoleText.Jinx": "Jinx Wins!",
"WinnerRoleText.CursedSoul": "Cursed Soul Wins!",
"WinnerRoleText.Ritualist": "Ritualist Wins!",
@@ -3834,7 +3905,7 @@
"WinnerRoleText.Sprayer": "Sprayer Wins!",
"WinnerRoleText.SoulHunter": "Soul Hunter Wins!",
"WinnerRoleText.Tiger": "Tiger Wins!",
"WinnerRoleText.Specter": "Specter Wins!",
"WinnerRoleText.Specter": "Phantom Wins!",
"WinnerRoleText.Default": "{0} Wins!",
"AdditionalWinnerRoleText.Sidekick": "along with Sidekicks",
"AdditionalWinnerRoleText.Opportunist": "The Opportunist survived",
@@ -3848,13 +3919,13 @@
"AdditionalWinnerRoleText.Jester": "The Jester wins along",
"AdditionalWinnerRoleText.Lovers": "Lovers win along",
"AdditionalWinnerRoleText.Executioner": "Executioner wins along",
"AdditionalWinnerRoleText.Phantom": "Phantom wins along",
"AdditionalWinnerRoleText.Phantom": "Specter wins along",
"AdditionalWinnerRoleText.Maverick": "The Maverick survived",
"AdditionalWinnerRoleText.Romantic": "The Romantic wins along",
"AdditionalWinnerRoleText.VengefulRomantic": "The Vengeful Romantic avenged their lover",
"AdditionalWinnerRoleText.Postman": "The Postman delivered mail to everyone",
"AdditionalWinnerRoleText.SoulHunter": "Soul Hunter wins along",
"AdditionalWinnerRoleText.Specter": "Specter wins along",
"AdditionalWinnerRoleText.Specter": "Phantom wins along",
"AdditionalWinnerRoleText.Default": "{0} wins along",
"ErrorEndText": "An error occurred",
"ErrorEndTextDescription": "To avoid crashing, the game was forcibly ended.",
+3 -3
View File
@@ -2820,9 +2820,9 @@
"StealthDarkened": "Cegado: {0}",
"StealthExcludeImpostors": "Ignorar Impostores ao Cegar",
"StealthDarkenDuration": "Duração da Visão Zero",
"Analyzer": "Analisador",
"AnalyzerInfo": "Analize jogadores para obter Informações",
"AnalyzerInfoLong": "(Tripulantes):\\nComo Analisador, você pode usar seu botão matar para analizar um jogador. Você deverá ficar ao lado do alvo por alguns segundos para concluir a analise. Quando a analise estiver completa, Você verá quantos jogadores ele matou, quantoas vezes ele entrou no duto e a sua função base.\\n\\nNota: Se as configurações do Anfitrião não permitirem você ver uma informação, o resultado será 0 ou nada!\\nNota: Nenhum destes fatores determinam se o jogador e maligno ou não! Pense duas vezes antes de acusar!",
"Analyst": "Analisador",
"AnalystInfo": "Analize jogadores para obter Informações",
"AnalystInfoLong": "(Tripulantes):\\nComo Analisador, você pode usar seu botão matar para analizar um jogador. Você deverá ficar ao lado do alvo por alguns segundos para concluir a analise. Quando a analise estiver completa, Você verá quantos jogadores ele matou, quantoas vezes ele entrou no duto e a sua função base.\\n\\nNota: Se as configurações do Anfitrião não permitirem você ver uma informação, o resultado será 0 ou nada!\\nNota: Nenhum destes fatores determinam se o jogador e maligno ou não! Pense duas vezes antes de acusar!",
"AnalyzerCD": "Recarga para analisar",
"AnalyzerDur": "Duração da analise",
"AnalyzerResult": "n\\nMostrando resultados da analise de <color=#00ffa5>{0}</color>:\\nAbates: <color=#ff1919>{1}</color>, Usou dutos: <color=#ffa500>{2}</color>, Função Base: <color=#00ffff>{3}</color>",
+1 -1
View File
@@ -27,7 +27,7 @@ namespace EHR.Roles.AddOns.GhostRoles
GameData.Instance.RpcSetTasks(pc.PlayerId, Array.Empty<byte>());
pc.SyncSettings();
pc.RpcResetAbilityCooldown();
_ = new LateTask(pc.RpcResetAbilityCooldown, 1f, log: false);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}, 1f, "Specter Assign");
}
+366
View File
@@ -0,0 +1,366 @@
using EHR.Patches;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace EHR.Roles.Crewmate
{
internal class Adventurer : RoleBase
{
public static bool On;
public override bool IsEnable => On;
enum Resource
{
TaskCompletion,
Random,
DeadBody,
ShapeshiftSkin,
LightsFix,
Grouping
}
public enum Weapon
{
Gun,
Shield,
Lantern,
Portal,
Wrench,
Proxy,
Prediction,
RNG
}
private static readonly Dictionary<Resource, (char Icon, Color Color)> ResourceDisplayData = new()
{
{ Resource.TaskCompletion, ('\u2756', Color.blue) },
{ Resource.Random, ('Θ', Color.cyan) },
{ Resource.DeadBody, ('\u2673', Color.magenta) },
{ Resource.ShapeshiftSkin, ('⁂', Color.gray) },
{ Resource.LightsFix, ('\u2600', Color.yellow) },
{ Resource.Grouping, ('\u25cb', Color.green) }
};
private static readonly Dictionary<Weapon, List<(Resource Resource, int Count)>> Ingredients = new()
{
{ Weapon.Gun, [(Resource.DeadBody, 1), (Resource.TaskCompletion, 1)] },
{ Weapon.Shield, [(Resource.TaskCompletion, 3)] },
{ Weapon.Lantern, [(Resource.LightsFix, 1), (Resource.TaskCompletion, 1)] },
{ Weapon.Portal, [(Resource.Random, 2)] },
{ Weapon.Wrench, [(Resource.LightsFix, 1), (Resource.TaskCompletion, 2)] },
{ Weapon.Proxy, [(Resource.Grouping, 2), (Resource.TaskCompletion, 1)] },
{ Weapon.Prediction, [(Resource.DeadBody, 1), (Resource.ShapeshiftSkin, 2), (Resource.TaskCompletion, 1)] },
{ Weapon.RNG, [(Resource.Random, 2), (Resource.TaskCompletion, 2)] }
};
private static OptionItem IncreasedVisionDuration;
private static readonly Dictionary<Weapon, OptionItem> WeaponEnabledSettings = [];
private static List<Weapon> EnabledWeapons = [];
static OptionItem CreateWeaponEnabledSetting(int id, Weapon weapon) => BooleanOptionItem.Create(id, $"AdventurerWeaponEnabled.{weapon}", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Adventurer]);
public static void SetupCustomOption()
{
Options.SetupRoleOptions(11330, TabGroup.CrewmateRoles, CustomRoles.Adventurer);
IncreasedVisionDuration = IntegerOptionItem.Create(11332, "AdventurerIncreasedVisionDuration", new(1, 60, 1), 30, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Adventurer])
.SetValueFormat(OptionFormat.Seconds);
foreach (var weapon in EnumHelper.GetAllValues<Weapon>())
{
WeaponEnabledSettings[weapon] = CreateWeaponEnabledSetting(11333 + (int)weapon, weapon);
}
}
private PlayerControl AdventurerPC;
private readonly Dictionary<Resource, int> ResourceCounts = [];
public bool InCraftingMode;
private Weapon SelectedWeaponToCraft;
public List<Weapon> OrderedWeapons;
public List<Weapon> ActiveWeapons;
public HashSet<byte> ShieldedPlayers;
public HashSet<byte> RevealedPlayers;
private long LastRandomResourceTimeStamp;
private long LastGroupingResourceTimeStamp;
private Dictionary<Resource, Vector2> ResourceLocations;
public override void Add(byte playerId)
{
On = true;
AdventurerPC = Utils.GetPlayerById(playerId);
InCraftingMode = false;
SelectedWeaponToCraft = new();
OrderedWeapons = [];
ActiveWeapons = [];
ShieldedPlayers = [];
RevealedPlayers = [];
LastRandomResourceTimeStamp = Utils.TimeStamp + 8;
LastGroupingResourceTimeStamp = Utils.TimeStamp + 20;
ResourceLocations = [];
foreach (var resource in EnumHelper.GetAllValues<Resource>())
{
ResourceCounts[resource] = 0;
}
}
public override void Init()
{
On = false;
EnabledWeapons = WeaponEnabledSettings
.Where(x => x.Value.GetBool())
.Select(x => x.Key)
.ToList();
}
public override void OnExitVent(PlayerControl pc, Vent vent)
{
InCraftingMode = !InCraftingMode;
switch (InCraftingMode)
{
case true:
OrderedWeapons = [.. EnabledWeapons.OrderBy(x => !Ingredients[x].All(r => r.Count <= ResourceCounts[r.Resource]))];
SelectedWeaponToCraft = OrderedWeapons.FirstOrDefault();
break;
case false when Ingredients[SelectedWeaponToCraft].All(x => x.Count <= ResourceCounts[x.Resource]):
var weapon = SelectedWeaponToCraft == Weapon.RNG ? EnabledWeapons[IRandom.Instance.Next(EnabledWeapons.Count)] : SelectedWeaponToCraft;
ActiveWeapons.Add(weapon);
pc.Notify(string.Format(Translator.GetString("AdventurerWeaponCrafted"), Translator.GetString($"AdventurerGun.{weapon}")));
foreach ((Resource resource, int count) in Ingredients[weapon])
{
ResourceCounts[resource] -= count;
}
switch (weapon)
{
case Weapon.Lantern:
Utils.MarkEveryoneDirtySettings();
_ = new LateTask(() => { ActiveWeapons.Remove(Weapon.Lantern); }, IncreasedVisionDuration.GetInt(), log: false);
break;
case Weapon.Portal:
_ = new LateTask(() =>
{
var e = Main.AllAlivePlayerControls.Where(x => x.PlayerId != pc.PlayerId && !x.inVent && !x.inMovingPlat && !x.onLadder);
var filtered = e as PlayerControl[] ?? e.ToArray();
if (filtered.Length == 0) return;
var target = filtered[IRandom.Instance.Next(filtered.Length)];
target.TP(pc);
pc.TP(target);
ActiveWeapons.Remove(weapon);
}, 1f, log: false);
break;
case Weapon.Wrench:
if (Utils.IsActive(SystemTypes.Electrical))
{
var SwitchSystem = ShipStatus.Instance?.Systems?[SystemTypes.Electrical]?.TryCast<SwitchSystem>();
if (SwitchSystem != null)
{
SwitchSystem.ActualSwitches = 0;
SwitchSystem.ExpectedSwitches = 0;
}
}
else if (Utils.IsActive(SystemTypes.Reactor))
{
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Reactor, 16);
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Reactor, 17);
}
else if (Utils.IsActive(SystemTypes.Laboratory))
{
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Laboratory, 67);
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Laboratory, 66);
}
else if (Utils.IsActive(SystemTypes.LifeSupp))
{
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.LifeSupp, 67);
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.LifeSupp, 66);
}
else if (Utils.IsActive(SystemTypes.Comms))
{
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Comms, 16);
ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Comms, 17);
}
ActiveWeapons.Remove(weapon);
break;
case Weapon.Prediction:
var closest = Main.AllAlivePlayerControls.Where(x => x.PlayerId != pc.PlayerId).MinBy(x => Vector2.Distance(pc.Pos(), x.Pos()));
RevealedPlayers.Add(closest.PlayerId);
ActiveWeapons.Remove(weapon);
break;
}
break;
}
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
public override void OnPet(PlayerControl pc)
{
switch (InCraftingMode)
{
case true:
SelectedWeaponToCraft = OrderedWeapons[(OrderedWeapons.IndexOf(SelectedWeaponToCraft) + 1) % OrderedWeapons.Count];
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
break;
case false when ActiveWeapons.Count > 0:
var target = ExternalRpcPetPatch.SelectKillButtonTarget(pc);
switch (ActiveWeapons[0])
{
case Weapon.Gun when target != null:
pc.RpcCheckAndMurder(target);
ActiveWeapons.RemoveAt(0);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
break;
case Weapon.Shield when target != null:
ShieldedPlayers.Add(target.PlayerId);
ActiveWeapons.RemoveAt(0);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
break;
}
break;
}
}
public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
{
ResourceCounts[Resource.TaskCompletion]++;
}
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
{
long now = Utils.TimeStamp;
if (lowLoad) return;
if (!GameStates.IsInTask)
{
LastGroupingResourceTimeStamp = now;
LastRandomResourceTimeStamp = now;
return;
}
if (LastRandomResourceTimeStamp + 20 <= now && pc.PlayerId != AdventurerPC.PlayerId && IRandom.Instance.Next(50) == 0)
{
var pos = pc.Pos();
LocateArrow.Add(AdventurerPC.PlayerId, pos);
ResourceLocations[Resource.Random] = pos;
LastRandomResourceTimeStamp = now;
Utils.NotifyRoles(SpecifySeer: AdventurerPC, SpecifyTarget: AdventurerPC);
}
if (LastGroupingResourceTimeStamp + 20 <= now && Main.AllAlivePlayerControls.Count(x => x.PlayerId != pc.PlayerId && Vector2.Distance(x.Pos(), pc.Pos()) < 2f) >= 2)
{
var pos = pc.Pos();
LocateArrow.Add(AdventurerPC.PlayerId, pos);
ResourceLocations[Resource.Grouping] = pos;
LastGroupingResourceTimeStamp = now;
Utils.NotifyRoles(SpecifySeer: AdventurerPC, SpecifyTarget: AdventurerPC);
}
}
public override void OnFixedUpdate(PlayerControl pc)
{
foreach ((Resource resource, Vector2 location) in ResourceLocations)
{
if (Vector2.Distance(pc.Pos(), location) < 2f)
{
ResourceCounts[resource]++;
ResourceLocations.Remove(resource);
LocateArrow.Remove(pc.PlayerId, location);
var displayData = ResourceDisplayData[resource];
pc.Notify(string.Format(Translator.GetString("AdventurerFound"), Utils.ColorString(displayData.Color, $"{displayData.Icon}")));
break;
}
}
}
public override void AfterMeetingTasks()
{
ActiveWeapons.Remove(Weapon.Proxy);
}
public void OnLightsFix()
{
ResourceCounts[Resource.LightsFix]++;
}
public static void OnAnyoneShapeshiftLoop(Adventurer av, PlayerControl shapeshifter)
{
av.ResourceLocations[Resource.ShapeshiftSkin] = shapeshifter.Pos();
}
public static void OnAnyoneDead(PlayerControl target)
{
foreach (var state in Main.PlayerStates.Values)
{
if (state.Role is Adventurer { IsEnable: true } av)
{
av.ResourceLocations[Resource.DeadBody] = target.Pos();
}
}
}
public static bool OnAnyoneCheckMurder(PlayerControl target)
{
bool any = false;
foreach (var s in Main.PlayerStates.Values)
{
if (s.Role is Adventurer { IsEnable: true } av && av.ShieldedPlayers.Contains(target.PlayerId))
{
any = true;
av.ShieldedPlayers.Remove(target.PlayerId);
break;
}
}
return !any;
}
public static bool KnowRole(PlayerControl seer, PlayerControl target)
{
return Main.PlayerStates[seer.PlayerId].Role is Adventurer { IsEnable: true } av && av.RevealedPlayers.Contains(target.PlayerId);
}
public static string GetSuffixAndHUDText(PlayerControl pc, bool hud = false, bool isForMeeting = false)
{
if (pc.IsModClient() && !hud) return string.Empty;
if (Main.PlayerStates[pc.PlayerId].Role is not Adventurer { IsEnable: true } av) return string.Empty;
IEnumerable<string> resources =
from resource in EnumHelper.GetAllValues<Resource>()
let displayData = ResourceDisplayData[resource]
select $"{Utils.ColorString(displayData.Color, $"{displayData.Icon}")}{av.ResourceCounts[resource]}";
string finalText = string.Join(' ', resources);
if (isForMeeting) return finalText;
finalText += $"\n{LocateArrow.GetArrows(pc)}\n";
finalText += "<size=80%>";
finalText += av.InCraftingMode
? string.Format(
Translator.GetString("AdventurerIngredientsDisplay"),
Translator.GetString($"AdventurerGun.{av.SelectedWeaponToCraft}"),
string.Join(' ', Ingredients[av.SelectedWeaponToCraft]
.Select(x => $"{Utils.ColorString(ResourceDisplayData[x.Resource].Color, $"{ResourceDisplayData[x.Resource].Icon}")}" +
$"{Utils.ColorString(x.Count > av.ResourceCounts[x.Resource] ? Color.red : Color.white, $"{x.Count}")}")))
: Translator.GetString("AdventurerVentToEnterCrafting");
finalText += "</size>";
return finalText;
}
}
}
@@ -7,7 +7,7 @@ using static EHR.Translator;
namespace EHR.Roles.Crewmate
{
internal class Analyzer : RoleBase
internal class Analyst : RoleBase
{
private const int Id = 643100;
private static byte playerId = byte.MaxValue;
@@ -20,32 +20,32 @@ namespace EHR.Roles.Crewmate
private static OptionItem SeeRoleBasis;
public static OptionItem UsePet;
private static readonly Dictionary<string, string> ReplacementDict = new() { { "Analyze", Utils.ColorString(Utils.GetRoleColor(CustomRoles.Analyzer), "Analyze") } };
private static readonly Dictionary<string, string> ReplacementDict = new() { { "Analyze", Utils.ColorString(Utils.GetRoleColor(CustomRoles.Analyst), "Analyze") } };
public static Dictionary<byte, int> VentCount = [];
public (byte ID, long TIME) CurrentTarget = (byte.MaxValue, Utils.TimeStamp);
public static void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Analyzer);
SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Analyst);
UseLimitOpt = IntegerOptionItem.Create(Id + 10, "AbilityUseLimit", new(0, 30, 1), 3, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer])
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst])
.SetValueFormat(OptionFormat.Times);
CD = FloatOptionItem.Create(Id + 11, "AnalyzeCD", new(0f, 60f, 2.5f), 15f, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer])
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst])
.SetValueFormat(OptionFormat.Seconds);
CD.ReplacementDictionary = ReplacementDict;
Duration = IntegerOptionItem.Create(Id + 12, "AnalyzeDur", new(1, 30, 1), 5, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer])
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst])
.SetValueFormat(OptionFormat.Seconds);
Duration.ReplacementDictionary = ReplacementDict;
SeeKillCount = BooleanOptionItem.Create(Id + 13, "AnalyzerSeeKillCount", true, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer]);
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst]);
SeeVentCount = BooleanOptionItem.Create(Id + 14, "AnalyzerSeeVentCount", true, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer]);
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst]);
SeeRoleBasis = BooleanOptionItem.Create(Id + 15, "AnalyzerSeeRoleBasis", true, TabGroup.CrewmateRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyzer]);
UsePet = CreatePetUseSetting(Id + 16, CustomRoles.Analyzer);
.SetParent(CustomRoleSpawnChances[CustomRoles.Analyst]);
UsePet = CreatePetUseSetting(Id + 16, CustomRoles.Analyst);
}
public override bool CanUseKillButton(PlayerControl pc) => CurrentTarget.ID == byte.MaxValue && pc.GetAbilityUseLimit() > 0;
+156
View File
@@ -0,0 +1,156 @@
using HarmonyLib;
using System.Collections.Generic;
using System.Linq;
namespace EHR.Roles.Impostor
{
internal class Sentry : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public static OptionItem ShowInfoCooldown;
private static OptionItem ShowInfoDuration;
private static OptionItem PlayersKnowAboutCamera;
private static OptionItem AbilityUseLimit;
public static OptionItem AbilityUseGainWithEachTaskCompleted;
public static OptionItem AbilityChargesWhenFinishedTasks;
private PlainShipRoom MonitoredRoom;
private HashSet<byte> DeadBodiesInRoom;
private HashSet<byte> PlayersVentedInRoom;
private HashSet<byte> PlayersShiftedInRoom;
public static void SetupCustomOption()
{
const int id = 11370;
Options.SetupRoleOptions(id, TabGroup.CrewmateRoles, CustomRoles.Sentry);
ShowInfoCooldown = IntegerOptionItem.Create(id + 2, "AbilityCooldown", new(1, 60, 1), 15, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry])
.SetValueFormat(OptionFormat.Seconds);
ShowInfoDuration = IntegerOptionItem.Create(id + 3, "Sentry.ShowInfoDuration", new(1, 60, 1), 5, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry])
.SetValueFormat(OptionFormat.Seconds);
PlayersKnowAboutCamera = BooleanOptionItem.Create(id + 4, "Sentry.PlayersKnowAboutCamera", true, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry]);
AbilityUseLimit = IntegerOptionItem.Create(id + 5, "AbilityUseLimit", new(0, 20, 1), 0, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry]);
AbilityUseGainWithEachTaskCompleted = FloatOptionItem.Create(id + 6, "AbilityUseGainWithEachTaskCompleted", new(0.1f, 5f, 0.1f), 1f, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry]);
AbilityChargesWhenFinishedTasks = FloatOptionItem.Create(id + 7, "AbilityChargesWhenFinishedTasks", new(0.1f, 5f, 0.1f), 0.2f, TabGroup.CrewmateRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sentry]);
}
public override void Add(byte playerId)
{
On = true;
MonitoredRoom = null;
DeadBodiesInRoom = [];
PlayersVentedInRoom = [];
PlayersShiftedInRoom = [];
playerId.SetAbilityUseLimit(AbilityUseLimit.GetInt());
}
public override void Init()
{
On = false;
}
public override void OnPet(PlayerControl pc)
{
if (MonitoredRoom == null || MonitoredRoom == default || MonitoredRoom == default(PlainShipRoom)) MonitoredRoom = pc.GetPlainShipRoom();
else DisplayRoomInfo(pc);
}
void DisplayRoomInfo(PlayerControl pc)
{
if (pc.GetAbilityUseLimit() < 1) return;
pc.RpcRemoveAbilityUse();
var players = Main.AllAlivePlayerControls.Where(IsInMonitoredRoom).Select(x => Utils.ColorString(Main.PlayerColors[x.PlayerId], x.GetRealName())).Join();
var bodies = GetColoredNames(DeadBodiesInRoom);
var vented = GetColoredNames(PlayersVentedInRoom);
var shifted = GetColoredNames(PlayersShiftedInRoom);
var noDataString = Translator.GetString("Sentry.Notify.Info.NoData");
if (players.Length == 0) players = noDataString;
if (bodies.Length == 0) bodies = noDataString;
if (vented.Length == 0) vented = noDataString;
if (shifted.Length == 0) shifted = noDataString;
pc.Notify(string.Format(Translator.GetString("Sentry.Notify.Info"), players, bodies, vented, shifted), ShowInfoDuration.GetInt());
return;
static string GetColoredNames(IEnumerable<byte> ids) => ids.Where(x => Utils.GetPlayerById(x) != null).Select(x => Utils.ColorString(Main.PlayerColors[x], Utils.GetPlayerById(x).GetRealName())).Join();
}
public bool IsInMonitoredRoom(PlayerControl pc) => MonitoredRoom != null && pc.GetPlainShipRoom() == MonitoredRoom;
public void OnAnyoneShapeshiftLoop(PlayerControl shapeshifter)
{
if (IsInMonitoredRoom(shapeshifter)) PlayersShiftedInRoom.Add(shapeshifter.PlayerId);
}
public static void OnAnyoneMurder(PlayerControl target)
{
foreach (var state in Main.PlayerStates.Values)
{
if (state.Role is Sentry st && st.IsInMonitoredRoom(target))
{
st.DeadBodiesInRoom.Add(target.PlayerId);
}
}
}
public static void OnAnyoneEnterVent(PlayerControl pc)
{
foreach (var state in Main.PlayerStates.Values)
{
if (state.Role is Sentry st && st.IsInMonitoredRoom(pc))
{
st.PlayersVentedInRoom.Add(pc.PlayerId);
}
}
}
public static string GetSuffix(PlayerControl seer)
{
if (!PlayersKnowAboutCamera.GetBool()) return string.Empty;
if (Main.PlayerStates[seer.PlayerId].Role is Sentry s && s.MonitoredRoom != null) return string.Format(Translator.GetString("Sentry.Suffix.Self"), Translator.GetString($"{s.MonitoredRoom.RoomId}"));
foreach (var state in Main.PlayerStates.Values)
{
if (state.Role is Sentry st && st.IsInMonitoredRoom(seer))
{
return string.Format(Translator.GetString("Sentry.Suffix.MonitoredRoom"), CustomRoles.Sentry.ToColoredString());
}
}
return string.Empty;
}
private readonly HashSet<byte> LastNotified = [];
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
{
if (!GameStates.IsInTask || lowLoad) return;
bool nowInMonitoredRoom = IsInMonitoredRoom(pc);
bool wasInMonitoredRoom = LastNotified.Contains(pc.PlayerId);
switch (nowInMonitoredRoom)
{
case true when !wasInMonitoredRoom:
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
LastNotified.Add(pc.PlayerId);
break;
case false when wasInMonitoredRoom:
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
LastNotified.Remove(pc.PlayerId);
break;
}
}
}
}
+184
View File
@@ -0,0 +1,184 @@
using AmongUs.GameOptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace EHR.Roles.Impostor
{
internal static class GeneratorStatic
{
enum Action
{
Kill,
Shapeshift,
Vent,
Sabotage
}
private static int GetDefaultCost(this Action action) => action switch
{
Action.Kill => 5,
Action.Shapeshift => 10,
Action.Vent => 20,
Action.Sabotage => 15,
_ => 15
};
internal class Generator : RoleBase
{
public static bool On;
public override bool IsEnable => On;
private static OptionItem ChargesGainedPerSecond;
private static OptionItem StartingCharges;
private static OptionItem ChargesLostWhileShiftedPerSecond;
private static OptionItem MaxChargesStored;
private static readonly Dictionary<Action, OptionItem> ActionCostSettings = [];
private static int Gain;
private static int ChargesLostWhileShifted;
private static Dictionary<Action, int> ActionCosts = [];
private int Charges;
private long LastUpdate;
public static void SetupCustomOption()
{
const int id = 11380;
Options.SetupRoleOptions(id, TabGroup.ImpostorRoles, CustomRoles.Generator);
ChargesGainedPerSecond = IntegerOptionItem.Create(id + 2, "Generator.ChargesGainedEverySecond", new(1, 30, 1), 1, TabGroup.ImpostorRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Generator]);
StartingCharges = IntegerOptionItem.Create(id + 3, "Generator.StartingCharges", new(0, 100, 1), 0, TabGroup.ImpostorRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Generator]);
ChargesLostWhileShiftedPerSecond = IntegerOptionItem.Create(id + 4, "Generator.ChargesLostWhileShiftedPerSecond", new(1, 30, 1), 1, TabGroup.ImpostorRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Generator]);
MaxChargesStored = IntegerOptionItem.Create(id + 5, "Generator.MaxChargesStored", new(0, 200, 1), 100, TabGroup.ImpostorRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Generator]);
foreach (var action in EnumHelper.GetAllValues<Action>())
{
var option = IntegerOptionItem.Create(id + 6 + (int)action, $"Generator.{action}.Cost", new(0, 100, 1), action.GetDefaultCost(), TabGroup.ImpostorRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Generator]);
ActionCostSettings.Add(action, option);
}
}
public override void Add(byte playerId)
{
On = true;
Charges = StartingCharges.GetInt();
playerId.SetAbilityUseLimit(Charges);
LastUpdate = Utils.TimeStamp + 8;
}
public override void Init()
{
On = false;
Gain = ChargesGainedPerSecond.GetInt();
ChargesLostWhileShifted = ChargesLostWhileShiftedPerSecond.GetInt();
ActionCosts = ActionCostSettings.ToDictionary(x => x.Key, x => x.Value.GetInt());
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
AURoleOptions.ShapeshifterCooldown = 1f;
AURoleOptions.ShapeshifterDuration = 300f;
}
public override void OnFixedUpdate(PlayerControl pc)
{
if (!GameStates.IsInTask || pc == null || !pc.IsAlive()) return;
long now = Utils.TimeStamp;
if (now <= LastUpdate) return;
LastUpdate = now;
int beforeCharges = Charges;
bool shifted = pc.IsShifted();
if (shifted) Charges -= ChargesLostWhileShifted;
else Charges += Gain;
Charges = Math.Clamp(Charges, 0, MaxChargesStored.GetInt());
if (Charges <= 0 && shifted)
{
pc.Notify(Translator.GetString("Generator.Notify.RevertShapeshift"));
pc.RpcShapeshift(pc, !Options.DisableShapeshiftAnimations.GetBool());
pc.RpcResetAbilityCooldown();
}
if (beforeCharges != Charges) pc.SetAbilityUseLimit(Charges, log: false);
}
public override void OnCoEnterVent(PlayerPhysics physics, int ventId)
{
int cost = ActionCosts[Action.Vent];
if (Charges < cost)
{
_ = new LateTask(() =>
{
physics.RpcBootFromVent(ventId);
physics.myPlayer.Notify(string.Format(Translator.GetString("Generator.Notify.NotEnoughCharges"), cost));
}, 0.5f, "Generator not enough charges to vent");
return;
}
Charges -= cost;
}
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (!base.OnCheckMurder(killer, target)) return false;
int cost = ActionCosts[Action.Kill];
if (Charges < cost)
{
killer.Notify(string.Format(Translator.GetString("Generator.Notify.NotEnoughCharges"), cost));
return false;
}
return true;
}
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
Charges -= ActionCosts[Action.Kill];
}
public override bool OnSabotage(PlayerControl pc)
{
if (!pc.IsAlive()) return true;
int cost = ActionCosts[Action.Sabotage];
if (Charges < cost)
{
pc.Notify(string.Format(Translator.GetString("Generator.Notify.NotEnoughCharges"), cost));
return false;
}
Charges -= cost;
return true;
}
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
if (!shapeshifting) return true;
int cost = ActionCosts[Action.Shapeshift];
if (Charges < cost)
{
shapeshifter.Notify(string.Format(Translator.GetString("Generator.Notify.NotEnoughCharges"), cost));
return false;
}
Charges -= cost;
return true;
}
}
}
}
+1 -1
View File
@@ -221,7 +221,7 @@ public class Witch : RoleBase
{
if (Main.PlayerStates[id].Role is Witch { IsEnable: true } wc && wc.IsSpelled(target))
{
return Utils.ColorString(Palette.ImpostorRed, "†");
return Utils.ColorString(wc.IsHM ? Utils.GetRoleColor(CustomRoles.HexMaster) : Palette.ImpostorRed, "†");
}
}
+10 -5
View File
@@ -34,13 +34,18 @@ public class Amnesiac : RoleBase
public static void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Amnesiac);
RememberMode = StringOptionItem.Create(Id + 9, "RememberMode", RememberModes, 0, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
RememberCooldown = FloatOptionItem.Create(Id + 10, "RememberCooldown", new(0f, 180f, 2.5f), 5f, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac])
RememberMode = StringOptionItem.Create(Id + 9, "RememberMode", RememberModes, 0, TabGroup.NeutralRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
RememberCooldown = FloatOptionItem.Create(Id + 10, "RememberCooldown", new(0f, 180f, 2.5f), 5f, TabGroup.NeutralRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac])
.SetValueFormat(OptionFormat.Seconds);
RefugeeKillCD = FloatOptionItem.Create(Id + 11, "RefugeeKillCD", new(0f, 180f, 2.5f), 25f, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac])
RefugeeKillCD = FloatOptionItem.Create(Id + 11, "RefugeeKillCD", new(0f, 180f, 2.5f), 25f, TabGroup.NeutralRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac])
.SetValueFormat(OptionFormat.Seconds);
IncompatibleNeutralMode = StringOptionItem.Create(Id + 12, "IncompatibleNeutralMode", AmnesiacIncompatibleNeutralMode, 0, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
CanVent = BooleanOptionItem.Create(Id + 13, "CanVent", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
IncompatibleNeutralMode = StringOptionItem.Create(Id + 12, "IncompatibleNeutralMode", AmnesiacIncompatibleNeutralMode, 0, TabGroup.NeutralRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
CanVent = BooleanOptionItem.Create(Id + 13, "CanVent", false, TabGroup.NeutralRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
}
public override void Init()
+48
View File
@@ -0,0 +1,48 @@
using EHR.Patches;
namespace EHR.Roles.Impostor
{
internal class Cherokious : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public static OptionItem KillCooldown;
public static Options.OverrideTasksData Tasks;
public static void SetupCustomOption()
{
const int id = 13860;
Options.SetupRoleOptions(id, TabGroup.NeutralRoles, CustomRoles.Cherokious);
KillCooldown = IntegerOptionItem.Create(id + 2, "KillCooldown", new(0, 60, 1), 15, TabGroup.NeutralRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Cherokious])
.SetValueFormat(OptionFormat.Seconds);
Tasks = Options.OverrideTasksData.Create(id + 3, TabGroup.NeutralRoles, CustomRoles.Cherokious);
}
public override void Add(byte playerId)
{
On = true;
playerId.SetAbilityUseLimit(0);
}
public override void Init()
{
On = false;
}
public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
{
pc.RpcIncreaseAbilityUseLimitBy(1);
}
public override void OnPet(PlayerControl pc)
{
if (pc.GetAbilityUseLimit() < 1) return;
var target = ExternalRpcPetPatch.SelectKillButtonTarget(pc);
if (target != null && pc.RpcCheckAndMurder(target)) pc.RpcRemoveAbilityUse();
}
public override bool CanUseKillButton(PlayerControl pc) => false;
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ namespace EHR.Roles.Neutral
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(true);
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
public override void OnPet(PlayerControl pc)
{
+1 -1
View File
@@ -96,7 +96,7 @@ public class Glitch : RoleBase
public override bool IsEnable => playerIdList.Count > 0;
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
void SendRPCSyncTimers()
{
+1 -1
View File
@@ -52,7 +52,7 @@ namespace EHR.Roles.Neutral
public override bool IsEnable => HookshotId != byte.MaxValue;
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
void SendRPC()
+1 -1
View File
@@ -88,7 +88,7 @@ public class Jackal : RoleBase
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public static bool CanRecruit(byte id) => id.GetAbilityUseLimit() > 0;
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool() && pc.IsAlive();
public override void SetButtonTexts(HudManager hud, byte id)
{
+1 -1
View File
@@ -107,7 +107,7 @@ public class Magician : RoleBase
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
+1 -1
View File
@@ -139,6 +139,6 @@ namespace EHR.Roles.Neutral
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) => IsEnable && target != null && InfectedPlayers.Contains(target.PlayerId);
public override void AfterMeetingTasks() => Mycologist_.AddAbilityCD(CD.GetInt());
public override bool CanUseImpostorVentButton(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => SpreadAction.GetValue() == 1;
public override bool CanUseSabotage(PlayerControl pc) => SpreadAction.GetValue() == 1 && pc.IsAlive();
}
}
+8 -9
View File
@@ -1,42 +1,41 @@
using AmongUs.GameOptions;
using System.Collections.Generic;
using static EHR.Options;
namespace EHR.Roles.Neutral;
public class NSerialKiller : RoleBase
{
public static bool On;
private const int Id = 12800;
public static List<byte> playerIdList = [];
private static OptionItem KillCooldown;
public static OptionItem CanVent;
private static OptionItem CanVent;
private static OptionItem HasImpostorVision;
public static void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.NSerialKiller);
KillCooldown = FloatOptionItem.Create(Id + 10, "KillCooldown", new(0f, 180f, 2.5f), 22.5f, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller])
KillCooldown = FloatOptionItem.Create(Id + 2, "KillCooldown", new(0f, 180f, 2.5f), 22.5f, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller])
.SetValueFormat(OptionFormat.Seconds);
CanVent = BooleanOptionItem.Create(Id + 11, "CanVent", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller]);
HasImpostorVision = BooleanOptionItem.Create(Id + 13, "ImpostorVision", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller]);
CanVent = BooleanOptionItem.Create(Id + 3, "CanVent", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller]);
HasImpostorVision = BooleanOptionItem.Create(Id + 4, "ImpostorVision", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.NSerialKiller]);
}
public override void Init()
{
playerIdList = [];
On = false;
}
public override void Add(byte playerId)
{
playerIdList.Add(playerId);
On = true;
if (!AmongUsClient.Instance.AmHost) return;
if (!Main.ResetCamPlayerList.Contains(playerId))
Main.ResetCamPlayerList.Add(playerId);
}
public override bool IsEnable => playerIdList.Count > 0;
public override bool IsEnable => On;
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
+46
View File
@@ -0,0 +1,46 @@
namespace EHR.Roles.Impostor
{
internal class SchrodingersCat : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public static OptionItem WinsWithCrewIfNotAttacked;
public static void SetupCustomOption()
{
const int id = 13840;
Options.SetupRoleOptions(id, TabGroup.NeutralRoles, CustomRoles.SchrodingersCat);
WinsWithCrewIfNotAttacked = BooleanOptionItem.Create(id + 2, "SchrodingersCat.WinsWithCrewIfNotAttacked", true, TabGroup.NeutralRoles, false)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.SchrodingersCat]);
}
public override void Add(byte playerId)
{
On = true;
}
public override void Init()
{
On = false;
}
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
{
var killerRole = killer.GetCustomRole();
if (Options.SingleRoles.Contains(killerRole)) killerRole = CustomRoles.Amnesiac;
target.RpcSetCustomRole(killerRole);
killer.SetKillCooldown(5f);
killer.Notify(string.Format(Translator.GetString("SchrodingersCat.Notify.KillerRecruited"), target.GetRealName(), CustomRoles.SchrodingersCat.ToColoredString()));
target.Notify(string.Format(Translator.GetString("SchrodingersCat.Notify.RecruitedByKiller"), killer.GetRealName(), killerRole.ToColoredString()));
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer);
return false;
}
}
}
+1 -1
View File
@@ -26,5 +26,5 @@ public class Sidekick : RoleBase
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Jackal.KillCooldownSK.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(Jackal.HasImpostorVision.GetBool());
public override void SetButtonTexts(HudManager __instance, byte id) => __instance.SabotageButton.ToggleVisible(Jackal.CanSabotageSK.GetBool());
public override bool CanUseSabotage(PlayerControl pc) => Jackal.CanSabotageSK.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => Jackal.CanSabotageSK.GetBool() && pc.IsAlive();
}
+1 -1
View File
@@ -85,7 +85,7 @@ namespace EHR.Roles.Neutral
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
public override bool OnSabotage(PlayerControl pc)
{
+1 -1
View File
@@ -60,7 +60,7 @@ namespace EHR.Roles.Neutral
public override bool CanUseSabotage(PlayerControl pc)
{
return true;
return pc.IsAlive();
}
public override bool OnSabotage(PlayerControl pc)
+1 -1
View File
@@ -43,5 +43,5 @@ public class Traitor : RoleBase
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool() && pc.IsAlive();
}
+1 -1
View File
@@ -88,7 +88,7 @@ public class WeaponMaster : RoleBase
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool CanUseSabotage(PlayerControl pc) => true;
public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive();
public override void ApplyGameOptions(IGameOptions opt, byte id)
{
+1 -1
View File
@@ -51,7 +51,7 @@ namespace EHR
public virtual bool CanUseSabotage(PlayerControl pc)
{
return pc.Is(Team.Impostor) || pc.Is(CustomRoles.Mischievous) || (pc.Is(CustomRoles.Bloodlust) && Bloodlust.HasImpVision.GetBool());
return pc.Is(Team.Impostor) || pc.Is(CustomRoles.Mischievous) || (pc.Is(CustomRoles.Bloodlust) && Bloodlust.HasImpVision.GetBool()) && pc.IsAlive();
}
public virtual void ApplyGameOptions(IGameOptions opt, byte playerId)
+15 -7
View File
@@ -35,8 +35,8 @@ public class Main : BasePlugin
public const string DebugKeySalt = "59687b";
public static ConfigEntry<string> DebugKeyInput { get; private set; }
public const string PluginGuid = "com.gurge44.endlesshostroles";
public const string PluginVersion = "3.1.1";
public const string PluginDisplayVersion = "3.1.1";
public const string PluginVersion = "3.1.4";
public const string PluginDisplayVersion = "3.1.4";
public static readonly string SupportedAUVersion = "2024.3.5";
public Harmony Harmony { get; } = new(PluginGuid);
@@ -305,6 +305,7 @@ public class Main : BasePlugin
{ CustomRoles.Express, "#00ffff" },
{ CustomRoles.NiceEraser, "#00a5ff" },
{ CustomRoles.TaskManager, "#00ffa5" },
{ CustomRoles.Adventurer, "#fbbb04" },
{ CustomRoles.Randomizer, "#fcba03" },
{ CustomRoles.Beacon, "#a3fdff" },
{ CustomRoles.Rabbit, "#88d2ff" },
@@ -321,6 +322,7 @@ public class Main : BasePlugin
{ CustomRoles.Gaulois, "#42d1f5" },
{ CustomRoles.Druid, "#ffb694" },
{ CustomRoles.Autocrat, "#e2ed64" },
{ CustomRoles.Sentry, "#db55f2" },
{ CustomRoles.Perceiver, "#ebeb34" },
{ CustomRoles.Convener, "#34eb7a" },
{ CustomRoles.Mathematician, "#eb3474" },
@@ -334,7 +336,7 @@ public class Main : BasePlugin
{ CustomRoles.NiceHacker, "#75fa4c" },
{ CustomRoles.Aid, "#D7BDE2" },
{ CustomRoles.DonutDelivery, "#a46efa" },
{ CustomRoles.Analyzer, "#33ddff" },
{ CustomRoles.Analyst, "#33ddff" },
{ CustomRoles.Escort, "#ff94e6" },
{ CustomRoles.Spy, "#34495E" },
{ CustomRoles.Doormaster, "#7FB3D5" },
@@ -431,6 +433,7 @@ public class Main : BasePlugin
{ CustomRoles.Sprayer, "#ffc038" },
{ CustomRoles.PlagueDoctor, "#ff6633" },
{ CustomRoles.Postman, "#00b893" },
{ CustomRoles.SchrodingersCat, "#616161" },
{ CustomRoles.Impartial, "#4287f5" },
{ CustomRoles.Predator, "#c73906" },
{ CustomRoles.Reckless, "#6e000d" },
@@ -445,6 +448,7 @@ public class Main : BasePlugin
{ CustomRoles.Agitater, "#F4A460" },
{ CustomRoles.BloodKnight, "#630000" },
{ CustomRoles.Juggernaut, "#A41342" },
{ CustomRoles.Cherokious, "#de4b9e" },
{ CustomRoles.Parasite, "#ff1919" },
{ CustomRoles.Crewpostor, "#ff1919" },
{ CustomRoles.Refugee, "#ff1919" },
@@ -610,8 +614,8 @@ public class Main : BasePlugin
{
AllRoleClasses.AddRange(Assembly.GetAssembly(typeof(RoleBase))!
.GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(RoleBase)))
.Select(type => (RoleBase)Activator.CreateInstance(type, null)));
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(RoleBase)))
.Select(t => (RoleBase)Activator.CreateInstance(t, null)));
AllRoleClasses.Sort();
}
catch (Exception e)
@@ -711,7 +715,10 @@ public enum CustomWinner
Doomsayer = CustomRoles.Doomsayer,
RuthlessRomantic = CustomRoles.RuthlessRomantic,
Doppelganger = CustomRoles.Doppelganger,
Imitator = CustomRoles.Imitator
Imitator = CustomRoles.Imitator,
Cherokious = CustomRoles.Cherokious,
Bloodlust = CustomRoles.Bloodlust
}
public enum AdditionalWinners
@@ -740,7 +747,8 @@ public enum AdditionalWinners
Postman = CustomRoles.Postman,
Impartial = CustomRoles.Impartial,
Predator = CustomRoles.Predator,
SoulHunter = CustomRoles.SoulHunter
SoulHunter = CustomRoles.SoulHunter,
SchrodingersCat = CustomRoles.SchrodingersCat
}
public enum SuffixModes