Refactor antag rule code (#23445)
* Initial Pass, Rev, Thief * Zombie initial pass * Rebase, Traitor * Nukeops, More overloads * Revert RevolutionaryRuleComponent * Use TryRoundStartAttempt, Rewrite nukie spawning * Comments, Add task scheduler to GameRuleSystem * Zombie initial testing done * Sort methods, rework GameRuleTask * Add CCVar, Initial testing continues * Might as well get rid of the obsolete logging * Oops, i dont know how to log apparently * Suggested formatting fixes * Suggested changes * Fix merge issues * Minor optimisation * Allowed thief to choose other antags * Review changes * Spawn items on floor first, then inserting * minor tweaks * Shift as much as possible to ProtoId<> * Remove unneeded * Add exclusive antag attribute * Fix merge issues * Minor formatting fix * Convert to struct * Cleanup * Review cleanup (need to test a lot) * Some fixes, (mostly) tested * oop * Pass tests (for real) --------- Co-authored-by: Rainfall <rainfey0+git@gmail.com> Co-authored-by: AJCM <AJCM@tutanota.com>
This commit is contained in:
22
Content.Shared/Antag/AntagAcceptability.cs
Normal file
22
Content.Shared/Antag/AntagAcceptability.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Content.Shared.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Used by AntagSelectionSystem to indicate which types of antag roles are allowed to choose the same entity
|
||||
/// For example, Thief HeadRev
|
||||
/// </summary>
|
||||
public enum AntagAcceptability
|
||||
{
|
||||
/// <summary>
|
||||
/// Dont choose anyone who already has an antag role
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Dont choose anyone who has an exclusive antag role
|
||||
/// </summary>
|
||||
NotExclusive,
|
||||
/// <summary>
|
||||
/// Choose anyone
|
||||
/// </summary>
|
||||
All
|
||||
}
|
||||
|
||||
@@ -487,6 +487,13 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<int> PiratesPlayersPerOp =
|
||||
CVarDef.Create("pirates.players_per_pirate", 5);
|
||||
|
||||
/*
|
||||
* Nukeops
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> NukeopsSpawnGhostRoles =
|
||||
CVarDef.Create("nukeops.spawn_ghost_roles", false);
|
||||
|
||||
/*
|
||||
* Tips
|
||||
*/
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Inventory;
|
||||
|
||||
public partial class InventorySystem
|
||||
{
|
||||
[Dependency] private readonly SharedStorageSystem _storageSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Yields all entities in hands or inventory slots with the specific flags.
|
||||
/// </summary>
|
||||
@@ -86,4 +90,55 @@ public partial class InventorySystem
|
||||
// We finally try to equip the item, otherwise we delete it.
|
||||
return TryEquip(uid, item, slot, silent, force) || DeleteItem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to spawn a list of items inside of an entities bag, pockets, hands or nearby
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that you want to spawn an item on</param>
|
||||
/// <param name="items">A list of prototype IDs that you want to spawn in the bag.</param>
|
||||
public void SpawnItemsOnEntity(EntityUid entity, List<EntProtoId> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
SpawnItemOnEntity(entity, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to spawn an item inside of an entities bag, pockets, hands or nearby
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that you want to spawn an item on</param>
|
||||
/// <param name="item">The prototype ID that you want to spawn in the bag.</param>
|
||||
public void SpawnItemOnEntity(EntityUid entity, EntProtoId item)
|
||||
{
|
||||
//Transform() throws error if TransformComponent doesnt exist
|
||||
if (!HasComp<TransformComponent>(entity))
|
||||
return;
|
||||
|
||||
var xform = Transform(entity);
|
||||
var mapCoords = _transform.GetMapCoordinates(xform);
|
||||
|
||||
var itemToSpawn = Spawn(item, mapCoords);
|
||||
|
||||
//Try insert into the backpack
|
||||
if (TryGetSlotContainer(entity, "back", out var backSlot, out _)
|
||||
&& backSlot.ContainedEntity.HasValue
|
||||
&& _storageSystem.Insert(backSlot.ContainedEntity.Value, itemToSpawn, out _)
|
||||
)
|
||||
return;
|
||||
|
||||
//Try insert into pockets
|
||||
if (TryGetSlotContainer(entity, "pocket1", out var pocket1, out _)
|
||||
&& _containerSystem.Insert(itemToSpawn, pocket1)
|
||||
)
|
||||
return;
|
||||
|
||||
if (TryGetSlotContainer(entity, "pocket2", out var pocket2, out _)
|
||||
&& _containerSystem.Insert(itemToSpawn, pocket2)
|
||||
)
|
||||
return;
|
||||
|
||||
//Try insert into hands, or drop on the floor
|
||||
_handsSystem.PickupOrDrop(entity, itemToSpawn, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,30 +10,28 @@ public enum WarDeclaratorUiKey
|
||||
|
||||
public enum WarConditionStatus : byte
|
||||
{
|
||||
WAR_READY,
|
||||
WAR_DELAY,
|
||||
YES_WAR,
|
||||
NO_WAR_UNKNOWN,
|
||||
NO_WAR_TIMEOUT,
|
||||
NO_WAR_SMALL_CREW,
|
||||
NO_WAR_SHUTTLE_DEPARTED
|
||||
WarReady,
|
||||
YesWar,
|
||||
NoWarUnknown,
|
||||
NoWarTimeout,
|
||||
NoWarSmallCrew,
|
||||
NoWarShuttleDeparted
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class WarDeclaratorBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public WarConditionStatus Status;
|
||||
public int MinCrew;
|
||||
public TimeSpan Delay;
|
||||
public WarConditionStatus? Status;
|
||||
public TimeSpan ShuttleDisabledTime;
|
||||
public TimeSpan EndTime;
|
||||
|
||||
public WarDeclaratorBoundUserInterfaceState(WarConditionStatus status, int minCrew, TimeSpan delay, TimeSpan endTime)
|
||||
public WarDeclaratorBoundUserInterfaceState(WarConditionStatus? status, TimeSpan endTime, TimeSpan shuttleDisabledTime)
|
||||
{
|
||||
Status = status;
|
||||
MinCrew = minCrew;
|
||||
Delay = delay;
|
||||
EndTime = endTime;
|
||||
ShuttleDisabledTime = shuttleDisabledTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Shared.Antag;
|
||||
using Robust.Shared.GameStates;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Revolutionary.Components;
|
||||
|
||||
@@ -17,8 +18,14 @@ public sealed partial class RevolutionaryComponent : Component, IAntagStatusIcon
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "RevolutionaryFaction";
|
||||
|
||||
/// <summary>
|
||||
/// Sound that plays when you are chosen as Rev. (Placeholder until I find something cool I guess)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier RevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg");
|
||||
|
||||
public override bool SessionSpecific => true;
|
||||
|
||||
[DataField]
|
||||
public bool IconVisibleToGhost { get; set; } = true;
|
||||
public bool IconVisibleToGhost { get; set; } = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
@@ -7,3 +8,13 @@ public abstract partial class AntagonistRoleComponent : Component
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string? PrototypeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the antagonist role component as being exclusive
|
||||
/// IE by default other antagonists should refuse to select the same entity for a different antag role
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(AntagonistRoleComponent))]
|
||||
public sealed partial class ExclusiveAntagonistAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
namespace Content.Shared.Roles;
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on a mind entity id to get whether or not the player is considered an antagonist,
|
||||
/// depending on their roles.
|
||||
/// </summary>
|
||||
/// <param name="IsAntagonist">Whether or not the player is an antagonist.</param>
|
||||
/// <param name="IsExclusiveAntagonist">Whether or not AntagSelectionSystem should exclude this player from other antag roles</param
|
||||
[ByRefEvent]
|
||||
public record struct MindIsAntagonistEvent(bool IsAntagonist);
|
||||
public record struct MindIsAntagonistEvent(bool IsAntagonist, bool IsExclusiveAntagonist);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
@@ -57,7 +58,7 @@ public abstract class SharedRoleSystem : EntitySystem
|
||||
args.Roles.Add(new RoleInfo(component, name, true, null, prototype));
|
||||
});
|
||||
|
||||
SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => args.IsAntagonist = true);
|
||||
SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => { args.IsAntagonist = true; args.IsExclusiveAntagonist |= typeof(T).TryGetCustomAttribute<ExclusiveAntagonistAttribute>(out _); });
|
||||
_antagTypes.Add(typeof(T));
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ public abstract class SharedRoleSystem : EntitySystem
|
||||
AddComp(mindId, component);
|
||||
var antagonist = IsAntagonistRole<T>();
|
||||
|
||||
var mindEv = new MindRoleAddedEvent();
|
||||
var mindEv = new MindRoleAddedEvent(silent);
|
||||
RaiseLocalEvent(mindId, ref mindEv);
|
||||
|
||||
var message = new RoleAddedEvent(mindId, mind, antagonist, silent);
|
||||
@@ -156,6 +157,21 @@ public abstract class SharedRoleSystem : EntitySystem
|
||||
return ev.IsAntagonist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this mind possess an exclusive antagonist role
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <returns>True if the mind possesses an exclusive antag role</returns>
|
||||
public bool MindIsExclusiveAntagonist(EntityUid? mindId)
|
||||
{
|
||||
if (mindId == null)
|
||||
return false;
|
||||
|
||||
var ev = new MindIsAntagonistEvent();
|
||||
RaiseLocalEvent(mindId.Value, ref ev);
|
||||
return ev.IsExclusiveAntagonist;
|
||||
}
|
||||
|
||||
public bool IsAntagonistRole<T>()
|
||||
{
|
||||
return _antagTypes.Contains(typeof(T));
|
||||
|
||||
Reference in New Issue
Block a user