diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs
index fb45912482..b3e263a689 100644
--- a/Content.Server/NPC/Systems/NPCSystem.cs
+++ b/Content.Server/NPC/Systems/NPCSystem.cs
@@ -53,7 +53,7 @@ namespace Content.Server.NPC.Systems
private void OnPlayerNPCDetach(EntityUid uid, NPCComponent component, PlayerDetachedEvent args)
{
- if (_mobState.IsIncapacitated(uid) || Deleted(uid))
+ if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid))
return;
WakeNPC(uid, component);
diff --git a/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs b/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs
new file mode 100644
index 0000000000..045b7b444e
--- /dev/null
+++ b/Content.Server/Salvage/Expeditions/SalvageEliminationExpeditionComponent.cs
@@ -0,0 +1,16 @@
+using Content.Shared.Salvage;
+
+namespace Content.Server.Salvage.Expeditions.Structure;
+
+///
+/// Tracks expedition data for
+///
+[RegisterComponent, Access(typeof(SalvageSystem), typeof(SpawnSalvageMissionJob))]
+public sealed class SalvageEliminationExpeditionComponent : Component
+{
+ ///
+ /// List of mobs that need to be killed for the mission to be complete.
+ ///
+ [DataField("megafauna")]
+ public readonly List Megafauna = new();
+}
diff --git a/Content.Server/Salvage/SalvageSystem.Runner.cs b/Content.Server/Salvage/SalvageSystem.Runner.cs
index 9c7cd903bd..bc3e527fe1 100644
--- a/Content.Server/Salvage/SalvageSystem.Runner.cs
+++ b/Content.Server/Salvage/SalvageSystem.Runner.cs
@@ -7,6 +7,7 @@ using Content.Server.Station.Components;
using Content.Shared.Chat;
using Content.Shared.Humanoid;
using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Salvage;
using Content.Shared.Shuttles.Components;
using Robust.Shared.Audio;
@@ -22,6 +23,8 @@ public sealed partial class SalvageSystem
* Handles actively running a salvage expedition.
*/
+ [Dependency] private readonly MobStateSystem _mobState = default!;
+
private void InitializeRunner()
{
SubscribeLocalEvent(OnFTLRequest);
@@ -41,11 +44,15 @@ public sealed partial class SalvageSystem
// TODO: This is terrible but need bluespace harnesses or something.
var query = EntityQueryEnumerator();
- while (query.MoveNext(out var _, out var _, out var mobXform))
+ while (query.MoveNext(out var uid, out var _, out var mobState, out var mobXform))
{
if (mobXform.MapUid != xform.MapUid)
continue;
+ // Don't count unidentified humans (loot) or anyone you murdered so you can still maroon them once dead.
+ if (_mobState.IsDead(uid, mobState))
+ continue;
+
// Okay they're on salvage, so are they on the shuttle.
if (mobXform.GridUid != ev.Uid)
{
@@ -236,5 +243,37 @@ public sealed partial class SalvageSystem
Announce(uid, Loc.GetString("salvage-expedition-completed"));
}
}
+
+ // Elimination missions
+ var eliminationQuery = EntityQueryEnumerator();
+ while (eliminationQuery.MoveNext(out var uid, out var elimination, out var comp))
+ {
+ if (comp.Completed)
+ continue;
+
+ var announce = false;
+
+ for (var i = 0; i < elimination.Megafauna.Count; i++)
+ {
+ var mob = elimination.Megafauna[i];
+
+ if (Deleted(mob) || _mobState.IsDead(mob))
+ {
+ elimination.Megafauna.RemoveSwap(i);
+ announce = true;
+ }
+ }
+
+ if (announce)
+ {
+ Announce(uid, Loc.GetString("salvage-expedition-megafauna-remaining", ("count", elimination.Megafauna.Count)));
+ }
+
+ if (elimination.Megafauna.Count == 0)
+ {
+ comp.Completed = true;
+ Announce(uid, Loc.GetString("salvage-expedition-completed"));
+ }
+ }
}
}
diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs
index 224d04a586..0cc6d6c628 100644
--- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs
+++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs
@@ -197,6 +197,9 @@ public sealed class SpawnSalvageMissionJob : Job
case SalvageMissionType.Destruction:
await SetupStructure(mission, dungeon, mapUid, grid, random);
break;
+ case SalvageMissionType.Elimination:
+ await SetupElimination(mission, dungeon, mapUid, grid, random);
+ break;
default:
throw new NotImplementedException();
}
@@ -337,9 +340,34 @@ public sealed class SpawnSalvageMissionJob : Job
}
}
- private async Task SpawnMobsRandomRooms(SalvageMission mission, Dungeon dungeon, SalvageFactionPrototype faction, MapGridComponent grid, Random random)
+ private async Task SetupElimination(
+ SalvageMission mission,
+ Dungeon dungeon,
+ EntityUid gridUid,
+ MapGridComponent grid,
+ Random random)
{
- var groupSpawns = _salvage.GetSpawnCount(mission.Difficulty);
+ // spawn megafauna in a random place
+ var roomIndex = random.Next(dungeon.Rooms.Count);
+ var room = dungeon.Rooms[roomIndex];
+ var tile = room.Tiles.ElementAt(random.Next(room.Tiles.Count));
+ var position = grid.GridTileToLocal(tile);
+
+ var faction = _prototypeManager.Index(mission.Faction);
+ var prototype = faction.Configs["Megafauna"];
+ var uid = _entManager.SpawnEntity(prototype, position);
+ // not removing ghost role since its 1 megafauna, expect that you won't be able to cheese it.
+ var eliminationComp = _entManager.EnsureComponent(gridUid);
+ eliminationComp.Megafauna.Add(uid);
+
+ // spawn less mobs than usual since there's megafauna to deal with too
+ await SpawnMobsRandomRooms(mission, dungeon, faction, grid, random, 0.5f);
+ }
+
+ private async Task SpawnMobsRandomRooms(SalvageMission mission, Dungeon dungeon, SalvageFactionPrototype faction, MapGridComponent grid, Random random, float scale = 1f)
+ {
+ // scale affects how many groups are spawned, not the size of the groups themselves
+ var groupSpawns = _salvage.GetSpawnCount(mission.Difficulty) * scale;
var groupSum = faction.MobGroups.Sum(o => o.Prob);
for (var i = 0; i < groupSpawns; i++)
diff --git a/Content.Shared/Salvage/SharedSalvageSystem.cs b/Content.Shared/Salvage/SharedSalvageSystem.cs
index 13e05eacdf..2461476bd1 100644
--- a/Content.Shared/Salvage/SharedSalvageSystem.cs
+++ b/Content.Shared/Salvage/SharedSalvageSystem.cs
@@ -33,6 +33,8 @@ public abstract class SharedSalvageSystem : EntitySystem
return Loc.GetString("salvage-expedition-desc-structure",
("count", GetStructureCount(mission.Difficulty)),
("structure", _loc.GetEntityData(proto).Name));
+ case SalvageMissionType.Elimination:
+ return Loc.GetString("salvage-expedition-desc-elimination");
default:
throw new NotImplementedException();
}
@@ -219,6 +221,11 @@ public enum SalvageMissionType : byte
/// Destroy the specified structures in a dungeon.
///
Destruction,
+
+ ///
+ /// Kill a large creature in a dungeon.
+ ///
+ Elimination,
}
[Serializable, NetSerializable]
diff --git a/Resources/Locale/en-US/procedural/expeditions.ftl b/Resources/Locale/en-US/procedural/expeditions.ftl
index bed82b0fb0..81888a3511 100644
--- a/Resources/Locale/en-US/procedural/expeditions.ftl
+++ b/Resources/Locale/en-US/procedural/expeditions.ftl
@@ -4,6 +4,8 @@ salvage-expedition-structure-remaining = {$count ->
*[other] {$count} structures remaining.
}
+salvage-expedition-megafauna-remaining = {$count} megafauna remaining.
+
salvage-expedition-window-title = Salvage expeditions
salvage-expedition-window-difficulty = Difficulty:
salvage-expedition-window-details = Details:
@@ -25,9 +27,11 @@ salvage-expedition-desc-structure = {$count ->
[one] Destroy {$count} {$structure} inside the area.
*[other] Destroy {$count} {$structure}s inside the area.
}
+salvage-expedition-desc-elimination = Kill a large and dangerous creature inside the area.
salvage-expedition-type-Mining = Mining
salvage-expedition-type-Destruction = Destruction
+salvage-expedition-type-Elimination = Elimination
salvage-expedition-difficulty-Minimal = Minimal
salvage-expedition-difficulty-Minor = Minor
diff --git a/Resources/Prototypes/Entities/Structures/Specific/xeno.yml b/Resources/Prototypes/Entities/Structures/Specific/xeno.yml
index a7dbe7bba7..aa1dcf0e11 100644
--- a/Resources/Prototypes/Entities/Structures/Specific/xeno.yml
+++ b/Resources/Prototypes/Entities/Structures/Specific/xeno.yml
@@ -4,47 +4,59 @@
placement:
mode: SnapgridCenter
snap:
- - Wall
+ - Wall
components:
- - type: RangedDamageSound
- soundGroups:
- Brute:
- collection:
- MeatBulletImpact
- soundTypes:
- Heat:
- collection:
- MeatLaserImpact
- - type: Clickable
- - type: InteractionOutline
- - type: Sprite
- netsync: false
- sprite: Structures/Specific/xeno_building.rsi
- layers:
- - state: wardingtower
- - state: wardingtower-unshaded
- shader: unshaded
- - type: Damageable
- damageContainer: Inorganic
- damageModifierSet: Metallic
- - type: Physics
- bodyType: Static
- - type: Fixtures
- fixtures:
- fix1:
- shape:
- !type:PhysShapeAabb
- bounds: "-0.5,-0.5,0.5,0.5"
- mask:
- - FullTileMask
- layer:
- - WallLayer
- density: 1000
- - type: Destructible
- thresholds:
- - trigger:
- !type:DamageTrigger
- damage: 50
- behaviors:
- - !type:DoActsBehavior
- acts: [ "Destruction" ]
+ - type: RangedDamageSound
+ soundGroups:
+ Brute:
+ collection:
+ MeatBulletImpact
+ soundTypes:
+ Heat:
+ collection:
+ MeatLaserImpact
+ - type: Clickable
+ - type: InteractionOutline
+ - type: Sprite
+ sprite: Structures/Specific/xeno_building.rsi
+ layers:
+ - state: wardingtower
+ - state: wardingtower-unshaded
+ shader: unshaded
+ - type: Damageable
+ damageContainer: Inorganic
+ damageModifierSet: Metallic
+ - type: Physics
+ bodyType: Static
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.5,-0.5,0.5,0.5"
+ mask:
+ - FullTileMask
+ layer:
+ - WallLayer
+ density: 1000
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 50
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+
+- type: entity
+ parent: XenoWardingTower
+ id: CarpStatue
+ name: carp statue
+ description: A statue of one of the brave carp that got us where we are today. Made with real teeth!
+ components:
+ - type: Sprite
+ sprite: Structures/Specific/carp_statue.rsi
+ layers:
+ - state: statue
+ - state: unshaded
+ shader: unshaded
diff --git a/Resources/Prototypes/Procedural/biome_markers.yml b/Resources/Prototypes/Procedural/biome_markers.yml
index 2f386f676b..9696e2d65e 100644
--- a/Resources/Prototypes/Procedural/biome_markers.yml
+++ b/Resources/Prototypes/Procedural/biome_markers.yml
@@ -8,6 +8,10 @@
id: Xenos
proto: MobXeno
+- type: biomeMarkerLayer
+ id: Carps
+ proto: MobCarp
+
#- type: biomeMarkerLayer
# id: Experiment
diff --git a/Resources/Prototypes/Procedural/salvage_factions.yml b/Resources/Prototypes/Procedural/salvage_factions.yml
index 06e088ac86..fd4a870cbe 100644
--- a/Resources/Prototypes/Procedural/salvage_factions.yml
+++ b/Resources/Prototypes/Procedural/salvage_factions.yml
@@ -1,16 +1,37 @@
- type: salvageFaction
id: Xenos
groups:
- - entries:
- - id: MobXeno
- amount: 2
- maxAmount: 3
- - id: MobXenoDrone
- amount: 1
- - entries:
- - id: MobXenoRavager
- amount: 1
- prob: 0.1
+ - entries:
+ - id: MobXeno
+ amount: 2
+ maxAmount: 3
+ - id: MobXenoDrone
+ amount: 1
+ - entries:
+ - id: MobXenoRavager
+ amount: 1
+ prob: 0.1
configs:
DefenseStructure: XenoWardingTower
Mining: Xenos
+ Megafauna: MobXenoQueen
+
+- type: salvageFaction
+ id: Carps
+ groups:
+ - entries:
+ - id: MobCarp
+ amount: 1
+ maxAmount: 3
+ - id: MobCarpMagic
+ amount: 1
+ maxAmount: 2
+ - entries:
+ - id: MobCarpHolo
+ amount: 1
+ maxAmount: 3
+ prob: 0.5
+ configs:
+ DefenseStructure: CarpStatue
+ Mining: Carps
+ Megafauna: MobDragon
diff --git a/Resources/Textures/Structures/Specific/carp_statue.rsi/meta.json b/Resources/Textures/Structures/Specific/carp_statue.rsi/meta.json
new file mode 100644
index 0000000000..5c8d12c6c6
--- /dev/null
+++ b/Resources/Textures/Structures/Specific/carp_statue.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+ "license": "CC0-1.0",
+ "copyright": "created by deltanedas (github) for SS14",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "statue"
+ },
+ {
+ "name": "unshaded"
+ }
+ ]
+}
diff --git a/Resources/Textures/Structures/Specific/carp_statue.rsi/statue.png b/Resources/Textures/Structures/Specific/carp_statue.rsi/statue.png
new file mode 100644
index 0000000000..2030067540
Binary files /dev/null and b/Resources/Textures/Structures/Specific/carp_statue.rsi/statue.png differ
diff --git a/Resources/Textures/Structures/Specific/carp_statue.rsi/unshaded.png b/Resources/Textures/Structures/Specific/carp_statue.rsi/unshaded.png
new file mode 100644
index 0000000000..a5ee7e88a9
Binary files /dev/null and b/Resources/Textures/Structures/Specific/carp_statue.rsi/unshaded.png differ