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