This commit is contained in:
Rane
2022-02-10 17:15:06 -05:00
committed by GitHub
parent 65faa85620
commit 81c7a03c97
22 changed files with 502 additions and 2 deletions

View File

@@ -56,6 +56,7 @@ namespace Content.Client.Entry
"Electrified",
"Electrocution",
"Paper",
"Drone",
"Bloodstream",
"TransformableContainer",
"Mind",

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using Content.Server.Act;
using Content.Server.Actions.Events;
using Content.Server.Administration.Logs;
using Content.Server.Interaction;
using Content.Server.Popups;
@@ -44,11 +45,16 @@ namespace Content.Server.Actions.Actions
[ViewVariables]
[DataField("disarmSuccessSound")]
private SoundSpecifier DisarmSuccessSound { get; } = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg");
public void DoTargetEntityAction(TargetEntityActionEventArgs args)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var disarmedActs = entMan.GetComponents<IDisarmedAct>(args.Target).ToArray();
var attemptEvent = new DisarmAttemptEvent(args.Target, args.Performer);
entMan.EventBus.RaiseLocalEvent(args.Target, attemptEvent);
if (attemptEvent.Cancelled)
return;
if (!args.Performer.InRangeUnobstructed(args.Target)) return;

View File

@@ -0,0 +1,15 @@
using Robust.Shared.GameObjects;
namespace Content.Server.Actions.Events
{
public class DisarmAttemptEvent : CancellableEntityEventArgs
{
public readonly EntityUid TargetUid;
public readonly EntityUid DisarmerUid;
public DisarmAttemptEvent(EntityUid targetUid, EntityUid disarmerUid)
{
TargetUid = targetUid;
DisarmerUid = disarmerUid;
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Content.Server.Storage;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Drone.Components
{
[RegisterComponent]
public sealed class DroneComponent : Component
{
[DataField("tools")] public List<EntitySpawnEntry> Tools = new();
public List<EntityUid> ToolUids = new();
public bool AlreadyAwoken = false;
}
}

View File

@@ -0,0 +1,132 @@
using Content.Shared.Drone;
using Content.Server.Drone.Components;
using Content.Shared.Drone.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
using Content.Shared.MobState.Components;
using Content.Shared.MobState;
using Content.Shared.DragDrop;
using Content.Shared.Examine;
using Content.Server.Popups;
using Content.Server.Mind.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Hands.Components;
using Content.Shared.Body.Components;
using Content.Server.Actions.Events;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using Content.Shared.Tag;
namespace Content.Server.Drone
{
public class DroneSystem : SharedDroneSystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DroneComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<DroneComponent, DisarmAttemptEvent>(OnDisarmAttempt);
SubscribeLocalEvent<DroneComponent, DropAttemptEvent>(OnDropAttempt);
SubscribeLocalEvent<DroneComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<DroneComponent, MindAddedMessage>(OnMindAdded);
SubscribeLocalEvent<DroneComponent, MindRemovedMessage>(OnMindRemoved);
}
private void OnExamined(EntityUid uid, DroneComponent component, ExaminedEvent args)
{
if (args.IsInDetailsRange)
{
if (TryComp<MindComponent>(uid, out var mind) && mind.HasMind)
{
args.PushMarkup(Loc.GetString("drone-active"));
}
else
{
args.PushMarkup(Loc.GetString("drone-dormant"));
}
}
}
private void OnMobStateChanged(EntityUid uid, DroneComponent drone, MobStateChangedEvent args)
{
if (args.Component.IsDead())
{
var body = Comp<SharedBodyComponent>(uid); //There's no way something can have a mobstate but not a body...
foreach (var item in drone.ToolUids)
{
EntityManager.DeleteEntity(item);
}
body.Gib();
EntityManager.DeleteEntity(uid);
}
}
private void OnDisarmAttempt(EntityUid uid, DroneComponent drone, DisarmAttemptEvent args)
{
TryComp<HandsComponent>(args.TargetUid, out var hands);
var item = hands?.GetActiveHandItem;
if (TryComp<DroneToolComponent>(item?.Owner, out var itemInHand))
{
args.Cancel();
}
}
private void OnMindAdded(EntityUid uid, DroneComponent drone, MindAddedMessage args)
{
TryComp<TagComponent>(uid, out var tagComp);
UpdateDroneAppearance(uid, DroneStatus.On);
tagComp?.AddTag("DoorBumpOpener");
_popupSystem.PopupEntity(Loc.GetString("drone-activated"), uid, Filter.Pvs(uid));
if (drone.AlreadyAwoken == false)
{
var spawnCoord = Transform(uid).Coordinates;
if (drone.Tools.Count == 0) return;
if (TryComp<HandsComponent>(uid, out var hands) && hands.Count >= drone.Tools.Count)
{
foreach (var entry in drone.Tools)
{
var item = EntityManager.SpawnEntity(entry.PrototypeId, spawnCoord);
AddComp<DroneToolComponent>(item);
hands.PutInHand(item);
drone.ToolUids.Add(item);
}
}
drone.AlreadyAwoken = true;
}
}
private void OnMindRemoved(EntityUid uid, DroneComponent drone, MindRemovedMessage args)
{
TryComp<TagComponent>(uid, out var tagComp);
UpdateDroneAppearance(uid, DroneStatus.Off);
tagComp?.RemoveTag("DoorBumpOpener");
EnsureComp<GhostTakeoverAvailableComponent>(uid);
}
private void OnDropAttempt(EntityUid uid, DroneComponent drone, DropAttemptEvent args)
{
TryComp<HandsComponent>(uid, out var hands);
var item = hands?.GetActiveHandItem;
if (TryComp<DroneToolComponent>(item?.Owner, out var itemInHand))
{
args.Cancel();
}
}
private void UpdateDroneAppearance(EntityUid uid, DroneStatus status)
{
if (TryComp<AppearanceComponent>(uid, out var appearance))
{
appearance.SetData(DroneVisuals.Status, status);
}
}
}
}

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Analyzers;
namespace Content.Shared.Drone.Components
{
[RegisterComponent]
public sealed class DroneToolComponent : Component {}
}

View File

@@ -0,0 +1,22 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.Drone
{
public abstract class SharedDroneSystem : EntitySystem
{
[Serializable, NetSerializable]
public enum DroneVisuals : byte
{
Status
}
[Serializable, NetSerializable]
public enum DroneStatus : byte
{
Off,
On
}
}
}

View File

@@ -0,0 +1,3 @@
drone-active = A maintenance drone. It seems totally unconcerned with you.
drone-dormant = A dormant maintenance drone. Who knows when it will wake up?
drone-activated = The drone whirrs to life!

View File

@@ -0,0 +1,50 @@
- type: entity
id: PartSilicon
parent: BaseItem
name: "silicon body part"
abstract: true
components:
- type: Damageable
damageContainer: Inorganic
- type: entity
id: LeftHandDrone
name: "left drone hand"
parent: PartSilicon
components:
- type: Sprite
netsync: false
sprite: Mobs/Silicon/drone.rsi
state: "l_hand"
- type: Icon
sprite: Mobs/Silicon/drone.rsi
state: "l_hand"
- type: BodyPart
partType: Hand
size: 3
compatibility: Biological ##Does this do anything? Revisit when surgery is in
symmetry: Left
- type: Tag
tags:
- Trash
- type: entity
id: RightHandDrone
name: "right drone hand"
parent: PartSilicon
components:
- type: Sprite
netsync: false
sprite: Mobs/Silicon/drone.rsi
state: "r_hand"
- type: Icon
sprite: Mobs/Silicon/drone.rsi
state: "r_hand"
- type: BodyPart
partType: Hand
size: 3
compatibility: Biological
symmetry: Right
- type: Tag
tags:
- Trash

View File

@@ -0,0 +1,10 @@
- type: bodyPreset
name: "drone"
id: DronePreset
partIDs:
hand 1: LeftHandDrone
hand 2: LeftHandDrone
hand 3: LeftHandDrone
hand 4: RightHandDrone
hand 5: RightHandDrone
hand 6: RightHandDrone

View File

@@ -0,0 +1,11 @@
- type: bodyTemplate
id: DroneTemplate
name: "drone"
centerSlot: "torso"
slots:
hand 1: hand
hand 2: hand
hand 3: hand
hand 4: hand
hand 5: hand
hand 6: hand

View File

@@ -277,6 +277,7 @@
- IndustrialEngineering
unlockedRecipes:
- ShuttleConsoleCircuitboard
- Drone
# Electromagnetic Theory Technology Tree

View File

@@ -44,3 +44,18 @@
prototypes:
- MobCorgi
- MobCorgiOld
## Player-controlled
- type: entity
name: Drone Spawner
id: SpawnMobDrone
parent: MarkerBase
components:
- type: Sprite
layers:
- state: green
- texture: Mobs/Silicon/drone.rsi/shell.png
- type: ConditionalSpawner
prototypes:
- Drone

View File

@@ -0,0 +1,135 @@
- type: entity
save: false
abstract: true
id: PlayerSiliconBase #for player controlled silicons
components:
- type: Reactive
groups:
Acidic: [Touch]
- type: Input
context: "human"
- type: MovedByPressure
- type: DamageOnHighSpeedImpact
damage:
types:
Blunt: 5
soundHit:
path: /Audio/Effects/hit_kick.ogg
- type: Clickable
- type: Damageable
damageContainer: Inorganic
- type: InteractionOutline
- type: Fixtures
fixtures:
- shape:
# Circles, cuz rotation of rectangles looks very bad
!type:PhysShapeCircle
radius: 0.35
mass: 20
mask:
- Impassable
- MobImpassable
- VaultImpassable
- SmallImpassable
layer:
- Opaque
- type: MovementSpeedModifier
baseWalkSpeed : 4
baseSprintSpeed : 3
- type: Sprite
noRot: true
drawdepth: Mobs
- type: Physics
bodyType: KinematicController
- type: Hands
- type: Body
template: DroneTemplate
preset: DronePreset
- type: DoAfter
- type: Pullable
- type: Examiner
- type: Puller
- type: Recyclable
safe: false
- type: StandingState
- type: Alerts
- type: entity
name: drone
id: Drone
parent: PlayerSiliconBase
components:
- type: Drone
tools:
- id: PowerDrill
- id: JawsOfLife
- id: WelderExperimental
- type: GhostTakeoverAvailable
makeSentient: true
name: Maintenance Drone
description: Maintain the station. Ignore organics.
rules: |
You are bound by these laws both in-game and out-of-character:
1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.
2. You may not harm any being, regardless of intent or circumstance.
3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals.
- type: MovementSpeedModifier
baseWalkSpeed : 6
baseSprintSpeed : 6
- type: MobState
thresholds:
0: !type:NormalMobState {}
70: !type:DeadMobState {}
- type: Sprite
drawdepth: Mobs
netsync: false
layers:
- state: shell
sprite: Mobs/Silicon/drone.rsi
- type: Fixtures
fixtures:
- shape:
!type:PhysShapeCircle
radius: 0.25
mass: 5
mask:
- VaultImpassable
layer:
- Opaque
- type: Tag
- type: Access
tags:
- Maintenance
- Cargo
# - Quartermaster
- Engineering
- ChiefEngineer
- Medical
- ChiefMedicalOfficer
- Research
- ResearchDirector
- Security
- Service
- Captain
- Command
- External
- HeadOfSecurity
- HeadOfPersonnel
- Bar
- Hydroponics
- Kitchen
- Janitor
- Theatre
- type: Appearance
visuals:
- type: GenericEnumVisualizer
key: enum.DroneVisuals.Status
layer: 0
states:
enum.DroneStatus.Off: shell
enum.DroneStatus.On: drone
- type: ReplacementAccent
accent: silicon
- type: Repairable
fuelcost: 15
doAfterDelay: 8

View File

@@ -160,6 +160,7 @@
- Dropper
- Syringe
- PillCanister
- Drone
- Flash
- Handcuffs
- Stunbaton

View File

@@ -71,6 +71,18 @@
materials:
Wood: 100
- type: latheRecipe
id: Drone
icon:
sprite: Mobs/Silicon/drone.rsi
state: shell
result: Drone
completetime: 1500
materials:
Steel: 500
Glass: 500
Plastic: 500
- type: latheRecipe
id: SynthesizerInstrument
icon:

View File

@@ -30,3 +30,11 @@
- Mmfph!
- Mmmf mrrfff!
- Mmmf mnnf!
- type: accent
id: silicon
words:
- Beep.
- Boop.
- Whirr.
- Beep-boop.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

View File

@@ -0,0 +1,53 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a",
"states": [
{
"name": "drone",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
]
]
},
{
"name": "shell",
"delays": [
[
1
]
]
},
{
"name": "l_hand",
"directions": 4
},
{
"name": "r_hand",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B