]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
New Thief minor antagonist (#21520)
authorEd <96445749+TheShuEd@users.noreply.github.com>
Sun, 24 Dec 2023 09:58:28 +0000 (12:58 +0300)
committerGitHub <noreply@github.com>
Sun, 24 Dec 2023 09:58:28 +0000 (02:58 -0700)
* start working

* add right-click thief antagins
some architecture restruct

* add meh thief greeting audio

* add thief subgamemode to Traitors gamemode

* add late join thief (not tested yet)
add briefing

* add pacifism

* add Steal tasks to thief

* fix crash thief+traitor on person

* add new condition: collection steal

* add tracking of succes collection objective

* add stamp collection target
remove some boring steal target
add check pulling entity to collection target

* finalize first 2 group objective

* start merging stealing objective systems

* merging

* finish merging. Now traitor steal objective work better

* we don't check the items of pullable sentient entity

* clear naming, enable thief signle item objective start

* objective pack add

* finish with steal item objectives

* convert string to ProtoId<>

* some clean up

* add thieves to revolution game mode

* Update Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl

Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com>
* Update Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl

Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com>
* update pacifism: fix crashing, monkey-thief without pacified

* adaptive animal briefing, cleaning locales

* add structure steal objectives

* remove RCD target

* add thiefs to manifest, but bug with traitor duplications

* add escape objective

* add chat briefing

* setup animal objective group system

* add animal steal objectives

* add animal objectives notroleconditions

* add morty

* now thief mode has a chance of not launching Now there are a random number of thieves per round from 1 to 3

* 6 hours of trying to fix duplicate tasks. Failure

* added thief pinpointer (buggy)

* start thief backpack UI work

* revert pinpointer for scope reason

* UI continue work

* add thief starter kits content

* remove ERP kit :trollface:

* finally! giving starting items to thief. Now it playable, but still need more work

* clean up

* fix

* fox

* add merged items into thief
new Starting Kit (buggy)

* fix YES antag menu

* objection tweaks

* remove hearts objective, working on spawning things from toolbox

* smug

* fixes

* add race specifier objective condition LAMPS

* meh

* fix fix fix

* the alive

* Adding stamps

* Update backpack.ftl

* Revert1

* Revert ftl

* add voice mask to communicator kit

* Update Resources/Locale/en-US/administration/antag.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/objectives/conditions/steal.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/prototypes/roles/antags.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* Update Resources/Locale/en-US/thief/backpack.ftl

Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
* update

* fix

* more reusable function, add documentation

* fix doc

* faint fixes

---------

Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com>
Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
94 files changed:
Content.Client/Thief/ThiefBackpackBoundUserInterface.cs [new file with mode: 0644]
Content.Client/Thief/ThiefBackpackMenu.xaml [new file with mode: 0644]
Content.Client/Thief/ThiefBackpackMenu.xaml.cs [new file with mode: 0644]
Content.Client/Thief/ThiefBackpackSet.xaml [new file with mode: 0644]
Content.Client/Thief/ThiefBackpackSet.xaml.cs [new file with mode: 0644]
Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs
Content.Server/Antag/AntagSelectionSystem.cs
Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs [new file with mode: 0644]
Content.Server/GameTicking/Rules/ThiefRuleSystem.cs [new file with mode: 0644]
Content.Server/GameTicking/Rules/TraitorRuleSystem.cs
Content.Server/Objectives/Components/SpeciesRequirmentComponent.cs [new file with mode: 0644]
Content.Server/Objectives/Components/StealConditionComponent.cs
Content.Server/Objectives/Components/StealTargetComponent.cs [new file with mode: 0644]
Content.Server/Objectives/ObjectivesSystem.cs
Content.Server/Objectives/Systems/SpeciesRequirementSystem.cs [new file with mode: 0644]
Content.Server/Objectives/Systems/StealConditionSystem.cs
Content.Server/Roles/RoleSystem.cs
Content.Server/Roles/ThiefRoleComponent.cs [new file with mode: 0644]
Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs [new file with mode: 0644]
Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs [new file with mode: 0644]
Content.Shared/Objectives/Prototypes/StealTargetGroupPrototype.cs [new file with mode: 0644]
Content.Shared/Thief/Components/ThiefBackpackUI.cs [new file with mode: 0644]
Content.Shared/Thief/Prototypes/ThiefBackpackSetPrototype.cs [new file with mode: 0644]
Resources/Audio/Misc/attributions.yml
Resources/Audio/Misc/thief_greeting.ogg [new file with mode: 0644]
Resources/Locale/en-US/administration/antag.ftl
Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl [new file with mode: 0644]
Resources/Locale/en-US/objectives/conditions/steal.ftl
Resources/Locale/en-US/prototypes/roles/antags.ftl
Resources/Locale/en-US/thief/backpack.ftl [new file with mode: 0644]
Resources/Prototypes/Catalog/thief_toolbox_sets.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Clothing/Ears/headsets_alt.yml
Resources/Prototypes/Entities/Clothing/Eyes/hud.yml
Resources/Prototypes/Entities/Clothing/Head/hats.yml
Resources/Prototypes/Entities/Clothing/Neck/cloaks.yml
Resources/Prototypes/Entities/Clothing/Neck/medals.yml
Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml
Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml
Resources/Prototypes/Entities/Mobs/NPCs/pets.yml
Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml
Resources/Prototypes/Entities/Objects/Decoration/jackolantern.yml
Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml
Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml
Resources/Prototypes/Entities/Objects/Devices/door_remote.yml
Resources/Prototypes/Entities/Objects/Devices/encryption_keys.yml
Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml
Resources/Prototypes/Entities/Objects/Devices/hand_teleporter.yml
Resources/Prototypes/Entities/Objects/Devices/nuke.yml
Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml
Resources/Prototypes/Entities/Objects/Fun/figurines.yml
Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml
Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml
Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml
Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
Resources/Prototypes/Entities/Objects/Misc/paper.yml
Resources/Prototypes/Entities/Objects/Misc/secret_documents.yml
Resources/Prototypes/Entities/Objects/Power/antimatter_part.yml
Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml
Resources/Prototypes/Entities/Objects/Specific/Hydroponics/leaves.yml
Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml
Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml
Resources/Prototypes/Entities/Objects/Specific/Research/disk.yml
Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml
Resources/Prototypes/Entities/Objects/Tools/flashlights.yml
Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml
Resources/Prototypes/Entities/Objects/Tools/lantern.yml
Resources/Prototypes/Entities/Objects/Tools/toolbox.yml
Resources/Prototypes/Entities/Objects/Vehicles/buckleable.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml
Resources/Prototypes/Entities/Structures/Dispensers/booze.yml
Resources/Prototypes/Entities/Structures/Dispensers/chem.yml
Resources/Prototypes/Entities/Structures/Furniture/altar.yml
Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml
Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml
Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml
Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml
Resources/Prototypes/GameRules/midround.yml
Resources/Prototypes/GameRules/roundstart.yml
Resources/Prototypes/Objectives/base_objectives.yml
Resources/Prototypes/Objectives/objectiveGroups.yml
Resources/Prototypes/Objectives/stealTargetGroups.yml [new file with mode: 0644]
Resources/Prototypes/Objectives/thief.yml [new file with mode: 0644]
Resources/Prototypes/Objectives/traitor.yml
Resources/Prototypes/Roles/Antags/Thief.yml [new file with mode: 0644]
Resources/Textures/Objects/Devices/pinpointer.rsi/meta.json
Resources/Textures/Objects/Devices/pinpointer.rsi/pinpointer_thief.png [new file with mode: 0644]
Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Vehicles/secway.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Vehicles/secway.rsi/meta.json

diff --git a/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs b/Content.Client/Thief/ThiefBackpackBoundUserInterface.cs
new file mode 100644 (file)
index 0000000..2d492c5
--- /dev/null
@@ -0,0 +1,52 @@
+using Content.Shared.Thief;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Thief;
+
+[UsedImplicitly]
+public sealed class ThiefBackpackBoundUserInterface : BoundUserInterface
+{
+    private ThiefBackpackMenu? _window;
+
+    public ThiefBackpackBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
+
+    protected override void Open()
+    {
+        base.Open();
+
+        _window = new ThiefBackpackMenu(this);
+        _window.OnClose += Close;
+        _window.OpenCentered();
+    }
+
+    protected override void Dispose(bool disposing)
+    {
+        base.Dispose(disposing);
+        if (!disposing)
+            return;
+
+        _window?.Dispose();
+        _window = null;
+    }
+
+    protected override void UpdateState(BoundUserInterfaceState state)
+    {
+        base.UpdateState(state);
+
+        if (state is not ThiefBackpackBoundUserInterfaceState current)
+            return;
+
+        _window?.UpdateState(current);
+    }
+
+    public void SendChangeSelected(int setNumber)
+    {
+        SendMessage(new ThiefBackpackChangeSetMessage(setNumber));
+    }
+
+    public void SendApprove()
+    {
+        SendMessage(new ThiefBackpackApproveMessage());
+    }
+}
diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml b/Content.Client/Thief/ThiefBackpackMenu.xaml
new file mode 100644 (file)
index 0000000..c1739eb
--- /dev/null
@@ -0,0 +1,39 @@
+<controls:FancyWindow xmlns="https://spacestation14.io"
+                                               xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+                                               xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
+                                               Title="{Loc 'thief-backpack-window-title'}"
+                                               MinSize="700 700">
+       <BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
+               <!-- First Informational panel -->
+               <Label Text="{Loc 'thief-backpack-window-description'}" Margin="5 5"/>
+               <controls:HLine Color="#404040" Thickness="2" Margin="0 5"/>
+               <Label Name="SelectedSets" Text="{Loc 'thief-backpack-window-selected'}" Margin="5 5"/>
+
+               <!-- Second sets panel -->
+               <PanelContainer Margin="5 5">
+                       <PanelContainer.PanelOverride>
+                               <gfx:StyleBoxFlat BackgroundColor="#050505" />
+                       </PanelContainer.PanelOverride>
+                       <ScrollContainer
+                                       HScrollEnabled="False"
+                                       MinSize="0 600"
+                                       SizeFlagsStretchRatio="2"
+                                       VerticalExpand="True">
+                               <BoxContainer Name="SetsGrid" Orientation="Vertical" MinSize="460 200">
+                                       <!-- Sets is filled by code -->
+
+                               </BoxContainer>
+                       </ScrollContainer>
+               </PanelContainer>
+               
+               <!-- Third approve button panel -->
+               <PanelContainer Margin="10">
+                       <Button Name="ApproveButton"
+                                       Text="{Loc 'thief-backpack-window-approve-button'}"
+                                       Margin="0 5"
+                                       Access="Public"
+                                       HorizontalAlignment ="Right"
+                                       StyleClasses="OpenRight"/>
+               </PanelContainer>
+       </BoxContainer>
+</controls:FancyWindow>
diff --git a/Content.Client/Thief/ThiefBackpackMenu.xaml.cs b/Content.Client/Thief/ThiefBackpackMenu.xaml.cs
new file mode 100644 (file)
index 0000000..b2314cf
--- /dev/null
@@ -0,0 +1,56 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Thief;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Thief;
+
+[GenerateTypedNameReferences]
+public sealed partial class ThiefBackpackMenu : FancyWindow
+{
+    [Dependency] private readonly IEntitySystemManager _sysMan = default!;
+    private readonly SpriteSystem _spriteSystem;
+
+    private readonly ThiefBackpackBoundUserInterface _owner;
+
+    public ThiefBackpackMenu(ThiefBackpackBoundUserInterface owner)
+    {
+        RobustXamlLoader.Load(this);
+        IoCManager.InjectDependencies(this);
+        _spriteSystem = _sysMan.GetEntitySystem<SpriteSystem>();
+
+        _owner = owner;
+
+        ApproveButton.OnButtonDown += (args) =>
+        {
+            _owner.SendApprove();
+        };
+    }
+
+    public void UpdateState(ThiefBackpackBoundUserInterfaceState state)
+    {
+        SetsGrid.RemoveAllChildren();
+        int count = 0;
+        int selectedNumber = 0;
+        foreach (var set in state.Sets)
+        {
+            var child = new ThiefBackpackSet(set.Value, _spriteSystem);
+
+            child.SetButton.OnButtonDown += (args) =>
+            {
+                _owner.SendChangeSelected(set.Key);
+            };
+
+            SetsGrid.AddChild(child);
+
+            count++;
+
+            if (set.Value.Selected)
+                selectedNumber++;
+        }
+
+        SelectedSets.Text = Loc.GetString("thief-backpack-window-selected", ("selectedCount", selectedNumber), ("maxCount", state.MaxSelectedSets));
+        ApproveButton.Disabled = selectedNumber == state.MaxSelectedSets ? false : true;
+    }
+}
diff --git a/Content.Client/Thief/ThiefBackpackSet.xaml b/Content.Client/Thief/ThiefBackpackSet.xaml
new file mode 100644 (file)
index 0000000..0d8b5e2
--- /dev/null
@@ -0,0 +1,23 @@
+<Control xmlns="https://spacestation14.io"
+               xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+               xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
+       <!-- Name and button -->
+       <PanelContainer Margin="5 5 0 5">
+                       <PanelContainer.PanelOverride>
+                               <gfx:StyleBoxFlat BackgroundColor="#18211b" />
+                       </PanelContainer.PanelOverride>
+                       <BoxContainer Orientation="Vertical" HorizontalExpand="True">
+                       <GridContainer Margin="5" Columns="2" MinSize="600 0">
+                               <Label Name="SetName" Text="Set" StyleClasses="StatusFieldTitle"></Label>
+                               <Button Margin="0 10" Name="SetButton" Text="Select" StyleClasses="OpenRight" Access="Public" HorizontalAlignment="Right"/>
+                       </GridContainer>
+                       <controls:HLine Color="#404040" Thickness="1" Margin="0 5"/>
+                       <!-- Icon and Description -->
+                       <GridContainer Margin="0 5" Columns="2">
+                               <TextureRect Name="Icon" Margin="10" Stretch="KeepAspectCentered"
+                                                       VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="64 64"/>
+                               <Label Name="SetDescription" Text="Description"></Label>
+                       </GridContainer>
+               </BoxContainer>
+       </PanelContainer>
+</Control>
diff --git a/Content.Client/Thief/ThiefBackpackSet.xaml.cs b/Content.Client/Thief/ThiefBackpackSet.xaml.cs
new file mode 100644 (file)
index 0000000..84be57d
--- /dev/null
@@ -0,0 +1,22 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+using Content.Shared.Thief;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Thief;
+
+[GenerateTypedNameReferences]
+public sealed partial class ThiefBackpackSet : Control
+{
+    public ThiefBackpackSet(ThiefBackpackSetInfo set, SpriteSystem spriteSystem)
+    {
+        RobustXamlLoader.Load(this);
+
+        Icon.Texture = spriteSystem.Frame0(set.Sprite);
+        SetName.Text = Loc.GetString(set.Name);
+        SetDescription.Text = Loc.GetString(set.Description);
+        SetButton.Text = Loc.GetString(set.Selected ? "thief-backpack-button-deselect" : "thief-backpack-button-select");
+        SetButton.ModulateSelfOverride = set.Selected ? new Color(40, 84, 35) : new Color(68, 75, 103);
+    }
+}
index 76cb1e0da98b2df650701cb89dbf7a0faf47af54..3adb2d5605612a5ea023eb04479e4171deb91962 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.GameTicking;
 using Content.Server.GameTicking.Rules;
+using Content.Server.StationEvents.Events;
 using Content.Server.Zombies;
 using Content.Shared.Administration;
 using Content.Shared.Database;
@@ -15,6 +16,7 @@ namespace Content.Server.Administration.Systems;
 public sealed partial class AdminVerbSystem
 {
     [Dependency] private readonly ZombieSystem _zombie = default!;
+    [Dependency] private readonly ThiefRuleSystem _thief = default!;
     [Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
     [Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!;
     [Dependency] private readonly PiratesRuleSystem _piratesRule = default!;
@@ -119,5 +121,22 @@ public sealed partial class AdminVerbSystem
             Message = Loc.GetString("admin-verb-make-head-rev"),
         };
         args.Verbs.Add(headRev);
+
+        Verb thief = new()
+        {
+            Text = Loc.GetString("admin-verb-text-make-thief"),
+            Category = VerbCategory.Antag,
+            Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/ihscombat.rsi"), "icon"),
+            Act = () =>
+            {
+                if (!_minds.TryGetSession(targetMindComp.Mind, out var session))
+                    return;
+
+                _thief.MakeThief(session);
+            },
+            Impact = LogImpact.High,
+            Message = Loc.GetString("admin-verb-make-thief"),
+        };
+        args.Verbs.Add(thief);
     }
 }
index b3b8a375088e5b5c35fa5635150f851e153a1e92..6fbbd030d044ada53eff9e922cdd2a3245dced30 100644 (file)
@@ -25,6 +25,7 @@ using Robust.Server.Audio;
 using Robust.Server.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Content.Server.Shuttles.Components;
 
 namespace Content.Server.Antag;
 
@@ -149,6 +150,72 @@ public sealed class AntagSelectionSystem : GameRuleSystem<GameRuleComponent>
         }
     }
 
+    /// <summary>
+    /// The function walks through all players, checking their role and preferences to generate a list of players who can become antagonists.
+    /// </summary>
+    /// <param name="candidates">a list of players to check out</param>
+    /// <param name="antagPreferenceId">antagonist's code id</param>
+    /// <returns></returns>
+    public List<ICommonSession> FindPotentialAntags(in Dictionary<ICommonSession, HumanoidCharacterProfile> candidates, string antagPreferenceId)
+    {
+        var list = new List<ICommonSession>();
+        var pendingQuery = GetEntityQuery<PendingClockInComponent>();
+
+        foreach (var player in candidates.Keys)
+        {
+            // Role prevents antag.
+            if (!_jobs.CanBeAntag(player))
+                continue;
+
+            // Latejoin
+            if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value))
+                continue;
+
+            list.Add(player);
+        }
+
+        var prefList = new List<ICommonSession>();
+
+        foreach (var player in list)
+        {
+            //player preferences to play as this antag
+            var profile = candidates[player];
+            if (profile.AntagPreferences.Contains(antagPreferenceId))
+            {
+                prefList.Add(player);
+            }
+        }
+        if (prefList.Count == 0)
+        {
+            Log.Info($"Insufficient preferred antag:{antagPreferenceId}, picking at random.");
+            prefList = list;
+        }
+        return prefList;
+    }
+
+    /// <summary>
+    /// selects the specified number of players from the list
+    /// </summary>
+    /// <param name="antagCount">how many players to take</param>
+    /// <param name="prefList">a list of players from which to draw</param>
+    /// <returns></returns>
+    public List<ICommonSession> PickAntag(int antagCount, List<ICommonSession> prefList)
+    {
+        var results = new List<ICommonSession>(antagCount);
+        if (prefList.Count == 0)
+        {
+            Log.Info("Insufficient ready players to fill up with antags, stopping the selection.");
+            return results;
+        }
+
+        for (var i = 0; i < antagCount; i++)
+        {
+            results.Add(_random.PickAndTake(prefList));
+            Log.Info("Selected a preferred antag.");
+        }
+        return results;
+    }
+
     /// <summary>
     /// Will take a group of entities and check if they are all alive or dead
     /// </summary>
diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs
new file mode 100644 (file)
index 0000000..31ea30f
--- /dev/null
@@ -0,0 +1,54 @@
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Content.Shared.Roles;
+using Robust.Shared.Player;
+using Content.Shared.Preferences;
+
+namespace Content.Server.GameTicking.Rules.Components;
+
+/// <summary>
+/// Stores data for <see cref="ThiefRuleSystem/">.
+/// </summary>
+[RegisterComponent, Access(typeof(ThiefRuleSystem))]
+public sealed partial class ThiefRuleComponent : Component
+{
+    /// <summary>
+    /// A chance for this mode to be added to the game.
+    /// </summary>
+    [DataField]
+    public float RuleChance = 1f;
+
+    [DataField]
+    public ProtoId<AntagPrototype> ThiefPrototypeId = "Thief";
+
+    public Dictionary<ICommonSession, HumanoidCharacterProfile> StartCandidates = new();
+
+    [DataField]
+    public float MaxObjectiveDifficulty = 2.5f;
+
+    [DataField]
+    public int MaxStealObjectives = 10;
+
+    /// <summary>
+    /// Things that will be given to thieves
+    /// </summary>
+    [DataField]
+    public List<EntProtoId> StarterItems = new List<EntProtoId> { "ToolboxThief", "ClothingHandsChameleonThief" }; //TO DO - replace to chameleon thieving gloves whem merg
+
+    /// <summary>
+    /// All Thiefes created by this rule
+    /// </summary>
+    public readonly List<EntityUid> ThiefMinds = new();
+
+    /// <summary>
+    /// Max Thiefs created by rule on roundstart
+    /// </summary>
+    [DataField]
+    public int MaxAllowThief = 3;
+
+    /// <summary>
+    /// Sound played when making the player a thief via antag control or ghost role
+    /// </summary>
+    [DataField]
+    public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/thief_greeting.ogg");
+}
diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs
new file mode 100644 (file)
index 0000000..d0f7888
--- /dev/null
@@ -0,0 +1,194 @@
+using Content.Server.Chat.Managers;
+using Content.Server.GameTicking.Rules.Components;
+using Content.Server.Mind;
+using Content.Server.Objectives;
+using Content.Server.Shuttles.Components;
+using Content.Server.Roles;
+using Content.Shared.Mind;
+using Content.Shared.Objectives.Components;
+using Content.Shared.Preferences;
+using Content.Shared.Roles;
+using Content.Shared.Roles.Jobs;
+using Robust.Shared.Player;
+using Robust.Shared.Random;
+using Robust.Shared.Prototypes;
+using System.Linq;
+using Content.Shared.Humanoid;
+using Content.Server.Antag;
+using Robust.Server.Audio;
+
+namespace Content.Server.GameTicking.Rules;
+
+public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
+{
+    [Dependency] private readonly IChatManager _chatManager = default!;
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly AntagSelectionSystem _antagSelection = default!;
+    [Dependency] private readonly AudioSystem _audio = default!;
+    [Dependency] private readonly MindSystem _mindSystem = default!;
+    [Dependency] private readonly SharedRoleSystem _roleSystem = default!;
+    [Dependency] private readonly SharedJobSystem _jobs = default!;
+    [Dependency] private readonly ObjectivesSystem _objectives = default!;
+
+    const string bigObjectiveGroup = "ThiefBigObjectiveGroups";
+    const string smallObjectiveGroup = "ThiefObjectiveGroups";
+    const string escapeObjectiveGroup = "ThiefEscapeObjectiveGroups";
+
+    private const float BigObjectiveChance = 0.7f;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayersSpawned);
+
+        SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
+        SubscribeLocalEvent<ThiefRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
+    }
+
+    private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev)
+    {
+        var query = EntityQueryEnumerator<ThiefRuleComponent, GameRuleComponent>();
+        while (query.MoveNext(out var uid, out var thief, out var gameRule))
+        {
+            //Chance to not lauch gamerule
+            if (!_random.Prob(thief.RuleChance))
+            {
+                RemComp<ThiefRuleComponent>(uid);
+                continue;
+            }
+
+            if (!GameTicker.IsGameRuleAdded(uid, gameRule))
+                continue;
+
+            foreach (var player in ev.Players)
+            {
+                if (!ev.Profiles.ContainsKey(player.UserId))
+                    continue;
+
+                thief.StartCandidates[player] = ev.Profiles[player.UserId];
+            }
+            DoThiefStart(thief);
+        }
+    }
+
+    private void DoThiefStart(ThiefRuleComponent component)
+    {
+        if (!component.StartCandidates.Any())
+        {
+            Log.Error("There are no players who can become thieves.");
+            return;
+        }
+
+        var startThiefCount = Math.Min(component.MaxAllowThief, component.StartCandidates.Count);
+        var thiefPool = _antagSelection.FindPotentialAntags(component.StartCandidates, component.ThiefPrototypeId);
+        //TO DO: When voxes specifies are added, increase their chance of becoming a thief by 4 times >:)
+        var selectedThieves = _antagSelection.PickAntag(_random.Next(1, startThiefCount), thiefPool);
+
+        foreach(var thief in selectedThieves)
+        {
+            MakeThief(thief);
+        }
+    }
+
+    public bool MakeThief(ICommonSession thief)
+    {
+        var thiefRule = EntityQuery<ThiefRuleComponent>().FirstOrDefault();
+        if (thiefRule == null)
+        {
+            GameTicker.StartGameRule("Thief", out var ruleEntity);
+            thiefRule = Comp<ThiefRuleComponent>(ruleEntity);
+        }
+
+        //checks
+        if (!_mindSystem.TryGetMind(thief, out var mindId, out var mind))
+        {
+            Log.Info("Failed getting mind for picked thief.");
+            return false;
+        }
+        if (HasComp<ThiefRoleComponent>(mindId))
+        {
+            Log.Error($"Player {thief.Name} is already a thief.");
+            return false;
+        }
+        if (mind.OwnedEntity is not { } entity)
+        {
+            Log.Error("Mind picked for thief did not have an attached entity.");
+            return false;
+        }
+
+        // Assign thief roles
+        _roleSystem.MindAddRole(mindId, new ThiefRoleComponent
+        {
+            PrototypeId = thiefRule.ThiefPrototypeId
+        });
+
+        // Notificate player about new role assignment
+        if (_mindSystem.TryGetSession(mindId, out var session))
+        {
+            _audio.PlayGlobal(thiefRule.GreetingSound, session);
+            _chatManager.DispatchServerMessage(session, MakeBriefing(mind.OwnedEntity.Value));
+        }
+
+        // Give thieves their objectives
+        var difficulty = 0f;
+
+        if (_random.Prob(BigObjectiveChance)) // 70% chance to 1 big objective (structure or animal)
+        {
+            var objective = _objectives.GetRandomObjective(mindId, mind, bigObjectiveGroup);
+            if (objective != null)
+            {
+                _mindSystem.AddObjective(mindId, mind, objective.Value);
+                difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
+            }
+        }
+
+        for (var i = 0; i < thiefRule.MaxStealObjectives && thiefRule.MaxObjectiveDifficulty > difficulty; i++)  // Many small objectives
+        {
+            var objective = _objectives.GetRandomObjective(mindId, mind, smallObjectiveGroup);
+            if (objective == null)
+                continue;
+
+            _mindSystem.AddObjective(mindId, mind, objective.Value);
+            difficulty += Comp<ObjectiveComponent>(objective.Value).Difficulty;
+        }
+
+        //Escape target
+        var escapeObjective = _objectives.GetRandomObjective(mindId, mind, escapeObjectiveGroup);
+        if (escapeObjective != null)
+            _mindSystem.AddObjective(mindId, mind, escapeObjective.Value);
+
+        // Give starting items
+        _antagSelection.GiveAntagBagGear(mind.OwnedEntity.Value, thiefRule.StarterItems);
+
+        thiefRule.ThiefMinds.Add(mindId);
+        return true;
+    }
+
+    //Add mind briefing
+    private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args)
+    {
+        if (!TryComp<MindComponent>(thief.Owner, out var mind) || mind.OwnedEntity == null)
+            return;
+
+        args.Append(MakeBriefing(mind.OwnedEntity.Value));
+    }
+
+    private string MakeBriefing(EntityUid thief)
+    {
+        var isHuman = HasComp<HumanoidAppearanceComponent>(thief);
+        var briefing = "\n";
+        briefing = isHuman
+            ? Loc.GetString("thief-role-greeting-human")
+            : Loc.GetString("thief-role-greeting-animal");
+
+        briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
+        return briefing;
+    }
+
+    private void OnObjectivesTextGetInfo(Entity<ThiefRuleComponent> thiefs, ref ObjectivesTextGetInfoEvent args)
+    {
+        args.Minds = thiefs.Comp.ThiefMinds;
+        args.AgentName = Loc.GetString("thief-round-end-agent-name");
+    }
+}
index 60a35d704b034cb80997047847b970159a1e1b86..26125a6da750260e006071e8d82559bd25621434 100644 (file)
@@ -1,4 +1,5 @@
 using System.Linq;
+using Content.Server.Antag;
 using Content.Server.Chat.Managers;
 using Content.Server.GameTicking.Rules.Components;
 using Content.Server.Mind;
@@ -30,6 +31,7 @@ namespace Content.Server.GameTicking.Rules;
 
 public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
 {
+    [Dependency] private readonly AntagSelectionSystem _antagSelection = default!;
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly IConfigurationManager _cfg = default!;
@@ -117,8 +119,8 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
         }
 
         var numTraitors = MathHelper.Clamp(component.StartCandidates.Count / PlayersPerTraitor, 1, MaxTraitors);
-        var traitorPool = FindPotentialTraitors(component.StartCandidates, component);
-        var selectedTraitors = PickTraitors(numTraitors, traitorPool);
+        var traitorPool = _antagSelection.FindPotentialAntags(component.StartCandidates, component.TraitorPrototypeId);
+        var selectedTraitors = _antagSelection.PickAntag(numTraitors, traitorPool);
 
         foreach (var traitor in selectedTraitors)
         {
@@ -153,61 +155,6 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
         }
     }
 
-    private List<ICommonSession> FindPotentialTraitors(in Dictionary<ICommonSession, HumanoidCharacterProfile> candidates, TraitorRuleComponent component)
-    {
-        var list = new List<ICommonSession>();
-        var pendingQuery = GetEntityQuery<PendingClockInComponent>();
-
-        foreach (var player in candidates.Keys)
-        {
-            // Role prevents antag.
-            if (!_jobs.CanBeAntag(player))
-            {
-                continue;
-            }
-
-            // Latejoin
-            if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value))
-                continue;
-
-            list.Add(player);
-        }
-
-        var prefList = new List<ICommonSession>();
-
-        foreach (var player in list)
-        {
-            var profile = candidates[player];
-            if (profile.AntagPreferences.Contains(component.TraitorPrototypeId))
-            {
-                prefList.Add(player);
-            }
-        }
-        if (prefList.Count == 0)
-        {
-            Log.Info("Insufficient preferred traitors, picking at random.");
-            prefList = list;
-        }
-        return prefList;
-    }
-
-    private List<ICommonSession> PickTraitors(int traitorCount, List<ICommonSession> prefList)
-    {
-        var results = new List<ICommonSession>(traitorCount);
-        if (prefList.Count == 0)
-        {
-            Log.Info("Insufficient ready players to fill up with traitors, stopping the selection.");
-            return results;
-        }
-
-        for (var i = 0; i < traitorCount; i++)
-        {
-            results.Add(_random.PickAndTake(prefList));
-            Log.Info("Selected a preferred traitor.");
-        }
-        return results;
-    }
-
     public bool MakeTraitor(ICommonSession traitor, bool giveUplink = true, bool giveObjectives = true)
     {
         var traitorRule = EntityQuery<TraitorRuleComponent>().FirstOrDefault();
diff --git a/Content.Server/Objectives/Components/SpeciesRequirmentComponent.cs b/Content.Server/Objectives/Components/SpeciesRequirmentComponent.cs
new file mode 100644 (file)
index 0000000..9324ba4
--- /dev/null
@@ -0,0 +1,15 @@
+using Content.Server.Objectives.Systems;
+using Content.Shared.Humanoid.Prototypes;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Objectives.Components;
+
+/// <summary>
+/// Requires that the player's species matches a whitelist.
+/// </summary>
+[RegisterComponent, Access(typeof(SpeciesRequirementSystem))]
+public sealed partial class SpeciesRequirementComponent : Component
+{
+    [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
+    public List<ProtoId<SpeciesPrototype>> AllowedSpecies = new();
+}
index b52ac9cbe85c5c1c49b7e50376dc197daa38bc52..4da028de259e1ad8373818a289224f35b6085af6 100644 (file)
@@ -1,28 +1,64 @@
 using Content.Server.Objectives.Systems;
+using Content.Shared.Objectives;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Server.Objectives.Components;
 
 /// <summary>
-/// Requires that you steal a certain item.
+/// Requires that you steal a certain item (or several)
 /// </summary>
 [RegisterComponent, Access(typeof(StealConditionSystem))]
 public sealed partial class StealConditionComponent : Component
 {
     /// <summary>
-    /// The id of the item to steal.
+    /// A group of items to be stolen
     /// </summary>
-    /// <remarks>
-    /// Works by prototype id not tags or anything so it has to be the exact item.
-    /// </remarks>
-    [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
-    public string Prototype = string.Empty;
+    [DataField(required: true)]
+    public ProtoId<StealTargetGroupPrototype> StealGroup;
+
+    /// <summary>
+    /// When enabled, disables generation of this target if there is no entity on the map (disable for objects that can be created mid-round).
+    /// </summary>
+    [DataField]
+    public bool VerifyMapExistance = true;
+
+    /// <summary>
+    /// If the target may be alive but has died, it will not be counted
+    /// </summary>
+    [DataField]
+    public bool CheckAlive = false;
+    /// <summary>
+    /// The minimum number of items you need to steal to fulfill a objective
+    /// </summary>
+    [DataField]
+    public int MinCollectionSize = 1;
+
+    /// <summary>
+    /// The maximum number of items you need to steal to fulfill a objective
+    /// </summary>
+    [DataField]
+    public int MaxCollectionSize = 1;
+
+    /// <summary>
+    /// Target collection size after calculation
+    /// </summary>
+    [DataField]
+    public int CollectionSize;
 
     /// <summary>
     /// Help newer players by saying e.g. "steal the chief engineer's advanced magboots"
     /// instead of "steal advanced magboots. Should be a loc string.
     /// </summary>
-    [DataField("owner"), ViewVariables(VVAccess.ReadWrite)]
+    [DataField("owner")]
     public string? OwnerText;
+
+    // All this need to be loc string
+    [DataField(required: true)]
+    public LocId ObjectiveText;
+    [DataField(required: true)]
+    public LocId ObjectiveNoOwnerText;
+    [DataField(required: true)]
+    public LocId DescriptionText;
+    [DataField(required: true)]
+    public LocId DescriptionMultiplyText;
 }
diff --git a/Content.Server/Objectives/Components/StealTargetComponent.cs b/Content.Server/Objectives/Components/StealTargetComponent.cs
new file mode 100644 (file)
index 0000000..25fb9d3
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Server.Objectives.Systems;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.Objectives.Components.Targets;
+
+/// <summary>
+/// Allows an object to become the target of a StealCollection  objection
+/// </summary>
+[RegisterComponent]
+public sealed partial class StealTargetComponent : Component
+{
+    /// <summary>
+    /// The theft group to which this item belongs.
+    /// </summary>
+    [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
+    public string StealGroup;
+}
index bf5fe1d4d40c61bc5d9e39a3f5e9f1eaf68ebd42..f59066ff99f9a7491c6e2f0707fa37f9efc47c09 100644 (file)
@@ -129,6 +129,9 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem
 
             foreach (var objectiveGroup in objectives.GroupBy(o => Comp<ObjectiveComponent>(o).Issuer))
             {
+                //TO DO:
+                //check for the right group here. Getting the target issuer is easy: objectiveGroup.Key
+                //It should be compared to the type of the group's issuer.
                 result += "\n" + Loc.GetString($"objective-issuer-{objectiveGroup.Key}");
 
                 foreach (var objective in objectiveGroup)
diff --git a/Content.Server/Objectives/Systems/SpeciesRequirementSystem.cs b/Content.Server/Objectives/Systems/SpeciesRequirementSystem.cs
new file mode 100644 (file)
index 0000000..ab2d1d4
--- /dev/null
@@ -0,0 +1,34 @@
+using Content.Server.Objectives.Components;
+using Content.Shared.Humanoid;
+using Content.Shared.Objectives.Components;
+
+namespace Content.Server.Objectives.Systems;
+
+/// <summary>
+/// Handles species requirement for objectives that require a certain species.
+/// </summary>
+public sealed class SpeciesRequirementSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<SpeciesRequirementComponent, RequirementCheckEvent>(OnCheck);
+    }
+
+    private void OnCheck(Entity<SpeciesRequirementComponent> requirement, ref RequirementCheckEvent args)
+    {
+        if (args.Cancelled)
+            return;
+
+        if (!TryComp<HumanoidAppearanceComponent>(args.Mind.OwnedEntity, out var appearance)) {
+            args.Cancelled = true;
+            return;
+        }
+        if (!requirement.Comp.AllowedSpecies.Contains(appearance.Species))
+        {
+            args.Cancelled = true;
+            return;
+        }
+    }
+}
index 28ab164e0dc2b50fbd59d9a5c97eee5471b8930b..2b552285aded197bdd79f15288114f5255d0c67c 100644 (file)
 using Content.Server.Objectives.Components;
+using Content.Server.Objectives.Components.Targets;
 using Content.Shared.Mind;
 using Content.Shared.Objectives.Components;
 using Content.Shared.Objectives.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
+using Robust.Shared.Random;
+using Content.Shared.Pulling.Components;
+using Content.Shared.Objectives;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Systems;
+using Content.Shared.Mobs.Components;
 
 namespace Content.Server.Objectives.Systems;
 
 public sealed class StealConditionSystem : EntitySystem
 {
+    [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly IPrototypeManager _proto = default!;
     [Dependency] private readonly MetaDataSystem _metaData = default!;
+    [Dependency] private readonly MobStateSystem _mobState = default!;
     [Dependency] private readonly SharedObjectivesSystem _objectives = default!;
 
-    private EntityQuery<ContainerManagerComponent> containerQuery;
-    private EntityQuery<MetaDataComponent> metaQuery;
+    private EntityQuery<ContainerManagerComponent> _containerQuery;
+    private EntityQuery<MetaDataComponent> _metaQuery;
 
     public override void Initialize()
     {
         base.Initialize();
 
-        containerQuery = GetEntityQuery<ContainerManagerComponent>();
-        metaQuery = GetEntityQuery<MetaDataComponent>();
+        _containerQuery = GetEntityQuery<ContainerManagerComponent>();
+        _metaQuery = GetEntityQuery<MetaDataComponent>();
 
         SubscribeLocalEvent<StealConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
         SubscribeLocalEvent<StealConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
         SubscribeLocalEvent<StealConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
     }
 
-    private void OnAssigned(EntityUid uid, StealConditionComponent comp, ref ObjectiveAssignedEvent args)
+    /// start checks of target acceptability, and generation of start values.
+    private void OnAssigned(Entity<StealConditionComponent> condition, ref ObjectiveAssignedEvent args)
     {
-        // cancel if the item to steal doesn't exist
-        args.Cancelled |= !_proto.HasIndex<EntityPrototype>(comp.Prototype);
+        List<StealTargetComponent?> targetList = new();
+
+        // cancel if invalid TargetStealName
+        var group = _proto.Index<StealTargetGroupPrototype>(condition.Comp.StealGroup);
+        if (group == null)
+        {
+            args.Cancelled = true;
+            Log.Error("StealTargetGroup invalid prototype!");
+            return;
+        }
+
+        var query = EntityQueryEnumerator<StealTargetComponent>();
+        while (query.MoveNext(out var uid, out var target))
+        {
+            if (condition.Comp.StealGroup != target.StealGroup)
+                continue;
+
+            targetList.Add(target);
+        }
+
+        // cancel if the required items do not exist
+        if (targetList.Count == 0 && condition.Comp.VerifyMapExistance)
+        {
+            args.Cancelled = true;
+            return;
+        }
+
+        //setup condition settings
+        var maxSize = condition.Comp.VerifyMapExistance
+            ? Math.Min(targetList.Count, condition.Comp.MaxCollectionSize)
+            : condition.Comp.MaxCollectionSize;
+        var minSize = condition.Comp.VerifyMapExistance
+            ? Math.Min(targetList.Count, condition.Comp.MinCollectionSize)
+            : condition.Comp.MinCollectionSize;
+
+        condition.Comp.CollectionSize = _random.Next(minSize, maxSize);
     }
 
-    private void OnAfterAssign(EntityUid uid, StealConditionComponent comp, ref ObjectiveAfterAssignEvent args)
+    //Set the visual, name, icon for the objective.
+    private void OnAfterAssign(Entity<StealConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
     {
-        var proto = _proto.Index<EntityPrototype>(comp.Prototype);
-        var title = comp.OwnerText == null
-            ? Loc.GetString("objective-condition-steal-title-no-owner", ("itemName", proto.Name))
-            : Loc.GetString("objective-condition-steal-title", ("owner", Loc.GetString(comp.OwnerText)), ("itemName", proto.Name));
-        var description = Loc.GetString("objective-condition-steal-description", ("itemName", proto.Name));
-
-        _metaData.SetEntityName(uid, title, args.Meta);
-        _metaData.SetEntityDescription(uid, description, args.Meta);
-        _objectives.SetIcon(uid, new SpriteSpecifier.EntityPrototype(comp.Prototype), args.Objective);
-    }
+        var group = _proto.Index(condition.Comp.StealGroup);
 
-    private void OnGetProgress(EntityUid uid, StealConditionComponent comp, ref ObjectiveGetProgressEvent args)
+        var title =condition.Comp.OwnerText == null
+            ? Loc.GetString(condition.Comp.ObjectiveNoOwnerText, ("itemName", group.Name))
+            : Loc.GetString(condition.Comp.ObjectiveText, ("owner", Loc.GetString(condition.Comp.OwnerText)), ("itemName", group.Name));
+
+        var description = condition.Comp.CollectionSize > 1
+            ? Loc.GetString(condition.Comp.DescriptionMultiplyText, ("itemName", group.Name), ("count", condition.Comp.CollectionSize))
+            : Loc.GetString(condition.Comp.DescriptionText, ("itemName", group.Name));
+
+        _metaData.SetEntityName(condition.Owner, title, args.Meta);
+        _metaData.SetEntityDescription(condition.Owner, description, args.Meta);
+        _objectives.SetIcon(condition.Owner, group.Sprite, args.Objective);
+    }
+    private void OnGetProgress(Entity<StealConditionComponent> condition, ref ObjectiveGetProgressEvent args)
     {
-        args.Progress = GetProgress(args.Mind, comp.Prototype);
+        args.Progress = GetProgress(args.Mind, condition);
     }
 
-    private float GetProgress(MindComponent mind, string prototype)
+    private float GetProgress(MindComponent mind, StealConditionComponent condition)
     {
-        // TODO make this a container system function
-        // or: just iterate through transform children, instead of containers?
-
-        if (!metaQuery.TryGetComponent(mind.OwnedEntity, out var meta))
+        if (!_metaQuery.TryGetComponent(mind.OwnedEntity, out var meta))
+            return 0;
+        if (!_containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
             return 0;
 
-        // who added this check bruh
-        if (meta.EntityPrototype?.ID == prototype)
-            return 1;
+        var stack = new Stack<ContainerManagerComponent>();
+        var count = 0;
 
-        if (!containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
-            return 0;
+        //check pulling object 
+        if (TryComp<SharedPullerComponent>(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition
+        {
+            var pullid = pull.Pulling;
+            if (pullid != null)
+            {
+                // check if this is the item
+                if (CheckStealTarget(pullid.Value, condition)) count++;
+
+                //we don't check the inventories of sentient entity
+                if (!TryComp<MindContainerComponent>(pullid, out var pullMind))
+                {
+                    // if it is a container check its contents
+                    if (_containerQuery.TryGetComponent(pullid, out var containerManager))
+                        stack.Push(containerManager);
+                }
+            }
+        }
 
         // recursively check each container for the item
         // checks inventory, bag, implants, etc.
-        var stack = new Stack<ContainerManagerComponent>();
         do
         {
             foreach (var container in currentManager.Containers.Values)
@@ -78,16 +138,38 @@ public sealed class StealConditionSystem : EntitySystem
                 foreach (var entity in container.ContainedEntities)
                 {
                     // check if this is the item
-                    if (metaQuery.GetComponent(entity).EntityPrototype?.ID == prototype)
-                        return 1;
+                    if (CheckStealTarget(entity, condition)) count++; //To Do: add support for stackable items
 
                     // if it is a container check its contents
-                    if (containerQuery.TryGetComponent(entity, out var containerManager))
+                    if (_containerQuery.TryGetComponent(entity, out var containerManager))
                         stack.Push(containerManager);
                 }
             }
         } while (stack.TryPop(out currentManager));
 
-        return 0;
+        var result = (float) count / (float) condition.CollectionSize;
+        result = Math.Clamp(result, 0, 1);
+        return result;
+    }
+
+    private bool CheckStealTarget(EntityUid entity, StealConditionComponent condition)
+    {
+        // check if this is the target
+        if (!TryComp<StealTargetComponent>(entity, out var target))
+            return false;
+
+        if (target.StealGroup != condition.StealGroup)
+            return false;
+
+        // check if needed target alive
+        if (condition.CheckAlive)
+        {
+            if (TryComp<MobStateComponent>(entity, out var state))
+            {
+                if (!_mobState.IsAlive(entity))
+                    return false;
+            }
+        }
+        return true;
     }
 }
index 6ac0e24540e8d396fe54b30de151f7f20a1c41ec..c53fa1cf9ebcc7ab1a3f3e87068efe8e6a5171e8 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Shared.Roles;
+using Content.Shared.Roles;
 
 namespace Content.Server.Roles;
 
@@ -17,6 +17,7 @@ public sealed class RoleSystem : SharedRoleSystem
         SubscribeAntagEvents<SubvertedSiliconRoleComponent>();
         SubscribeAntagEvents<TraitorRoleComponent>();
         SubscribeAntagEvents<ZombieRoleComponent>();
+        SubscribeAntagEvents<ThiefRoleComponent>();
     }
 
     public string? MindGetBriefing(EntityUid? mindId)
diff --git a/Content.Server/Roles/ThiefRoleComponent.cs b/Content.Server/Roles/ThiefRoleComponent.cs
new file mode 100644 (file)
index 0000000..82e350e
--- /dev/null
@@ -0,0 +1,8 @@
+using Content.Shared.Roles;
+
+namespace Content.Server.Roles;
+
+[RegisterComponent]
+public sealed partial class ThiefRoleComponent : AntagonistRoleComponent
+{
+}
diff --git a/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs b/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs
new file mode 100644 (file)
index 0000000..c398074
--- /dev/null
@@ -0,0 +1,25 @@
+using Content.Shared.Thief;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Thief.Components;
+
+/// <summary>
+/// This component stores the possible contents of the backpack,
+/// which can be selected via the interface.
+/// </summary>
+[RegisterComponent]
+public sealed partial class ThiefUndeterminedBackpackComponent : Component
+{
+    /// <summary>
+    /// List of sets available for selection
+    /// </summary>
+    [DataField]
+    public List<ProtoId<ThiefBackpackSetPrototype>> PossibleSets = new();
+
+    [DataField]
+    public List<int> SelectedSets = new();
+
+    [DataField]
+    public SoundSpecifier ApproveSound = new SoundPathSpecifier("/Audio/Effects/rustle1.ogg");
+}
diff --git a/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs b/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs
new file mode 100644 (file)
index 0000000..99cbcfa
--- /dev/null
@@ -0,0 +1,79 @@
+using Content.Server.Thief.Components;
+using Content.Shared.Item;
+using Content.Shared.Thief;
+using Robust.Server.GameObjects;
+using Robust.Server.Audio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Thief.Systems;
+public sealed class ThiefUndeterminedBackpackSystem : EntitySystem
+{
+    [Dependency] private readonly AudioSystem _audio = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly UserInterfaceSystem _ui = default!;
+
+    private const int MaxSelectedSets = 2;
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ThiefUndeterminedBackpackComponent, BoundUIOpenedEvent>(OnUIOpened);
+        SubscribeLocalEvent<ThiefUndeterminedBackpackComponent, ThiefBackpackApproveMessage>(OnApprove);
+        SubscribeLocalEvent<ThiefUndeterminedBackpackComponent, ThiefBackpackChangeSetMessage>(OnChangeSet);
+    }
+
+    private void OnUIOpened(Entity<ThiefUndeterminedBackpackComponent> backpack, ref BoundUIOpenedEvent args)
+    {
+        UpdateUI(backpack.Owner, backpack.Comp);
+    }
+
+    private void OnApprove(Entity<ThiefUndeterminedBackpackComponent> backpack, ref ThiefBackpackApproveMessage args)
+    {
+        if (backpack.Comp.SelectedSets.Count != MaxSelectedSets)
+            return;
+
+        foreach (var i in backpack.Comp.SelectedSets)
+        {
+            var set = _proto.Index(backpack.Comp.PossibleSets[i]);
+            foreach (var item in set.Content)
+            {
+                var ent = Spawn(item, _transform.GetMapCoordinates(backpack.Owner));
+                if (TryComp<ItemComponent>(ent, out var itemComponent))
+                    _transform.DropNextTo(ent, backpack.Owner);
+            }
+        }
+        _audio.PlayPvs(backpack.Comp.ApproveSound, backpack.Owner);
+        QueueDel(backpack); //hehe
+    }
+    private void OnChangeSet(Entity<ThiefUndeterminedBackpackComponent> backpack, ref ThiefBackpackChangeSetMessage args)
+    {
+        //Swith selecting set
+        if (!backpack.Comp.SelectedSets.Remove(args.SetNumber))
+            backpack.Comp.SelectedSets.Add(args.SetNumber);
+
+        UpdateUI(backpack.Owner, backpack.Comp);
+    }
+
+    private void UpdateUI(EntityUid uid, ThiefUndeterminedBackpackComponent? component = null)
+    {
+        if (!Resolve(uid, ref component))
+            return;
+
+        Dictionary<int, ThiefBackpackSetInfo> data = new();
+
+        for (int i = 0; i < component.PossibleSets.Count; i++)
+        {
+            var set = _proto.Index(component.PossibleSets[i]);
+            var selected = component.SelectedSets.Contains(i);
+            var info = new ThiefBackpackSetInfo(
+                set.Name,
+                set.Description,
+                set.Sprite,
+                selected);
+            data.Add(i, info);
+        }
+
+        _ui.TrySetUiState(uid, ThiefBackpackUIKey.Key, new ThiefBackpackBoundUserInterfaceState(data, MaxSelectedSets));
+    }
+}
diff --git a/Content.Shared/Objectives/Prototypes/StealTargetGroupPrototype.cs b/Content.Shared/Objectives/Prototypes/StealTargetGroupPrototype.cs
new file mode 100644 (file)
index 0000000..2730acb
--- /dev/null
@@ -0,0 +1,15 @@
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Objectives;
+
+/// <summary>
+/// General data about a group of items, such as icon, description, name. Used for Steal objective
+/// </summary>
+[Prototype("stealTargetGroup")]
+public sealed partial class StealTargetGroupPrototype : IPrototype
+{
+    [IdDataField] public string ID { get; private set; } = default!;
+    [DataField] public string Name { get; private set; } = string.Empty;
+    [DataField] public SpriteSpecifier Sprite { get; private set; } = SpriteSpecifier.Invalid;
+}
diff --git a/Content.Shared/Thief/Components/ThiefBackpackUI.cs b/Content.Shared/Thief/Components/ThiefBackpackUI.cs
new file mode 100644 (file)
index 0000000..c68bd4c
--- /dev/null
@@ -0,0 +1,63 @@
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Thief;
+
+[Serializable, NetSerializable]
+public sealed class ThiefBackpackBoundUserInterfaceState : BoundUserInterfaceState
+{
+    public readonly Dictionary<int, ThiefBackpackSetInfo> Sets;
+    public int MaxSelectedSets;
+
+    public ThiefBackpackBoundUserInterfaceState(Dictionary<int, ThiefBackpackSetInfo> sets, int max)
+    {
+        Sets = sets;
+        MaxSelectedSets = max;
+    }
+}
+
+[Serializable, NetSerializable]
+public sealed class ThiefBackpackChangeSetMessage : BoundUserInterfaceMessage
+{
+    public readonly int SetNumber;
+
+    public ThiefBackpackChangeSetMessage(int setNumber)
+    {
+        SetNumber = setNumber;
+    }
+}
+
+[Serializable, NetSerializable]
+public sealed class ThiefBackpackApproveMessage : BoundUserInterfaceMessage
+{
+    public ThiefBackpackApproveMessage() { }
+}
+
+[Serializable, NetSerializable]
+public enum ThiefBackpackUIKey : byte
+{
+    Key
+};
+
+[Serializable, NetSerializable, DataDefinition]
+public partial struct ThiefBackpackSetInfo
+{
+    [DataField]
+    public string Name;
+
+    [DataField]
+    public string Description;
+
+    [DataField]
+    public SpriteSpecifier Sprite;
+
+    public bool Selected;
+
+    public ThiefBackpackSetInfo(string name, string desc, SpriteSpecifier sprite, bool selected)
+    {
+        Name = name;
+        Description = desc;
+        Sprite = sprite;
+        Selected = selected;
+    }
+}
diff --git a/Content.Shared/Thief/Prototypes/ThiefBackpackSetPrototype.cs b/Content.Shared/Thief/Prototypes/ThiefBackpackSetPrototype.cs
new file mode 100644 (file)
index 0000000..571db09
--- /dev/null
@@ -0,0 +1,18 @@
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Thief;
+
+/// <summary>
+/// A prototype that defines a set of items and visuals in a specific starter set for the antagonist thief
+/// </summary>
+[Prototype("thiefBackpackSet")]
+public sealed partial class ThiefBackpackSetPrototype : IPrototype
+{
+    [IdDataField] public string ID { get; private set; } = default!;
+    [DataField] public string Name { get; private set; } = string.Empty;
+    [DataField] public string Description { get; private set; } = string.Empty;
+    [DataField] public SpriteSpecifier Sprite { get; private set; } = SpriteSpecifier.Invalid;
+
+    [DataField] public List<EntProtoId> Content = new();
+}
index 29cab4382aa6411307ecacbd719b8376db78e1ca..2599cf8083c068440ab104cd04f9aaeb5dc714ce 100644 (file)
@@ -8,6 +8,11 @@
   copyright: "Taken from TG station."
   source: "https://github.com/tgstation/tgstation/blob/b02b93ce2ab891164511a973493cdf951b4120f7/sound/effects/ninja_greeting.ogg"
 
+- files: ["thief_greeting.ogg"]
+  license: "CC-BY-NC-4.0"
+  copyright: "Taken from SergeQuadrado via freesound.org, edit and mono by TheShuEd"
+  source: "https://freesound.org/people/SergeQuadrado/sounds/683504/"
+
 - files: ["narsie_rises.ogg"]
   license: "CC-BY-SA-3.0"
   copyright: "Taken from TG station."
diff --git a/Resources/Audio/Misc/thief_greeting.ogg b/Resources/Audio/Misc/thief_greeting.ogg
new file mode 100644 (file)
index 0000000..826d48c
Binary files /dev/null and b/Resources/Audio/Misc/thief_greeting.ogg differ
index f3b3d91945ba67cd097c2edf6c51722714cb1bf8..535659f27e5349c3be534d8c7dd25e059b3d565c 100644 (file)
@@ -4,9 +4,11 @@ admin-verb-make-zombie = Zombifies the target immediately.
 admin-verb-make-nuclear-operative = Make target into a lone Nuclear Operative.
 admin-verb-make-pirate = Make the target into a pirate. Note this doesn't configure the game rule.
 admin-verb-make-head-rev = Make the target into a Head Revolutionary.
+admin-verb-make-thief = Make the target into a thief.
 
 admin-verb-text-make-traitor = Make Traitor
 admin-verb-text-make-zombie = Make Zombie
 admin-verb-text-make-nuclear-operative = Make Nuclear Operative
 admin-verb-text-make-pirate = Make Pirate
 admin-verb-text-make-head-rev = Make Head Rev
+admin-verb-text-make-thief = Make Thief
\ No newline at end of file
diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl
new file mode 100644 (file)
index 0000000..4a03735
--- /dev/null
@@ -0,0 +1,20 @@
+thief-role-greeting-human = 
+    You are a criminal scum.
+    You want to add to your collection
+    some property of Nanotrasen station. 
+    You're not a murderer. 
+    You are forbidden to kill.
+
+thief-role-greeting-animal = 
+    You are a kleptomaniaс animal.
+    Steal things you like.
+
+thief-role-greeting-equipment = 
+    You have a toolbox of thief's 
+    tools and chameleon thief's gloves.
+    Choose your starting equipment,
+    and do your work stealthily.
+
+objective-issuer-thief = [color=#746694]Criminal[/color]
+
+thief-round-end-agent-name = thief
\ No newline at end of file
index 35095c2a2df26ee40bc4245d32460a9847b3b8c9..bce645da8e8bff3c548e18582f0c29ba334e2a88 100644 (file)
@@ -1,6 +1,11 @@
 objective-condition-steal-title-no-owner = Steal the {$itemName}.
+objective-condition-steal-title-alive-no-owner = Steal {$itemName}.
 objective-condition-steal-title = Steal the {$owner}'s {$itemName}.
 objective-condition-steal-description = We need you to steal {$itemName}. Don't get caught.
 
 objective-condition-steal-station = station
 objective-condition-steal-Ian = head of personnel's corgi
+
+objective-condition-thief-description = The {$itemName} would be a great addition to my collection! 
+objective-condition-thief-animal-description = The {$itemName} would be a great addition to my collection! Most importantly, alive.
+objective-condition-thief-multiply-description = I need to get {$count} {$itemName} and take them with me.
\ No newline at end of file
index f771b5d911606f53e9c4b281dc206934b6c0a8a2..2848300c190357c18bbd8145b0780bde474b9d32 100644 (file)
@@ -27,3 +27,6 @@ roles-antag-subverted-silicon-objective = Follow your new laws and do bad unto t
 
 roles-antag-space-ninja-name = Space Ninja
 roles-antag-space-ninja-objective = Use your stealth to sabotage the station, nom on electrical wires.
+
+roles-antag-thief-name = Thief
+roles-antag-thief-objective = Add some NT property to your personal collection without using violence.
\ No newline at end of file
diff --git a/Resources/Locale/en-US/thief/backpack.ftl b/Resources/Locale/en-US/thief/backpack.ftl
new file mode 100644 (file)
index 0000000..bdc463f
--- /dev/null
@@ -0,0 +1,62 @@
+thief-backpack-window-title = Thief toolbox
+
+thief-backpack-window-description = 
+    This toolbox is filled with unspecified contents. 
+    Now you need to remember what you put in it. 
+    Choose up to two different sets from the list.
+
+thief-backpack-window-selected = Kits selected: ({$selectedCount}/{$maxCount})
+
+thief-backpack-window-approve-button = Approve
+thief-backpack-button-select = Select [  ]
+thief-backpack-button-deselect = Select [X]
+
+# Sets
+
+thief-backpack-category-chameleon-name = Chameleon's kit.
+thief-backpack-category-chameleon-description = 
+    Includes a full set of clothing that contain 
+    chameleon technology, allowing you to disguise 
+    as pretty much anything on the station.
+
+thief-backpack-category-tools-name = Bearcatcher's kit
+thief-backpack-category-tools-description = 
+    A set of tools for roughing up doors, walls, windows, 
+    and anything else that for whatever reason doesn't 
+    want to let you in where you need to go. 
+    Includes two C4s, a multitool, jaws of life, 
+    a pair of advanced welder meson glasses and some insulated gloves.
+
+thief-backpack-category-chemistry-name = Chemistry kit
+thief-backpack-category-chemistry-description = 
+    A set for those who love to improve their body. 
+    It includes a storage implanter, 
+    a dna scrambler implanter, 
+    and a set of chemicals for a rainy day.
+
+thief-backpack-category-syndie-name = Syndie kit
+thief-backpack-category-syndie-description = 
+    A set of items from a syndicate agent you've robbed 
+    in the past. Includes an Agent ID card, a syndicate pAI,
+    and some strange red crystals.
+
+thief-backpack-category-sleeper-name = Sleepwalker's kit
+thief-backpack-category-sleeper-description = 
+    A set for those who like to sleep in,
+    or for everyone around you to sleep. 
+    Includes a set of nocturine vials, a hypopen and 
+    a tank of sleeping gas.
+
+thief-backpack-category-communicator-name = Communicator's kit
+thief-backpack-category-communicator-description = 
+    A communication enthusiast's kit. Includes a master key
+    for all station channels, a radio jammer, a portable 
+    crew monitor, a voice chameleon mask and lots of money for business deals.
+
+thief-backpack-category-smuggler-name = Smuggler's kit
+thief-backpack-category-smuggler-description = 
+    A kit for those who like to have big pockets. 
+    Includes a fulton beacon, ten fultons 
+    and an invisible crate. You can't move in them, 
+    but you can quickly hide or carry valuable loot.
+    This kit also has a cool void cloak to go along with it.
diff --git a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml
new file mode 100644 (file)
index 0000000..d879fd7
--- /dev/null
@@ -0,0 +1,107 @@
+- type: thiefBackpackSet
+  id: ChameleonSet
+  name: thief-backpack-category-chameleon-name
+  description: thief-backpack-category-chameleon-description
+  sprite: 
+    sprite: /Textures/Clothing/OuterClothing/Misc/black_hoodie.rsi
+    state: icon
+  content:
+  - ClothingUniformJumpsuitChameleon
+  - ClothingOuterChameleon
+  - ClothingNeckChameleon
+  - ClothingMaskGasChameleon
+  - ClothingHeadHatChameleon
+  - ClothingEyesChameleon
+  - ClothingHeadsetChameleon
+  - ClothingShoesChameleon
+
+- type: thiefBackpackSet
+  id: ToolsSet
+  name: thief-backpack-category-tools-name
+  description: thief-backpack-category-tools-description
+  sprite: 
+    sprite: Objects/Tools/jaws_of_life.rsi
+    state: jaws_pry
+  content:
+  - WelderIndustrialAdvanced
+  - ClothingEyesGlassesMeson
+  - ClothingHandsGlovesColorYellow
+  - JawsOfLife
+  - Multitool
+  - C4
+  - C4
+  - ClothingMaskClown
+
+- type: thiefBackpackSet
+  id: ChemistrySet
+  name: thief-backpack-category-chemistry-name
+  description: thief-backpack-category-chemistry-description
+  sprite:
+    sprite: Objects/Specific/Medical/implanter.rsi
+    state: implanter0
+  content:
+  - StorageImplanter
+  - DnaScramblerImplanter
+  - EphedrineChemistryBottle
+  - OmnizineChemistryBottle
+  - Syringe
+  - DrinkVodkaBottleFull
+
+- type: thiefBackpackSet
+  id: SyndieSet
+  name: thief-backpack-category-syndie-name
+  description: thief-backpack-category-syndie-description
+  sprite:
+    sprite: Objects/Specific/Syndicate/telecrystal.rsi
+    state: telecrystal
+  content:
+  - AgentIDCard
+  - SyndicatePersonalAI
+  - ClothingHeadHatSyndieMAA
+  - CigPackSyndicate
+  - Telecrystal10 #The thief cannot use them, but it may induce communication with traitors
+
+- type: thiefBackpackSet
+  id: SleeperSet
+  name: thief-backpack-category-sleeper-name
+  description: thief-backpack-category-sleeper-description
+  sprite: 
+    sprite: Objects/Tanks/anesthetic.rsi
+    state: icon
+  content:
+  - ClothingHeadPyjamaSyndicateBlack
+  - ClothingUniformJumpsuitPyjamaSyndicateBlack
+  - NocturineChemistryBottle
+  - NocturineChemistryBottle
+  - NocturineChemistryBottle
+  - HypopenBox
+  - NitrousOxideTankFilled
+  - BedsheetSyndie
+
+- type: thiefBackpackSet
+  id: CommunicatorSet
+  name: thief-backpack-category-communicator-name
+  description: thief-backpack-category-communicator-description
+  sprite: 
+    sprite: Objects/Tools/spy_device.rsi
+    state: icon
+  content:
+  - EncryptionKeyStationMaster
+  - RadioJammer
+  - SpyCrewMonitor
+  - BriefcaseSyndieLobbyingBundleFilled
+  - ClothingMaskGasVoiceChameleon
+  #- todo Chameleon Stamp
+  
+- type: thiefBackpackSet
+  id: SmugglerSet
+  name: thief-backpack-category-smuggler-name
+  description: thief-backpack-category-smuggler-description
+  sprite: 
+    sprite: Clothing/Neck/Cloaks/void.rsi
+    state: icon
+  content:
+  - InvisibleCrate
+  - ClothingNeckCloakVoid
+  - FultonBeacon
+  - Fulton
index 7981fc2b9e9cdf6c27eb38e47fbbdd22f9b75921..9dd72691b5a0a01ccd0677ea95b66a307f063236 100644 (file)
@@ -96,6 +96,8 @@
     sprite: Clothing/Ears/Headsets/medical.rsi
   - type: Clothing
     sprite: Clothing/Ears/Headsets/medical.rsi
+  - type: StealTarget
+    stealGroup: ClothingHeadsetAltMedical
 
 - type: entity
   parent: ClothingHeadsetAlt
index 0cc37f0b56f85fd70438b7a96f9469c1a2e4d028..18dc207a9e39f39cf0752c6ba1d3120457b39b15 100644 (file)
@@ -43,6 +43,8 @@
   - type: Clothing
     sprite: Clothing/Eyes/Hud/beergoggles.rsi
   - type: ShowThirstIcons
+  - type: StealTarget
+    stealGroup: ClothingEyesHudBeer
   - type: SolutionScanner
 
 - type: entity
index df85596948374168ebdfe9e3651fd4dbadc43d8a..d3fa79511a818ebb1302d6c49e8a81cacf53f5c7 100644 (file)
     sprite: Clothing/Head/Hats/warden.rsi
   - type: Clothing
     sprite: Clothing/Head/Hats/warden.rsi
+  - type: StealTarget
+    stealGroup: ClothingHeadHatWarden
 
 - type: entity
   parent: ClothingHeadBase
index 361ef6573428b00a0335dd87fd67e903b78ad07e..6d6e2fb8a8ba3099c60ae6e733a65f0be0b1dfef 100644 (file)
@@ -6,6 +6,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/cap.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -15,6 +17,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/hos.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -24,6 +28,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/ce.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -33,6 +39,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/cmo.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -42,6 +50,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/rd.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -51,6 +61,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/qm.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
@@ -60,6 +72,8 @@
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/hop.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/capcloakformal.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
   components:
   - type: Sprite
     sprite: Clothing/Neck/Cloaks/admin.rsi
+  - type: StealTarget
+    stealGroup: HeadCloak
 
 - type: entity
   parent: ClothingNeckBase
index 3ac3fb126e9be5560b6349751d81e01c25570cb1..016258a6ed8177399d949558a3a6422292d88d7d 100644 (file)
@@ -20,6 +20,8 @@
     sprite: Clothing/Neck/Medals/gold.rsi\r
   - type: Clothing\r
     sprite: Clothing/Neck/Medals/gold.rsi\r
+  - type: StealTarget\r
+    stealGroup: ClothingNeckGoldmedal\r
 \r
 - type: entity\r
   parent: ClothingNeckBase\r
@@ -86,3 +88,5 @@
     sprite: Clothing/Neck/Medals/clownmedal.rsi\r
   - type: Clothing\r
     sprite: Clothing/Neck/Medals/clownmedal.rsi\r
+  - type: StealTarget\r
+    stealGroup: ClothingNeckClownmedal\r
index d73c8ab0467fd464fae291f3d2828986f217dd79..f88ad818ac743ceff21400518aa0e87b65d67c11 100644 (file)
     clothingPrototype: ClothingHeadHelmetHardsuitRd
   - type: StaticPrice
     price: 750
+  - type: StealTarget
+    stealGroup: ClothingOuterHardsuitRd
 
 #Head of Security's Hardsuit
 - type: entity
index e46db13bd29cbb4a09f4e7f79e5c681a19444056..128d2db5fb6efe206ac8b3756a6d3d23ee3d3b21 100644 (file)
         Radiation: 0.75
         Caustic: 0.5
   - type: GroupExamine
+  - type: StealTarget
+    stealGroup: ClothingOuterHardsuitVoidParamed
\ No newline at end of file
index 2b8f4202731608efbbd8c85bce1e318b0a177c31..38127f8a3ef7363220da555ea4a59d31e0985ff3 100644 (file)
@@ -53,6 +53,8 @@
     - HighRiskItem
   - type: StaticPrice
     price: 750
+  - type: StealTarget
+    stealGroup: ClothingShoesBootsMagAdv
 
 - type: entity
   parent: ClothingShoesBootsMag
index 9e669d7646193f6d0fafcea710ed2ebbb5c5f935..f8d37e0db0e464536142a466179044c0f3b70460 100644 (file)
@@ -34,6 +34,8 @@
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalIan
 
 - type: entity
   name: Old Ian
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalBingus
 
 - type: entity
   name: McGriff
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalMcGriff
 
 - type: entity
   name: Paperwork
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalWalter
 
 - type: entity
   name: Morty
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalMorty
 
 - type: entity
   name: Morty
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalRenault
 
 - type: entity
   name: Hamlet
     - CannotSuicide
     - Hamster
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalHamlet
 
 - type: entity
   name: Shiva
     tags:
     - CannotSuicide
     - VimPilot
+  - type: StealTarget
+    stealGroup: AnimalShiva
 
 - type: entity
   name: Willow
     attributes:
       proper: true
       gender: female
+  - type: StealTarget
+    stealGroup: AnimalSmile
 
 - type: entity
   name: Pun Pun
     attributes:
       proper: true
       gender: male
+  - type: StealTarget
+    stealGroup: AnimalPunPun
index a2db895c5cfeab57857b07b64810c5ddf05c54a9..ceafc1230266cb0e4e0a228494f8753eb42a40b6 100644 (file)
           Quantity: 20
   - type: StaticPrice
     price: 750
+  - type: StealTarget
+    stealGroup: FoodMeatCorgi
 
 - type: entity
   name: raw crab meat
index 4dbcddb983f9bce1be7c23c37a666621c6968960..73c737ad7c9808c96baa36201ca4cd3179561532 100644 (file)
@@ -27,6 +27,8 @@
       color: "#cc6600"
       radius: 2.0
       energy: 4.0
+    - type: StealTarget
+      stealGroup: LAMP
 
 - type: entity
   parent: CarvedPumpkin
index 8612f84197e562b57e3859835dc3d7becc3e7bb7..e0d084cec504deb1aae5a5c1f387538062aed27b 100644 (file)
@@ -61,6 +61,8 @@
       requirements:
         MatterBin: 1
         Manipulator: 1
+    - type: StealTarget
+      stealGroup: AmmoTechFabCircuitboard
 
 - type: entity
   id: MedicalTechFabCircuitboard
@@ -80,6 +82,8 @@
           Amount: 2
           DefaultPrototype: Beaker
           ExamineName: Glass Beaker
+    - type: StealTarget
+      stealGroup: MedicalTechFabCircuitboard
 
 - type: entity
   id: CircuitImprinterMachineCircuitboard
index 13967b9df597f58a06217d6569fa90f59cc4a40d..db15d9f6293b2d6873e28316bf9ab163e5bc6434 100644 (file)
       state: cpu_supply
     - type: ComputerBoard
       prototype: ComputerSalvageExpedition
+    - type: StealTarget
+      stealGroup: SalvageExpeditionsComputerCircuitboard
 
 - type: entity
   parent: BaseComputerCircuitboard
   components:
     - type: ComputerBoard
       prototype: ComputerShuttleCargo
+    - type: StealTarget
+      stealGroup: CargoShuttleConsoleCircuitboard
 
 - type: entity
   parent: BaseComputerCircuitboard
   components:
     - type: ComputerBoard
       prototype: ComputerShuttleSalvage
+    - type: StealTarget
+      stealGroup: SalvageShuttleConsoleCircuitboard
 
 - type: entity
   parent: BaseComputerCircuitboard
index 9ec14f6589f15a67d4395c4237eee1fff9fcae98..ae40b645142cd435996be5b3057823845ac08a5b 100644 (file)
@@ -9,6 +9,8 @@
     sprite: Objects/Devices/door_remote.rsi
   - type: Access
   - type: DoorRemote
+  - type: StealTarget
+    stealGroup: DoorRemote
 
 - type: entity
   parent: DoorRemoteDefault
index a47d9c8d610a584ecf63e397dfaa25dd68b25a0f..1408423c5c9bc9f7d43be89fb289ae7683b32d4f 100644 (file)
@@ -10,6 +10,8 @@
     sprite: Objects/Devices/encryption_keys.rsi
   - type: Sprite
     sprite: Objects/Devices/encryption_keys.rsi
+  - type: StealTarget
+    stealGroup: EncryptionKey
 
 - type: entity
   parent: EncryptionKey
index 21943bfaff390175e8bc2af74c18356c99300c6e..ebfba2f918f305573d8f439362ca571fb344bde1 100644 (file)
@@ -26,6 +26,8 @@
   - type: GuideHelp
     guides:
     - DNA
+  - type: StealTarget
+    stealGroup: ForensicScanner
 
 - type: entity
   name: forensic scanner report
index 0acd4fbf19f9a0d3f998d38b913374fce68ad04c..deac20e05ec42db30d32751616068e53f316fb03 100644 (file)
@@ -12,3 +12,5 @@
   - type: Tag
     tags:
     - HighRiskItem
+  - type: StealTarget
+    stealGroup: HandTeleporter
index 878353f80fe8e18cbe6bb8d728a88d6addc622e1..63e28648c3dae7d067af7a2d109a6dad2cda8778 100644 (file)
@@ -67,6 +67,8 @@
   - type: ContainerContainer
     containers:
       Nuke: !type:ContainerSlot
+  - type: StealTarget
+    stealGroup: NuclearBomb
 
 - type: entity
   parent: NuclearBomb
index 8d48573d6521367affea77395e34a7d5a2cd7c4f..a30f96b96bc261c37526ee16b79fb15f720a0ca8 100644 (file)
@@ -83,4 +83,4 @@
     state: pinpointer-station
   - type: Pinpointer
     component: BecomesStation
-    targetName: the station
+    targetName: the station
\ No newline at end of file
index 243d8eaea3d64a7d00bb6d11703b94bf47c3f811..5f23dc4ff78ef29a88bc6190f93be457e1fc6887 100644 (file)
@@ -17,6 +17,8 @@
   - type: Tag
     tags:
     - Figurine
+  - type: StealTarget
+    stealGroup: Figurines
 
 - type: entity
   parent: BaseFigurine
index 2945c4f68e7e25b932e0c0d6d7845865b5043043..3b9b51593ec0131b757e6b5495cb1a006cdc461f 100644 (file)
@@ -61,6 +61,8 @@
     state: sheetcaptain
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/captain.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetCE
@@ -72,6 +74,8 @@
     state: sheetce
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/ce.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetCentcom
@@ -83,6 +87,8 @@
     state: sheetcentcom
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/centcom.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetClown
     state: sheetcmo
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/cmo.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetCosmos
     state: sheethop
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/hop.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetHOS
     state: sheethos
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/hos.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetIan
     state: sheetqm
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/qm.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetRainbow
     state: sheetrd
   - type: Clothing
     sprite: Clothing/Neck/Bedsheets/rd.rsi
+  - type: StealTarget
+    stealGroup: HeadBedsheet
 
 - type: entity
   id: BedsheetBrigmedic
index ec0b64ade2fe2a516c2b2cfb688eeca671e58cc0..e389bc6b3789ff1a59352ba7d868ffd640930b0b 100644 (file)
@@ -20,6 +20,8 @@
   - type: Tag
     tags:
     - HighRiskItem
+  - type: StealTarget
+    stealGroup: NukeDisk
 
 - type: entity
   name: nuclear authentication disk
index 1b925b5cb081f4d8942a72c46c37a4f88037ab46..83701249d0ceca7af2eb1811722e42c62d7ebe48 100644 (file)
@@ -49,6 +49,8 @@
   - type: Appearance
   - type: Physics
     canCollide: false
+  - type: StealTarget
+    stealGroup: LAMP
 
 - type: entity
   name: lamp
index 69c0099825d407b7d0b59341f7a41d5b9b0afa9d..705733aa61a2c43327170fcdcb1b46fb59002b2b 100644 (file)
@@ -22,6 +22,8 @@
     tags:
     - DoorBumpOpener
     - WhitelistChameleon
+  - type: StealTarget
+    stealGroup: IDCard
 
 #IDs with layers
 
     - DoorBumpOpener
     - WhitelistChameleon
     - HighRiskItem
+  - type: StealTarget
+    stealGroup: CaptainIDCard
 
 - type: entity
   parent: IDCardStandard
index 01a642ff60b37b96d2f8af7763c6751f073c4ac9..2abec491389783157aac9510d504febad85c2b3b 100644 (file)
     damage:
       types:
         Blunt: 10
+  - type: StealTarget
+    stealGroup: BoxFolderQmClipboard
 
 # Stamps
 - type: entity
     state: stamp-mime
   - type: Item
     size: Tiny
+  - type: StealTarget
+    stealGroup: Stamp
 
 - type: entity
   name: alternate rubber stamp
index 4f0289da4f1f627c2c34a644e696a899e6c12407..ae6238d87cc210ca5acec853ee987492ff6b93ca 100644 (file)
@@ -12,3 +12,5 @@
       tags:
         - Book
         - HighRiskItem
+    - type: StealTarget
+      stealGroup: BookSecretDocuments
index b44779581314f5f633b61aa3a2c4852b6de1039e..a3674178bf5a280dcb8fc2b548ff4ae04e3d865a 100644 (file)
@@ -18,3 +18,5 @@
   - type: Tag
     tags:
       - DroneUsable
+  - type: StealTarget
+    stealGroup: AmePart
index 6096de5c7c7eb8935060eda979a14b271d9ef7ca..75ce118d9fb432db5e21aebd2262c4ca5d05c083 100644 (file)
@@ -46,6 +46,8 @@
   - type: Tag
     tags:
     - Book
+  - type: StealTarget
+    stealGroup: Bible
 
 - type: entity
   parent: Bible
index 8e26898daad029b9771e7fd60d9e3b6bee38c576..e87fec22acc380d976c62cb865c96153280db72d 100644 (file)
@@ -17,6 +17,8 @@
         reagents:
         - ReagentId: THC
           Quantity: 15
+  - type: StealTarget
+    stealGroup: Cannabis
 
 - type: entity
   name: dried cannabis leaves
@@ -36,6 +38,8 @@
   - type: Sprite
     sprite: Objects/Specific/Hydroponics/tobacco.rsi
     state: dried
+  - type: StealTarget
+    stealGroup: Cannabis
 
 - type: entity
   name: ground cannabis
@@ -64,6 +68,8 @@
       - Smokable
   - type: Item
     size: Tiny
+  - type: StealTarget
+    stealGroup: Cannabis
 
 - type: entity
   name: tobacco leaves
index 5827e832c82594986cea6d8369fe279a271102ed..ce29f10a5b44f4a1e9156ea321eb5838d0dcef7c 100644 (file)
@@ -52,7 +52,9 @@
   - type: Item
     sprite: Objects/Tools/spy_device.rsi
   - type: PowerCellDraw
-    useRate: 40 #Should be weaker than the original
+    useRate: 10
+  - type: StaticPrice
+    price: 750
 
 - type: entity
   id: SpyCrewMonitorEmpty
index b9a6b08052500db7314e096deb2d07fdbaf84c5b..3d28487d68e65b3e2c7feb0d74fe948b9c4ae967 100644 (file)
@@ -26,6 +26,8 @@
   - type: Tag
     tags:
     - HighRiskItem
+  - type: StealTarget
+    stealGroup: Hypospray
 
 - type: entity
   name: gorlex hypospray
index 2b9d76b1b39750c9153cb5cf5e73b386978eafa9..0cb605cee6461811060614bfbc5ee50796ef1f23 100644 (file)
@@ -59,6 +59,8 @@
   - type: TechnologyDisk
   - type: StaticPrice
     price: 100
+  - type: StealTarget
+    stealGroup: TechnologyDisk
 
 - type: entity
   parent: TechnologyDisk
index eb612a8bc0b0be87a08ead27714c79e96cce18fa..ed4afd061f552a0d88734f0fa50691b286679b08 100644 (file)
@@ -58,6 +58,8 @@
     - type: GuideHelp
       guides:
       - Xenoarchaeology
+    - type: StealTarget
+      stealGroup: XenoArtifact
 
 - type: entity
   parent: BaseXenoArtifact
index c43ffc830ccdef2049e6c64a7f57bfd60b775829..8e880c0bf5e747b2c247a211f393e02e9998e5f3 100644 (file)
@@ -66,6 +66,8 @@
   - type: Appearance
   - type: StaticPrice
     price: 40
+  - type: StealTarget
+    stealGroup: LAMP
 
 - type: entity
   name: seclite
index 15a16d99f6bdf3124e11cecf9cb548b9f1c53743..f40f47115bb7f7138d9796513933fb4b705ec64a 100644 (file)
   - type: Tag
     tags:
     - HighRiskItem
+  - type: StealTarget
+    stealGroup: JetpackCaptainFilled
 
 # Filled captain
 - type: entity
index 4d861ffd542b82a23838fd79926e6e3d2162633d..f4d6ea8364f34e07b0f6823892906c397eaa74f0 100644 (file)
@@ -53,6 +53,8 @@
     - type: ContainerContainer
       containers:
         cell_slot: !type:ContainerSlot {}
+    - type: StealTarget
+      stealGroup: LAMP
 
 - type: entity
   parent: Lantern
index 30bcdf3739d3742d90e6099b188fdf28d980371a..2627bbeedaf1c8029e1bf69956b37ba3360d9cfc 100644 (file)
     state: icon
   - type: Item
     sprite: Objects/Tools/Toolboxes/toolbox_gold.rsi
+
+- type: entity
+  id: ToolboxThief
+  name: thief undetermined toolbox
+  description: This is where your favorite thief's supplies lie. Try to remember which ones.
+  parent: BaseItem
+  components:
+  - type: Sprite
+    sprite: Objects/Tools/Toolboxes/toolbox_thief.rsi
+    state: icon
+  - type: ThiefUndeterminedBackpack
+    transformAfterSelect: AlwaysPoweredWallLight
+    possibleSets:
+    # - TO DO Thief pinpointer needed
+    - ChemistrySet
+    - ToolsSet
+    - ChameleonSet # - TO DO Chameleon stump PR needed
+    - SyndieSet
+    - SleeperSet
+    - CommunicatorSet
+    - SmugglerSet
+  - type: ActivatableUI
+    key: enum.ThiefBackpackUIKey.Key
+  - type: UserInterface
+    interfaces:
+      - key: enum.ThiefBackpackUIKey.Key
+        type: ThiefBackpackBoundUserInterface
\ No newline at end of file
index 4f245c3f0938690ded078b9ef499176609a14a3e..ccb5d36a0afa2a67c083119d09f8e7dc5f178a98 100644 (file)
           path: /Audio/Effects/Vehicle/vehiclestartup.ogg
           params:
             volume: -3
+  - type: StealTarget
+    stealGroup: VehicleSecway
 
 - type: entity
   parent: BaseVehicleRideable
index b4d3ce5b914ea825278750996bef1996d3341e0e..3c4dc98543f173bea988b295ede110f4c0dffda0 100644 (file)
     - Sidearm
   - type: StaticPrice
     price: 750
+  - type: StealTarget
+    stealGroup: WeaponAntiqueLaser
 
 - type: entity
   name: advanced laser pistol
index 1a96d7d17b52dfccf20e5ba7f24a19ddd07d1ed9..b98cf7d1e411f36eeadb2acaf8f780f9ec661776 100644 (file)
@@ -42,6 +42,8 @@
   - type: Prying
   - type: UseDelay
     delay: 1
+  - type: StealTarget
+    stealGroup: FireAxe
   - type: IgniteOnMeleeHit
     fireStacks: -4
 
index f64bc4ce018aeee7428b0534c61d8d0665db5c8d..15e40f79c35ca9d787ce7119d4cca8dfebb20138 100644 (file)
@@ -22,3 +22,5 @@
   - type: GuideHelp
     guides:
     - Bartender
+  - type: StealTarget
+    stealGroup: BoozeDispenser
index fd39cf2a5ec749abc0c7ec4e8250b6fad770d454..916b2b748c8f45a36d88e0f99111b29f22a61b72 100644 (file)
@@ -35,3 +35,5 @@
     guides:
     - Chemicals
     - Chemist
+  - type: StealTarget
+    stealGroup: ChemDispenser
index e51d31f571db59271d8dbcbafb9bc881ab277195..d34030eb973fdea80de8917ef8af7a71535bab3e 100644 (file)
@@ -64,6 +64,8 @@
             max: 3
       - !type:DoActsBehavior
         acts: [ "Destruction" ]
+  - type: StealTarget
+    stealGroup: AltarNanotrasen
 
 - type: entity
   id: AltarChaos
index 72a445a8a4bb54647a2b192fa67d96f040eaac1d..0c0aa495dcb3147714d72cb5a19a5e0f4fee33e1 100644 (file)
   components:
   - type: Sprite
     state: plant-25
+  - type: StealTarget
+    stealGroup: PlantRD
 
 - type: entity
   id: PottedPlant26
index 89e1e695eb6d87eec7b7c010dcd8e6df8d35062a..8cec88d7e182d8bc74815b40d4b9c12a672db7c1 100644 (file)
     - type: FaxMachine
       name: "Captain's Office"
       receiveNukeCodes: true
+    - type: StealTarget
+      stealGroup: FaxMachineCaptain
index 83fe7d206e06e1caaf7e54b4806e1af725d9540a..934c446eaf8f540e9751a8ef8e0287bb21f4cb10 100644 (file)
       board: ThermomachineFreezerMachineCircuitBoard
     - type: DeviceNetwork
       prefix: device-address-prefix-freezer
+    - type: StealTarget
+      stealGroup: FreezerHeater
 
 - type: entity
   parent: GasThermoMachineFreezer
       board: ThermomachineHeaterMachineCircuitBoard
     - type: DeviceNetwork
       prefix: device-address-prefix-heater
+    - type: StealTarget
+      stealGroup: FreezerHeater
 
 - type: entity
   parent: GasThermoMachineHeater
index af482b2dbbe17fbb0d0db51cb4a69dd41a3de343..9a378c26a449bd2460fc8797e6299029b4822789 100644 (file)
@@ -98,6 +98,8 @@
         path: /Audio/Ambience/Objects/vending_machine_hum.ogg
     - type: GuideHelp
       guides: [ TEG, Power ]
+    - type: StealTarget
+      stealGroup: Teg
 
 - type: entity
   id: TegCirculator
 
     - type: AtmosUnsafeUnanchor
     - type: TegCirculator
+    - type: StealTarget
+      stealGroup: Teg
 
 - # Spawned by the client-side circulator examine code to indicate the inlet/outlet direction.
   type: entity
index d7dff95b51e0212be64fa96a910e573c8d89a8b8..241ee931bb58f090d9fb3074ea5202e047067894 100644 (file)
     objectives:
     - CarpRiftsObjective
     - DragonSurviveObjective
+
+# need for admin panel antag create (because the rule doesn't have a roundstart entity like TraitorRule)
+- type: entity
+  id: Thief
+  parent: BaseGameRule
+  noSpawn: true
+  components:
+  - type: ThiefRule 
\ No newline at end of file
index a0fc66bb8115997299c7d6be5a17d014906867e7..066bf9b6269c7c90e56044840dc6f93c39e52c3a 100644 (file)
@@ -74,6 +74,8 @@
   noSpawn: true
   components:
   - type: TraitorRule
+  - type: ThiefRule #the thieves come as an extension of another gamemode
+    ruleChance: 0.5
 
 - type: entity
   id: Revolutionary
@@ -81,6 +83,8 @@
   noSpawn: true
   components:
   - type: RevolutionaryRule
+  - type: ThiefRule #the thieves come as an extension of another gamemode
+    ruleChance: 0.5
 
 - type: entity
   id: Sandbox
index b4ce16a6f9ba3a767ae8ffbc1230174976622b4a..e24b26e6e86a4c6f2fbd56a37baa25faf5b52e52 100644 (file)
   id: BaseStealObjective
   components:
   - type: StealCondition
+    objectiveNoOwnerText: objective-condition-steal-title-no-owner
+    objectiveText: objective-condition-steal-title
+    descriptionText: objective-condition-steal-description
+    descriptionMultiplyText: objective-condition-thief-multiply-description
 
 # requires that the player not die, ignores being on emergency shuttle or cuffed
 - type: entity
index 7d6c9a48a344e89f75815a68ee726cf0b547c2fd..95b1fb85931ea174d9a84e6e3fa99593731731ad 100644 (file)
     RandomTraitorAliveObjective: 1
     RandomTraitorProgressObjective: 1
 
+#Thief groups
+- type: weightedRandom
+  id: ThiefObjectiveGroups
+  weights:
+    ThiefObjectiveGroupCollection: 1
+    ThiefObjectiveGroupItem: 1
+
+- type: weightedRandom
+  id: ThiefBigObjectiveGroups
+  weights:
+    ThiefObjectiveGroupStructure: 1
+    ThiefObjectiveGroupAnimal: 2
+
+- type: weightedRandom
+  id: ThiefEscapeObjectiveGroups
+  weights:
+    ThiefObjectiveGroupEscape: 1
+
+
+
+- type: weightedRandom
+  id: ThiefObjectiveGroupCollection
+  weights:
+    HeadCloakStealCollectionObjective: 1           #command
+    HeadBedsheetStealCollectionObjective: 1
+    StampStealCollectionObjective: 1
+    DoorRemoteStealCollectionObjective: 1
+    TechnologyDiskStealCollectionObjective: 1        #rnd
+    FigurineStealCollectionObjective: 0.3          #service
+    IDCardsStealCollectionObjective: 1
+    EncryptionKeyStealCollectionObjective: 1
+    CannabisStealCollectionObjective: 1
+    LAMPStealCollectionObjective: 2 #only for moth
+
+- type: weightedRandom
+  id: ThiefObjectiveGroupItem
+  weights:
+    ForensicScannerStealObjective: 1                    #sec
+    AmmoTechFabCircuitboardStealObjective: 1
+    ClothingHeadHatWardenStealObjective: 1
+    ClothingOuterHardsuitVoidParamedStealObjective: 1   #med
+    MedicalTechFabCircuitboardStealObjective: 1
+    ClothingHeadsetAltMedicalStealObjective: 1
+    FireAxeStealObjective: 1                            #eng
+    AmePartStealObjective: 1
+    ExpeditionsCircuitboardStealObjective: 1            #sup
+    CargoShuttleCircuitboardStealObjective: 1
+    SalvageShuttleCircuitboardStealObjective: 1
+    ClothingEyesHudBeerStealObjective: 1                #srv
+    BibleStealObjective: 1
+    ClothingNeckGoldmedalStealObjective: 1              #other
+    ClothingNeckClownmedalStealObjective: 0.5
+    
+- type: weightedRandom
+  id: ThiefObjectiveGroupStructure
+  weights:
+    NuclearBombStealObjective: 0.5
+    FaxMachineCaptainStealObjective: 1
+    VehicleSecwayStealObjective: 1
+    ChemDispenserStealObjective: 1
+    XenoArtifactStealObjective: 1
+    FreezerHeaterStealObjective: 1
+    TegStealObjective: 1
+    BoozeDispenserStealObjective: 1
+    AltarNanotrasenStealObjective: 1
+    PlantRDStealObjective: 1
+
+- type: weightedRandom
+  id: ThiefObjectiveGroupAnimal
+  weights:
+    IanStealObjective: 1
+    BingusStealObjective: 1
+    McGriffStealObjective: 1
+    WalterStealObjective: 1
+    MortyStealObjective: 1
+    RenaultStealObjective: 1
+    HamletStealObjective: 1
+    ShivaStealObjective: 1
+    SmileStealObjective: 1
+    PunPunStealObjective: 1
+    
+- type: weightedRandom
+  id: ThiefObjectiveGroupEscape
+  weights:
+    EscapeThiefShuttleObjective: 1
 #Changeling, crew, wizard, when you code it...
diff --git a/Resources/Prototypes/Objectives/stealTargetGroups.yml b/Resources/Prototypes/Objectives/stealTargetGroups.yml
new file mode 100644 (file)
index 0000000..712b773
--- /dev/null
@@ -0,0 +1,408 @@
+# Traitor single items
+
+- type: stealTargetGroup
+  id: Hypospray
+  name: hypospray
+  sprite: 
+    sprite: Objects/Specific/Medical/hypospray.rsi
+    state: hypo
+
+- type: stealTargetGroup
+  id: ClothingOuterHardsuitRd
+  name: experimental research hardsuit
+  sprite: 
+    sprite: Clothing/OuterClothing/Hardsuits/rd.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: HandTeleporter
+  name: hand teleporter
+  sprite: 
+    sprite: Objects/Devices/hand_teleporter.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: BookSecretDocuments
+  name: "emergency security orders"
+  sprite: 
+    sprite: Objects/Misc/bureaucracy.rsi
+    state: folder-sec-doc
+    
+- type: stealTargetGroup
+  id: ClothingShoesBootsMagAdv
+  name: advanced magboots
+  sprite: 
+    sprite: Clothing/Shoes/Boots/magboots-advanced.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: BoxFolderQmClipboard
+  name: requisition digi-board
+  sprite: 
+    sprite: Objects/Misc/qm_clipboard.rsi
+    state: qm_clipboard
+    
+- type: stealTargetGroup
+  id: FoodMeatCorgi
+  name: prime-cut corgi meat
+  sprite: 
+    sprite: Objects/Consumable/Food/meat.rsi
+    state: corgi
+    #
+- type: stealTargetGroup
+  id: CaptainIDCard
+  name: captain ID card
+  sprite: 
+    sprite: Objects/Misc/id_cards.rsi
+    state: ert_commander #no one will know the difference.
+
+- type: stealTargetGroup
+  id: JetpackCaptainFilled
+  name: captain's jetpack
+  sprite: 
+    sprite: Objects/Tanks/Jetpacks/captain.rsi
+    state: icon
+
+- type: stealTargetGroup
+  id: WeaponAntiqueLaser
+  name: antique laser pistol
+  sprite: 
+    sprite: Objects/Weapons/Guns/Battery/antiquelasergun.rsi
+    state: base
+
+- type: stealTargetGroup
+  id: NukeDisk
+  name: nuclear authentication disk
+  sprite: 
+    sprite: Objects/Misc/nukedisk.rsi
+    state: icon
+
+# Thief Collection
+
+- type: stealTargetGroup
+  id: Figurines
+  name: figurines (any)
+  sprite: 
+    sprite: Objects/Fun/figurines.rsi
+    state: figurine_spawner
+
+- type: stealTargetGroup
+  id: HeadCloak
+  name: head's cloaks (any)
+  sprite: 
+    sprite: Clothing/Neck/Cloaks/cap.rsi
+    state: icon
+
+- type: stealTargetGroup
+  id: HeadBedsheet
+  name: head's bedsheets (any)
+  sprite: 
+    sprite: Objects/Misc/bedsheets.rsi
+    state: sheetNT
+
+- type: stealTargetGroup
+  id: Stamp
+  name: stamps (any)
+  sprite:
+    sprite: Objects/Misc/bureaucracy.rsi
+    state: stamp-cap
+    
+- type: stealTargetGroup
+  id: DoorRemote
+  name: door remotes (any)
+  sprite:
+    sprite: Objects/Devices/door_remote.rsi
+    state: door_remotebase
+    
+- type: stealTargetGroup
+  id: EncryptionKey
+  name: encryption keys (any)
+  sprite:
+    sprite: Objects/Devices/encryption_keys.rsi
+    state: crypt_gray
+    
+- type: stealTargetGroup
+  id: TechnologyDisk
+  name: technology disks
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: datadisk_base
+
+- type: stealTargetGroup
+  id: IDCard
+  name: ID Cards (any)
+  sprite:
+    sprite: Objects/Misc/id_cards.rsi
+    state: default
+
+- type: stealTargetGroup
+  id: Cannabis
+  name: cannabis
+  sprite:
+    sprite: Objects/Specific/Hydroponics/cannabis.rsi
+    state: produce
+
+- type: stealTargetGroup
+  id: LAMP
+  name: LAMPS
+  sprite:
+    sprite: Objects/Tools/lantern.rsi
+    state: lantern
+
+# Thief single item
+
+- type: stealTargetGroup
+  id: ForensicScanner
+  name: forensic scanner
+  sprite:
+    sprite: Objects/Devices/forensic_scanner.rsi
+    state: forensicnew
+    
+- type: stealTargetGroup
+  id: AmmoTechFabCircuitboard
+  name: ammo techfab circuit board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: security
+    
+- type: stealTargetGroup
+  id: ClothingHeadHatWarden
+  name: warden's cap
+  sprite:
+    sprite: Clothing/Head/Hats/warden.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: ClothingOuterHardsuitVoidParamed
+  name: paramedic Void Suit
+  sprite:
+    sprite: Clothing/OuterClothing/Hardsuits/paramed.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: MedicalTechFabCircuitboard
+  name: medical techfab machine board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: medical
+    
+- type: stealTargetGroup
+  id: ClothingHeadsetAltMedical
+  name: chief medical officer's over-ear headset
+  sprite:
+    sprite: Clothing/Ears/Headsets/medical.rsi
+    state: icon_alt
+    
+- type: stealTargetGroup
+  id: ResearchAndDevelopmentServerMachineCircuitboard
+  name: R&D server machine board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: science
+    
+- type: stealTargetGroup
+  id: FireAxe
+  name: fireaxe
+  sprite:
+    sprite: Objects/Weapons/Melee/fireaxe.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: AmePart
+  name: AME part
+  sprite:
+    sprite: Objects/Power/AME/ame_part.rsi
+    state: box
+    
+- type: stealTargetGroup
+  id: SalvageExpeditionsComputerCircuitboard
+  name: salvage expeditions computer board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: cpu_supply
+    
+- type: stealTargetGroup
+  id: CargoShuttleConsoleCircuitboard
+  name: cargo shuttle console board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: cpuboard
+    
+- type: stealTargetGroup
+  id: SalvageShuttleConsoleCircuitboard
+  name: salvage shuttle console board
+  sprite:
+    sprite: Objects/Misc/module.rsi
+    state: cpuboard
+    
+- type: stealTargetGroup
+  id: ClothingEyesHudBeer
+  name: beer goggles
+  sprite:
+    sprite: Clothing/Eyes/Hud/beergoggles.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: Bible
+  name: bible
+  sprite:
+    sprite: Objects/Specific/Chapel/bible.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: ClothingNeckGoldmedal
+  name: gold medal of crewmanship
+  sprite:
+    sprite: Clothing/Neck/Medals/gold.rsi
+    state: icon
+    
+- type: stealTargetGroup
+  id: ClothingNeckClownmedal
+  name: clown medal
+  sprite:
+    sprite: Clothing/Neck/Medals/clownmedal.rsi
+    state: icon
+
+#Thief structures
+
+- type: stealTargetGroup
+  id: NuclearBomb
+  name: nuclear fission explosive
+  sprite:
+    sprite: Objects/Devices/nuke.rsi
+    state: nuclearbomb_base
+
+- type: stealTargetGroup
+  id: FaxMachineCaptain
+  name: captain long range fax machine
+  sprite:
+    sprite: Structures/Machines/fax_machine.rsi
+    state: icon
+
+- type: stealTargetGroup
+  id: VehicleSecway
+  name: secway
+  sprite:
+    sprite: Objects/Vehicles/secway.rsi
+    state: icon
+
+- type: stealTargetGroup
+  id: ChemDispenser
+  name: chemical dispenser
+  sprite:
+    sprite: Structures/dispensers.rsi
+    state: industrial-working
+
+- type: stealTargetGroup
+  id: XenoArtifact
+  name: big alien artifact
+  sprite:
+    sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi
+    state: ano28
+
+- type: stealTargetGroup
+  id: FreezerHeater
+  name: freezer or heater
+  sprite:
+    sprite: Structures/Piping/Atmospherics/thermomachine.rsi
+    state: heaterOff
+
+- type: stealTargetGroup
+  id: Teg
+  name: teg generator part
+  sprite:
+    sprite: Structures/Power/Generation/teg.rsi
+    state: teg
+
+- type: stealTargetGroup
+  id: BoozeDispenser
+  name: booze dispenser
+  sprite:
+    sprite: Structures/smalldispensers.rsi
+    state: booze
+
+- type: stealTargetGroup
+  id: AltarNanotrasen
+  name: nanotrasen altar (any)
+  sprite:
+    sprite: Structures/Furniture/Altars/Gods/nanotrasen.rsi
+    state: nanotrasen
+
+- type: stealTargetGroup
+  id: PlantRD
+  name: RD's potted plant
+  sprite:
+    sprite: Structures/Furniture/potted_plants.rsi
+    state: plant-25
+
+# Thief Animal
+
+- type: stealTargetGroup
+  id: AnimalIan
+  name: Ian
+  sprite:
+    sprite: Mobs/Pets/corgi.rsi
+    state: ian
+    
+- type: stealTargetGroup
+  id: AnimalBingus
+  name: Bingus
+  sprite:
+    sprite: Mobs/Pets/bingus.rsi
+    state: bingus
+    
+- type: stealTargetGroup
+  id: AnimalMcGriff
+  name: McGriff
+  sprite:
+    sprite: Mobs/Pets/mcgriff.rsi
+    state: mcgriff
+    
+- type: stealTargetGroup
+  id: AnimalWalter
+  name: Walter
+  sprite:
+    sprite: Mobs/Pets/walter.rsi
+    state: walter
+    
+- type: stealTargetGroup
+  id: AnimalMorty
+  name: Morty
+  sprite:
+    sprite: Mobs/Animals/possum.rsi
+    state: possum
+    
+- type: stealTargetGroup
+  id: AnimalRenault
+  name: Renault
+  sprite:
+    sprite: Mobs/Animals/fox.rsi
+    state: fox
+
+- type: stealTargetGroup
+  id: AnimalHamlet
+  name: Hamlet
+  sprite:
+    sprite: Mobs/Pets/hamlet.rsi
+    state: hamster-0
+
+- type: stealTargetGroup
+  id: AnimalShiva
+  name: Shiva
+  sprite:
+    sprite: Mobs/Pets/shiva.rsi
+    state: shiva
+
+- type: stealTargetGroup
+  id: AnimalSmile
+  name: Smile
+  sprite:
+    sprite: Mobs/Aliens/slimes.rsi
+    state: rainbow_baby_slime
+
+- type: stealTargetGroup
+  id: AnimalPunPun
+  name: Pun Pun
+  sprite:
+    sprite: Mobs/Animals/monkey.rsi
+    state: monkey
\ No newline at end of file
diff --git a/Resources/Prototypes/Objectives/thief.yml b/Resources/Prototypes/Objectives/thief.yml
new file mode 100644 (file)
index 0000000..3201a99
--- /dev/null
@@ -0,0 +1,620 @@
+- type: entity
+  abstract: true
+  parent: BaseObjective
+  id: BaseThiefObjective
+  components:
+  - type: Objective
+    issuer: thief
+  - type: RoleRequirement
+    roles:
+      components:
+      - ThiefRole
+
+- type: entity
+  abstract: true
+  parent: [BaseThiefObjective, BaseStealObjective]
+  id: BaseThiefStealObjective
+  components:
+  - type: StealCondition
+    verifyMapExistance: false
+    descriptionText: objective-condition-thief-description
+
+- type: entity
+  abstract: true
+  parent: [BaseThiefObjective, BaseStealObjective]
+  id: BaseThiefStealCollectionObjective
+  components:
+  - type: StealCondition
+    verifyMapExistance: true
+    descriptionText: objective-condition-thief-description
+    
+- type: entity
+  abstract: true
+  parent: [BaseThiefObjective, BaseStealObjective]
+  id: BaseThiefStealStructureObjective
+  components:
+  - type: StealCondition
+    verifyMapExistance: true
+    descriptionText: objective-condition-thief-description
+  - type: Objective
+    difficulty: 2 # it's hard to hide
+    
+- type: entity
+  abstract: true
+  parent: [BaseThiefObjective, BaseStealObjective]
+  id: BaseThiefStealAnimalObjective
+  components:
+  - type: StealCondition
+    verifyMapExistance: false
+    checkAlive: true
+    objectiveNoOwnerText: objective-condition-steal-title-alive-no-owner
+    descriptionText: objective-condition-thief-animal-description
+  - type: Objective
+    difficulty: 2 # it's hard to hide
+
+# Collections
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: FigurineStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: Figurines
+    minCollectionSize: 10
+    maxCollectionSize: 50 #will be limited to the number of figures on the station anyway.
+  - type: Objective
+    difficulty: 0.25
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: HeadCloakStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: HeadCloak
+    minCollectionSize: 3
+    maxCollectionSize: 6
+  - type: Objective
+    difficulty: 1.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: HeadBedsheetStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: HeadBedsheet
+    minCollectionSize: 3
+    maxCollectionSize: 6
+  - type: Objective
+    difficulty: 1.0
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: StampStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: Stamp
+    minCollectionSize: 5
+    maxCollectionSize: 15
+  - type: Objective
+    difficulty: 1.0
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: DoorRemoteStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: DoorRemote
+    minCollectionSize: 2
+    maxCollectionSize: 5
+  - type: Objective
+    difficulty: 1.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: TechnologyDiskStealCollectionObjective
+  components:
+  - type: NotJobRequirement
+    job: Scientist
+  - type: StealCondition
+    stealGroup: TechnologyDisk
+    minCollectionSize: 10
+    maxCollectionSize: 20
+    verifyMapExistance: false
+  - type: Objective
+    difficulty: 0.8
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: IDCardsStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: IDCard
+    minCollectionSize: 10
+    maxCollectionSize: 20
+    verifyMapExistance: false
+  - type: Objective
+    difficulty: 0.7
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: EncryptionKeyStealCollectionObjective
+  components:
+  - type: StealCondition
+    stealGroup: EncryptionKey
+    minCollectionSize: 5
+    maxCollectionSize: 25
+  - type: Objective
+    difficulty: 0.7
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: CannabisStealCollectionObjective
+  components:
+  - type: NotJobRequirement
+    job: Botanist
+  - type: StealCondition
+    stealGroup: Cannabis
+    minCollectionSize: 20
+    maxCollectionSize: 30
+    verifyMapExistance: false
+  - type: Objective
+    difficulty: 0.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealCollectionObjective
+  id: LAMPStealCollectionObjective
+  components:
+  - type: SpeciesRequirement
+    allowedSpecies:
+    - Moth
+  - type: StealCondition
+    stealGroup: LAMP
+    minCollectionSize: 1
+    maxCollectionSize: 30
+    verifyMapExistance: true
+  - type: Objective
+    difficulty: 0.5 # just for fun, collectings LAMP on Moth
+
+# steal item
+
+- type: entity                                      #Security subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ForensicScannerStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Detective
+  - type: StealCondition
+    stealGroup: ForensicScanner
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: AmmoTechFabCircuitboardStealObjective
+  components:
+  - type: StealCondition
+    stealGroup: AmmoTechFabCircuitboard
+  - type: Objective
+    difficulty: 1.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingHeadHatWardenStealObjective
+  components:
+  - type: StealCondition
+    stealGroup: ClothingHeadHatWarden
+  - type: Objective
+    difficulty: 1.2
+
+- type: entity                                      #Medical subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingOuterHardsuitVoidParamedStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Paramedic
+  - type: StealCondition
+    stealGroup: ClothingOuterHardsuitVoidParamed
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: MedicalTechFabCircuitboardStealObjective
+  components:
+  - type: NotJobRequirement
+    job: MedicalDoctor
+  - type: StealCondition
+    stealGroup: MedicalTechFabCircuitboard
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingHeadsetAltMedicalStealObjective
+  components:
+  - type: NotJobRequirement
+    job: ChiefMedicalOfficer
+  - type: StealCondition
+    stealGroup: ClothingHeadsetAltMedical
+  - type: Objective
+    difficulty: 1
+
+- type: entity                                      #Engineering subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: FireAxeStealObjective
+  components:
+  - type: NotJobRequirement
+    job: AtmosphericTechnician
+  - type: StealCondition
+    stealGroup: FireAxe
+  - type: Objective
+    difficulty: 0.8
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: AmePartStealObjective
+  components:
+  - type: NotJobRequirement
+    job: StationEngineer
+  - type: StealCondition
+    stealGroup: AmePart
+  - type: Objective
+    difficulty: 1
+
+- type: entity                                      #Cargo subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ExpeditionsCircuitboardStealObjective
+  components:
+  - type: NotJobRequirement
+    job: SalvageSpecialist
+  - type: StealCondition
+    stealGroup: SalvageExpeditionsComputerCircuitboard
+  - type: Objective
+    difficulty: 0.7
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: CargoShuttleCircuitboardStealObjective
+  components:
+  - type: NotJobRequirement
+    job: CargoTechnician
+  - type: StealCondition
+    stealGroup: CargoShuttleConsoleCircuitboard
+  - type: Objective
+    difficulty: 0.7
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: SalvageShuttleCircuitboardStealObjective
+  components:
+  - type: NotJobRequirement
+    job: SalvageSpecialist
+  - type: StealCondition
+    stealGroup: SalvageShuttleConsoleCircuitboard
+  - type: Objective
+    difficulty: 0.7
+
+- type: entity                                      #Service subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingEyesHudBeerStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Bartender
+  - type: StealCondition
+    stealGroup: ClothingEyesHudBeer
+  - type: Objective
+    difficulty: 0.3
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: BibleStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Chaplain
+  - type: StealCondition
+    stealGroup: Bible
+  - type: Objective
+    difficulty: 0.4
+
+- type: entity                                      #Other subgroup
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingNeckGoldmedalStealObjective
+  components:
+  - type: NotJobRequirement
+    job: HeadOfPersonnel
+  - type: StealCondition
+    stealGroup: ClothingNeckGoldmedal
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealObjective
+  id: ClothingNeckClownmedalStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Captain
+  - type: StealCondition
+    stealGroup: ClothingNeckClownmedal
+  - type: Objective
+    difficulty: 1
+
+# Structures
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: NuclearBombStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Captain
+  - type: StealCondition
+    stealGroup: NuclearBomb
+  - type: Objective
+    difficulty: 2.5 #Good luck
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: FaxMachineCaptainStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Captain
+  - type: StealCondition
+    stealGroup: FaxMachineCaptain
+  - type: Objective
+    difficulty: 2
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: VehicleSecwayStealObjective
+  components:
+  - type: NotJobRequirement
+    job: SecurityOfficer
+  - type: StealCondition
+    stealGroup: VehicleSecway
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: ChemDispenserStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Chemist
+  - type: StealCondition
+    stealGroup: ChemDispenser
+  - type: Objective
+    difficulty: 1
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: XenoArtifactStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Scientist
+  - type: StealCondition
+    stealGroup: XenoArtifact
+  - type: Objective
+    difficulty: 0.5
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: FreezerHeaterStealObjective
+  components:
+  - type: NotJobRequirement
+    job: AtmosphericTechnician
+  - type: StealCondition
+    stealGroup: FreezerHeater
+  - type: Objective
+    difficulty: 0.5
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: TegStealObjective
+  components:
+  - type: NotJobRequirement
+    job: AtmosphericTechnician
+  - type: StealCondition
+    stealGroup: Teg
+  - type: Objective
+    difficulty: 1
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: BoozeDispenserStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Bartender
+  - type: StealCondition
+    stealGroup: BoozeDispenser
+  - type: Objective
+    difficulty: 0.5
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: AltarNanotrasenStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Chaplain
+  - type: StealCondition
+    stealGroup: AltarNanotrasen
+  - type: Objective
+    difficulty: 0.5
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealStructureObjective
+  id: PlantRDStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Scientist
+  - type: StealCondition
+    stealGroup: PlantRD
+  - type: Objective
+    difficulty: 0.8
+
+# Animal
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: IanStealObjective
+  components:
+  - type: NotJobRequirement
+    job: HeadOfPersonnel
+  - type: StealCondition
+    stealGroup: AnimalIan
+  - type: Objective
+    difficulty: 2.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: BingusStealObjective
+  components:
+  - type: StealCondition
+    stealGroup: AnimalBingus
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: McGriffStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Detective
+  - type: StealCondition
+    stealGroup: AnimalMcGriff
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: WalterStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Chemist
+  - type: StealCondition
+    stealGroup: AnimalWalter
+  - type: Objective
+    difficulty: 1
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: MortyStealObjective
+  components:
+  - type: StealCondition
+    stealGroup: AnimalMorty
+  - type: Objective
+    difficulty: 0.5
+
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: RenaultStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Captain
+  - type: StealCondition
+    stealGroup: AnimalRenault
+  - type: Objective
+    difficulty: 2
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: HamletStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Captain
+  - type: StealCondition
+    stealGroup: AnimalHamlet
+  - type: Objective
+    difficulty: 1
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: ShivaStealObjective
+  components:
+  - type: NotJobRequirement
+    job: SecurityOfficer
+  - type: StealCondition
+    stealGroup: AnimalShiva
+  - type: Objective
+    difficulty: 2
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: SmileStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Scientist
+  - type: StealCondition
+    stealGroup: AnimalSmile
+  - type: Objective
+    difficulty: 1
+    
+- type: entity
+  noSpawn: true
+  parent: BaseThiefStealAnimalObjective
+  id: PunPunStealObjective
+  components:
+  - type: NotJobRequirement
+    job: Bartender
+  - type: StealCondition
+    stealGroup: AnimalPunPun
+  - type: Objective
+    difficulty: 2
+
+# Escape
+
+- type: entity
+  noSpawn: true
+  parent: [BaseThiefObjective, BaseLivingObjective]
+  id: EscapeThiefShuttleObjective
+  name: Escape to centcom alive and unrestrained.
+  description: You don't want your illegal activities to be discovered by anyone, do you?
+  components:
+  - type: Objective
+    difficulty: 1.3
+    icon:
+      sprite: Structures/Furniture/chairs.rsi
+      state: shuttle
+  - type: EscapeShuttleCondition
index 840564a96983069db124018f99e05c54b8ec829f..4684e2d343d847ef04184cd20fe2a9737efde6ec 100644 (file)
@@ -26,6 +26,8 @@
   parent: [BaseTraitorObjective, BaseStealObjective]
   id: BaseTraitorStealObjective
   components:
+  - type: StealCondition
+    verifyMapExistance: false
   - type: Objective
     difficulty: 2.75
 
   - type: NotJobRequirement
     job: ChiefMedicalOfficer
   - type: StealCondition
-    prototype: Hypospray
+    stealGroup: Hypospray
     owner: job-name-cmo
 
 ## rd
 
-- type: entity
-  abstract: true
-  parent: BaseTraitorStealObjective
-  id: BaseRDObjective
-  components:
-  - type: NotJobRequirement
-    job: ResearchDirector
-  - type: StealCondition
-    owner: job-name-rd
-
 - type: entity
   noSpawn: true
-  parent: BaseRDObjective
+  parent: BaseTraitorStealObjective
   id: RDHardsuitStealObjective
   components:
   - type: StealCondition
-    prototype: ClothingOuterHardsuitRd
+    stealGroup: ClothingOuterHardsuitRd
+    owner: job-name-rd
 
 - type: entity
   noSpawn: true
-  parent: BaseRDObjective
+  parent: BaseTraitorStealObjective
   id: HandTeleporterStealObjective
   components:
   - type: StealCondition
-    prototype: HandTeleporter
+    stealGroup: HandTeleporter
+    owner: job-name-rd
 
 ## hos
 
   - type: NotJobRequirement
     job: HeadOfSecurity
   - type: StealCondition
-    prototype: BookSecretDocuments
+    stealGroup: BookSecretDocuments
     owner: job-name-hos
 
 ## ce
   - type: NotJobRequirement
     job: ChiefEngineer
   - type: StealCondition
-    prototype: ClothingShoesBootsMagAdv
+    stealGroup: ClothingShoesBootsMagAdv
     owner: job-name-ce
 
 ## qm
   - type: NotJobRequirement
     job: Quartermaster
   - type: StealCondition
-    prototype: BoxFolderQmClipboard
+    stealGroup: BoxFolderQmClipboard
     owner: job-name-qm
 
 ## hop
   - type: NotJobRequirement
     job: HeadOfPersonnel
   - type: StealCondition
-    prototype: FoodMeatCorgi
+    stealGroup: FoodMeatCorgi
     owner: objective-condition-steal-Ian
 
 ## cap
   id: CaptainIDStealObjective
   components:
   - type: StealCondition
-    prototype: CaptainIDCard
+    stealGroup: CaptainIDCard
 
 - type: entity
   noSpawn: true
   id: CaptainJetpackStealObjective
   components:
   - type: StealCondition
-    prototype: JetpackCaptainFilled
+    stealGroup: JetpackCaptainFilled
 
 - type: entity
   noSpawn: true
   id: CaptainGunStealObjective
   components:
   - type: StealCondition
-    prototype: WeaponAntiqueLaser
+    stealGroup: WeaponAntiqueLaser
     owner: job-name-captain
 
 - type: entity
     difficulty: 4
   - type: NotCommandRequirement
   - type: StealCondition
-    prototype: NukeDisk
+    stealGroup: NukeDisk
     owner: objective-condition-steal-station
diff --git a/Resources/Prototypes/Roles/Antags/Thief.yml b/Resources/Prototypes/Roles/Antags/Thief.yml
new file mode 100644 (file)
index 0000000..72ea68f
--- /dev/null
@@ -0,0 +1,6 @@
+- type: antag
+  id: Thief
+  name: roles-antag-thief-name
+  antagonist: true
+  setPreference: true
+  objective: roles-antag-space-ninja-objective
\ No newline at end of file
index d827f509cf671a9ad24a0685524e8f1bae89772b..796fb8c2aa4661eb7c466bf54f74e992ffd73b16 100644 (file)
         {
             "name": "pinpointer-syndicate"
         },
+        {
+            "name": "pinpointer_thief"
+        },
         {
             "name": "pinpointer-way"
         },
diff --git a/Resources/Textures/Objects/Devices/pinpointer.rsi/pinpointer_thief.png b/Resources/Textures/Objects/Devices/pinpointer.rsi/pinpointer_thief.png
new file mode 100644 (file)
index 0000000..745d26a
Binary files /dev/null and b/Resources/Textures/Objects/Devices/pinpointer.rsi/pinpointer_thief.png differ
diff --git a/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/icon.png b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/icon.png
new file mode 100644 (file)
index 0000000..28c3121
Binary files /dev/null and b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..bba3c79
Binary files /dev/null and b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..574d59a
Binary files /dev/null and b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/meta.json b/Resources/Textures/Objects/Tools/Toolboxes/toolbox_thief.rsi/meta.json
new file mode 100644 (file)
index 0000000..068495f
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "version": 1,
+  "license": "CC0-1.0",
+  "copyright": "Created by TheShuEd(github) for ss14",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "icon"
+    }
+  ]
+}
diff --git a/Resources/Textures/Objects/Vehicles/secway.rsi/icon.png b/Resources/Textures/Objects/Vehicles/secway.rsi/icon.png
new file mode 100644 (file)
index 0000000..cae639b
Binary files /dev/null and b/Resources/Textures/Objects/Vehicles/secway.rsi/icon.png differ
index 853fbde3d9fad81ccc6adad29cecbec0f9aedec7..27d17beb56eeb1a6a1f5614d553d612ef551f76d 100644 (file)
@@ -35,6 +35,9 @@
     },
     {
       "name": "keys"
+    },
+    {
+      "name": "icon"
     }
   ]
 }