Bläddra i källkod

TDM (#183)

* tdm jobs init

* more job stuff

* missing inhand sprites

* adding other jobs

* sprite fixes, swordsmen and pikemen

* shield icon fix

* crenelated walls

* cobblestone floors

* loadouts

* barricading

* tilefix

* barricades working

* map tweaks

* medieval walls

* lanterns, stove

* mapping

* respawn fixes, localised names, return to lobby button for ghosts, icons for soldiers

* shield and armour weights, faction component

* faction icons, WIP grace wall and capture area systems

* framework for captureareas and gracewall

* capturable and grace wall areas

* gracewall fix, capturing mechanic working, tabards and gambeson

* castle gates, randomised loadouts

* faction spawnpoints, longquan sprite fix (this time for real), more mapping

* fixes mining with swords, arrow blindness

* attribution fix

* yaml fixes
Taislin 7 månader sedan
förälder
incheckning
edc2331315
100 ändrade filer med 3687 tillägg och 225 borttagningar
  1. 5 0
      Content.Client/Ghost/GhostSystem.cs
  2. 17 7
      Content.Client/LateJoin/LateJoinGui.cs
  3. 73 0
      Content.Client/Overlays/ShowFactionIconsSystem.cs
  4. 7 0
      Content.Client/UserInterface/Systems/Ghost/GhostUIController.cs
  5. 7 3
      Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml
  6. 2 0
      Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml.cs
  7. 1 1
      Content.Server/GameTicking/Commands/JoinGameCommand.cs
  8. 69 69
      Content.Server/GameTicking/GameTicker.Player.cs
  9. 6 4
      Content.Server/GameTicking/GameTicker.RoundFlow.cs
  10. 6 3
      Content.Server/GameTicking/GameTicker.Spawning.cs
  11. 118 0
      Content.Server/GameTicking/Rules/CaptureAreaSystem.cs
  12. 54 0
      Content.Server/GameTicking/Rules/Components/CaptureAreaComponent.cs
  13. 48 0
      Content.Server/GameTicking/Rules/Components/GracewallComponent.cs
  14. 142 0
      Content.Server/GameTicking/Rules/GracewallRuleSystem.cs
  15. 20 14
      Content.Server/Ghost/GhostSystem.cs
  16. 1 1
      Content.Server/Jobs/AddComponentSpecial.cs
  17. 8 0
      Content.Server/Spawners/Components/SpawnPointComponent.cs
  18. 12 2
      Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs
  19. 21 3
      Content.Server/Station/Systems/StationSpawningSystem.cs
  20. 35 7
      Content.Shared/Camera/SharedCameraRecoilSystem.cs
  21. 18 0
      Content.Shared/Civ14/Barricade/BarricadeComponent.cs
  22. 8 1
      Content.Shared/Ghost/SharedGhostSystem.cs
  23. 4 0
      Content.Shared/Inventory/InventorySystem.Relay.cs
  24. 39 0
      Content.Shared/Overlays/ShowFactionIconsComponent.cs
  25. 13 2
      Content.Shared/Projectiles/SharedProjectileSystem.cs
  26. 15 0
      Content.Shared/Roles/JobPrototype.cs
  27. 15 0
      Resources/Audio/Announcements/attributions.yml
  28. BIN
      Resources/Audio/Announcements/civ_announcement.ogg
  29. BIN
      Resources/Audio/Announcements/horn.ogg
  30. BIN
      Resources/Audio/Announcements/horn_long.ogg
  31. 8 2
      Resources/ConfigPresets/Build/development.toml
  32. 1 1
      Resources/ConfigPresets/Civ/production.toml
  33. 14 0
      Resources/Locale/en-US/Civ14/jobs-descriptions.ftl
  34. 39 0
      Resources/Locale/en-US/Civ14/jobs.ftl
  35. 1 1
      Resources/Locale/en-US/administration/commands/announce-command.ftl
  36. 1 1
      Resources/Locale/en-US/administration/ui/admin-announce-window.ftl
  37. 1 1
      Resources/Locale/en-US/chat/managers/chat-manager.ftl
  38. 1 1
      Resources/Locale/en-US/communications/communications-console-component.ftl
  39. 3 0
      Resources/Locale/en-US/game-ticking/game-presets/preset-extended.ftl
  40. 2 1
      Resources/Locale/en-US/ghost/ghost-gui.ftl
  41. 2 0
      Resources/Locale/en-US/job/department-desc.ftl
  42. 2 0
      Resources/Locale/en-US/job/department.ftl
  43. 4 2
      Resources/Locale/en-US/late-join/late-join-gui.ftl
  44. 50 0
      Resources/Maps/civ/tdm/camp.yml
  45. 42 11
      Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_accessories.yml
  46. 4 4
      Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_gloves.yml
  47. 15 15
      Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_hats.yml
  48. 4 6
      Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_shoes.yml
  49. 100 15
      Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_suit.yml
  50. 29 0
      Resources/Prototypes/Civ14/Entities/Markers/capturable_areas.yml
  51. 29 0
      Resources/Prototypes/Civ14/Entities/Markers/grace_wall.yml
  52. 27 0
      Resources/Prototypes/Civ14/Entities/Markers/jobs.yml
  53. 9 2
      Resources/Prototypes/Civ14/Entities/Objects/Tools/tools.yml
  54. 7 5
      Resources/Prototypes/Civ14/Entities/Objects/Weapons/spears.yml
  55. 51 17
      Resources/Prototypes/Civ14/Entities/Structures/Craft/shields.yml
  56. 285 0
      Resources/Prototypes/Civ14/Entities/Structures/Walls/barricades.yml
  57. 133 0
      Resources/Prototypes/Civ14/Entities/Structures/Walls/castle_gate.yml
  58. 49 1
      Resources/Prototypes/Civ14/Entities/Structures/Walls/walls.yml
  59. 4 0
      Resources/Prototypes/Civ14/Entities/Structures/Walls/windows.yml
  60. 90 0
      Resources/Prototypes/Civ14/Entities/Structures/warmth.yml
  61. 29 1
      Resources/Prototypes/Civ14/Recipes/Construction/floors.yml
  62. 54 0
      Resources/Prototypes/Civ14/Recipes/Construction/warmth.yml
  63. 29 0
      Resources/Prototypes/Civ14/Recipes/Walls/walls.yml
  64. 629 0
      Resources/Prototypes/Civ14/StatusIcon/faction.yml
  65. 176 0
      Resources/Prototypes/Civ14/StatusIcon/job.yml
  66. 72 0
      Resources/Prototypes/Civ14/Tiles/floors.yml
  67. 33 8
      Resources/Prototypes/Damage/modifier_sets.yml
  68. 9 0
      Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml
  69. 21 1
      Resources/Prototypes/Entities/Objects/Shields/shields.yml
  70. 17 2
      Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml
  71. 3 1
      Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml
  72. 14 0
      Resources/Prototypes/Entities/Stations/nanotrasen.yml
  73. 1 1
      Resources/Prototypes/Entities/Structures/Walls/asteroid.yml
  74. 23 0
      Resources/Prototypes/GameRules/roundstart.yml
  75. 1 0
      Resources/Prototypes/Maps/Pools/default.yml
  76. 31 0
      Resources/Prototypes/Maps/civ/camp.yml
  77. 283 0
      Resources/Prototypes/Roles/Jobs/Civ14/TDM/english.yml
  78. 283 0
      Resources/Prototypes/Roles/Jobs/Civ14/TDM/french.yml
  79. 26 0
      Resources/Prototypes/Roles/Jobs/departments.yml
  80. 45 6
      Resources/Prototypes/StatusIcon/faction.yml
  81. 31 0
      Resources/Prototypes/ai_factions.yml
  82. 13 1
      Resources/Prototypes/game_presets.yml
  83. 1 1
      Resources/Prototypes/round_announcements.yml
  84. BIN
      Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/equipped-OUTERCLOTHING.png
  85. BIN
      Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/gambeson.png
  86. BIN
      Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/icon.png
  87. 23 0
      Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/meta.json
  88. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/customtabard.png
  89. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/equipped-NECK.png
  90. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/icon.png
  91. 23 0
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/meta.json
  92. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/customtabard.png
  93. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/equipped-NECK.png
  94. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/icon.png
  95. 23 0
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/meta.json
  96. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/customtabard.png
  97. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/equipped-NECK.png
  98. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/icon.png
  99. 23 0
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/meta.json
  100. BIN
      Resources/Textures/Civ14/Clothing/exported/ties/tabard_red_yellow.rsi/customtabard.png

+ 5 - 0
Content.Client/Ghost/GhostSystem.cs

@@ -191,6 +191,11 @@ public void ReturnToBody()
             RaiseNetworkEvent(msg);
         }
 
+        public void ReturnToLobby()
+        {
+            var msg = new GhostReturnToLobbyRequest();
+            RaiseNetworkEvent(msg);
+        }
         public void OpenGhostRoles()
         {
             _console.RemoteExecuteCommand(null, "ghostroles");

+ 17 - 7
Content.Client/LateJoin/LateJoinGui.cs

@@ -233,7 +233,7 @@ private void RebuildUI()
                             Margin = new Thickness(5f, 0, 0, 0)
                         };
 
-                        var jobButton = new JobButton(jobLabel, prototype.ID, prototype.LocalizedName, value);
+                        var jobButton = new JobButton(jobLabel, prototype.ID, prototype.LocalizedName, prototype.OriginalName, value);
 
                         var jobSelector = new BoxContainer
                         {
@@ -272,7 +272,7 @@ private void RebuildUI()
                             {
                                 TextureScale = new Vector2(0.4f, 0.4f),
                                 Stretch = TextureRect.StretchMode.KeepCentered,
-                                Texture = _sprites.Frame0(new SpriteSpecifier.Texture(new ("/Textures/Interface/Nano/lock.svg.192dpi.png"))),
+                                Texture = _sprites.Frame0(new SpriteSpecifier.Texture(new("/Textures/Interface/Nano/lock.svg.192dpi.png"))),
                                 HorizontalExpand = true,
                                 HorizontalAlignment = HAlignment.Right,
                             });
@@ -340,14 +340,16 @@ sealed class JobButton : ContainerButton
         public Label JobLabel { get; }
         public string JobId { get; }
         public string JobLocalisedName { get; }
+        public string OriginalName { get; }
         public int? Amount { get; private set; }
         private bool _initialised = false;
 
-        public JobButton(Label jobLabel, ProtoId<JobPrototype> jobId, string jobLocalisedName, int? amount)
+        public JobButton(Label jobLabel, ProtoId<JobPrototype> jobId, string jobLocalisedName, string originalName, int? amount)
         {
             JobLabel = jobLabel;
             JobId = jobId;
             JobLocalisedName = jobLocalisedName;
+            OriginalName = originalName;
             RefreshLabel(amount);
             AddStyleClass(StyleClassButton);
             _initialised = true;
@@ -360,10 +362,18 @@ public void RefreshLabel(int? amount)
                 return;
             }
             Amount = amount;
-
-            JobLabel.Text = Amount != null ?
-                Loc.GetString("late-join-gui-job-slot-capped", ("jobName", JobLocalisedName), ("amount", Amount)) :
-                Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", JobLocalisedName));
+            if (OriginalName != string.Empty)
+            {
+                JobLabel.Text = Amount != null ?
+                    Loc.GetString("late-join-gui-job-slot-capped", ("originalName", OriginalName), ("jobName", JobLocalisedName), ("amount", Amount)) :
+                    Loc.GetString("late-join-gui-job-slot-uncapped", ("originalName", OriginalName), ("jobName", JobLocalisedName));
+            }
+            else
+            {
+                JobLabel.Text = Amount != null ?
+                    Loc.GetString("late-join-gui-job-slot-capped-no-original", ("jobName", JobLocalisedName), ("amount", Amount)) :
+                    Loc.GetString("late-join-gui-job-slot-uncapped-no-original", ("jobName", JobLocalisedName));
+            }
         }
     }
 }

+ 73 - 0
Content.Client/Overlays/ShowFactionIconsSystem.cs

@@ -0,0 +1,73 @@
+using Content.Shared.Access.Components;
+using Content.Shared.Access.Systems;
+using Content.Shared.NPC.Components;
+using Content.Shared.Overlays;
+using System.Linq;
+using Content.Shared.StatusIcon;
+using Content.Shared.StatusIcon.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Overlays;
+
+public sealed class ShowFactionIconsSystem : EquipmentHudSystem<ShowFactionIconsComponent>
+{
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ShowFactionIconsComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
+
+    }
+
+    private void OnGetStatusIconsEvent(EntityUid uid, ShowFactionIconsComponent component, ref GetStatusIconsEvent ev)
+    {
+        if (!IsActive)
+            return;
+
+        if (_prototype.TryIndex<FactionIconPrototype>(component.FactionIcon, out var iconPrototype))
+            ev.StatusIcons.Add(iconPrototype);
+    }
+}
+
+public sealed class ShowFrenchFactionIconsSystem : EquipmentHudSystem<ShowFrenchFactionIconsComponent>
+{
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ShowFrenchFactionIconsComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
+
+    }
+
+    private void OnGetStatusIconsEvent(EntityUid uid, ShowFrenchFactionIconsComponent component, ref GetStatusIconsEvent ev)
+    {
+        if (!IsActive)
+            return;
+
+        if (_prototype.TryIndex<FactionIconPrototype>(component.FactionIcon, out var iconPrototype))
+            ev.StatusIcons.Add(iconPrototype);
+    }
+}
+public sealed class ShowEnglishFactionIconsSystem : EquipmentHudSystem<ShowEnglishFactionIconsComponent>
+{
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ShowEnglishFactionIconsComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
+
+    }
+
+    private void OnGetStatusIconsEvent(EntityUid uid, ShowEnglishFactionIconsComponent component, ref GetStatusIconsEvent ev)
+    {
+        if (!IsActive)
+            return;
+
+        if (_prototype.TryIndex<FactionIconPrototype>(component.FactionIcon, out var iconPrototype))
+            ev.StatusIcons.Add(iconPrototype);
+    }
+}

+ 7 - 0
Content.Client/UserInterface/Systems/Ghost/GhostUIController.cs

@@ -124,6 +124,7 @@ public void LoadGui()
 
         Gui.RequestWarpsPressed += RequestWarps;
         Gui.ReturnToBodyPressed += ReturnToBody;
+        Gui.ReturnToLobbyPressed += ReturnToLobby;
         Gui.GhostRolesPressed += GhostRolesPressed;
         Gui.TargetWindow.WarpClicked += OnWarpClicked;
         Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
@@ -138,6 +139,7 @@ public void UnloadGui()
 
         Gui.RequestWarpsPressed -= RequestWarps;
         Gui.ReturnToBodyPressed -= ReturnToBody;
+        Gui.ReturnToLobbyPressed -= ReturnToLobby;
         Gui.GhostRolesPressed -= GhostRolesPressed;
         Gui.TargetWindow.WarpClicked -= OnWarpClicked;
 
@@ -149,6 +151,11 @@ private void ReturnToBody()
         _system?.ReturnToBody();
     }
 
+    private void ReturnToLobby()
+    {
+        _system?.ReturnToLobby();
+    }
+
     private void RequestWarps()
     {
         _system?.RequestWarps();

+ 7 - 3
Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml

@@ -2,8 +2,12 @@
                   xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Ghost.Widgets"
                   HorizontalAlignment="Center">
     <BoxContainer Orientation="Horizontal">
-        <Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
-        <Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
-        <Button Name="GhostRolesButton" />
+        <Button Name="ReturnToLobbyButton"
+                Text="{Loc ghost-gui-return-to-lobby-button}"/>
+        <Button Name="ReturnToBodyButton"
+                Text="{Loc ghost-gui-return-to-body-button}"/>
+        <Button Name="GhostWarpButton"
+                Text="{Loc ghost-gui-ghost-warp-button}"/>
+        <Button Name="GhostRolesButton"/>
     </BoxContainer>
 </widgets:GhostGui>

+ 2 - 0
Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml.cs

@@ -13,6 +13,7 @@ public sealed partial class GhostGui : UIWidget
 
     public event Action? RequestWarpsPressed;
     public event Action? ReturnToBodyPressed;
+    public event Action? ReturnToLobbyPressed;
     public event Action? GhostRolesPressed;
 
     public GhostGui()
@@ -24,6 +25,7 @@ public GhostGui()
         MouseFilter = MouseFilterMode.Ignore;
 
         GhostWarpButton.OnPressed += _ => RequestWarpsPressed?.Invoke();
+        ReturnToLobbyButton.OnPressed += _ => ReturnToLobbyPressed?.Invoke();
         ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
         GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
     }

+ 1 - 1
Content.Server/GameTicking/Commands/JoinGameCommand.cs

@@ -67,7 +67,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args)
 
                 var station = _entManager.GetEntity(new NetEntity(sid));
                 var jobPrototype = _prototypeManager.Index<JobPrototype>(id);
-                if(stationJobs.TryGetJobSlot(station, jobPrototype, out var slots) == false || slots == 0)
+                if (stationJobs.TryGetJobSlot(station, jobPrototype, out var slots) == false || slots == 0)
                 {
                     shell.WriteLine($"{jobPrototype.LocalizedName} has no available slots.");
                     return;

+ 69 - 69
Content.Server/GameTicking/GameTicker.Player.cs

@@ -44,97 +44,97 @@ private async void PlayerStatusChanged(object? sender, SessionStatusEventArgs ar
             switch (args.NewStatus)
             {
                 case SessionStatus.Connected:
-                {
-                    AddPlayerToDb(args.Session.UserId.UserId);
-
-                    // Always make sure the client has player data.
-                    if (session.Data.ContentDataUncast == null)
                     {
-                        var data = new ContentPlayerData(session.UserId, args.Session.Name);
-                        data.Mind = mindId;
-                        session.Data.ContentDataUncast = data;
-                    }
+                        AddPlayerToDb(args.Session.UserId.UserId);
 
-                    // Make the player actually join the game.
-                    // timer time must be > tick length
-                    Timer.Spawn(0, () => _playerManager.JoinGame(args.Session));
-
-                    var record = await _db.GetPlayerRecordByUserId(args.Session.UserId);
-                    var firstConnection = record != null &&
-                                          Math.Abs((record.FirstSeenTime - record.LastSeenTime).TotalMinutes) < 1;
+                        // Always make sure the client has player data.
+                        if (session.Data.ContentDataUncast == null)
+                        {
+                            var data = new ContentPlayerData(session.UserId, args.Session.Name);
+                            data.Mind = mindId;
+                            session.Data.ContentDataUncast = data;
+                        }
 
-                    _chatManager.SendAdminAnnouncement(firstConnection
-                        ? Loc.GetString("player-first-join-message", ("name", args.Session.Name))
-                        : Loc.GetString("player-join-message", ("name", args.Session.Name)));
+                        // Make the player actually join the game.
+                        // timer time must be > tick length
+                        Timer.Spawn(0, () => _playerManager.JoinGame(args.Session));
 
-                    RaiseNetworkEvent(GetConnectionStatusMsg(), session.Channel);
+                        var record = await _db.GetPlayerRecordByUserId(args.Session.UserId);
+                        var firstConnection = record != null &&
+                                              Math.Abs((record.FirstSeenTime - record.LastSeenTime).TotalMinutes) < 1;
 
-                    if (firstConnection && _cfg.GetCVar(CCVars.AdminNewPlayerJoinSound))
-                        _audio.PlayGlobal(new SoundPathSpecifier("/Audio/Effects/newplayerping.ogg"),
-                            Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false,
-                            audioParams: new AudioParams { Volume = -5f });
+                        _chatManager.SendAdminAnnouncement(firstConnection
+                            ? Loc.GetString("player-first-join-message", ("name", args.Session.Name))
+                            : Loc.GetString("player-join-message", ("name", args.Session.Name)));
 
-                    if (LobbyEnabled && _roundStartCountdownHasNotStartedYetDueToNoPlayers)
-                    {
-                        _roundStartCountdownHasNotStartedYetDueToNoPlayers = false;
-                        _roundStartTime = _gameTiming.CurTime + LobbyDuration;
-                    }
+                        RaiseNetworkEvent(GetConnectionStatusMsg(), session.Channel);
 
-                    break;
-                }
+                        if (firstConnection && _cfg.GetCVar(CCVars.AdminNewPlayerJoinSound))
+                            _audio.PlayGlobal(new SoundPathSpecifier("/Audio/Effects/newplayerping.ogg"),
+                                Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false,
+                                audioParams: new AudioParams { Volume = -5f });
 
-                case SessionStatus.InGame:
-                {
-                    _userDb.ClientConnected(session);
-
-                    if (mind == null)
-                    {
-                        if (LobbyEnabled)
-                            PlayerJoinLobby(session);
-                        else
-                            SpawnWaitDb();
+                        if (LobbyEnabled && _roundStartCountdownHasNotStartedYetDueToNoPlayers)
+                        {
+                            _roundStartCountdownHasNotStartedYetDueToNoPlayers = false;
+                            _roundStartTime = _gameTiming.CurTime + LobbyDuration;
+                        }
 
                         break;
                     }
 
-                    if (mind.CurrentEntity == null || Deleted(mind.CurrentEntity))
+                case SessionStatus.InGame:
                     {
-                        DebugTools.Assert(mind.CurrentEntity == null, "a mind's current entity was deleted without updating the mind");
+                        _userDb.ClientConnected(session);
 
-                        // This player is joining the game with an existing mind, but the mind has no entity.
-                        // Their entity was probably deleted sometime while they were disconnected, or they were an observer.
-                        // Instead of allowing them to spawn in, we will dump and their existing mind in an observer ghost.
-                        SpawnObserverWaitDb();
-                    }
-                    else
-                    {
-                        if (_playerManager.SetAttachedEntity(session, mind.CurrentEntity))
+                        if (mind == null)
                         {
-                            PlayerJoinGame(session);
+                            if (LobbyEnabled)
+                                PlayerJoinLobby(session);
+                            else
+                                SpawnWaitDb();
+
+                            break;
                         }
-                        else
+
+                        if (mind.CurrentEntity == null || Deleted(mind.CurrentEntity))
                         {
-                            Log.Error(
-                                $"Failed to attach player {session} with mind {ToPrettyString(mindId)} to its current entity {ToPrettyString(mind.CurrentEntity)}");
+                            DebugTools.Assert(mind.CurrentEntity == null, "a mind's current entity was deleted without updating the mind");
+
+                            // This player is joining the game with an existing mind, but the mind has no entity.
+                            // Their entity was probably deleted sometime while they were disconnected, or they were an observer.
+                            // Instead of allowing them to spawn in, we will dump and their existing mind in an observer ghost.
                             SpawnObserverWaitDb();
                         }
-                    }
+                        else
+                        {
+                            if (_playerManager.SetAttachedEntity(session, mind.CurrentEntity))
+                            {
+                                PlayerJoinGame(session);
+                            }
+                            else
+                            {
+                                Log.Error(
+                                    $"Failed to attach player {session} with mind {ToPrettyString(mindId)} to its current entity {ToPrettyString(mind.CurrentEntity)}");
+                                SpawnObserverWaitDb();
+                            }
+                        }
 
-                    break;
-                }
+                        break;
+                    }
 
                 case SessionStatus.Disconnected:
-                {
-                    _chatManager.SendAdminAnnouncement(Loc.GetString("player-leave-message", ("name", args.Session.Name)));
-                    if (mind != null)
                     {
-                        _pvsOverride.ClearOverride(GetNetEntity(mindId!.Value));
-                        mind.Session = null;
-                    }
+                        _chatManager.SendAdminAnnouncement(Loc.GetString("player-leave-message", ("name", args.Session.Name)));
+                        if (mind != null)
+                        {
+                            _pvsOverride.ClearOverride(GetNetEntity(mindId!.Value));
+                            mind.Session = null;
+                        }
 
-                    _userDb.ClientDisconnected(session);
-                    break;
-                }
+                        _userDb.ClientDisconnected(session);
+                        break;
+                    }
             }
             //When the status of a player changes, update the server info text
             UpdateInfoText();
@@ -182,7 +182,7 @@ async void AddPlayerToDb(Guid id)
 
         public HumanoidCharacterProfile GetPlayerProfile(ICommonSession p)
         {
-            return (HumanoidCharacterProfile) _prefsManager.GetPreferences(p.UserId).SelectedCharacter;
+            return (HumanoidCharacterProfile)_prefsManager.GetPreferences(p.UserId).SelectedCharacter;
         }
 
         public void PlayerJoinGame(ICommonSession session, bool silent = false)
@@ -205,7 +205,7 @@ public void PlayerJoinGame(ICommonSession session, bool silent = false)
             RaiseNetworkEvent(new TickerJoinGameEvent(), session.Channel);
         }
 
-        private void PlayerJoinLobby(ICommonSession session)
+        public void PlayerJoinLobby(ICommonSession session)
         {
             _playerGameStatuses[session.UserId] = LobbyEnabled ? PlayerGameStatus.NotReadyToPlay : PlayerGameStatus.ReadyToPlay;
             _db.AddRoundPlayers(RoundId, session.UserId);

+ 6 - 4
Content.Server/GameTicking/GameTicker.RoundFlow.cs

@@ -208,7 +208,7 @@ private void LoadMaps()
                 }
 
                 _metaData.SetEntityName(mapUid, proto.MapName);
-                var g = new List<EntityUid> {grid.Value.Owner};
+                var g = new List<EntityUid> { grid.Value.Owner };
                 RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
                 return g;
             }
@@ -258,7 +258,7 @@ private void LoadMaps()
                 }
 
                 _metaData.SetEntityName(mapUid, proto.MapName);
-                var g = new List<EntityUid> {grid.Value.Owner};
+                var g = new List<EntityUid> { grid.Value.Owner };
                 RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
                 return g;
             }
@@ -308,7 +308,7 @@ private void LoadMaps()
                     throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
                 }
 
-                var g = new List<EntityUid> {grid.Value.Owner};
+                var g = new List<EntityUid> { grid.Value.Owner };
                 // TODO MAP LOADING use a new event?
                 RaiseLocalEvent(new PostGameMapLoad(proto, targetMap, g, stationName));
                 return g;
@@ -390,7 +390,7 @@ public void StartRound(bool force = false)
                 HumanoidCharacterProfile profile;
                 if (_prefsManager.TryGetCachedPreferences(userId, out var preferences))
                 {
-                    profile = (HumanoidCharacterProfile) preferences.SelectedCharacter;
+                    profile = (HumanoidCharacterProfile)preferences.SelectedCharacter;
                 }
                 else
                 {
@@ -598,6 +598,8 @@ public void ShowRoundEndScoreboard(string text = "")
             );
             RaiseNetworkEvent(roundEndMessageEvent);
             RaiseLocalEvent(roundEndMessageEvent);
+            var soundEnd = new SoundPathSpecifier("/Audio/Announcements/civ_announcement.ogg");
+            _audio.PlayGlobal(_audio.ResolveSound(soundEnd), Filter.Broadcast(), true);
 
             _replayRoundPlayerInfo = listOfPlayerInfoFinal;
             _replayRoundText = roundEndText;

+ 6 - 3
Content.Server/GameTicking/GameTicker.Spawning.cs

@@ -212,9 +212,12 @@ private List<EntityUid> GetSpawnableStations()
                     JoinAsObserver(player);
                 }
 
+                if (LobbyEnabled)
+                {
+                    PlayerJoinLobby(player);
+                }
                 var evNoJobs = new NoJobsAvailableSpawningEvent(player); // Used by gamerules to wipe their antag slot, if they got one
                 RaiseLocalEvent(evNoJobs);
-
                 _chatManager.DispatchServerMessage(player,
                     Loc.GetString("game-ticker-player-no-jobs-available-when-joining"));
                 return;
@@ -239,7 +242,7 @@ private List<EntityUid> GetSpawnableStations()
 
             _mind.TransferTo(newMind, mob);
 
-            _roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
+            _roles.MindAddJobRole(newMind, silent: silent, jobPrototype: jobId);
             var jobName = _jobs.MindTryGetJobName(newMind);
             _admin.UpdatePlayerList(player);
 
@@ -332,7 +335,7 @@ public void Respawn(ICommonSession player)
         /// <param name="station">The station they're spawning on</param>
         /// <param name="jobId">An optional job for them to spawn as</param>
         /// <param name="silent">Whether or not the player should be greeted upon joining</param>
-        public void MakeJoinGame(ICommonSession player, EntityUid station, string? jobId = null, bool silent = false)
+        public void MakeJoinGame(ICommonSession player, EntityUid station, string? jobId = null, bool silent = true)
         {
             if (!_playerGameStatuses.ContainsKey(player.UserId))
                 return;

+ 118 - 0
Content.Server/GameTicking/Rules/CaptureAreaSystem.cs

@@ -0,0 +1,118 @@
+
+using Content.Server.GameTicking.Rules.Components;
+using Content.Shared.NPC.Components;
+using Content.Shared.Physics;
+using Robust.Shared.Timing;
+using Content.Server.Chat.Systems;
+using Content.Server.RoundEnd;
+
+namespace Content.Server.GameTicking.Rules;
+
+public sealed class CaptureAreaSystem : GameRuleSystem<CaptureAreaRuleComponent>
+{
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IEntityManager _entityManager = default!;
+    [Dependency] private readonly EntityLookupSystem _lookup = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly ChatSystem _chat = default!;
+    [Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
+    [Dependency] private readonly GameTicker _gameTicker = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<CaptureAreaComponent>();
+        while (query.MoveNext(out var uid, out var area))
+        {
+            ProcessArea(uid, area, frameTime);
+        }
+    }
+    private void ProcessArea(EntityUid uid, CaptureAreaComponent area, float frameTime)
+    {
+        var areaXform = _transform.GetMapCoordinates(uid);
+        var factionCounts = new Dictionary<string, int>();
+
+        // Initialize counts for all capturable factions to 0
+        foreach (var faction in area.CapturableFactions)
+        {
+            factionCounts[faction] = 0;
+        }
+
+        // Find entities in range and count factions
+        var entitiesInRange = _lookup.GetEntitiesInRange(areaXform, area.CaptureRadius, LookupFlags.Dynamic | LookupFlags.Sundries); // Include dynamic entities and items/mobs etc.
+        foreach (var entity in entitiesInRange)
+        {
+            // Check if the entity has a faction and if it's one we care about
+            if (_entityManager.TryGetComponent<NpcFactionMemberComponent>(entity, out var factionMember))
+            {
+                foreach (var faction in factionMember.Factions)
+                {
+                    if (area.CapturableFactions.Contains(faction))
+                        factionCounts[faction]++;
+                }
+            }
+        }
+
+        // Determine the controlling faction
+        string currentController = "";
+        int controllerCount = 0;
+        foreach (var (faction, count) in factionCounts)
+        {
+            if (count > 0)
+            {
+                currentController = faction;
+                controllerCount++;
+            }
+        }
+
+        // If more than one faction is present, it's contested
+        if (controllerCount > 1)
+            currentController = ""; // Reset controller if contested
+
+        // Update component state
+        area.Occupied = controllerCount > 0;
+
+        if (currentController != area.Controller)
+        {
+            // Controller changed (or became contested/empty)
+            area.Controller = currentController;
+            area.CaptureTimer = 0f; // Reset timer on change
+            if (currentController == "")
+            {
+                _chat.DispatchGlobalAnnouncement($"{area.PreviousController} has lost control of {area.Name}!", "Objective", false, null, Color.Red);
+            }
+            else
+            {
+                _chat.DispatchGlobalAnnouncement($"{currentController} has gained control of {area.Name}!", "Objective", false, null, Color.DodgerBlue);
+            }
+        }
+        else if (!string.IsNullOrEmpty(currentController))
+        {
+            // Controller remains the same, increment timer
+            area.CaptureTimer += frameTime;
+
+            //Check for capture completion
+            if (area.CaptureTimer >= area.CaptureDuration)
+            {
+                if (_gameTicker.RunLevel == GameRunLevel.InRound)
+                {
+                    _chat.DispatchGlobalAnnouncement($"{currentController} has captured {area.Name} and is victorious!", "Round", false, null, Color.Green);
+                    _roundEndSystem.EndRound();
+                }
+            }
+
+        }
+        else
+        {
+            // Area is empty or contested, and wasn't previously controlled by a single faction
+            area.CaptureTimer = 0f; // Ensure timer is reset/stays reset
+        }
+        area.PreviousController = currentController;
+    }
+
+}

+ 54 - 0
Content.Server/GameTicking/Rules/Components/CaptureAreaComponent.cs

@@ -0,0 +1,54 @@
+namespace Content.Server.GameTicking.Rules.Components;
+
+[RegisterComponent, Access(typeof(CaptureAreaSystem))]
+public sealed partial class CaptureAreaRuleComponent : Component
+{
+}
+[RegisterComponent]
+public sealed partial class CaptureAreaComponent : Component
+{
+    /// <summary>
+    /// The name for this capturable area
+    /// </summary>
+    [DataField("name")]
+    public string Name { get; set; } = "Objective Area";
+    /// <summary>
+    /// How long does the area need to be held for, in seconds
+    /// </summary>
+    [DataField("captureDuration")]
+    public float CaptureDuration { get; set; } = 300f;
+    /// <summary>
+    /// How far entities need to be to count towards capture
+    /// </summary>
+    [DataField("captureRadius")]
+    public float CaptureRadius { get; set; } = 4f;
+    /// <summary>
+    /// What the capture timer is currently at
+    /// </summary>
+
+    [DataField("captureTimer")]
+    public float CaptureTimer { get; set; } = 0f;
+    /// <summary>
+    /// Is the area currently occupied?
+    /// </summary>
+    [DataField("occupied")]
+    public bool Occupied { get; set; } = false;
+    /// <summary>
+    /// Which faction is occupying the area?
+    /// </summary>
+    [DataField("controller")]
+    public string Controller { get; set; } = "";
+    /// <summary>
+    /// The previous controller (for announcements when controller changes)
+    /// </summary>
+    [DataField("previousController")]
+    public string PreviousController { get; set; } = "";
+    /// <summary>
+    /// Which factions can occupy this area?
+    /// </summary>
+    [DataField("capturableFactions")]
+    public List<string> CapturableFactions { get; set; } = [];
+
+
+
+}

+ 48 - 0
Content.Server/GameTicking/Rules/Components/GracewallComponent.cs

@@ -0,0 +1,48 @@
+using Content.Shared.Physics;
+using Robust.Shared.Physics.Collision.Shapes;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Physics.Systems;
+
+namespace Content.Server.GameTicking.Rules.Components;
+
+[RegisterComponent, Access(typeof(GracewallRuleSystem))]
+public sealed partial class GracewallRuleComponent : Component
+{
+    /// <summary>
+    /// How long the grace wall lasts since the round started
+    /// </summary>
+    [DataField("gracewallDuration")]
+    public TimeSpan GracewallDuration { get; set; } = TimeSpan.FromMinutes(5);
+    /// <summary>
+    /// Is the grace wall currently active?
+    /// </summary>
+    [DataField("gracewallActive")]
+    public bool GracewallActive { get; set; } = true;
+    /// <summary>
+    /// How much time is remaining until the grace wall drops.
+    /// </summary>
+    public float Timer;
+
+
+}
+
+[RegisterComponent]
+public sealed partial class GracewallAreaComponent : Component
+{
+    /// <summary>
+    /// How long the grace wall lasts since the round started
+    /// </summary>
+    [DataField("gracewallRadius")]
+    public float GracewallRadius { get; set; } = 1.5f;
+    /// <summary>
+    /// Is the grace wall currently active?
+    /// </summary>
+    [DataField("gracewallActive")]
+    public bool GracewallActive { get; set; } = true;
+    /// <summary>
+    /// Which factions are blocked by the wall? If 'All', it applies to all
+    /// </summary>
+    [DataField("blockingFactions")]
+    public List<string> BlockingFactions { get; set; } = ["All"];
+
+}

+ 142 - 0
Content.Server/GameTicking/Rules/GracewallRuleSystem.cs

@@ -0,0 +1,142 @@
+
+using Content.Server.GameTicking.Rules.Components;
+using Content.Shared.NPC.Components;
+using Content.Shared.Physics;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Timing;
+using Content.Server.Chat.Systems;
+using Robust.Shared.Physics;
+using Content.Shared.GameTicking.Components;
+using Robust.Shared.Physics.Components;
+
+namespace Content.Server.GameTicking.Rules;
+
+public sealed class GracewallRuleSystem : GameRuleSystem<GracewallRuleComponent>
+{
+    [Dependency] private readonly SharedPhysicsSystem _physics = default!;
+    // Define a specific collision group for the grace wall.
+    // Make sure this group is defined in CollisionGroups.yml and NPCs are set to not collide with it.
+    [Dependency] private readonly ChatSystem _chat = default!;
+    private const int GraceWallCollisionGroup = (int)CollisionGroup.MidImpassable; // Example: Use MidImpassable or define a custom one
+
+    private FixturesComponent? _fixtures;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<GracewallAreaComponent, StartCollideEvent>(OnStartCollide);
+        // Potentially subscribe to PreventCollideEvent if more fine-grained control is needed
+    }
+
+    protected override void Started(EntityUid uid, GracewallRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
+    {
+        base.Started(uid, component, gameRule, args);
+
+        component.Timer = (float)component.GracewallDuration.TotalSeconds;
+        component.GracewallActive = true;
+        // Schedule the announcement for 15 seconds later
+        var announcementMessage = $"The grace wall is up for {component.GracewallDuration.TotalMinutes} minutes!";
+        Timer.Spawn(TimeSpan.FromSeconds(15), () =>
+        {
+            _chat.DispatchGlobalAnnouncement(announcementMessage, "Round", false, null, Color.Yellow);
+        });
+        Log.Info($"Grace wall active for {component.GracewallDuration.TotalMinutes} minutes.");
+
+        // Activate all grace wall areas
+        var query = EntityQueryEnumerator<GracewallAreaComponent, TransformComponent, FixturesComponent>();
+        while (query.MoveNext(out var wallUid, out var area, out var xform, out var fixtures))
+        {
+            area.GracewallActive = true;
+            UpdateGracewallPhysics(wallUid, area, fixtures, true);
+        }
+    }
+
+    protected override void Ended(EntityUid uid, GracewallRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
+    {
+        base.Ended(uid, component, gameRule, args);
+
+        // Ensure walls are deactivated if the rule ends unexpectedly
+        DeactivateAllGraceWalls(component);
+        _chat.DispatchGlobalAnnouncement("The grace wall is now down!", "Round", false, null, Color.Yellow);
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<GracewallRuleComponent, GameRuleComponent>();
+        while (query.MoveNext(out var uid, out var gracewall, out var gameRule))
+        {
+            if (!GameTicker.IsGameRuleActive(uid, gameRule) || !gracewall.GracewallActive)
+                continue;
+
+            gracewall.Timer -= frameTime;
+
+            if (gracewall.Timer <= 0)
+            {
+                Log.Info("Grace wall duration ended.");
+                DeactivateAllGraceWalls(gracewall);
+                _chat.DispatchGlobalAnnouncement("The grace wall is now down!", "Round", false, null, Color.Yellow);
+            }
+        }
+    }
+
+    private void DeactivateAllGraceWalls(GracewallRuleComponent component)
+    {
+        component.GracewallActive = false;
+
+        // Deactivate all grace wall areas
+        var query = EntityQueryEnumerator<GracewallAreaComponent, FixturesComponent>();
+        while (query.MoveNext(out var wallUid, out var area, out var fixtures))
+        {
+            area.GracewallActive = false;
+            UpdateGracewallPhysics(wallUid, area, fixtures, false);
+        }
+    }
+
+    private void UpdateGracewallPhysics(EntityUid uid, GracewallAreaComponent component, FixturesComponent fixtures, bool active)
+    {
+        // Check if the specific fixture we defined in the prototype exists
+        if (!fixtures.Fixtures.TryGetValue("gracewall", out var fixture))
+
+        {
+            Log.Warning($"Gracewall entity {ToPrettyString(uid)} is missing the 'gracewall' fixture!");
+            // Attempt to create it dynamically if missing? Or just rely on the prototype.
+            // For now, we'll just log a warning. If it's consistently missing, the prototype needs fixing.
+            return;
+        }
+
+        // Modify the fixture's collision properties
+        // This assumes NPCs have a mask that *doesn't* include GraceWallCollisionGroup
+        _physics.SetCollisionLayer(uid, "gracewall", fixture, active ? GraceWallCollisionGroup : (int)CollisionGroup.None);
+        // Setting the layer might require refreshing contacts or waking the body if it's asleep
+        // to ensure the change takes effect immediately.
+        if (TryComp<PhysicsComponent>(uid, out var physics))
+            _physics.WakeBody(uid, body: physics);
+        // Ensure the radius is correct (it might change dynamically later)
+        // This requires getting the specific fixture and modifying its shape.
+        // For simplicity, we assume the prototype radius is sufficient for now.
+        // If dynamic radius is needed, we'd need more complex logic here.
+    }
+
+    private void OnStartCollide(EntityUid uid, GracewallAreaComponent component, ref StartCollideEvent args)
+    {
+        // This event triggers when something *starts* colliding with the grace wall fixture.
+        // We only care when the wall is active.
+        if (!component.GracewallActive)
+            return;
+
+        // Check if the other entity is an NPC
+        var otherUid = args.OtherEntity;
+        if (HasComp<NpcFactionMemberComponent>(otherUid))
+        {
+            // The collision system *should* prevent entry based on layers/masks.
+            // However, if an NPC somehow starts colliding (e.g., spawned inside, teleported),
+            // we could potentially push them out here.
+            // For now, we rely on the collision group preventing entry.
+            // Log.Debug($"NPC {ToPrettyString(otherUid)} collided with active grace wall {ToPrettyString(uid)}.");
+        }
+    }
+
+}

+ 20 - 14
Content.Server/Ghost/GhostSystem.cs

@@ -91,6 +91,7 @@ public override void Initialize()
 
             SubscribeNetworkEvent<GhostWarpsRequestEvent>(OnGhostWarpsRequest);
             SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest);
+            SubscribeNetworkEvent<GhostReturnToLobbyRequest>(OnGhostReturnToLobbyRequest);
             SubscribeNetworkEvent<GhostWarpToTargetRequestEvent>(OnGhostWarpToTargetRequest);
             SubscribeNetworkEvent<GhostnadoRequestEvent>(OnGhostnadoRequest);
 
@@ -192,8 +193,8 @@ private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentSt
 
             if (_gameTicker.RunLevel != GameRunLevel.PostRound)
             {
-                _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false);
-                _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false);
+                _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Ghost, false);
+                _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Normal, false);
                 _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility);
             }
 
@@ -211,8 +212,8 @@ private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentS
             // Entity can't be seen by ghosts anymore.
             if (TryComp(uid, out VisibilityComponent? visibility))
             {
-                _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ghost, false);
-                _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false);
+                _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Ghost, false);
+                _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Normal, false);
                 _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility);
             }
 
@@ -269,7 +270,7 @@ private void DeleteEntity(EntityUid uid)
 
         private void OnGhostReturnToBodyRequest(GhostReturnToBodyRequest msg, EntitySessionEventArgs args)
         {
-            if (args.SenderSession.AttachedEntity is not {Valid: true} attached
+            if (args.SenderSession.AttachedEntity is not { Valid: true } attached
                 || !_ghostQuery.TryComp(attached, out var ghost)
                 || !ghost.CanReturnToBody
                 || !TryComp(attached, out ActorComponent? actor))
@@ -281,11 +282,16 @@ private void OnGhostReturnToBodyRequest(GhostReturnToBodyRequest msg, EntitySess
             _mind.UnVisit(actor.PlayerSession);
         }
 
+        private void OnGhostReturnToLobbyRequest(GhostReturnToLobbyRequest msg, EntitySessionEventArgs args)
+        {
+            _gameTicker.PlayerJoinLobby(args.SenderSession);
+        }
+
         #region Warp
 
         private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args)
         {
-            if (args.SenderSession.AttachedEntity is not {Valid: true} entity
+            if (args.SenderSession.AttachedEntity is not { Valid: true } entity
                 || !_ghostQuery.HasComp(entity))
             {
                 Log.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost.");
@@ -298,7 +304,7 @@ private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventA
 
         private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
         {
-            if (args.SenderSession.AttachedEntity is not {Valid: true} attached
+            if (args.SenderSession.AttachedEntity is not { Valid: true } attached
                 || !_ghostQuery.HasComp(attached))
             {
                 Log.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Target} without being a ghost.");
@@ -318,14 +324,14 @@ private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, Entit
 
         private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArgs args)
         {
-            if (args.SenderSession.AttachedEntity is not {} uid
+            if (args.SenderSession.AttachedEntity is not { } uid
                 || !_ghostQuery.HasComp(uid))
             {
                 Log.Warning($"User {args.SenderSession.Name} tried to ghostnado without being a ghost.");
                 return;
             }
 
-            if (_followerSystem.GetMostGhostFollowed() is not {} target)
+            if (_followerSystem.GetMostGhostFollowed() is not { } target)
                 return;
 
             WarpTo(uid, target);
@@ -362,7 +368,7 @@ private IEnumerable<GhostWarp> GetPlayerWarps(EntityUid except)
         {
             foreach (var player in _playerManager.Sessions)
             {
-                if (player.AttachedEntity is not {Valid: true} attached)
+                if (player.AttachedEntity is not { Valid: true } attached)
                     continue;
 
                 if (attached == except) continue;
@@ -406,13 +412,13 @@ public void MakeVisible(bool visible)
 
                 if (visible)
                 {
-                    _visibilitySystem.AddLayer((uid, vis), (int) VisibilityFlags.Normal, false);
-                    _visibilitySystem.RemoveLayer((uid, vis), (int) VisibilityFlags.Ghost, false);
+                    _visibilitySystem.AddLayer((uid, vis), (int)VisibilityFlags.Normal, false);
+                    _visibilitySystem.RemoveLayer((uid, vis), (int)VisibilityFlags.Ghost, false);
                 }
                 else
                 {
-                    _visibilitySystem.AddLayer((uid, vis), (int) VisibilityFlags.Ghost, false);
-                    _visibilitySystem.RemoveLayer((uid, vis), (int) VisibilityFlags.Normal, false);
+                    _visibilitySystem.AddLayer((uid, vis), (int)VisibilityFlags.Ghost, false);
+                    _visibilitySystem.RemoveLayer((uid, vis), (int)VisibilityFlags.Normal, false);
                 }
                 _visibilitySystem.RefreshVisibility(uid, visibilityComponent: vis);
             }

+ 1 - 1
Content.Server/Jobs/AddComponentSpecial.cs

@@ -13,7 +13,7 @@ public sealed partial class AddComponentSpecial : JobSpecial
     /// </summary>
     [DataField]
     public bool RemoveExisting = true;
-
+    private readonly ISawmill _sawmill;
     public override void AfterEquip(EntityUid mob)
     {
         var entMan = IoCManager.Resolve<IEntityManager>();

+ 8 - 0
Content.Server/Spawners/Components/SpawnPointComponent.cs

@@ -1,3 +1,4 @@
+using Content.Shared.NPC.Prototypes;
 using Content.Shared.Roles;
 using Robust.Shared.Prototypes;
 
@@ -8,6 +9,12 @@ public sealed partial class SpawnPointComponent : Component, ISpawnPoint
 {
     [DataField("job_id")]
     public ProtoId<JobPrototype>? Job;
+    /// <summary>
+    /// The faction that this spawn point applies to
+    /// Remember to set SpawnType to Faction!
+    /// </summary>
+    [DataField("faction")]
+    public ProtoId<NpcFactionPrototype>? Faction;
 
     /// <summary>
     /// The type of spawn point
@@ -27,4 +34,5 @@ public enum SpawnPointType
     LateJoin,
     Job,
     Observer,
+    Faction,
 }

+ 12 - 2
Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs

@@ -3,6 +3,7 @@
 using Content.Server.Station.Systems;
 using Robust.Shared.Map;
 using Robust.Shared.Random;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Spawners.EntitySystems;
 
@@ -12,7 +13,7 @@ public sealed class SpawnPointSystem : EntitySystem
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly StationSystem _stationSystem = default!;
     [Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
-
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     public override void Initialize()
     {
         SubscribeLocalEvent<PlayerSpawningEvent>(OnPlayerSpawning);
@@ -27,10 +28,19 @@ private void OnPlayerSpawning(PlayerSpawningEvent args)
         var points = EntityQueryEnumerator<SpawnPointComponent, TransformComponent>();
         var possiblePositions = new List<EntityCoordinates>();
 
-        while ( points.MoveNext(out var uid, out var spawnPoint, out var xform))
+        while (points.MoveNext(out var uid, out var spawnPoint, out var xform))
         {
             if (args.Station != null && _stationSystem.GetOwningStation(uid, xform) != args.Station)
                 continue;
+            // Try to get the actual JobPrototype data from the ID (args.Job)
+            if (args.Job != null && _prototypeManager.TryIndex(args.Job.Value, out var jobPrototype))
+            {
+                if (jobPrototype != null && jobPrototype.Faction == spawnPoint.Faction)
+                {
+                    possiblePositions.Add(xform.Coordinates);
+                }
+            }
+
 
             if (_gameTicker.RunLevel == GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.LateJoin)
             {

+ 21 - 3
Content.Server/Station/Systems/StationSpawningSystem.cs

@@ -167,10 +167,28 @@ public override void Initialize()
             EquipRoleLoadout(entity.Value, loadout, roleProto!);
         }
 
-        if (prototype?.StartingGear != null)
+        // Equip starting gear if specified in the job prototype
+        if (prototype != null)
         {
-            var startingGear = _prototypeManager.Index<StartingGearPrototype>(prototype.StartingGear);
-            EquipStartingGear(entity.Value, startingGear, raiseEvent: false);
+            StartingGearPrototype? startingGearProto = null;
+
+            // Check if random gears are available and populated
+            if (prototype.RandomStartingGears != null && prototype.RandomStartingGears.Count > 0)
+            {
+                var startingGearId = _random.Pick(prototype.RandomStartingGears); // Safe now
+                _prototypeManager.TryIndex(startingGearId, out startingGearProto);
+            }
+            // Otherwise, check if the single starting gear is specified
+            else if (prototype.StartingGear != null)
+            {
+                _prototypeManager.TryIndex(prototype.StartingGear.Value, out startingGearProto); // Safe now, using .Value for ProtoId?
+            }
+
+            // If we found a valid gear prototype (either random or specific), equip it
+            if (startingGearProto != null)
+            {
+                EquipStartingGear(entity.Value, startingGearProto, raiseEvent: false);
+            }
         }
 
         var gearEquippedEv = new StartingGearEquippedEvent(entity.Value);

+ 35 - 7
Content.Shared/Camera/SharedCameraRecoilSystem.cs

@@ -3,6 +3,7 @@
 using Content.Shared.Movement.Systems;
 using JetBrains.Annotations;
 using Robust.Shared.Network;
+using Robust.Shared.Maths;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Camera;
@@ -58,9 +59,23 @@ private void UpdateEyes(float frameTime)
 
         while (query.MoveNext(out var uid, out var recoil, out var eye))
         {
+            // Check if CurrentKick is invalid (NaN or Infinite) and reset if necessary.
+            // This prevents downstream errors, especially with Math.Sign.
+            if (!float.IsFinite(recoil.CurrentKick.X) || !float.IsFinite(recoil.CurrentKick.Y))
+            {
+                // Log error and reset
+                Log.Error($"Camera recoil CurrentKick is invalid for entity {ToPrettyString(uid)}. Resetting kick. Value: {recoil.CurrentKick}");
+                recoil.CurrentKick = Vector2.Zero;
+                // Allow logic to continue to potentially update eye if LastKick wasn't zero
+            }
+
             var magnitude = recoil.CurrentKick.Length();
             if (magnitude <= 0.005f)
             {
+                // If it's already zero, skip updates. Otherwise, set it to zero.
+                if (recoil.CurrentKick == Vector2.Zero)
+                    continue;
+
                 recoil.CurrentKick = Vector2.Zero;
             }
             else // Continually restore camera to 0.
@@ -69,14 +84,27 @@ private void UpdateEyes(float frameTime)
                 recoil.LastKickTime += frameTime;
                 var restoreRate = MathHelper.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, recoil.LastKickTime / RestoreRateRamp));
                 var restore = normalized * restoreRate * frameTime;
-                var (x, y) = recoil.CurrentKick - restore;
-                if (Math.Sign(x) != Math.Sign(recoil.CurrentKick.X))
-                    x = 0;
-
-                if (Math.Sign(y) != Math.Sign(recoil.CurrentKick.Y))
-                    y = 0;
 
-                recoil.CurrentKick = new Vector2(x, y);
+                // Sanity check: ensure restore vector is finite.
+                if (!float.IsFinite(restore.X) || !float.IsFinite(restore.Y))
+                {
+                    Log.Error($"Camera recoil restore vector is invalid for entity {ToPrettyString(uid)}. Resetting kick. Restore: {restore}, Normalized: {normalized}, Rate: {restoreRate}, FrameTime: {frameTime}");
+                    recoil.CurrentKick = Vector2.Zero;
+                }
+                else
+                {
+                    var newKick = recoil.CurrentKick - restore;
+                    var (x, y) = newKick;
+
+                    // Check if the sign flipped (meaning we overshot zero)
+                    if (Math.Sign(x) != Math.Sign(recoil.CurrentKick.X))
+                        x = 0;
+
+                    if (Math.Sign(y) != Math.Sign(recoil.CurrentKick.Y))
+                        y = 0;
+
+                    recoil.CurrentKick = new Vector2(x, y);
+                }
             }
 
             if (recoil.CurrentKick == recoil.LastKick)

+ 18 - 0
Content.Shared/Civ14/Barricade/BarricadeComponent.cs

@@ -0,0 +1,18 @@
+using Robust.Shared.GameObjects;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+using System.Collections.Generic;
+using Content.Shared.Tag;
+
+namespace Content.Shared.Barricade;
+
+[RegisterComponent]
+public sealed partial class BarricadeComponent : Component
+{
+    /// <summary>
+    /// % chance of blocking a projectile passing overhead
+    /// </summary>
+    [DataField("blocking")]
+    public int Blocking = 66;
+
+}

+ 8 - 1
Content.Shared/Ghost/SharedGhostSystem.cs

@@ -96,7 +96,7 @@ public GhostWarp(NetEntity entity, string displayName, bool isWarpPoint)
         /// <summary>
         /// Whether this warp represents a warp point or a player
         /// </summary>
-        public bool IsWarpPoint { get;  }
+        public bool IsWarpPoint { get; }
     }
 
     /// <summary>
@@ -144,6 +144,13 @@ public sealed class GhostnadoRequestEvent : EntityEventArgs;
     public sealed class GhostReturnToBodyRequest : EntityEventArgs
     {
     }
+    /// <summary>
+    /// A client to server request for their ghost to return to lobby
+    /// </summary>
+    [Serializable, NetSerializable]
+    public sealed class GhostReturnToLobbyRequest : EntityEventArgs
+    {
+    }
 
     /// <summary>
     /// A server to client update with the available ghost role count

+ 4 - 0
Content.Shared/Inventory/InventorySystem.Relay.cs

@@ -66,6 +66,10 @@ public void InitializeRelay()
         SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RefRelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent);
 
+        SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowFactionIconsComponent>>(RefRelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowFrenchFactionIconsComponent>>(RefRelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowEnglishFactionIconsComponent>>(RefRelayInventoryEvent);
+
         SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
     }
 

+ 39 - 0
Content.Shared/Overlays/ShowFactionIconsComponent.cs

@@ -0,0 +1,39 @@
+using Content.Shared.StatusIcon;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.Overlays;
+
+/// <summary>
+///     This component allows you to see faction icons above mobs.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowFactionIconsComponent : Component
+{
+
+    /// <summary>
+    /// The faction icon to display
+    /// </summary>
+    [DataField("factionIcon", customTypeSerializer: typeof(PrototypeIdSerializer<FactionIconPrototype>))]
+    public string FactionIcon = "HostileFaction";
+}
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowEnglishFactionIconsComponent : Component
+{
+
+    /// <summary>
+    /// The faction icon to display
+    /// </summary>
+    [DataField("factionIcon", customTypeSerializer: typeof(PrototypeIdSerializer<FactionIconPrototype>))]
+    public string FactionIcon = "EnglishFaction";
+}
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ShowFrenchFactionIconsComponent : Component
+{
+
+    /// <summary>
+    /// The faction icon to display
+    /// </summary>
+    [DataField("factionIcon", customTypeSerializer: typeof(PrototypeIdSerializer<FactionIconPrototype>))]
+    public string FactionIcon = "FrenchFaction";
+}

+ 13 - 2
Content.Shared/Projectiles/SharedProjectileSystem.cs

@@ -20,6 +20,8 @@
 using Robust.Shared.Physics.Systems;
 using Robust.Shared.Player;
 using Robust.Shared.Serialization;
+using Content.Shared.Barricade;
+using Robust.Shared.Random;
 
 namespace Content.Shared.Projectiles;
 
@@ -38,7 +40,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
     [Dependency] private readonly DamageableSystem _damageableSystem = default!;
     [Dependency] private readonly SharedGunSystem _guns = default!;
     [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
-
+    [Dependency] private readonly IRobustRandom _random = default!;
     public override void Initialize()
     {
         base.Initialize();
@@ -58,6 +60,7 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
             || component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
             return;
 
+
         ProjectileCollide((uid, component, args.OurBody), args.OtherEntity);
     }
 
@@ -118,7 +121,7 @@ public void ProjectileCollide(Entity<ProjectileComponent, PhysicsComponent> proj
         if (!deleted)
         {
             _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound, filter, projectile);
-            _sharedCameraRecoil.KickCamera(target, direction);
+            //_sharedCameraRecoil.KickCamera(target, direction); # this makes people blind or something
         }
 
         component.DamagedEntity = true;
@@ -245,6 +248,14 @@ private void PreventCollision(EntityUid uid, ProjectileComponent component, ref
         {
             args.Cancelled = true;
         }
+        //check for barricade component (percentage of chance to hit/pass over)
+        if (TryComp(args.OtherEntity, out BarricadeComponent? barricade))
+        {
+            if (_random.NextFloat(0.0f, 100.0f) <= barricade.Blocking)
+            {
+                args.Cancelled = true;
+            }
+        }
     }
 
     public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid? shooterId = null)

+ 15 - 0
Content.Shared/Roles/JobPrototype.cs

@@ -35,6 +35,18 @@ public sealed partial class JobPrototype : IPrototype
         [ViewVariables(VVAccess.ReadOnly)]
         public string LocalizedName => Loc.GetString(Name);
 
+        /// <summary>
+        ///     The "original" name of the role, untranslated.
+        ///     This would be, for example, a medic for the french faction being called Médecin
+        /// </summary>
+        [DataField("originalName")]
+        public string OriginalName { get; private set; } = string.Empty;
+        /// <summary>
+        ///     The faction this job belongs to. Used to look for spawnpoints.
+        /// </summary>
+        [DataField("faction")]
+        public string Faction { get; private set; } = string.Empty;
+
         /// <summary>
         ///     The name of this job as displayed to players.
         /// </summary>
@@ -111,6 +123,9 @@ public sealed partial class JobPrototype : IPrototype
         [DataField]
         public ProtoId<StartingGearPrototype>? StartingGear { get; private set; }
 
+        [DataField]
+        public List<ProtoId<StartingGearPrototype>>? RandomStartingGears { get; private set; } = new();
+
         /// <summary>
         /// Use this to spawn in as a non-humanoid (borg, test subject, etc.)
         /// Starting gear will be ignored.

+ 15 - 0
Resources/Audio/Announcements/attributions.yml

@@ -47,3 +47,18 @@
   license: "CC-BY-SA-3.0"
   copyright: "Taken from tgstation"
   source: "https://github.com/tgstation/tgstation/blob/95731342b97167d7883ff091d389f79c36442ee6/sound/ai/default/intercept.ogg"
+
+- files: ["civ_announcement.ogg"]
+  license: "Custom"
+  copyright: "Attribution 4.0 - Trumpet Brass Fanfare.wav by ohforheavensake"
+  source: "https://freesound.org/people/ohforheavensake/sounds/423455/"
+
+- files: ["horn.ogg"]
+  license: "Custom"
+  copyright: "AGPL v3 - From Civ13"
+  source: "https://github.com/civ13/civ13"
+
+- files: ["horn_long.ogg"]
+  license: "Custom"
+  copyright: "AGPL v3 - From Civ13"
+  source: "https://github.com/civ13/civ13"

BIN
Resources/Audio/Announcements/civ_announcement.ogg


BIN
Resources/Audio/Announcements/horn.ogg


BIN
Resources/Audio/Announcements/horn_long.ogg


+ 8 - 2
Resources/ConfigPresets/Build/development.toml

@@ -1,9 +1,12 @@
 [game]
 # Straight in-game baby
-lobbyenabled = false
+lobbyenabled = true
 # Dev map for faster loading & convenience
-map = "Nomads"
+map = "Camp"
 role_timers = false
+lobbyduration = 15
+disallowlatejoins = false
+defaultpreset = "tdm"
 
 [events]
 enabled = false
@@ -37,3 +40,6 @@ see_own_notes = true
 
 [net]
 tickrate = 20
+
+[server]
+lobby_right_panel_width = 500

+ 1 - 1
Resources/ConfigPresets/Civ/production.toml

@@ -74,4 +74,4 @@ github = "https://github.com/Civ13/Civ14"
 wiki = "https://civ13.github.io/Civ14"
 
 [server]
-lobby_right_panel_width = 300
+lobby_right_panel_width = 500

+ 14 - 0
Resources/Locale/en-US/Civ14/jobs-descriptions.ftl

@@ -0,0 +1,14 @@
+job-description-civ-english-lord = You are the highest ranking in your faction. Lead your men to victory!
+job-description-civ-english-knight = You are a heavily armoured knight. Direct the troops while answering to the Lord!
+job-description-civ-english-man-at-arms = You are a swordsman. Use your shield to keep archers at bay!
+job-description-civ-english-spearman = You are a spearman, the bread-and-butter of the army. Onwards to victory!
+job-description-civ-english-ranged = As a ranged unit, harass the lightly armoured enemies to submission!
+job-description-civ-english-medic = As a field surgeon, keep your troops alive!
+
+
+job-description-civ-french-lord = You are the highest ranking in your faction. Lead your men to victory!
+job-description-civ-french-knight = You are a heavily armoured knight. Direct the troops while answering to the Lord!
+job-description-civ-french-man-at-arms = You are a swordsman. Use your shield to keep archers at bay!
+job-description-civ-french-spearman = You are a spearman, the bread-and-butter of the army. Onwards to victory!
+job-description-civ-french-ranged = As a ranged unit, harass the lightly armoured enemies to submission!
+job-description-civ-french-medic = As a field surgeon, keep your troops alive!

+ 39 - 0
Resources/Locale/en-US/Civ14/jobs.ftl

@@ -0,0 +1,39 @@
+job-supervisors-nobody = nobody, you are the highest ranking person in your faction
+job-supervisors-sgt = your Sargeant
+job-supervisors-officer = any Officer
+job-supervisors-squadleader = your Squad Leader
+job-supervisors-lord = your Lord
+
+job-name-commander = Commander
+job-name-designated-marksman = Designated Marksman
+job-name-i-cpl = Corporal
+job-name-i-cpt = Captain
+job-name-i-lt = Lieutenant
+job-name-i-maj = Major
+job-name-i-sgt = Sergeant
+job-name-i-ssgt = Staff Sergeant
+job-name-medic = Medic
+job-name-mg = Machine Gunner
+job-name-nco = NCO
+job-name-officer = Officer
+job-name-rifleman = Rifleman
+job-name-soldier = Soldier
+
+job-name-infantry-sword = Sword Infantry
+job-name-infantry-heavy = Heavy Infantry
+job-name-infantry-spear = Spear Infantry
+job-name-infantry-ranged = Ranged Infantry
+
+job-name-civ-english-lord = Leader
+job-name-civ-english-knight = Heavy Inf.
+job-name-civ-english-man-at-arms = Sword Inf.
+job-name-civ-english-spearman = Spear Inf.
+job-name-civ-english-ranged = Ranged Inf.
+job-name-civ-english-medic = Medic
+
+job-name-civ-french-lord = Leader
+job-name-civ-french-knight = Heavy Inf.
+job-name-civ-french-man-at-arms = Sword Inf.
+job-name-civ-french-spearman = Spear Inf.
+job-name-civ-french-ranged = Ranged Inf.
+job-name-civ-french-medic = Medic

+ 1 - 1
Resources/Locale/en-US/administration/commands/announce-command.ftl

@@ -2,7 +2,7 @@ cmd-announce-desc = Send an in-game announcement with custom color and sound.
 cmd-announce-help = {$command} <message> [sender] [color] [sound] - Send announcement. Sender defaults to CentCom, color to Gold, sound to announce.ogg. The color should be in a #RRGGBB format.
 
 # The default sender for the announcement
-cmd-announce-sender = Central Command
+cmd-announce-sender = World
 
 # Completion hints
 cmd-announce-arg-message = <message>

+ 1 - 1
Resources/Locale/en-US/administration/ui/admin-announce-window.ftl

@@ -1,7 +1,7 @@
 admin-announce-title = Make Announcement
 admin-announce-announcement-placeholder = Announcement text...
 admin-announce-announcer-placeholder = Announcer
-admin-announce-announcer-default = Central Command
+admin-announce-announcer-default = World
 admin-announce-button = Announce
 admin-announce-type-station = Station
 admin-announce-type-server = Server

+ 1 - 1
Resources/Locale/en-US/chat/managers/chat-manager.ftl

@@ -19,7 +19,7 @@ chat-manager-no-such-channel = There is no channel with key '{$key}'!
 chat-manager-whisper-headset-on-message = You can't whisper on the radio!
 
 chat-manager-server-wrap-message = [bold]{$message}[/bold]
-chat-manager-sender-announcement = Central Command
+chat-manager-sender-announcement = World
 chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12]
                                                 {$message}[/bold][/font]
 chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font]

+ 1 - 1
Resources/Locale/en-US/communications/communications-console-component.ftl

@@ -22,7 +22,7 @@ comms-console-announcement-unknown-sender = Unknown
 
 # Comms console variant titles
 comms-console-announcement-title-station = Communications Console
-comms-console-announcement-title-centcom = Central Command
+comms-console-announcement-title-centcom = World
 comms-console-announcement-title-nukie = Syndicate Nuclear Operative
 comms-console-announcement-title-station-ai = Station AI
 comms-console-announcement-title-wizard = Wizard

+ 3 - 0
Resources/Locale/en-US/game-ticking/game-presets/preset-extended.ftl

@@ -1,2 +1,5 @@
 extended-title = Nomads
 extended-description = No preset factions or objectives.
+
+tdm-title = Team Deathmatch
+tdm-description = Two factions competing to capture the target.

+ 2 - 1
Resources/Locale/en-US/ghost/ghost-gui.ftl

@@ -1,4 +1,5 @@
-ghost-gui-return-to-body-button = Return to body
+ghost-gui-return-to-body-button = Return to Body
+ghost-gui-return-to-lobby-button = Return to Lobby
 ghost-gui-ghost-warp-button = Ghost Warp
 ghost-gui-ghost-roles-button = Ghost Roles ({$count})
 ghost-gui-toggle-ghost-visibility-popup-on = Enabled visibility of ghosts.

+ 2 - 0
Resources/Locale/en-US/job/department-desc.ftl

@@ -10,3 +10,5 @@ department-Silicon-description = Obey your laws and serve the crew.
 department-Specific-description = Jobs that not all stations have.
 
 department-Nomads-description = Nomads are not bound to any faction.
+department-French-description = The French are one of the factions present in this map.
+department-English-description = The English are one of the factions present in this map.

+ 2 - 0
Resources/Locale/en-US/job/department.ftl

@@ -10,3 +10,5 @@ department-Silicon = Silicon
 department-Specific = Station specific
 
 department-Nomads = Nomads
+department-French = French
+department-English = English

+ 4 - 2
Resources/Locale/en-US/late-join/late-join-gui.ftl

@@ -1,5 +1,7 @@
 late-join-gui-title = Late Join
 late-join-gui-jobs-amount-in-department-tooltip = Jobs in the {$departmentName} department
 late-join-gui-department-jobs-label = {$departmentName} jobs
-late-join-gui-job-slot-capped = {$jobName} ({$amount} open)
-late-join-gui-job-slot-uncapped = {$jobName} (∞ open)
+late-join-gui-job-slot-capped = {$originalName} ({$jobName}) ({$amount} open)
+late-join-gui-job-slot-uncapped = {$originalName} ({$jobName}) (∞ open)
+late-join-gui-job-slot-capped-no-original = {$jobName} ({$amount} open)
+late-join-gui-job-slot-uncapped-no-original = {$jobName} (∞ open)

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 50 - 0
Resources/Maps/civ/tdm/camp.yml


+ 42 - 11
Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_accessories.yml

@@ -1139,21 +1139,52 @@
       sprite: Civ14/Clothing/exported/ties/customtabard.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/ties/customtabard.rsi
-    - type: Armor
-      modifiers:
-        coefficients:
-          Blunt: 1
-          Slash: 1
-          Piercing: 1
-          Arrow: 1
-          Heat: 1
-          Radiation: 1
     - type: Construction
       graph: civ13_accessory_tabard
       node: end
       cost: 2
       material: Cloth
       time: 3
+- type: entity
+  name: red tabard
+  parent: ClothingNeckBase
+  id: civ13_accessory_tabard_red
+  description: A cloth tabard, dyed red.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/ties/tabard_red.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/ties/tabard_red.rsi
+- type: entity
+  name: red and yellow tabard
+  parent: ClothingNeckBase
+  id: civ13_accessory_tabard_red_yellow
+  description: A cloth tabard, dyed red and yellow.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/ties/tabard_red_yellow.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/ties/tabard_red_yellow.rsi
+- type: entity
+  name: blue tabard
+  parent: ClothingNeckBase
+  id: civ13_accessory_tabard_blue
+  description: A cloth tabard, dyed blue.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/ties/tabard_blue.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/ties/tabard_blue.rsi
+- type: entity
+  name: blue and white tabard
+  parent: ClothingNeckBase
+  id: civ13_accessory_tabard_blue_white
+  description: A cloth tabard, dyed blue and white.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/ties/tabard_blue_white.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/ties/tabard_blue_white.rsi
 - type: entity
   name: apron
   parent: ClothingNeckBase
@@ -5890,8 +5921,8 @@
         coefficients:
           Blunt: 0.5
           Slash: 0.5
-          Piercing: 0.92
-          Arrow: 0.65
+          Piercing: 0.7
+          Arrow: 0.7
           Heat: 0.85
     - type: Construction
       graph: civ13_accessory_chainmail

+ 4 - 4
Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_gloves.yml

@@ -1029,10 +1029,10 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.25
-          Slash: 0.25
-          Piercing: 0.9
-          Arrow: 0.4
+          Blunt: 0.8
+          Slash: 0.8
+          Piercing: 0.7
+          Arrow: 0.7
           Heat: 0.92
     - type: Construction
       graph: civ13_gloves_armored_gauntlets

+ 15 - 15
Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_hats.yml

@@ -2285,10 +2285,10 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.30
-          Slash: 0.30
-          Piercing: 0.9
-          Arrow: 0.1
+          Blunt: 0.7
+          Slash: 0.7
+          Piercing: 0.7
+          Arrow: 0.6
           Heat: 0.85
     - type: Construction
       graph: civ13_head_hounskull_bascinet
@@ -2327,10 +2327,10 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.5
-          Slash: 0.5
+          Blunt: 0.6
+          Slash: 0.6
           Piercing: 0.95
-          Arrow: 0.6
+          Arrow: 0.65
           Heat: 0.85
 - type: entity
   name: kettle helmet
@@ -2345,8 +2345,8 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.55
-          Slash: 0.55
+          Blunt: 0.6
+          Slash: 0.6
           Piercing: 0.95
           Arrow: 0.65
           Heat: 0.85
@@ -2363,10 +2363,10 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.6
-          Slash: 0.6
+          Blunt: 0.55
+          Slash: 0.55
           Piercing: 0.95
-          Arrow: 0.7
+          Arrow: 0.65
           Heat: 0.85
 - type: entity
   name: coif wrapped mamluk helmet
@@ -7801,7 +7801,7 @@
   name: brown noble hat
   parent: ClothingHeadBase
   id: civ13_head_brown_noble_hat
-  description: ""
+  description: "A medieval brown cap."
   components:
     - type: Sprite
       sprite: Civ14/Clothing/exported/hats/noblehat1.rsi
@@ -7815,10 +7815,10 @@
       material: Cloth
       time: 5
 - type: entity
-  name: black noble hat
+  name: black cap
   parent: ClothingHeadBase
   id: civ13_head_black_noble_hat
-  description: ""
+  description: "A medieval black cap."
   components:
     - type: Sprite
       sprite: Civ14/Clothing/exported/hats/noblehat2.rsi

+ 4 - 6
Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_shoes.yml

@@ -1346,17 +1346,15 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.25
-          Slash: 0.25
+          Blunt: 0.8
+          Slash: 0.7
           Piercing: 0.9
-          Arrow: 0.4
-          Heat: 0.92
+          Arrow: 0.7
     - type: Construction
-
       graph: civ13_shoes_Sabatons
       node: end
       cost: 5
-      material: Cloth
+      material: Steel
       time: 5
 - type: entity
   name: steppe wool shoe

+ 100 - 15
Resources/Prototypes/Civ14/Entities/Clothing/entities_clothing_suit.yml

@@ -3601,18 +3601,59 @@
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.15
-          Slash: 0.15
-          Piercing: 0.9
-          Arrow: 0.1
+          Blunt: 0.3
+          Slash: 0.3
+          Piercing: 0.4
+          Arrow: 0.15
           Heat: 0.85
     - type: Construction
-
       graph: civ13_suit_green_plated_armor
       node: end
       cost: 5
-      material: Cloth
+      material: Steel
       time: 5
+
+- type: entity
+  name: red plated armor
+  parent: ClothingOuterBase
+  id: civ13_suit_red_plated_armor
+  description: A thick, expensive iron armor, covering most of the body.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/suits/knight_red.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/suits/knight_red.rsi
+    - type: STWeight
+      self: 25
+    - type: Armor
+      modifiers:
+        coefficients:
+          Blunt: 0.3
+          Slash: 0.3
+          Piercing: 0.4
+          Arrow: 0.15
+          Heat: 0.85
+- type: entity
+  name: blue plated armor
+  parent: ClothingOuterBase
+  id: civ13_suit_blue_plated_armor
+  description: A thick, expensive iron armor, covering most of the body.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/suits/knight_blue.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/suits/knight_blue.rsi
+    - type: STWeight
+      self: 25
+    - type: Armor
+      modifiers:
+        coefficients:
+          Blunt: 0.3
+          Slash: 0.3
+          Piercing: 0.4
+          Arrow: 0.15
+          Heat: 0.85
+
 - type: entity
   name: seal lifevest
   parent: ClothingOuterBase
@@ -4457,6 +4498,8 @@
       sprite: Civ14/Clothing/exported/suits/chainmail.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/chainmail.rsi
+    - type: STWeight
+      self: 8
     - type: Armor
       modifiers:
         coefficients:
@@ -4727,6 +4770,8 @@
       sprite: Civ14/Clothing/exported/suits/imperial_breastplate.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/imperial_breastplate.rsi
+    - type: STWeight
+      self: 8
     - type: Armor
       modifiers:
         coefficients:
@@ -4781,13 +4826,15 @@
       sprite: Civ14/Clothing/exported/suits/knight_simple.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/knight_simple.rsi
+    - type: STWeight
+      self: 25
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.15
-          Slash: 0.15
-          Piercing: 0.9
-          Arrow: 0.1
+          Blunt: 0.3
+          Slash: 0.3
+          Piercing: 0.4
+          Arrow: 0.15
           Heat: 0.85
     - type: Construction
 
@@ -4833,13 +4880,15 @@
       sprite: Civ14/Clothing/exported/suits/knight_templar.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/knight_templar.rsi
+    - type: STWeight
+      self: 25
     - type: Armor
       modifiers:
         coefficients:
-          Blunt: 0.15
-          Slash: 0.15
-          Piercing: 0.9
-          Arrow: 0.1
+          Blunt: 0.3
+          Slash: 0.3
+          Piercing: 0.4
+          Arrow: 0.15
           Heat: 0.85
     - type: Construction
 
@@ -4858,6 +4907,8 @@
       sprite: Civ14/Clothing/exported/suits/bronze_chestplate.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/bronze_chestplate.rsi
+    - type: STWeight
+      self: 8
     - type: Armor
       modifiers:
         coefficients:
@@ -4877,6 +4928,8 @@
       sprite: Civ14/Clothing/exported/suits/iron_chestplate.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/iron_chestplate.rsi
+    - type: STWeight
+      self: 8
     - type: Armor
       modifiers:
         coefficients:
@@ -4902,6 +4955,8 @@
       sprite: Civ14/Clothing/exported/suits/leather_armor.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/leather_armor.rsi
+    - type: STWeight
+      self: 4
     - type: Armor
       modifiers:
         coefficients:
@@ -4910,12 +4965,32 @@
           Arrow: 0.85
           Heat: 0.85
     - type: Construction
-
       graph: civ13_suit_leather_armor
       node: end
       cost: 6
       material: Leather
       time: 13
+
+- type: entity
+  name: gambeson
+  parent: ClothingOuterBase
+  id: civ13_suit_gambeson
+  description: A thick, padded jacket made of layered cloth, offering basic protection for infantry.
+  components:
+    - type: Sprite
+      sprite: Civ14/Clothing/exported/suits/gambeson.rsi
+    - type: Clothing
+      sprite: Civ14/Clothing/exported/suits/gambeson.rsi
+    - type: STWeight
+      self: 6
+    - type: Armor
+      modifiers:
+        coefficients:
+          Blunt: 0.65
+          Slash: 0.65
+          Arrow: 0.80
+          Heat: 0.85
+
 - type: entity
   name: hauberk
   parent: ClothingOuterBase
@@ -4928,6 +5003,8 @@
       sprite: Civ14/Clothing/exported/suits/hauberk.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/hauberk.rsi
+    - type: STWeight
+      self: 9
     - type: Armor
       modifiers:
         coefficients:
@@ -5009,6 +5086,8 @@
       sprite: Civ14/Clothing/exported/suits/imperial_chinese.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/imperial_chinese.rsi
+    - type: STWeight
+      self: 12
     - type: Armor
       modifiers:
         coefficients:
@@ -5062,6 +5141,8 @@
       sprite: Civ14/Clothing/exported/suits/royalplate.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/royalplate.rsi
+    - type: STWeight
+      self: 25
     - type: Armor
       modifiers:
         coefficients:
@@ -5372,6 +5453,8 @@
       sprite: Civ14/Clothing/exported/suits/ork_plate_elite.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/ork_plate_elite.rsi
+    - type: STWeight
+      self: 28
     - type: Armor
       modifiers:
         coefficients:
@@ -5398,6 +5481,8 @@
       sprite: Civ14/Clothing/exported/suits/ork_plate_commander.rsi
     - type: Clothing
       sprite: Civ14/Clothing/exported/suits/ork_plate_commander.rsi
+    - type: STWeight
+      self: 30
     - type: Armor
       modifiers:
         coefficients:

+ 29 - 0
Resources/Prototypes/Civ14/Entities/Markers/capturable_areas.yml

@@ -0,0 +1,29 @@
+- type: entity
+  name: capturable area
+  id: MarkerCapturableArea
+  parent: MarkerBase
+  abstract: true
+  components:
+    - type: Sprite
+      sprite: Civ14/Markers/areas.rsi
+      state: capture_red
+    - type: CaptureArea
+      name: "Objective Area"
+      captureDuration: 300 # 5 min
+      captureRadius: 6
+
+- type: entity
+  name: capturable area - KOTH Camp
+  id: MarkerKOTHCamp
+  parent: MarkerCapturableArea
+  components:
+    - type: Sprite
+      sprite: Civ14/Markers/areas.rsi
+      state: capture_green
+    - type: CaptureArea
+      name: "the Castle"
+      captureDuration: 300 # 5 min
+      captureRadius: 6
+      capturableFactions:
+        - England
+        - France

+ 29 - 0
Resources/Prototypes/Civ14/Entities/Markers/grace_wall.yml

@@ -0,0 +1,29 @@
+- type: entity
+  name: grace wall
+  id: MarkerGracewall
+  parent: MarkerBase
+  components:
+    - type: Sprite
+      sprite: Civ14/Markers/areas.rsi
+      state: gracewall
+    - type: GracewallArea # Use the new component
+      gracewallActive: true
+      gracewallRadius: 1.5
+    - type: Physics
+      bodyType: Static
+    - type: Fixtures
+      fixtures:
+        gracewall: # Define a fixture for the area
+          shape: !type:PhysShapeAabb
+            bounds: "-1.5, -1.5, 1.5, 1.5"
+          # Collision layer/mask will be managed by the GracewallRuleSystem
+          density: 100
+          hard: true
+          mask:
+            - LowImpassable
+            - MidImpassable
+            - HighImpassable
+          layer:
+            - LowImpassable
+            - MidImpassable
+            - HighImpassable

+ 27 - 0
Resources/Prototypes/Civ14/Entities/Markers/jobs.yml

@@ -11,3 +11,30 @@
       layers:
         - state: green
         - state: passenger
+
+- type: entity
+  id: SpawnPointEnglish
+  parent: SpawnPointJobBase
+  name: english spawner
+  suffix: faction spawn
+  components:
+    - type: SpawnPoint
+      faction: England
+      spawn_type: Faction
+    - type: Sprite
+      sprite: Markers/cross.rsi
+      layers:
+        - state: red
+- type: entity
+  id: SpawnPointFrench
+  parent: SpawnPointJobBase
+  name: french spawner
+  suffix: faction spawn
+  components:
+    - type: SpawnPoint
+      faction: France
+      spawn_type: Faction
+    - type: Sprite
+      sprite: Markers/cross.rsi
+      layers:
+        - state: blue

+ 9 - 2
Resources/Prototypes/Civ14/Entities/Objects/Tools/tools.yml

@@ -34,6 +34,13 @@
         types:
           Piercing: 8
           Blunt: 3
+    - type: Wieldable
+    - type: IncreaseDamageOnWield
+      damage:
+        groups:
+          Brute: 6
+        types:
+          Structural: 30
     - type: STWeight
       self: 5
     - type: Item
@@ -87,7 +94,7 @@
         types:
           Blunt: 4
           Slash: 8
-          Structural: 4
+          Structural: 6
     - type: STWeight
       self: 3
     - type: Item
@@ -119,7 +126,7 @@
         types:
           Blunt: 5
           Slash: 8
-          Structural: 4
+          Structural: 6
     - type: STWeight
       self: 3
     - type: Item

+ 7 - 5
Resources/Prototypes/Civ14/Entities/Objects/Weapons/spears.yml

@@ -40,7 +40,7 @@
       soundHit:
         path: /Audio/Weapons/bladeslice.ogg
     - type: STWeight
-      self: 16        
+      self: 16
     - type: Item
       heldPrefix: null
       sprite: Civ14/Weapons/dory.rsi
@@ -66,7 +66,7 @@
       soundHit:
         path: /Audio/Weapons/bladeslice.ogg
     - type: STWeight
-      self: 16        
+      self: 16
     - type: Item
       heldPrefix: null
       sprite: Civ14/Weapons/sarissa.rsi
@@ -85,13 +85,15 @@
       sprite: Civ14/Weapons/pike.rsi
 
     - type: MeleeWeapon
+      range: 1.8
+      attackRate: 0.8
       damage:
         types:
-          Piercing: 15   
+          Piercing: 15
       soundHit:
         path: /Audio/Weapons/bladeslice.ogg
     - type: STWeight
-      self: 12     
+      self: 12
     - type: Item
       heldPrefix: null
       sprite: Civ14/Weapons/pike.rsi
@@ -119,7 +121,7 @@
       soundHit:
         path: /Audio/Weapons/bladeslice.ogg
     - type: IncreaseDamageOnWield
-      damage: 
+      damage:
         types:
           Piercing: 5
           Slash: 2

+ 51 - 17
Resources/Prototypes/Civ14/Entities/Structures/Craft/shields.yml

@@ -7,7 +7,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/nguni_shield.rsi
-
+    - type: STWeight
+      self: 5
     - type: Blocking
       passiveBlockModifier:
         coefficients:
@@ -46,7 +47,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/steel_shield.rsi
-
+    - type: STWeight
+      self: 7
     - type: Blocking
       passiveBlockModifier:
         coefficients:
@@ -83,7 +85,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/iron_shield.rsi
-
+    - type: STWeight
+      self: 7
     - type: Blocking
       passiveBlockModifier:
         coefficients:
@@ -120,7 +123,11 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/semioval_shield.rsi
-
+    - type: Clothing
+      sprite: Civ14/Weapons/semioval_shield.rsi
+      slots: [Back]
+    - type: STWeight
+      self: 8
     - type: Blocking
       passiveBlockModifier:
         coefficients:
@@ -157,18 +164,19 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/semioval_shield_templar.rsi
-
+    - type: STWeight
+      self: 8
     - type: Blocking
       passiveBlockModifier:
         coefficients:
-          Blunt: 0.850
-          Slash: 0.850
-          Piercing: 0.850
+          Blunt: 0.630
+          Slash: 0.630
+          Piercing: 0.630
       activeBlockModifier:
         coefficients:
-          Blunt: 0.748
-          Slash: 0.748
-          Piercing: 0.748
+          Blunt: 0.554
+          Slash: 0.554
+          Piercing: 0.554
         flatReductions:
           Blunt: 1
           Slash: 1
@@ -195,17 +203,19 @@
       state: icon
       sprite: Civ14/Weapons/semioval_shield_templar2.rsi
 
+    - type: STWeight
+      self: 8
     - type: Blocking
       passiveBlockModifier:
         coefficients:
-          Blunt: 0.850
-          Slash: 0.850
-          Piercing: 0.850
+          Blunt: 0.630
+          Slash: 0.630
+          Piercing: 0.630
       activeBlockModifier:
         coefficients:
-          Blunt: 0.748
-          Slash: 0.748
-          Piercing: 0.748
+          Blunt: 0.554
+          Slash: 0.554
+          Piercing: 0.554
         flatReductions:
           Blunt: 1
           Slash: 1
@@ -231,6 +241,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/orc_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -268,6 +280,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/bronze_shield.rsi
+    - type: STWeight
+      self: 6
 
     - type: Blocking
       passiveBlockModifier:
@@ -305,6 +319,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/athenian_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -341,6 +357,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/pegasus_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -377,6 +395,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/owl_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -414,6 +434,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/spartan_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -451,6 +473,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/egyptian_shield.rsi
+    - type: STWeight
+      self: 5
 
     - type: Blocking
       passiveBlockModifier:
@@ -488,6 +512,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/scutum.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -528,6 +554,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/prae_roman_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -565,6 +593,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/roman_shield.rsi
+    - type: STWeight
+      self: 7
 
     - type: Blocking
       passiveBlockModifier:
@@ -610,6 +640,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/chimalli.rsi
+    - type: STWeight
+      self: 6
 
     - type: Blocking
       passiveBlockModifier:
@@ -650,6 +682,8 @@
     - type: Sprite
       state: icon
       sprite: Civ14/Weapons/arabic_shield.rsi
+    - type: STWeight
+      self: 6
 
     - type: Blocking
       passiveBlockModifier:

+ 285 - 0
Resources/Prototypes/Civ14/Entities/Structures/Walls/barricades.yml

@@ -0,0 +1,285 @@
+- type: entity
+  id: CrenelatedWall
+  description: A stone wall with crenelations.
+  parent: BaseBarricade
+  name: crenelated wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: crenelated_wall
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Physics
+    - type: Fixtures
+      fixtures:
+        fix1:
+          shape: !type:PhysShapeAabb
+            bounds: "-0.49,-0.45,0.49,-0.02"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+    - type: Damageable
+      damageModifierSet: Rock
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 250
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Barricade
+      blocking: 80
+    - type: AtmosExposed
+- type: entity
+  id: CrenelatedWallFull
+  description: A stone wall with crenelations.
+  parent: CrenelatedWall
+  name: crenelated wall
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/walls.rsi
+      state: stone_brick
+      drawdepth: FloorObjects
+    - type: Fixtures
+      fixtures:
+        fix1:
+          shape: !type:PhysShapeAabb {}
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+
+# sandbags and other similar stuff
+- type: entity
+  id: BarricadeSandbags
+  description: A sandbag wall.
+  parent: BaseBarricade
+  name: sandbag wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: sandbag
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Physics
+    - type: Fixtures
+      fixtures:
+        fix1:
+          shape: !type:PhysShapeAabb
+            bounds: "-0.49,-0.45,0.49,-0.02"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+    - type: Damageable
+      damageModifierSet: Inert
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 180
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Barricade
+      blocking: 66
+    - type: AtmosExposed
+
+- type: entity
+  id: BarricadeSandbagsMedium
+  description: An unfinished sandbag wall.
+  parent: BarricadeSandbags
+  name: sandbag wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: sandbag_66%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 50
+- type: entity
+  id: BarricadeSandbagsLow
+  description: An unfinished sandbag wall.
+  parent: BarricadeSandbags
+  name: sandbag wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: sandbag_33%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 33
+# snow wall
+- type: entity
+  id: BarricadeSnowwall
+  description: A snowwall barricade.
+  parent: BarricadeSandbags
+  name: snow wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: snow_wall
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Damageable
+      damageModifierSet: Snow
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 110
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+
+- type: entity
+  id: BarricadeSnowwallMedium
+  description: An unfinished snow barricade.
+  parent: BarricadeSnowwall
+  name: snow wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: snow_wall_66%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 50
+- type: entity
+  id: BarricadeSnowwallLow
+  description: An unfinished snow barricade.
+  parent: BarricadeSnowwall
+  name: snow wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: snow_wall_33%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 33
+
+# dirt wall
+- type: entity
+  id: BarricadeDirtwall
+  description: A dirt barricade.
+  parent: BarricadeSandbags
+  name: dirt wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: dirt_wall
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Damageable
+      damageModifierSet: Inert
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 130
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+
+- type: entity
+  id: BarricadeDirtwallMedium
+  description: An unfinished dirt barricade.
+  parent: BarricadeDirtwall
+  name: dirt wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: dirt_wall_66%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 50
+
+- type: entity
+  id: BarricadeDirtwallLow
+  description: An unfinished dirt barricade.
+  parent: BarricadeDirtwall
+  name: dirt wall
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: dirt_wall_33%
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Barricade
+      blocking: 33
+
+- type: entity
+  id: BarricadeStonewall
+  description: A low stone wall.
+  parent: BarricadeSandbags
+  name: stone barricade
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: rock_barricade
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Damageable
+      damageModifierSet: Rock
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 200
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+
+- type: entity
+  id: BarricadeSandstonewall
+  description: A low sandstone wall.
+  parent: BarricadeSandbags
+  name: sandstone barricade
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: Sprite
+      sprite: Civ14/Structures/barricades.rsi
+      state: sandstone_barricade
+      drawdepth: FloorObjects
+      noRot: true
+    - type: Damageable
+      damageModifierSet: Rock
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 200
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]

+ 133 - 0
Resources/Prototypes/Civ14/Entities/Structures/Walls/castle_gate.yml

@@ -0,0 +1,133 @@
+- type: entity
+  id: CastleGate
+  description: A castle gate with metal bars.
+  parent: BaseBarricade
+  name: castle gate
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/Doors/doors_64x32.rsi
+      state: gate_large_closed
+      drawdepth: Overdoors
+      noRot: true
+    - type: Physics
+    - type: Fixtures
+      fixtures:
+        fix1:
+          density: 1000
+          shape: !type:PhysShapeAabb
+            bounds: "-1.49,-0.49,1.49,0.49"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+    - type: Damageable
+      damageModifierSet: Rock
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 300
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Barricade
+      blocking: 80
+    - type: AtmosExposed
+
+- type: entity
+  id: CastleGateSmallOpen
+  description: A castle gate with metal bars.
+  parent: BaseBarricade
+  name: castle gate
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/Doors/material_doors.rsi
+      state: gate1
+      drawdepth: Overdoors
+      noRot: true
+    - type: Fixtures
+      fixtures:
+        fix1:
+          density: 1000
+          shape: !type:PhysShapeAabb
+            bounds: "-1.49,-0.49,1.49,0.49"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+          hard: false
+    - type: Barricade
+      blocking: 15
+
+- type: entity
+  id: CastleSmallGate
+  description: A castle gate with metal bars.
+  parent: BaseBarricade
+  name: castle gate
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/Doors/material_doors.rsi
+      state: gate0
+      drawdepth: Overdoors
+      noRot: true
+    - type: Physics
+    - type: Fixtures
+      fixtures:
+        fix1:
+          density: 1000
+          shape: !type:PhysShapeAabb
+            bounds: "-0.49,-0.49,0.49,0.49"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+    - type: Damageable
+      damageModifierSet: Rock
+      damageContainer: StructuralInorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 300
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Barricade
+      blocking: 80
+    - type: AtmosExposed
+
+- type: entity
+  id: CastleGateOpen
+  description: A castle gate with metal bars.
+  parent: BaseBarricade
+  name: castle gate
+  placement:
+    mode: SnapgridCenter
+  components:
+    - type: InteractionOutline
+    - type: Sprite
+      sprite: Civ14/Structures/Doors/doors_64x32.rsi
+      state: gate_large_open
+      drawdepth: Overdoors
+      noRot: true
+    - type: Fixtures
+      fixtures:
+        fix1:
+          density: 1000
+          shape: !type:PhysShapeAabb
+            bounds: "-0.49,-0.49,0.49,0.49"
+          mask:
+            - FullTileMask
+          layer:
+            - WallLayer
+          hard: false
+    - type: Barricade
+      blocking: 15

+ 49 - 1
Resources/Prototypes/Civ14/Entities/Structures/Walls/walls.yml

@@ -181,6 +181,54 @@
       key: walls
       base: oldwood
 
+- type: entity
+  parent: BaseWall
+  id: MedievalWall
+  name: medieval wall
+  description: A medieval wood and lime washed plaster wall.
+  components:
+    - type: MeleeSound
+      soundGroups:
+        Brute:
+          path: "/Audio/Weapons/boxingpunch1.ogg"
+    - type: Tag
+      tags:
+        - Wooden
+        - Wall
+    - type: Sprite
+      sprite: Civ14/Structures/Walls/medieval.rsi
+    - type: Icon
+      sprite: Civ14/Structures/Walls/medieval.rsi
+    - type: Damageable
+      damageContainer: StructuralInorganic
+      damageModifierSet: Wood
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 120
+          behaviors:
+            - !type:PlaySoundBehavior
+              sound:
+                collection: WoodDestroy
+            - !type:SpawnEntitiesBehavior
+              spawn:
+                MaterialWoodPlank:
+                  min: 1
+                  max: 2
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Construction
+      agemin: 2
+      agemax: 8
+      graph: MedievalWall
+      node: end
+      cost: 5
+      time: 8
+      material: WoodPlank
+    - type: IconSmooth
+      key: walls
+      base: medieval
+
 # stone walls
 - type: entity
   parent: BaseWall
@@ -475,7 +523,7 @@
   id: WallRockIndestructible
   parent: BaseWall
   name: rock
-  suffix: planetmap
+  suffix: indestructible
   description: This wall cannot be destroyed.
   components:
     - type: Transform

+ 4 - 0
Resources/Prototypes/Civ14/Entities/Structures/Walls/windows.yml

@@ -120,7 +120,11 @@
     - type: Icon
       sprite: Civ14/Structures/windows.rsi
       state: windownew_frame
+    - type: IconSmooth
+      key: walls
+      mode: NoSprite
     - type: Construction
+      graph: WoodWindowClassic
       agemin: 0
       agemax: 8
       node: end

+ 90 - 0
Resources/Prototypes/Civ14/Entities/Structures/warmth.yml

@@ -74,6 +74,48 @@
             Medium: { visible: true, state: s_brazier2 }
             High: { visible: true, state: s_brazier2 }
 
+- type: entity
+  id: StovePotbelly
+  parent: Brazier
+  name: potbelly stove
+  description: An iron stove.
+  components:
+    - type: Sprite
+      noRot: true
+      sprite: Civ14/Objects/kitchen.rsi
+      layers:
+        - state: oven_off
+        - map: ["enum.EntityHeaterVisuals.Setting"]
+          shader: unshaded
+          visible: false
+    - type: PointLight
+      enabled: false
+      radius: 4
+      energy: 5
+      color: "#E25822"
+    - type: HeatEmitter
+      heatingRate: 12
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 250
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+    - type: Construction
+      graph: StovePotbelly
+      node: end
+      agemin: 2
+      agemax: 8
+    - type: GenericVisualizer
+      visuals:
+        enum.EntityHeaterVisuals.Setting:
+          enum.EntityHeaterVisuals.Setting:
+            Off: { visible: false, state: oven_off }
+            Low: { visible: true, state: oven_on }
+            Medium: { visible: true, state: oven_on }
+            High: { visible: true, state: oven_on }
+
 - type: entity
   name: tiki torch
   parent: Torch
@@ -127,7 +169,55 @@
       thresholds:
         - trigger: !type:DamageTrigger
             damage: 40
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
 
+- type: entity
+  name: iron lantern
+  parent: Torch
+  id: IronLantern
+  description: A fueled iron lantern.
+  components:
+    - type: Sprite
+      sprite: Civ14/Objects/lighting.rsi
+      layers:
+        - map: [enum.ExpendableLightVisualLayers.Base]
+          state: lantern
+        - map: [enum.ExpendableLightVisualLayers.Glow]
+          state: lantern-on
+          visible: true
+          shader: unshaded
+    - type: Icon
+      sprite: Civ14/Objects/lighting.rsi
+      state: lantern
+    - type: Construction
+      graph: IronLantern
+      node: end
+    - type: PointLight
+      enabled: true
+      color: "#E25822"
+      radius: 4.0
+      energy: 5.0
+      netsync: true
+    - type: Transform
+      anchored: true
+    - type: Physics
+      bodyType: Static
+    - type: Fixtures
+      fixtures:
+        fix1:
+          shape: !type:PhysShapeAabb
+            bounds: "-0.25,-0.25,0.25,0.25"
+          density: 190
+          mask:
+            - MachineMask
+          layer:
+            - MachineLayer
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 70
           behaviors:
             - !type:DoActsBehavior
               acts: ["Destruction"]

+ 29 - 1
Resources/Prototypes/Civ14/Recipes/Construction/floors.yml

@@ -52,12 +52,40 @@
             - !type:SetStackCount
               amount: 4
           steps:
-            # Needs StackType ID
             - material: Stone
               amount: 1
     - node: end
       entity: FloorTileItemRoad
 
+- type: construction
+  name: cobblestone floor
+  id: TileCobblestone
+  graph: TileCobblestone
+  startNode: start
+  targetNode: end
+  category: construction-category-tiles
+  description: A cobblestone floor.
+  icon: { sprite: Civ14/Objects/floors.rsi, state: cobble_vertical_dark }
+  objectType: Item
+  agemin: 1
+  agemax: 8
+
+- type: constructionGraph
+  id: TileCobblestone
+  start: start
+  graph:
+    - node: start
+      edges:
+        - to: end
+          completed:
+            - !type:SetStackCount
+              amount: 4
+          steps:
+            - material: Stone
+              amount: 1
+    - node: end
+      entity: FloorTileItemCobblestone
+
 - type: construction
   name: bridging tile
   id: TileBridge

+ 54 - 0
Resources/Prototypes/Civ14/Recipes/Construction/warmth.yml

@@ -25,6 +25,33 @@
     - node: end
       entity: Brazier
 
+- type: construction
+  name: potbelly stove
+  id: StovePotbelly
+  graph: StovePotbelly
+  startNode: start
+  targetNode: end
+  category: construction-category-structures
+  description: An iron stove.
+  icon: { sprite: Civ14/Objects/kitchen.rsi, state: oven_off }
+  objectType: Structure
+  agemin: 2
+  agemax: 8
+
+- type: constructionGraph
+  id: StovePotbelly
+  start: start
+  graph:
+    - node: start
+      edges:
+        - to: end
+          steps:
+            - material: Iron
+              amount: 6
+              doAfter: 10
+    - node: end
+      entity: StovePotbelly
+
 - type: construction
   name: tiki torch
   id: TikiTorch
@@ -51,3 +78,30 @@
               doAfter: 8
     - node: end
       entity: TikiTorch
+
+- type: construction
+  name: iron lantern
+  id: IronLantern
+  graph: IronLantern
+  startNode: start
+  targetNode: end
+  category: construction-category-misc
+  description: A static light source.
+  icon: { sprite: Civ14/Objects/lighting.rsi, state: lantern-on }
+  objectType: Structure
+  agemin: 2
+  agemax: 8
+
+- type: constructionGraph
+  id: IronLantern
+  start: start
+  graph:
+    - node: start
+      edges:
+        - to: end
+          steps:
+            - material: Iron
+              amount: 3
+              doAfter: 8
+    - node: end
+      entity: IronLantern

+ 29 - 0
Resources/Prototypes/Civ14/Recipes/Walls/walls.yml

@@ -90,6 +90,35 @@
     - node: end
       entity: StrawWall
 
+- type: construction
+  name: medieval wall
+  id: MedievalWall
+  graph: MedievalWall
+  startNode: start
+  targetNode: end
+  category: construction-category-structures
+  description: A medieval wood and lime washed plaster wall.
+  icon:
+    sprite: Civ14/Structures/Walls/medieval.rsi
+    state: full
+  objectType: Structure
+  agemin: 0
+  agemax: 8
+  placementMode: SnapgridCenter
+- type: constructionGraph
+  id: MedievalWall
+  start: start
+  graph:
+    - node: start
+      edges:
+        - to: end
+          steps:
+            - material: WoodPlank
+              amount: 5
+              doAfter: 5
+    - node: end
+      entity: MedievalWall
+
 - type: constructionGraph
   id: LogWall
   start: start

+ 629 - 0
Resources/Prototypes/Civ14/StatusIcon/faction.yml

@@ -0,0 +1,629 @@
+- type: factionIcon
+  id: AHFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ah_basic
+
+- type: factionIcon
+  id: ArabFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: arab_basic
+
+- type: factionIcon
+  id: BallasFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ballas
+
+- type: factionIcon
+  id: BarbarianFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: barbarian_basic
+
+- type: factionIcon
+  id: BlugoslaviaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: blugoslavia
+
+- type: factionIcon
+  id: BritFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: brit_basic
+
+- type: factionIcon
+  id: ChechenFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: chechen_basic
+
+- type: factionIcon
+  id: DanishFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: danish_basic
+
+- type: factionIcon
+  id: EasternFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: eastern
+
+- type: factionIcon
+  id: EmpireFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: empire
+
+- type: factionIcon
+  id: EngFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: eng_basic
+
+- type: factionIcon
+  id: FinnishFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: finnish_basic
+
+- type: factionIcon
+  id: FpFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: fp_basic
+
+- type: factionIcon
+  id: Fr2Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: fr2_basic
+
+- type: factionIcon
+  id: FrFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: fr_basic
+
+- type: factionIcon
+  id: Ger0Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ger0_basic
+
+- type: factionIcon
+  id: Ger2Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ger2_basic
+
+- type: factionIcon
+  id: Ger3Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ger3_basic
+
+- type: factionIcon
+  id: GerFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ger_basic
+
+- type: factionIcon
+  id: GreekFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: greek_basic
+
+- type: factionIcon
+  id: HezFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: hez_basic
+
+- type: factionIcon
+  id: IchiwaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ichiwa
+
+- type: factionIcon
+  id: IdfFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: idf_basic
+
+- type: factionIcon
+  id: ImpFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: imp_basic
+
+- type: factionIcon
+  id: IndFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ind_basic
+
+- type: factionIcon
+  id: IsisFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: isis_basic
+
+- type: factionIcon
+  id: ItFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: it_basic
+
+- type: factionIcon
+  id: JpFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: jp_basic
+
+- type: factionIcon
+  id: NlFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: nl_basic
+
+- type: factionIcon
+  id: NlOldFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: nl_old_basic
+
+- type: factionIcon
+  id: NorwayFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: norway_basic
+
+- type: factionIcon
+  id: OtFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ot_basic
+
+- type: factionIcon
+  id: PirateFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: pirate_basic
+
+- type: factionIcon
+  id: PoliceFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: police
+
+- type: factionIcon
+  id: PolFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: pol_basic
+
+- type: factionIcon
+  id: Pt2Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: pt2_basic
+
+- type: factionIcon
+  id: PtFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: pt_basic
+
+- type: factionIcon
+  id: RebelFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: rebel_basic
+
+- type: factionIcon
+  id: RedmeniaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: redmenia
+
+- type: factionIcon
+  id: RednikovFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: rednikov
+
+- type: factionIcon
+  id: Rn2Faction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: rn2_basic
+
+- type: factionIcon
+  id: RnFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: rn_basic
+
+- type: factionIcon
+  id: RobbersFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: robbers
+
+- type: factionIcon
+  id: RocFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: roc_basic
+
+- type: factionIcon
+  id: RomanFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: roman_basic
+
+- type: factionIcon
+  id: RuFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ru_basic
+
+- type: factionIcon
+  id: SovFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: sov_basic
+
+- type: factionIcon
+  id: SpnFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: spn_basic
+
+- type: factionIcon
+  id: SprFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: spr_basic
+
+- type: factionIcon
+  id: SpFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: sp_basic
+
+- type: factionIcon
+  id: StormcloakFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: stormcloak
+
+- type: factionIcon
+  id: SwedishFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: swedish_basic
+
+- type: factionIcon
+  id: SyriaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: syria_basic
+
+- type: factionIcon
+  id: SyriaFsaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: syria_fsa
+
+- type: factionIcon
+  id: UkrFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: ukr_basic
+
+- type: factionIcon
+  id: UnFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: un
+
+- type: factionIcon
+  id: UpaFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: upa_basic
+
+- type: factionIcon
+  id: UsFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: us_basic
+
+- type: factionIcon
+  id: VcFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: vc_basic
+
+- type: factionIcon
+  id: VnFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: vn_basic
+
+- type: factionIcon
+  id: WagnerFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: wagner
+
+- type: factionIcon
+  id: WesternFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: western
+
+- type: factionIcon
+  id: YamaguchiFaction
+  priority: 1
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: yamaguchi

+ 176 - 0
Resources/Prototypes/Civ14/StatusIcon/job.yml

@@ -0,0 +1,176 @@
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 1
+  id: JobIconCommander
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: commander
+  jobName: job-name-commander
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  id: JobIconIMaj
+  priority: 2
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_maj
+  jobName: job-name-i-maj
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 3
+  id: JobIconICpt
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_cpt
+  jobName: job-name-i-cpt
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 4
+  id: JobIconOfficer
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: officer
+  jobName: job-name-officer
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 4
+  id: JobIconILt
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_lt
+  jobName: job-name-i-lt
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 5
+  id: JobIconISsgt
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_ssgt
+  jobName: job-name-i-ssgt
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 6
+  id: JobIconISgt
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_sgt
+  jobName: job-name-i-sgt
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 6
+  id: JobIconNco
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: nco
+  jobName: job-name-nco
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 7
+  id: JobIconICpl
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: i_cpl
+  jobName: job-name-i-cpl
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 8
+  id: JobIconDesignatedMarksman
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: designated_marksman
+  jobName: job-name-designated-marksman
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 8
+  id: JobIconMedic
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: medic
+  jobName: job-name-medic
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 8
+  id: JobIconMg
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: mg
+  jobName: job-name-mg
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconRifleman
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: rifleman
+  jobName: job-name-rifleman
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconSoldier
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_ranks.rsi
+    state: soldier
+  jobName: job-name-soldier
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconSword
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_squads.rsi
+    state: inf_sword
+  jobName: job-name-infantry-sword
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconHeavy
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_squads.rsi
+    state: inf_heavy
+  jobName: job-name-infantry-heavy
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconSpear
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_squads.rsi
+    state: inf_spear
+  jobName: job-name-infantry-spear
+
+- type: jobIcon
+  locationPreference: Left
+  parent: JobIcon
+  priority: 9
+  id: JobIconRanged
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_squads.rsi
+    state: inf_ranged
+  jobName: job-name-infantry-ranged

+ 72 - 0
Resources/Prototypes/Civ14/Tiles/floors.yml

@@ -234,6 +234,78 @@
             - !type:DoActsBehavior
               acts: ["Destruction"]
 
+- type: tile
+  id: FloorCobblestone
+  name: cobblestone floor
+  sprite: /Textures/Civ14/Objects/floors.rsi/cobble_vertical_dark.png
+  variants: 1
+  baseTurf: Plating
+  weather: true
+  isSubfloor: false
+  deconstructTools: [Prying]
+  footstepSounds:
+    collection: FootstepFloor
+  barestepSounds:
+    collection: FootstepFloor
+  itemDrop: FloorTileItemCobblestone
+  heatCapacity: 10000
+
+- type: stack
+  id: FloorTileCobblestone
+  name: cobblestone floor
+  spawn: FloorTileItemCobblestone
+  maxCount: 30
+
+- type: entity
+  name: cobblestone floor
+  parent: FloorTileItemBase
+  id: FloorTileItemCobblestone
+  components:
+    - type: Sprite
+      sprite: Civ14/Objects/floors.rsi
+      state: cobble_vertical_dark
+    - type: Item
+      heldPrefix: cobblestone
+    - type: FloorTile
+      outputs:
+        - Plating
+        - FloorRoad
+    - type: Stack
+      stackType: FloorTileCobblestone
+    - type: SpawnAfterInteract #Nuke after convert to FloorTile
+      prototype: FloorCobblestone
+      doAfter: 0.5
+      removeOnInteract: true
+    - type: Construction
+      graph: TileCobblestone
+      node: end
+
+- type: entity
+  id: FloorCobblestone
+  parent: BaseStructure
+  name: cobblestone floor
+  description: "A cobblestone floor."
+  components:
+    - type: Sprite
+      sprite: Civ14/Objects/floors.rsi
+      state: cobble_vertical_dark
+      drawdepth: FloorTiles
+    - type: Tag
+      tags:
+        - ForceFixRotations
+    - type: Physics
+      canCollide: false
+    - type: Fixtures
+    - type: Damageable
+      damageContainer: Inorganic
+    - type: Destructible
+      thresholds:
+        - trigger: !type:DamageTrigger
+            damage: 180
+          behaviors:
+            - !type:DoActsBehavior
+              acts: ["Destruction"]
+
 - type: tile
   id: FloorBridge
   name: bridge

+ 33 - 8
Resources/Prototypes/Damage/modifier_sets.yml

@@ -41,14 +41,38 @@
 - type: damageModifierSet
   id: Rock
   coefficients:
-    Structural: 4
-    Blunt: 0.5
-    Slash: 0.25
-    Piercing: 0.75
-    Heat: 0.9
+    Structural: 3
+    Blunt: 0.1
+    Slash: 0.05
+    Piercing: 0.15
+    Heat: 0.1
   flatReductions:
     Blunt: 5
+    Slash: 5
 
+#this is for sandbags and the like
+- type: damageModifierSet
+  id: Inert
+  coefficients:
+    Structural: 0.5
+    Blunt: 0.3
+    Slash: 1
+    Piercing: 0.2
+    Heat: 1
+  flatReductions:
+    Blunt: 5
+
+- type: damageModifierSet
+  id: Snow
+  coefficients:
+    Structural: 0.7
+    Blunt: 0.25
+    Slash: 35
+    Piercing: 0.5
+    Heat: 4
+  flatReductions:
+    Blunt: 5
+    Slash: 5
 - type: damageModifierSet
   id: PerforatedMetallic
   coefficients:
@@ -137,13 +161,14 @@
 - type: damageModifierSet
   id: Wood
   coefficients:
-    Blunt: 0.5
-    Slash: 2.0
-    Piercing: 1.0
+    Blunt: 0.1
+    Slash: 0.3
+    Piercing: 0.2
     Heat: 2.0
     Structural: 0.5
   flatReductions:
     Blunt: 5
+    Piercing: 5
 
 - type: damageModifierSet
   id: Web # Very flammable, can be easily hacked and slashed, but shooting or hitting it is another story.

+ 9 - 0
Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml

@@ -55,6 +55,15 @@
       state: sheetbrown
     - type: Clothing
       sprite: Clothing/Neck/Bedsheets/brown.rsi
+# only because this one does not have the white sheet underneath and looks "older"
+- type: entity
+  id: BedsheetBrownCiv
+  parent: BedsheetBrown
+  name: brown bedsheet
+  components:
+    - type: Sprite
+      sprite: Civ14/Objects/items.rsi
+      state: sheetbrown
 
 - type: entity
   id: BedsheetCaptain

+ 21 - 1
Resources/Prototypes/Entities/Objects/Shields/shields.yml

@@ -8,6 +8,8 @@
     - type: Sprite
       sprite: Objects/Weapons/Melee/shields.rsi
       state: riot-icon
+    - type: STWeight
+      self: 5
     - type: Item
       sprite: Objects/Weapons/Melee/shields.rsi
       size: Ginormous
@@ -133,7 +135,11 @@
       sprite: Civ14/Weapons/buckler.rsi
       state: buckler-icon
     - type: Item
-      heldPrefix: buckler
+      heldPrefix: null
+      sprite: Civ14/Weapons/buckler.rsi
+    - type: Clothing
+      sprite: Civ14/Weapons/buckler.rsi
+      slots: [Back]
     - type: Blocking
       passiveBlockModifier:
         coefficients:
@@ -163,6 +169,8 @@
               acts: ["Destruction"]
     - type: StaticPrice
       price: 150
+    - type: STWeight
+      self: 5
 
 - type: entity
   name: red wooden buckler
@@ -173,6 +181,12 @@
     - type: Sprite
       sprite: Civ14/Weapons/red_buckler.rsi
       state: icon
+    - type: Clothing
+      sprite: Civ14/Weapons/red_buckler.rsi
+      slots: [Back]
+    - type: Item
+      sprite: Civ14/Weapons/red_buckler.rsi
+      heldPrefix: null
     - type: Construction
       graph: WoodenBuckler
       node: woodenBucklerRed
@@ -186,6 +200,12 @@
     - type: Sprite
       sprite: Civ14/Weapons/blue_buckler.rsi
       state: icon
+    - type: Clothing
+      sprite: Civ14/Weapons/blue_buckler.rsi
+      slots: [Back]
+    - type: Item
+      sprite: Civ14/Weapons/blue_buckler.rsi
+      heldPrefix: null
     - type: Construction
       graph: WoodenBuckler
       node: woodenBucklerBlue

+ 17 - 2
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml

@@ -76,8 +76,23 @@
       angle: 0
       damage:
         types:
-          Piercing: 22
-
+          Piercing: 20
+- type: entity
+  parent: BaseArrow
+  id: ArrowIron
+  name: arrow
+  description: A basic arrow with an iron tip.
+  components:
+    - type: Sprite
+      sprite: Civ14/Objects/weapons.rsi
+      state: arrow_iron
+    - type: ThrowingAngle
+      angle: 225
+    - type: Projectile
+      angle: 0
+      damage:
+        types:
+          Piercing: 25
 - type: entity
   parent: BaseArrow
   id: ArrowImprovised

+ 3 - 1
Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml

@@ -20,7 +20,9 @@
     - type: Tool
       qualities:
         - Slicing
-
+    - type: Clothing
+      sprite: Civ14/Weapons/armingsword.rsi
+      slots: [Belt]
 - type: entity
   name: captain's sabre
   parent: [BaseSword, BaseCommandContraband]

+ 14 - 0
Resources/Prototypes/Entities/Stations/nanotrasen.yml

@@ -16,3 +16,17 @@
   categories: [HideSpawnMenu]
   components:
     - type: Transform
+
+- type: entity
+  id: StandardStationTDM
+  parent:
+    - BaseStation
+    - BaseStationJobsSpawning
+    - BaseStationRecords
+  categories: [HideSpawnMenu]
+  components:
+    - type: Transform
+    - type: NpcFactionMember
+      factions:
+        - France
+        - England

+ 1 - 1
Resources/Prototypes/Entities/Structures/Walls/asteroid.yml

@@ -706,7 +706,7 @@
     - type: SoundOnGather
     - type: Damageable
       damageContainer: StructuralInorganic
-      damageModifierSet: Metallic
+      damageModifierSet: Rock
     - type: OreVein
       oreChance: 1
       oreRarityPrototypeId: RandomOreDistributionCiv

+ 23 - 0
Resources/Prototypes/GameRules/roundstart.yml

@@ -14,6 +14,17 @@
       respawnDelay: 300
       deleteBody: false
 
+#shorter respawn timer for TDM
+- type: entity
+  parent: BaseGameRule
+  id: RespawnDeadRuleTDM
+  components:
+    - type: RespawnDeadRule
+      alwaysRespawnDead: true
+    - type: RespawnTracker
+      respawnDelay: 120
+      deleteBody: false
+
 - type: entity
   id: InactivityTimeRestart
   parent: BaseGameRule
@@ -41,3 +52,15 @@
   parent: BaseGameRule
   components:
     - type: SecretRule
+
+- type: entity
+  parent: BaseGameRule
+  id: GracewallRule
+  components:
+    - type: GracewallRule
+
+- type: entity
+  parent: BaseGameRule
+  id: CaptureAreaRule
+  components:
+    - type: CaptureAreaRule

+ 1 - 0
Resources/Prototypes/Maps/Pools/default.yml

@@ -1,4 +1,5 @@
 - type: gameMapPool
   id: DefaultMapPool
   maps:
+    - Camp
     - Nomads

+ 31 - 0
Resources/Prototypes/Maps/civ/camp.yml

@@ -0,0 +1,31 @@
+- type: gameMap
+  id: Camp
+  mapName: "Camp (Medieval)"
+  mapPath: /Maps/civ/tdm/camp.yml
+  minPlayers: 0
+  maxPlayers: 128
+  maxRandomOffset: 0
+  randomRotation: false
+  stations:
+    Camp:
+      stationProto: StandardStationTDM
+      components:
+        - type: StationNameSetup
+          mapNameTemplate: "Camp (Medieval)"
+        - type: StationJobs
+          availableJobs:
+            # English
+            EnglishLord: [1, 1]
+            EnglishKnight: [2, 5]
+            EnglishSwordsman: [5, 20]
+            EnglishSpearman: [20, 50]
+            EnglishRanged: [5, 20]
+            EnglishMedic: [1, 3]
+
+            # French
+            FrenchLord: [1, 1]
+            FrenchKnight: [2, 5]
+            FrenchSwordsman: [5, 20]
+            FrenchSpearman: [20, 50]
+            FrenchRanged: [5, 20]
+            FrenchMedic: [1, 3]

+ 283 - 0
Resources/Prototypes/Roles/Jobs/Civ14/TDM/english.yml

@@ -0,0 +1,283 @@
+- type: job
+  id: EnglishLord
+  name: job-name-civ-english-lord
+  faction: England
+  originalName: Lorde
+  description: job-description-civ-english-lord
+  playTimeTracker: JobEnglishLord
+  requirements:
+    - !type:OverallPlaytimeRequirement
+      time: 10800 #3 hrs
+  startingGear: EnglishLordGear
+  icon: "JobIconCommander"
+  supervisors: job-supervisors-nobody
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishLord
+
+- type: startingGear
+  id: EnglishLordGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_iron_chestplate
+    jumpsuit: civ13_uniform_white_tunic_1
+    belt: civ13_sword_arming_sword
+    head: civ13_head_brown_noble_hat
+
+# Knight
+
+- type: job
+  id: EnglishKnight
+  originalName: Knyghte
+  name: job-name-civ-english-knight
+  faction: England
+  description: job-description-civ-english-knight
+  playTimeTracker: JobEnglishKnight
+  requirements:
+    - !type:OverallPlaytimeRequirement
+      time: 5400 #1.5 hrs
+  startingGear: EnglishKnightGear
+  icon: "JobIconOfficer"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishKnight
+
+- type: startingGear
+  id: EnglishKnightGear
+  equipment:
+    shoes: civ13_shoes_Sabatons
+    outerClothing: civ13_suit_red_plated_armor
+    jumpsuit: civ13_uniform_red_tunic
+    head: civ13_head_knight_helmet
+    gloves: civ13_gloves_armored_gauntlets
+  inhand:
+    - civ13_sword_arming_sword
+    - civ13_shield_semioval_iron_shield
+
+# Swordsman
+
+- type: job
+  id: EnglishSwordsman
+  name: job-name-civ-english-man-at-arms
+  faction: England
+  originalName: Man-at-Armes
+  description: job-description-civ-english-man-at-arms
+  playTimeTracker: JobEnglishSwordsman
+  startingGear: EnglishSwordsmanGear
+  randomStartingGears: [EnglishSwordsmanGear, EnglishSwordsmanGear2]
+  icon: "JobIconSword"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishSwordsman
+
+- type: startingGear
+  id: EnglishSwordsmanGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_chainmail
+    neck: civ13_accessory_tabard_red_yellow
+    jumpsuit: civ13_uniform_yellow_red_tunic
+    head: civ13_head_kettle_helmet
+  inhand:
+    - civ13_sword_arming_sword
+    - WoodenBucklerRed
+- type: startingGear
+  id: EnglishSwordsmanGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_chainmail
+    neck: civ13_accessory_tabard_red
+    jumpsuit: civ13_uniform_red_tunic
+    head: civ13_head_kettle_helmet
+  inhand:
+    - civ13_sword_arming_sword
+    - WoodenBucklerRed
+# Spearman
+
+- type: job
+  id: EnglishSpearman
+  name: job-name-civ-english-spearman
+  faction: England
+  originalName: Spereman
+  description: job-description-civ-english-spearman
+  playTimeTracker: JobEnglishSpearman
+  startingGear: EnglishSpearmanGear
+  randomStartingGears: [EnglishSpearmanGear, EnglishSpearmanGear2]
+  icon: "JobIconSpear"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishSpearman
+
+- type: startingGear
+  id: EnglishSpearmanGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_red
+    jumpsuit: civ13_uniform_red_tunic
+    head: civ13_head_protective_conical_helmet
+  inhand:
+    - civ13_spear_pike
+    - WoodenBucklerRed
+- type: startingGear
+  id: EnglishSpearmanGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_red_yellow
+    jumpsuit: civ13_uniform_yellow_red_tunic
+    head: civ13_head_protective_conical_helmet
+  inhand:
+    - civ13_spear_pike
+    - WoodenBucklerRed
+# Ranged
+
+- type: job
+  id: EnglishRanged
+  name: job-name-civ-english-ranged
+  faction: England
+  originalName: Longe Boweman
+  description: job-description-civ-english-ranged
+  playTimeTracker: JobEnglishRanged
+  startingGear: EnglishRangedGear
+  randomStartingGears: [EnglishRangedGear, EnglishRangedGear2]
+  icon: "JobIconRanged"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishRanged
+
+- type: startingGear
+  id: EnglishRangedGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    jumpsuit: civ13_uniform_red_tunic
+    neck: civ13_accessory_tabard_red
+    head: civ13_head_conical_helmet
+    belt: ClothingBeltQuiver
+    pocket1: CombatKnife
+    back: WoodenBucklerRed
+  inhand:
+    - BowImprovised
+  storage:
+    belt:
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+- type: startingGear
+  id: EnglishRangedGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_red_yellow
+    jumpsuit: civ13_uniform_red_tunic
+    head: civ13_head_kettle_helmet
+    belt: ClothingBeltQuiver
+    pocket1: CombatKnife
+    back: WoodenBucklerRed
+  inhand:
+    - BowImprovised
+  storage:
+    belt:
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+# Medic
+
+- type: job
+  id: EnglishMedic
+  name: job-name-civ-english-medic
+  faction: England
+  originalName: Monk
+  description: job-description-civ-english-medic
+  playTimeTracker: JobEnglishMedic
+  startingGear: EnglishMedicGear
+  icon: "JobIconMedic"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - England
+        - type: ShowEnglishFactionIcons
+          factionIcon: EnglishFaction
+
+- type: playTimeTracker
+  id: JobEnglishMedic
+
+- type: startingGear
+  id: EnglishMedicGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_monk_robes
+    jumpsuit: civ13_uniform_light_brown_tunic
+    head: civ13_head_black_noble_hat
+    belt: LeatherClothingBelt
+    pocket1: CombatKnife
+  storage:
+    belt:
+      - ComfreyHealingHerbs
+      - ComfreyHealingHerbs
+      - FoodPoppy
+      - FoodPoppy
+      - YarrowHealingHerbs
+      - YarrowHealingHerbs
+      - ElderflowerHealingHerbs
+      - ElderflowerHealingHerbs
+      - GauzeLeather1
+      - GauzeLeather1

+ 283 - 0
Resources/Prototypes/Roles/Jobs/Civ14/TDM/french.yml

@@ -0,0 +1,283 @@
+- type: job
+  id: FrenchLord
+  name: job-name-civ-french-lord
+  faction: France
+  originalName: Seigneur
+  description: job-description-civ-french-lord
+  playTimeTracker: JobFrenchLord
+  requirements:
+    - !type:OverallPlaytimeRequirement
+      time: 10800 #3 hrs
+  startingGear: FrenchLordGear
+  icon: "JobIconCommander"
+  supervisors: job-supervisors-nobody
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchLord
+
+- type: startingGear
+  id: FrenchLordGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_iron_chestplate
+    jumpsuit: civ13_uniform_white_tunic_1
+    belt: civ13_sword_arming_sword
+    head: civ13_head_brown_noble_hat
+# Knight
+
+- type: job
+  id: FrenchKnight
+  originalName: Chevalier
+  name: job-name-civ-french-knight
+  faction: France
+  description: job-description-civ-french-knight
+  playTimeTracker: JobFrenchKnight
+  requirements:
+    - !type:OverallPlaytimeRequirement
+      time: 5400 #1.5 hrs
+  startingGear: FrenchKnightGear
+  icon: "JobIconOfficer"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchKnight
+
+- type: startingGear
+  id: FrenchKnightGear
+  equipment:
+    shoes: civ13_shoes_Sabatons
+    outerClothing: civ13_suit_blue_plated_armor
+    jumpsuit: civ13_uniform_blue_tunic
+    head: civ13_head_knight_helmet
+    gloves: civ13_gloves_armored_gauntlets
+  inhand:
+    - civ13_sword_arming_sword
+    - civ13_shield_semioval_iron_shield
+
+# Swordsman
+
+- type: job
+  id: FrenchSwordsman
+  originalName: Homme d'Armes
+  name: job-name-civ-french-man-at-arms
+  faction: France
+  description: job-description-civ-french-man-at-arms
+  playTimeTracker: JobFrenchSwordsman
+  startingGear: FrenchSwordsmanGear
+  randomStartingGears: [FrenchSwordsmanGear, FrenchSwordsmanGear2]
+  icon: "JobIconSword"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchSwordsman
+
+- type: startingGear
+  id: FrenchSwordsmanGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_chainmail
+    neck: civ13_accessory_tabard_blue_white
+    jumpsuit: civ13_uniform_blue_white_tunic
+    head: civ13_head_kettle_helmet
+  inhand:
+    - civ13_sword_arming_sword
+    - WoodenBucklerBlue
+- type: startingGear
+  id: FrenchSwordsmanGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_chainmail
+    neck: civ13_accessory_tabard_blue
+    jumpsuit: civ13_uniform_blue_tunic
+    head: civ13_head_kettle_helmet
+  inhand:
+    - civ13_sword_arming_sword
+    - WoodenBucklerBlue
+# Spearman
+
+- type: job
+  id: FrenchSpearman
+  originalName: Piquier
+  name: job-name-civ-french-spearman
+  faction: France
+  description: job-description-civ-french-spearman
+  playTimeTracker: JobFrenchSpearman
+  startingGear: FrenchSpearmanGear
+  randomStartingGears: [FrenchSpearmanGear, FrenchSpearmanGear2]
+  icon: "JobIconSpear"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchSpearman
+
+- type: startingGear
+  id: FrenchSpearmanGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_blue_white
+    jumpsuit: civ13_uniform_blue_white_tunic
+    head: civ13_head_protective_conical_helmet
+  inhand:
+    - civ13_spear_pike
+    - WoodenBucklerBlue
+
+- type: startingGear
+  id: FrenchSpearmanGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_blue
+    jumpsuit: civ13_uniform_blue_tunic
+    head: civ13_head_protective_conical_helmet
+  inhand:
+    - civ13_spear_pike
+    - WoodenBucklerBlue
+# Ranged
+
+- type: job
+  id: FrenchRanged
+  originalName: Archer
+  name: job-name-civ-french-ranged
+  faction: France
+  description: job-description-civ-french-ranged
+  playTimeTracker: JobFrenchRanged
+  startingGear: FrenchRangedGear
+  randomStartingGears: [FrenchRangedGear, FrenchRangedGear2]
+  icon: "JobIconRanged"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchRanged
+
+- type: startingGear
+  id: FrenchRangedGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_blue_white
+    jumpsuit: civ13_uniform_blue_white_tunic
+    head: civ13_head_conical_helmet
+    belt: ClothingBeltQuiver
+    pocket1: CombatKnife
+    back: WoodenBucklerBlue
+  inhand:
+    - BowImprovised
+  storage:
+    belt:
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+- type: startingGear
+  id: FrenchRangedGear2
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_gambeson
+    neck: civ13_accessory_tabard_blue
+    jumpsuit: civ13_uniform_blue_tunic
+    head: civ13_head_kettle_helmet
+    belt: ClothingBeltQuiver
+    pocket1: CombatKnife
+    back: WoodenBucklerBlue
+  inhand:
+    - BowImprovised
+  storage:
+    belt:
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+      - ArrowIron
+# Medic
+
+- type: job
+  id: FrenchMedic
+  name: job-name-civ-french-medic
+  faction: France
+  originalName: Moine
+  description: job-description-civ-french-medic
+  playTimeTracker: JobFrenchMedic
+  startingGear: FrenchMedicGear
+  icon: "JobIconMedic"
+  supervisors: job-supervisors-lord
+  special:
+    - !type:AddComponentSpecial
+      components:
+        - type: NpcFactionMember
+          factions:
+            - France
+        - type: ShowFrenchFactionIcons
+          factionIcon: FrenchFaction
+
+- type: playTimeTracker
+  id: JobFrenchMedic
+
+- type: startingGear
+  id: FrenchMedicGear
+  equipment:
+    shoes: civ13_shoes_leather_shoes_1
+    outerClothing: civ13_suit_monk_robes
+    jumpsuit: civ13_uniform_light_brown_tunic
+    head: civ13_head_black_noble_hat
+    belt: LeatherClothingBelt
+    pocket1: CombatKnife
+  storage:
+    belt:
+      - ComfreyHealingHerbs
+      - ComfreyHealingHerbs
+      - FoodPoppy
+      - FoodPoppy
+      - YarrowHealingHerbs
+      - YarrowHealingHerbs
+      - ElderflowerHealingHerbs
+      - ElderflowerHealingHerbs
+      - GauzeLeather1
+      - GauzeLeather1

+ 26 - 0
Resources/Prototypes/Roles/Jobs/departments.yml

@@ -143,3 +143,29 @@
   color: "#a36156"
   roles:
     - Nomad
+
+- type: department
+  id: DepFrench
+  name: department-French
+  description: department-French-description
+  color: "#6490ce"
+  roles:
+    - FrenchLord
+    - FrenchKnight
+    - FrenchSwordsman
+    - FrenchSpearman
+    - FrenchRanged
+    - FrenchMedic
+
+- type: department
+  id: DepEnglish
+  name: department-English
+  description: department-English-description
+  color: "#dc3f3f"
+  roles:
+    - EnglishLord
+    - EnglishKnight
+    - EnglishSwordsman
+    - EnglishSpearman
+    - EnglishRanged
+    - EnglishMedic

+ 45 - 6
Resources/Prototypes/StatusIcon/faction.yml

@@ -42,7 +42,18 @@
     state: Syndicate
 
 - type: factionIcon
-  id: NomadsFaction1
+  id: NomadsFaction
+  priority: 0
+  locationPreference: Right
+  showTo:
+    components:
+      - ShowAntagIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud.rsi
+    state: civ2
+
+- type: factionIcon
+  id: Nomads1Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -53,7 +64,7 @@
     state: civ1
 
 - type: factionIcon
-  id: NomadsFaction2
+  id: Nomads2Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -64,7 +75,7 @@
     state: civ2
 
 - type: factionIcon
-  id: NomadsFaction3
+  id: Nomads3Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -75,7 +86,7 @@
     state: civ3
 
 - type: factionIcon
-  id: NomadsFaction4
+  id: Nomads4Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -86,7 +97,7 @@
     state: civ4
 
 - type: factionIcon
-  id: NomadsFaction5
+  id: Nomads5Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -97,7 +108,7 @@
     state: civ5
 
 - type: factionIcon
-  id: NomadsFaction6
+  id: Nomads6Faction
   priority: 0
   locationPreference: Right
   showTo:
@@ -118,3 +129,31 @@
   icon:
     sprite: /Textures/Interface/Misc/civ_hud.rsi
     state: hostile
+
+- type: factionIcon
+  id: FrenchFaction
+  priority: 0
+  locationPreference: Right
+  showTo:
+    components:
+      - ShowAntagIcons
+      - ShowFactionIcons
+      - ShowFrenchFactionIcons
+      - ShowEnglishFactionIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: fr_basic
+
+- type: factionIcon
+  id: EnglishFaction
+  priority: 0
+  locationPreference: Right
+  showTo:
+    components:
+      - ShowAntagIcons
+      - ShowFactionIcons
+      - ShowFrenchFactionIcons
+      - ShowEnglishFactionIcons
+  icon:
+    sprite: /Textures/Interface/Misc/civ_hud_countries.rsi
+    state: eng_basic

+ 31 - 0
Resources/Prototypes/ai_factions.yml

@@ -84,6 +84,8 @@
     - Dragon
     - AllHostile
     - Wizard
+    - France
+    - England
 
 - type: npcFaction
   id: Xeno
@@ -190,3 +192,32 @@
     - Zombie
     - Revolutionary
     - Predator
+
+## TDM
+- type: npcFaction
+  id: France
+  hostile:
+    - SimpleHostile
+    - Syndicate
+    - Xeno
+    - Zombie
+    - Revolutionary
+    - Dragon
+    - AllHostile
+    - Wizard
+    - Predator
+    - England
+
+- type: npcFaction
+  id: England
+  hostile:
+    - SimpleHostile
+    - Syndicate
+    - Xeno
+    - Zombie
+    - Revolutionary
+    - Dragon
+    - AllHostile
+    - Wizard
+    - Predator
+    - France

+ 13 - 1
Resources/Prototypes/game_presets.yml

@@ -4,7 +4,19 @@
     - extended
     - nomads
   name: extended-title
-  showInVote: true #2boring2vote
+  showInVote: true
   description: extended-description
   rules:
     - RespawnDeadRule
+
+- type: gamePreset
+  id: TDM
+  alias:
+    - tdm
+  name: tdm-title
+  showInVote: true
+  description: tdm-description
+  rules:
+    - RespawnDeadRuleTDM
+    - GracewallRule
+    - CaptureAreaRule

+ 1 - 1
Resources/Prototypes/round_announcements.yml

@@ -1,3 +1,3 @@
 - type: roundAnnouncement
   id: Welcome
-  sound: /Audio/Announcements/welcome.ogg
+  sound: /Audio/Announcements/horn.ogg

BIN
Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/equipped-OUTERCLOTHING.png


BIN
Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/gambeson.png


BIN
Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/icon.png


+ 23 - 0
Resources/Textures/Civ14/Clothing/exported/suits/gambeson.rsi/meta.json

@@ -0,0 +1,23 @@
+{
+    "version": 1,
+    "license": "AGPLv3",
+    "copyright": "Taken from Civilization 13 https://github.com/civ13/civ13",
+    "size": {
+        "x": 32,
+        "y": 32
+    },
+    "states": [
+        {
+            "name": "gambeson",
+            "directions": 1
+        },
+        {
+            "name": "equipped-OUTERCLOTHING",
+            "directions": 4
+        },
+        {
+            "name": "icon",
+            "directions": 1
+        }
+    ]
+}

BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/customtabard.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/equipped-NECK.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/icon.png


+ 23 - 0
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue.rsi/meta.json

@@ -0,0 +1,23 @@
+{
+  "version": 1,
+  "license": "AGPLv3",
+  "copyright": "Taken from Civilization 13 https://github.com/civ13/civ13",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "customtabard",
+      "directions": 1
+    },
+    {
+      "name": "equipped-NECK",
+      "directions": 4
+    },
+    {
+      "name": "icon",
+      "directions": 1
+    }
+  ]
+}

BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/customtabard.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/equipped-NECK.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/icon.png


+ 23 - 0
Resources/Textures/Civ14/Clothing/exported/ties/tabard_blue_white.rsi/meta.json

@@ -0,0 +1,23 @@
+{
+  "version": 1,
+  "license": "AGPLv3",
+  "copyright": "Taken from Civilization 13 https://github.com/civ13/civ13",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "customtabard",
+      "directions": 1
+    },
+    {
+      "name": "equipped-NECK",
+      "directions": 4
+    },
+    {
+      "name": "icon",
+      "directions": 1
+    }
+  ]
+}

BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/customtabard.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/equipped-NECK.png


BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/icon.png


+ 23 - 0
Resources/Textures/Civ14/Clothing/exported/ties/tabard_red.rsi/meta.json

@@ -0,0 +1,23 @@
+{
+  "version": 1,
+  "license": "AGPLv3",
+  "copyright": "Taken from Civilization 13 https://github.com/civ13/civ13",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "customtabard",
+      "directions": 1
+    },
+    {
+      "name": "equipped-NECK",
+      "directions": 4
+    },
+    {
+      "name": "icon",
+      "directions": 1
+    }
+  ]
+}

BIN
Resources/Textures/Civ14/Clothing/exported/ties/tabard_red_yellow.rsi/customtabard.png


Vissa filer visades inte eftersom för många filer har ändrats