| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- using Content.Server.Administration.Logs;
- using Content.Server.Antag;
- using Content.Server.GameTicking.Rules.Components;
- using Content.Server.Mind;
- using Content.Server.Objectives;
- using Content.Server.PDA.Ringer;
- using Content.Server.Roles;
- using Content.Server.Traitor.Uplink;
- using Content.Shared.Database;
- using Content.Shared.FixedPoint;
- using Content.Shared.GameTicking.Components;
- using Content.Shared.Mind;
- using Content.Shared.NPC.Systems;
- using Content.Shared.PDA;
- using Content.Shared.Random.Helpers;
- using Content.Shared.Roles;
- using Content.Shared.Roles.Jobs;
- using Content.Shared.Roles.RoleCodeword;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Random;
- using System.Linq;
- using System.Text;
- namespace Content.Server.GameTicking.Rules;
- public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
- {
- private static readonly Color TraitorCodewordColor = Color.FromHex("#cc3b3b");
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly AntagSelectionSystem _antag = default!;
- [Dependency] private readonly SharedJobSystem _jobs = default!;
- [Dependency] private readonly MindSystem _mindSystem = default!;
- [Dependency] private readonly NpcFactionSystem _npcFaction = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly SharedRoleCodewordSystem _roleCodewordSystem = default!;
- [Dependency] private readonly SharedRoleSystem _roleSystem = default!;
- [Dependency] private readonly UplinkSystem _uplink = default!;
- public override void Initialize()
- {
- base.Initialize();
- Log.Level = LogLevel.Debug;
- SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
- SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
- }
- protected override void Added(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
- {
- base.Added(uid, component, gameRule, args);
- SetCodewords(component, args.RuleEntity);
- }
- private void AfterEntitySelected(Entity<TraitorRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
- {
- Log.Debug($"AfterAntagEntitySelected {ToPrettyString(ent)}");
- MakeTraitor(args.EntityUid, ent);
- }
- private void SetCodewords(TraitorRuleComponent component, EntityUid ruleEntity)
- {
- component.Codewords = GenerateTraitorCodewords(component);
- _adminLogger.Add(LogType.EventStarted, LogImpact.Low, $"Codewords generated for game rule {ToPrettyString(ruleEntity)}: {string.Join(", ", component.Codewords)}");
- }
- public string[] GenerateTraitorCodewords(TraitorRuleComponent component)
- {
- var adjectives = _prototypeManager.Index(component.CodewordAdjectives).Values;
- var verbs = _prototypeManager.Index(component.CodewordVerbs).Values;
- var codewordPool = adjectives.Concat(verbs).ToList();
- var finalCodewordCount = Math.Min(component.CodewordCount, codewordPool.Count);
- string[] codewords = new string[finalCodewordCount];
- for (var i = 0; i < finalCodewordCount; i++)
- {
- codewords[i] = Loc.GetString(_random.PickAndTake(codewordPool));
- }
- return codewords;
- }
- public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - start");
- //Grab the mind if it wasn't provided
- if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - failed, no Mind found");
- return false;
- }
- var briefing = "";
- if (component.GiveCodewords)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - added codewords flufftext to briefing");
- briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords)));
- }
- var issuer = _random.Pick(_prototypeManager.Index(component.ObjectiveIssuers));
- // Uplink code will go here if applicable, but we still need the variable if there aren't any
- Note[]? code = null;
- if (component.GiveUplink)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink start");
- // Calculate the amount of currency on the uplink.
- var startingBalance = component.StartingBalance;
- if (_jobs.MindTryGetJob(mindId, out var prototype))
- {
- if (startingBalance < prototype.AntagAdvantage) // Can't use Math functions on FixedPoint2
- startingBalance = 0;
- else
- startingBalance = startingBalance - prototype.AntagAdvantage;
- }
- // Choose and generate an Uplink, and return the uplink code if applicable
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink request start");
- var uplinkParams = RequestUplink(traitor, startingBalance, briefing);
- code = uplinkParams.Item1;
- briefing = uplinkParams.Item2;
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink request completed");
- }
- string[]? codewords = null;
- if (component.GiveCodewords)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - set codewords from component");
- codewords = component.Codewords;
- }
- if (component.GiveBriefing)
- {
- _antag.SendBriefing(traitor, GenerateBriefing(codewords, code, issuer), null, null);
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Sent the Briefing");
- }
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Adding TraitorMind");
- component.TraitorMinds.Add(mindId);
- // Assign briefing
- //Since this provides neither an antag/job prototype, nor antag status/roletype,
- //and is intrinsically related to the traitor role
- //it does not need to be a separate Mind Role Entity
- _roleSystem.MindHasRole<TraitorRoleComponent>(mindId, out var traitorRole);
- if (traitorRole is not null)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Add traitor briefing components");
- AddComp<RoleBriefingComponent>(traitorRole.Value.Owner);
- Comp<RoleBriefingComponent>(traitorRole.Value.Owner).Briefing = briefing;
- }
- else
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - did not get traitor briefing");
- }
- // Send codewords to only the traitor client
- var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found
- RoleCodewordComponent codewordComp = EnsureComp<RoleCodewordComponent>(mindId);
- _roleCodewordSystem.SetRoleCodewords(codewordComp, "traitor", component.Codewords.ToList(), color);
- // Change the faction
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Change faction");
- _npcFaction.RemoveFaction(traitor, component.NanoTrasenFaction, false);
- _npcFaction.AddFaction(traitor, component.SyndicateFaction);
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Finished");
- return true;
- }
- private (Note[]?, string) RequestUplink(EntityUid traitor, FixedPoint2 startingBalance, string briefing)
- {
- var pda = _uplink.FindUplinkTarget(traitor);
- Note[]? code = null;
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink add");
- var uplinked = _uplink.AddUplink(traitor, startingBalance, pda, true);
- if (pda is not null && uplinked)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is PDA");
- // Codes are only generated if the uplink is a PDA
- code = EnsureComp<RingerUplinkComponent>(pda.Value).Code;
- // If giveUplink is false the uplink code part is omitted
- briefing = string.Format("{0}\n{1}",
- briefing,
- Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#"))));
- return (code, briefing);
- }
- else if (pda is null && uplinked)
- {
- Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is implant");
- briefing += "\n" + Loc.GetString("traitor-role-uplink-implant-short");
- }
- return (null, briefing);
- }
- // TODO: AntagCodewordsComponent
- private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args)
- {
- if (comp.GiveCodewords)
- args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
- }
- // TODO: figure out how to handle this? add priority to briefing event?
- private string GenerateBriefing(string[]? codewords, Note[]? uplinkCode, string? objectiveIssuer = null)
- {
- var sb = new StringBuilder();
- sb.AppendLine(Loc.GetString("traitor-role-greeting", ("corporation", objectiveIssuer ?? Loc.GetString("objective-issuer-unknown"))));
- if (codewords != null)
- sb.AppendLine(Loc.GetString("traitor-role-codewords", ("codewords", string.Join(", ", codewords))));
- if (uplinkCode != null)
- sb.AppendLine(Loc.GetString("traitor-role-uplink-code", ("code", string.Join("-", uplinkCode).Replace("sharp", "#"))));
- else
- sb.AppendLine(Loc.GetString("traitor-role-uplink-implant"));
- return sb.ToString();
- }
- public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind)
- {
- List<(EntityUid Id, MindComponent Mind)> allTraitors = new();
- var query = EntityQueryEnumerator<TraitorRuleComponent>();
- while (query.MoveNext(out var uid, out var traitor))
- {
- foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, (uid, traitor)))
- {
- if (!allTraitors.Contains(role))
- allTraitors.Add(role);
- }
- }
- return allTraitors;
- }
- private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, Entity<TraitorRuleComponent> rule)
- {
- var traitors = new List<(EntityUid Id, MindComponent Mind)>();
- foreach (var mind in _antag.GetAntagMinds(rule.Owner))
- {
- if (mind.Comp == ourMind)
- continue;
- traitors.Add((mind, mind));
- }
- return traitors;
- }
- }
|