Zombie Mode [New Game Mode] (#8501)

Co-authored-by: Kara <lunarautomaton6@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Nemanja
2022-07-05 23:42:51 -04:00
committed by GitHub
parent ab12345168
commit 836c0bb1c4
43 changed files with 821 additions and 51 deletions

View File

@@ -207,7 +207,7 @@ namespace Content.Server.Chat.Managers
#region Utility
public void ChatMessageToOne(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, INetChannel client)
public void ChatMessageToOne(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null)
{
var msg = new MsgChatMessage();
msg.Channel = channel;
@@ -215,6 +215,10 @@ namespace Content.Server.Chat.Managers
msg.MessageWrap = messageWrap;
msg.SenderEntity = source;
msg.HideChat = hideChat;
if (colorOverride != null)
{
msg.MessageColorOverride = colorOverride.Value;
}
_netManager.ServerSendMessage(msg, client);
}

View File

@@ -24,7 +24,7 @@ namespace Content.Server.Chat.Managers
void SendAdminAnnouncement(string message);
void ChatMessageToOne(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat,
INetChannel client);
INetChannel client, Color? colorOverride = null);
void ChatMessageToMany(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat,
List<INetChannel> clients, Color? colorOverride = null);
void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, Color? colorOverride);

View File

@@ -0,0 +1,312 @@
using System.Linq;
using Content.Server.Actions;
using Content.Server.Chat.Managers;
using Content.Server.Disease;
using Content.Server.GameTicking.Rules.Configurations;
using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Server.Popups;
using Content.Server.Preferences.Managers;
using Content.Server.RoundEnd;
using Content.Server.Traitor;
using Content.Server.Zombies;
using Content.Shared.Actions.ActionTypes;
using Content.Shared.CCVar;
using Content.Shared.CharacterAppearance.Components;
using Content.Shared.FixedPoint;
using Content.Shared.MobState;
using Content.Shared.MobState.Components;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Zombies;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.GameTicking.Rules;
public sealed class ZombieRuleSystem : GameRuleSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerPreferencesManager _prefs = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
[Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly ZombifyOnDeathSystem _zombify = default!;
private Dictionary<string, string> _initialInfectedNames = new();
public override string Prototype => "Zombie";
private const string PatientZeroPrototypeID = "InitialInfected";
private const string InitialZombieVirusPrototype = "PassiveZombieVirus";
private const string ZombifySelfActionPrototype = "TurnUndead";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnJobAssigned);
SubscribeLocalEvent<EntityZombifiedEvent>(OnEntityZombified);
SubscribeLocalEvent<ZombifyOnDeathComponent, ZombifySelfActionEvent>(OnZombifySelf);
}
private void OnRoundEndText(RoundEndTextAppendEvent ev)
{
if (!Enabled)
return;
//this is just the general condition thing used for determining the win/lose text
var percent = Math.Round(GetInfectedPercentage(out var livingHumans), 2);
if (percent <= 0)
ev.AddLine(Loc.GetString("zombie-round-end-amount-none"));
else if (percent <= 0.25)
ev.AddLine(Loc.GetString("zombie-round-end-amount-low"));
else if (percent <= 0.5)
ev.AddLine(Loc.GetString("zombie-round-end-amount-medium", ("percent", (percent * 100).ToString())));
else if (percent < 1)
ev.AddLine(Loc.GetString("zombie-round-end-amount-high", ("percent", (percent * 100).ToString())));
else
ev.AddLine(Loc.GetString("zombie-round-end-amount-all"));
ev.AddLine(Loc.GetString("zombie-round-end-initial-count", ("initialCount", _initialInfectedNames.Count)));
foreach (var player in _initialInfectedNames)
{
ev.AddLine(Loc.GetString("zombie-round-end-user-was-initial",
("name", player.Key),
("username", player.Value)));
}
///Gets a bunch of the living players and displays them if they're under a threshold.
///InitialInfected is used for the threshold because it scales with the player count well.
if (livingHumans.Count > 0 && livingHumans.Count <= _initialInfectedNames.Count)
{
ev.AddLine("");
ev.AddLine(Loc.GetString("zombie-round-end-survivor-count", ("count", livingHumans.Count)));
foreach (var survivor in livingHumans)
{
var meta = MetaData(survivor);
var username = string.Empty;
if (TryComp<MindComponent>(survivor, out var mindcomp))
if (mindcomp.Mind != null && mindcomp.Mind.Session != null)
username = mindcomp.Mind.Session.Name;
ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor",
("name", meta.EntityName),
("username", username)));
}
}
}
private void OnJobAssigned(RulePlayerJobsAssignedEvent ev)
{
if (!Enabled)
return;
_initialInfectedNames = new();
InfectInitialPlayers();
}
/// <remarks>
/// This is just checked if the last human somehow dies
/// by starving or flying off into space.
/// </remarks>
private void OnMobStateChanged(MobStateChangedEvent ev)
{
if (!Enabled)
return;
CheckRoundEnd(ev.Entity);
}
private void OnEntityZombified(EntityZombifiedEvent ev)
{
if (!Enabled)
return;
CheckRoundEnd(ev.Target);
}
/// <summary>
/// The big kahoona function for checking if the round is gonna end
/// </summary>
/// <param name="target">depending on this uid, we should care about the round ending</param>
private void CheckRoundEnd(EntityUid target)
{
//we only care about players, not monkeys and such.
if (!HasComp<HumanoidAppearanceComponent>(target))
return;
var percent = GetInfectedPercentage(out var num);
if (num.Count == 1) //only one human left. spooky
_popup.PopupEntity(Loc.GetString("zombie-alone"), num[0], Filter.Entities(num[0]));
if (percent >= 1) //oops, all zombies
_roundEndSystem.EndRound();
}
private void OnStartAttempt(RoundStartAttemptEvent ev)
{
if (!Enabled)
return;
var minPlayers = _cfg.GetCVar(CCVars.ZombieMinPlayers);
if (!ev.Forced && ev.Players.Length < minPlayers)
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("zombie-not-enough-ready-players", ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers)));
ev.Cancel();
return;
}
if (ev.Players.Length == 0)
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("zombie-no-one-ready"));
ev.Cancel();
return;
}
}
public override void Started(GameRuleConfiguration configuration)
{
//this technically will run twice with zombies on roundstart, but it doesn't matter because it fails instantly
InfectInitialPlayers();
}
public override void Ended(GameRuleConfiguration configuration) { }
private void OnZombifySelf(EntityUid uid, ZombifyOnDeathComponent component, ZombifySelfActionEvent args)
{
_zombify.ZombifyEntity(uid);
var action = new InstantAction(_prototypeManager.Index<InstantActionPrototype>(ZombifySelfActionPrototype));
_action.RemoveAction(uid, action);
}
private float GetInfectedPercentage(out List<EntityUid> livingHumans)
{
var allPlayers = EntityQuery<HumanoidAppearanceComponent, MobStateComponent>(true);
var allZombers = GetEntityQuery<ZombieComponent>();
var totalPlayers = new List<EntityUid>();
var livingZombies = new List<EntityUid>();
livingHumans = new();
foreach (var ent in allPlayers)
{
if (ent.Item2.IsAlive())
{
totalPlayers.Add(ent.Item2.Owner);
if (allZombers.HasComponent(ent.Item1.Owner))
livingZombies.Add(ent.Item2.Owner);
else
livingHumans.Add(ent.Item2.Owner);
}
}
return ((float) livingZombies.Count) / (float) totalPlayers.Count;
}
/// <summary>
/// Infects the first players with the passive zombie virus.
/// Also records their names for the end of round screen.
/// </summary>
/// <remarks>
/// The reason this code is written separately is to facilitate
/// allowing this gamemode to be started midround. As such, it doesn't need
/// any information besides just running.
/// </remarks>
private void InfectInitialPlayers()
{
var allPlayers = _playerManager.ServerSessions.ToList();
var playerList = new List<IPlayerSession>();
var prefList = new List<IPlayerSession>();
foreach (var player in allPlayers)
{
if (player.AttachedEntity != null)
{
playerList.Add(player);
var pref = (HumanoidCharacterProfile) _prefs.GetPreferences(player.UserId).SelectedCharacter;
if (pref.AntagPreferences.Contains(PatientZeroPrototypeID))
prefList.Add(player);
}
}
if (playerList.Count == 0)
return;
var playersPerInfected = _cfg.GetCVar(CCVars.ZombiePlayersPerInfected);
var maxInfected = _cfg.GetCVar(CCVars.ZombieMaxInitialInfected);
var numInfected = Math.Max(1,
(int) Math.Min(
Math.Floor((double) playerList.Count / playersPerInfected), maxInfected));
for (var i = 0; i < numInfected; i++)
{
IPlayerSession zombie;
if (prefList.Count == 0)
{
if (playerList.Count == 0)
{
Logger.InfoS("preset", "Insufficient number of players. stopping selection.");
break;
}
zombie = _random.PickAndTake(playerList);
Logger.InfoS("preset", "Insufficient preferred patient 0, picking at random.");
}
else
{
zombie = _random.PickAndTake(prefList);
playerList.Remove(zombie);
Logger.InfoS("preset", "Selected a patient 0.");
}
var mind = zombie.Data.ContentData()?.Mind;
if (mind == null)
{
Logger.ErrorS("preset", "Failed getting mind for picked patient 0.");
continue;
}
DebugTools.AssertNotNull(mind.OwnedEntity);
mind.AddRole(new TraitorRole(mind, _prototypeManager.Index<AntagPrototype>(PatientZeroPrototypeID)));
var inCharacterName = string.Empty;
if (mind.OwnedEntity != null)
{
_diseaseSystem.TryAddDisease(mind.OwnedEntity.Value, InitialZombieVirusPrototype);
inCharacterName = MetaData(mind.OwnedEntity.Value).EntityName;
var action = new InstantAction(_prototypeManager.Index<InstantActionPrototype>(ZombifySelfActionPrototype));
_action.AddAction(mind.OwnedEntity.Value, action, null);
}
if (mind.Session != null)
{
var messageWrapper = Loc.GetString("chat-manager-server-wrap-message");
//gets the names now in case the players leave.
if (inCharacterName != null)
_initialInfectedNames.Add(inCharacterName, mind.Session.Name);
// I went all the way to ChatManager.cs and all i got was this lousy T-shirt
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server, Loc.GetString("zombie-patientzero-role-greeting"),
messageWrapper, default, false, mind.Session.ConnectedClient, Color.Plum);
}
}
}
}

View File

@@ -1,3 +1,7 @@
using Content.Shared.Roles;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Zombies
{
[RegisterComponent]
@@ -7,6 +11,44 @@ namespace Content.Server.Zombies
/// The coefficient of the damage reduction applied when a zombie
/// attacks another zombie. longe name
/// </summary>
public float OtherZombieDamageCoefficient = 0.75f;
[ViewVariables]
public float OtherZombieDamageCoefficient = 0.5f;
/// <summary>
/// The baseline infection chance you have if you are completely nude
/// </summary>
[ViewVariables]
public float MaxZombieInfectionChance = 0.75f;
/// <summary>
/// The minimum infection chance possible. This is simply to prevent
/// being invincible by bundling up.
/// </summary>
[ViewVariables]
public float MinZombieInfectionChance = 0.1f;
/// <summary>
/// The skin color of the zombie
/// </summary>
[ViewVariables, DataField("skinColor")]
public Color SkinColor = new(0.45f, 0.51f, 0.29f);
/// <summary>
/// The eye color of the zombie
/// </summary>
[ViewVariables, DataField("eyeColor")]
public Color EyeColor = new(0.96f, 0.13f, 0.24f);
/// <summary>
/// The attack arc of the zombie
/// </summary>
[ViewVariables, DataField("attackArc", customTypeSerializer: typeof(PrototypeIdSerializer<MeleeWeaponAnimationPrototype>))]
public string AttackArc = "claw";
/// <summary>
/// The role prototype of the zombie antag role
/// </summary>
[ViewVariables, DataField("zombieRoldId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
public readonly string ZombieRoleId = "Zombie";
}
}

View File

@@ -7,6 +7,11 @@ using Content.Server.Weapon.Melee;
using Content.Shared.Chemistry.Components;
using Content.Shared.MobState.Components;
using Content.Server.Disease;
using Content.Shared.Inventory;
using Content.Server.Popups;
using Robust.Shared.Player;
using Content.Server.Inventory;
using Robust.Shared.Prototypes;
namespace Content.Server.Zombies
{
@@ -15,13 +20,52 @@ namespace Content.Server.Zombies
[Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly ZombifyOnDeathSystem _zombify = default!;
[Dependency] private readonly ServerInventorySystem _inv = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit);
}
private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component)
{
float baseChance = component.MaxZombieInfectionChance;
if (!TryComp<InventoryComponent>(uid, out var inventoryComponent))
return baseChance;
var enumerator =
new InventorySystem.ContainerSlotEnumerator(uid, inventoryComponent.TemplateId, _protoManager, _inv,
SlotFlags.FEET |
SlotFlags.HEAD |
SlotFlags.EYES |
SlotFlags.GLOVES |
SlotFlags.MASK |
SlotFlags.NECK |
SlotFlags.INNERCLOTHING |
SlotFlags.OUTERCLOTHING);
var items = 0f;
var total = 0f;
while (enumerator.MoveNext(out var con))
{
total++;
if (con.ContainedEntity != null)
items++;
}
var max = component.MaxZombieInfectionChance;
var min = component.MinZombieInfectionChance;
//gets a value between the max and min based on how many items the entity is wearing
float chance = (max-min) * ((total - items)/total) + min;
return chance;
}
private void OnMeleeHit(EntityUid uid, ZombieComponent component, MeleeHitEvent args)
{
if (!EntityManager.TryGetComponent<ZombieComponent>(args.User, out var zombieComp))
@@ -38,11 +82,11 @@ namespace Content.Server.Zombies
if (!TryComp<MobStateComponent>(entity, out var mobState) || HasComp<DroneComponent>(entity))
continue;
if (_robustRandom.Prob(0.5f) && HasComp<DiseaseCarrierComponent>(entity))
if (HasComp<DiseaseCarrierComponent>(entity) && _robustRandom.Prob(GetZombieInfectionChance(entity, component)))
_disease.TryAddDisease(entity, "ActiveZombieVirus");
if (HasComp<ZombieComponent>(entity))
args.BonusDamage = args.BaseDamage * zombieComp.OtherZombieDamageCoefficient;
args.BonusDamage = -args.BaseDamage * zombieComp.OtherZombieDamageCoefficient;
if ((mobState.IsDead() || mobState.IsCritical())
&& !HasComp<ZombieComponent>(entity))

View File

@@ -1,15 +1,8 @@
using Content.Shared.Weapons.Melee;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Zombies
{
[RegisterComponent]
public sealed class ZombifyOnDeathComponent : Component
{
[DataField("skinColor")]
public Color SkinColor = new Color(0.70f, 0.72f, 0.48f, 1);
[DataField("attackArc", customTypeSerializer: typeof(PrototypeIdSerializer<MeleeWeaponAnimationPrototype>))]
public string AttackArc = "claw";
//this is not the component you are looking for
}
}

View File

@@ -20,16 +20,23 @@ using Content.Server.Hands.Components;
using Content.Server.Mind.Commands;
using Content.Server.Temperature.Components;
using Content.Server.Weapon.Melee.Components;
using Content.Server.Disease;
using Robust.Shared.Containers;
using Content.Shared.Movement.Components;
using Content.Shared.MobState;
using Robust.Shared.Prototypes;
using Content.Shared.Roles;
using Content.Server.Traitor;
using Content.Shared.Zombies;
using Content.Server.Atmos.Miasma;
namespace Content.Server.Zombies
{
/// <summary>
/// Handles zombie propagation and inherent zombie traits
/// Handles zombie propagation and inherent zombie traits
/// </summary>
/// <remarks>
/// Don't Shitcode Open Inside
/// </remarks>
public sealed class ZombifyOnDeathSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _sharedHands = default!;
@@ -37,9 +44,9 @@ namespace Content.Server.Zombies
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly ServerInventorySystem _serverInventory = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _sharedHuApp = default!;
[Dependency] private readonly IChatManager _chatMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
@@ -53,95 +60,135 @@ namespace Content.Server.Zombies
/// </summary>
private void OnDamageChanged(EntityUid uid, ZombifyOnDeathComponent component, MobStateChangedEvent args)
{
if (!TryComp<MobStateComponent>(uid, out var mobstate))
return;
if (mobstate.IsDead() ||
mobstate.IsCritical())
if (args.CurrentMobState.IsDead() ||
args.CurrentMobState.IsCritical())
{
ZombifyEntity(uid);
}
}
/// <summary>
/// This is the general purpose function to call if you want to zombify an entity.
/// It handles both humanoid and nonhumanoid transformation.
/// This is the general purpose function to call if you want to zombify an entity.
/// It handles both humanoid and nonhumanoid transformation and everything should be called through it.
/// </summary>
/// <param name="target">the entity being zombified</param>
/// <remarks>
/// ALRIGHT BIG BOY. YOU'VE COME TO THE LAYER OF THE BEAST. THIS IS YOUR WARNING.
/// This function is the god function for zombie stuff, and it is cursed. I have
/// attempted to label everything thouroughly for your sanity. I have attempted to
/// rewrite this, but this is how it shall lie eternal. Turn back now.
/// -emo
/// </remarks>
public void ZombifyEntity(EntityUid target)
{
//Don't zombfiy zombies
if (HasComp<ZombieComponent>(target))
return;
_disease.CureAllDiseases(target);
//you're a real zombie now, son.
var zombiecomp = AddComp<ZombieComponent>(target);
///we need to basically remove all of these because zombies shouldn't
///get diseases, breath, be thirst, be hungry, or die in space
RemComp<DiseaseCarrierComponent>(target);
RemComp<RespiratorComponent>(target);
RemComp<BarotraumaComponent>(target);
RemComp<HungerComponent>(target);
RemComp<ThirstComponent>(target);
var zombiecomp = EnsureComp<ZombifyOnDeathComponent>(target);
if (TryComp<HumanoidAppearanceComponent>(target, out var huApComp))
{
var appearance = huApComp.Appearance;
_sharedHuApp.UpdateAppearance(target, appearance.WithSkinColor(zombiecomp.SkinColor), huApComp);
_sharedHuApp.ForceAppearanceUpdate(target, huApComp);
}
if (!HasComp<SharedDummyInputMoverComponent>(target))
MakeSentientCommand.MakeSentient(target, EntityManager);
//funny voice
EnsureComp<ReplacementAccentComponent>(target).Accent = "zombie";
EnsureComp<RottingComponent>(target);
//funny add delet go brrr
///This is needed for stupid entities that fuck up combat mode component
///in an attempt to make an entity not attack. This is the easiest way to do it.
RemComp<CombatModeComponent>(target);
AddComp<CombatModeComponent>(target);
///This is the actual damage of the zombie. We assign the visual appearance
///and range here because of stuff we'll find out later
var melee = EnsureComp<MeleeWeaponComponent>(target);
melee.Arc = zombiecomp.AttackArc;
melee.ClickArc = zombiecomp.AttackArc;
//lord forgive me for the hardcoded damage
DamageSpecifier dspec = new();
dspec.DamageDict.Add("Slash", 13);
dspec.DamageDict.Add("Piercing", 7);
melee.Damage = dspec;
melee.Range = 0.75f;
//We have specific stuff for humanoid zombies because they matter more
if (TryComp<HumanoidAppearanceComponent>(target, out var huApComp)) //huapcomp
{
//this bs is done because you can't directly update humanoid appearances
var appearance = huApComp.Appearance;
appearance = appearance.WithSkinColor(zombiecomp.SkinColor).WithEyeColor(zombiecomp.EyeColor);
_sharedHuApp.UpdateAppearance(target, appearance, huApComp);
_sharedHuApp.ForceAppearanceUpdate(target, huApComp);
///This is done here because non-humanoids shouldn't get baller damage
///lord forgive me for the hardcoded damage
DamageSpecifier dspec = new();
dspec.DamageDict.Add("Slash", 13);
dspec.DamageDict.Add("Piercing", 7);
melee.Damage = dspec;
}
//The zombie gets the assigned damage weaknesses and strengths
_damageable.SetDamageModifierSetId(target, "Zombie");
///This makes it so the zombie doesn't take bloodloss damage.
///NOTE: they are supposed to bleed, just not take damage
_bloodstream.SetBloodLossThreshold(target, 0f);
_popupSystem.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, Filter.Pvs(target));
//This is specifically here to combat insuls, because frying zombies on grilles is funny as shit.
_serverInventory.TryUnequip(target, "gloves", true, true);
//popup
_popupSystem.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, Filter.Pvs(target));
//Make it sentient if it's an animal or something
if (!HasComp<SharedDummyInputMoverComponent>(target)) //this component is cursed and fucks shit up
MakeSentientCommand.MakeSentient(target, EntityManager);
//Make the zombie not die in the cold. Good for space zombies
if (TryComp<TemperatureComponent>(target, out var tempComp))
tempComp.ColdDamage.ClampMax(0);
//Heals the zombie from all the damage it took while human
if (TryComp<DamageableComponent>(target, out var damageablecomp))
_damageable.SetAllDamage(damageablecomp, 0);
//gives it the funny "Zombie ___" name.
if (TryComp<MetaDataComponent>(target, out var meta))
meta.EntityName = Loc.GetString("zombie-name-prefix", ("target", meta.EntityName));
//He's gotta have a mind
var mindcomp = EnsureComp<MindComponent>(target);
if (mindcomp.Mind != null && mindcomp.Mind.TryGetSession(out var session))
_chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting"));
{
//Zombie role for player manifest
mindcomp.Mind.AddRole(new TraitorRole(mindcomp.Mind, _proto.Index<AntagPrototype>(zombiecomp.ZombieRoleId)));
//Greeting message for new bebe zombers
_chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting"));
}
if (!HasComp<GhostRoleMobSpawnerComponent>(target) && !mindcomp.HasMind) //this specific component gives build test trouble so pop off, ig
{
//yet more hardcoding. Visit zombie.ftl for more information.
EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(target, out var ghostcomp);
ghostcomp.RoleName = Loc.GetString("zombie-generic");
ghostcomp.RoleDescription = Loc.GetString("zombie-role-desc");
ghostcomp.RoleRules = Loc.GetString("zombie-role-rules");
}
///Goes through every hand, drops the items in it, then removes the hand
///may become the source of various bugs.
foreach (var hand in _sharedHands.EnumerateHands(target))
{
_sharedHands.SetActiveHand(target, hand);
hand.Container?.EmptyContainer();
_sharedHands.DoDrop(target, hand);
_sharedHands.RemoveHand(target, hand.Name);
}
RemComp<HandsComponent>(target);
EnsureComp<ZombieComponent>(target);
//zombie gamemode stuff
RaiseLocalEvent(new EntityZombifiedEvent(target));
}
}
}