Delta-v/Content.Server/_Harmony/GameTicking/Rules/ConspiratorRuleSystem.cs

322 lines
14 KiB
C#

using Content.Server._Harmony.GameTicking.Rules.Components;
using Content.Server.Antag;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.Roles;
using Content.Server.Administration.Logs;
using Content.Server.Voting.Managers;
using Content.Server.Voting;
using Content.Server.Objectives;
using Content.Server.Polymorph.Components;
using Content.Shared._Harmony.Conspirators.Components;
using Content.Shared._Harmony.Roles.Components;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mind;
using Content.Shared.IdentityManagement;
using Content.Shared.Random.Helpers;
using Content.Shared._DV.CCVars;
using Content.Shared.Database;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Robust.Shared.Timing;
using Robust.Shared.Enums;
using Robust.Shared.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server._Harmony.GameTicking.Rules;
public sealed class ConspiratorRuleSystem : GameRuleSystem<ConspiratorRuleComponent>
{
// [Dependency] private readonly AntagSelectionSystem _antag = default!; // Delta V - Never used
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IVoteManager _votes = default!;
private TimeSpan _objectiveVoteTimer = default!;
private TimeSpan _objectiveVoteDelay = default!;
private TimeSpan _leaderVoteTimer = default!;
private TimeSpan _leaderVoteDelay = default!;
private readonly SoundSpecifier _conspiratorBriefing = new SoundPathSpecifier("/Audio/_Harmony/Misc/conspirator_greeting.ogg");
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ConspiratorRoleComponent, GetBriefingEvent>(OnGetBriefing);
SubscribeLocalEvent<ConspiratorRuleComponent, AfterAntagEntitySelectedEvent>(OnAntagSelected);
// deltav additions, conspirators v2
Subs.CVar(_config,
DCCVars.ConspiratorObjectiveVoteTimer,
value => _objectiveVoteTimer = TimeSpan.FromSeconds(value),
true);
Subs.CVar(_config,
DCCVars.ConspiratorObjectiveVoteDelayTimer,
value => _objectiveVoteDelay = TimeSpan.FromSeconds(value),
true);
Subs.CVar(_config,
DCCVars.ConspiratorLeaderVoteTimer,
value => _leaderVoteTimer = TimeSpan.FromSeconds(value),
true);
Subs.CVar(_config,
DCCVars.ConspiratorLeaderVoteDelayTimer,
value => _leaderVoteDelay = TimeSpan.FromSeconds(value),
true);
// deltav additions, conspirators v2
}
/* DeltaV - removed custom round end text in favor of individually displayed objective summaries
protected override void AppendRoundEndText(EntityUid uid, ConspiratorRuleComponent component,GameRuleComponent gameRule,ref RoundEndTextAppendEvent args)
{
base.AppendRoundEndText(uid, component, gameRule, ref args);
var sessionData = _antag.GetAntagIdentifiers(uid);
args.AddLine(Loc.GetString("conspirator-count", ("count", sessionData.Count)));
foreach (var (_, data, name) in sessionData)
{
args.AddLine(Loc.GetString("conspirator-name-user",
("name", name),
("username", data.UserName)));
}
if (!_proto.TryIndex(component.Objective, out var objectiveProto))
return;
args.AddLine(Loc.GetString("conspirator-objective", ("objective", objectiveProto.Name)));
}
*/
private void OnGetBriefing(Entity<ConspiratorRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("conspirator-identities"));
var conspirators = AllEntityQuery<ConspiratorComponent>();
while (conspirators.MoveNext(out var id, out _))
{
args.Append(Loc.GetString("conspirator-name", ("name", Name(id))));
}
args.Append(Loc.GetString("conspirator-radio-implant"));
}
private void OnAntagSelected(Entity<ConspiratorRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
if (!_mind.TryGetMind(args.Session, out var mindId, out var mind))
return;
/*
if (ent.Comp.Objective is null){
if (GetRandomObjectivePrototype(ent.Comp, out var objectiveProtoId))
ent.Comp.Objective = objectiveProtoId;
}
*/
if (ent.Comp.Objective is not null)
_mind.TryAddObjective(mindId, mind, ent.Comp.Objective);
}
/* deltaV conspirators v2, now uneeded so.
private bool GetRandomObjectivePrototype(ConspiratorRuleComponent comp, [NotNullWhen(true)] out EntProtoId? objectiveProto)
{
objectiveProto = null;
if (!_proto.TryIndex(comp.ObjectiveGroup, out var group))
return false;
var objectives = group.Weights.ShallowClone();
while (_random.TryPickAndTake(objectives, out var proto))
{
objectiveProto = proto!;
return true;
}
return false;
}
*/
//delta V addition - conspirators vote system. i am too scared to make generic systems so woe, copy paste code be upon ye
protected override void Started(EntityUid uid, ConspiratorRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
component.ConspiratorLeaderVoteTimer = _timing.CurTime + _leaderVoteDelay;
component.ConspiratorObjectiveVoteTimer = _timing.CurTime + _objectiveVoteDelay;
}
protected override void ActiveTick(EntityUid uid, ConspiratorRuleComponent component, GameRuleComponent gameRule, float frameTime)
{
if (component.ConspiratorLeaderVoteTimer is { } _objectiveVoteTimer && _timing.CurTime >= _objectiveVoteTimer)
{
component.ConspiratorLeaderVoteTimer = null;
ConspiratorObjectiveVote(component);
} else if (component.ConspiratorObjectiveVoteTimer is { } _leaderVoteTimer && _timing.CurTime >= _leaderVoteTimer)
{
component.ConspiratorObjectiveVoteTimer = null;
ConspiratorLeaderVote();
}
}
private void ConspiratorLeaderVote()
{
// If there's already an entity with ConspiratorLeader, don't hold a vote. This allows admins to add the conspirators rule a 2nd time
// in the case that there is only one cultist and they've been chosen as the leader already.
if (EntityQuery<ConspiratorLeaderComponent>().Any())
{
_adminLogger.Add(LogType.Vote, LogImpact.Medium,
$"conspirator leader already exists. Cancelling leader vote.");
return;
}
var conspirators = new List<(string, EntityUid)>();
var conspiratorsQuery = EntityQueryEnumerator<ConspiratorComponent, MetaDataComponent>();
while (conspiratorsQuery.MoveNext(out var conspirator, out _, out var metadata))
{
var playerInfo = metadata.EntityName;
if (TryComp<PolymorphedEntityComponent>(conspirator, out var polyComp) && polyComp.Parent.HasValue) // If the cultist is polymorphed, we use the original entity instead and hope that they'll polymorph back eventually
conspirators.Add((playerInfo, polyComp.Parent.Value));
else
conspirators.Add((playerInfo, conspirator));
}
var options = new VoteOptions
{
Title = Loc.GetString("conspirator-vote-leader-title"),
InitiatorText = Loc.GetString("conspirators-vote-leader-initiator"),
Duration = _leaderVoteTimer,
VoterEligibility = VoteManager.VoterEligibility.Conspirators
};
// If there are no conspirators, don't hold a vote, or the server will crash.
if (conspirators.Count == 0)
{
Log.Warning($"There are no conspirators present for the leader vote. Voting is cancelled to prevent the server crashing.");
_adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"There are no conspirators for the leader vote. Leader vote is cancelled to prevent the server crashing.");
return;
}
foreach (var (name, ent) in conspirators)
{
options.Options.Add((Loc.GetString(name), ent));
}
// If somehow there are conspirators but no options, still don't hold a vote.
// Holding a vote with zero options crashes the server.
if (options.Options.Count == 0)
{
Log.Warning($"There are {conspirators.Count} conspirators but no options for the leader vote. Voting is cancelled to prevent the server crashing.");
_adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"There are {conspirators.Count} conspirators but no options for the leader vote. Steward vote is cancelled to prevent the server crashing.");
return;
}
var vote = _votes.CreateVote(options);
vote.OnFinished += (_, args) =>
{
EntityUid picked;
if (args.Winner == null)
{
picked = (EntityUid)_random.Pick(args.Winners);
}
else
{
picked = (EntityUid)args.Winner;
}
//changing the icon of the head conspirator
EnsureComp<ConspiratorLeaderComponent>(picked);
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"conspirators leadership vote finished: {Identity.Entity(picked, EntityManager)} is now leader.");
};
}
//summary -> voting system for the conspirators objective
private void ConspiratorObjectiveVote(ConspiratorRuleComponent component)
{
//getting all conspirators and checking if they already have a objective
var conspirators = new List<EntityUid>();
var conspiratorsQuery = EntityQueryEnumerator<ConspiratorComponent>();
while (conspiratorsQuery.MoveNext(out var conspirator, out _))
{
if (TryComp<PolymorphedEntityComponent>(conspirator, out var polyComp) && polyComp.Parent.HasValue) // If the cultist is polymorphed, we use the original entity instead and hope that they'll polymorph back eventually
conspirators.Add((polyComp.Parent.Value));
else
conspirators.Add((conspirator));
}
// If there are no conspirators, don't hold a vote, or the server will crash.
if (conspirators.Count == 0)
{
Log.Warning($"There are no conspirators present for the objective vote. Voting is cancelled");
_adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"There are no conspirators for the objective vote. objective vote is cancelled");
return;
}
var options = new VoteOptions
{
Title = Loc.GetString("conspirator-vote-objective-title"),
InitiatorText = Loc.GetString("conspirators-vote-leader-initiator"),
Duration = _objectiveVoteTimer,
VoterEligibility = VoteManager.VoterEligibility.Conspirators
};
//dumb array go! from here use it to add the options, will have to add the new objectives to this.
int ObjectiveArrayNumber = 0;
string[] ConspiratorObjectiveIds = ["ConspiratorBusinessObjective","ConspiratorUsurpObjective","ConspiratorHordeObjective","ConspiratorDistrustObjective","ConspiratorVigilanteObjective","ConspiratorFreedomObjective","ConspiratorNukeObjective","ConspiratorDangerObjective","ConspiratorFreeObjective"];
string[] ConspiratorObjectiveNames = ["Set up a business outside Nanotrasen.","Become the true leaders of the station.","Build a horde of valuables.","Brew distrust and hatred.","Enforce the laws secuirty can not.","Free the station of access.","Steal the nuke disk","Arm yourselves","Make your own conspiracy."];
foreach (string objective in ConspiratorObjectiveNames)
{
options.Options.Add((Loc.GetString(objective),ConspiratorObjectiveIds[ObjectiveArrayNumber]));
ObjectiveArrayNumber++;
}
// If somehow there are cultists but no options, still don't hold a vote.
// Holding a vote with zero options crashes the server.
if (options.Options.Count == 0)
{
Log.Warning($"There are {conspirators.Count} conspirators but no options for the leader vote. Voting is cancelled to prevent the server crashing.");
_adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"There are {conspirators.Count} conspirators but no options for the leader vote. Steward vote is cancelled to prevent the server crashing.");
return;
}
var vote = _votes.CreateVote(options);
vote.OnFinished += (_, args) =>
{
string picked;
if (args.Winner == null)
{
picked = (string)_random.Pick(args.Winners);
}
else
{
picked = (string)args.Winner;
}
//add an objective for each member of the conspiracy, skip if it cant get them
foreach (EntityUid ent in conspirators){
_mind.TryGetMind(ent, out var mindId, out var mind);
if (mind == null){
continue;
}
_mind.TryAddObjective(mindId, mind, picked);
}
component.Objective = picked;
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"conspirators objective vote finished: {picked} is the objective.");
};
}
// deltav additions, conspirators v2
}