Skip to content
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
11 changes: 9 additions & 2 deletions Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Content.Server.Station.Systems;
using Content.Shared.GameTicking;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mobs;
using Content.Shared.Points;
using Content.Shared.Storage;
using Robust.Server.GameObjects;
Expand Down Expand Up @@ -57,7 +58,9 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)

_mind.TransferTo(newMind, mob);
_outfitSystem.SetOutfit(mob, dm.Gear);
EnsureComp<KillTrackerComponent>(mob);
// We need to ensure any spawned player has the kill tracker, and that Critical is used for the DeathMatch gamemode.
var killTracker = EnsureComp<KillTrackerComponent>(mob);
killTracker.KillState = MobState.Critical;
_respawn.AddToTracker(ev.Player.UserId, (uid, tracker));

_point.EnsurePlayer(ev.Player.UserId, uid, point);
Expand All @@ -69,12 +72,16 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)

private void OnSpawnComplete(PlayerSpawnCompleteEvent ev)
{
EnsureComp<KillTrackerComponent>(ev.Mob);
var query = EntityQueryEnumerator<DeathMatchRuleComponent, RespawnTrackerComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out _, out var tracker, out var rule))
{
if (!GameTicker.IsGameRuleActive(uid, rule))
continue;

// We need to ensure any spawned player has the kill tracker, and that Critical is used for the DeathMatch gamemode.
var killTracker = EnsureComp<KillTrackerComponent>(ev.Mob);
killTracker.KillState = MobState.Critical;

_respawn.AddToTracker((ev.Mob, null), (uid, tracker));
}
}
Expand Down
6 changes: 3 additions & 3 deletions Content.Server/KillTracking/KillTrackerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ namespace Content.Server.KillTracking;
/// <summary>
/// This is used for entities that track player damage sources and killers.
/// </summary>
[RegisterComponent, Access(typeof(KillTrackingSystem))]
[RegisterComponent]
public sealed partial class KillTrackerComponent : Component
{
/// <summary>
/// The mobstate that registers as a "kill"
/// </summary>
[DataField("killState")]
[DataField]
public MobState KillState = MobState.Critical;

/// <summary>
/// A dictionary of sources and how much damage they've done to this entity over time.
/// </summary>
[DataField("lifetimeDamage")]
[DataField]
public Dictionary<KillSource, FixedPoint2> LifetimeDamage = new();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Content.Server.Objectives.Systems;

namespace Content.Server.Objectives.Components;

/// <summary>
/// Requires that a player doesn't kill more than a set number of characters, or it fails.
/// </summary>
[RegisterComponent, Access(typeof(KillLimitConditionSystem))]
public sealed partial class KillLimitConditionComponent : Component
{
/// <summary>
/// The number of kills that are permissible for this condition; set upon the objective being assigned.
/// </summary>
[DataField]
public HashSet<EntityUid> KillList = new();

/// <summary>
/// The number of kills that are permissible for this condition; set upon the objective being assigned.
/// </summary>
[DataField]
public int PermissibleKillCount;

/// <summary>
/// The minimum roll for permissible kills for this objective.
/// </summary>
[DataField]
public int MinKillCount = 5;

/// <summary>
/// The maximum roll for permissible kills for this objective.
/// </summary>
[DataField]
public int MaxKillCount = 5;

/// <summary>
/// If true, an entity that gets revived will be removed from the kill limit tracker.
/// </summary>
[DataField]
public bool AllowReviving = true;

/// <summary>
/// The title of the objective. Takes the kill limit as input as "limit".
/// </summary>
[DataField]
public LocId ObjectiveTitle = "objective-condition-kill-limit-title";

/// <summary>
/// The title of the objective. Takes the kill limit as input as "limit", and kill count as "value".
/// </summary>
[DataField]
public LocId ObjectiveDescription = "objective-condition-kill-limit-description";
}
72 changes: 72 additions & 0 deletions Content.Server/Objectives/Systems/KillLimitConditionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Content.Server.KillTracking;
using Content.Server.Objectives.Components;
using Content.Shared.Mind;
using Content.Shared.Mobs;
using Content.Shared.Objectives.Components;
using Robust.Shared.Random;

namespace Content.Server.Objectives.Systems;

public sealed class KillLimitConditionSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;

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

SubscribeLocalEvent<KillLimitConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
SubscribeLocalEvent<KillLimitConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
SubscribeLocalEvent<KillLimitConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<KillReportedEvent>(OnKillReported);
}

private void OnAssigned(Entity<KillLimitConditionComponent> condition, ref ObjectiveAssignedEvent args)
{
condition.Comp.PermissibleKillCount = _random.Next(condition.Comp.MinKillCount, condition.Comp.MaxKillCount);
}
private void OnAfterAssign(Entity<KillLimitConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
string title;
title = Loc.GetString(condition.Comp.ObjectiveTitle, ("limit", condition.Comp.PermissibleKillCount));

_metaData.SetEntityName(condition.Owner, title, args.Meta);
}

private void OnGetProgress(Entity<KillLimitConditionComponent> condition, ref ObjectiveGetProgressEvent args)
{
args.Progress = condition.Comp.PermissibleKillCount >= condition.Comp.KillList.Count ? 1f : 0f;

string description;
description = Loc.GetString(condition.Comp.ObjectiveDescription, ("limit", condition.Comp.PermissibleKillCount), ("value", condition.Comp.KillList.Count));
_metaData.SetEntityDescription(condition.Owner, description);
}

/// <summary>
/// Tracks revival of a possible target.
/// </summary>
private void OnMobStateChanged(MobStateChangedEvent ev)
{
if (ev.NewMobState == MobState.Dead)
return;

var query = EntityQueryEnumerator<KillLimitConditionComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.AllowReviving)
comp.KillList.Remove(ev.Target);
}
}

private void OnKillReported(ref KillReportedEvent ev)
{
if (ev.Primary is KillPlayerSource killer)
{
if (_mind.TryGetMind(killer.PlayerId, out var mind) && _mind.TryGetObjectiveComp<KillLimitConditionComponent>(mind.Value.Owner, out var condition, mind.Value.Comp))
condition.KillList.Add(ev.Entity);
}
}
}
2 changes: 2 additions & 0 deletions Content.Server/Zombies/ZombieSystem.Transform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Content.Server.Ghost.Roles.Components;
using Content.Server.Humanoid;
using Content.Server.Inventory;
using Content.Server.KillTracking;
using Content.Server.Mind;
using Content.Server.NPC;
using Content.Server.NPC.HTN;
Expand Down Expand Up @@ -143,6 +144,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null)
RemComp<LegsParalyzedComponent>(target);
RemComp<ComplexInteractionComponent>(target);
RemComp<SentienceTargetComponent>(target);
RemComp<KillTrackerComponent>(target); //A dead person is already dead - maybe worth reapplying if we want to track zombie slaying kills

//funny voice
var accentType = "zombie";
Expand Down
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/objectives/conditions/kill-limit.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
objective-condition-kill-limit-title = Do not permanently kill more than {$limit} people.
objective-condition-kill-limit-description = Ninjas act with honor. You are responsible for {$value} deaths.
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/Mobs/Species/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@
- type: MobPrice
price: 1500 # Kidnapping a living person and selling them for cred is a good move.
deathPenalty: 0.01 # However they really ought to be living and intact, otherwise they're worth 100x less.
- type: KillTracker
killState: Dead
- type: Tag
tags:
- CanPilot
Expand Down
4 changes: 4 additions & 0 deletions Resources/Prototypes/GameRules/events.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
- TerrorObjective
- MassArrestObjective
- NinjaSurviveObjective
- NinjaKillLimitObjective
- type: AntagSelection
agentName: ninja-round-end-agent-name
definitions:
Expand Down Expand Up @@ -262,6 +263,9 @@
- NamesNinjaTitle
- NamesNinja
nameFormat: name-format-ninja
- type: GibOnRoundEnd
preventGibbingObjectives:
- NinjaKillLimitObjective
mindRoles:
- MindRoleNinja
- type: DynamicRuleCost
Expand Down
12 changes: 12 additions & 0 deletions Resources/Prototypes/Objectives/ninja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,15 @@
icon:
sprite: Objects/Weapons/Melee/stunbaton.rsi
state: stunbaton_on

- type: entity
parent: [BaseNinjaObjective]
id: NinjaKillLimitObjective
components:
- type: Objective
icon:
sprite: Objects/Weapons/Melee/energykatana.rsi
state: icon
- type: KillLimitCondition
minKillCount: 3
maxKillCount: 3
Loading