Eye damage (#10262)

This commit is contained in:
Rane
2022-08-14 01:59:14 -04:00
committed by GitHub
parent ef924faae7
commit 572a4f7fb3
39 changed files with 653 additions and 20 deletions

View File

@@ -1,4 +1,5 @@
using Content.Client.Verbs;
using Content.Shared.Eye.Blinding;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
@@ -116,7 +117,7 @@ namespace Content.Client.Examine
// Tooltips coming in from the server generally prioritize
// opening at the old tooltip rather than the cursor/another entity,
// since there's probably one open already if it's coming in from the server.
OpenTooltip(player.Value, ev.EntityUid, ev.CenterAtCursor, ev.OpenAtOldTooltip);
OpenTooltip(player.Value, ev.EntityUid, ev.CenterAtCursor, ev.OpenAtOldTooltip, ev.KnowTarget);
UpdateTooltipInfo(player.Value, ev.EntityUid, ev.Message, ev.Verbs);
}
@@ -131,7 +132,7 @@ namespace Content.Client.Examine
/// not fill it with information. This is done when the server sends examine info/verbs,
/// or immediately if it's entirely clientside.
/// </summary>
public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCursor=true, bool openAtOldTooltip=true)
public void OpenTooltip(EntityUid player, EntityUid target, bool centeredOnCursor=true, bool openAtOldTooltip=true, bool knowTarget = true)
{
// Close any examine tooltip that might already be opened
// Before we do that, save its position. We'll prioritize opening any new popups there if
@@ -191,11 +192,22 @@ namespace Content.Client.Examine
});
}
if (knowTarget)
{
hBox.AddChild(new Label
{
Text = Identity.Name(target, EntityManager, player),
HorizontalExpand = true,
});
}
else
{
hBox.AddChild(new Label
{
Text = "???",
HorizontalExpand = true,
});
}
panel.Measure(Vector2.Infinity);
var size = Vector2.ComponentMax((minWidth, 0), panel.DesiredSize);
@@ -294,7 +306,13 @@ namespace Content.Client.Examine
return;
FormattedMessage message;
OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false);
// Basically this just predicts that we can't make out the entity if we have poor vision.
var canSeeClearly = true;
if (HasComp<BlurryVisionComponent>(playerEnt))
canSeeClearly = false;
OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false, knowTarget: canSeeClearly);
if (entity.IsClientSide())
{
message = GetExamineText(entity, playerEnt);

View File

@@ -0,0 +1,68 @@
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Content.Shared.Eye.Blinding;
namespace Content.Client.Eye.Blinding
{
public sealed class BlurryVisionOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _blurryVisionXShader;
private readonly ShaderInstance _blurryVisionYShader;
private BlurryVisionComponent _blurryVisionComponent = default!;
public BlurryVisionOverlay()
{
IoCManager.InjectDependencies(this);
_blurryVisionXShader = _prototypeManager.Index<ShaderPrototype>("BlurryVisionX").InstanceUnique();
_blurryVisionYShader = _prototypeManager.Index<ShaderPrototype>("BlurryVisionY").InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
if (playerEntity == null)
return false;
if (!_entityManager.TryGetComponent<BlurryVisionComponent>(playerEntity, out var blurComp))
return false;
if (!blurComp.Active)
return false;
if (_entityManager.TryGetComponent<BlindableComponent>(playerEntity, out var blindComp)
&& blindComp.Sources > 0)
return false;
_blurryVisionComponent = blurComp;
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
_blurryVisionXShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_blurryVisionXShader?.SetParameter("BLUR_AMOUNT", (_blurryVisionComponent.Magnitude / 10));
_blurryVisionYShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_blurryVisionYShader?.SetParameter("BLUR_AMOUNT", (_blurryVisionComponent.Magnitude / 10));
var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.UseShader(_blurryVisionXShader);
worldHandle.DrawRect(viewport, Color.White);
worldHandle.UseShader(_blurryVisionYShader);
worldHandle.DrawRect(viewport, Color.White);
}
}
}

View File

@@ -0,0 +1,61 @@
using Content.Shared.Eye.Blinding;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.GameStates;
namespace Content.Client.Eye.Blinding;
public sealed class BlurryVisionSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private BlurryVisionOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BlurryVisionComponent, ComponentInit>(OnBlurryInit);
SubscribeLocalEvent<BlurryVisionComponent, ComponentShutdown>(OnBlurryShutdown);
SubscribeLocalEvent<BlurryVisionComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<BlurryVisionComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<BlurryVisionComponent, ComponentHandleState>(OnHandleState);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, BlurryVisionComponent component, PlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, BlurryVisionComponent component, PlayerDetachedEvent args)
{
_overlayMan.RemoveOverlay(_overlay);
}
private void OnBlurryInit(EntityUid uid, BlurryVisionComponent component, ComponentInit args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnBlurryShutdown(EntityUid uid, BlurryVisionComponent component, ComponentShutdown args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
{
_overlayMan.RemoveOverlay(_overlay);
}
}
private void OnHandleState(EntityUid uid, BlurryVisionComponent component, ref ComponentHandleState args)
{
if (args.Current is not BlurryVisionComponentState state)
return;
component.Magnitude = state.Magnitude;
}
}

View File

@@ -0,0 +1,23 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Eye.Blinding;
using JetBrains.Annotations;
namespace Content.Server.Chemistry.ReagentEffects
{
/// <summary>
/// Heal eye damage (or deal)
/// </summary>
[UsedImplicitly]
public sealed class ChemHealEyeDamage : ReagentEffect
{
/// <summary>
/// Add or remove eye damage?
[DataField("add")]
public bool Add = false;
public override void Effect(ReagentEffectArgs args)
{
EntitySystem.Get<SharedBlindingSystem>().AdjustEyeDamage(args.SolutionEntity, Add);
}
}
}

View File

@@ -16,10 +16,14 @@ namespace Content.Server.Examine
private static readonly FormattedMessage _entityNotFoundMessage;
private static readonly FormattedMessage _entityOutOfRangeMessage;
static ExamineSystem()
{
_entityNotFoundMessage = new FormattedMessage();
_entityNotFoundMessage.AddText(Loc.GetString("examine-system-entity-does-not-exist"));
_entityOutOfRangeMessage = new FormattedMessage();
_entityOutOfRangeMessage.AddText(Loc.GetString("examine-system-cant-see-entity"));
}
public override void Initialize()
@@ -54,14 +58,20 @@ namespace Content.Server.Examine
var channel = player.ConnectedClient;
if (session.AttachedEntity is not {Valid: true} playerEnt
|| !EntityManager.EntityExists(request.EntityUid)
|| !CanExamine(playerEnt, request.EntityUid))
|| !EntityManager.EntityExists(request.EntityUid))
{
RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
request.EntityUid, _entityNotFoundMessage), channel);
return;
}
if (!CanExamine(playerEnt, request.EntityUid))
{
RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage(
request.EntityUid, _entityOutOfRangeMessage, knowTarget: false), channel);
return;
}
SortedSet<Verb>? verbs = null;
if (request.GetVerbs)
verbs = _verbSystem.GetLocalVerbs(request.EntityUid, playerEnt, typeof(ExamineVerb));

View File

@@ -0,0 +1,77 @@
using Content.Shared.Eye.Blinding.EyeProtection; // why aren't tools predicted 🙂
using Content.Shared.Eye.Blinding;
using Content.Shared.StatusEffect;
using Content.Shared.Clothing.Components;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Server.Tools;
namespace Content.Server.Eye.Blinding.EyeProtection
{
public sealed class EyeProtectionSystem : EntitySystem
{
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
[Dependency] private readonly SharedBlindingSystem _blindingSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RequiresEyeProtectionComponent, ToolUseAttemptEvent>(OnUseAttempt);
SubscribeLocalEvent<RequiresEyeProtectionComponent, WelderToggledEvent>(OnWelderToggled);
SubscribeLocalEvent<EyeProtectionComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<EyeProtectionComponent, GotUnequippedEvent>(OnUnequipped);
}
private void OnUseAttempt(EntityUid uid, RequiresEyeProtectionComponent component, ToolUseAttemptEvent args)
{
if (!component.Toggled)
return;
if (!TryComp<StatusEffectsComponent>(args.User, out var status) || !TryComp<BlindableComponent>(args.User, out var blindable))
return;
if (blindable.Sources > 0)
return;
float statusTime = (float) component.StatusEffectTime.TotalSeconds - blindable.BlindResistance;
if (statusTime <= 0)
return;
var statusTimeSpan = TimeSpan.FromSeconds(statusTime * (blindable.EyeDamage + 1));
// Add permanent eye damage if they had zero protection, also scale their temporary blindness by how much they already accumulated.
if (_statusEffectsSystem.TryAddStatusEffect(args.User, SharedBlindingSystem.BlindingStatusEffect, statusTimeSpan, false, "TemporaryBlindness") && blindable.BlindResistance <= 0)
_blindingSystem.AdjustEyeDamage(args.User, true, blindable);
}
private void OnWelderToggled(EntityUid uid, RequiresEyeProtectionComponent component, WelderToggledEvent args)
{
component.Toggled = args.WelderOn;
}
private void OnEquipped(EntityUid uid, EyeProtectionComponent component, GotEquippedEvent args)
{
if (!TryComp<SharedClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP)
return;
if (!clothing.Slots.HasFlag(args.SlotFlags))
return;
component.IsActive = true;
if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
return;
blindComp.BlindResistance += (float) component.ProtectionTime.TotalSeconds;
}
private void OnUnequipped(EntityUid uid, EyeProtectionComponent component, GotUnequippedEvent args)
{
if (!component.IsActive)
return;
component.IsActive = false;
if (!TryComp<BlindableComponent>(args.Equipee, out var blindComp))
return;
blindComp.BlindResistance -= (float) component.ProtectionTime.TotalSeconds;
}
}
}

View File

@@ -104,6 +104,9 @@ namespace Content.Server.Tools
welder.Lit = true;
var ev = new WelderToggledEvent(true);
RaiseLocalEvent(welder.Owner, ev, false);
if(item != null)
_itemSystem.SetHeldPrefix(uid, "on", item);
@@ -140,6 +143,9 @@ namespace Content.Server.Tools
welder.Lit = false;
var ev = new WelderToggledEvent(false);
RaiseLocalEvent(welder.Owner, ev, false);
// TODO: Make all this use visualizers.
if (item != null)
_itemSystem.SetHeldPrefix(uid, "off", item);
@@ -332,4 +338,14 @@ namespace Content.Server.Tools
_welderTimer -= WelderUpdateTimer;
}
}
public sealed class WelderToggledEvent : EntityEventArgs
{
public bool WelderOn;
public WelderToggledEvent(bool welderOn)
{
WelderOn = welderOn;
}
}
}

View File

@@ -31,14 +31,17 @@ namespace Content.Shared.Examine
public readonly bool CenterAtCursor;
public readonly bool OpenAtOldTooltip;
public readonly bool KnowTarget;
public ExamineInfoResponseMessage(EntityUid entityUid, FormattedMessage message, List<Verb>? verbs=null,
bool centerAtCursor=true, bool openAtOldTooltip=true)
bool centerAtCursor=true, bool openAtOldTooltip=true, bool knowTarget = true)
{
EntityUid = entityUid;
Message = message;
Verbs = verbs;
CenterAtCursor = centerAtCursor;
OpenAtOldTooltip = openAtOldTooltip;
KnowTarget = knowTarget;
}
}
}

View File

@@ -101,6 +101,12 @@ namespace Content.Shared.Examine
return DeadExamineRange;
else if (MobStateSystem.IsCritical(examiner, mobState) || (TryComp<BlindableComponent>(examiner, out var blind) && blind.Sources > 0))
return CritExamineRange;
else if (TryComp<BlurryVisionComponent>(examiner, out var blurry) && blurry.Magnitude != 0)
{
float range = ExamineRange - (2 * (8 - blurry.Magnitude));
return Math.Clamp(range, 2, 16);
}
}
return ExamineRange;
}

View File

@@ -12,6 +12,24 @@ namespace Content.Shared.Eye.Blinding
[DataField("sources")]
public int Sources = 0;
/// <summary>
/// How many seconds will be subtracted from each attempt to add blindness to us?
/// </summary>
[DataField("blindResistance")]
public float BlindResistance = 0;
/// <summary>
/// Replace with actual eye damage after bobby I guess
/// </summary>
[ViewVariables]
public int EyeDamage = 0;
/// <summary>
/// Whether eye damage has accumulated enough to blind them.
/// <summary>
[ViewVariables]
public bool EyeTooDamaged = false;
/// <description>
/// Used to ensure that this doesn't break with sandbox or admin tools.
/// This is not "enabled/disabled".

View File

@@ -0,0 +1,14 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Eye.Blinding
{
[RegisterComponent]
[NetworkedComponent]
public sealed class BlurryVisionComponent : Component
{
[DataField("mangitude")]
public float Magnitude = 1f;
public bool Active => Magnitude < 10f;
}
}

View File

@@ -0,0 +1,18 @@
namespace Content.Shared.Eye.Blinding.EyeProtection
{
/// <summary>
/// For welding masks, sunglasses, etc.
/// </summary>
[RegisterComponent]
public sealed class EyeProtectionComponent : Component
{
/// <summary>
/// How many seconds to subtract from the status effect. If it's greater than the source
/// of blindness, do not blind.
/// </summary>
[DataField("protectionTime")]
public TimeSpan ProtectionTime = TimeSpan.FromSeconds(10);
public bool IsActive = false;
}
}

View File

@@ -0,0 +1,21 @@
namespace Content.Shared.Eye.Blinding.EyeProtection
{
/// <summary>
/// For tools like welders that will damage your eyes when you use them.
/// </summary>
[RegisterComponent]
public sealed class RequiresEyeProtectionComponent : Component
{
/// <summary>
/// How long to apply temporary blindness to the user.
/// </summary>
[DataField("statusEffectTime")]
public TimeSpan StatusEffectTime = TimeSpan.FromSeconds(10);
/// <summary>
/// You probably want to turn this on in yaml if it's something always on and not a welder.
/// </summary>
[DataField("toggled")]
public bool Toggled = false;
}
}

View File

@@ -1,18 +1,28 @@
using Content.Shared.Clothing.Components;
using Content.Shared.Inventory.Events;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using JetBrains.Annotations;
namespace Content.Shared.Eye.Blinding
{
public sealed class SharedBlindingSystem : EntitySystem
{
public const string BlindingStatusEffect = "TemporaryBlindness";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BlindfoldComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<BlindfoldComponent, GotUnequippedEvent>(OnUnequipped);
SubscribeLocalEvent<VisionCorrectionComponent, GotEquippedEvent>(OnGlassesEquipped);
SubscribeLocalEvent<VisionCorrectionComponent, GotUnequippedEvent>(OnGlassesUnequipped);
SubscribeLocalEvent<BlurryVisionComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<TemporaryBlindnessComponent, ComponentShutdown>(OnShutdown);
}
private void OnEquipped(EntityUid uid, BlindfoldComponent component, GotEquippedEvent args)
@@ -39,6 +49,46 @@ namespace Content.Shared.Eye.Blinding
AdjustBlindSources(args.Equipee, false, blindComp);
}
private void OnGlassesEquipped(EntityUid uid, VisionCorrectionComponent component, GotEquippedEvent args)
{
if (!TryComp<SharedClothingComponent>(uid, out var clothing) || clothing.Slots == SlotFlags.PREVENTEQUIP) // we live in a society
return;
// Is the clothing in its actual slot?
if (!clothing.Slots.HasFlag(args.SlotFlags))
return;
if (!TryComp<BlurryVisionComponent>(args.Equipee, out var blur))
return;
component.IsActive = true;
blur.Magnitude += component.VisionBonus;
blur.Dirty();
}
private void OnGlassesUnequipped(EntityUid uid, VisionCorrectionComponent component, GotUnequippedEvent args)
{
if (!component.IsActive || !TryComp<BlurryVisionComponent>(args.Equipee, out var blur))
return;
component.IsActive = false;
blur.Magnitude -= component.VisionBonus;
blur.Dirty();
}
private void OnGetState(EntityUid uid, BlurryVisionComponent component, ref ComponentGetState args)
{
args.State = new BlurryVisionComponentState(component.Magnitude);
}
private void OnInit(EntityUid uid, TemporaryBlindnessComponent component, ComponentInit args)
{
AdjustBlindSources(uid, true);
}
private void OnShutdown(EntityUid uid, TemporaryBlindnessComponent component, ComponentShutdown args)
{
AdjustBlindSources(uid, false);
}
[PublicAPI]
public void AdjustBlindSources(EntityUid uid, bool Add, BlindableComponent? blindable = null)
{
@@ -52,6 +102,56 @@ namespace Content.Shared.Eye.Blinding
{
blindable.Sources--;
}
blindable.Sources = Math.Max(blindable.Sources, 0);
}
public void AdjustEyeDamage(EntityUid uid, bool add, BlindableComponent? blindable = null)
{
if (!Resolve(uid, ref blindable, false))
return;
if (add)
{
blindable.EyeDamage++;
} else
{
blindable.EyeDamage--;
}
if (blindable.EyeDamage > 0)
{
var blurry = EnsureComp<BlurryVisionComponent>(uid);
blurry.Magnitude = (9 - blindable.EyeDamage);
blurry.Dirty();
} else
{
RemComp<BlurryVisionComponent>(uid);
}
if (!blindable.EyeTooDamaged && blindable.EyeDamage >= 8)
{
blindable.EyeTooDamaged = true;
AdjustBlindSources(uid, true, blindable);
}
if (blindable.EyeTooDamaged && blindable.EyeDamage < 8)
{
blindable.EyeTooDamaged = false;
AdjustBlindSources(uid, false, blindable);
}
blindable.EyeDamage = Math.Clamp(blindable.EyeDamage, 0, 8);
}
}
// I have no idea why blurry vision needs this but blindness doesn't
[Serializable, NetSerializable]
public sealed class BlurryVisionComponentState : ComponentState
{
public float Magnitude;
public BlurryVisionComponentState(float magnitude)
{
Magnitude = magnitude;
}
}
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Eye.Blinding
{
/// <summary>
/// Blind status effect.
/// </summary>
[NetworkedComponent, RegisterComponent]
public sealed class TemporaryBlindnessComponent : Component
{}
}

View File

@@ -0,0 +1,15 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Eye.Blinding
{
[RegisterComponent]
[NetworkedComponent]
public sealed class VisionCorrectionComponent : Component
{
[ViewVariables]
public bool IsActive = false;
[DataField("visionBonus")]
public float VisionBonus = 3f;
}
}

View File

@@ -2,6 +2,8 @@
examine-system-entity-does-not-exist = That entity doesn't exist
examine-system-cant-see-entity = You can't make out whatever that is.
examine-verb-name = Basic
examinable-anchored = It is anchored to the floor

View File

@@ -85,5 +85,8 @@ reagent-desc-omnizine = A soothing milky liquid with an iridescent gleam. A well
reagent-name-ultravasculine = ultravasculine
reagent-desc-ultravasculine = Rapidly flushes toxins from the body, but places some stress on the veins. Do not overdose.
reagent-name-oculine = oculine
reagent-desc-oculine = Heals eye damage.
reagent-name-ethylredoxrazine = ethylredoxrazine
reagent-desc-ethylredoxrazine = Neutralises the effects of alcohol in the blood stream. Though it is commonly needed, it is rarely requested.

View File

@@ -5,6 +5,7 @@
spriteName: engivend
startingInventory:
ClothingEyesGlassesMeson: 4
ClothingHeadHatWelding: 4
Multitool: 4
PowerCellMedium: 5
ClothingHandsGlovesColorYellow: 6

View File

@@ -65,17 +65,14 @@
- type: entity
parent: ClothingEyesBase
id: ClothingEyesGlassesMeson
name: optical meson scanners
description: The pinnacle of modern science, wallhacks in real life
name: engineering goggles #less confusion
description: Green-tinted goggles using a proprietary polymer that provides protection from eye damage of all types.
components:
- type: Sprite
sprite: Clothing/Eyes/Glasses/meson.rsi
- type: Clothing
sprite: Clothing/Eyes/Glasses/meson.rsi
- type: Armor
modifiers:
coefficients:
Radiation: 0.5
- type: EyeProtection
- type: entity
parent: ClothingEyesBase
@@ -87,6 +84,7 @@
sprite: Clothing/Eyes/Glasses/glasses.rsi
- type: Clothing
sprite: Clothing/Eyes/Glasses/glasses.rsi
- type: VisionCorrection
- type: entity
parent: ClothingEyesBase
@@ -99,6 +97,8 @@
- type: Clothing
sprite: Clothing/Eyes/Glasses/sunglasses.rsi
- type: FlashImmunity
- type: EyeProtection
protectionTime: 5
- type: entity
parent: ClothingEyesBase
@@ -111,6 +111,8 @@
- type: Clothing
sprite: Clothing/Eyes/Glasses/secglasses.rsi
- type: FlashImmunity
- type: EyeProtection
protectionTime: 5
#Make a scanner category when these actually function and we get the trayson
- type: entity

View File

@@ -8,6 +8,8 @@
sprite: Clothing/Eyes/Misc/eyepatch.rsi
- type: Clothing
sprite: Clothing/Eyes/Misc/eyepatch.rsi
- type: EyeProtection
protectionTime: 5
- type: entity
parent: ClothingEyesBase

View File

@@ -7,6 +7,7 @@
- type: IngestionBlocker
- type: FlashImmunity
- type: IdentityBlocker
- type: EyeProtection
- type: entity
parent: WeldingMaskBase

View File

@@ -36,6 +36,8 @@
- type: Clothing
sprite: Clothing/Mask/gassyndicate.rsi
- type: FlashImmunity
- type: EyeProtection
protectionTime: 5
- type: entity
parent: ClothingMaskGas

View File

@@ -112,6 +112,7 @@
- Stutter
- Electrocution
- ForcedSleep
- TemporaryBlindness
- type: Body
template: AnimalTemplate
preset: AnimalPreset
@@ -179,6 +180,7 @@
- Stutter
- Electrocution
- ForcedSleep
- TemporaryBlindness
- type: ThermalRegulator
metabolismHeat: 800
radiatedHeat: 100

View File

@@ -80,6 +80,7 @@
- PressureImmunity
- Muted
- ForcedSleep
- TemporaryBlindness
- type: DiseaseCarrier
- type: Blindable
# Other

View File

@@ -108,6 +108,17 @@
- state: carrot
- type: SliceableFood
slice: FoodCakeCarrotSlice
- type: SolutionContainerManager
solutions:
food:
maxVol: 30
reagents:
- ReagentId: JuiceCarrot
Quantity: 15
- ReagentId: Sugar
Quantity: 5
- ReagentId: Vitamin
Quantity: 5
- type: entity
name: slice of carrot cake
@@ -120,6 +131,18 @@
- state: plate-small
- state: plate-slice-shading
- state: carrot-slice
- type: SolutionContainerManager
solutions:
food:
maxVol: 6
reagents:
- ReagentId: JuiceCarrot
Quantity: 3
- ReagentId: Sugar
Quantity: 1
- ReagentId: Vitamin
Quantity: 1
# Tastes like sweetness, cake, carrot.
- type: entity

View File

@@ -265,5 +265,7 @@
reagents:
- ReagentId: Nutriment
Quantity: 2
- ReagentId: JuiceCarrot
Quantity: 1
- type: Sprite
state: dink

View File

@@ -74,7 +74,8 @@
- type: Sprite
layers:
- state: margherita-slice
# Tastes like crust, tomato, cheese.
- type: SliceableFood
slice: FoodPizzaMeatSlice
- type: entity
name: meat pizza
@@ -133,6 +134,17 @@
- state: vegetable
- type: SliceableFood
slice: FoodPizzaVegetableSlice
- type: SolutionContainerManager
solutions:
food:
maxVol: 40
reagents:
- ReagentId: Nutriment
Quantity: 25
- ReagentId: JuiceCarrot
Quantity: 5
- ReagentId: Vitamin
Quantity: 5
- type: entity
name: slice of vegetable pizza
@@ -143,6 +155,18 @@
- type: Sprite
layers:
- state: vegetable-slice
- type: SolutionContainerManager
solutions:
food:
maxVol: 40
reagents:
- ReagentId: Nutriment
Quantity: 4
- ReagentId: JuiceCarrot
Quantity: 1
- ReagentId: Vitamin
Quantity: 1
# Tastes like crust, tomato, cheese, carrot.
- type: entity

View File

@@ -61,6 +61,14 @@
components:
- type: Sprite
state: fries-carrot
netsync: false
- type: SolutionContainerManager
solutions:
food:
maxVol: 26
reagents:
- ReagentId: JuiceCarrot
Quantity: 20
# Tastes like carrots, salt.
- type: entity

View File

@@ -228,7 +228,7 @@
food:
maxVol: 9
reagents:
- ReagentId: Nutriment
- ReagentId: JuiceCarrot
Quantity: 5
- ReagentId: Vitamin
Quantity: 4

View File

@@ -50,6 +50,7 @@
radius: 1.5
color: orange
- type: Appearance
- type: RequiresEyeProtection
- type: entity
name: industrial welding tool

View File

@@ -86,7 +86,7 @@
growthStages: 3
waterConsumption: 6
chemicals:
Nutriment:
JuiceCarrot:
Min: 1
Max: 5
PotencyDivisor: 20

View File

@@ -57,6 +57,17 @@
desc: reagent-desc-juice-carrot
physicalDesc: reagent-physical-desc-crisp
color: "#FF8820"
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 2
- !type:AdjustReagent
reagent: Oculine
amount: 0.15
- !type:AdjustReagent
reagent: Nutriment
amount: 0.5
- type: reagent
id: JuiceGrape

View File

@@ -658,3 +658,14 @@
reagent: Ultravasculine
amount: 0.5
- type: reagent
id: Oculine
name: reagent-name-oculine
desc: reagent-desc-oculine
group: Medicine
physicalDesc: reagent-physical-desc-translucent
color: "#404040"
metabolisms:
Medicine:
effects:
- !type:ChemHealEyeDamage

View File

@@ -323,6 +323,20 @@
products:
Ultravasculine: 2
- type: reaction
id: Oculine
reactants:
TableSalt:
amount: 1
Blood:
amount: 1
Carbon:
amount: 1
Hydrogen:
amount: 1
products:
Oculine: 4
- type: reaction
id: Siderlac
reactants:

View File

@@ -55,3 +55,13 @@
pixelSize: 32, 32
alphaCutoff: 0
removeTransparency: false
- type: shader
id: BlurryVisionX
kind: source
path: "/Textures/Shaders/blurryx.swsl"
- type: shader
id: BlurryVisionY
kind: source
path: "/Textures/Shaders/blurryy.swsl"

View File

@@ -48,3 +48,5 @@
- type: statusEffect
id: ForcedSleep #I.e., they will not wake on damage or similar
- type: statusEffect
id: TemporaryBlindness

View File

@@ -0,0 +1,16 @@
uniform sampler2D SCREEN_TEXTURE;
uniform highp float BLUR_AMOUNT;
void fragment() {
highp vec3 col = texture(SCREEN_TEXTURE, UV).xyz * BLUR_AMOUNT;
highp vec4 color = zTexture(UV);
col += texture(SCREEN_TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(SCREEN_TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(SCREEN_TEXTURE, UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(SCREEN_TEXTURE, UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(SCREEN_TEXTURE, UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(SCREEN_TEXTURE, UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(SCREEN_TEXTURE, UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
col += texture(SCREEN_TEXTURE, UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR = vec4(vec3(col), color.a);
}

View File

@@ -0,0 +1,16 @@
uniform sampler2D SCREEN_TEXTURE;
uniform highp float BLUR_AMOUNT;
void fragment() {
highp vec3 col = texture(SCREEN_TEXTURE, UV).xyz * BLUR_AMOUNT;
highp vec4 color = zTexture(UV);
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
col += texture(SCREEN_TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR = vec4(vec3(col), color.a);
}