diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 237d97520c..08db5ede9d 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -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); } diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index c3e7c8bd81..606a186163 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -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 clients, Color? colorOverride = null); void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, Color? colorOverride); diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs new file mode 100644 index 0000000000..f86ad355ef --- /dev/null +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -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 _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(OnStartAttempt); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnRoundEndText); + SubscribeLocalEvent(OnJobAssigned); + + SubscribeLocalEvent(OnEntityZombified); + SubscribeLocalEvent(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(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(); + } + + /// + /// This is just checked if the last human somehow dies + /// by starving or flying off into space. + /// + private void OnMobStateChanged(MobStateChangedEvent ev) + { + if (!Enabled) + return; + CheckRoundEnd(ev.Entity); + } + + private void OnEntityZombified(EntityZombifiedEvent ev) + { + if (!Enabled) + return; + CheckRoundEnd(ev.Target); + } + + /// + /// The big kahoona function for checking if the round is gonna end + /// + /// depending on this uid, we should care about the round ending + private void CheckRoundEnd(EntityUid target) + { + //we only care about players, not monkeys and such. + if (!HasComp(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(ZombifySelfActionPrototype)); + _action.RemoveAction(uid, action); + } + + private float GetInfectedPercentage(out List livingHumans) + { + var allPlayers = EntityQuery(true); + var allZombers = GetEntityQuery(); + + var totalPlayers = new List(); + var livingZombies = new List(); + + 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; + } + + /// + /// Infects the first players with the passive zombie virus. + /// Also records their names for the end of round screen. + /// + /// + /// 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. + /// + private void InfectInitialPlayers() + { + var allPlayers = _playerManager.ServerSessions.ToList(); + var playerList = new List(); + var prefList = new List(); + 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(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(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); + } + } + } +} diff --git a/Content.Server/Zombies/ZombieComponent.cs b/Content.Server/Zombies/ZombieComponent.cs index 57f59c5b5f..0204ac1df8 100644 --- a/Content.Server/Zombies/ZombieComponent.cs +++ b/Content.Server/Zombies/ZombieComponent.cs @@ -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 /// - public float OtherZombieDamageCoefficient = 0.75f; + [ViewVariables] + public float OtherZombieDamageCoefficient = 0.5f; + + /// + /// The baseline infection chance you have if you are completely nude + /// + [ViewVariables] + public float MaxZombieInfectionChance = 0.75f; + + /// + /// The minimum infection chance possible. This is simply to prevent + /// being invincible by bundling up. + /// + [ViewVariables] + public float MinZombieInfectionChance = 0.1f; + + /// + /// The skin color of the zombie + /// + [ViewVariables, DataField("skinColor")] + public Color SkinColor = new(0.45f, 0.51f, 0.29f); + + /// + /// The eye color of the zombie + /// + [ViewVariables, DataField("eyeColor")] + public Color EyeColor = new(0.96f, 0.13f, 0.24f); + + /// + /// The attack arc of the zombie + /// + [ViewVariables, DataField("attackArc", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string AttackArc = "claw"; + + /// + /// The role prototype of the zombie antag role + /// + [ViewVariables, DataField("zombieRoldId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public readonly string ZombieRoleId = "Zombie"; } } diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index 818e3941b4..902b58f255 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -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(OnMeleeHit); } + private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component) + { + float baseChance = component.MaxZombieInfectionChance; + + if (!TryComp(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(args.User, out var zombieComp)) @@ -38,11 +82,11 @@ namespace Content.Server.Zombies if (!TryComp(entity, out var mobState) || HasComp(entity)) continue; - if (_robustRandom.Prob(0.5f) && HasComp(entity)) + if (HasComp(entity) && _robustRandom.Prob(GetZombieInfectionChance(entity, component))) _disease.TryAddDisease(entity, "ActiveZombieVirus"); if (HasComp(entity)) - args.BonusDamage = args.BaseDamage * zombieComp.OtherZombieDamageCoefficient; + args.BonusDamage = -args.BaseDamage * zombieComp.OtherZombieDamageCoefficient; if ((mobState.IsDead() || mobState.IsCritical()) && !HasComp(entity)) diff --git a/Content.Server/Zombies/ZombifyOnDeathComponent.cs b/Content.Server/Zombies/ZombifyOnDeathComponent.cs index c43ad57f51..793e356eeb 100644 --- a/Content.Server/Zombies/ZombifyOnDeathComponent.cs +++ b/Content.Server/Zombies/ZombifyOnDeathComponent.cs @@ -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))] - public string AttackArc = "claw"; + //this is not the component you are looking for } } diff --git a/Content.Server/Zombies/ZombifyOnDeathSystem.cs b/Content.Server/Zombies/ZombifyOnDeathSystem.cs index 580bfb9ced..51b181f3b2 100644 --- a/Content.Server/Zombies/ZombifyOnDeathSystem.cs +++ b/Content.Server/Zombies/ZombifyOnDeathSystem.cs @@ -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 { /// - /// Handles zombie propagation and inherent zombie traits + /// Handles zombie propagation and inherent zombie traits /// + /// + /// Don't Shitcode Open Inside + /// 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 /// private void OnDamageChanged(EntityUid uid, ZombifyOnDeathComponent component, MobStateChangedEvent args) { - if (!TryComp(uid, out var mobstate)) - return; - - if (mobstate.IsDead() || - mobstate.IsCritical()) + if (args.CurrentMobState.IsDead() || + args.CurrentMobState.IsCritical()) { ZombifyEntity(uid); } } /// - /// 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. /// /// the entity being zombified + /// + /// 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 + /// public void ZombifyEntity(EntityUid target) { + //Don't zombfiy zombies if (HasComp(target)) return; - _disease.CureAllDiseases(target); + //you're a real zombie now, son. + var zombiecomp = AddComp(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(target); RemComp(target); RemComp(target); RemComp(target); RemComp(target); - var zombiecomp = EnsureComp(target); - if (TryComp(target, out var huApComp)) - { - var appearance = huApComp.Appearance; - _sharedHuApp.UpdateAppearance(target, appearance.WithSkinColor(zombiecomp.SkinColor), huApComp); - _sharedHuApp.ForceAppearanceUpdate(target, huApComp); - } - - if (!HasComp(target)) - MakeSentientCommand.MakeSentient(target, EntityManager); - + //funny voice EnsureComp(target).Accent = "zombie"; + EnsureComp(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(target); AddComp(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(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(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(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(target, out var tempComp)) tempComp.ColdDamage.ClampMax(0); + //Heals the zombie from all the damage it took while human if (TryComp(target, out var damageablecomp)) _damageable.SetAllDamage(damageablecomp, 0); + //gives it the funny "Zombie ___" name. if (TryComp(target, out var meta)) meta.EntityName = Loc.GetString("zombie-name-prefix", ("target", meta.EntityName)); + //He's gotta have a mind var mindcomp = EnsureComp(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(zombiecomp.ZombieRoleId))); + //Greeting message for new bebe zombers + _chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting")); + } if (!HasComp(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(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(target); - EnsureComp(target); + //zombie gamemode stuff + RaiseLocalEvent(new EntityZombifiedEvent(target)); } } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 68724b75d5..d2652756a2 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -299,6 +299,19 @@ namespace Content.Shared.CCVar public static readonly CVarDef NukeopsPlayersPerOp = CVarDef.Create("nukeops.players_per_op", 5); + /* + * Zombie + */ + + public static readonly CVarDef ZombieMinPlayers = + CVarDef.Create("zombie.min_players", 20); + + public static readonly CVarDef ZombieMaxInitialInfected = + CVarDef.Create("zombie.max_initial_infected", 6); + + public static readonly CVarDef ZombiePlayersPerInfected = + CVarDef.Create("zombie.players_per_infected", 10); + /* * Pirates */ diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 88498a3ffe..6dc5270a0e 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -25,7 +25,6 @@ public abstract partial class InventorySystem [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly INetManager _netMan = default!; diff --git a/Content.Shared/Zombies/ZombieEvents.cs b/Content.Shared/Zombies/ZombieEvents.cs new file mode 100644 index 0000000000..9fa0e3c728 --- /dev/null +++ b/Content.Shared/Zombies/ZombieEvents.cs @@ -0,0 +1,25 @@ +using Content.Shared.Actions; + +namespace Content.Shared.Zombies; + +/// +/// Event that is broadcast whenever an entity is zombified. +/// Used by the zombie gamemode to track total infections. +/// +public readonly struct EntityZombifiedEvent +{ + /// + /// The entity that was zombified. + /// + public readonly EntityUid Target; + + public EntityZombifiedEvent(EntityUid target) + { + Target = target; + } +}; + +/// +/// Event raised when a player zombifies themself using the "turn" action +/// +public sealed class ZombifySelfActionEvent : InstantActionEvent { }; diff --git a/Resources/Locale/en-US/actions/actions/zombie.ftl b/Resources/Locale/en-US/actions/actions/zombie.ftl new file mode 100644 index 0000000000..3ae2d928e9 --- /dev/null +++ b/Resources/Locale/en-US/actions/actions/zombie.ftl @@ -0,0 +1,2 @@ +turn-undead-action-name = Turn Undead +turn-undead-action-description = Succumb to your infection and become a zombie. \ No newline at end of file diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-zombies.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-zombies.ftl new file mode 100644 index 0000000000..d7e487ba52 --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-zombies.ftl @@ -0,0 +1,27 @@ +zombie-title = Zombies +zombie-description = A virus able to animate the dead has been unleashed unto the station! Work with your crewmates to contain the outbreak and survive. + +zombie-not-enough-ready-players = Not enough players readied up for the game! There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start Zombies. +zombie-no-one-ready = No players readied up! Can't start Zombies. + +zombie-patientzero-role-greeting = You are patient 0. Hide your infection, get supplies, and be prepared to turn once you die. + +zombie-alone = You feel entirely alone. + +zombie-round-end-initial-count = {$initialCount -> + [one] There was one initial infected: + *[other] There were {$initialCount} initial infected: +} +zombie-round-end-user-was-initial = - [color=plum]{$name}[/color] ([color=gray]{$username}[/color]) was one of the initial infected. + +zombie-round-end-amount-none = [color=green]All of the zombies were eradicated![/color] +zombie-round-end-amount-low = [color=green]Almost all of the zombies were exterminated.[/color] +zombie-round-end-amount-medium = [color=yellow]{$percent}% of the crew were turned into zombies.[/color] +zombie-round-end-amount-high = [color=crimson]{$percent}% of the crew were turned into zombies.[/color] +zombie-round-end-amount-all = [color=darkred]The entire crew became zombies![/color] + +zombie-round-end-survivor-count = {$count -> + [one] There was only one survivor left: + *[other] There were only {$count} survivors left: +} +zombie-round-end-user-was-survivor = - [color=White]{$name}[/color] ([color=gray]{$username}[/color]) survived the outbreak. \ No newline at end of file diff --git a/Resources/Locale/en-US/zombies/zombie.ftl b/Resources/Locale/en-US/zombies/zombie.ftl index 606717ed35..f534a962e0 100644 --- a/Resources/Locale/en-US/zombies/zombie.ftl +++ b/Resources/Locale/en-US/zombies/zombie.ftl @@ -2,6 +2,6 @@ zombie-transform = {CAPITALIZE(THE($target))} turned into a zombie! zombie-infection-greeting = You have become a zombie. Your goal is to seek out the living and to try to infect them. Work together with your fellow zombies to overpower the remaining crewmates. zombie-generic = zombie -zombie-name-prefix = zombified {$target} +zombie-name-prefix = Zombified {$target} zombie-role-desc = A malevolent creature of the dead. zombie-role-rules = You are an antagonist. Search out the living and bite them in order to infect them and turn them into zombies. Work together with other the zombies to overtake the station. diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index c6aa383550..29a85ce3e3 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -7,6 +7,13 @@ serverEvent: !type:ScreamActionEvent checkCanInteract: false +- type: instantAction + id: TurnUndead + name: turn-undead-action-name + description: turn-undead-action-description + icon: Interface/Actions/zombie-turn.png + event: !type:ZombifySelfActionEvent + - type: instantAction id: ToggleLight name: action-name-toggle-light diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml index 51cef5a3fc..826c86dd07 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml @@ -13,6 +13,22 @@ - id: Retractor - id: Scalpel +- type: entity + id: ClothingBackpackDuffelCBURN + parent: ClothingBackpackDuffel + name: CBURN duffel bag + description: A duffel bag containing a variety of biological containment equipment. + components: + - type: StorageFill + contents: + - id: WeaponShotgunDoubleBarreled + - id: BoxShotgunFlare + amount: 2 + - id: PillRomerol + amount: 5 + - id: GrenadeFlash + amount: 2 + - type: entity parent: ClothingBackpackDuffelSyndicateMedical id: ClothingBackpackDuffelSyndicateFilledMedical diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index c446cfe876..e24b8e9196 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -117,8 +117,11 @@ Piercing: 0.9 Shock: 1.5 Cold: 0.2 - Heat: 3.0 + Heat: 2.5 Poison: 0.0 + flatReductions: #offsets rotting damage + Blunt: 0.3 + Cellular: 0.3 # immune to everything except physical and heat damage - type: damageModifierSet diff --git a/Resources/Prototypes/Diseases/zombie.yml b/Resources/Prototypes/Diseases/zombie.yml index cbdca7bbaf..086685c02d 100644 --- a/Resources/Prototypes/Diseases/zombie.yml +++ b/Resources/Prototypes/Diseases/zombie.yml @@ -23,4 +23,13 @@ cures: - !type:DiseaseReagentCure reagent: Romerol - min: 5 \ No newline at end of file + min: 5 + +- type: disease + id: PassiveZombieVirus + name: Zombie Virus + infectious: false + cureResist: 1 #no cure. Death is your cure. + effects: + - !type:DiseaseAddComponent + comp: ZombifyOnDeath \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index 2cea9b1e36..6945f861ac 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -405,6 +405,56 @@ Heat: 0.2 Radiation: 0.5 +- type: entity + parent: ClothingHeadHardsuitWithLightBase + id: ClothingHeadHelmetCBURN + noSpawn: true + name: syndicate elite helmet + description: A pressure resistant and fireproof hood worn by special cleanup units. + components: + - type: Sprite + netsync: false + sprite: Clothing/Head/Hardsuits/cburn.rsi + layers: + - state: icon + - state: icon-unshaded + shader: unshaded + - state: light-overlay + visible: false + shader: unshaded + map: [ "light" ] + - type: Clothing + clothingVisuals: + head: + - state: equipped-head + - state: equipped-head-unshaded + shader: unshaded + inhandVisuals: + left: + - state: inhand-left + - state: inhand-left-unshaded + shader: unshaded + right: + - state: inhand-right + - state: inhand-right-unshaded + shader: unshaded + - type: PointLight + color: orange + - type: PressureProtection + highPressureMultiplier: 0.08 + lowPressureMultiplier: 1000 + - type: TemperatureProtection + coefficient: 0.005 + - type: Armor + modifiers: + coefficients: + Blunt: 0.9 + Slash: 0.9 + Piercing: 0.9 + Heat: 0.1 + Shock: 0.1 + Cold: 0.2 + Radiation: 0.2 #ERT - type: entity diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index 0977bd1a12..76c03baeaf 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -571,6 +571,38 @@ - type: ToggleableClothing clothingPrototype: ClothingHeadHelmetHardsuitSyndieElite +- type: entity + parent: ClothingOuterHardsuitBase + id: ClothingOuterHardsuitCBURN + name: CBURN exosuit + description: A lightweight yet strong exosuit used for special cleanup operations. + components: + - type: Sprite + sprite: Clothing/OuterClothing/Hardsuits/cburn.rsi + - type: Clothing + sprite: Clothing/OuterClothing/Hardsuits/cburn.rsi + - type: PressureProtection + highPressureMultiplier: 0.02 + lowPressureMultiplier: 1000 + - type: ClothingSpeedModifier + walkModifier: 1.0 + sprintModifier: 1.0 + - type: TemperatureProtection + coefficient: 0.001 + - type: Armor + modifiers: + coefficients: + Blunt: 0.7 + Slash: 0.7 + Piercing: 0.6 + Heat: 0.05 + Cold: 0.1 + Shock: 0.1 + Radiation: 0.1 + - type: ExplosionResistance + resistance: 0.7 + - type: ToggleableClothing + clothingPrototype: ClothingHeadHelmetCBURN #ERT - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml index 6916e21264..b7471a3a93 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml @@ -34,6 +34,20 @@ - Idle - Spirate +- type: entity + parent: MobHuman + id: MobCBURNUnit + name: CBURN Agent + description: A miserable pile of secrets + components: + - type: RandomHumanoidAppearance + - type: Loadout + prototype: CBURNGear + - type: GhostTakeoverAvailable + makeSentient: false + name: CBURN Agent + description: A highly trained Centcomm agent, capable of dealing with various threats. + - type: entity parent: MobHumanBase suffix: Dead diff --git a/Resources/Prototypes/Roles/Antags/zombie.yml b/Resources/Prototypes/Roles/Antags/zombie.yml new file mode 100644 index 0000000000..3da86c0555 --- /dev/null +++ b/Resources/Prototypes/Roles/Antags/zombie.yml @@ -0,0 +1,13 @@ +- type: antag + id: InitialInfected + name: "Initial Infected" + antagonist: true + setPreference: true + objective: "Once you turn, infect as many other crew members as possible" + +- type: antag + id: Zombie + name: "Zombie" + antagonist: true + setPreference: false + objective: "Turn as many humans as possible into zombies." \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index 3ee81e3479..027b9ff60a 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -137,3 +137,24 @@ innerclothingskirt: ClothingUniformJumpskirtOperative satchel: ClothingBackpackDuffelSyndicateOperativeMedic duffelbag: ClothingBackpackDuffelSyndicateOperativeMedic + +#CBURN Unit Gear - Full Kit +- type: startingGear + id: CBURNGear + equipment: + jumpsuit: ClothingUniformJumpsuitColorBrown + back: ClothingBackpackDuffelCBURN + mask: ClothingMaskGas + eyes: ClothingEyesGlassesSecurity + ears: ClothingHeadsetService + gloves: ClothingHandsGlovesFingerless + outerClothing: ClothingOuterHardsuitCBURN + shoes: ClothingShoesBootsJack + id: CentcomPDA + pocket1: CombatKnife + pocket2: WeaponLaserGun + suitstorage: YellowOxygenTankFilled + belt: ClothingBeltBandolier + innerclothingskirt: ClothingUniformJumpsuitColorBrown + satchel: ClothingBackpackDuffelCBURN + duffelbag: ClothingBackpackDuffelCBURN \ No newline at end of file diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index 21c892fda3..601b14f1a8 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -80,6 +80,20 @@ rules: - Nukeops +- type: gamePreset + id: Zombie + alias: + - zombie + - zombies + - Zombies + - zz14 + - zomber + name: zombie-title + description: zombie-description + showInVote: true + rules: + - Zombie + - type: gamePreset id: Pirates alias: diff --git a/Resources/Prototypes/game_rules.yml b/Resources/Prototypes/game_rules.yml index 4334e7667d..849f5715c7 100644 --- a/Resources/Prototypes/game_rules.yml +++ b/Resources/Prototypes/game_rules.yml @@ -59,3 +59,9 @@ config: !type:GenericGameRuleConfiguration id: Secret + +- type: gameRule + id: Zombie + config: + !type:GenericGameRuleConfiguration + id: Zombie diff --git a/Resources/Prototypes/secret_weights.yml b/Resources/Prototypes/secret_weights.yml index 6866e6875c..fb64d74325 100644 --- a/Resources/Prototypes/secret_weights.yml +++ b/Resources/Prototypes/secret_weights.yml @@ -3,4 +3,5 @@ weights: Extended: 0.25 Nukeops: 0.25 - Traitor: 0.75 \ No newline at end of file + Traitor: 0.75 + Zombie: 0.05 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head-unshaded.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head-unshaded.png new file mode 100644 index 0000000000..7fd9a0b229 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head-unshaded.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head.png new file mode 100644 index 0000000000..98b9db4665 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/equipped-head.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-flash.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-flash.png new file mode 100644 index 0000000000..d37c1afe3e Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-flash.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-unshaded.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-unshaded.png new file mode 100644 index 0000000000..9da6cf4b4a Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon-unshaded.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon.png new file mode 100644 index 0000000000..adfb7557f7 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left-unshaded.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left-unshaded.png new file mode 100644 index 0000000000..60ca0efa13 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left-unshaded.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left.png new file mode 100644 index 0000000000..43f27a890f Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right-unshaded.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right-unshaded.png new file mode 100644 index 0000000000..118914c8d3 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right-unshaded.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right.png new file mode 100644 index 0000000000..602020b276 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/light-overlay.png b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/light-overlay.png new file mode 100644 index 0000000000..ff7347fe6e Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/light-overlay.png differ diff --git a/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/meta.json b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/meta.json new file mode 100644 index 0000000000..16f509fa99 --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/meta.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by EmoGarbage404", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "icon-unshaded" + }, + { + "name": "icon-flash" + }, + { + "name": "light-overlay" + }, + { + "name": "equipped-head", + "directions": 4 + }, + { + "name": "equipped-head-unshaded", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-left-unshaded", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-right-unshaded", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 0000000000..c7ed8fa9ff Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/icon.png new file mode 100644 index 0000000000..8d7c6be6a6 Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-left.png new file mode 100644 index 0000000000..9071444e11 Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-right.png new file mode 100644 index 0000000000..715c65f4b6 Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json new file mode 100644 index 0000000000..2cfc2220e1 --- /dev/null +++ b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by EmoGarbage404", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Interface/Actions/meta.json b/Resources/Textures/Interface/Actions/meta.json index 20880fc792..f487a86173 100644 --- a/Resources/Textures/Interface/Actions/meta.json +++ b/Resources/Textures/Interface/Actions/meta.json @@ -36,6 +36,9 @@ }, { "name": "ratKingDomain" + }, + { + "name": "zombie-turn" } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Interface/Actions/zombie-turn.png b/Resources/Textures/Interface/Actions/zombie-turn.png new file mode 100644 index 0000000000..2e7173d4d3 Binary files /dev/null and b/Resources/Textures/Interface/Actions/zombie-turn.png differ