Skip to content

Commit

Permalink
Spider queen antag v0.2 (#1789)
Browse files Browse the repository at this point in the history
* Spider queen antag v0.2

* move actions to unique component

* add mana system

* Add cocooning action

* Actions optimization and delete needless implants

* some optimization

* add mana generation bonus from cocoons

* Add game rule and some fixes

* add cocoons sprites and some changes

* some fixes...

* Game Rule fixes

* Add cocoons objective

* Add survive objective and fixes

* Add mana information into character briefing

* Add station announcement

* fix greentext

* Balance changes and some fixes

* Create cocoons conditoin fixes

* yaml fixes

* Add bonus to MaxMana from cocoons

* Review fixes

* Add doAfter to SpiderWorldSpawnEvent, add wallspawn animation and some
fixes

* ops...

* Cocoons bonus change

* Some fixes

* Some fixes 2

* Rename mana and some changes

* Some fixes

* Tag fix

* Add hunger conversion

* Mother of balance

* some fixes

* change actions description and some changes

* some changes

* yaml fixes

* fix cocoons damage per second

* balance changes

* add alert for blood points and some fixes

* add alert name and desc

* post-review fixes

* forgot add to BasicAntagEventsTable

* some optimization

* post-tests fixes

* delete needless usings

* delete needless using and dependency

* delete needless comment
  • Loading branch information
Kirus59 authored Oct 10, 2024
1 parent 8a3e4ba commit e6fb001
Show file tree
Hide file tree
Showing 80 changed files with 2,048 additions and 434 deletions.
3 changes: 3 additions & 0 deletions Content.Server/Roles/RoleSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Server.SS220.Roles;
using Content.Shared.Roles;
using Content.Shared.SS220.DarkReaper;
using Content.Shared.SS220.MindSlave;
Expand All @@ -24,6 +25,8 @@ public override void Initialize()
SubscribeAntagEvents<MindSlaveRoleComponent>();
//SS220 DarkReaper
SubscribeAntagEvents<DarkReaperRoleComponent>();
//SS220 Spider queen
SubscribeAntagEvents<SpiderQueenRoleComponent>();
}

public string? MindGetBriefing(EntityUid? mindId)
Expand Down
7 changes: 7 additions & 0 deletions Content.Server/SS220/Markers/AntagSpawnMarkerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
namespace Content.Server.SS220.Markers;

[RegisterComponent]
public sealed partial class AntagSpawnMarkerComponent : Component
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
namespace Content.Server.SS220.Objectives.Components;

[RegisterComponent]
public sealed partial class CreateCocoonsConditionComponent : Component
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Server.Objectives.Systems;
using Content.Server.SS220.Objectives.Components;
using Content.Server.SS220.Roles;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.SS220.SpiderQueen.Components;

namespace Content.Server.SS220.Objectives.Systems;

public sealed partial class CreateCocoonsConditionSystem : EntitySystem
{
[Dependency] private readonly NumberObjectiveSystem _number = default!;

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

SubscribeLocalEvent<CreateCocoonsConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
}

private void OnGetProgress(Entity<CreateCocoonsConditionComponent> ent, ref ObjectiveGetProgressEvent args)
{
args.Progress = GetProgress(args.MindId, args.Mind, _number.GetTarget(ent.Owner));
}

private float GetProgress(EntityUid mindId, MindComponent mind, int target)
{
if (!TryComp<SpiderQueenRoleComponent>(mindId, out var spiderQueenRole))
return 0f;

if (spiderQueenRole.IsCreateCocoonsCompletedOnce)
return 1f;

var mobUid = mind.CurrentEntity;
if (mobUid is null ||
!TryComp<SpiderQueenComponent>(mobUid, out var spiderQueen))
return 0f;

var progress = spiderQueen.CocoonsList.Count >= target
? 1f
: (float)spiderQueen.CocoonsList.Count / (float)target;

if (progress == 1f)
spiderQueenRole.IsCreateCocoonsCompletedOnce = true;

return progress;
}
}
11 changes: 11 additions & 0 deletions Content.Server/SS220/Roles/SpiderQueenRoleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Shared.Roles;

namespace Content.Server.SS220.Roles;

[RegisterComponent, ExclusiveAntagonist]
public sealed partial class SpiderQueenRoleComponent : AntagonistRoleComponent
{
[ViewVariables]
public bool IsCreateCocoonsCompletedOnce = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Shared.Whitelist;

namespace Content.Server.SS220.SpiderQueen.Components;

[RegisterComponent]
public sealed partial class SpiderQueenRuleComponent : Component
{
/// <summary>
/// Spawn on a random entity that passed whitelist.
/// If null - spawn on a random tile.
/// </summary>
[DataField]
public EntityWhitelist? MarkersWhitelist;
}
172 changes: 172 additions & 0 deletions Content.Server/SS220/SpiderQueen/Systems/SpiderCocoonSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.SS220.SpiderQueen;
using Content.Shared.SS220.SpiderQueen.Components;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Robust.Shared.Timing;

namespace Content.Server.SS220.SpiderQueen.Systems;

public sealed partial class SpiderCocoonSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly SpiderQueenSystem _spiderQueen = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly HungerSystem _hunger = default!;

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

SubscribeLocalEvent<SpiderCocoonComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<SpiderCocoonComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<SpiderCocoonComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerb);
SubscribeLocalEvent<SpiderCocoonComponent, CocoonExtractBloodPointsEvent>(OnExtractBloodPoints);
}

public override void Update(float frameTime)
{
base.Update(frameTime);

var query = EntityQueryEnumerator<SpiderCocoonComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (_gameTiming.CurTime < component.NextSecond)
continue;

component.NextSecond = _gameTiming.CurTime + TimeSpan.FromSeconds(1);
if (!_container.TryGetContainer(uid, component.CocoonContainerId, out var container) ||
container.ContainedEntities is not { } entities ||
entities.Count <= 0)
continue;

foreach (var entity in entities)
{
ConvertBloodIntoBloodPoints(uid, component, entity, component.BloodConversionPerSecond);
CauseCocoonDamage(uid, component, entity);
}
}
}

private void OnShutdown(Entity<SpiderCocoonComponent> entity, ref ComponentShutdown args)
{
var (uid, comp) = entity;
if (comp.CocoonOwner is null ||
!TryComp<SpiderQueenComponent>(comp.CocoonOwner, out var queenComponent))
return;

queenComponent.CocoonsList.Remove(uid);
queenComponent.MaxBloodPoints -= entity.Comp.BloodPointsBonus;
}

private void OnExamine(Entity<SpiderCocoonComponent> entity, ref ExaminedEvent args)
{
if (HasComp<SpiderQueenComponent>(args.Examiner))
{
args.PushMarkup(Loc.GetString("spider-cocoon-blood-points-amount", ("amount", entity.Comp.BloodPointsAmount)));
}
}

private void OnAlternativeVerb(EntityUid uid, SpiderCocoonComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess ||
!TryComp<SpiderQueenComponent>(args.User, out var spiderQueen))
return;

var extractVerb = new AlternativeVerb
{
Text = Loc.GetString("spider-cocoon-extract-blood-points-verb"),
Act = () =>
{
var doAfterEventArgs = new DoAfterArgs(EntityManager,
args.User,
spiderQueen.CocoonExtractTime,
new CocoonExtractBloodPointsEvent(),
uid,
uid)
{
Broadcast = false,
BreakOnDamage = false,
BreakOnMove = true,
NeedHand = false
};

_doAfter.TryStartDoAfter(doAfterEventArgs);
}
};

args.Verbs.Add(extractVerb);
}

private void OnExtractBloodPoints(Entity<SpiderCocoonComponent> entity, ref CocoonExtractBloodPointsEvent args)
{
if (args.Cancelled ||
!TryComp<SpiderQueenComponent>(args.User, out var spiderQueen))
return;

var amountToMax = spiderQueen.MaxBloodPoints - spiderQueen.CurrentBloodPoints;
var extractedValue = MathF.Min((float)amountToMax, (float)entity.Comp.BloodPointsAmount);
entity.Comp.BloodPointsAmount -= extractedValue;
spiderQueen.CurrentBloodPoints += extractedValue;

_hunger.ModifyHunger(args.User, extractedValue * spiderQueen.HungerExtractCoefficient);

Dirty(args.User, spiderQueen);
Dirty(entity);
_spiderQueen.UpdateAlert((args.User, spiderQueen));
}

/// <summary>
/// Converts entity blood into blood points based on the <see cref="SpiderCocoonComponent.BloodConversionCoefficient"/>
/// </summary>
private void ConvertBloodIntoBloodPoints(EntityUid uid, SpiderCocoonComponent component, EntityUid target, FixedPoint2 amount)
{
if (!TryComp<BloodstreamComponent>(target, out var bloodstream) ||
!_solutionContainer.ResolveSolution(target, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution))
return;

var solutionEnt = bloodstream.BloodSolution.Value;
if (solutionEnt.Comp.Solution.Volume <= FixedPoint2.Zero)
return;

_bloodstream.TryModifyBleedAmount(target, -1f, bloodstream);
_solutionContainer.SplitSolution(solutionEnt, amount);
component.BloodPointsAmount += amount * component.BloodConversionCoefficient;
Dirty(uid, component);
}

private void CauseCocoonDamage(EntityUid uid, SpiderCocoonComponent component, EntityUid target)
{
if (!TryComp<DamageableComponent>(target, out var damageable) ||
component.DamagePerSecond is not { } damagePerSecond)
return;

DamageSpecifier causedDamage = new();
foreach (var damage in damagePerSecond.DamageDict)
{
var (type, value) = damage;
if (component.DamageCap.TryGetValue(type, out var cap) &&
damageable.Damage.DamageDict.TryGetValue(type, out var total) &&
total >= cap)
continue;

causedDamage.DamageDict.Add(type, value);
}

_damageable.TryChangeDamage(target, causedDamage, true);
Dirty(uid, component);
}
}
95 changes: 95 additions & 0 deletions Content.Server/SS220/SpiderQueen/Systems/SpiderQueenRuleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Server.Antag;
using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Respawn;
using Content.Server.Roles;
using Content.Server.SS220.Markers;
using Content.Server.SS220.Roles;
using Content.Server.SS220.SpiderQueen.Components;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Whitelist;
using Robust.Shared.Map;

namespace Content.Server.SS220.SpiderQueen.Systems;

public sealed class SpiderQueenRuleSystem : GameRuleSystem<SpiderQueenRuleComponent>
{
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly SpecialRespawnSystem _specialRespawn = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;

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

SubscribeLocalEvent<SpiderQueenRuleComponent, AntagSelectLocationEvent>(OnAntagSelectLocation);
SubscribeLocalEvent<SpiderQueenRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);

SubscribeLocalEvent<SpiderQueenRoleComponent, GetBriefingEvent>(OnGetBriefing);
}

private void OnAntagSelectLocation(Entity<SpiderQueenRuleComponent> ent, ref AntagSelectLocationEvent args)
{
if (args.Handled)
return;

List<MapCoordinates> validCoordinates = new();
var query = EntityQueryEnumerator<AntagSpawnMarkerComponent>();
while (query.MoveNext(out var uid, out _))
{
if (_entityWhitelist.IsWhitelistFail(ent.Comp.MarkersWhitelist, uid) ||
!TryComp<TransformComponent>(uid, out var transform))
continue;

validCoordinates.Add(_transform.ToMapCoordinates(transform.Coordinates));
}

if (validCoordinates.Count > 0)
args.Coordinates = validCoordinates;
else
{
EntityUid? grid = null;
EntityUid? map = null;
foreach (var station in _station.GetStationsSet())
{
if (!TryComp<StationDataComponent>(station, out var data))
continue;

grid = _station.GetLargestGrid(data);
if (!grid.HasValue)
continue;

map = Transform(grid.Value).MapUid;
if (!map.HasValue)
continue;

break;
}

if (grid.HasValue && map.HasValue &&
_specialRespawn.TryFindRandomTile(grid.Value, map.Value, 30, out var randomCoords))
args.Coordinates.Add(_transform.ToMapCoordinates(randomCoords));
}
}

private void AfterEntitySelected(Entity<SpiderQueenRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
var spider = args.EntityUid;
if (!_mind.TryGetMind(spider, out var mindId, out var mind))
return;

var briefing = Loc.GetString("spider-queen-role-greeting");
_antag.SendBriefing(spider, briefing, null, null);
}

private void OnGetBriefing(Entity<SpiderQueenRoleComponent> ent, ref GetBriefingEvent args)
{
var briefing = Loc.GetString("spider-queen-role-greeting");
args.Append(briefing);
}
}
Loading

0 comments on commit e6fb001

Please sign in to comment.