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

Bloodcult #17

Merged
merged 2 commits into from
Jun 20, 2024
Merged
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
27 changes: 27 additions & 0 deletions Content.Client/Goobstation/Cult/CultistSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Content.Shared.Goobstation.Cult;
using Content.Shared.Goobstation.Cult.Components;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;

namespace Content.Client.Goobstation.Cult;

public sealed class CultistSystem : SharedCultistSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<CultistComponent, GetStatusIconsEvent>(GetStatusIcon);
}

private void GetStatusIcon(Entity<CultistComponent> ent, ref GetStatusIconsEvent args)
{
if (HasComp<CultistComponent>(ent))
return;

if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
}
}
18 changes: 18 additions & 0 deletions Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Content.Server.Administration.Commands;
using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Goobstation.GameTicking.Rules.Components;
using Content.Server.Zombies;
using Content.Shared.Administration;
using Content.Shared.Database;
Expand Down Expand Up @@ -30,6 +31,9 @@ public sealed partial class AdminVerbSystem
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultThiefRule = "Thief";

[ValidatePrototypeId<EntityPrototype>]
private const string DefaultCultistRule = "Cult";

[ValidatePrototypeId<StartingGearPrototype>]
private const string PirateGearId = "PirateGear";

Expand Down Expand Up @@ -134,5 +138,19 @@ private void AddAntagVerbs(GetVerbsEvent<Verb> args)
Message = Loc.GetString("admin-verb-make-thief"),
};
args.Verbs.Add(thief);

Verb cultist = new()
{
Text = Loc.GetString("admin-verb-text-make-cultist"),
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Goobstation/Interface/Misc/cultist_icon.rsi"), "Cultist"),
Act = () =>
{
_antag.ForceMakeAntag<CultistRuleComponent>(targetPlayer, DefaultCultistRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-cultist"),
};
args.Verbs.Add(cultist);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using System.Collections.Generic;

namespace Content.Server.Goobstation.GameTicking.Rules.Components;

[RegisterComponent, Access(typeof(CultistRuleSystem))]
public sealed partial class CultistRuleComponent : Component
{
public SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Goobstation/Ambience/Antag/cultist_start.ogg");
public List<EntityUid> Cultists = new();
}
107 changes: 107 additions & 0 deletions Content.Server/Goobstation/GameTicking/Rules/CultistRuleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Content.Server.Antag;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.Goobstation.GameTicking.Rules.Components;
using Content.Server.Mind;
using Content.Server.Roles;
using Content.Server.RoundEnd;
using Content.Shared.GameTicking.Components;
using Content.Shared.Goobstation.Cult.Components;
using Content.Shared.Mind;
using Content.Shared.Mindshield.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
using System.Linq;

namespace Content.Server.Goobstation.GameTicking.Rules;

public sealed partial class CultistRuleSystem : GameRuleSystem<CultistRuleComponent>
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;

public ProtoId<AntagPrototype> CultistPrototypeId = "Cultist";

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<CultistRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
}

protected override void Started(EntityUid uid, CultistRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
}

protected override void ActiveTick(EntityUid uid, CultistRuleComponent component, GameRuleComponent gameRule, float frameTime)
{
base.ActiveTick(uid, component, gameRule, frameTime);
if (NarsieSummoned())
{
_roundEnd.DoRoundEndBehavior(RoundEndBehavior.InstantEnd, TimeSpan.FromMinutes(3));
GameTicker.EndGameRule(uid, gameRule);
}
else if (AllCultistsDead())
{
//_roundEnd.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, TimeSpan.FromMinutes(5));
//GameTicker.EndGameRule(uid, gameRule);
}
}

protected override void AppendRoundEndText(EntityUid uid, CultistRuleComponent component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args)
{
base.AppendRoundEndText(uid, component, gameRule, ref args);

if (NarsieSummoned())
{
args.AddLine(Loc.GetString("cult-roundend-win"));
}
if (AllCultistsDead())
{
args.AddLine(Loc.GetString("cult-roundend-lose"));
}
}

private void AfterEntitySelected(Entity<CultistRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
MakeCultist(args.EntityUid, ent);
}

public void MakeCultist(EntityUid uid, bool force = false)
{
foreach (var q in EntityQuery<CultistRuleComponent>())
MakeCultist(uid, q, force);
}

public bool MakeCultist(EntityUid uid, CultistRuleComponent comp, bool force = false)
{
if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind))
return false;

if (HasComp<MindShieldComponent>(uid) && force)
RemCompDeferred<MindShieldComponent>(uid);

_antag.SendBriefing(uid, "\n" + Loc.GetString("cultist-role-greeting"), Color.DarkRed, comp.BriefingSound);
comp.Cultists.Add(mindId);

_roleSystem.MindAddRole(mindId, new RoleBriefingComponent
{
Briefing = Loc.GetString("cultist-role-briefing")
}, mind, true);

_npcFaction.RemoveFaction(uid, "Nanotrasen", false);
_npcFaction.AddFaction(uid, "Cultist");

return true;
}

public bool NarsieSummoned()
=> EntityQuery<GeometerComponent>().Count() > 0;
public bool AllCultistsDead()
=> EntityQuery<CultistComponent>().Count() == 0;
}
8 changes: 8 additions & 0 deletions Content.Server/Roles/CultistRoleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Roles;

namespace Content.Server.Roles;

[RegisterComponent, ExclusiveAntagonist]
public sealed partial class CultistRoleComponent : AntagonistRoleComponent
{
}
18 changes: 18 additions & 0 deletions Content.Shared/Goobstation/Cult/Components/CultistComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Content.Shared.StatusIcon;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.Goobstation.Cult.Components;

[RegisterComponent, NetworkedComponent, Access(typeof(SharedCultistSystem))]
public sealed partial class CultistComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadWrite)]
public ProtoId<StatusIconPrototype> StatusIcon { get; set; } = "CultistFaction";

[DataField]
public SoundSpecifier StartSound = new SoundPathSpecifier("/Audio/Goobstation/Ambience/Antag/cultist_start.ogg");

public override bool SessionSpecific => true;
}
11 changes: 11 additions & 0 deletions Content.Shared/Goobstation/Cult/Components/GeometerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Goobstation.Cult.Components;

/// <summary>
/// Nar'Sie exclusive component.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class GeometerComponent : Component
{
}
45 changes: 45 additions & 0 deletions Content.Shared/Goobstation/Cult/SharedCultistSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Content.Shared.Antag;
using Content.Shared.Goobstation.Cult.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Mindshield.Components;
using Content.Shared.Popups;
using Content.Shared.Stunnable;

namespace Content.Shared.Goobstation.Cult;

public abstract class SharedCultistSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedStunSystem _sharedStun = default!;

public override void Initialize()
{
base.Initialize();

//SubscribeLocalEvent<MindShieldComponent, MapInitEvent>(MindshieldImplanted);
SubscribeLocalEvent<CultistComponent, ComponentStartup>(DirtyUp);
//SubscribeLocalEvent<ShowAntagIconsComponent, ComponentStartup>(DirtyUp);
}

private void MindshieldImplanted(EntityUid uid, MindShieldComponent comp, MapInitEvent init)
{
if (HasComp<CultistComponent>(uid))
{
var stunTime = TimeSpan.FromSeconds(4);
var name = Identity.Entity(uid, EntityManager);
RemComp<CultistComponent>(uid);
_sharedStun.TryParalyze(uid, stunTime, true);
// rev loc because it kinda fits
_popupSystem.PopupEntity(Loc.GetString("rev-break-control", ("name", name)), uid);
}
}

private void DirtyUp<T>(EntityUid someUid, T someComp, ComponentStartup ev)
{
var revComps = AllEntityQuery<CultistComponent>();
while (revComps.MoveNext(out var uid, out var comp))
{
Dirty(uid, comp);
}
}
}
Binary file not shown.
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/Goobstation/administration/antag.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
admin-verb-make-cultist = Make the target into a Cultist.
admin-verb-text-make-cultist = Make Cultist.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Cult

cult-title = Blood Cult
cult-description = Unfortunately for the station, it looks like Nanotrasen accidentally positioned the station near an undetectable weak point in time and space. Whoops. Who would've known?

cultist-role-greeting =
You are a cultist!
You are tasked with resurrecting the Geometer of Blood, Nar'Sie.
You have a ritual dagger and runed metal in your backpack to get you started.

cultist-role-briefing =
Work with your brethren to summon an avatar of your eldritch goddess!

cult-roundend-win = The cult has succeeded in resurrecting Nar'Sie!

cult-roundend-lose = The cult has fallen apart!
8 changes: 1 addition & 7 deletions Resources/Prototypes/Entities/Mobs/Player/narsie.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,7 @@
bodyType: Dynamic
bodyStatus: InAir
- type: CanMoveInAir
# singulose components
- type: EventHorizon
radius: 5
canBreachContainment: true
- type: GravityWell
baseRadialAcceleration: 6
maxRange: 8
- type: WarpPoint
follow: true
location: Nar'Sie
- type: Geometer # GoobStation - blood cult
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
- type: entity
parent: SheetMetalBase
id: SheetRunedMetal
name: runed metal
description: Mir'ntrath barhah Nar'sie.
suffix: Full
components:
- type: Material
- type: Stack
stackType: RunedMetal
baseLayer: base
layerStates:
- runedmetal
- runedmetal_2
- runedmetal_3
- type: Sprite
sprite: Goobstation/Objects/Materials/Sheets/metal.rsi
state: runedmetal_3
map: ["base"]
- type: Item
heldPrefix: runedmetal
- type: Appearance

- type: entity
parent: SheetRunedMetal
id: SheetRunedMetal10
name: runed metal
description: Mir'ntrath barhah Nar'sie.
suffix: 10
components:
- type: Sprite
state: runedmetal
- type: Stack
stackType: RunedMetal
count: 10

- type: entity
parent: SheetRunedMetal
id: SheetRunedMetal1
name: runed metal
description: Mir'ntrath barhah Nar'sie.
suffix: Single
components:
- type: Sprite
state: runedmetal
- type: Stack
stackType: RunedMetal
count: 1
18 changes: 18 additions & 0 deletions Resources/Prototypes/Goobstation/GameRules/roundstart.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
- type: entity
id: Cult
parent: BaseGameRule
components:
- type: GameRule
minPlayers: 15
- type: CultistRule
- type: AntagSelection
definitions:
- prefRoles: [ Cultist ]
max: 3
playerRatio: 15
startingGear: CultMasterGear
components:
- type: Cultist
mindComponents:
- type: CultistRole
prototype: Cultist
4 changes: 4 additions & 0 deletions Resources/Prototypes/Goobstation/Guidebook/antagonist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- type: guideEntry
id: Cultists
name: guide-entry-cult
text: "/ServerInfo/Guidebook/Antagonist/Cultists.xml"
14 changes: 14 additions & 0 deletions Resources/Prototypes/Goobstation/Roles/Antag/cultist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- type: antag
id: Cultist
name: roles-antag-cultist-name
antagonist: true
setPreference: true
objective: roles-antag-cultist-objective
guides: [ Cultists ]

- type: startingGear
id: CultMasterGear
storage:
back:
- RitualDagger
#- SheetRunedMetal10
Loading
Loading