* Add DamageType And DamageGroup Prototypes * Remove DamageTypePrototype Field "name" as its redundant * Change I/DamageableComponent to use prototypes * Update DamageContainer, ReisistanceSet and DamageChangeData * Change Barotrauma Component to use DamageType from DamageSystem * Update AsteroidRockComponent * update some more components * update some more components * Fix m o r e c o m p o n e n t s and their damageType * all thats left is bug/missing node hunting then verification. * push changes * update submodule * Merge fixes * push DGP for example * update damagecomponent across shared and server * fix a few bugs * Fix Merge issues * Refactor damageablecomponent update (#4406) * Fixing merge. I messed up part of the merge. this should fix it? * Barotrauma now uses prototypeManager As System.Runtime.CompilerServices also has a [Dependency], I think I had to use the full path [Robust.Shared.IoC.Dependency] * FlammableComponent now uses prototypeManager * SuicideCommands now use prototypeManager * Changed many files to use prototypeManager to resolve damaege prototypes Yeah.... prototype references would be very nice. maybe this was all a waste of time. * Grouping prototypeManager.Index with datafield definitions This will make it easier to eventually add prototype references * removed unused variable * Moved lines around. Lines now consistent with other TODO PROTOTYPE blocks * Grouping more prototypeManager.Index with datafield definitions * Removed unnecessary code * Added more prototypeManager indexing These ones weren't pointed out by DrSmug. But I think this is all of them? That or my regex is shit. * Remove redundant _damage field * Remove redundant _currentTemperature * Moved variables down * Added prototypeManager indexing to TemperatureComponent * WeaponComponent/System now use ProtptypeManager And as far as I can tell damageType is required, and therefore should never have been null anyway? * Make ranged weapon clumsy fire effects datafields And yes, the order in which the clumsy effects occur is very important. * Made damage on vital body part loss a datafield * Renamed several damageGroup variables to group * Capitalised DamageListToDamageGroup * Make radiation and explosion damage types datafields * Renamed _supportedDamageGroupIDs and _supportedDamageTypeIDs * Fixed mistakes Frogot to remove prototypeManager index DamageTypeTrigger, and wrong variable visibility in TemperatureComponent * Added necessary code Is something tragically wrong? * MeleeWeapon damageType is not actually required * Fixing someone else's mistakes A search comes up with nothing in the yaml files, and its not a required field. So no one uses it? Hopefully? * Changed and renamed damageTypeToDamageGroup Previously would incorrectly return the total container damage for each group, not the total in the group * renaming varitables * Renamed variable DamageClasses * Added dictionary converting functions * Added ID-keyed dictionaries * Making MedicalScanner use ID dictionaries, instead of prototype dictionaries Oh oh no. I've been able to avoid UI & networking up until now. I have no Idea what I am doing. * Fix Medical Scanner * Summary (required) The joke here is that this fixes the empty summary. * Removed DamageableComponent.GetDamageGroup/Type * Renamed "damage classes" to groups. * Update ChangeDamage description * Replaced Heal() with SettAllDamage() Heal() was just a confusing name, * More Class -> Group renaming * Replace Class with Group in yaml files DamageClassTrigger does not appear in any yaml? only in testing? DamageTypeTrigger appears only in human.yaml? HealthChangeMetabolism is Mostly in medicine.yml and one in soad.yaml Why the hell is Cola metabolizable by plants? Who is pouring cola on their plants!?!? * Fix _prototypeManager being null errors. * Changing comments Where are the prototype references * MetabolismComponent doesn't give free heals anymore. * Changes HungerComponent healing. Previously I think it would actually damage you. Only did this as I though it was causing the fast healing. Turns out that was just BREATHING. * Generalised a function in DamageableComponent and moved it to DamageGroupPrototype previously DamageTypesDictToDamageGroupDict was private to DamageableComponent, but was also quite general (nearly a static function). As this sort of function may be needed by other components using DamageGroupPrototypes in the future, I moved it there as a static function instead. * modified DamageableComponent.ChangeDamage() ignoreResistances was renamed to ignoreDamageResistances to make it clearer that it had no effect on healing. Now uses default argument for ignoreDamageResistances, so when healing you are not forced to specify an argument that does nothing. Also made some general changes to ignoreResistances() * Changed class->group and added missing damage type functionality to DamageContainerPrototypes * Added Comments to damage.yml * Misc Changes to DamageableComponent * Differentiated between group support and group applicability So far, every damage type is a member of one, and only one, damage group. So this change has no real effect so far. * Added proposed alternative to ChangeDamage() * fixed error in DamageGroupPrototype * Changes to DamageableComponent Lots of changes to comments. Some variables renamed in IDamageableComponent and DamageableComponent (also required renaming in other files) Some minor logic changes, mostly for incorrect descirptions of boolean return values. Also further differentiating between ApplicableGroups and SupportedGroups... if that will ever even matter * Generalised MedicalScannerComponent If needed, can print miscellaneous damage types now * Fixed HealthChangeMetabolism bug * Changing Comments around * More questions * Made Barotrauma default to blunt * Fix RejuvenateTest.cs * Comments * Coments and variable names * fix some master-merge issues * Removed redundant fields * Misc changes for readbility of PR diff * Consistent naming * Fixed atmos damage bug * Removed Ranting * Fixed Hunger after I broke it * Fixing Bugs * Removed stupid question * Removed more stupid questions * Fix potential null errors. * Made boolean return values consistent Also renamed several functions, to make it clear they return a bool. Docs were also updated. * Removed IoCManager.InjectDependencies() * Removed unnecessary 'suffocation' prefix * Fixed Spelling Also removed accidentally left in logger call * Fixed Medical Scanner * Apply suggestions from code review Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Changing comments and whitespaces * Made damage thresholds trigger datafields required * So many typos * Changes to DamageableComponents Changed documentation in IDamageableComponent Made testing code more readable. Relabelled groups as 'Applicable' either 'Fully Supported' * Removed function and degeneralised * Update DamageableComponent.cs Removed unused parameters Fixed Networking * Added IoCManager.Resolve * Now using alternative TryChangeDamage() * Removed function from DamageGroupPrototype * Removing comments * Remove bad if statement? * Fix damageChanged ordering * Fix hurt server command * Changed //TODO PROTOTYPE blocks Now use PrototypeManager differently. Wherever possible, only retrieve the prototype once. Also added default damage types to some more datafields * Update Content.Shared/Damage/Container/DamageContainerPrototype.cs Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * renamed _accumulatedHealth -> _accumulatedDamage and added TODOs * Another class-> group * Fix bug in generalisation of damage container prototypes * Addes Tests to make sure I dont keep adding bugs to my own code. * Changed Return values when setting * Removed unused class * Added more tests, split tests into three files * Made damage types public and VV read-write-able * Minor changes to DamageableComponent Replaced internal use of GetDamagePerType with _damageDict and removed some unnecessary fields * Fix Suicide, by adding IoC Resolve() * Fix DamageGroupTrigger bug * Fix typos in tests * Change comments./docstrings & spacing * Merge tests, use test prototypes Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Fix merge issues Co-authored-by: Silver <Silvertorch5@gmail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com>
357 lines
12 KiB
C#
357 lines
12 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using Content.Server.Ghost;
|
|
using Content.Server.Hands.Components;
|
|
using Content.Server.Items;
|
|
using Content.Server.MachineLinking.Components;
|
|
using Content.Server.MachineLinking.Signals;
|
|
using Content.Server.Power.Components;
|
|
using Content.Server.Temperature.Components;
|
|
using Content.Shared.Actions.Behaviors;
|
|
using Content.Shared.Damage;
|
|
using Content.Shared.Damage.Components;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Light;
|
|
using Content.Shared.Notification;
|
|
using Content.Shared.Notification.Managers;
|
|
using Content.Shared.Sound;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Localization;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Content.Server.Light.Components
|
|
{
|
|
/// <summary>
|
|
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
|
|
/// </summary>
|
|
[RegisterComponent]
|
|
public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver<bool>, ISignalReceiver<ToggleSignal>, IGhostBooAffected
|
|
{
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
|
|
public override string Name => "PoweredLight";
|
|
|
|
private static readonly TimeSpan _thunkDelay = TimeSpan.FromSeconds(2);
|
|
// time to blink light when ghost made boo nearby
|
|
private static readonly TimeSpan ghostBlinkingTime = TimeSpan.FromSeconds(10);
|
|
private static readonly TimeSpan ghostBlinkingCooldown = TimeSpan.FromSeconds(60);
|
|
|
|
[ComponentDependency]
|
|
private readonly AppearanceComponent? _appearance;
|
|
|
|
private TimeSpan _lastThunk;
|
|
private TimeSpan? _lastGhostBlink;
|
|
|
|
[DataField("burnHandSound")]
|
|
private SoundSpecifier _burnHandSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
|
|
|
|
[DataField("turnOnSound")]
|
|
private SoundSpecifier _turnOnSound = new SoundPathSpecifier("/Audio/Machines/light_tube_on.ogg");
|
|
|
|
[DataField("hasLampOnSpawn")]
|
|
private bool _hasLampOnSpawn = true;
|
|
|
|
[ViewVariables]
|
|
[DataField("on")]
|
|
private bool _on = true;
|
|
|
|
[ViewVariables]
|
|
private bool _currentLit;
|
|
|
|
[ViewVariables]
|
|
private bool _isBlinking;
|
|
|
|
[ViewVariables]
|
|
[DataField("ignoreGhostsBoo")]
|
|
private bool _ignoreGhostsBoo;
|
|
|
|
[DataField("bulb")] private LightBulbType _bulbType = LightBulbType.Tube;
|
|
public LightBulbType BulbType => _bulbType;
|
|
|
|
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
|
|
|
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
[DataField("damageType")]
|
|
private readonly string _damageTypeID = "Heat";
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public DamageTypePrototype DamageType = default!;
|
|
|
|
protected override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
|
}
|
|
|
|
[ViewVariables]
|
|
public LightBulbComponent? LightBulb
|
|
{
|
|
get
|
|
{
|
|
if (_lightBulbContainer.ContainedEntity == null) return null;
|
|
|
|
_lightBulbContainer.ContainedEntity.TryGetComponent(out LightBulbComponent? bulb);
|
|
|
|
return bulb;
|
|
}
|
|
}
|
|
|
|
// TODO CONSTRUCTION make this use a construction graph
|
|
|
|
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
{
|
|
return InsertBulb(eventArgs.Using);
|
|
}
|
|
|
|
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
|
{
|
|
if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent))
|
|
{
|
|
Eject();
|
|
return false;
|
|
}
|
|
if (eventArgs.User.TryGetComponent(out HeatResistanceComponent? heatResistanceComponent))
|
|
{
|
|
if (CanBurn(heatResistanceComponent.GetHeatResistance()))
|
|
{
|
|
Burn();
|
|
return true;
|
|
}
|
|
}
|
|
Eject();
|
|
return true;
|
|
|
|
bool CanBurn(int heatResistance)
|
|
{
|
|
if (LightBulb == null)
|
|
return false;
|
|
|
|
return _currentLit && heatResistance < LightBulb.BurningTemperature;
|
|
}
|
|
|
|
void Burn()
|
|
{
|
|
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
|
damageableComponent.TryChangeDamage(DamageType, 20);
|
|
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
|
}
|
|
|
|
void Eject()
|
|
{
|
|
EjectBulb(eventArgs.User);
|
|
UpdateLight();
|
|
}
|
|
}
|
|
|
|
/// <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>
|
|
/// <returns>True if it could insert it, false if it couldn't.</returns>
|
|
private bool InsertBulb(IEntity bulb)
|
|
{
|
|
if (LightBulb != null) return false;
|
|
if (!bulb.TryGetComponent(out LightBulbComponent? lightBulb)) return false;
|
|
if (lightBulb.Type != _bulbType) return false;
|
|
|
|
var inserted = _lightBulbContainer.Insert(bulb);
|
|
|
|
lightBulb.OnLightBulbStateChange += UpdateLight;
|
|
lightBulb.OnLightColorChange += UpdateLight;
|
|
|
|
UpdateLight();
|
|
|
|
return inserted;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ejects the bulb to a mob's hand if possible.
|
|
/// </summary>
|
|
private void EjectBulb(IEntity? user = null)
|
|
{
|
|
if (LightBulb == null) return;
|
|
|
|
var bulb = LightBulb;
|
|
|
|
bulb.OnLightBulbStateChange -= UpdateLight;
|
|
bulb.OnLightColorChange -= UpdateLight;
|
|
|
|
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.
|
|
/// </summary>
|
|
public void UpdateLight(object? sender, EventArgs? e)
|
|
{
|
|
UpdateLight();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the light's power drain, sprite and actual light state.
|
|
/// </summary>
|
|
public void UpdateLight()
|
|
{
|
|
var powerReceiver = Owner.GetComponent<ApcPowerReceiverComponent>();
|
|
|
|
if (LightBulb == null) // No light bulb.
|
|
{
|
|
_currentLit = false;
|
|
powerReceiver.Load = 0;
|
|
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Empty);
|
|
return;
|
|
}
|
|
|
|
switch (LightBulb.State)
|
|
{
|
|
case LightBulbState.Normal:
|
|
if (powerReceiver.Powered && _on)
|
|
{
|
|
_currentLit = true;
|
|
powerReceiver.Load = LightBulb.PowerUse;
|
|
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.On);
|
|
_appearance?.SetData(PoweredLightVisuals.BulbColor, LightBulb.Color);
|
|
var time = _gameTiming.CurTime;
|
|
if (time > _lastThunk + _thunkDelay)
|
|
{
|
|
_lastThunk = time;
|
|
SoundSystem.Play(Filter.Pvs(Owner), _turnOnSound.GetSound(), Owner, AudioParams.Default.WithVolume(-10f));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_currentLit = false;
|
|
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Off);
|
|
}
|
|
break;
|
|
case LightBulbState.Broken:
|
|
_currentLit = false;
|
|
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Broken);
|
|
break;
|
|
case LightBulbState.Burned:
|
|
_currentLit = false;
|
|
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Burned);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
{
|
|
base.HandleMessage(message, component);
|
|
switch (message)
|
|
{
|
|
case PowerChangedMessage:
|
|
UpdateLight();
|
|
break;
|
|
case DamageChangedMessage msg:
|
|
TryDestroyBulb(msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void TryDestroyBulb(DamageChangedMessage msg)
|
|
{
|
|
if (!msg.TookDamage)
|
|
return;
|
|
|
|
if (LightBulb == null || LightBulb.State == LightBulbState.Broken)
|
|
return;
|
|
|
|
LightBulb.State = LightBulbState.Broken;
|
|
LightBulb.PlayBreakSound();
|
|
UpdateLight();
|
|
}
|
|
|
|
void IMapInit.MapInit()
|
|
{
|
|
if (_hasLampOnSpawn)
|
|
{
|
|
var prototype = _bulbType switch
|
|
{
|
|
LightBulbType.Bulb => "LightBulb",
|
|
LightBulbType.Tube => "LightTube",
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates);
|
|
_lightBulbContainer.Insert(entity);
|
|
}
|
|
|
|
// need this to update visualizers
|
|
UpdateLight();
|
|
}
|
|
|
|
public void TriggerSignal(bool signal)
|
|
{
|
|
_on = signal;
|
|
UpdateLight();
|
|
}
|
|
|
|
public void TriggerSignal(ToggleSignal signal)
|
|
{
|
|
_on = !_on;
|
|
UpdateLight();
|
|
}
|
|
|
|
public void ToggleBlinkingLight(bool isNowBlinking)
|
|
{
|
|
if (_isBlinking == isNowBlinking)
|
|
return;
|
|
|
|
_isBlinking = isNowBlinking;
|
|
_appearance?.SetData(PoweredLightVisuals.Blinking, _isBlinking);
|
|
}
|
|
|
|
public bool AffectedByGhostBoo(InstantActionEventArgs args)
|
|
{
|
|
if (_ignoreGhostsBoo)
|
|
return false;
|
|
|
|
// check cooldown first to prevent abuse
|
|
var time = _gameTiming.CurTime;
|
|
if (_lastGhostBlink != null)
|
|
{
|
|
if (time <= _lastGhostBlink + ghostBlinkingCooldown)
|
|
return false;
|
|
}
|
|
_lastGhostBlink = time;
|
|
|
|
ToggleBlinkingLight(true);
|
|
Owner.SpawnTimer(ghostBlinkingTime, () =>
|
|
{
|
|
ToggleBlinkingLight(false);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|