* Hotfix

* Fuck custom server event! Hello gamerule!

* add some shit

* oopsie

* Add CCVAR of loading a ERT map and disable it on dev build
This commit is contained in:
Cinkafox
2023-08-17 23:41:13 +03:00
committed by Aviu00
parent 28bbd8db89
commit 0964ee123a
22 changed files with 347 additions and 509 deletions

View File

@@ -1,13 +1,13 @@
using Content.Server.Access.Systems;
using Content.Server.GameTicking;
using Content.Server.Popups;
using Content.Server.Station.Systems;
using Content.Server.White.ERTRecruitment;
using Content.Server.White.ServerEvent;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.White.AuthPanel;
using Content.Shared.White.Cyborg.Components;
using Content.Shared.White.GhostRecruitment;
using Robust.Server.GameObjects;
using Robust.Shared.Random;
using Robust.Shared.Timing;
@@ -20,11 +20,12 @@ public sealed class AuthPanelSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly AccessReaderSystem _access = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly ServerEventSystem _event = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ERTRecruitmentSystem _ert = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly ERTRecruitmentRule _ert = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly StationSystem _station = default!;
public Dictionary<AuthPanelAction, HashSet<EntityUid>> Counter = new();
public Dictionary<AuthPanelAction, HashSet<int>> CardIndexes = new();
@@ -38,10 +39,16 @@ public sealed class AuthPanelSystem : EntitySystem
{
SubscribeLocalEvent<AuthPanelComponent,AuthPanelButtonPressedMessage>(OnButtonPressed);
SubscribeLocalEvent<AuthPanelComponent,AuthPanelPerformActionEvent>(OnPerformAction);
SubscribeLocalEvent<RecruitedComponent,ERTRecruitedReasonEvent>(OnReason);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRestart);
}
private void OnReason(EntityUid uid, RecruitedComponent component, ERTRecruitedReasonEvent args)
{
args.Reason = Reason;
}
private void OnRestart(RoundRestartCleanupEvent ev)
{
Counter.Clear();
@@ -54,15 +61,16 @@ public sealed class AuthPanelSystem : EntitySystem
{
if (args.Action is AuthPanelAction.ERTRecruit)
{
if (_random.Next(0, 10) < 2
&& _event.TryStartEvent(ERTRecruitmentSystem.EventName)
&& _event.TryGetEvent(ERTRecruitmentSystem.EventName,out var eventPrototype))
if (_random.Next(10) < 2)
{
eventPrototype.Description = Reason;
_gameTicker.AddGameRule(ERTRecruitmentRuleComponent.EventName);
}
else
{
_ert.DeclineERT();
var station = _station.GetStationInMap(Transform(uid).MapID);
if (station != null)
_ert.DeclineERT(station.Value);
}
foreach (var entities in Counter.Values)
@@ -81,9 +89,6 @@ public sealed class AuthPanelSystem : EntitySystem
if(args.Session.AttachedEntity == null)
return;
if(HasComp<CyborgComponent>(args.Session.AttachedEntity))
return;
var access = _access.FindAccessTags(args.Session.AttachedEntity.Value);
if (!access.Contains("Command"))
@@ -168,7 +173,7 @@ public sealed class AuthPanelSystem : EntitySystem
var state = new AuthPanelConfirmationActionState(action);
var ui = _ui.GetUi(uid, AuthPanelUiKey.Key);
UserInterfaceSystem.SetUiState(ui, state);
_ui.SetUiState(ui, state);
_appearance.SetData(uid,AuthPanelVisualLayers.Confirm,true);
}
}

View File

@@ -16,16 +16,16 @@ public sealed class BlockERT : IConsoleCommand
public string Help => "blockert <true/false>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var ertsys = _entities.System<ERTRecruitmentSystem>();
var isBlocked = !ertsys.IsBlocked;
var ertsys = _entities.System<ERTRecruitmentRule>();
var isDisabled = !ertsys.IsDisabled;
if (args.Length > 0)
{
isBlocked = args[0] == "true";
isDisabled = args[0] == "true";
}
ertsys.IsBlocked = isBlocked;
var message = isBlocked ? "ERT is blocked!" : "ERT is no longer blocked!";
ertsys.IsDisabled = isDisabled;
var message = isDisabled ? "ERT is blocked!" : "ERT is no longer blocked!";
shell.WriteLine(message);
_chatManager.SendAdminAnnouncement(message);
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.White.ERTRecruitment;
[RegisterComponent]
public sealed partial class ERTMapComponent : Component
{
[ViewVariables]
public MapId? MapId;
[ViewVariables]
public EntityUid? Shuttle;
public static ResPath OutpostMap = new("/Maps/ERT/ERTStation.yml");
public static ResPath ShuttleMap = new("/Maps/ERT/ERTShuttle.yml");
}

View File

@@ -0,0 +1,13 @@
namespace Content.Server.White.ERTRecruitment;
[Serializable]
public sealed class ERTRecruitedReasonEvent : EntityEventArgs
{
public string Reason = "";
public void SetReason(string reason)
{
Reason = reason;
}
}

View File

@@ -0,0 +1,193 @@
using System.Linq;
using System.Numerics;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Server.StationEvents.Events;
using Content.Server.White.GhostRecruitment;
using Content.Shared.CCVar;
using Content.Shared.White;
using Content.Shared.White.GhostRecruitment;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
namespace Content.Server.White.ERTRecruitment;
[UsedImplicitly]
public sealed class ERTRecruitmentRule : StationEventSystem<ERTRecruitmentRuleComponent>
{
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly GhostRecruitmentSystem _recruitment = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
private ISawmill _logger = default!;
public bool IsDisabled = false;
public override void Initialize()
{
base.Initialize();
_logger = Logger.GetSawmill("ERTRecruit");
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
SubscribeLocalEvent<RecruitedComponent,GhostRecruitmentSuccessEvent>(OnRecruitmentSuccess);
}
protected override void Added(EntityUid uid, ERTRecruitmentRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
if (TryGetRandomStation(out var stationUid))
{
component.TargetStation = stationUid;
}
if (IsDisabled)
{
if (component.TargetStation != null)
DeclineERT(component.TargetStation.Value);
component.IsBlocked = true;
return;
}
var query = EntityQueryEnumerator<ERTMapComponent>();
if (query.MoveNext(out uid, out var ertMapComponent))
{
component.Outpost = uid;
component.Shuttle = ertMapComponent.Shuttle;
component.MapId = ertMapComponent.MapId;
}
}
protected override void Started(EntityUid uid, ERTRecruitmentRuleComponent component, GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
_logger.Debug("Event is started");
if (component.TargetStation == null || component.IsBlocked)
{
ForceEndSelf(uid,gameRule);
_logger.Debug("oopsie doopsie we make a poopie poopie on starting event!");
return;
}
if (_recruitment.GetEventSpawners(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayer)
{
_logger.Error("Not enough spawners!");
DeclineERT(component.TargetStation.Value);
return;
}
_chatSystem.DispatchStationAnnouncement(component.TargetStation.Value,Loc.GetString("ert-wait-message"),colorOverride: Color.Gold);
if (TryComp<ShuttleComponent>(component.Shuttle, out var shuttle) && component.Outpost != null)
{
_shuttle.TryFTLDock(component.Shuttle.Value, shuttle, component.Outpost.Value);
}
_recruitment.StartRecruitment(ERTRecruitmentRuleComponent.EventName);
}
protected override void Ended(EntityUid uid, ERTRecruitmentRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
{
base.Ended(uid, component, gameRule, args);
if (component.IsBlocked || _recruitment.GetAllRecruited(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayer ||
!_recruitment.EndRecruitment(ERTRecruitmentRuleComponent.EventName))
{
if (component.TargetStation != null)
DeclineERT(component.TargetStation.Value);
_recruitment.Cleanup(ERTRecruitmentRuleComponent.EventName);
return;
}
if (component.TargetStation != null)
AcceptERT(component.TargetStation.Value);
}
private void OnRecruitmentSuccess(EntityUid uid, RecruitedComponent component, GhostRecruitmentSuccessEvent args)
{
var ev = new ERTRecruitedReasonEvent();
RaiseLocalEvent(uid,ev);
_chat.DispatchServerMessage(args.PlayerSession,Loc.GetString("ert-description"));
_chat.DispatchServerMessage(args.PlayerSession, Loc.GetString("ert-reason", ("reason", ev.Reason)));
}
private void OnStartAttempt(RoundStartAttemptEvent ev)
{
if(_cfgManager.GetCVar(WhiteCVars.LoadERTMap))
SpawnMap();
}
public void AcceptERT(EntityUid targetStation)
{
_chatSystem.DispatchStationAnnouncement(targetStation,Loc.GetString("ert-accept-message"),
colorOverride: Color.Gold,announcementSound:ERTRecruitmentRuleComponent.ERTYes);
}
public void DeclineERT(EntityUid targetStation)
{
_chatSystem.DispatchStationAnnouncement(targetStation,Loc.GetString("ert-deny-message"),
colorOverride: Color.Gold,announcementSound:ERTRecruitmentRuleComponent.ERTNo);
}
private bool SpawnMap()
{
_logger.Debug($"Loading maps!");
var mapId = _mapManager.CreateMap();
var options = new MapLoadOptions
{
LoadMap = true,
};
if (!_map.TryLoad(mapId, ERTMapComponent.OutpostMap.ToString(), out var outpostGrids, options) || outpostGrids.Count == 0)
{
_logger.Error( $"Error loading map {ERTMapComponent.OutpostMap}!");
return false;
}
_logger.Debug($"Loaded map {ERTMapComponent.OutpostMap} on {mapId}!");
// Assume the first grid is the outpost grid.
var outpost = outpostGrids[0];
// Listen I just don't want it to overlap.
if (!_map.TryLoad(mapId, ERTMapComponent.ShuttleMap.ToString(), out var grids, new MapLoadOptions {Offset = Vector2.One * 1000f}) || !grids.Any())
{
_logger.Error( $"Error loading grid {ERTMapComponent.ShuttleMap}!");
return false;
}
_logger.Debug($"Loaded shuttle {ERTMapComponent.ShuttleMap} on {mapId}!");
var shuttleId = grids.First();
// Naughty, someone saved the shuttle as a map.
if (Deleted(shuttleId))
{
_logger.Error( $"Tried to load shuttle as a map, aborting.");
_mapManager.DeleteMap(mapId);
return false;
}
var ERTMap = EnsureComp<ERTMapComponent>(outpost);
ERTMap.MapId = mapId;
ERTMap.Shuttle = shuttleId;
return true;
}
}

View File

@@ -0,0 +1,28 @@
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.White.ERTRecruitment;
[RegisterComponent, Access(typeof(ERTRecruitmentRule))]
public sealed partial class ERTRecruitmentRuleComponent : Component
{
public static string EventName = "ERTRecruitment";
[ViewVariables]
public MapId? MapId = null;
[DataField("minPlayer")] public int MinPlayer = 4;
public static SoundSpecifier ERTYes = new SoundPathSpecifier("/Audio/Announcements/ert_yes.ogg");
public static SoundSpecifier ERTNo = new SoundPathSpecifier("/Audio/Announcements/ert_no.ogg");
[ViewVariables]
public bool IsBlocked = false;
[ViewVariables]
public EntityUid? Outpost;
[ViewVariables]
public EntityUid? Shuttle;
[ViewVariables]
public EntityUid? TargetStation;
}

View File

@@ -1,213 +0,0 @@
using System.Linq;
using System.Numerics;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Systems;
using Content.Server.White.GhostRecruitment;
using Content.Server.White.ServerEvent;
using Content.Shared.GameTicking;
using Content.Shared.White.GhostRecruitment;
using Content.Shared.White.ServerEvent;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.White.ERTRecruitment;
public sealed class ERTRecruitmentSystem : EntitySystem
{
public static string EventName = "ERTRecruitment";
private ISawmill _logger = default!;
private MapId? _mapId = null;
public ResPath OutpostMap = new("/Maps/ERT/ERTStation.yml");
public ResPath ShuttleMap = new("/Maps/ERT/ERTShuttle.yml");
public SoundSpecifier ERTYes = new SoundPathSpecifier("/Audio/Announcements/ert_yes.ogg");
public SoundSpecifier ERTNo = new SoundPathSpecifier("/Audio/Announcements/ert_no.ogg");
public bool IsBlocked = false;
public EntityUid? Outpost;
public EntityUid? Shuttle;
public EntityUid? TargetStation;
[Dependency] private readonly ServerEventSystem _event = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly GhostRecruitmentSystem _recruitment = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
public override void Initialize()
{
_logger = Logger.GetSawmill(EventName);
SubscribeLocalEvent<RoundStartAttemptEvent>(OnRoundStart);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundEnd);
SubscribeLocalEvent<RecruitedComponent,GhostRecruitmentSuccessEvent>(OnRecruitmentSuccess);
SubscribeLocalEvent<EventStarted>(OnEventStart);
SubscribeLocalEvent<EventEnded>(OnEventEnd);
}
private void OnEventEnd(EventEnded ev)
{
if(ev.EventName != EventName)
return;
EventEnd();
}
private void OnEventStart(EventStarted ev)
{
if(ev.EventName != EventName)
return;
EventStart();
}
private void OnRoundEnd(RoundRestartCleanupEvent ev)
{
Outpost = null;
Shuttle = null;
TargetStation = null;
_mapId = null;
_logger.Debug("Deleted map");
}
private void OnRecruitmentSuccess(EntityUid uid, RecruitedComponent component, GhostRecruitmentSuccessEvent args)
{
if(args.RecruitmentName != EventName || !_event.TryGetEvent(EventName, out var prototype))
return;
_chat.DispatchServerMessage(args.PlayerSession,Loc.GetString("ert-description"));
_chat.DispatchServerMessage(args.PlayerSession, Loc.GetString("ert-reason",("reason",prototype.Description)));
}
private void OnRoundStart(RoundStartAttemptEvent ev)
{
var stations = _station.GetStations();
if (stations.Count > 0)
{
TargetStation = stations[0];
}
SpawnMap();
}
public void EventStart()
{
if(TargetStation == null || IsBlocked)
return;
if (!_event.TryGetEvent(EventName, out var prototype) ||
_recruitment.GetEventSpawners(EventName).Count() < prototype.MinPlayer)
{
_logger.Error("Not enough spawners!");
_event.BreakEvent(EventName);
DeclineERT();
return;
}
_chatSystem.DispatchStationAnnouncement(TargetStation.Value,Loc.GetString("ert-wait-message"),colorOverride: Color.Gold);
if (TryComp<ShuttleComponent>(Shuttle, out var shuttle) && Outpost != null)
{
_shuttle.TryFTLDock(Shuttle.Value, shuttle, Outpost.Value);
}
_recruitment.StartRecruitment(EventName);
}
public void EventEnd()
{
if (!_event.TryGetEvent(EventName, out var prototype) )
return;
if (_recruitment.GetAllRecruited(EventName).Count() < prototype.MinPlayer ||
!_recruitment.EndRecruitment(EventName))
{
DeclineERT();
return;
}
AcceptERT();
}
public void AcceptERT()
{
if(TargetStation == null)
return;
_chatSystem.DispatchStationAnnouncement(TargetStation.Value,Loc.GetString("ert-accept-message"),
colorOverride: Color.Gold,announcementSound:ERTYes);
}
public void DeclineERT()
{
if(TargetStation == null)
return;
_chatSystem.DispatchStationAnnouncement(TargetStation.Value,Loc.GetString("ert-deny-message"),
colorOverride: Color.Gold,announcementSound:ERTNo);
}
private bool SpawnMap()
{
_logger.Debug($"Loading maps!");
if (_mapId != null)
{
// The map is already loaded, so there is no point in loading further
_logger.Debug("Map is already loaded " + _mapId);
return true;
}
var mapId = _mapManager.CreateMap();
var options = new MapLoadOptions
{
LoadMap = true,
};
if (!_map.TryLoad(mapId, OutpostMap.ToString(), out var outpostGrids, options) || outpostGrids.Count == 0)
{
_logger.Error( $"Error loading map {OutpostMap}!");
return false;
}
_logger.Debug($"Loaded map {OutpostMap} on {mapId}!");
// Assume the first grid is the outpost grid.
Outpost = outpostGrids[0];
// Listen I just don't want it to overlap.
if (!_map.TryLoad(mapId, ShuttleMap.ToString(), out var grids, new MapLoadOptions {Offset = Vector2.One * 1000f}) || !grids.Any())
{
_logger.Error( $"Error loading grid {ShuttleMap}!");
return false;
}
_logger.Debug($"Loaded shuttle {ShuttleMap} on {mapId}!");
var shuttleId = grids.First();
// Naughty, someone saved the shuttle as a map.
if (Deleted(shuttleId))
{
_logger.Error( $"Tried to load shuttle as a map, aborting.");
_mapManager.DeleteMap(mapId);
return false;
}
_mapId = mapId;
Shuttle = shuttleId;
return true;
}
}

View File

@@ -1,5 +1,4 @@
using Robust.Server.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Player;
namespace Content.Server.White.GhostRecruitment;
@@ -7,9 +6,9 @@ namespace Content.Server.White.GhostRecruitment;
public sealed class GhostRecruitmentSuccessEvent : EntityEventArgs
{
public string RecruitmentName;
public IPlayerSession PlayerSession;
public ICommonSession PlayerSession;
public GhostRecruitmentSuccessEvent(string recruitmentName, IPlayerSession playerSession)
public GhostRecruitmentSuccessEvent(string recruitmentName, ICommonSession playerSession)
{
RecruitmentName = recruitmentName;
PlayerSession = playerSession;

View File

@@ -4,9 +4,12 @@ using Content.Server.Ghost.Components;
using Content.Server.Humanoid.Components;
using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Ghost;
using Content.Shared.Players;
using Content.Shared.White.GhostRecruitment;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.White.GhostRecruitment;
@@ -19,7 +22,7 @@ public sealed class GhostRecruitmentSystem : EntitySystem
[Dependency] private readonly EuiManager _eui = default!;
[Dependency] private readonly MindSystem _mind = default!;
private readonly Dictionary<IPlayerSession, GhostRecruitmentEuiAccept> _openUis = new();
private readonly Dictionary<ICommonSession, GhostRecruitmentEuiAccept> _openUis = new();
/// <summary>
/// starts recruiting ghosts, showing them a menu with a choice to recruit.
@@ -51,8 +54,6 @@ public sealed class GhostRecruitmentSystem : EntitySystem
/// <returns>is success?</returns>
public bool EndRecruitment(string recruitmentName)
{
var query = EntityQueryEnumerator<GhostRecruitedComponent>();
var spawners = GetEventSpawners(recruitmentName).ToList();
// We prioritize the queue, for example, the commander first, and then the engineer
@@ -60,29 +61,16 @@ public sealed class GhostRecruitmentSystem : EntitySystem
var count = 0;
var attemptRecruitment = new GhostRecruitmentAttemptEvent(recruitmentName);
RaiseLocalEvent(attemptRecruitment);
if (attemptRecruitment.Cancelled)
{
var failEvent = new GhostsRecruitmentFailEvent(recruitmentName);
RaiseLocalEvent(failEvent);
return false;
}
var query = EntityQueryEnumerator<GhostRecruitedComponent>();
while (query.MoveNext(out var uid,out var ghostRecruitedComponent))
{
if(ghostRecruitedComponent.RecruitmentName != recruitmentName)
continue;
RemComp<GhostRecruitedComponent>(uid);
if (!TryComp<ActorComponent>(uid, out var actorComponent))
continue;
CloseEui(uid,recruitmentName,actorComponent);
// if there are too many recruited, then just skip
if(count >= spawners.Count)
continue;
@@ -100,9 +88,26 @@ public sealed class GhostRecruitmentSystem : EntitySystem
var ghostsEvent = new GhostsRecruitmentSuccessEvent(recruitmentName);
RaiseLocalEvent(ghostsEvent);
Cleanup(recruitmentName);
return true;
}
public void Cleanup(string recruitmentName)
{
ClearEui(recruitmentName);
var query = EntityQueryEnumerator<GhostRecruitedComponent>();
while (query.MoveNext(out var uid, out var ghostRecruitedComponent))
{
if (ghostRecruitedComponent.RecruitmentName != recruitmentName)
continue;
RemComp<GhostRecruitedComponent>(uid);
}
}
private void TransferMind(EntityUid from,EntityUid spawnerUid,GhostRecruitmentSpawnPointComponent? component = null)
{
if (!Resolve(spawnerUid, ref component) || !TryComp<ActorComponent>(from,out var actorComponent))
@@ -112,10 +117,14 @@ public sealed class GhostRecruitmentSystem : EntitySystem
if(!entityUid.HasValue)
return;
var mind = actorComponent.PlayerSession.GetMind()!;
_mind.TransferTo(mind,entityUid.Value);
_mind.UnVisit(mind);
var mind = actorComponent.PlayerSession.GetMind();
if (!mind.HasValue)
return;
_mind.TransferTo(mind.Value, entityUid.Value);
_mind.UnVisit(mind.Value);
}
private EntityUid? Spawn(EntityUid spawnerUid,GhostRecruitmentSpawnPointComponent? component = null)
@@ -159,20 +168,32 @@ public sealed class GhostRecruitmentSystem : EntitySystem
return;
var eui = new GhostRecruitmentEuiAccept(uid, recruitmentName, this);
_openUis.Add(actorComponent.PlayerSession,eui);
_eui.OpenEui(eui,actorComponent.PlayerSession);
Logger.Debug("Added EUI to "+ uid);
if(_openUis.TryAdd(actorComponent.PlayerSession,eui))
_eui.OpenEui(eui,actorComponent.PlayerSession);
}
public void ClearEui(string recruitmentName)
{
foreach (var (session,eui) in _openUis)
{
if (session.AttachedEntity != null)
CloseEui(session.AttachedEntity.Value, recruitmentName);
}
}
public void CloseEui(EntityUid uid,string recruitmentName,ActorComponent? actorComponent = null)
{
if(!Resolve(uid,ref actorComponent))
if (!Resolve(uid, ref actorComponent))
return;
var session = actorComponent.PlayerSession;
if (!_openUis.ContainsKey(session))
return;
Logger.Debug("Removed EUI from "+ uid);
_openUis.Remove(session, out var eui);
eui?.Close();

View File

@@ -1,35 +0,0 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.White.ServerEvent;
using Robust.Shared.Console;
namespace Content.Server.White.ServerEvent.Commands;
[AdminCommand(AdminFlags.Admin)]
public sealed class StartServerEvent : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entities = default!;
public string Command => "startevent";
public string Description => "starting a new event";
public string Help => "startevent <event>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
shell.WriteError("Not enough args!");
}
if(!_entities.System<ServerEventSystem>().TryStartEvent(args[0]))
shell.WriteError("Oopsie! error on starting event!");
else
shell.WriteLine("Done!");
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
return CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<ServerEventPrototype>(),
"<serverEventPrototype>");
}
}

View File

@@ -1,122 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameTicking;
using Content.Shared.GameTicking;
using Content.Shared.White.ServerEvent;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.White.ServerEvent;
public sealed class ServerEventSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
private readonly Dictionary<string,ServerEventPrototype> _eventCache = new();
public override void Initialize()
{
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnEnd);
}
private void OnEnd(RoundRestartCleanupEvent ev)
{
foreach (var eventPrototype in GetActiveEvents())
{
BreakEvent(eventPrototype);
}
_eventCache.Clear();
}
public bool TryGetEvent(string eventName,[MaybeNullWhen(false)] out ServerEventPrototype prototype)
{
if (!_eventCache.TryGetValue(eventName, out prototype))
{
if (!_prototype.TryIndex(eventName, out prototype))
{
Logger.Debug("Failed load " + eventName);
return false;
}
Logger.Debug("Caching " + eventName);
_eventCache.Add(eventName,prototype);
}
Logger.Debug("Successful get " + eventName);
return true;
}
public bool IsEventRunning(string eventName)
{
if (!TryGetEvent(eventName, out var prototype))
return false;
return IsEventRunning(prototype);
}
private bool IsEventRunning(ServerEventPrototype prototype)
{
return prototype.EndPlayerGatherTime.HasValue && !prototype.IsBreak;
}
public IEnumerable<ServerEventPrototype> GetActiveEvents()
{
foreach (var (_,prototype) in _eventCache)
{
if (IsEventRunning(prototype))
yield return prototype;
}
}
public void BreakEvent(string eventName)
{
if(!TryGetEvent(eventName,out var prototype))
return;
BreakEvent(prototype);
}
private void BreakEvent(ServerEventPrototype prototype)
{
Logger.Debug("Event is break! " + prototype.ID);
prototype.CurrentPlayerGatherTime = null;
prototype.EndPlayerGatherTime = null;
prototype.IsBreak = true;
}
public bool TryStartEvent(string eventName)
{
if (!TryGetEvent(eventName, out var prototype) || IsEventRunning(prototype) || _gameTicker.RunLevel != GameRunLevel.InRound)
return false;
prototype.IsBreak = false;
prototype.EndPlayerGatherTime = _timing.CurTime + prototype.PlayerGatherTime;
Logger.Debug("Event is started " + eventName);
return true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var prototype in GetActiveEvents())
{
if (!prototype.CurrentPlayerGatherTime.HasValue)
{
Logger.Debug("Started with " + prototype.CurrentPlayerGatherTime + " " + prototype.EndPlayerGatherTime + " " + prototype.IsBreak );
RaiseLocalEvent(new EventStarted(prototype.ID));
prototype.OnStart?.Execute(prototype);
}
prototype.CurrentPlayerGatherTime = _timing.CurTime;
if (prototype.CurrentPlayerGatherTime > prototype.EndPlayerGatherTime)
{
prototype.OnEnd?.Execute(prototype);
RaiseLocalEvent(new EventEnded(prototype.ID));
Logger.Debug("Ended with " + prototype.CurrentPlayerGatherTime + " " + prototype.EndPlayerGatherTime + " " + prototype.IsBreak );
BreakEvent(prototype);
}
}
}
}

View File

@@ -1,7 +1,7 @@
namespace Content.Shared.White.AuthPanel;
[RegisterComponent]
public sealed class AuthPanelComponent : Component
public sealed partial class AuthPanelComponent : Component
{
}

View File

@@ -2,7 +2,7 @@ namespace Content.Shared.White.GhostRecruitment;
//this for ghosts
[RegisterComponent]
public sealed class GhostRecruitedComponent : Component
public sealed partial class GhostRecruitedComponent : Component
{
[DataField("recruitmentName")]
public string RecruitmentName = "default";

View File

@@ -13,27 +13,6 @@ public sealed class GhostsRecruitmentSuccessEvent
}
}
[Serializable,NetSerializable]
public sealed class GhostsRecruitmentFailEvent
{
public string RecruitmentName;
public GhostsRecruitmentFailEvent(string recruitmentName)
{
RecruitmentName = recruitmentName;
}
}
[Serializable,NetSerializable]
public sealed class GhostRecruitmentAttemptEvent : CancelableEventArgs
{
public string RecruitmentName;
public GhostRecruitmentAttemptEvent(string recruitmentName)
{
RecruitmentName = recruitmentName;
}
}
[Serializable,NetSerializable]

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Shared.White.GhostRecruitment;
[RegisterComponent]
public sealed class GhostRecruitmentSpawnPointComponent : Component
public sealed partial class GhostRecruitmentSpawnPointComponent : Component
{
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>),required:true)]
public string EntityPrototype = default!;

View File

@@ -3,7 +3,7 @@ namespace Content.Shared.White.GhostRecruitment;
// this for spawned prototype
[RegisterComponent]
public sealed class RecruitedComponent : Component
public sealed partial class RecruitedComponent : Component
{
[DataField("recruitmentName")]
public string RecruitmentName = "default";

View File

@@ -1,7 +0,0 @@
namespace Content.Shared.White.ServerEvent.Data;
[ImplicitDataDefinitionForInheritors]
public interface IEventAction
{
void Execute(ServerEventPrototype prototype);
}

View File

@@ -1,26 +0,0 @@
using Robust.Shared.Serialization;
namespace Content.Shared.White.ServerEvent;
[Serializable, NetSerializable]
public sealed class EventStarted
{
public string EventName;
public EventStarted(string eventName)
{
EventName = eventName;
}
}
[Serializable, NetSerializable]
public sealed class EventEnded
{
public string EventName;
public EventEnded(string eventName)
{
EventName = eventName;
}
}

View File

@@ -1,31 +0,0 @@
using Content.Shared.White.ServerEvent.Data;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.White.ServerEvent;
[Prototype("serverEvent")]
public sealed class ServerEventPrototype : IPrototype
{
[IdDataField]
public string ID { get; } = default!;
[DataField("name")]
public string Name { get; } = string.Empty;
[DataField("description")]
public string Description = string.Empty;
[DataField("playerGatherTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan PlayerGatherTime = TimeSpan.FromSeconds(10);
[DataField("onStart")] public IEventAction? OnStart;
[DataField("onEnd")] public IEventAction? OnEnd;
[DataField("minPlayer")] public int MinPlayer;
[ViewVariables] public TimeSpan? CurrentPlayerGatherTime = null;
[ViewVariables] public TimeSpan? EndPlayerGatherTime = null;
[ViewVariables] public bool IsBreak;
}

View File

@@ -291,4 +291,12 @@ public sealed class WhiteCVars
/// </summary>
public static readonly CVarDef<bool> SlippedCountTopSlipper =
CVarDef.Create("eorstats.slippedcount_topslipper", true, CVar.SERVERONLY);
public static readonly CVarDef<string>
ServerCulture = CVarDef.Create("white.culture", "ru-RU", CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// Should load a ERT map?
/// </summary>
public static readonly CVarDef<bool> LoadERTMap = CVarDef.Create("white.ert_load", false, CVar.SERVERONLY);
}

View File

@@ -29,3 +29,6 @@ see_own_notes = true
[worldgen]
enabled = false
[white]
ert_load = false

View File

@@ -1,5 +1,12 @@
- type: serverEvent
- type: entity
id: ERTRecruitment
name: ERT
description: Your tasks is meow!
minPlayer: 4
parent: BaseGameRule
noSpawn: true
components:
- type: StationEvent
earliestStart: 25
weight: 0
minimumPlayers: 125
duration: 20
- type: ERTRecruitmentRule