Skip to content

Commit

Permalink
Suspicion Gamemode
Browse files Browse the repository at this point in the history
This is a catchup from the PR. Everything should be namespace properly... Should be...
  • Loading branch information
Simyon264 committed Oct 18, 2024
1 parent adb7aee commit cc1af44
Show file tree
Hide file tree
Showing 36 changed files with 265,975 additions and 3 deletions.
75 changes: 75 additions & 0 deletions Content.Client/_SSS/RadarOverlay/RadarOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Robust.Client.Graphics;
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Enums;

namespace Content.Client._SSS.RadarOverlay;

public sealed class RadarOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;

private readonly RadarOverlaySystem _suspicionRadarOverlaySystem;
private readonly TransformSystem _transformSystem;


private const float MinSize = 10f;
private const float MaxSize = 35f;

public override OverlaySpace Space => OverlaySpace.ScreenSpace;

public RadarOverlay()
{
IoCManager.InjectDependencies(this);

_suspicionRadarOverlaySystem = _entityManager.System<RadarOverlaySystem>();
_transformSystem = _entityManager.System<TransformSystem>();
}

protected override void Draw(in OverlayDrawArgs args)
{
var handle = args.ScreenHandle;

if (_player.LocalEntity is not { } localEntity)
return;

if (args.ViewportControl == null)
return;

var localPlayerPosition = _transformSystem.GetWorldPosition(localEntity);
var bounds = args.ViewportBounds;

foreach (var radarInfo in _suspicionRadarOverlaySystem.RadarInfos)
{
var distance = Vector2.Distance(radarInfo.Position, localPlayerPosition);
var screenPosition = args.ViewportControl.WorldToScreen(radarInfo.Position);

// Size of the radar blip is based on the distance from the player. The closer the player is, the smaller the blip.
var radius = Math.Clamp((int)(MaxSize - distance), MinSize, MaxSize);

// We clamp the radar blips to the screen bounds so you always see them.

if (screenPosition.X > bounds.Right)
{
screenPosition.X = bounds.Right;
}
else if (screenPosition.X < bounds.Left)
{
screenPosition.X = bounds.Left;
}

if (screenPosition.Y > bounds.Bottom)
{
screenPosition.Y = bounds.Bottom;
}
else if (screenPosition.Y < bounds.Top)
{
screenPosition.Y = bounds.Top;
}

handle.DrawCircle(screenPosition, radius, radarInfo.Color, false);
}
}
}
39 changes: 39 additions & 0 deletions Content.Client/_SSS/RadarOverlay/RadarOverlaySystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Content.Shared._SSS.RadarOverlay;
using Robust.Client.Graphics;

namespace Content.Client._SSS.RadarOverlay;

public sealed class RadarOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;

public RadarInfo[] RadarInfos = [];

public override void Initialize()
{
base.Initialize();

SubscribeNetworkEvent<OnRadarOverlayToggledEvent>(OnOverlayToggled);
SubscribeNetworkEvent<RadarOverlayUpdatedEvent>(OnOverlayUpdate);
}

public override void Shutdown()
{
base.Shutdown();

_overlayManager.RemoveOverlay<RadarOverlay>();
}

private void OnOverlayToggled(OnRadarOverlayToggledEvent ev)
{
if (ev.IsEnabled)
_overlayManager.AddOverlay(new RadarOverlay());
else
_overlayManager.RemoveOverlay<RadarOverlay>();
}

private void OnOverlayUpdate(RadarOverlayUpdatedEvent ev)
{
RadarInfos = ev.RadarInfos;
}
}
3 changes: 3 additions & 0 deletions Content.Server/Body/Systems/BodySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ public override HashSet<EntityUid> GibBody(
return new HashSet<EntityUid>();
}

if (!body.CanGib)
return new HashSet<EntityUid>();

var xform = Transform(bodyId);
if (xform.MapUid is null)
return new HashSet<EntityUid>();
Expand Down
13 changes: 13 additions & 0 deletions Content.Server/Ghost/GhostSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent arg
_actions.AddAction(uid, ref component.ToggleLightingActionEntity, component.ToggleLightingAction);
_actions.AddAction(uid, ref component.ToggleFoVActionEntity, component.ToggleFoVAction);
_actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction);

// SSS - Fix for map init not working ???
RaiseLocalEvent(new GhostSpawnedEvent(uid));
}

private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args)
Expand Down Expand Up @@ -587,4 +590,14 @@ public sealed class GhostAttemptHandleEvent(MindComponent mind, bool canReturnGl
public bool CanReturnGlobal { get; } = canReturnGlobal;
public bool Result { get; set; }
}

/// SSS - New event
/// <summary>
/// This is a workaround for me being unable to subscribe to the map init event of the ghost component; says theres already a subscriber to it...????
/// This gets called on the ghost.
/// </summary>
public sealed class GhostSpawnedEvent(EntityUid ghost)
{
public EntityUid Ghost = ghost;
}
}
16 changes: 16 additions & 0 deletions Content.Server/Spawners/Components/EntityTableSpawnerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,21 @@ public sealed partial class EntityTableSpawnerComponent : Component
/// </summary>
[DataField]
public bool DeleteSpawnerAfterSpawn = true;

// SSS - New fields

/// <summary>
/// How many times the item will spawn.
/// </summary>
[DataField]
public int Count = 1;

/// <summary>
/// How delayed the item will spawn in.
/// </summary>
[DataField]
public TimeSpan Delay = TimeSpan.Zero;

// SSS - End of new fields
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.Spawners.EntitySystems
{
Expand Down Expand Up @@ -135,7 +136,17 @@ private void Spawn(Entity<EntityTableSpawnerComponent> ent)
var yOffset = _robustRandom.NextFloat(-ent.Comp.Offset, ent.Comp.Offset);
var trueCoords = coords.Offset(new Vector2(xOffset, yOffset));

Spawn(proto, trueCoords);
for (var i = 0; i < ent.Comp.Count; i++)
{
if (ent.Comp.Delay != TimeSpan.Zero)
{
Timer.Spawn(ent.Comp.Delay, () => Spawn(proto, trueCoords));
}
else
{
Spawn(proto, trueCoords);
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Content.Server._SSS.GridMarker;

[RegisterComponent]
public sealed partial class SuspicionGridMarkerComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Content.Server._SSS;

/// <summary>
/// Main component that marks a player "active" in a round of SSS.
/// </summary>
[RegisterComponent]
public sealed partial class SuspicionPlayerComponent : Component
{
/// <summary>
/// If true, the examine window will show the dead person's role.
/// </summary>
[ViewVariables]
public bool Revealed = false;

/// <summary>
/// How many units (probably meters? idk what this game uses) a person must be from the station to be considered "off-station" and start taking damage.
/// </summary>
[ViewVariables]
public float SpacewalkThreshold = 15;

[ViewVariables]
public DateTime LastTookSpacewalkDamage = DateTime.Now;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Content.Server._SSS.SuspicionGameRule.Components;

[RegisterComponent]
public sealed partial class SuspicionRadarComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Shared.Roles;

namespace Content.Server._SSS.SuspicionGameRule.Components;

[RegisterComponent]
public sealed partial class SuspicionRoleComponent : BaseMindRoleComponent
{
[ViewVariables]
public SuspicionRole Role { get; set; } = SuspicionRole.Pending;
}

public static class SusRoleExtensions
{
public static string GetRoleColor(this SuspicionRole role)
{
return role switch
{
SuspicionRole.Traitor => "red",
SuspicionRole.Detective => "blue",
SuspicionRole.Innocent => "green",
_ => "white",
};
}
}

public enum SuspicionRole
{
Pending,

Traitor,
Detective,
Innocent,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Content.Shared.NPC.Prototypes;
using Content.Shared.Radio;
using Content.Shared.Roles;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server._SSS.SuspicionGameRule.Components;

[RegisterComponent, Access(typeof(SuspicionRuleSystem))]
public sealed partial class SuspicionRuleComponent : Component
{
#region State management

public SuspicionGameState GameState = SuspicionGameState.Preparing;

/// <summary>
/// Defines when the round will end.
/// </summary>
public TimeSpan EndAt = TimeSpan.MinValue;

public List<int> AnnouncedTimeLeft = new List<int>();

#endregion

/// <summary>
/// Percentage of total players that will be a traitor.
/// The number of players will be multiplied by this number, and then rounded down.
/// If the result is less than 1 or more than the player count, it is clamped to those values.
/// </summary>
[DataField]
public float TraitorPercentage = 0.25f;

/// <summary>
/// Percentage of total players that will be a detective (detective innocent). Handled similar to traitor percentage (rounded down etc).
/// </summary>
[DataField]
public float DetectivePercentage = 0.20f;

/// <summary>
/// How long to wait before the game starts after the round starts.
/// </summary>
[DataField]
public int PreparingDuration = 30;

/// <summary>
/// How long the round lasts in seconds.
/// </summary>
[DataField]
public int RoundDuration = 480;

/// <summary>
/// How long to add to the round time when a player is killed.
/// </summary>
[DataField]
public int TimeAddedPerKill = 30;

/// <summary>
/// How long to wait before restarting the round after the summary is displayed.
/// </summary>
[DataField]
public int PostRoundDuration = 30;

/// <summary>
/// The gear all players spawn with.
/// </summary>
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<StartingGearPrototype>))]
public string Gear = "SuspicionGear";

[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string UplinkImplant = "SusTraitorUplinkImplant";

[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string DetectiveImplant = "SusDetectiveUplinkImplant";


[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
public string TraitorRadio = "Syndicate";

[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<NpcFactionPrototype>))]
public string TraitorFaction = "Syndicate";

/// <summary>
/// How much TC to give to traitors/detectives for their performance
/// </summary>
[DataField]
public int AmountAddedPerKill = 1;

}

public enum SuspicionGameState
{
/// <summary>
/// The game is preparing to start. No roles have been assigned yet and new joining players will be spawned in.
/// </summary>
Preparing,

/// <summary>
/// The game is in progress. Roles have been assigned and players are hopefully killing each other. New joining players will be forced to spectate.
/// </summary>
InProgress,

/// <summary>
/// The game has ended. The summary is being displayed and players are waiting for the round to restart.
/// </summary>
PostRound
}
Loading

0 comments on commit cc1af44

Please sign in to comment.