fish-station/Content.Server/_Sunrise/AssaultOps/AssaultOpsRuleSystem.cs
Vigers Ray f84969630d
Диверсионный отряд (#985)
* Диверсионный отряд

* фиксы

* фиксы линтера

* Спрайт терминала икаруса

* фикс пинпоинтера

* Доработки ДО

* Фиксы удалённого управления шаттлом

* Не запускать ДО если на может быть меньше 3 глав
2025-01-01 07:53:31 +03:00

349 lines
12 KiB
C#

using Content.Server._Sunrise.AssaultOps.Icarus;
using Content.Server.Antag;
using Content.Server.Antag.Components;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Revolutionary.Components;
using Content.Server.RoundEnd;
using Content.Server.Station.Components;
using Content.Server.Traitor.Uplink;
using Content.Shared._Sunrise.AssaultOps;
using Content.Shared.GameTicking;
using Content.Shared.GameTicking.Components;
using Content.Shared.Implants;
using Content.Shared.Implants.Components;
using Content.Shared.Mind;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Roles;
using Content.Shared.Tag;
using Robust.Shared.Containers;
using Robust.Shared.Random;
namespace Content.Server._Sunrise.AssaultOps;
public sealed class AssaultOpsRuleSystem : GameRuleSystem<AssaultOpsRuleComponent>
{
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly UplinkSystem _uplinkSystem = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[ValidatePrototypeId<TagPrototype>]
private const string UplinkTagPrototype = "AssaultOpsUplink";
[ValidatePrototypeId<AntagPrototype>]
private const string CommanderAntagProto = "AssaultCommander";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnRunLevelChanged);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawned);
SubscribeLocalEvent<IcarusTerminalSystem.IcarusActivatedEvent>(OnIcarusActivated);
SubscribeLocalEvent<AssaultOperativeComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<AssaultOpsRuleComponent, AfterAntagEntitySelectedEvent>(OnAfterAntagEntSelected);
SubscribeLocalEvent<AssaultOpsRuleComponent, AntagSelectionCompleteEvent>(OnAfterAntagSelectionComplete); // Sunrise-Edit
SubscribeLocalEvent<AssaultOpsRuleComponent, RuleLoadedGridsEvent>(OnRuleLoadedGrids);
}
protected override void Started(EntityUid uid,
AssaultOpsRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
var eligible = new List<Entity<StationEventEligibleComponent, NpcFactionMemberComponent>>();
var eligibleQuery = EntityQueryEnumerator<StationEventEligibleComponent, NpcFactionMemberComponent>();
while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member))
{
if (!_npcFaction.IsFactionHostile(component.Faction, (eligibleUid, member)))
continue;
eligible.Add((eligibleUid, eligibleComp, member));
}
if (eligible.Count == 0)
return;
component.TargetStation = RobustRandom.Pick(eligible);
if (GameTicker.RunLevel == GameRunLevel.InRound)
{
InsertIcarusKeys(uid, component);
}
}
private void OnRuleLoadedGrids(Entity<AssaultOpsRuleComponent> ent, ref RuleLoadedGridsEvent args)
{
var query = EntityQueryEnumerator<AssaultOpsShuttleComponent>();
while (query.MoveNext(out var uid, out var shuttle))
{
if (Transform(uid).MapID == args.Map)
{
shuttle.AssociatedRule = ent;
break;
}
}
}
private void OnAfterAntagEntSelected(Entity<AssaultOpsRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
var target = (ent.Comp.TargetStation is not null) ? Name(ent.Comp.TargetStation.Value) : "the target";
_antag.SendBriefing(args.Session,
Loc.GetString("assaultops-welcome",
("station", target),
("name", Name(ent))),
Color.Red,
ent.Comp.GreetSoundNotification);
// Sunrise-Start
if (!args.GameRule.Comp.UseSpawners)
return;
ent.Comp.RoundstartOperatives = args.GameRule.Comp.SpawnersCount;
var commander = GetCommander(args.GameRule);
if (commander != null)
SetupUplink(commander.Value, ent.Comp);
// Sunrise-End
}
private void OnAfterAntagSelectionComplete(Entity<AssaultOpsRuleComponent> ent, ref AntagSelectionCompleteEvent args)
{
ent.Comp.RoundstartOperatives = args.GameRule.Comp.SelectedMinds.Count;
var commander = GetCommander(args.GameRule);
if (commander != null)
SetupUplink(commander.Value, ent.Comp);
}
private EntityUid? GetCommander(Entity<AntagSelectionComponent> antagSelection)
{
EntityUid? commander = null;
foreach (var compSelectedMind in antagSelection.Comp.SelectedMinds)
{
if (!TryComp<MindComponent>(compSelectedMind.Item1, out var mindComp))
continue;
foreach (var roleInfo in _roles.MindGetAllRoleInfo((compSelectedMind.Item1, mindComp)))
{
if (roleInfo.Prototype != CommanderAntagProto || mindComp.CurrentEntity == null)
continue;
commander = mindComp.CurrentEntity.Value;
}
}
return commander;
}
private void SetupUplink(EntityUid user, AssaultOpsRuleComponent rule)
{
var uplink = _uplinkSystem.FindUplinkByTag(user, UplinkTagPrototype);
if (uplink != null)
_uplinkSystem.SetUplink(user, uplink.Value, rule.TCAmountPerOperative * rule.RoundstartOperatives, true);
}
private void InsertIcarusKeys(EntityUid uid, AssaultOpsRuleComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
var query = EntityQueryEnumerator<CommandStaffComponent>();
while (query.MoveNext(out var ent, out var mind))
{
var haveKey = false;
if (_container.TryGetContainer(ent, ImplanterComponent.ImplantSlotId, out var implantContainer))
{
foreach (var implant in implantContainer.ContainedEntities)
{
if (MetaData(implant).EntityPrototype!.ID == component.IcarusKeyImplant)
haveKey = true;
}
}
if (!haveKey)
InsertKey(ent, component.IcarusKeyImplant);
}
}
private bool InsertKey(EntityUid uid, string icarusKeyImplant)
{
var ownedCoords = Transform(uid).Coordinates;
var implant = Spawn(icarusKeyImplant, ownedCoords);
if (!TryComp<SubdermalImplantComponent>(implant, out var implantComp))
return false;
_subdermalImplant.ForceImplant(uid, implant, implantComp);
return true;
}
private void OnIcarusActivated(IcarusTerminalSystem.IcarusActivatedEvent ev)
{
var query = EntityQueryEnumerator<AssaultOpsRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out var assaultops, out var gameRule))
{
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
{
Logger.Info("AssaultopsRule not added");
continue;
}
assaultops.WinConditions.Add(WinCondition.IcarusActivated);
}
}
private void OnPlayerSpawned(PlayerSpawnCompleteEvent ev)
{
var query = EntityQueryEnumerator<AssaultOpsRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out var assaultops, out var gameRule))
{
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
continue;
var session = ev.Player;
var mind = _mind.GetMind(session.UserId);
if (mind == null)
continue;
if (HasComp<CommandStaffComponent>(ev.Mob))
InsertKey(ev.Mob, assaultops.IcarusKeyImplant);
}
}
private void OnRunLevelChanged(GameRunLevelChangedEvent ev)
{
if (ev.New is not GameRunLevel.PostRound)
return;
var query = QueryActiveRules();
while (query.MoveNext(out var uid, out _, out var rule, out _))
{
OnRoundEnd(uid, rule);
}
}
private void OnRoundEnd(EntityUid uid, AssaultOpsRuleComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
var total = 0;
var alive = 0;
foreach (var (_, state) in EntityQuery<AssaultOperativeComponent, MobStateComponent>())
{
total++;
if (state.CurrentState != MobState.Alive)
continue;
alive++;
break;
}
var allAlive = alive == total;
if (allAlive)
{
if (component.WinConditions.Contains(WinCondition.IcarusActivated))
{
component.WinType = WinType.OpsMajor;
}
else
{
component.WinType = WinType.OpsMinor;
component.WinConditions.Add(WinCondition.AllOpsAlive);
}
}
else if (alive == 0)
{
if (component.WinConditions.Contains(WinCondition.IcarusActivated))
{
component.WinType = WinType.Hearty;
}
else
{
component.WinType = WinType.CrewMajor;
component.WinConditions.Add(WinCondition.AllOpsDead);
}
}
else
{
if (component.WinConditions.Contains(WinCondition.IcarusActivated))
{
component.WinType = WinType.OpsMinor;
}
else
{
component.WinType = WinType.Stalemate;
component.WinConditions.Add(WinCondition.SomeOpsAlive);
}
}
}
private void OnMobStateChanged(EntityUid uid, AssaultOperativeComponent component, MobStateChangedEvent ev)
{
if(ev.NewMobState == MobState.Dead)
CheckRoundShouldEnd();
}
private void CheckRoundShouldEnd()
{
var query = EntityQueryEnumerator<AssaultOpsRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out var assaultops, out var gameRule))
{
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
continue;
if (assaultops.WinType == WinType.CrewMajor || assaultops.WinType == WinType.OpsMajor)
continue;
var operativesAlive = false;
var operatives = EntityQuery<AssaultOperativeComponent, MobStateComponent>(true);
foreach (var (assaultOp, mobState) in operatives)
{
if (mobState.CurrentState is MobState.Alive)
{
operativesAlive = true;
}
}
if (operativesAlive)
continue;
_roundEndSystem.EndRound();
}
}
protected override void AppendRoundEndText(EntityUid uid,
AssaultOpsRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
var winText = Loc.GetString($"assaultops-{component.WinType.ToString().ToLower()}");
args.AddLine(winText);
foreach (var cond in component.WinConditions)
{
var text = Loc.GetString($"assaultops-cond-{cond.ToString().ToLower()}");
args.AddLine(text);
}
args.AddLine(Loc.GetString("assaultops-list-start"));
var antags =_antag.GetAntagIdentifiers(uid);
foreach (var (_, sessionData, name) in antags)
{
args.AddLine(Loc.GetString("assaultops-list-name", ("name", name), ("user", sessionData.UserName)));
}
}
}