Zombie Mode [New Game Mode] (#8501)
Co-authored-by: Kara <lunarautomaton6@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
312
Content.Server/GameTicking/Rules/ZombieRuleSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
//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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,6 +299,19 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<int> NukeopsPlayersPerOp =
|
||||
CVarDef.Create("nukeops.players_per_op", 5);
|
||||
|
||||
/*
|
||||
* Zombie
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<int> ZombieMinPlayers =
|
||||
CVarDef.Create("zombie.min_players", 20);
|
||||
|
||||
public static readonly CVarDef<int> ZombieMaxInitialInfected =
|
||||
CVarDef.Create("zombie.max_initial_infected", 6);
|
||||
|
||||
public static readonly CVarDef<int> ZombiePlayersPerInfected =
|
||||
CVarDef.Create("zombie.players_per_infected", 10);
|
||||
|
||||
/*
|
||||
* Pirates
|
||||
*/
|
||||
|
||||
@@ -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!;
|
||||
|
||||
25
Content.Shared/Zombies/ZombieEvents.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Zombies;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is broadcast whenever an entity is zombified.
|
||||
/// Used by the zombie gamemode to track total infections.
|
||||
/// </summary>
|
||||
public readonly struct EntityZombifiedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity that was zombified.
|
||||
/// </summary>
|
||||
public readonly EntityUid Target;
|
||||
|
||||
public EntityZombifiedEvent(EntityUid target)
|
||||
{
|
||||
Target = target;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a player zombifies themself using the "turn" action
|
||||
/// </summary>
|
||||
public sealed class ZombifySelfActionEvent : InstantActionEvent { };
|
||||
2
Resources/Locale/en-US/actions/actions/zombie.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
turn-undead-action-name = Turn Undead
|
||||
turn-undead-action-description = Succumb to your infection and become a zombie.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,3 +24,12 @@
|
||||
- !type:DiseaseReagentCure
|
||||
reagent: Romerol
|
||||
min: 5
|
||||
|
||||
- type: disease
|
||||
id: PassiveZombieVirus
|
||||
name: Zombie Virus
|
||||
infectious: false
|
||||
cureResist: 1 #no cure. Death is your cure.
|
||||
effects:
|
||||
- !type:DiseaseAddComponent
|
||||
comp: ZombifyOnDeath
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
Resources/Prototypes/Roles/Antags/zombie.yml
Normal file
@@ -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."
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -59,3 +59,9 @@
|
||||
config:
|
||||
!type:GenericGameRuleConfiguration
|
||||
id: Secret
|
||||
|
||||
- type: gameRule
|
||||
id: Zombie
|
||||
config:
|
||||
!type:GenericGameRuleConfiguration
|
||||
id: Zombie
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
Extended: 0.25
|
||||
Nukeops: 0.25
|
||||
Traitor: 0.75
|
||||
Zombie: 0.05
|
||||
|
||||
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 623 B |
|
After Width: | Height: | Size: 652 B |
|
After Width: | Height: | Size: 141 B |
BIN
Resources/Textures/Clothing/Head/Hardsuits/cburn.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 519 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 333 B |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -36,6 +36,9 @@
|
||||
},
|
||||
{
|
||||
"name": "ratKingDomain"
|
||||
},
|
||||
{
|
||||
"name": "zombie-turn"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Interface/Actions/zombie-turn.png
Normal file
|
After Width: | Height: | Size: 858 B |