Merge remote-tracking branch 'upstream/master' into ed-06-11-2024-upstream

# Conflicts:
#	Resources/Changelog/Changelog.yml
#	Resources/Credits/GitHub.txt
#	Resources/Prototypes/Entities/Mobs/Player/silicon.yml
This commit is contained in:
Ed
2024-11-06 16:53:24 +03:00
207 changed files with 2180 additions and 1314 deletions

View File

@@ -461,6 +461,18 @@ namespace Content.Shared.CCVar
* Discord
*/
/// <summary>
/// The role that will get mentioned if a new SOS ahelp comes in.
/// </summary>
public static readonly CVarDef<string> DiscordAhelpMention =
CVarDef.Create("discord.on_call_ping", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
/// <summary>
/// URL of the discord webhook to relay unanswered ahelp messages.
/// </summary>
public static readonly CVarDef<string> DiscordOnCallWebhook =
CVarDef.Create("discord.on_call_webhook", string.Empty, CVar.SERVERONLY | CVar.CONFIDENTIAL);
/// <summary>
/// URL of the Discord webhook which will relay all ahelp messages.
/// </summary>
@@ -1472,7 +1484,7 @@ namespace Content.Shared.CCVar
/// Config for when the votekick should be allowed to be called based on number of eligible voters.
/// </summary>
public static readonly CVarDef<int> VotekickEligibleNumberRequirement =
CVarDef.Create("votekick.eligible_number", 10, CVar.SERVERONLY);
CVarDef.Create("votekick.eligible_number", 5, CVar.SERVERONLY);
/// <summary>
/// Whether a votekick initiator must be a ghost or not.
@@ -1480,6 +1492,18 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<bool> VotekickInitiatorGhostRequirement =
CVarDef.Create("votekick.initiator_ghost_requirement", true, CVar.SERVERONLY);
/// <summary>
/// Should the initiator be whitelisted to initiate a votekick?
/// </summary>
public static readonly CVarDef<bool> VotekickInitiatorWhitelistedRequirement =
CVarDef.Create("votekick.initiator_whitelist_requirement", true, CVar.SERVERONLY);
/// <summary>
/// Should the initiator be able to start a votekick if they are bellow the votekick.voter_playtime requirement?
/// </summary>
public static readonly CVarDef<bool> VotekickInitiatorTimeRequirement =
CVarDef.Create("votekick.initiator_time_requirement", false, CVar.SERVERONLY);
/// <summary>
/// Whether a votekick voter must be a ghost or not.
/// </summary>
@@ -1496,7 +1520,7 @@ namespace Content.Shared.CCVar
/// Config for how many seconds a player must have been dead to initiate a votekick / be able to vote on a votekick.
/// </summary>
public static readonly CVarDef<int> VotekickEligibleVoterDeathtime =
CVarDef.Create("votekick.voter_deathtime", 180, CVar.REPLICATED | CVar.SERVER);
CVarDef.Create("votekick.voter_deathtime", 30, CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// The required ratio of eligible voters that must agree for a votekick to go through.
@@ -1540,6 +1564,12 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<int> VotekickBanDuration =
CVarDef.Create("votekick.ban_duration", 180, CVar.SERVERONLY);
/// <summary>
/// Whether the ghost requirement settings for votekicks should be ignored for the lobby.
/// </summary>
public static readonly CVarDef<bool> VotekickIgnoreGhostReqInLobby =
CVarDef.Create("votekick.ignore_ghost_req_in_lobby", true, CVar.SERVERONLY);
/*
* BAN
*/

View File

@@ -33,7 +33,7 @@ public sealed partial class CartridgeLoaderComponent : Component
/// The maximum amount of programs that can be installed on the cartridge loader entity
/// </summary>
[DataField]
public int DiskSpace = 5;
public int DiskSpace = 8;
/// <summary>
/// Controls whether the cartridge loader will play notifications if it supports it at all

View File

@@ -0,0 +1,21 @@
namespace Content.Shared.Chemistry.Components;
/// <summary>
/// Represents a container that also contains a solution.
/// This means that reactive entities react when inserted into the container.
/// </summary>
[RegisterComponent]
public sealed partial class ReactiveContainerComponent : Component
{
/// <summary>
/// The container that holds the solution.
/// </summary>
[DataField(required: true)]
public string Container = default!;
/// <summary>
/// The solution in the container.
/// </summary>
[DataField(required: true)]
public string Solution = default!;
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Robust.Shared.Containers;
namespace Content.Shared.Chemistry.EntitySystems;
public sealed class ReactiveContainerSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReactiveContainerComponent, EntInsertedIntoContainerMessage>(OnInserted);
SubscribeLocalEvent<ReactiveContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
}
private void OnInserted(EntityUid uid, ReactiveContainerComponent comp, EntInsertedIntoContainerMessage args)
{
// Only reactive entities can react with the solution
if (!HasComp<ReactiveComponent>(args.Entity))
return;
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
return;
if (solution.Volume == 0)
return;
_reactiveSystem.DoEntityReaction(args.Entity, solution, ReactionMethod.Touch);
}
private void OnSolutionChange(EntityUid uid, ReactiveContainerComponent comp, SolutionContainerChangedEvent args)
{
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
return;
if (solution.Volume == 0)
return;
if (!TryComp<ContainerManagerComponent>(uid, out var manager))
return;
if (!_containerSystem.TryGetContainer(uid, comp.Container, out var container))
return;
foreach (var entity in container.ContainedEntities)
{
if (!HasComp<ReactiveComponent>(entity))
continue;
_reactiveSystem.DoEntityReaction(entity, solution, ReactionMethod.Touch);
}
}
}

View File

@@ -60,6 +60,7 @@ namespace Content.Shared.Containers.ItemSlots
}
#region ComponentManagement
/// <summary>
/// Spawn in starting items for any item slots that should have one.
/// </summary>
@@ -70,7 +71,8 @@ namespace Content.Shared.Containers.ItemSlots
if (slot.HasItem || string.IsNullOrEmpty(slot.StartingItem))
continue;
var item = EntityManager.SpawnEntity(slot.StartingItem, EntityManager.GetComponent<TransformComponent>(uid).Coordinates);
var item = Spawn(slot.StartingItem, Transform(uid).Coordinates);
if (slot.ContainerSlot != null)
_containers.Insert(item, slot.ContainerSlot);
}
@@ -99,7 +101,8 @@ namespace Content.Shared.Containers.ItemSlots
if (itemSlots.Slots.TryGetValue(id, out var existing))
{
if (existing.Local)
Log.Error($"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(uid).EntityName} ({uid}), key: {id}");
Log.Error(
$"Duplicate item slot key. Entity: {EntityManager.GetComponent<MetaDataComponent>(uid).EntityName} ({uid}), key: {id}");
else
// server state takes priority
slot.CopyFrom(existing);
@@ -134,7 +137,10 @@ namespace Content.Shared.Containers.ItemSlots
Dirty(uid, itemSlots);
}
public bool TryGetSlot(EntityUid uid, string slotId, [NotNullWhen(true)] out ItemSlot? itemSlot, ItemSlotsComponent? component = null)
public bool TryGetSlot(EntityUid uid,
string slotId,
[NotNullWhen(true)] out ItemSlot? itemSlot,
ItemSlotsComponent? component = null)
{
itemSlot = null;
@@ -143,9 +149,11 @@ namespace Content.Shared.Containers.ItemSlots
return component.Slots.TryGetValue(slotId, out itemSlot);
}
#endregion
#region Interactions
/// <summary>
/// Attempt to take an item from a slot, if any are set to EjectOnInteract.
/// </summary>
@@ -201,20 +209,50 @@ namespace Content.Shared.Containers.ItemSlots
if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands))
return;
if (itemSlots.Slots.Count == 0)
return;
// If any slot can be inserted into don't show popup.
// If any whitelist passes, but slot is locked, then show locked.
// If whitelist fails all, show whitelist fail.
// valid, insertable slots (if any)
var slots = new List<ItemSlot>();
string? whitelistFailPopup = null;
string? lockedFailPopup = null;
foreach (var slot in itemSlots.Slots.Values)
{
if (!slot.InsertOnInteract)
continue;
if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User))
continue;
if (CanInsert(uid, args.Used, args.User, slot, slot.Swap))
{
slots.Add(slot);
}
else
{
var allowed = CanInsertWhitelist(args.Used, slot);
if (lockedFailPopup == null && slot.LockedFailPopup != null && allowed && slot.Locked)
lockedFailPopup = slot.LockedFailPopup;
slots.Add(slot);
if (whitelistFailPopup == null && slot.WhitelistFailPopup != null)
whitelistFailPopup = slot.WhitelistFailPopup;
}
}
if (slots.Count == 0)
{
// it's a bit weird that the popupMessage is stored with the item slots themselves, but in practice
// the popup messages will just all be the same, so it's probably fine.
//
// doing a check to make sure that they're all the same or something is probably frivolous
if (lockedFailPopup != null)
_popupSystem.PopupClient(Loc.GetString(lockedFailPopup), uid, args.User);
else if (whitelistFailPopup != null)
_popupSystem.PopupClient(Loc.GetString(whitelistFailPopup), uid, args.User);
return;
}
// Drop the held item onto the floor. Return if the user cannot drop.
if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands))
@@ -236,23 +274,31 @@ namespace Content.Shared.Containers.ItemSlots
return;
}
}
#endregion
#region Insert
/// <summary>
/// Insert an item into a slot. This does not perform checks, so make sure to also use <see
/// cref="CanInsert"/> or just use <see cref="TryInsert"/> instead.
/// </summary>
/// <param name="excludeUserAudio">If true, will exclude the user when playing sound. Does nothing client-side.
/// Useful for predicted interactions</param>
private void Insert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user, bool excludeUserAudio = false)
private void Insert(EntityUid uid,
ItemSlot slot,
EntityUid item,
EntityUid? user,
bool excludeUserAudio = false)
{
bool? inserted = slot.ContainerSlot != null ? _containers.Insert(item, slot.ContainerSlot) : null;
// ContainerSlot automatically raises a directed EntInsertedIntoContainerMessage
// Logging
if (inserted != null && inserted.Value && user != null)
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user.Value)} inserted {ToPrettyString(item)} into {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(user.Value)} inserted {ToPrettyString(item)} into {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_audioSystem.PlayPredicted(slot.InsertSound, uid, excludeUserAudio ? user : null);
}
@@ -261,46 +307,53 @@ namespace Content.Shared.Containers.ItemSlots
/// Check whether a given item can be inserted into a slot. Unless otherwise specified, this will return
/// false if the slot is already filled.
/// </summary>
/// <remarks>
/// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to
/// pass the whitelist or due to slot being locked, then this will generate an appropriate popup.
/// </remarks>
public bool CanInsert(EntityUid uid, EntityUid usedUid, EntityUid? user, ItemSlot slot, bool swap = false, EntityUid? popup = null)
public bool CanInsert(EntityUid uid,
EntityUid usedUid,
EntityUid? user,
ItemSlot slot,
bool swap = false)
{
if (slot.ContainerSlot == null)
return false;
if (_whitelistSystem.IsWhitelistFail(slot.Whitelist, usedUid) || _whitelistSystem.IsBlacklistPass(slot.Blacklist, usedUid))
{
if (popup.HasValue && slot.WhitelistFailPopup.HasValue)
_popupSystem.PopupClient(Loc.GetString(slot.WhitelistFailPopup), uid, popup.Value);
if (slot.HasItem && (!swap || swap && !CanEject(uid, user, slot)))
return false;
if (!CanInsertWhitelist(usedUid, slot))
return false;
}
if (slot.Locked)
{
if (popup.HasValue && slot.LockedFailPopup.HasValue)
_popupSystem.PopupClient(Loc.GetString(slot.LockedFailPopup), uid, popup.Value);
return false;
}
if (slot.HasItem && (!swap || (swap && !CanEject(uid, user, slot))))
return false;
var ev = new ItemSlotInsertAttemptEvent(uid, usedUid, user, slot);
RaiseLocalEvent(uid, ref ev);
RaiseLocalEvent(usedUid, ref ev);
if (ev.Cancelled)
{
return false;
}
return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: swap);
}
private bool CanInsertWhitelist(EntityUid usedUid, ItemSlot slot)
{
if (_whitelistSystem.IsWhitelistFail(slot.Whitelist, usedUid)
|| _whitelistSystem.IsBlacklistPass(slot.Blacklist, usedUid))
return false;
return true;
}
/// <summary>
/// Tries to insert item into a specific slot.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsert(EntityUid uid, string id, EntityUid item, EntityUid? user, ItemSlotsComponent? itemSlots = null, bool excludeUserAudio = false)
public bool TryInsert(EntityUid uid,
string id,
EntityUid item,
EntityUid? user,
ItemSlotsComponent? itemSlots = null,
bool excludeUserAudio = false)
{
if (!Resolve(uid, ref itemSlots))
return false;
@@ -315,7 +368,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Tries to insert item into a specific slot.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user, bool excludeUserAudio = false)
public bool TryInsert(EntityUid uid,
ItemSlot slot,
EntityUid item,
EntityUid? user,
bool excludeUserAudio = false)
{
if (!CanInsert(uid, item, user, slot))
return false;
@@ -329,7 +386,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Does not check action blockers.
/// </summary>
/// <returns>False if failed to insert item</returns>
public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, HandsComponent? hands = null, bool excludeUserAudio = false)
public bool TryInsertFromHand(EntityUid uid,
ItemSlot slot,
EntityUid user,
HandsComponent? hands = null,
bool excludeUserAudio = false)
{
if (!Resolve(user, ref hands, false))
return false;
@@ -443,6 +504,7 @@ namespace Content.Shared.Containers.ItemSlots
return 1;
}
#endregion
#region Eject
@@ -462,7 +524,7 @@ namespace Content.Shared.Containers.ItemSlots
return false;
}
if (slot.ContainerSlot?.ContainedEntity is not {} item)
if (slot.ContainerSlot?.ContainedEntity is not { } item)
return false;
var ev = new ItemSlotEjectAttemptEvent(uid, item, user, slot);
@@ -487,7 +549,9 @@ namespace Content.Shared.Containers.ItemSlots
// Logging
if (ejected != null && ejected.Value && user != null)
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user.Value)} ejected {ToPrettyString(item)} from {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_adminLogger.Add(LogType.Action,
LogImpact.Low,
$"{ToPrettyString(user.Value)} ejected {ToPrettyString(item)} from {slot.ContainerSlot?.ID + " slot of "}{ToPrettyString(uid)}");
_audioSystem.PlayPredicted(slot.EjectSound, uid, excludeUserAudio ? user : null);
}
@@ -496,7 +560,11 @@ namespace Content.Shared.Containers.ItemSlots
/// Try to eject an item from a slot.
/// </summary>
/// <returns>False if item slot is locked or has no item inserted</returns>
public bool TryEject(EntityUid uid, ItemSlot slot, EntityUid? user, [NotNullWhen(true)] out EntityUid? item, bool excludeUserAudio = false)
public bool TryEject(EntityUid uid,
ItemSlot slot,
EntityUid? user,
[NotNullWhen(true)] out EntityUid? item,
bool excludeUserAudio = false)
{
item = null;
@@ -518,8 +586,12 @@ namespace Content.Shared.Containers.ItemSlots
/// Try to eject item from a slot.
/// </summary>
/// <returns>False if the id is not valid, the item slot is locked, or it has no item inserted</returns>
public bool TryEject(EntityUid uid, string id, EntityUid? user,
[NotNullWhen(true)] out EntityUid? item, ItemSlotsComponent? itemSlots = null, bool excludeUserAudio = false)
public bool TryEject(EntityUid uid,
string id,
EntityUid? user,
[NotNullWhen(true)] out EntityUid? item,
ItemSlotsComponent? itemSlots = null,
bool excludeUserAudio = false)
{
item = null;
@@ -550,12 +622,16 @@ namespace Content.Shared.Containers.ItemSlots
return true;
}
#endregion
#region Verbs
private void AddAlternativeVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent<AlternativeVerb> args)
private void AddAlternativeVerbs(EntityUid uid,
ItemSlotsComponent itemSlots,
GetVerbsEvent<AlternativeVerb> args)
{
if (args.Hands == null || !args.CanAccess ||!args.CanInteract)
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
{
return;
}
@@ -649,7 +725,9 @@ namespace Content.Shared.Containers.ItemSlots
}
}
private void AddInteractionVerbsVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent<InteractionVerb> args)
private void AddInteractionVerbsVerbs(EntityUid uid,
ItemSlotsComponent itemSlots,
GetVerbsEvent<InteractionVerb> args)
{
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
return;
@@ -708,7 +786,7 @@ namespace Content.Shared.Containers.ItemSlots
new SpriteSpecifier.Texture(
new ResPath("/Textures/Interface/VerbIcons/insert.svg.192dpi.png"));
}
else if(slot.EjectOnInteract)
else if (slot.EjectOnInteract)
{
// Inserting/ejecting is a primary interaction for this entity. Instead of using the insert
// category, we will use a single "Place <item>" verb.
@@ -727,9 +805,11 @@ namespace Content.Shared.Containers.ItemSlots
args.Verbs.Add(insertVerb);
}
}
#endregion
#region BUIs
private void HandleButtonPressed(EntityUid uid, ItemSlotsComponent component, ItemSlotButtonPressedEvent args)
{
if (!component.Slots.TryGetValue(args.SlotId, out var slot))
@@ -740,6 +820,7 @@ namespace Content.Shared.Containers.ItemSlots
else if (args.TryInsert && !slot.HasItem)
TryInsertFromHand(uid, slot, args.Actor);
}
#endregion
/// <summary>

View File

@@ -47,7 +47,7 @@ public abstract partial class InventorySystem
private void OnEntRemoved(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
var unequippedEvent = new DidUnequipEvent(uid, args.Entity, slotDef);
@@ -59,8 +59,8 @@ public abstract partial class InventorySystem
private void OnEntInserted(EntityUid uid, InventoryComponent component, EntInsertedIntoContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
return;
var equippedEvent = new DidEquipEvent(uid, args.Entity, slotDef);
RaiseLocalEvent(uid, equippedEvent, true);
@@ -118,7 +118,7 @@ public abstract partial class InventorySystem
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter:true);
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter: true);
}
public bool TryEquip(EntityUid uid, EntityUid itemUid, string slot, bool silent = false, bool force = false, bool predicted = false,
@@ -365,6 +365,25 @@ public abstract partial class InventorySystem
ClothingComponent? clothing = null,
bool reparent = true,
bool checkDoafter = false)
{
var itemsDropped = 0;
return TryUnequip(actor, target, slot, out removedItem, ref itemsDropped,
silent, force, predicted, inventory, clothing, reparent, checkDoafter);
}
private bool TryUnequip(
EntityUid actor,
EntityUid target,
string slot,
[NotNullWhen(true)] out EntityUid? removedItem,
ref int itemsDropped,
bool silent = false,
bool force = false,
bool predicted = false,
InventoryComponent? inventory = null,
ClothingComponent? clothing = null,
bool reparent = true,
bool checkDoafter = false)
{
removedItem = null;
@@ -423,17 +442,27 @@ public abstract partial class InventorySystem
return false;
}
if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent))
return false;
// this is in order to keep track of whether this is the first instance of a recursion call
var firstRun = itemsDropped == 0;
++itemsDropped;
foreach (var slotDef in inventory.Slots)
{
if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name)
{
//this recursive call might be risky
TryUnequip(actor, target, slotDef.Name, true, true, predicted, inventory, reparent: reparent);
TryUnequip(actor, target, slotDef.Name, out _, ref itemsDropped, true, true, predicted, inventory, reparent: reparent);
}
}
if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent))
return false;
// we check if any items were dropped, and make a popup if they were.
// the reason we check for > 1 is because the first item is always the one we are trying to unequip,
// whereas we only want to notify for extra dropped items.
if (!silent && _gameTiming.IsFirstTimePredicted && firstRun && itemsDropped > 1)
_popup.PopupClient(Loc.GetString("inventory-component-dropped-from-unequip", ("items", itemsDropped - 1)), target, target);
// TODO: Inventory needs a hot cleanup hoo boy
// Check if something else (AKA toggleable) dumped it into a container.
@@ -466,7 +495,7 @@ public abstract partial class InventorySystem
if ((containerSlot == null || slotDefinition == null) && !TryGetSlotContainer(target, slot, out containerSlot, out slotDefinition, inventory))
return false;
if (containerSlot.ContainedEntity is not {} itemUid)
if (containerSlot.ContainedEntity is not { } itemUid)
return false;
if (!_containerSystem.CanRemove(itemUid, containerSlot))

View File

@@ -81,6 +81,24 @@ public sealed partial class NpcFactionSystem : EntitySystem
return ent.Comp.Factions.Contains(faction);
}
/// <summary>
/// Returns whether an entity is a member of any listed faction.
/// If the list is empty this returns false.
/// </summary>
public bool IsMemberOfAny(Entity<NpcFactionMemberComponent?> ent, IEnumerable<ProtoId<NpcFactionPrototype>> factions)
{
if (!Resolve(ent, ref ent.Comp, false))
return false;
foreach (var faction in factions)
{
if (ent.Comp.Factions.Contains(faction))
return true;
}
return false;
}
/// <summary>
/// Adds this entity to the particular faction.
/// </summary>

View File

@@ -33,18 +33,10 @@ public sealed class HungerSystem : EntitySystem
[ValidatePrototypeId<SatiationIconPrototype>]
private const string HungerIconStarvingId = "HungerIconStarving";
private SatiationIconPrototype? _hungerIconOverfed;
private SatiationIconPrototype? _hungerIconPeckish;
private SatiationIconPrototype? _hungerIconStarving;
public override void Initialize()
{
base.Initialize();
DebugTools.Assert(_prototype.TryIndex(HungerIconOverfedId, out _hungerIconOverfed) &&
_prototype.TryIndex(HungerIconPeckishId, out _hungerIconPeckish) &&
_prototype.TryIndex(HungerIconStarvingId, out _hungerIconStarving));
SubscribeLocalEvent<HungerComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<HungerComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<HungerComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
@@ -221,13 +213,13 @@ public sealed class HungerSystem : EntitySystem
switch (component.CurrentThreshold)
{
case HungerThreshold.Overfed:
prototype = _hungerIconOverfed;
_prototype.TryIndex(HungerIconOverfedId, out prototype);
break;
case HungerThreshold.Peckish:
prototype = _hungerIconPeckish;
_prototype.TryIndex(HungerIconPeckishId, out prototype);
break;
case HungerThreshold.Starving:
prototype = _hungerIconStarving;
_prototype.TryIndex(HungerIconStarvingId, out prototype);
break;
default:
prototype = null;

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Nutrition.EntitySystems;
@@ -31,18 +32,10 @@ public sealed class ThirstSystem : EntitySystem
[ValidatePrototypeId<SatiationIconPrototype>]
private const string ThirstIconParchedId = "ThirstIconParched";
private SatiationIconPrototype? _thirstIconOverhydrated = null;
private SatiationIconPrototype? _thirstIconThirsty = null;
private SatiationIconPrototype? _thirstIconParched = null;
public override void Initialize()
{
base.Initialize();
DebugTools.Assert(_prototype.TryIndex(ThirstIconOverhydratedId, out _thirstIconOverhydrated) &&
_prototype.TryIndex(ThirstIconThirstyId, out _thirstIconThirsty) &&
_prototype.TryIndex(ThirstIconParchedId, out _thirstIconParched));
SubscribeLocalEvent<ThirstComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<ThirstComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ThirstComponent, RejuvenateEvent>(OnRejuvenate);
@@ -128,26 +121,28 @@ public sealed class ThirstSystem : EntitySystem
}
}
public bool TryGetStatusIconPrototype(ThirstComponent component, out SatiationIconPrototype? prototype)
public bool TryGetStatusIconPrototype(ThirstComponent component, [NotNullWhen(true)] out SatiationIconPrototype? prototype)
{
switch (component.CurrentThirstThreshold)
{
case ThirstThreshold.OverHydrated:
prototype = _thirstIconOverhydrated;
return true;
_prototype.TryIndex(ThirstIconOverhydratedId, out prototype);
break;
case ThirstThreshold.Thirsty:
prototype = _thirstIconThirsty;
return true;
_prototype.TryIndex(ThirstIconThirstyId, out prototype);
break;
case ThirstThreshold.Parched:
prototype = _thirstIconParched;
return true;
_prototype.TryIndex(ThirstIconParchedId, out prototype);
break;
default:
prototype = null;
return false;
break;
}
return prototype != null;
}
private void UpdateEffects(EntityUid uid, ThirstComponent component)

View File

@@ -2,7 +2,6 @@ using Content.Shared.Damage.Prototypes;
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Overlays;
@@ -15,8 +14,11 @@ public sealed partial class ShowHealthBarsComponent : Component
/// <summary>
/// Displays health bars of the damage containers.
/// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
public List<string> DamageContainers = new();
[DataField]
public List<ProtoId<DamageContainerPrototype>> DamageContainers = new()
{
"Biological"
};
[DataField]
public ProtoId<HealthIconPrototype>? HealthStatusIcon = "HealthIconFine";

View File

@@ -1,6 +1,6 @@
using Content.Shared.Damage.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Prototypes;
namespace Content.Shared.Overlays;
@@ -13,6 +13,9 @@ public sealed partial class ShowHealthIconsComponent : Component
/// <summary>
/// Displays health status icons of the damage containers.
/// </summary>
[DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer<DamageContainerPrototype>))]
public List<string> DamageContainers = new();
[DataField]
public List<ProtoId<DamageContainerPrototype>> DamageContainers = new()
{
"Biological"
};
}

View File

@@ -24,7 +24,7 @@ public sealed partial class SiliconLawProviderComponent : Component
/// <summary>
/// The sound that plays for the Silicon player
/// when the particular lawboard has been inserted.
/// when the law change is processed for the provider.
/// </summary>
[DataField]
public SoundSpecifier? LawUploadSound = new SoundPathSpecifier("/Audio/Misc/cryo_warning.ogg");

View File

@@ -324,6 +324,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
if (TryComp(user, out EyeComponent? eyeComp))
{
_eye.SetDrawFov(user, false, eyeComp);
_eye.SetTarget(user, ent.Comp.RemoteEntity.Value, eyeComp);
}
@@ -356,6 +357,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
if (TryComp(args.Entity, out EyeComponent? eyeComp))
{
_eye.SetDrawFov(args.Entity, true, eyeComp);
_eye.SetTarget(args.Entity, null, eyeComp);
}
ClearEye(ent);

View File

@@ -3,6 +3,7 @@ using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
@@ -125,7 +126,7 @@ public sealed class WieldableSystem : EntitySystem
private void OnExamine(EntityUid uid, GunWieldBonusComponent component, ref ExaminedEvent args)
{
if (HasComp<GunRequiresWieldComponent>(uid))
if (HasComp<GunRequiresWieldComponent>(uid))
return;
if (component.WieldBonusExamineMessage != null)
@@ -253,7 +254,7 @@ public sealed class WieldableSystem : EntitySystem
return false;
var selfMessage = Loc.GetString("wieldable-component-successful-wield", ("item", used));
var othersMessage = Loc.GetString("wieldable-component-successful-wield-other", ("user", user), ("item", used));
var othersMessage = Loc.GetString("wieldable-component-successful-wield-other", ("user", Identity.Entity(user, EntityManager)), ("item", used));
_popupSystem.PopupPredicted(selfMessage, othersMessage, user, user);
var targEv = new ItemWieldedEvent();
@@ -298,7 +299,7 @@ public sealed class WieldableSystem : EntitySystem
_audioSystem.PlayPredicted(component.UnwieldSound, uid, args.User);
var selfMessage = Loc.GetString("wieldable-component-failed-wield", ("item", uid));
var othersMessage = Loc.GetString("wieldable-component-failed-wield-other", ("user", args.User.Value), ("item", uid));
var othersMessage = Loc.GetString("wieldable-component-failed-wield-other", ("user", Identity.Entity(args.User.Value, EntityManager)), ("item", uid));
_popupSystem.PopupPredicted(selfMessage, othersMessage, args.User.Value, args.User.Value);
}