New Role: Gardener

This commit is contained in:
Gurge44 committed 2025-08-07 11:20:01 +02:00
1 parent 7d7de07a73
commit 667ecb2596
9 files changed
+119 -1

No files matched your search

+1
View File
@@ -180,6 +180,7 @@ public enum CustomRoles
Express,
Farseer,
Divinator, // Fortune Teller
Gardener,
Gaulois,
Goose,
Grappler,
+1
View File
@@ -407,6 +407,7 @@ public class Main : BasePlugin
{ CustomRoles.Mathematician, "#eb3474" },
{ CustomRoles.Helper, "#fcf1bd" },
{ CustomRoles.Astral, "#b329d6" },
{ CustomRoles.Gardener, "#00ff00" },
{ CustomRoles.Transmitter, "#c9a11e" },
{ CustomRoles.Imitator, "#c99e28" },
{ CustomRoles.PortalMaker, "#700078" },
+18
View File
@@ -639,6 +639,24 @@ namespace EHR
CreateNetObject("<size=70%><line-height=67%><alpha=#00>█<#2b006b>█<#2b006b>█<#2b006b>█<#2b006b>█<alpha=#00>█<br><alpha=#00>█<#2b006b>█<#fa69ff>█<#fa69ff>█<#2b006b>█<alpha=#00>█<br><alpha=#00>█<#2b006b>█<#fa69ff>█<#fa69ff>█<#2b006b>█<alpha=#00>█<br><alpha=#00>█<#2b006b>█<#fa69ff>█<#fa69ff>█<#2b006b>█<alpha=#00>█<br><alpha=#00>█<#2b006b>█<#fa69ff>█<#fa69ff>█<#2b006b>█<alpha=#00>█<br><alpha=#00>█<#2b006b>█<#2b006b>█<#2b006b>█<#2b006b>█<alpha=#00>█<br></line-height></size>", position);
}
}
internal sealed class Plant : CustomNetObject
{
public bool Spawned;
public Plant(Vector2 position)
{
Position = position;
Spawned = false;
}
public void SpawnIfNotSpawned()
{
if (Spawned) return;
CreateNetObject("<size=100%><line-height=67%><alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<br><alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<br><#00ff15>█<#00ff15>█<alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<br><#00ff15>█<alpha=#00>█<alpha=#00>█<#00ff15>█<alpha=#00>█<#00ff15>█<br><#00ff15>█<alpha=#00>█<#00ff15>█<#00ff15>█<alpha=#00>█<#00ff15>█<br><#00ff15>█<alpha=#00>█<#00ff15>█<alpha=#00>█<alpha=#00>█<#00ff15>█<br></line-height></size>", Position);
Spawned = true;
}
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RawSetName))]
+3
View File
@@ -37,6 +37,7 @@ internal static class CustomRolesHelper
CustomRoles.Amogus,
CustomRoles.Wiper,
CustomRoles.PortalMaker,
CustomRoles.Gardener,
// Add-ons
CustomRoles.Energetic,
@@ -227,6 +228,7 @@ internal static class CustomRolesHelper
CustomRoles.MeetingManager => CustomRoles.Crewmate,
CustomRoles.Bane => CustomRoles.Crewmate,
CustomRoles.Transmitter => CustomRoles.Crewmate,
CustomRoles.Gardener => CustomRoles.Crewmate,
CustomRoles.Imitator => CustomRoles.Crewmate,
CustomRoles.PortalMaker => CustomRoles.Crewmate,
CustomRoles.Astral => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
@@ -1722,6 +1724,7 @@ internal static class CustomRolesHelper
CustomRoles.DovesOfNeace => RoleOptionType.Crewmate_Support,
CustomRoles.Electric => RoleOptionType.Crewmate_Support,
CustomRoles.Escort => RoleOptionType.Crewmate_Support,
CustomRoles.Gardener => RoleOptionType.Crewmate_Support,
CustomRoles.Gaulois => RoleOptionType.Crewmate_Support,
CustomRoles.Grappler => RoleOptionType.Crewmate_Support,
CustomRoles.Grenadier => RoleOptionType.Crewmate_Support,
+1 -1
View File
@@ -36,7 +36,7 @@ public static class Statistics
CustomGameMode gm = Options.CurrentGameMode;
if (gm != CustomGameMode.Standard && GameStates.CurrentServerType is not GameStates.ServerType.Modded and not GameStates.ServerType.Niko)
if (GameStates.CurrentServerType is not GameStates.ServerType.Modded and not GameStates.ServerType.Niko)
{
try
{
+1
View File
@@ -3183,6 +3183,7 @@ public static class Utils
CustomRoles.Mole => Mole.CD.GetInt(),
CustomRoles.Monitor => Monitor.VentCooldown.GetInt(),
CustomRoles.Tether => Tether.VentCooldown.GetInt(),
CustomRoles.Gardener => Gardener.AbilityCooldown.GetInt(),
CustomRoles.Mayor when Mayor.MayorHasPortableButton.GetBool() => (int)Math.Round(Options.AdjustedDefaultKillCooldown),
CustomRoles.Paranoia => (int)Math.Round(Options.AdjustedDefaultKillCooldown),
CustomRoles.Grenadier => Options.GrenadierSkillCooldown.GetInt() + (includeDuration ? Options.GrenadierSkillDuration.GetInt() : 0),
+6
View File
@@ -474,6 +474,12 @@ internal static class CheckMurderPatch
return false;
}
if (!Gardener.OnAnyoneCheckMurder(killer, target))
{
Notify("GardenerPlantNearby");
return false;
}
if (!Socialite.OnAnyoneCheckMurder(killer, target))
{
Notify("SocialiteTarget");
+7
View File
@@ -1845,6 +1845,7 @@
"CovenKillEachOther": "As a Coven member, you cannot (and shouldn't) kill other Coven members",
"CovenKillEntranced": "Coven members cannot kill Entranced players",
"EntrancedKillCoven": "As an Entranced player, you cannot kill Coven members",
"GardenerPlantNearby": "You cannot kill near plants",
"SomeSortOfProtection": "This player is shielded due to a role-specific action",
"HexButtonText": "Hex",
"EscapeeAbilityButtonText": "TP",
@@ -9228,6 +9229,12 @@
"DazzlerResetDazzledVisionOnDeath": "Reset vision of dazzled players on death/eject",
"DazzleCooldown": "Dazzle Cooldown",
"DazzleButtonText": "Dazzle",
"Gardener": "Gardener",
"GardenerInfo": "Plant Plants to Block Kills",
"GardenerInfoLong": "(Crewmates):\\nAs the Gardener, pet to place a plant. After the next meeting, it will be grown. No one can directly kill near grown plants. However, once a plant has blocked a kill attempt, it despawns.",
"Gardener.PlantRange": "Plants' Kill Blocking Range",
"Gardener.PlantAlreadyExistsNearby": "<#ff0000>Can't place a new plant here, one is already nearby!</color>",
"Gardener.PlantCreated": "<#00ff00>Plant created!</color>\\nIt will be visible after the next meeting.",
"Pawn": "Pawn",
"PawnInfo": "Complete Tasks Then Pick Your Role",
"PawnInfoLong": "(Neutrals):\\nAs the Pawn, you must complete all of your tasks, then use <b>/choose {role}</b> to pick your role.\\n\\n(Replace {role} with the name of the role that you want to become.) (You can only choose roles that are enabled.)\\nYou lose if you have this role when the game ends.",
+81
View File
@@ -0,0 +1,81 @@
using System.Collections.Generic;
namespace EHR.Crewmate;
public class Gardener : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public static OptionItem AbilityCooldown;
private static OptionItem PlantRange;
private static OptionItem AbilityUseLimit;
private static OptionItem AbilityUseGainWithEachTaskCompleted;
private static OptionItem AbilityChargesWhenFinishedTasks;
private static List<Plant> Plants = [];
public override void SetupCustomOption()
{
StartSetup(653400)
.AutoSetupOption(ref AbilityCooldown, 30, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref PlantRange, 2.5f, new FloatValueRule(0.1f, 10f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityUseGainWithEachTaskCompleted, 0.3f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityChargesWhenFinishedTasks, 0.05f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times);
}
public override void Init()
{
On = false;
Plants = [];
}
public override void Add(byte playerId)
{
On = true;
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
}
public override void OnPet(PlayerControl pc)
{
if (pc.GetAbilityUseLimit() < 1f) return;
Vector2 pos = pc.Pos();
float range = PlantRange.GetFloat() * 2f;
if (Plants.Exists(x => Vector2.Distance(x.Position, pos) <= range))
{
pc.Notify(Translator.GetString("Gardener.PlantAlreadyExistsNearby"));
return;
}
Plants.Add(new Plant(pos));
pc.Notify(Translator.GetString("Gardener.PlantCreated"));
pc.RpcRemoveAbilityUse();
}
public override void AfterMeetingTasks()
{
Plants.ForEach(x => x.SpawnIfNotSpawned());
}
public static bool OnAnyoneCheckMurder(PlayerControl killer, PlayerControl target)
{
if (!On || Plants.Count == 0) return true;
float range = PlantRange.GetFloat();
List<Vector2> positions = [killer.Pos(), target.Pos()];
if (Plants.FindFirst(x => x.Spawned && positions.Exists(p => Vector2.Distance(p, x.Position) <= range), out Plant plant))
{
plant.Despawn();
Plants.Remove(plant);
killer.SetKillCooldown();
return false;
}
return true;
}
}