Skip to content
This repository was archived by the owner on Aug 22, 2025. It is now read-only.
Open
78 changes: 78 additions & 0 deletions Content.Server/Points/PointSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Server._Miracle.GameRules;
using Content.Shared.FixedPoint;
using Content.Shared.Points;
using JetBrains.Annotations;
Expand Down Expand Up @@ -86,4 +87,81 @@ public override FormattedMessage GetScoreboard(EntityUid uid, PointManagerCompon

return msg;
}

// WD EDIT START. Violence gamemode team points

/// <summary>
/// Adds the specified point value to a player.
/// </summary>
[PublicAPI]
public void AdjustTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component)
{
if (!Resolve(uid, ref component))
return;

if (!component.TeamPoints.TryGetValue(team, out var current))
current = FixedPoint2.Zero;

SetTeamPointValue(team, current + value, uid, component);
}

/// <summary>
/// Sets the amount of points for a player
/// </summary>
[PublicAPI]
public void SetTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component)
{
if (!Resolve(uid, ref component))
return;

if (component.TeamPoints.TryGetValue(team, out var current) && current == value)
return;

component.TeamPoints[team] = value;
component.TeamScoreboard = GetTeamScoreboard(uid, component);
Dirty(uid, component);

var ev = new TeamPointChangedEvent(team, value);
RaiseLocalEvent(uid, ref ev, true);
}

/// <summary>
/// Gets the amount of points for a given player
/// </summary>
[PublicAPI]
public FixedPoint2 GetTeamPointValue(ushort team, EntityUid uid, PointManagerComponent? component)
{
if (!Resolve(uid, ref component))
return FixedPoint2.Zero;

return component.TeamPoints.TryGetValue(team, out var value)
? value
: FixedPoint2.Zero;
}

Comment on lines +90 to +141
Copy link

@coderabbitai coderabbitai bot Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to avoid code duplication in team point management methods.

The methods AdjustTeamPointValue, SetTeamPointValue, and GetTeamPointValue share similar logic with their player counterparts. Consider abstracting common functionalities into helper methods to reduce duplication and improve maintainability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which counterparts?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhailrake, the counterparts to the new team point management methods (AdjustTeamPointValue, SetTeamPointValue, GetTeamPointValue) in the PointSystem class are the methods that manage individual player points. These include:

  • AdjustPointValue: Adds a specified point value to a player's total points.
  • SetPointValue: Sets the total point value for a player.
  • GetPointValue: Retrieves the total point value for a player.

These methods are similar in functionality to the new team methods but operate on individual player points rather than team points. The logic for adjusting, setting, and getting points is quite similar between these sets of methods, which is why refactoring to reduce code duplication was suggested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest your version then

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhailrake, here's a suggested refactor for the PointSystem class to reduce code duplication between the team and individual player point management methods. I've introduced helper methods to encapsulate common functionalities:

public sealed class PointSystem : SharedPointSystem
{
    // Helper methods to abstract common functionalities
    private bool TryResolveComponents(EntityUid uid, out PointManagerComponent? component, EntityUid? user = null, ActorComponent? actor = null)
    {
        return Resolve(uid, ref component) && (user == null || Resolve(user.Value, ref actor, false));
    }

    private void UpdatePoints<T>(Dictionary<T, FixedPoint2> points, T key, FixedPoint2 value, EntityUid uid, PointManagerComponent? component)
    {
        if (!points.TryGetValue(key, out var current) || current != value)
        {
            points[key] = value;
            Dirty(uid, component);
        }
    }

    private void RaisePointChangeEvent<T>(T key, FixedPoint2 value, EntityUid uid)
    {
        var ev = new PointChangedEvent<T>(key, value);
        RaiseLocalEvent(uid, ref ev, true);
    }

    // Refactored team point management methods
    public void AdjustTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component)
    {
        if (!TryResolveComponents(uid, out component)) return;
        UpdatePoints(component.TeamPoints, team, component.TeamPoints.TryGetValue(team, out var current) ? current + value : value, uid, component);
        RaisePointChangeEvent(team, component.TeamPoints[team], uid);
    }

    public void SetTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component)
    {
        if (!TryResolveComponents(uid, out component)) return;
        UpdatePoints(component.TeamPoints, team, value, uid, component);
        RaisePointChangeEvent(team, value, uid);
    }

    public FixedPoint2 GetTeamPointValue(ushort team, EntityUid uid, PointManagerComponent? component)
    {
        return TryResolveComponents(uid, out component) && component.TeamPoints.TryGetValue(team, out var value) ? value : FixedPoint2.Zero;
    }

    // Example of refactoring individual point methods similarly
    public void AdjustPointValue(EntityUid user, FixedPoint2 value, EntityUid uid, PointManagerComponent? component, ActorComponent? actor = null)
    {
        if (!TryResolveComponents(uid, out component, user, actor)) return;
        var userId = actor.PlayerSession.UserId;
        UpdatePoints(component.Points, userId, component.Points.TryGetValue(userId, out var current) ? current + value : value, uid, component);
        RaisePointChangeEvent(userId, component.Points[userId], uid);
    }
}

This refactoring introduces three helper methods: TryResolveComponents, UpdatePoints, and RaisePointChangeEvent, which are used in both the team and individual point management methods to handle common operations such as component resolution, point updating, and event raising. This reduces duplication and enhances maintainability.

// Ignore this method, I will finish it later myself.
public override FormattedMessage GetTeamScoreboard(EntityUid uid, PointManagerComponent? component = null)
{
var msg = new FormattedMessage();

if (!Resolve(uid, ref component))
return msg;

var orderedPlayers = component.Points.OrderByDescending(p => p.Value).ToList();
var place = 1;
foreach (var (id, points) in orderedPlayers)
{
if (!_player.TryGetPlayerData(id, out var data))
continue;

msg.AddMarkup(Loc.GetString("point-scoreboard-list",
("place", place),
("name", data.UserName),
("points", points.Int())));
msg.PushNewline();
place++;
}

return msg;
}
}
4 changes: 2 additions & 2 deletions Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ private void OnRoundRestart(RoundRestartCleanupEvent _)

/// <summary>
/// Used internally to ensure a paused map that is
/// stores polymorphed entities.
/// stores polymorphed entities. WD EDIT to make it public
/// </summary>
private void EnsurePausedMap()
public void EnsurePausedMap()
{
if (PausedMap != null && Exists(PausedMap))
return;
Expand Down
7 changes: 7 additions & 0 deletions Content.Server/Spawners/Components/SpawnPointComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public sealed partial class SpawnPointComponent : Component
[DataField("spawn_type")]
public SpawnPointType SpawnType { get; private set; } = SpawnPointType.Unset;

/// <summary>
/// WD EDIT. This is needed for Violence gamemode for team spawners.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("teamId")]
public ushort? TeamId = null;

public JobPrototype? Job => string.IsNullOrEmpty(_jobId) ? null : _prototypeManager.Index<JobPrototype>(_jobId);

public override string ToString()
Expand Down
6 changes: 4 additions & 2 deletions Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ private void OnPlayerSpawning(PlayerSpawningEvent args)

if (_gameTicker.RunLevel == GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.LateJoin)
{
possiblePositions.Add(xform.Coordinates);
if (args.Team == spawnPoint.TeamId) // WD EDIT. Violence gamemode spawns needed here
possiblePositions.Add(xform.Coordinates);
}

if (_gameTicker.RunLevel != GameRunLevel.InRound &&
spawnPoint.SpawnType == SpawnPointType.Job &&
(args.Job == null || spawnPoint.Job?.ID == args.Job.Prototype))
{
possiblePositions.Add(xform.Coordinates);
if (args.Team == spawnPoint.TeamId) // WD EDIT.
possiblePositions.Add(xform.Coordinates);
}
}

Expand Down
16 changes: 12 additions & 4 deletions Content.Server/Station/Systems/StationSpawningSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,24 @@ public override void Initialize()
}

/// <summary>
/// Attempts to spawn a player character onto the given station.
/// Attempts to spawn a player character onto the given station. WD EDIT
/// </summary>
/// <param name="station">Station to spawn onto.</param>
/// <param name="job">The job to assign, if any.</param>
/// <param name="profile">The character profile to use, if any.</param>
/// <param name="stationSpawning">Resolve pattern, the station spawning component for the station.</param>
/// <param name="team">Player's team for Violence GameRule. WD EDIT</param>;
/// <returns>The resulting player character, if any.</returns>
/// <exception cref="ArgumentException">Thrown when the given station is not a station.</exception>
/// <remarks>
/// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable.
/// </remarks>
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null)
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null, ushort? team = null)
{
if (station != null && !Resolve(station.Value, ref stationSpawning))
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));

var ev = new PlayerSpawningEvent(job, profile, station);
var ev = new PlayerSpawningEvent(job, profile, station, team);

if (station != null && profile != null)
{
Expand Down Expand Up @@ -326,10 +327,17 @@ public sealed class PlayerSpawningEvent : EntityEventArgs
/// </summary>
public readonly EntityUid? Station;

public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station)
/// <summary>
/// Player's team for Violence GameRule. WD EDIT
/// </summary>
public readonly ushort? Team;


public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station, ushort? team)
{
Job = job;
HumanoidCharacterProfile = humanoidCharacterProfile;
Station = station;
Team = team; //WD EDIT
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Content.Server._Miracle.Components;

/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class ViolenceParticipatorComponent : Component
{
/// <summary>
/// List of factions in this gamemode.
/// </summary>
[DataField]
public EntityUid? MatchUid { get; private set; } = null;
}
46 changes: 46 additions & 0 deletions Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Shared.Console;
using Robust.Shared.Network;

// TODO: test permissions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Ensure proper permissions are tested for the SwitchTeamCommand.

Would you like me to help implement the permission checks or create a test case for this command?

namespace Content.Server._Miracle.GameRules.Violence;

[AdminCommand(AdminFlags.Admin)]
internal class SwitchTeamCommand : IConsoleCommand
{
[Dependency] private readonly IPlayerLocator _locator = default!;

public string Command => "switchteam";
public string Description => "Switches the player's team.";
public string Help => "switchteam <playerId> <newTeamId>";

public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteLine("Expected exactly 2 arguments.");
return;
}

var target = args[0];
var located = await _locator.LookupIdByNameOrIdAsync(target);
var player = shell.Player;

if (player == null)
{
shell.WriteLine("Player not found.");
return;
}

if (!ushort.TryParse(args[1], out var newTeamId))
{
shell.WriteLine($"Invalid team ID: {args[1]}");
return;
}

var violenceRuleSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ViolenceRuleSystem>();
violenceRuleSystem.SwitchTeam(player.UserId, newTeamId);
}
}

Loading