Compare commits

...

5 Commits

Author SHA1 Message Date
Ed
00ecd0f766 antag definitions + servant background 2025-09-24 15:59:37 +03:00
Ed
22254734aa lurker hunt trying 2025-09-24 01:13:53 +03:00
Ed
ab47b92ad2 rules can add modifiers to map! 2025-09-23 22:46:46 +03:00
Ed
fe8b2d1122 add storm weather to maps back 2025-09-23 20:41:02 +03:00
Ed
b028d711fe delete outdated game rules 2025-09-23 20:37:17 +03:00
39 changed files with 325 additions and 659 deletions

View File

@@ -169,7 +169,7 @@ public sealed partial class AntagSelectionSystem
return true;
if (def.PrefRoles.Count == 0)
return false;
return true; //CP14 - If definition dont have PrefRoles, everyone can be this antag
var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter;
return pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p));

View File

@@ -393,7 +393,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
if (!antagEnt.HasValue)
{
var getEntEv = new AntagSelectEntityEvent(session, ent);
var getEntEv = new AntagSelectEntityEvent(session, ent, def); //CP14 def added
RaiseLocalEvent(ent, ref getEntEv, true);
antagEnt = getEntEv.Entity;
}
@@ -416,7 +416,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
// Therefore any component subscribing to this has to make sure both subscriptions return the same value
// or the ghost role raffle location preview will be wrong.
var getPosEv = new AntagSelectLocationEvent(session, ent);
var getPosEv = new AntagSelectLocationEvent(session, ent, def); //CP14 def added
RaiseLocalEvent(ent, ref getPosEv, true);
if (getPosEv.Handled)
{
@@ -603,7 +603,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
/// Only raised if the selected player's current entity is invalid.
/// </summary>
[ByRefEvent]
public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule)
public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule, AntagSelectionDefinition? Def = null) //CP14 Definition added
{
public readonly ICommonSession? Session = Session;
@@ -616,7 +616,7 @@ public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<Anta
/// Event raised on a game rule entity to determine the location for the antagonist.
/// </summary>
[ByRefEvent]
public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule)
public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule, AntagSelectionDefinition? Def = null) //CP14 Definition added
{
public readonly ICommonSession? Session = Session;

View File

@@ -73,6 +73,12 @@ public sealed partial class AntagSelectionComponent : Component
[DataDefinition]
public partial struct AntagSelectionDefinition()
{
/// <summary>
/// CP14 - unique keys that allow you to separate logic in AfterAntagEntitySelectedEvent, depending on the key
/// </summary>
[DataField]
public string DefinitionKey = string.Empty;
/// <summary>
/// A list of antagonist roles that are used for selecting which players will be antagonists.
/// </summary>

View File

@@ -1,114 +0,0 @@
using System.Linq;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Antag;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules;
using Content.Server.Popups;
using Content.Server.Station.Components;
using Content.Server.Stunnable;
using Content.Shared._CP14.BloodMoon;
using Content.Shared._CP14.DayCycle;
using Content.Shared.Actions;
using Content.Shared.Examine;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mobs;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14BloodMoonCurseRule : GameRuleSystem<CP14BloodMoonCurseRuleComponent>
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StartDayEvent>(OnStartDay);
SubscribeLocalEvent<CP14BloodMoonCurseRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagEntitySelected);
SubscribeLocalEvent<CP14BloodMoonCurseComponent, ExaminedEvent>(CurseExamined);
}
private void CurseExamined(Entity<CP14BloodMoonCurseComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("cp14-bloodmoon-curse-examined"));
}
private void AfterAntagEntitySelected(Entity<CP14BloodMoonCurseRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
SpawnAttachedTo(ent.Comp.CurseEffect, Transform(args.EntityUid).Coordinates);
var curseComp = EnsureComp<CP14BloodMoonCurseComponent>(args.EntityUid);
var effect = SpawnAttachedTo(curseComp.CurseEffect, Transform(args.EntityUid).Coordinates);
curseComp.SpawnedEffect = effect;
curseComp.CurseRule = ent;
_transform.SetParent(effect, args.EntityUid);
_action.AddAction(args.EntityUid, ref curseComp.ActionEntity, curseComp.Action);
}
protected override void Started(EntityUid uid,
CP14BloodMoonCurseRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(component.StartAnnouncement), colorOverride: component.AnnouncementColor);
_audio.PlayGlobal(component.GlobalSound, allPlayersInGame, true);
}
protected override void Ended(EntityUid uid,
CP14BloodMoonCurseRuleComponent component,
GameRuleComponent gameRule,
GameRuleEndedEvent args)
{
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(component.EndAnnouncement), colorOverride: component.AnnouncementColor);
var aliveAntags = _antag.GetAliveAntags(uid);
foreach (var antag in aliveAntags)
{
SpawnAttachedTo(component.CurseEffect, Transform(antag).Coordinates);
ClearCurse(antag);
}
GameTicker.EndRound();
}
private void OnStartDay(CP14StartDayEvent ev)
{
if (!HasComp<BecomesStationComponent>(ev.Map))
return;
var query = QueryActiveRules();
while (query.MoveNext(out var uid, out _, out var comp, out _))
{
ForceEndSelf(uid);
}
}
private void ClearCurse(Entity<CP14BloodMoonCurseComponent?> ent)
{
if (!Resolve(ent.Owner, ref ent.Comp, false))
return;
_stun.TryUpdateParalyzeDuration(ent, ent.Comp.EndStunDuration);
_popup.PopupEntity(Loc.GetString("cp14-bloodmoon-curse-removed"), ent, PopupType.SmallCaution);
if (TryComp<CP14BloodMoonCurseComponent>(ent, out var curseComp))
{
QueueDel(curseComp.SpawnedEffect);
RemCompDeferred<CP14BloodMoonCurseComponent>(ent);
}
_action.RemoveAction(ent.Comp.ActionEntity);
}
}

View File

@@ -1,54 +0,0 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Events;
using Content.Shared._CP14.DayCycle;
using Content.Shared.GameTicking.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14BloodMoonRule : GameRuleSystem<CP14BloodMoonRuleComponent>
{
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StartNightEvent>(OnStartNight);
}
protected override void Started(EntityUid uid,
CP14BloodMoonRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(
allPlayersInGame,
message: Loc.GetString(component.StartAnnouncement),
colorOverride: component.AnnouncementColor);
_audio.PlayGlobal(component.AnnounceSound, allPlayersInGame, true);
}
private void OnStartNight(CP14StartNightEvent ev)
{
if (!HasComp<BecomesStationComponent>(ev.Map))
return;
var query = QueryActiveRules();
while (query.MoveNext(out var uid, out _, out var comp, out _))
{
var ruleEnt = GameTicker.AddGameRule(comp.CurseRule);
GameTicker.StartGameRule(ruleEnt);
ForceEndSelf(uid);
}
}
}

View File

@@ -1,112 +0,0 @@
using System.Linq;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.GameTicking.Rules;
using Content.Server.Shuttles.Events;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.GameTicking.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14CrashingShipRule : GameRuleSystem<CP14CrashingShipRuleComponent>
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private ISawmill _sawmill = default!;
public override void Initialize()
{
base.Initialize();
_sawmill = _logManager.GetSawmill("cp14_crashing_ship_rule");
SubscribeLocalEvent<CP14CrashingShipComponent, FTLCompletedEvent>(OnFTLCompleted);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateExplosions(frameTime);
}
protected override void Started(EntityUid uid,
CP14CrashingShipRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
var station = _station.GetStations().First();
var largestStationGrid = _station.GetLargestGrid(station);
if (largestStationGrid is null)
{
_sawmill.Error($"Station {station} does not have a grid.");
return;
}
component.StartExplosionTime += _timing.CurTime;
component.Ship = largestStationGrid.Value;
}
private void OnFTLCompleted(Entity<CP14CrashingShipComponent> ent, ref FTLCompletedEvent args)
{
SpawnRandomExplosion(ent, ent.Comp.FinalExplosionProto, 10);
RemCompDeferred<CP14CrashingShipComponent>(ent);
}
private void UpdateExplosions(float frameTime)
{
var ruleQuery = EntityQueryEnumerator<CP14CrashingShipRuleComponent>();
while (ruleQuery.MoveNext(out var uid, out var rule))
{
if (!rule.PendingExplosions)
continue;
if (_timing.CurTime < rule.StartExplosionTime)
continue;
if (rule.Ship is null)
continue;
AddComp<CP14CrashingShipComponent>(rule.Ship.Value);
rule.PendingExplosions = false;
}
var query = EntityQueryEnumerator<CP14CrashingShipComponent>();
while (query.MoveNext(out var uid, out var ship))
{
if (_timing.CurTime < ship.NextExplosionTime)
continue;
ship.NextExplosionTime = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(2, 10));
SpawnRandomExplosion((uid, ship), ship.ExplosionProto, 1);
}
}
private void SpawnRandomExplosion(Entity<CP14CrashingShipComponent> grid, EntProtoId explosionProto, int count)
{
var station = _station.GetOwningStation(grid);
if (station is null)
return;
TryFindRandomTileOnStation((station.Value, Comp<StationDataComponent>(station.Value)),
out var tile,
out var targetGrid,
out var targetCoords);
for (var i = 0; i < count; i++)
{
Spawn(explosionProto, targetCoords);
}
}
}

View File

@@ -1,54 +0,0 @@
using System.Linq;
using System.Numerics;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server._CP14.Procedural;
using Content.Server.GameTicking.Rules;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Systems;
using Content.Shared.GameTicking.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Map;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14ExpeditionToWindlandsRule : GameRuleSystem<CP14ExpeditionToWindlandsRuleComponent>
{
[Dependency] private readonly ShuttleSystem _shuttles = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly CP14LocationGenerationSystem _generation = default!;
private ISawmill _sawmill = default!;
public override void Initialize()
{
base.Initialize();
_sawmill = _logManager.GetSawmill("cp14_expedition_to_windlands_rule");
}
protected override void Started(EntityUid uid,
CP14ExpeditionToWindlandsRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
var station = _station.GetStations().First();
var largestStationGrid = _station.GetLargestGrid(station);
if (largestStationGrid is null)
{
_sawmill.Error($"Station {station} does not have a grid.");
return;
}
EnsureComp<ShuttleComponent>(largestStationGrid.Value, out var shuttleComp);
var windlands = _mapSystem.CreateMap(out var mapId, runMapInit: true);
_generation.GenerateLocation(windlands, mapId, component.Location, component.Modifiers);
_shuttles.FTLToCoordinates(largestStationGrid.Value, shuttleComp, new EntityCoordinates(windlands, Vector2.Zero), 0f, 0f, component.FloatingTime);
}
}

View File

@@ -0,0 +1,91 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Antag;
using Content.Server.GameTicking.Rules;
using Content.Shared.GameTicking.Components;
using Content.Shared.Objectives.Systems;
using Content.Shared.Players;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14LurkerHuntRule : GameRuleSystem<CP14LurkerHuntRuleComponent>
{
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private const string LurkerDefinitionKey = "Lurker";
private const string HunterDefinitionKey = "Hunter";
private const string VictimDefinitionKey = "Victim";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14LurkerHuntRuleComponent, AfterAntagEntitySelectedEvent>(AfterRoleSelected);
SubscribeLocalEvent<CP14LurkerHuntRuleComponent, AntagSelectLocationEvent>(OnAntagSelectLocation);
SubscribeLocalEvent<CP14LurkerHuntRuleComponent, AntagSelectEntityEvent>(OnAntagSelectEntity);
}
private void OnAntagSelectEntity(Entity<CP14LurkerHuntRuleComponent> ent, ref AntagSelectEntityEvent args)
{
if (args.Handled)
return;
if (args.Def is null)
return;
if (args.Def.Value.DefinitionKey == LurkerDefinitionKey)
{
args.Entity = Spawn(ent.Comp.LurkerProto);
}
}
private void OnAntagSelectLocation(Entity<CP14LurkerHuntRuleComponent> ent, ref AntagSelectLocationEvent args)
{
if (args.Def is null)
return;
//Spawn lurker on random position on the map
if (args.Def.Value.DefinitionKey == LurkerDefinitionKey && TryFindRandomTile(out _, out _, out _, out var coords))
{
args.Coordinates.Add(_transform.ToMapCoordinates(coords));
}
}
private void AfterRoleSelected(Entity<CP14LurkerHuntRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
if (args.Session is null)
return;
var mind = args.Session.GetMind();
if (mind is null)
return;
switch (args.Def.DefinitionKey)
{
case LurkerDefinitionKey:
ent.Comp.Lurker = mind;
break;
case VictimDefinitionKey:
ent.Comp.Victims.Add(mind.Value);
break;
case HunterDefinitionKey:
ent.Comp.Hunters.Add(mind.Value);
break;
}
}
protected override void Added(EntityUid uid, CP14LurkerHuntRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
}
protected override void Started(EntityUid uid,
CP14LurkerHuntRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
}
}

View File

@@ -0,0 +1,36 @@
using Content.Server._CP14.Demiplane;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server._CP14.Procedural;
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14StationAdditionalModifierRule : GameRuleSystem<CP14StationAdditionalModifierRuleComponent>
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14BeforeStationLocationGenerationEvent>(OnBeforeStationLocationGeneration);
SubscribeLocalEvent<BecomesStationComponent, CP14LocationGeneratedEvent>(OnFinishMapGeneration);
}
private void OnFinishMapGeneration(Entity<BecomesStationComponent> ent, ref CP14LocationGeneratedEvent args)
{
var query = QueryAllRules();
while (query.MoveNext(out var uid, out var rule, out var gameRule))
{
}
}
private void OnBeforeStationLocationGeneration(CP14BeforeStationLocationGenerationEvent ev)
{
var query = QueryAllRules();
while (query.MoveNext(out var uid, out var rule, out var gameRule))
{
ev.AddModifiers(rule.Modifiers);
}
}
}

View File

@@ -1,58 +0,0 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server._CP14.WeatherControl;
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.StationEvents.Events;
using Content.Server.Weather;
using Content.Shared.GameTicking.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14WeatherRule : StationEventSystem<CP14WeatherRuleComponent>
{
[Dependency] private readonly WeatherSystem _weather = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IGameTiming _timing = default!;
protected override void Started(EntityUid uid, CP14WeatherRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
var query = EntityQueryEnumerator<MapComponent, BecomesStationComponent>();
while (query.MoveNext(out var mapUid, out var map, out var station))
{
if (!_proto.TryIndex(component.Weather, out var indexedWeather))
continue;
if (TryComp<CP14WeatherControllerComponent>(mapUid, out var controller))
{
controller.Enabled = false;
}
_weather.SetWeather(map.MapId, indexedWeather, null);
}
}
protected override void Ended(EntityUid uid, CP14WeatherRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
{
base.Ended(uid, component, gameRule, args);
var query = EntityQueryEnumerator<MapComponent, BecomesStationComponent>();
while (query.MoveNext(out var mapUid, out var map, out var station))
{
if (TryComp<CP14WeatherControllerComponent>(mapUid, out var controller))
{
controller.NextWeatherTime = _timing.CurTime;
controller.Enabled = true;
}
_weather.SetWeather(map.MapId, null, null);
}
}
}

View File

@@ -1,26 +0,0 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14BloodMoonCurseRule))]
public sealed partial class CP14BloodMoonCurseRuleComponent : Component
{
[DataField]
public LocId StartAnnouncement = "cp14-bloodmoon-start";
[DataField]
public LocId EndAnnouncement = "cp14-bloodmoon-end";
[DataField]
public Color? AnnouncementColor;
[DataField]
public EntProtoId CurseEffect = "CP14ImpactEffectMagicSplitting";
[DataField]
public SoundSpecifier GlobalSound = new SoundPathSpecifier("/Audio/_CP14/Ambience/blood_moon_raise.ogg")
{
Params = AudioParams.Default.WithVolume(-2f)
};
}

View File

@@ -1,20 +0,0 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14BloodMoonRule))]
public sealed partial class CP14BloodMoonRuleComponent : Component
{
[DataField]
public EntProtoId CurseRule = "CP14BloodMoonCurseRule";
[DataField]
public LocId StartAnnouncement = "cp14-bloodmoon-raising";
[DataField]
public Color? AnnouncementColor = Color.FromHex("#e32759");
[DataField]
public SoundSpecifier? AnnounceSound;
}

View File

@@ -1,19 +0,0 @@
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
/// <summary>
///When attached to shuttle, start firebombing it until FTL ends.
/// </summary>
[RegisterComponent, Access(typeof(CP14CrashingShipRule))]
public sealed partial class CP14CrashingShipComponent : Component
{
[DataField]
public TimeSpan NextExplosionTime = TimeSpan.Zero;
[DataField]
public EntProtoId ExplosionProto = "CP14ShipExplosion";
[DataField]
public EntProtoId FinalExplosionProto = "CP14ShipExplosionBig";
}

View File

@@ -1,17 +0,0 @@
namespace Content.Server._CP14.GameTicking.Rules.Components;
/// <summary>
/// A rule that assigns common goals to different roles. Common objectives are generated once at the beginning of a round and are shared between players.
/// </summary>
[RegisterComponent, Access(typeof(CP14CrashingShipRule))]
public sealed partial class CP14CrashingShipRuleComponent : Component
{
[DataField]
public EntityUid? Ship;
[DataField]
public bool PendingExplosions = true;
[DataField]
public TimeSpan StartExplosionTime = TimeSpan.FromMinutes(1);
}

View File

@@ -1,20 +0,0 @@
using Content.Shared._CP14.Procedural.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
/// <summary>
/// A rule that assigns common goals to different roles. Common objectives are generated once at the beginning of a round and are shared between players.
/// </summary>
[RegisterComponent, Access(typeof(CP14ExpeditionToWindlandsRule))]
public sealed partial class CP14ExpeditionToWindlandsRuleComponent : Component
{
[DataField]
public ProtoId<CP14ProceduralLocationPrototype> Location = "T1GrasslandIsland";
[DataField]
public List<ProtoId<CP14ProceduralModifierPrototype>> Modifiers = [];
[DataField]
public float FloatingTime = 120;
}

View File

@@ -0,0 +1,19 @@
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14LurkerHuntRule))]
public sealed partial class CP14LurkerHuntRuleComponent : Component
{
[DataField]
public EntProtoId LurkerProto = "CP14MobLurker";
[DataField]
public EntityUid? Lurker;
[DataField]
public HashSet<EntityUid> Hunters = new();
[DataField]
public HashSet<EntityUid> Victims = new();
}

View File

@@ -0,0 +1,11 @@
using Content.Shared._CP14.Procedural.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14StationAdditionalModifierRule))]
public sealed partial class CP14StationAdditionalModifierRuleComponent : Component
{
[DataField(required: true)]
public List<ProtoId<CP14ProceduralModifierPrototype>> Modifiers;
}

View File

@@ -1,11 +0,0 @@
using Content.Shared.Weather;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent]
public sealed partial class CP14WeatherRuleComponent : Component
{
[DataField(required: true)]
public ProtoId<WeatherPrototype> Weather = default!;
}

View File

@@ -49,9 +49,12 @@ public sealed class CP14LocationGenerationSystem : EntitySystem
return;
}
var ev = new CP14BeforeStationLocationGenerationEvent(ent.Comp.Modifiers);
RaiseLocalEvent(ent, ev, true);
var mapId = _transform.GetMapId(largestStationGrid.Value);
GenerateLocation(largestStationGrid.Value, mapId, ent.Comp.Location, ent.Comp.Modifiers);
GenerateLocation(largestStationGrid.Value, mapId, ent.Comp.Location, ev.Modifiers);
}
public override void Update(float frameTime)
@@ -121,3 +124,16 @@ public sealed class CP14LocationGenerationSystem : EntitySystem
}
}
}
/// <summary>
/// Called before procedural location generation on the main map so that other systems can edit modifiers added to the map.
/// </summary>
public sealed class CP14BeforeStationLocationGenerationEvent(List<ProtoId<CP14ProceduralModifierPrototype>> modifiers) : EntityEventArgs
{
public List<ProtoId<CP14ProceduralModifierPrototype>> Modifiers = modifiers;
public void AddModifiers(IEnumerable<ProtoId<CP14ProceduralModifierPrototype>> toAdd)
{
Modifiers.AddRange(toAdd);
}
}

View File

@@ -6,7 +6,7 @@ namespace Content.Server._CP14.WeatherControl;
/// <summary>
/// is the controller that hangs on the prototype map. It regulates which weather rules are run and where they are run.
/// </summary>
[RegisterComponent, AutoGenerateComponentPause, Access(typeof(CP14WeatherControllerSystem), typeof(CP14WeatherRule))]
[RegisterComponent, AutoGenerateComponentPause, Access(typeof(CP14WeatherControllerSystem))]
public sealed partial class CP14WeatherControllerComponent : Component
{
[DataField]

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.Background;
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14BackgroundMerkasServantComponent : Component
{
}

View File

@@ -1,6 +0,0 @@
cp14-bloodmoon-raising = The moon in the sky is stained with blood. The next night will be terrible.
cp14-bloodmoon-start = The blood moon shines in full force, enslaving minds.
cp14-bloodmoon-end = The blood moon sets over the horizon, losing its power.
cp14-bloodmoon-curse-removed = The curse of the blood moon is dispelled.
cp14-bloodmoon-curse-examined = [color=red]The aura of the blood moon hovers around this creature. Be careful, for his mind is unstable.[/color]

View File

@@ -0,0 +1,3 @@
cp14-lurker-briefing = In the past, you made a pact with Darkness for power and influence. Now it demands its payment. You must reap the harvest from those chosen by Lady Darkness. Use your Lurker form, to turn pitiful mortals into obedient servants of darkness.
cp14-lurker-victim-briefing = Shadows gather around you. Whispers call your name in the darkness. Dark entities are out hunting, and you are their prey. What has attracted their attention, that Darkness itself reaches out to embrace you?
cp14-lurker-hunter-briefing = Your duty as a servant of Merkas has brought you to these lands, where Darkness has set its sights on mortals. Protect the people by destroying the cursed creatures lurking in the darkness.

View File

@@ -22,3 +22,8 @@ cp14-trait-snoring-desc = It is simply impossible to sleep next to you because y
cp14-trait-mana-wasting-name = Magical mediocrity.
cp14-trait-mana-wasting-desc = Fate has decreed that magic is just an empty sound for you. You are unable to store or use magical energy.
# Backgrounds
cp14-trait-bg-merkas-servant-name = Servant of the Light Merkas
cp14-trait-bg-merkas-servant-desc = Your past is closely tied to serving Merkas, the god of light, and his dogma is still strong in your mind. You feel compelled to help those in need, banish darkness, and bring light to the hearts of those around you.

View File

@@ -1,6 +0,0 @@
cp14-bloodmoon-raising = Луна на небосводе обагривается кровью. Следующая ночь будет ужасной.
cp14-bloodmoon-start = Кровавая луна сияет в полную силу, порабощая разумы.
cp14-bloodmoon-end = Кровавая луна заходит за горизонт, теряя свою силу.
cp14-bloodmoon-curse-removed = Проклятье кровавой луны развеивается.
cp14-bloodmoon-curse-examined = [color=red]Аура кровавой луны витает вокруг этого существа. Будьте осторожны, ибо его разум нестабилен.[/color]

View File

@@ -0,0 +1,3 @@
cp14-lurker-briefing = прошлом вы заключили контракт с Тьмой ради силы и власти. Теперь она требует свою плату. Вы должны собрать жатву с тех, кого укажет госпожа Тьма. Используйте свой теневой облик Таящегося, чтобы обратить жалких смертных в покорных слуг тьмы.
cp14-lurker-victim-briefing = Тени сгущаются рядом с вами. В темноте слышны шепоты, зовущие по имени. Темные сущности выходят на охоту, и их цель — вы. Чем вы привлекли их внимание, что сама Тьма протягивает к вам свои объятия?
cp14-lurker-hunter-briefing = Ваш долг служителя Меркаса привел вас в эти земли, где Тьма устроила свою охоту на смертных. Защитите людей, уничтожив проклятых созданий, таящихся во мраке.

View File

@@ -22,3 +22,8 @@ cp14-trait-snoring-desc = Спать рядом с вами просто нев
cp14-trait-mana-wasting-name = Магическая бездарность
cp14-trait-mana-wasting-desc = Судьба распорядилась так, что магия для вас - лишь пустой звук. Вы не способны ни накапливать, ни использовать магическую энергию.
# Backgrounds
cp14-trait-bg-merkas-servant-name = Служитель светлого Меркаса
cp14-trait-bg-merkas-servant-desc = Ваше прошлое тесно связано с служением Меркасу - богу света, и его догмы все еще сильны в вашем сознании. Вы считаете себя должным помогать нуждающимся, изгонять тьму и нести свет в сердца окружающих.

View File

@@ -66,10 +66,13 @@ entities:
id: Comoss
- type: CP14WeatherController
entries:
- weight: 3
visuals: null
- visuals: null
weight: 3
- visuals: CP14Mist
- visuals: CP14Rain
- visuals: CP14Storm
weight: 0.25
- type: LightCycle
- type: SunShadow
- type: SunShadowCycle

View File

@@ -68,8 +68,8 @@ entities:
- 0
- type: CP14WeatherController
entries:
- weight: 2
visuals: null
- visuals: null
weight: 2
- visuals: CP14Mist
- type: MapGrid
chunks:

View File

@@ -69,12 +69,16 @@ entities:
id: Venicialis
- type: CP14WeatherController
entries:
- weight: 3
visuals: null
- visuals: null
weight: 3
- visuals: CP14Mist
- weight: 2
visuals: CP14SnowLight
- visuals: CP14SnowLight
weight: 2
- visuals: CP14SnowMedium
- visuals: CP14SnowHeavy
weight: 0.5
- visuals: CP14SnowHeavyWithStorm
weight: 0.25
- type: LightCycle
- type: SunShadow
- type: SunShadowCycle

View File

@@ -21,3 +21,23 @@
- state: green
- sprite: _CP14/Mobs/Pets/rat.rsi
state: rat
- type: entity
categories: [ HideSpawnMenu, Spawner ]
parent: BaseAntagSpawner
id: CP14SpawnPointLurker
components:
- type: GhostRole
name: cp14-ghost-role-information-name-lurker
description: cp14-ghost-role-information-description-lurker
rules: cp14-ghost-role-information-rules-demiplane #TODO
mindRoles:
- CP14MindRoleDemiplaneAntag
raffle:
settings: default
- type: Sprite
sprite: Markers/jobs.rsi
layers:
- state: green
- sprite: _CP14/Mobs/Monster/lurker.rsi
state: live

View File

@@ -17,25 +17,3 @@
delay:
min: 40
max: 60
- type: entityTable
id: CP14BasicCalmEventsTable
table: !type:AllSelector
children:
- id: CP14Storm
- type: entity
parent: CP14BaseStationEventLongDelay
id: CP14Storm
components:
- type: StationEvent
startAnnouncement: cp14-event-announcement-storm
startAudio:
collection: CP14LightningFar
earliestStart: 45
minimumPlayers: 10
duration: 180
maxDuration: 600
- type: CP14WeatherRule
weather: CP14Storm

View File

@@ -1,34 +0,0 @@
- type: entity
id: CP14BloodMoonRule
parent: CP14BaseGameRule
components:
- type: CP14BloodMoonRule
curseRule: CP14BloodMoonCurseRule
announceSound:
path: /Audio/_CP14/Announce/darkness_boom.ogg
- type: GameRule
delay:
min: 30
max: 60
- type: entity
id: CP14BloodMoonCurseRule
parent: CP14BaseGameRule
components:
- type: CP14BloodMoonCurseRule
announcementColor: "#e32759"
- type: AntagSelection
definitions:
- prefRoles: [ CP14BloodMoonCursed ]
max: 15
playerRatio: 2
multiAntagSetting: NotExclusive
lateJoinAdditional: true
allowNonHumans: true
mindRoles:
- CP14MindRoleBloodMoonCursed
briefing:
text: cp14-roles-antag-blood-moon-cursed-briefing
color: "#630f24"
sound: "/Audio/_CP14/Announce/darkness_boom_2.ogg"

View File

@@ -6,51 +6,20 @@
- type: GameRule
cP14Allowed: true
- type: entity
id: CP14RoundObjectivesRule
parent: CP14BaseGameRule
components:
- type: CP14PersonalObjectivesRule
roleObjectives:
CP14Adventurer:
- CP14PersonalCurrencyCollectObjectiveGroup
CP14Alchemist:
- CP14PersonalCurrencyCollectObjectiveGroup
CP14Apprentice:
- CP14PersonalCurrencyCollectObjectiveGroup
CP14Blacksmith:
- CP14PersonalCurrencyCollectObjectiveGroup
CP14Innkeeper:
- CP14PersonalCurrencyCollectObjectiveGroup
# crashing
#- type: entity
# id: CP14CrashToWindlandsRule
# parent: CP14BaseGameRule
# components:
# - type: CP14CrashingShipRule
# - type: CP14ExpeditionToWindlandsRule
# modifiers:
# - WeatherInfinityStorm
# - MapLightCycleDefault
# - Ruins
# - Geodes
# event schedulers
- type: entity
id: CP14BasicStationEventScheduler
parent: CP14BaseGameRule
components:
- type: BasicStationEventScheduler
scheduledGameRules: !type:NestedSelector
tableId: CP14BasicGameRulesTable
- type: entityTable
id: CP14BasicGameRulesTable
table: !type:AllSelector
children:
- !type:NestedSelector
tableId: CP14BasicCalmEventsTable
#- type: entity
# id: CP14BasicStationEventScheduler
# parent: CP14BaseGameRule
# components:
# - type: BasicStationEventScheduler
# scheduledGameRules: !type:NestedSelector
# tableId: CP14BasicGameRulesTable
#
#- type: entityTable
# id: CP14BasicGameRulesTable
# table: !type:AllSelector
# children:
# - !type:NestedSelector
# tableId: CP14BasicCalmEventsTable

View File

@@ -0,0 +1,42 @@
- type: entity
id: CP14LurkerHuntRule
parent: CP14BaseGameRule
components:
- type: CP14StationAdditionalModifierRule
modifiers:
- Geodes
- type: CP14LurkerHuntRule
- type: GameRule
minPlayers: 7
- type: AntagSelection
selectionTime: PrePlayerSpawn
definitions:
- definitionKey: Lurker
spawnerPrototype: CP14SpawnPointLurker
min: 1
max: 1
blacklist:
components:
- CP14BackgroundMerkasServant
briefing:
text: cp14-lurker-briefing
- definitionKey: Victim
lateJoinAdditional: true
min: 1
max: 3
playerRatio: 3
blacklist:
components:
- CP14BackgroundMerkasServant
briefing:
text: cp14-lurker-victim-briefing
- definitionKey: Hunter
lateJoinAdditional: true
min: 1
max: 3
playerRatio: 3
whitelist:
components:
- CP14BackgroundMerkasServant
briefing:
text: cp14-lurker-hunter-briefing

View File

@@ -0,0 +1,8 @@
- type: trait
id: CP14BackgroundMerkasServant
name: cp14-trait-bg-merkas-servant-name
description: cp14-trait-bg-merkas-servant-desc
category: CP14Background
cost: 1
components:
- type: CP14BackgroundMerkasServant

View File

@@ -1,7 +1,7 @@
- type: traitCategory
id: CP14PhysicalTraits
name: cp14-trait-category-physical
maxTraitPoints: 1
maxTraitPoints: 2
- type: traitCategory
id: CP14Background

View File

@@ -6,7 +6,7 @@
description: cp14-trait-blindness-desc
#traitGear: WhiteCane
category: CP14PhysicalTraits
cost: -2
cost: 2
whitelist:
components:
- Blindable
@@ -18,7 +18,7 @@
name: cp14-trait-mana-wasting-name
description: cp14-trait-mana-wasting-desc
category: CP14PhysicalTraits
cost: -2
cost: 2
whitelist:
components:
- CP14MagicEnergyContainer
@@ -38,7 +38,7 @@
description: cp14-trait-poor-vision-desc
#traitGear: ClothingEyesGlasses
category: CP14PhysicalTraits
cost: -1
cost: 1
whitelist:
components:
- Blindable
@@ -51,7 +51,7 @@
name: cp14-trait-narcolepsy-name
description: cp14-trait-narcolepsy-desc
category: CP14PhysicalTraits
cost: -1
cost: 1
components:
- type: Narcolepsy
timeBetweenIncidents: 300, 600
@@ -61,7 +61,7 @@
id: CP14Muted
name: cp14-trait-muted-name
description: cp14-trait-muted-desc
cost: -1
cost: 1
category: CP14PhysicalTraits
components:
- type: Muted
@@ -70,7 +70,7 @@
id: CP14PainNumbness
name: trait-painnumbness-name
description: trait-painnumbness-desc
cost: -1
cost: 1
category: CP14PhysicalTraits
components:
- type: PainNumbness

View File

@@ -19,8 +19,9 @@
showInVote: false
cP14Allowed: true
rules:
- CP14BasicStationEventScheduler
#- CP14BasicStationEventScheduler
- Sandbox
- CP14LurkerHuntRule
- type: gamePreset
id: CP14Peaceful
@@ -28,8 +29,8 @@
description: cp14-peaceful-description
showInVote: true
cP14Allowed: true
rules:
- CP14BasicStationEventScheduler
#rules:
# - CP14BasicStationEventScheduler
- type: gamePreset
id: CP14VampireClansBattleDouble
@@ -40,7 +41,7 @@
minPlayers: 20
maxPlayers: 29
rules:
- CP14BasicStationEventScheduler
#- CP14BasicStationEventScheduler
- CP14GameRuleVampireClanBattleDouble
- type: gamePreset
@@ -51,16 +52,5 @@
cP14Allowed: true
minPlayers: 30
rules:
- CP14BasicStationEventScheduler
#- CP14BasicStationEventScheduler
- CP14GameRuleVampireClanBattleTriple
#- type: gamePreset
# id: CP14Survival
# name: cp14-gamemode-survival-title
# description: cp14-gamemode-survival-description
# showInVote: true
# cP14Allowed: true
# supportedMaps: ShipMapPool
# rules:
# - Sandbox
# - CP14CrashToWindlandsRule