Light replacer (#3701)

* Add graphics

* Barebone light replacer

* Now light replacer stores bulbs

* More interactions

* Added text messages

* Better light refil

* Add light replacer to locker and lathe

* Added sound

* Fixed ignore error

* Better inhand

* You don't like this commas?

* Fixed typo

* Deferring spawning

* Moved interaction logic to system

Co-authored-by: Alex Evgrashin <evgrashin.adl@gmail.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
Alex Evgrashin
2021-04-08 16:28:06 +04:00
committed by GitHub
parent c7482140e1
commit cfe427eb8c
14 changed files with 335 additions and 9 deletions

View File

@@ -233,6 +233,7 @@ namespace Content.Client
"FoamSolutionAreaEffect",
"GasFilter",
"Recyclable",
"LightReplacer",
"SecretStash",
"Toilet",
"ClusterFlash",

View File

@@ -0,0 +1,167 @@
#nullable enable
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.ViewVariables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Content.Server.GameObjects.Components.Janitorial
{
/// <summary>
/// Device that allows user to quikly change bulbs in <see cref="PoweredLightComponent"/>
/// Can be reloaded by new light tubes or light bulbs
/// </summary>
[RegisterComponent]
public class LightReplacerComponent : Component
{
public override string Name => "LightReplacer";
public override uint? NetID => ContentNetIDs.LIGHT_REPLACER;
[DataField("sound")] private string _sound = "/Audio/Weapons/click.ogg";
// bulbs that were inside light replacer when it spawned
[DataField("contents")] private List<LightReplacerEntity> _contents = new();
// bulbs that were inserted inside light replacer
[ViewVariables] private IContainer _insertedBulbs = default!;
public override void Initialize()
{
base.Initialize();
_insertedBulbs = ContainerHelpers.EnsureContainer<Container>(Owner, "light_replacer_storage");
}
public bool TryReplaceBulb(PoweredLightComponent fixture, IEntity? user = null)
{
// check if light bulb is broken or missing
if (fixture.LightBulb != null && fixture.LightBulb.State == LightBulbState.Normal) return false;
// try get first inserted bulb of the same type as targeted light fixtutre
var bulb = _insertedBulbs.ContainedEntities.FirstOrDefault(
(e) => e.GetComponentOrNull<LightBulbComponent>()?.Type == fixture.BulbType);
// found bulb in inserted storage
if (bulb != null)
{
// try to remove it
var hasRemoved = _insertedBulbs.Remove(bulb);
if (!hasRemoved)
return false;
}
// try to create new instance of bulb from LightReplacerEntity
else
{
var bulbEnt = _contents.FirstOrDefault((e) => e.Type == fixture.BulbType && e.Amount > 0);
// found right bulb, let's spawn it
if (bulbEnt != null)
{
bulb = Owner.EntityManager.SpawnEntity(bulbEnt.PrototypeName, Owner.Transform.Coordinates);
bulbEnt.Amount--;
}
// not found any light bulbs
else
{
if (user != null)
{
var msg = Loc.GetString("comp-light-replacer-missing-light", ("light-replacer", Owner));
user.PopupMessage(msg);
}
return false;
}
}
// insert it into fixture
var wasReplaced = fixture.ReplaceBulb(bulb);
if (wasReplaced)
{
EntitySystem.Get<AudioSystem>().Play(Filter.Broadcast(), _sound,
Owner, AudioParams.Default.WithVolume(-4f));
}
return wasReplaced;
}
public bool TryInsertBulb(LightBulbComponent bulb, IEntity? user = null, bool showTooltip = false)
{
// only normal lights can be inserted inside light replacer
if (bulb.State != LightBulbState.Normal)
{
if (showTooltip && user != null)
{
var msg = Loc.GetString("comp-light-replacer-insert-broken-light");
user.PopupMessage(msg);
}
return false;
}
// try insert light and show message
var hasInsert = _insertedBulbs.Insert(bulb.Owner);
if (hasInsert && showTooltip && user != null)
{
var msg = Loc.GetString("comp-light-replacer-insert-light",
("light-replacer", Owner), ("bulb", bulb.Owner));
user.PopupMessage(msg);
}
return hasInsert;
}
public bool TryInsertBulb(ServerStorageComponent storage, IEntity? user = null)
{
if (storage.StoredEntities == null)
return false;
var insertedBulbs = 0;
var storagedEnts = storage.StoredEntities.ToArray();
foreach (var ent in storagedEnts)
{
if (ent.TryGetComponent(out LightBulbComponent? bulb))
{
if (TryInsertBulb(bulb))
insertedBulbs++;
}
}
// show some message if success
if (insertedBulbs > 0 && user != null)
{
var msg = Loc.GetString("comp-light-replacer-refill-from-storage", ("light-replacer", Owner));
user.PopupMessage(msg);
}
return insertedBulbs > 0;
}
[Serializable]
[DataDefinition]
public class LightReplacerEntity
{
[DataField("name", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? PrototypeName;
[DataField("amount")]
public int Amount;
[DataField("type")]
public LightBulbType Type;
}
}
}

View File

@@ -62,12 +62,13 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
[ViewVariables] [DataField("ignoreGhostsBoo")]
private bool _ignoreGhostsBoo;
[DataField("bulb")]
private LightBulbType BulbType = LightBulbType.Tube;
[DataField("bulb")] private LightBulbType _bulbType = LightBulbType.Tube;
public LightBulbType BulbType => _bulbType;
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
[ViewVariables]
private LightBulbComponent? LightBulb
public LightBulbComponent? LightBulb
{
get
{
@@ -126,6 +127,15 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
}
}
/// <summary>
/// Try to replace current bulb with a new one
/// </summary>
public bool ReplaceBulb(IEntity bulb)
{
EjectBulb();
return InsertBulb(bulb);
}
/// <summary>
/// Inserts the bulb if possible.
/// </summary>
@@ -134,7 +144,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
{
if (LightBulb != null) return false;
if (!bulb.TryGetComponent(out LightBulbComponent? lightBulb)) return false;
if (lightBulb.Type != BulbType) return false;
if (lightBulb.Type != _bulbType) return false;
var inserted = _lightBulbContainer.Insert(bulb);
@@ -149,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
/// <summary>
/// Ejects the bulb to a mob's hand if possible.
/// </summary>
private void EjectBulb(IEntity user)
private void EjectBulb(IEntity? user = null)
{
if (LightBulb == null) return;
@@ -160,10 +170,18 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
if (!_lightBulbContainer.Remove(bulb.Owner)) return;
if (user != null)
{
if (!user.TryGetComponent(out HandsComponent? hands)
|| !hands.PutInHand(bulb.Owner.GetComponent<ItemComponent>()))
bulb.Owner.Transform.Coordinates = user.Transform.Coordinates;
}
else
{
bulb.Owner.Transform.Coordinates = Owner.Transform.Coordinates;
}
}
/// <summary>
/// For attaching UpdateLight() to events.
@@ -259,7 +277,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
{
if (_hasLampOnSpawn)
{
var prototype = BulbType switch
var prototype = _bulbType switch
{
LightBulbType.Bulb => "LightBulb",
LightBulbType.Tube => "LightTube",

View File

@@ -0,0 +1,66 @@
#nullable enable
using System;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Janitorial;
using Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Interfaces.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems.Janitorial
{
[UsedImplicitly]
public class LightReplacerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightReplacerComponent, InteractUsingMessage>(HandleInteract);
SubscribeLocalEvent<LightReplacerComponent, AfterInteractMessage>(HandleAfterInteract);
}
public override void Shutdown()
{
base.Shutdown();
UnsubscribeLocalEvent<LightReplacerComponent, InteractUsingMessage>(HandleInteract);
UnsubscribeLocalEvent<LightReplacerComponent, AfterInteractMessage>(HandleAfterInteract);
}
private void HandleAfterInteract(EntityUid uid, LightReplacerComponent component, AfterInteractMessage eventArgs)
{
// standard interaction checks
if (!ActionBlockerSystem.CanUse(eventArgs.User)) return;
if (!eventArgs.CanReach) return;
// behaviour will depends on target type
if (eventArgs.Attacked != null)
{
// replace broken light in fixture?
if (eventArgs.Attacked.TryGetComponent(out PoweredLightComponent? fixture))
component.TryReplaceBulb(fixture, eventArgs.User);
// add new bulb to light replacer container?
else if (eventArgs.Attacked.TryGetComponent(out LightBulbComponent? bulb))
component.TryInsertBulb(bulb, eventArgs.User, true);
}
}
private void HandleInteract(EntityUid uid, LightReplacerComponent component, InteractUsingMessage eventArgs)
{
// standard interaction checks
if (!ActionBlockerSystem.CanInteract(eventArgs.User)) return;
if (eventArgs.ItemInHand != null)
{
// want to insert a new light bulb?
if (eventArgs.ItemInHand.TryGetComponent(out LightBulbComponent? bulb))
component.TryInsertBulb(bulb, eventArgs.User, true);
// add bulbs from storage?
else if (eventArgs.ItemInHand.TryGetComponent(out ServerStorageComponent? storage))
component.TryInsertBulb(storage, eventArgs.User);
}
}
}
}

View File

@@ -97,6 +97,7 @@ namespace Content.Shared.GameObjects
public const uint DOOR = 1087;
public const uint SPAWN_AFTER_INTERACT = 1088;
public const uint DISASSEMBLE_ON_ACTIVATE = 1089;
public const uint LIGHT_REPLACER = 1090;
// Net IDs for integration tests.
public const uint PREDICTION_TEST = 10001;

View File

@@ -0,0 +1,14 @@
### Interaction Messages
# Shown when player tries to replace light, but there is no lighs left
comp-light-replacer-missing-light = No lights left in {$light-replacer}.
# Shown when player inserts light bulb inside light replacer
comp-light-replacer-insert-light = You insert {$bulb} into {$light-replacer}.
# Shown when player tries to insert in light replacer brolen light bulb
comp-light-replacer-insert-broken-light = You can't insert broken lights!
# Shown when player refill light from light box
comp-light-replacer-refill-from-storage = You refill {$light-replacer}.

View File

@@ -47,6 +47,10 @@
amount: 3
- name: TrashBag
amount: 2
- name: LightReplacer
amount: 1
- name: BoxLightMixed
amount: 1
#- type: entity
# id: LockerLegalFilled

View File

@@ -266,3 +266,24 @@
quickInsert: true
areaInsert: true
storageSoundCollection: trashBagRustle
- type: entity
parent: BaseItem
name: light replacer
id: LightReplacer
description: An item which uses magnets to easily replace broken lights.
components:
- type: Sprite
sprite: Objects/Specific/Janitorial/light_replacer.rsi
state: icon
- type: Item
sprite: Objects/Specific/Janitorial/light_replacer.rsi
- type: LightReplacer
contents:
- name: LightTube
amount: 8
type: Tube
- name: LightBulb
amount: 5
type: Bulb

View File

@@ -46,3 +46,12 @@
completetime: 300
materials:
plastic: 100
- type: latheRecipe
id: LightReplacer
icon: Objects/Specific/Janitorial/light_replacer.rsi
result: LightReplacer
completetime: 600
materials:
steel: 100
glass: 1000

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

View File

@@ -0,0 +1,25 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris/commit/d76180f48949870dd57c7274d494175b3b3515ba",
"states": [
{
"name": "icon"
},
{
"name": "emagged"
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
}
]
}