23
Content.Server/Objectives/Components/StealAreaComponent.cs
Normal file
23
Content.Server/Objectives/Components/StealAreaComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Server.Objectives.Systems;
|
||||||
|
using Content.Server.Thief.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.Objectives.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(StealConditionSystem), typeof(ThiefBeaconSystem))]
|
||||||
|
public sealed partial class StealAreaComponent : Component
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public float Range = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// all the minds that will be credited with stealing from this area.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public HashSet<EntityUid> Owners = new();
|
||||||
|
}
|
||||||
@@ -22,11 +22,18 @@ public sealed partial class StealConditionComponent : Component
|
|||||||
[DataField]
|
[DataField]
|
||||||
public bool VerifyMapExistence = true;
|
public bool VerifyMapExistence = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, counts objects that are close to steal areas.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool CheckStealAreas = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the target may be alive but has died, it will not be counted
|
/// If the target may be alive but has died, it will not be counted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool CheckAlive = false;
|
public bool CheckAlive = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum number of items you need to steal to fulfill a objective
|
/// The minimum number of items you need to steal to fulfill a objective
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -21,16 +21,15 @@ public sealed class StealConditionSystem : EntitySystem
|
|||||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
|
||||||
private EntityQuery<ContainerManagerComponent> _containerQuery;
|
private EntityQuery<ContainerManagerComponent> _containerQuery;
|
||||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
_containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
_containerQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<StealConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
|
SubscribeLocalEvent<StealConditionComponent, ObjectiveAssignedEvent>(OnAssigned);
|
||||||
SubscribeLocalEvent<StealConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
SubscribeLocalEvent<StealConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
|
||||||
@@ -96,25 +95,33 @@ public sealed class StealConditionSystem : EntitySystem
|
|||||||
if (!_containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
|
if (!_containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var stack = new Stack<ContainerManagerComponent>();
|
var containerStack = new Stack<ContainerManagerComponent>();
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
|
||||||
|
//check stealAreas
|
||||||
|
if (condition.CheckStealAreas)
|
||||||
|
{
|
||||||
|
var areasQuery = AllEntityQuery<StealAreaComponent>();
|
||||||
|
while (areasQuery.MoveNext(out var uid, out var area))
|
||||||
|
{
|
||||||
|
if (!area.Owners.Contains(mind.Owner))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var nearestEnt = _lookup.GetEntitiesInRange(uid, area.Range);
|
||||||
|
foreach (var ent in nearestEnt)
|
||||||
|
{
|
||||||
|
CheckEntity(ent, condition, ref containerStack, ref count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//check pulling object
|
//check pulling object
|
||||||
if (TryComp<PullerComponent>(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition
|
if (TryComp<PullerComponent>(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition
|
||||||
{
|
{
|
||||||
var pulledEntity = pull.Pulling;
|
var pulledEntity = pull.Pulling;
|
||||||
if (pulledEntity != null)
|
if (pulledEntity != null)
|
||||||
{
|
{
|
||||||
// check if this is the item
|
CheckEntity(pulledEntity.Value, condition, ref containerStack, ref count);
|
||||||
count += CheckStealTarget(pulledEntity.Value, condition);
|
|
||||||
|
|
||||||
//we don't check the inventories of sentient entity
|
|
||||||
if (!HasComp<MindContainerComponent>(pulledEntity))
|
|
||||||
{
|
|
||||||
// if it is a container check its contents
|
|
||||||
if (_containerQuery.TryGetComponent(pulledEntity, out var containerManager))
|
|
||||||
stack.Push(containerManager);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,16 +138,30 @@ public sealed class StealConditionSystem : EntitySystem
|
|||||||
|
|
||||||
// if it is a container check its contents
|
// 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);
|
containerStack.Push(containerManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (stack.TryPop(out currentManager));
|
} while (containerStack.TryPop(out currentManager));
|
||||||
|
|
||||||
var result = count / (float) condition.CollectionSize;
|
var result = count / (float) condition.CollectionSize;
|
||||||
result = Math.Clamp(result, 0, 1);
|
result = Math.Clamp(result, 0, 1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckEntity(EntityUid entity, StealConditionComponent condition, ref Stack<ContainerManagerComponent> containerStack, ref int counter)
|
||||||
|
{
|
||||||
|
// check if this is the item
|
||||||
|
counter += CheckStealTarget(entity, condition);
|
||||||
|
|
||||||
|
//we don't check the inventories of sentient entity
|
||||||
|
if (!TryComp<MindContainerComponent>(entity, out var pullMind))
|
||||||
|
{
|
||||||
|
// if it is a container check its contents
|
||||||
|
if (_containerQuery.TryGetComponent(entity, out var containerManager))
|
||||||
|
containerStack.Push(containerManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int CheckStealTarget(EntityUid entity, StealConditionComponent condition)
|
private int CheckStealTarget(EntityUid entity, StealConditionComponent condition)
|
||||||
{
|
{
|
||||||
// check if this is the target
|
// check if this is the target
|
||||||
|
|||||||
17
Content.Server/Thief/Components/ThiefBeaconComponent.cs
Normal file
17
Content.Server/Thief/Components/ThiefBeaconComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Server.Thief.Systems;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
|
||||||
|
namespace Content.Server.Thief.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// working together with StealAreaComponent, allows the thief to count objects near the beacon as stolen when setting up.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(ThiefBeaconSystem))]
|
||||||
|
public sealed partial class ThiefBeaconComponent : Component
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier LinkSound = new SoundPathSpecifier("/Audio/Machines/high_tech_confirm.ogg");
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier UnlinkSound = new SoundPathSpecifier("/Audio/Machines/beep.ogg");
|
||||||
|
}
|
||||||
95
Content.Server/Thief/Systems/ThiefBeaconSystem.cs
Normal file
95
Content.Server/Thief/Systems/ThiefBeaconSystem.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using Content.Server.Mind;
|
||||||
|
using Content.Server.Objectives.Components;
|
||||||
|
using Content.Server.Roles;
|
||||||
|
using Content.Server.Thief.Components;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Foldable;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.Thief.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ThiefBeaconComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ThiefBeaconSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ThiefBeaconComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
|
||||||
|
SubscribeLocalEvent<ThiefBeaconComponent, FoldedEvent>(OnFolded);
|
||||||
|
SubscribeLocalEvent<ThiefBeaconComponent, ExaminedEvent>(OnExamined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetInteractionVerbs(Entity<ThiefBeaconComponent> beacon, ref GetVerbsEvent<InteractionVerb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanAccess || !args.CanInteract || args.Hands is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (TryComp<FoldableComponent>(beacon, out var foldable) && foldable.IsFolded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mind = _mind.GetMind(args.User);
|
||||||
|
if (!HasComp<ThiefRoleComponent>(mind))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var user = args.User;
|
||||||
|
args.Verbs.Add(new()
|
||||||
|
{
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
SetCoordinate(beacon, mind.Value);
|
||||||
|
},
|
||||||
|
Message = Loc.GetString("thief-fulton-verb-message"),
|
||||||
|
Text = Loc.GetString("thief-fulton-verb-text"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFolded(Entity<ThiefBeaconComponent> beacon, ref FoldedEvent args)
|
||||||
|
{
|
||||||
|
if (args.IsFolded)
|
||||||
|
ClearCoordinate(beacon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamined(Entity<ThiefBeaconComponent> beacon, ref ExaminedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<StealAreaComponent>(beacon, out var area))
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.PushText(Loc.GetString(area.Owners.Count == 0
|
||||||
|
? "thief-fulton-examined-unset"
|
||||||
|
: "thief-fulton-examined-set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid mind)
|
||||||
|
{
|
||||||
|
if (!TryComp<StealAreaComponent>(beacon, out var area))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_audio.PlayPvs(beacon.Comp.LinkSound, beacon);
|
||||||
|
_popup.PopupEntity(Loc.GetString("thief-fulton-set"), beacon);
|
||||||
|
area.Owners.Clear(); //We only reconfigure the beacon for ourselves, we don't need multiple thieves to steal from the same beacon.
|
||||||
|
area.Owners.Add(mind);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCoordinate(Entity<ThiefBeaconComponent> beacon)
|
||||||
|
{
|
||||||
|
if (!TryComp<StealAreaComponent>(beacon, out var area))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (area.Owners.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_audio.PlayPvs(beacon.Comp.UnlinkSound, beacon);
|
||||||
|
_popup.PopupEntity(Loc.GetString("thief-fulton-clear"), beacon);
|
||||||
|
area.Owners.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Resources/Locale/en-US/thief/beacon.ftl
Normal file
8
Resources/Locale/en-US/thief/beacon.ftl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
thief-fulton-set = Delivery coordinates are set.
|
||||||
|
thief-fulton-clear = Delivery coordinates cleared.
|
||||||
|
|
||||||
|
thief-fulton-examined-set = Coordinates entered. Bluespace teleportation of the nearest objects will be performed when the evacuation shuttle departs.
|
||||||
|
thief-fulton-examined-unset = Beacon coordinates are not set.
|
||||||
|
|
||||||
|
thief-fulton-verb-text = Set coordinates
|
||||||
|
thief-fulton-verb-message = Set the coordinates of your thief's hideout, where all nearby items will be sent at the end of the round.
|
||||||
38
Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml
Normal file
38
Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
- type: entity
|
||||||
|
id: ThiefBeacon
|
||||||
|
name: thieving beacon
|
||||||
|
description: A device that will teleport everything around it to the thief's vault at the end of the shift.
|
||||||
|
components:
|
||||||
|
- type: ThiefBeacon
|
||||||
|
- type: StealArea
|
||||||
|
- type: Item
|
||||||
|
size: Normal
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Dynamic
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.25,-0.4,0.25,0.1"
|
||||||
|
density: 20
|
||||||
|
mask:
|
||||||
|
- Impassable
|
||||||
|
- type: Foldable
|
||||||
|
folded: true
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Appearance
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.FoldedVisuals.State:
|
||||||
|
foldedLayer:
|
||||||
|
True: { state: folded_extraction }
|
||||||
|
False: { state: extraction_point }
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Tools/thief_beacon.rsi
|
||||||
|
drawdepth: SmallObjects
|
||||||
|
noRot: true
|
||||||
|
layers:
|
||||||
|
- state: extraction_point
|
||||||
|
map: [ "foldedLayer" ]
|
||||||
@@ -52,8 +52,8 @@
|
|||||||
- type: weightedRandom
|
- type: weightedRandom
|
||||||
id: ThiefBigObjectiveGroups
|
id: ThiefBigObjectiveGroups
|
||||||
weights:
|
weights:
|
||||||
ThiefObjectiveGroupStructure: 0 #Temporarily disabled until obvious ways to steal structures are added
|
ThiefObjectiveGroupStructure: 1
|
||||||
ThiefObjectiveGroupAnimal: 2
|
ThiefObjectiveGroupAnimal: 1
|
||||||
|
|
||||||
- type: weightedRandom
|
- type: weightedRandom
|
||||||
id: ThiefObjectiveGroupCollection
|
id: ThiefObjectiveGroupCollection
|
||||||
@@ -91,7 +91,6 @@
|
|||||||
weights:
|
weights:
|
||||||
NuclearBombStealObjective: 0.5
|
NuclearBombStealObjective: 0.5
|
||||||
FaxMachineCaptainStealObjective: 1
|
FaxMachineCaptainStealObjective: 1
|
||||||
VehicleSecwayStealObjective: 1
|
|
||||||
ChemDispenserStealObjective: 1
|
ChemDispenserStealObjective: 1
|
||||||
XenoArtifactStealObjective: 1
|
XenoArtifactStealObjective: 1
|
||||||
FreezerHeaterStealObjective: 1
|
FreezerHeaterStealObjective: 1
|
||||||
|
|||||||
@@ -18,30 +18,29 @@
|
|||||||
- type: StealCondition
|
- type: StealCondition
|
||||||
verifyMapExistence: false
|
verifyMapExistence: false
|
||||||
descriptionText: objective-condition-thief-description
|
descriptionText: objective-condition-thief-description
|
||||||
|
checkStealAreas: true
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
parent: [BaseThiefObjective, BaseStealObjective]
|
parent: [BaseThiefObjective, BaseThiefStealObjective]
|
||||||
id: BaseThiefStealCollectionObjective
|
id: BaseThiefStealCollectionObjective
|
||||||
components:
|
components:
|
||||||
- type: StealCondition
|
- type: StealCondition
|
||||||
verifyMapExistence: true
|
verifyMapExistence: true
|
||||||
descriptionText: objective-condition-thief-description
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
parent: [BaseThiefObjective, BaseStealObjective]
|
parent: [BaseThiefObjective, BaseThiefStealObjective]
|
||||||
id: BaseThiefStealStructureObjective
|
id: BaseThiefStealStructureObjective
|
||||||
components:
|
components:
|
||||||
- type: StealCondition
|
- type: StealCondition
|
||||||
verifyMapExistence: true
|
verifyMapExistence: true
|
||||||
descriptionText: objective-condition-thief-description
|
|
||||||
- type: Objective
|
- type: Objective
|
||||||
difficulty: 2 # it's hard to hide
|
difficulty: 2 # it's hard to hide
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
parent: [BaseThiefObjective, BaseStealObjective]
|
parent: [BaseThiefObjective, BaseThiefStealObjective]
|
||||||
id: BaseThiefStealAnimalObjective
|
id: BaseThiefStealAnimalObjective
|
||||||
components:
|
components:
|
||||||
- type: StealCondition
|
- type: StealCondition
|
||||||
|
|||||||
@@ -11,3 +11,4 @@
|
|||||||
back:
|
back:
|
||||||
- ToolboxThief
|
- ToolboxThief
|
||||||
- ClothingHandsChameleonThief
|
- ClothingHandsChameleonThief
|
||||||
|
- ThiefBeacon
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 954 B |
Binary file not shown.
|
After Width: | Height: | Size: 281 B |
Binary file not shown.
|
After Width: | Height: | Size: 681 B |
32
Resources/Textures/Objects/Tools/thief_beacon.rsi/meta.json
Normal file
32
Resources/Textures/Objects/Tools/thief_beacon.rsi/meta.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/austation/austation/commit/e2a4fefd01e702f48d3d4cc8d6a2686d54d104fa and edited by TheShuEd",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "folded_extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extraction_point",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.5,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "extraction_point_light",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.5,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user