Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gamemodes #23

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Server.GameTicking.Rules.Components;

[RegisterComponent, Access(typeof(LockdownRuleSystem))]
public sealed partial class LockdownRuleComponent : Component
{
/// <summary>
/// The gamerules that get added by lockdown.
/// </summary>
[DataField("additionalGameRules")]
public HashSet<EntityUid> AdditionalGameRules = new();
}
174 changes: 174 additions & 0 deletions Content.Server/GameTicking/Rules/SP14/LockdownRuleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking.Presets;
using Content.Server.GameTicking.Rules.Components;
using Content.Shared.GameTicking.Components;
using Content.Shared.Random;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Configuration;
using Robust.Shared.Utility;

namespace Content.Server.GameTicking.Rules;

// is this wildly inefficient? yes. do i care? not really.

public sealed class LockdownRuleSystem : GameRuleSystem<LockdownRuleComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IComponentFactory _compFact = default!;

private string _ruleCompName = default!;

public override void Initialize()
{
base.Initialize();
_ruleCompName = _compFact.GetComponentName(typeof(GameRuleComponent));
}

protected override void Added(EntityUid uid, LockdownRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
var weights = _configurationManager.GetCVar(CCVars.LockdownWeightPrototype);

if (!TryPickPreset(weights, out var preset))
{
Log.Error($"{ToPrettyString(uid)} failed to pick any preset. Removing rule.");
Del(uid);
return;
}

Log.Info($"Selected {preset.ID} as the lockdown preset.");
_adminLogger.Add(LogType.EventStarted, $"Selected {preset.ID} as the lockdown preset.");

foreach (var rule in preset.Rules)
{
EntityUid ruleEnt;

// if we're pre-round (i.e. will only be added)
// then just add rules. if we're added in the middle of the round (or at any other point really)
// then we want to start them as well
if (GameTicker.RunLevel <= GameRunLevel.InRound)
ruleEnt = GameTicker.AddGameRule(rule);
else
GameTicker.StartGameRule(rule, out ruleEnt);

component.AdditionalGameRules.Add(ruleEnt);
}
}

protected override void Ended(EntityUid uid, LockdownRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
{
base.Ended(uid, component, gameRule, args);

foreach (var rule in component.AdditionalGameRules)
{
GameTicker.EndGameRule(rule);
}
}

private bool TryPickPreset(ProtoId<WeightedRandomPrototype> weights, [NotNullWhen(true)] out GamePresetPrototype? preset)
{
var options = _prototypeManager.Index(weights).Weights.ShallowClone();
var players = GameTicker.ReadyPlayerCount();

GamePresetPrototype? selectedPreset = null;
var sum = options.Values.Sum();
while (options.Count > 0)
{
var accumulated = 0f;
var rand = _random.NextFloat(sum);
foreach (var (key, weight) in options)
{
accumulated += weight;
if (accumulated < rand)
continue;

if (!_prototypeManager.TryIndex(key, out selectedPreset))
Log.Error($"Invalid preset {selectedPreset} in lockdown rule weights: {weights}");

options.Remove(key);
sum -= weight;
break;
}

if (CanPick(selectedPreset, players))
{
preset = selectedPreset;
return true;
}

if (selectedPreset != null)
Log.Info($"Excluding {selectedPreset.ID} from lockdown preset selection.");
}

preset = null;
return false;
}

public bool CanPickAny()
{
var secretPresetId = _configurationManager.GetCVar(CCVars.SecretWeightPrototype);
return CanPickAny(secretPresetId);
}

/// <summary>
/// Can any of the given presets be picked, taking into account the currently available player count?
/// </summary>
public bool CanPickAny(ProtoId<WeightedRandomPrototype> weightedPresets)
{
var ids = _prototypeManager.Index(weightedPresets).Weights.Keys
.Select(x => new ProtoId<GamePresetPrototype>(x));

return CanPickAny(ids);
}

/// <summary>
/// Can any of the given presets be picked, taking into account the currently available player count?
/// </summary>
public bool CanPickAny(IEnumerable<ProtoId<GamePresetPrototype>> protos)
{
var players = GameTicker.ReadyPlayerCount();
foreach (var id in protos)
{
if (!_prototypeManager.TryIndex(id, out var selectedPreset))
Log.Error($"Invalid preset {selectedPreset} in lockdown rule weights: {id}");

if (CanPick(selectedPreset, players))
return true;
}

return false;
}

/// <summary>
/// Can the given preset be picked, taking into account the currently available player count?
/// </summary>
private bool CanPick([NotNullWhen(true)] GamePresetPrototype? selected, int players)
{
if (selected == null)
return false;

foreach (var ruleId in selected.Rules)
{
if (!_prototypeManager.TryIndex(ruleId, out EntityPrototype? rule)
|| !rule.TryGetComponent(_ruleCompName, out GameRuleComponent? ruleComp))
{
Log.Error($"Encountered invalid rule {ruleId} in preset {selected.ID}");
return false;
}

if (ruleComp.MinPlayers > players && ruleComp.CancelPresetOnTooFewPlayers)
return false;
}

return true;
}
}
14 changes: 12 additions & 2 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ public sealed class CCVars : CVars
* Server
*/

/// SP14

/// <summary>
/// The prototype to use for sp14's lockdown weights.
/// </summary>
public static readonly CVarDef<string> LockdownWeightPrototype =
CVarDef.Create("game.lockdown_weight_prototype", "Lockdown", CVar.SERVERONLY);

/// SS14

/// <summary>
/// Change this to have the changelog and rules "last seen" date stored separately.
/// </summary>
Expand Down Expand Up @@ -142,7 +152,7 @@ public static readonly CVarDef<bool>
/// Controls the default game preset.
/// </summary>
public static readonly CVarDef<string>
GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "secret", CVar.ARCHIVE);
GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "lockdown", CVar.ARCHIVE);

/// <summary>
/// Controls if the game can force a different preset if the current preset's criteria are not met.
Expand Down Expand Up @@ -1550,7 +1560,7 @@ public static readonly CVarDef<int>
/// Any value equal to or less than zero will disable this check.
/// </summary>
public static readonly CVarDef<float> FTLMassLimit =
CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY);
CVarDef.Create("shuttle.mass_limit", 650f, CVar.SERVERONLY); /// SP14 doubled mass for liberator shuttle :)

/// <summary>
/// How long to knock down entities for if they aren't buckled when FTL starts and stops.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Liberator-title = Liberation
Liberator-description = Liberators have been called in to extract inmates. stage your escape or defend the prison.

Liberator-welcome =
You are a liberator. Your goal is to extract any and all prisoners from {$station}. ensure minimal harm comes to any inmates. Your benefactors, the Syndicate, have provided you with the tools you'll need for the task.
Free the people! Death to Nanotrasen!

Liberator-opsmajor = [color=crimson]Liberator major victory![/color]
Liberator-opsminor = [color=crimson]Liberator minor victory![/color]
Liberator-neutral = [color=yellow]Neutral outcome![/color]
Liberator-crewminor = [color=green]Crew minor victory![/color]
Liberator-crewmajor = [color=green]Crew major victory![/color]

Liberator-role-commander = Commander
Liberator-role-agent = Agent
Liberator-role-operator = Operator
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lockdown-title = Lockdown
lockdown-description = It's a secret to everyone. The threats you encounter are randomized.
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,6 @@ ghost-role-information-artifact-description = Enact your eldritch whims. Forcibl

ghost-role-information-syndie-assaultborg-name = Syndicate Assault Borg
ghost-role-information-syndie-assaultborg-description = Nuclear operatives needs reinforcements. You, a cold silicon killing machine, will help them. More dakka!

#SP14
ghost-role-information-liberator-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with all other liberators. Prisoners are not guaranteed to help you.
11 changes: 11 additions & 0 deletions Resources/Locale/en-US/prototypes/roles/antags.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ roles-antag-thief-objective = Add some NT property to your personal collection w

roles-antag-dragon-name = Space Dragon
roles-antag-dragon-objective = Create a carp army to take over this quadrant.

#sp14

roles-antag-liberator-commander-name = Liberaton commander
roles-antag-liberator-commander-objective = Lead the liberation. Ensure resources are properly allocated and the operation is not at risk.

roles-antag-liberator-agent-name = Liberaton agent
roles-antag-liberator-agent-objective = The liberation efforts finest medic, keep your teammates in the fight.

roles-antag-liberator-name = Liberator
roles-antag-liberator-objective = break into the prison, break others out.
Loading
Loading