Антаг без ПДА - фикс от визденов (с костылями) (#788)

* Апстрима будет

* Also this
This commit is contained in:
BIGZi0348
2024-11-22 22:21:34 +03:00
committed by GitHub
parent 165838f5ef
commit 04e165c804
12 changed files with 259 additions and 133 deletions

View File

@@ -1,5 +1,6 @@
using Content.Shared._White.Mood;
using Content.Shared.Dataset;
using Content.Shared.FixedPoint;
using Content.Shared.NPC.Prototypes;
using Content.Shared.Random;
using Content.Shared.Roles;
@@ -34,7 +35,25 @@ public sealed partial class TraitorRuleComponent : Component
[DataField]
public ProtoId<DatasetPrototype> ObjectiveIssuers = "TraitorCorporations";
// WD EDIT START AHEAD OF WIZDEN UPSTREAM
/// <summary>
/// Give this traitor an Uplink on spawn.
/// </summary>
[DataField]
public bool GiveUplink = true;
/// <summary>
/// Give this traitor the codewords.
/// </summary>
[DataField]
public bool GiveCodewords = true;
/// <summary>
/// Give this traitor a briefing in chat.
/// </summary>
[DataField]
public bool GiveBriefing = true;
// WD EDIT END AHEAD OF WIZDEN UPSTREAM
// WD edit start
[DataField]
public ProtoId<MoodEffectPrototype> MoodBuffEffect = "TraitorFocused";
@@ -77,7 +96,7 @@ public sealed partial class TraitorRuleComponent : Component
/// The amount of TC traitors start with.
/// </summary>
[DataField]
public int StartingBalance = 20;
public FixedPoint2 StartingBalance = 20; // WD EDIT AHEAD OF WIZDEN UPSTREAM
[DataField]
public int MaxDifficulty = 5;

View File

@@ -5,6 +5,7 @@ using Content.Server.Objectives;
using Content.Server.PDA.Ringer;
using Content.Server.Roles;
using Content.Server.Traitor.Uplink;
using Content.Shared.FixedPoint;
using Content.Shared.Mind;
using Content.Shared.NPC.Systems;
using Content.Shared.Objectives.Components;
@@ -74,7 +75,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
}
}
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true)
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveObjectives = true) // WD EDIT AHEAD OF WIZDEN UPSTREAM
{
//Grab the mind if it wasnt provided
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
@@ -83,39 +84,46 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
// WD START
var richAspect = _gameTicker.GetActiveGameRules().Where(HasComp<TraitorRichAspectComponent>).Any();
// WD END
// WD EDIT START AHEAD OF WIZDEN UPSTREAM
var briefing = "";
var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords)));
if (component.GiveCodewords)
briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords)));
// WD EDIT END AHEAD OF WIZDEN UPSTREAM
var issuer = _random.Pick(_prototypeManager.Index(component.ObjectiveIssuers).Values);
Note[]? code = null;
if (giveUplink)
if (component.GiveUplink) // WD EDIT AHEAD OF WIZDEN UPSTREAM
{
// Calculate the amount of currency on the uplink.
var startingBalance = component.StartingBalance;
if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0);
// WD EDIT START START AHEAD OF WIZDEN UPSTREAM
{
if (startingBalance < prototype.AntagAdvantage) // Can't use Math functions on FixedPoint2
startingBalance = 0;
else
startingBalance = startingBalance - prototype.AntagAdvantage;
}
if (richAspect)
{
startingBalance += 10;
}
// creadth: we need to create uplink for the antag.
// PDA should be in place already
var pda = _uplink.FindUplinkTarget(traitor);
if (pda == null || !_uplink.AddUplink(traitor, startingBalance))
return false;
// Give traitors their codewords and uplink code to keep in their character info menu
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", "#"))));
// Choose and generate an Uplink, and return the uplink code if applicable
var uplinkParams = RequestUplink(traitor, startingBalance, briefing);
code = uplinkParams.Item1;
briefing = uplinkParams.Item2;
}
_antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code, issuer), null, component.GreetSoundNotification);
string[]? codewords = null;
if (component.GiveCodewords)
codewords = component.Codewords;
if (component.GiveBriefing)
_antag.SendBriefing(traitor, GenerateBriefing(codewords, code, issuer), null, component.GreetSoundNotification);
// WD EDIT END AHEAD OF WIZDEN UPSTREAM
component.TraitorMinds.Add(mindId);
// Assign briefing
@@ -146,7 +154,33 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
return true;
}
// WD EDIT START AHEAD OF WIZDEN UPSTREAM
private (Note[]?, string) RequestUplink(EntityUid traitor, FixedPoint2 startingBalance, string briefing)
{
var pda = _uplink.FindUplinkTarget(traitor);
Note[]? code = null;
var uplinked = _uplink.AddUplink(traitor, startingBalance, pda, true);
if (pda is not null && uplinked)
{
// 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)
{
briefing += "\n" + Loc.GetString("traitor-role-uplink-implant-short");
}
return (null, briefing);
}
// WD EDIT END AHEAD OF WIZDEN UPSTREAM
private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args)
{
args.Minds = _antag.GetAntagMindEntityUids(uid);
@@ -158,13 +192,16 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords)));
}
private string GenerateBriefing(string[] codewords, Note[]? uplinkCode, string? objectiveIssuer = null)
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"))));
sb.AppendLine(Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", codewords))));
if (codewords != null) // WD EDIT AHEAD OF WIZDEN UPSTREAM
sb.AppendLine(Loc.GetString("traitor-role-codewords", ("codewords", string.Join(", ", codewords))));
if (uplinkCode != null)
sb.AppendLine(Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", uplinkCode).Replace("sharp", "#"))));
else // WD EDIT AHEAD OF WIZDEN UPSTREAM
sb.AppendLine(Loc.GetString("traitor-role-uplink-implant"));
return sb.ToString();
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Traitor.Systems;
using Robust.Shared.Prototypes;
namespace Content.Server.Traitor.Components;
@@ -9,14 +10,8 @@ namespace Content.Server.Traitor.Components;
public sealed partial class AutoTraitorComponent : Component
{
/// <summary>
/// Whether to give the traitor an uplink or not.
/// The traitor profile to use
/// </summary>
[DataField("giveUplink"), ViewVariables(VVAccess.ReadWrite)]
public bool GiveUplink = true;
/// <summary>
/// Whether to give the traitor objectives or not.
/// </summary>
[DataField("giveObjectives"), ViewVariables(VVAccess.ReadWrite)]
public bool GiveObjectives = true;
[DataField]
public EntProtoId Profile = "Traitor"; // WD EDIT AHEAD OF WIZDEN UPSTREAM
}

View File

@@ -12,9 +12,6 @@ public sealed class AutoTraitorSystem : EntitySystem
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultTraitorRule = "Traitor";
public override void Initialize()
{
base.Initialize();
@@ -24,6 +21,6 @@ public sealed class AutoTraitorSystem : EntitySystem
private void OnMindAdded(EntityUid uid, AutoTraitorComponent comp, MindAddedMessage args)
{
_antag.ForceMakeAntag<AutoTraitorComponent>(args.Mind.Comp.Session, DefaultTraitorRule);
_antag.ForceMakeAntag<AutoTraitorComponent>(args.Mind.Comp.Session, comp.Profile); // WD EDIT AHEAD OF WIZDEN UPSTREAM
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Server.Traitor.Uplink;
/// <summary>
/// This is used for identifying something as a hidden uplink and showing the UI.
/// </summary>
[RegisterComponent]
public sealed partial class UplinkComponent : Component;

View File

@@ -1,94 +1,130 @@
using Content.Server.Store.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Implants;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Content.Server.Store.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Content.Server.Store.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Traitor.Uplink
namespace Content.Server.Traitor.Uplink;
public sealed class UplinkSystem : EntitySystem // WD EDIT AHEAD OF WIZDEN UPSTREAM
{
public sealed class UplinkSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly StoreSystem _store = default!;
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
[ValidatePrototypeId<CurrencyPrototype>]
public const string TelecrystalCurrencyPrototype = "Telecrystal";
private const string FallbackUplinkImplant = "UplinkImplant";
private const string FallbackUplinkCatalog = "UplinkUplinkImplanter";
/// <summary>
/// Adds an uplink to the target
/// </summary>
/// <param name="user">The person who is getting the uplink</param>
/// <param name="balance">The amount of currency on the uplink. If null, will just use the amount specified in the preset.</param>
/// <param name="uplinkEntity">The entity that will actually have the uplink functionality. Defaults to the PDA if null.</param>
/// <param name="giveDiscounts">Marker that enables discounts for uplink items.</param>
/// <returns>Whether or not the uplink was added successfully</returns>
public bool AddUplink(
EntityUid user,
FixedPoint2 balance,
EntityUid? uplinkEntity = null,
bool giveDiscounts = false)
{
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly StoreSystem _store = default!;
// Try to find target item if none passed
uplinkEntity ??= FindUplinkTarget(user);
if (uplinkEntity == null)
return ImplantUplink(user, balance, giveDiscounts);
[ValidatePrototypeId<CurrencyPrototype>]
public const string TelecrystalCurrencyPrototype = "Telecrystal";
EnsureComp<UplinkComponent>(uplinkEntity.Value);
/// <summary>
/// Gets the amount of TC on an "uplink"
/// Mostly just here for legacy systems based on uplink.
/// </summary>
/// <param name="component"></param>
/// <returns>the amount of TC</returns>
public int GetTCBalance(StoreComponent component)
SetUplink(user, uplinkEntity.Value, balance, giveDiscounts);
// TODO add BUI. Currently can't be done outside of yaml -_-
// ^ What does this even mean?
return true;
}
/// <summary>
/// Configure TC for the uplink
/// </summary>
private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts)
{
var store = EnsureComp<StoreComponent>(uplink);
store.AccountOwner = user;
_store.InitializeFromPreset("StorePresetUplink", uplink, store); // WD
store.Balance.Clear();
_store.TryAddCurrency(new Dictionary<string, FixedPoint2> { { TelecrystalCurrencyPrototype, balance } },
uplink,
store);
// WD REMOVED UNTIL PROPER UPSTREAM
// var uplinkInitializedEvent = new StoreInitializedEvent(
// TargetUser: user,
// Store: uplink,
// UseDiscounts: giveDiscounts,
// Listings: _store.GetAvailableListings(user, uplink, store)
// .ToArray());
// RaiseLocalEvent(ref uplinkInitializedEvent);
}
/// <summary>
/// Implant an uplink as a fallback measure if the traitor had no PDA
/// </summary>
private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts)
{
var implantProto = new string(FallbackUplinkImplant);
if (!_proto.TryIndex<ListingPrototype>(FallbackUplinkCatalog, out var catalog))
return false;
if (!catalog.Cost.TryGetValue(TelecrystalCurrencyPrototype, out var cost))
return false;
if (balance < cost) // Can't use Math functions on FixedPoint2
balance = 0;
else
balance = balance - cost;
var implant = _subdermalImplant.AddImplant(user, implantProto);
if (!HasComp<StoreComponent>(implant))
return false;
SetUplink(user, implant.Value, balance, giveDiscounts);
return true;
}
/// <summary>
/// Finds the entity that can hold an uplink for a user.
/// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.)
/// </summary>
public EntityUid? FindUplinkTarget(EntityUid user)
{
// Try to find PDA in inventory
if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator))
{
FixedPoint2? tcBalance = component.Balance.GetValueOrDefault(TelecrystalCurrencyPrototype);
return tcBalance?.Int() ?? 0;
while (containerSlotEnumerator.MoveNext(out var pdaUid))
{
if (!pdaUid.ContainedEntity.HasValue)
continue;
if (HasComp<PdaComponent>(pdaUid.ContainedEntity.Value) || HasComp<StoreComponent>(pdaUid.ContainedEntity.Value))
return pdaUid.ContainedEntity.Value;
}
}
// Also check hands
foreach (var item in _handsSystem.EnumerateHeld(user))
{
if (HasComp<PdaComponent>(item) || HasComp<StoreComponent>(item))
return item;
}
/// <summary>
/// Adds an uplink to the target
/// </summary>
/// <param name="user">The person who is getting the uplink</param>
/// <param name="balance">The amount of currency on the uplink. If null, will just use the amount specified in the preset.</param>
/// <param name="uplinkPresetId">The id of the storepreset</param>
/// <param name="uplinkEntity">The entity that will actually have the uplink functionality. Defaults to the PDA if null.</param>
/// <returns>Whether or not the uplink was added successfully</returns>
public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetId = "StorePresetUplink", EntityUid? uplinkEntity = null)
{
// Try to find target item
if (uplinkEntity == null)
{
uplinkEntity = FindUplinkTarget(user);
if (uplinkEntity == null)
return false;
}
var store = EnsureComp<StoreComponent>(uplinkEntity.Value);
_store.InitializeFromPreset(uplinkPresetId, uplinkEntity.Value, store);
store.AccountOwner = user;
store.Balance.Clear();
if (balance != null)
{
store.Balance.Clear();
_store.TryAddCurrency(new Dictionary<string, FixedPoint2> { { TelecrystalCurrencyPrototype, balance.Value } }, uplinkEntity.Value, store);
}
// TODO add BUI. Currently can't be done outside of yaml -_-
return true;
}
/// <summary>
/// Finds the entity that can hold an uplink for a user.
/// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.)
/// </summary>
public EntityUid? FindUplinkTarget(EntityUid user)
{
// Try to find PDA in inventory
if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator))
{
while (containerSlotEnumerator.MoveNext(out var pdaUid))
{
if (!pdaUid.ContainedEntity.HasValue) continue;
if (HasComp<PdaComponent>(pdaUid.ContainedEntity.Value) || HasComp<StoreComponent>(pdaUid.ContainedEntity.Value))
return pdaUid.ContainedEntity.Value;
}
}
// Also check hands
foreach (var item in _handsSystem.EnumerateHeld(user))
{
if (HasComp<PdaComponent>(item) || HasComp<StoreComponent>(item))
return item;
}
return null;
}
return null;
}
}

View File

@@ -108,22 +108,38 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
/// </summary>
public void AddImplants(EntityUid uid, IEnumerable<String> implants)
{
var coords = Transform(uid).Coordinates;
foreach (var id in implants)
{
var ent = Spawn(id, coords);
if (TryComp<SubdermalImplantComponent>(ent, out var implant))
{
ForceImplant(uid, ent, implant);
}
else
{
Log.Warning($"Found invalid starting implant '{id}' on {uid} {ToPrettyString(uid):implanted}");
Del(ent);
}
AddImplant(uid, id);
}
}
/// <summary>
/// Adds a single implant to a person, and returns the implant.
/// Logs any implant ids that don't have <see cref="SubdermalImplantComponent"/>.
/// </summary>
/// <returns>
/// The implant, if it was successfully created. Otherwise, null.
/// </returns>>
public EntityUid? AddImplant(EntityUid uid, String implantId) // WD EDIT AHEAD OF WIZDEN UPSTREAM
{
var coords = Transform(uid).Coordinates;
var ent = Spawn(implantId, coords);
if (TryComp<SubdermalImplantComponent>(ent, out var implant))
{
ForceImplant(uid, ent, implant);
}
else
{
Log.Warning($"Found invalid starting implant '{implantId}' on {uid} {ToPrettyString(uid):implanted}");
Del(ent);
return null;
}
return ent;
}
/// <summary>
/// Forces an implant into a person
/// Good for on spawn related code or admin additions
@@ -184,7 +200,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
var relayEv = new ImplantRelayEvent<T>(args);
foreach (var implant in implantContainer.ContainedEntities)
{
if (args is HandledEntityEventArgs { Handled : true })
if (args is HandledEntityEventArgs { Handled: true })
return;
RaiseLocalEvent(implant, relayEv);
@@ -223,7 +239,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
// Remove the implant from the donor's implant container
_container.Remove(donorImplant, donorImplantContainer, force: true);
if(!TryComp<SubdermalImplantComponent>(donorImplant, out var subdermal))
if (!TryComp<SubdermalImplantComponent>(donorImplant, out var subdermal))
return;
// Insert the implant into the recipient's implant container

View File

@@ -26,7 +26,7 @@ traitor-death-match-end-round-description-entry = {$originalName}'s PDA, with {$
traitor-role-greeting =
You are an agent sent by {$corporation} on behalf of The Syndicate.
Your objectives and codewords are listed in the character menu.
Use the uplink loaded into your PDA to buy the tools you'll need for this mission.
Use your uplink to buy the tools you'll need for this mission.
Death to Nanotrasen!
traitor-role-codewords =
The codewords are:
@@ -36,9 +36,13 @@ traitor-role-codewords =
traitor-role-uplink-code =
Set your ringtone to the notes {$code} to lock or unlock your uplink.
Remember to lock it after, or the stations crew will easily open it too!
traitor-role-uplink-implant =
Your uplink implant has been activated, access it from your hotbar.
The uplink is secure unless someone removes it from your body.
# don't need all the flavour text for character menu
traitor-role-codewords-short =
The codewords are:
{$codewords}.
traitor-role-uplink-code-short = Your uplink code is {$code}. Set it as your PDA ringtone to access uplink.
traitor-role-uplink-implant-short = Your uplink was implanted. Access it from your hotbar.

View File

@@ -25,7 +25,7 @@ traitor-death-match-end-round-description-entry = ПДА { $originalName }, с {
traitor-role-greeting =
Вы - агент Синдиката.
Ваши цели и кодовые слова перечислены в меню персонажа.
Воспользуйтесь аплинком, встроенным в ваш ПДА, чтобы приобрести всё необходимое для выполнения работы.
Воспользуйтесь аплинком, чтобы приобрести всё необходимое для выполнения работы.
Смерть НаноТрейзен!
traitor-role-codewords =
@@ -37,6 +37,9 @@ traitor-role-codewords =
traitor-role-uplink-code =
Установите мелодия рингтона на {$code} чтобы разблокировать аплинк.
Не забудьте заблокировать его после этого, иначе сотрудники станции тоже легко откроют его!
traitor-role-uplink-implant =
Ваш имплант аплинк активирован, воспользуйтесь им из хотбара.
Аплинк надежно защищён, пока кто-нибудь не извлечёт его из вашего тела.
# don't need all the flavour text for character menu
traitor-role-codewords-short =
@@ -44,3 +47,4 @@ traitor-role-codewords-short =
{$codewords}.
traitor-role-uplink-code-short = Код от вашего аплинка - {$code}. Установите его в качестве рингтона на своём ПДА для доступа к аплинку.
traitor-role-uplink-implant-short = Ваш аплинк был имплантирован. Воспользуйтесь им из хотбара.

View File

@@ -1335,8 +1335,7 @@
components:
# make the player a traitor once its taken
- type: AutoTraitor
giveUplink: false
giveObjectives: false
profile: TraitorReinforcement
- type: entity
id: MobMonkeySyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink
@@ -1491,8 +1490,7 @@
components:
# make the player a traitor once its taken
- type: AutoTraitor
giveUplink: false
giveObjectives: false
profile: TraitorReinforcement
- type: entity
id: MobKoboldSyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink

View File

@@ -27,8 +27,7 @@
components:
# make the player a traitor once its taken
- type: AutoTraitor
giveUplink: false
giveObjectives: false
profile: TraitorReinforcement
- type: entity
parent: MobHumanSyndicateAgentBase

View File

@@ -163,6 +163,20 @@
- type: TraitorRole
prototype: Traitor
- type: entity # WD AHEAD OF WIZDEN UPSTREAM
id: TraitorReinforcement
parent: Traitor
components:
- type: TraitorRule
giveUplink: false
giveCodewords: false # It would actually give them a different set of codewords than the regular traitors, anyway
giveBriefing: false
- type: AntagSelection
definitions:
- prefRoles: [ Traitor ]
mindRoles:
- MindRoleTraitor
- type: entity
id: Revolutionary
parent: BaseGameRule