Add Kamikaze, Fix Disperser, Optimize Pet Suffix

This commit is contained in:
Gurge44 committed 2024-01-08 19:46:44 +01:00
1 parent 1b1add7059
commit 38786daa7e
18 files changed
+249 -28

No files matched your search

+4
View File
@@ -52,6 +52,8 @@ internal static class CustomRolesHelper
CustomRoles.SpeedBooster => CustomRoles.Crewmate,
CustomRoles.Dictator => CustomRoles.Crewmate,
CustomRoles.Inhibitor => CustomRoles.Impostor,
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
CustomRoles.Kamikaze => CustomRoles.Impostor,
CustomRoles.Librarian => CustomRoles.Shapeshifter,
CustomRoles.Cantankerous => CustomRoles.Impostor,
CustomRoles.YinYanger => CustomRoles.Impostor,
@@ -754,6 +756,8 @@ internal static class CustomRolesHelper
CustomRoles.SerialKiller or
CustomRoles.Underdog or
CustomRoles.Inhibitor or
CustomRoles.Kidnapper or
CustomRoles.Kamikaze or
CustomRoles.Librarian or
CustomRoles.Cantankerous or
CustomRoles.Duellist or
+3 -6
View File
@@ -22,10 +22,7 @@ static class DoubleTrigger
{
PlayerIdList.Add(killer.PlayerId);
}
public static bool CanDoubleTrigger(this PlayerControl killer)
{
return PlayerIdList.Contains(killer.PlayerId);
}
public static bool CanDoubleTrigger(this PlayerControl killer) => PlayerIdList.Contains(killer.PlayerId);
/// <summary>
/// Checks for whether the killer pressed their kill button twice on the same player
@@ -41,7 +38,7 @@ static class DoubleTrigger
{
if (FirstTriggerTarget[killer.PlayerId] != target.PlayerId)
{
//2回目がターゲットずれてたら最初の相手にシングルアクション
// If the second target is off target, single action on the first opponent.
return false;
}
Logger.Info($"{killer.name} DoDoubleAction", "DoubleTrigger");
@@ -50,7 +47,7 @@ static class DoubleTrigger
if (doAction) FirstTriggerAction.Remove(killer.PlayerId);
return true;
}
//シングルアクション時はキル間隔を無視
// Ignore kill interval during single action
CheckMurderPatch.TimeSinceLastKill.Remove(killer.PlayerId);
FirstTriggerTimer.Add(killer.PlayerId, 1f);
FirstTriggerTarget.Add(killer.PlayerId, target.PlayerId);
+5 -1
View File
@@ -497,6 +497,8 @@ static class ExtendedPlayerControl
CustomRoles.RiftMaker => true,
CustomRoles.Hitman => true,
CustomRoles.Inhibitor => true,
CustomRoles.Kidnapper => true,
CustomRoles.Kamikaze => true,
CustomRoles.Cantankerous => true,
CustomRoles.YinYanger => true,
CustomRoles.Duellist => true,
@@ -588,7 +590,7 @@ static class ExtendedPlayerControl
CustomRoles.PlagueBearer => true,
CustomRoles.Pestilence => true,
_ => pc.Is(CustomRoleTypes.Impostor),
_ => pc.Is(CustomRoleTypes.Impostor) || pc.GetCustomRole().IsNK() || pc.GetCustomRole().IsTasklessCrewmate(),
};
}
public static bool CanUseKillButton(this PlayerControl pc)
@@ -627,6 +629,8 @@ static class ExtendedPlayerControl
CustomRoles.YinYanger => pc.IsAlive(),
CustomRoles.Librarian => pc.IsAlive() && Librarian.CanUseKillButton(pc),
CustomRoles.Cantankerous => pc.IsAlive() && Cantankerous.CanUseKillButton(pc.PlayerId),
CustomRoles.Kidnapper => pc.IsAlive(),
CustomRoles.Kamikaze => pc.IsAlive(),
CustomRoles.Inhibitor => !IsActive(SystemTypes.Electrical) && !IsActive(SystemTypes.Comms) && !IsActive(SystemTypes.MushroomMixupSabotage) && !IsActive(SystemTypes.Laboratory) && !IsActive(SystemTypes.LifeSupp) && !IsActive(SystemTypes.Reactor) && !IsActive(SystemTypes.HeliSabotage),
CustomRoles.Saboteur => IsActive(SystemTypes.Electrical) || IsActive(SystemTypes.Comms) || IsActive(SystemTypes.MushroomMixupSabotage) || IsActive(SystemTypes.Laboratory) || IsActive(SystemTypes.LifeSupp) || IsActive(SystemTypes.Reactor) || IsActive(SystemTypes.HeliSabotage),
CustomRoles.Sniper => Sniper.CanUseKillButton(pc),
@@ -652,6 +652,10 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
AURoleOptions.EngineerCooldown = Sentinel.PatrolCooldown.GetFloat();
AURoleOptions.EngineerInVentMaxTime = 1f;
break;
case CustomRoles.Kidnapper:
AURoleOptions.ShapeshifterCooldown = Kidnapper.SSCD.GetFloat();
AURoleOptions.ShapeshifterDuration = 1f;
break;
case CustomRoles.Spiritcaller:
opt.SetVision(Spiritcaller.ImpostorVision.GetBool());
break;
+1
View File
@@ -250,6 +250,7 @@ public class PlayerState(byte playerId)
Demolished,
YinYanged,
Hack,
Kamikazed,
etc = -1,
}
+4
View File
@@ -1225,6 +1225,10 @@ public static class Options
InhibitorCDAfterMeetings = FloatOptionItem.Create(1511, "AfterMeetingKillCooldown", new(0f, 180f, 2.5f), 22.5f, TabGroup.ImpostorRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Inhibitor])
.SetValueFormat(OptionFormat.Seconds);
RoleLoadingText = "Impostor roles\nKamikaze";
Kamikaze.SetupCustomOption();
RoleLoadingText = "Impostor roles\nKidnapper";
Kidnapper.SetupCustomOption();
RoleLoadingText = "Impostor roles\nLibrarian";
Librarian.SetupCustomOption();
RoleLoadingText = "Impostor roles\nCantankerous";
+14
View File
@@ -58,6 +58,8 @@ enum CustomRPC
//Roles
SetDrawPlayer,
SyncKamikazeLimit,
KamikazeAddTarget,
SyncMycologist,
SyncBubble,
AddTornado,
@@ -593,6 +595,12 @@ internal class RPCHandlerPatch
case CustomRPC.SyncBubble:
Bubble.ReceiveRPC(reader);
break;
case CustomRPC.KamikazeAddTarget:
Kamikaze.ReceiveRPCAddTarget(reader);
break;
case CustomRPC.SyncKamikazeLimit:
Kamikaze.ReceiveRPCSyncLimit(reader);
break;
case CustomRPC.SetCurrentDousingTarget:
byte arsonistId = reader.ReadByte();
byte dousingTargetId = reader.ReadByte();
@@ -1266,6 +1274,12 @@ internal static class RPC
case CustomRoles.EvilDiviner:
EvilDiviner.Add(targetId);
break;
case CustomRoles.Kamikaze:
Kamikaze.Add(targetId);
break;
case CustomRoles.Disperser:
Disperser.Add(targetId);
break;
case CustomRoles.Medic:
Medic.Add(targetId);
break;
+13 -12
View File
@@ -317,23 +317,24 @@ class BeginCrewmatePatch
CustomRoles.Disperser
=> ShipStatus.Instance.VentMoveSounds.FirstOrDefault(),
_ => PlayerControl.LocalPlayer.Is(RoleTypes.Impostor) ? PlayerControl.LocalPlayer.Is(RoleTypes.Shapeshifter) ? GetIntroSound(RoleTypes.Shapeshifter) : GetIntroSound(RoleTypes.Impostor) : GetIntroSound(RoleTypes.Crewmate),
_ => role.GetCustomRoleTypes() switch
{
CustomRoleTypes.Impostor => GetIntroSound(RoleTypes.Impostor),
CustomRoleTypes.Crewmate => GetIntroSound(RoleTypes.Crewmate),
CustomRoleTypes.Neutral => GetIntroSound(RoleTypes.Shapeshifter),
_ => GetIntroSound(RoleTypes.Crewmate),
}
};
}
catch (Exception ex)
{
switch (role.GetCustomRoleTypes())
PlayerControl.LocalPlayer.Data.Role.IntroSound = role.GetCustomRoleTypes() switch
{
case CustomRoleTypes.Impostor:
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Impostor);
break;
case CustomRoleTypes.Crewmate:
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Crewmate);
break;
case CustomRoleTypes.Neutral:
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Shapeshifter);
break;
}
CustomRoleTypes.Impostor => GetIntroSound(RoleTypes.Impostor),
CustomRoleTypes.Crewmate => GetIntroSound(RoleTypes.Crewmate),
CustomRoleTypes.Neutral => GetIntroSound(RoleTypes.Shapeshifter),
_ => GetIntroSound(RoleTypes.Crewmate),
};
Logger.Warn($"Could not set intro sound\n{ex}", "IntroSound");
}
+19 -1
View File
@@ -217,6 +217,9 @@ class CheckMurderPatch
case CustomRoles.BountyHunter:
BountyHunter.OnCheckMurder(killer, target);
break;
case CustomRoles.Kamikaze:
if (!Kamikaze.OnCheckMurder(killer, target)) return false;
break;
case CustomRoles.Reckless:
Reckless.OnCheckMurder(killer);
break;
@@ -1210,6 +1213,7 @@ class MurderPlayerPatch
break;
case CustomRoles.Hacker:
Hacker.HackLimit[killer.PlayerId] += Hacker.HackerAbilityUseGainWithEachKill.GetFloat();
Hacker.SendRPC(killer.PlayerId);
break;
case CustomRoles.Camouflager:
Camouflager.CamoLimit[killer.PlayerId] += Camouflager.CamoAbilityUseGainWithEachKill.GetFloat();
@@ -1235,6 +1239,10 @@ class MurderPlayerPatch
case CustomRoles.Twister:
Twister.TwistLimit[killer.PlayerId] += Twister.TwisterAbilityUseGainWithEachKill.GetFloat();
break;
case CustomRoles.Kamikaze:
Kamikaze.MarkLimit[killer.PlayerId] += Kamikaze.KamikazeAbilityUseGainWithEachKill.GetFloat();
Kamikaze.SendRPCSyncLimit(killer.PlayerId);
break;
}
if (killer.Is(CustomRoles.TicketsStealer) && killer.PlayerId != target.PlayerId)
@@ -1367,6 +1375,10 @@ class ShapeshiftPatch
EvilTracker.OnShapeshift(shapeshifter, target, shapeshifting);
isSSneeded = false;
break;
case CustomRoles.Kidnapper:
Kidnapper.OnShapeshift(shapeshifter, target);
isSSneeded = false;
break;
case CustomRoles.RiftMaker:
RiftMaker.OnShapeshift(shapeshifter, shapeshifting);
isSSneeded = false;
@@ -2011,6 +2023,7 @@ class FixedUpdatePatch
private static int LevelKickBufferTime = 10;
private static readonly Dictionary<byte, int> BufferTime = [];
private static readonly Dictionary<byte, int> DeadBufferTime = [];
private static readonly Dictionary<byte, long> LastUpdate = [];
public static async void Postfix(PlayerControl __instance)
{
@@ -2137,6 +2150,9 @@ class FixedUpdatePatch
case CustomRoles.BountyHunter:
BountyHunter.FixedUpdate(player);
break;
case CustomRoles.Kamikaze when !lowLoad:
Kamikaze.OnFixedUpdate();
break;
case CustomRoles.Glitch when !lowLoad:
Glitch.UpdateHackCooldown(player);
break;
@@ -2233,7 +2249,8 @@ class FixedUpdatePatch
}
}
if (!lowLoad && Options.UsePets.GetBool() && GameStates.IsInTask)
long now = GetTimeStamp();
if (!lowLoad && Options.UsePets.GetBool() && GameStates.IsInTask && (!LastUpdate.TryGetValue(playerId, out var lastPetNotify) || lastPetNotify < now))
{
if (Main.AbilityCD.TryGetValue(playerId, out var timer))
{
@@ -2242,6 +2259,7 @@ class FixedUpdatePatch
Main.AbilityCD.Remove(playerId);
}
if (!player.IsModClient()) NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
LastUpdate[playerId] = now;
}
}
+7 -3
View File
@@ -141,10 +141,14 @@ class OnPlayerLeftPatch
Spiritualist.RemoveTarget();
if (data.Character.PlayerId == Postman.Target)
Postman.SetNewTarget();
if (Main.PlayerStates[data.Character.PlayerId].deathReason == PlayerState.DeathReason.etc) //死因が設定されていなかったら
PlayerState state = Main.PlayerStates[data.Character.PlayerId];
if (state.deathReason == PlayerState.DeathReason.etc) // If no cause of death was established
{
Main.PlayerStates[data.Character.PlayerId].deathReason = PlayerState.DeathReason.Disconnected;
Main.PlayerStates[data.Character.PlayerId].SetDead();
state.deathReason = PlayerState.DeathReason.Disconnected;
}
if (!state.IsDead)
{
state.SetDead();
}
NameNotifyManager.Notice.Remove(data.Character.PlayerId);
AntiBlackout.OnDisconnect(data.Character.Data);
+4
View File
@@ -196,6 +196,7 @@ internal class ChangeRoleSettings
BountyHunter.Init();
SerialKiller.Init();
EvilDiviner.Init();
Kamikaze.Init();
FireWorks.Init();
NiceSwapper.Init();
Pickpocket.Init();
@@ -804,6 +805,9 @@ internal class SelectRolesPatch
case CustomRoles.EvilDiviner:
EvilDiviner.Add(pc.PlayerId);
break;
case CustomRoles.Kamikaze:
Kamikaze.Add(pc.PlayerId);
break;
case CustomRoles.Ritualist:
Ritualist.Add(pc.PlayerId);
break;
+10 -2
View File
@@ -1604,6 +1604,7 @@
"DeathReason.Jinx": "Jinxed",
"DeathReason.Demolished": "Demolished",
"DeathReason.YinYanged": "Yin Yanged",
"DeathReason.Kamikazed": "Kamikazed",
"DeathReason.etc": "Other",
"DeathReason.Hack": "Hacked",
"DeathReason.Curse": "Cursed",
@@ -2324,7 +2325,7 @@
"DivinatorCheck.Result4": "<b>{0}</b> is either Pestilence, Electric, Plaguebearer, Donut Delivery, Observer, Blood Knight, Guardian, or Wildling",
"DivinatorCheck.Result5": "<b>{0}</b> is either Bard, Sentinel, Ricochet, Juggernaut, Security Guard, Arrogance, Killing Machine, or Butcher",
"DivinatorCheck.Result6": "<b>{0}</b> is either Coroner, Mole, Stealth, Evil Tracker, Mortician, Tracefinder, Romantic, or Tracker",
"DivinatorCheck.Result7": "<b>{0}</b> is either Bodyguard, Bomber, Lookout, Vengeful Romantic, Ruthless Romantic, Fireworker, or Nuker",
"DivinatorCheck.Result7": "<b>{0}</b> is either Bodyguard, Bomber, Lookout, Vengeful Romantic, Ruthless Romantic, Kidnapper, Fireworker, or Nuker",
"DivinatorCheck.Result8": "<b>{0}</b> is either Bounty Hunter, Sprayer, Mafioso, Detective, Hater, Cleaner, Medusa, or Psychic",
"DivinatorCheck.Result9": "<b>{0}</b> is either Convict, Penguin, Hookshot, Executioner, Lawyer, Snitch, Disperser, or Doctor",
"DivinatorCheck.Result10": "<b>{0}</b> is either Councillor, Escort, Bubble, Shiftguard, Dictator, Judge, Cleanser, or Cursed Wolf",
@@ -2335,7 +2336,7 @@
"DivinatorCheck.Result15": "<b>{0}</b> is either Infectious, Detour, Virus, Agitator, Monarch, Revolutionist, Express, or Cultist",
"DivinatorCheck.Result16": "<b>{0}</b> is either Innocent, Gaulois, Sapper, Refugee, Bandit, Inhibitor, Mechanic, or Saboteur",
"DivinatorCheck.Result17": "<b>{0}</b> is either Medic, Vector, Analyzer, RiftMaker, Jester, The Glitch, Lurker, or Sunnyboy",
"DivinatorCheck.Result18": "<b>{0}</b> is either Nemesis, Hitman, Gangster, Glitch, Luckey, or Underdog",
"DivinatorCheck.Result18": "<b>{0}</b> is either Nemesis, Hitman, Gangster, Glitch, Kamikaze, Luckey, or Underdog",
"DivinatorCheck.Result19": "<b>{0}</b> is either Evil Guesser, Nice Guesser, Blackmailer, Eclipse, Reckless, Stalker, Camouflager, or Chameleon",
"DivinatorCheck.Result20": "<b>{0}</b> is either Jackal, Sidekick, Duellist, WeaponMaster, Maverick, Opportunist, Pursuer, or Provocateur",
"DivinatorCheck.Result21": "<b>{0}</b> is either Poisoner, Magician, Yin Yanger, Vampire, Pacifist, Head Hunter, Soul Catcher, or Traitor",
@@ -2395,6 +2396,13 @@
"pet_UFO": "UFO",
"pet_D2WormPet": "Worm",
"pet_RANDOM_FOR_EVERYONE": "<color=#ffff00>Random</color>",
"Kidnapper": "Kidnapper",
"KidnapperInfo": "TP Players to you by shifting into them",
"KidnapperInfoLong": "(Impostors):\\nAs the Kidnapper, when you shapeshift into someone, they teleport to you.",
"Kamikaze": "Kamikaze",
"KamikazeInfo": "Mark Players That Die With You",
"KamikazeInfoLong": "(Impostors):\\nAs the Kamikaze, you can mark players by clicking your kill button once on them. When you die, all players that you marked will also die. You can kill normally by double clicking your kill button on someone.",
"KamikazeMarkCD": "Cooldown After Marking",
"Jailor": "Jailor",
"JailorInfo": "Lock up your target in Jail",
"JailorInfoLong": "(Crewmates):\\nAs the Jailor, use your kill button to lock a player in jail. Jailed players cannot be guessed or judged and jailed players can only guess Jailor.",
+2
View File
@@ -167,6 +167,7 @@ public static class Divinator
CustomRoles.Lookout or
CustomRoles.RuthlessRomantic or
CustomRoles.VengefulRomantic or
CustomRoles.Kidnapper or
CustomRoles.Nuker
=> "Result7",
@@ -278,6 +279,7 @@ public static class Divinator
CustomRoles.Doppelganger or
CustomRoles.Hitman or
CustomRoles.Luckey or
CustomRoles.Kamikaze or
CustomRoles.Underdog
=> "Result18",
+6 -2
View File
@@ -80,6 +80,7 @@ namespace TOHE.Roles.Crewmate
private static OptionItem OptionCanSellExperimental;
private static OptionItem OptionSellOnlyHarmfulToEvil;
private static OptionItem OptionSellOnlyHelpfulToCrew;
private static OptionItem OptionGivesAllMoneyOnBribe;
private static int GetCurrentAmountOfMoney(byte playerId) => (addonsSold[playerId] * OptionMoneyPerSell.GetInt()) - (bribedKiller[playerId].Count * OptionMoneyRequiredToBribe.GetInt());
@@ -101,8 +102,9 @@ namespace TOHE.Roles.Crewmate
OptionSellOnlyEnabledAddons = BooleanOptionItem.Create(Id + 16, "MerchantSellOnlyEnabledAddons", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Merchant]);
OptionSellOnlyHarmfulToEvil = BooleanOptionItem.Create(Id + 14, "MerchantSellHarmfulToEvil", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Merchant]);
OptionSellOnlyHelpfulToCrew = BooleanOptionItem.Create(Id + 15, "MerchantSellHelpfulToCrew", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Merchant]);
OptionGivesAllMoneyOnBribe = BooleanOptionItem.Create(Id + 17, "MerchantGivesAllMoneyOnBribe", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Merchant]);
OverrideTasksData.Create(Id + 17, TabGroup.CrewmateRoles, CustomRoles.Merchant);
OverrideTasksData.Create(Id + 18, TabGroup.CrewmateRoles, CustomRoles.Merchant);
}
public static void Init()
{
@@ -204,7 +206,7 @@ namespace TOHE.Roles.Crewmate
return false;
}
if (bribedKiller[target.PlayerId].Contains(killer.PlayerId))
if (IsBribedKiller(killer, target))
{
NotifyBribery(killer, target);
return true;
@@ -224,6 +226,8 @@ namespace TOHE.Roles.Crewmate
private static void NotifyBribery(PlayerControl killer, PlayerControl target)
{
if (OptionGivesAllMoneyOnBribe.GetBool()) addonsSold[target.PlayerId] = 0;
killer.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Merchant), GetString("BribedByMerchant")));
if (OptionNotifyBribery.GetBool())
+1 -1
View File
@@ -42,7 +42,7 @@ public static class Hacker
HackLimit.TryAdd(playerId, HackLimitOpt.GetInt());
}
public static bool IsEnable => playerIdList.Count > 0;
private static void SendRPC(byte playerId)
public static void SendRPC(byte playerId)
{
if (!IsEnable || !Utils.DoRPC) return;
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetHackerHackLimit, SendOption.Reliable, -1);
+121
View File
@@ -0,0 +1,121 @@
using Epic.OnlineServices;
using Hazel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static TOHE.Options;
using static TOHE.Translator;
using static TOHE.Utils;
namespace TOHE.Roles.Impostor
{
internal class Kamikaze
{
private static int Id => 643310;
public static bool IsEnable = false;
private static readonly Dictionary<byte, List<byte>> MarkedPlayers = [];
public static readonly Dictionary<byte, float> MarkLimit = [];
private static OptionItem MarkCD;
private static OptionItem KamikazeLimitOpt;
public static OptionItem KamikazeAbilityUseGainWithEachKill;
public static void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Kamikaze);
MarkCD = FloatOptionItem.Create(Id + 2, "KamikazeMarkCD", new(0f, 180f, 2.5f), 30f, TabGroup.ImpostorRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Kamikaze])
.SetValueFormat(OptionFormat.Seconds);
KamikazeLimitOpt = IntegerOptionItem.Create(Id + 5, "AbilityUseLimit", new(0, 5, 1), 1, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Kamikaze])
.SetValueFormat(OptionFormat.Times);
KamikazeAbilityUseGainWithEachKill = FloatOptionItem.Create(Id + 6, "AbilityUseGainWithEachKill", new(0f, 5f, 0.1f), 0.5f, TabGroup.ImpostorRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Kamikaze])
.SetValueFormat(OptionFormat.Times);
}
public static void Init()
{
MarkedPlayers.Clear();
MarkLimit.Clear();
IsEnable = false;
}
public static void Add(byte playerId)
{
MarkedPlayers[playerId] = [];
MarkLimit[playerId] = KamikazeLimitOpt.GetInt();
IsEnable = true;
}
public static void SendRPCSyncLimit(byte playerId)
{
if (!IsEnable || !DoRPC || playerId == 0) return;
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncKamikazeLimit, SendOption.Reliable, -1);
writer.Write(playerId);
writer.Write(MarkLimit[playerId]);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
private static void SendRPCAddTarget(byte kamikazeId, byte targetId)
{
if (!IsEnable || !DoRPC || kamikazeId == 0) return;
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.KamikazeAddTarget, SendOption.Reliable, -1);
writer.Write(kamikazeId);
writer.Write(targetId);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
public static void ReceiveRPCSyncLimit(MessageReader reader)
{
byte playerId = reader.ReadByte();
float limit = reader.ReadSingle();
MarkLimit[playerId] = limit;
}
public static void ReceiveRPCAddTarget(MessageReader reader)
{
byte playerId = reader.ReadByte();
byte targetId = reader.ReadByte();
MarkedPlayers[playerId].Add(targetId);
}
public static bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (killer == null || target == null) return false;
if (MarkLimit.TryGetValue(killer.PlayerId, out var limit) && limit < 1) return true;
return killer.CheckDoubleTrigger(target, () =>
{
MarkedPlayers[killer.PlayerId].Add(target.PlayerId);
SendRPCAddTarget(killer.PlayerId, target.PlayerId);
killer.SetKillCooldown(MarkCD.GetFloat());
MarkLimit[killer.PlayerId]--;
SendRPCSyncLimit(killer.PlayerId);
});
}
public static void OnFixedUpdate()
{
if (!IsEnable) return;
foreach (var kvp in MarkedPlayers)
{
var kamikazePc = GetPlayerById(kvp.Key);
if (kamikazePc.IsAlive()) continue;
foreach (var id in kvp.Value)
{
var victim = GetPlayerById(id);
if (victim == null || !victim.IsAlive()) continue;
victim.Suicide(PlayerState.DeathReason.Kamikazed, kamikazePc);
}
kvp.Value.Clear();
MarkedPlayers.Remove(kvp.Key);
Logger.Info($"Murder {kamikazePc.GetRealName()}'s targets: {string.Join(", ", kvp.Value.Select(x => GetPlayerById(x).GetNameWithRole()))}", "Kamikaze");
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static TOHE.Options;
using static TOHE.Translator;
using static TOHE.Utils;
namespace TOHE.Roles.Impostor
{
internal class Kidnapper
{
private static int Id => 643300;
public static OptionItem SSCD;
public static void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Kidnapper);
SSCD = FloatOptionItem.Create(Id + 2, "ShapeshiftCooldown", new(0f, 180f, 2.5f), 30f, TabGroup.ImpostorRoles, false)
.SetParent(CustomRoleSpawnChances[CustomRoles.Kidnapper])
.SetValueFormat(OptionFormat.Seconds);
}
public static void OnShapeshift(PlayerControl kidnapper, PlayerControl target)
{
if (kidnapper == null || target == null) return;
target.TP(kidnapper);
}
}
}
+2
View File
@@ -678,6 +678,8 @@ public enum CustomRoles
Hangman,
Hitman,
Inhibitor,
Kamikaze,
Kidnapper,
Minimalism, // Killing Machine
BallLightning, // Lightning
Librarian,