diff --git a/Content.Shared/Alert/AlertPrototype.cs b/Content.Shared/Alert/AlertPrototype.cs index 465a1959f0..e57d4e6bbc 100644 --- a/Content.Shared/Alert/AlertPrototype.cs +++ b/Content.Shared/Alert/AlertPrototype.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -21,14 +21,11 @@ namespace Content.Shared.Alert public AlertType AlertType { get; private set; } /// - /// Path to the icon (png) to show in alert bar. If severity levels are supported, - /// this should be the path to the icon without the severity number - /// (i.e. hot.png if there is hot1.png and hot2.png). Use - /// to get the correct icon path for a particular severity level. + /// List of icons to use for this alert. Each entry corresponds to a different severity level, starting from the + /// minimum and incrementing upwards. If severities are not supported, the first entry is used. /// - [ViewVariables] - [DataField("icon")] - public SpriteSpecifier Icon { get; private set; } = SpriteSpecifier.Invalid; + [DataField("icons", required: true)] + public readonly List Icons = new(); /// /// Name to show in tooltip window. Accepts formatting. @@ -103,8 +100,14 @@ namespace Content.Shared.Alert throw new InvalidOperationException($"This alert ({AlertKey}) does not support severity"); } + var minIcons = SupportsSeverity + ? MaxSeverity - MinSeverity : 1; + + if (Icons.Count < minIcons) + throw new InvalidOperationException($"Insufficient number of icons given for alert {AlertType}"); + if (!SupportsSeverity) - return Icon; + return Icons[0]; if (severity == null) { @@ -121,20 +124,7 @@ namespace Content.Shared.Alert throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}."); } - var severityText = severity.Value.ToString(CultureInfo.InvariantCulture); - switch (Icon) - { - case SpriteSpecifier.EntityPrototype entityPrototype: - throw new InvalidOperationException($"Severity not supported for EntityPrototype icon in {AlertKey}"); - case SpriteSpecifier.Rsi rsi: - return new SpriteSpecifier.Rsi(rsi.RsiPath, rsi.RsiState + severityText); - case SpriteSpecifier.Texture texture: - var newName = texture.TexturePath.FilenameWithoutExtension + severityText; - return new SpriteSpecifier.Texture( - texture.TexturePath.WithName(newName + "." + texture.TexturePath.Extension)); - default: - throw new ArgumentOutOfRangeException(nameof(Icon)); - } + return Icons[severity.Value - _minSeverity]; } } } diff --git a/Content.Tests/Shared/Alert/AlertManagerTests.cs b/Content.Tests/Shared/Alert/AlertManagerTests.cs index 3edf34bd20..71046ff70c 100644 --- a/Content.Tests/Shared/Alert/AlertManagerTests.cs +++ b/Content.Tests/Shared/Alert/AlertManagerTests.cs @@ -16,11 +16,13 @@ namespace Content.Tests.Shared.Alert const string PROTOTYPES = @" - type: alert id: LowPressure - icon: /Textures/Interface/Alerts/Pressure/lowpressure.png + icons: + - /Textures/Interface/Alerts/Pressure/lowpressure.png - type: alert id: HighPressure - icon: /Textures/Interface/Alerts/Pressure/highpressure.png + icons: + - /Textures/Interface/Alerts/Pressure/highpressure.png "; [Test] @@ -37,14 +39,14 @@ namespace Content.Tests.Shared.Alert prototypeManager.LoadFromStream(new StringReader(PROTOTYPES)); Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out var lowPressure)); - Assert.That(lowPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highPressure)); - Assert.That(highPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png")))); Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out lowPressure)); - Assert.That(lowPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out highPressure)); - Assert.That(highPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png")))); } } } diff --git a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs index d1c2a429c5..fe293b050d 100644 --- a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs @@ -26,35 +26,44 @@ namespace Content.Tests.Shared.Alert - type: alert id: LowPressure + icons: [] category: Pressure - type: alert id: HighPressure + icons: [] category: Pressure - type: alert id: Peckish + icons: [] category: Hunger - type: alert id: Stun + icons: [] - type: alert id: Handcuffed + icons: [] - type: alert id: Hot + icons: [] category: Temperature - type: alert id: Cold + icons: [] category: Temperature - type: alert id: Weightless + icons: [] - type: alert id: PilotingShuttle + icons: [] "; [Test] diff --git a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs index 096107cf16..dffab940cc 100644 --- a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Content.Shared.Alert; using NUnit.Framework; @@ -17,7 +17,14 @@ namespace Content.Tests.Shared.Alert - type: alert id: HumanHealth category: Health - icon: /Textures/Interface/Alerts/Human/human.rsi/human.png + icons: + - /Textures/Interface/Alerts/Human/human.rsi/human0.png + - /Textures/Interface/Alerts/Human/human.rsi/human1.png + - /Textures/Interface/Alerts/Human/human.rsi/human2.png + - /Textures/Interface/Alerts/Human/human.rsi/human3.png + - /Textures/Interface/Alerts/Human/human.rsi/human4.png + - /Textures/Interface/Alerts/Human/human.rsi/human5.png + - /Textures/Interface/Alerts/Human/human.rsi/human6.png name: Health description: ""[color=green]Green[/color] good. [color=red]Red[/color] bad."" minSeverity: 0 diff --git a/Content.YAMLLinter/Program.cs b/Content.YAMLLinter/Program.cs index 0a49e59517..99c61071b6 100644 --- a/Content.YAMLLinter/Program.cs +++ b/Content.YAMLLinter/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -88,15 +88,28 @@ namespace Content.YAMLLinter foreach (var (key, val) in serverErrors) { + // Include all server errors marked as always relevant var newErrors = val.Where(n => n.AlwaysRelevant).ToHashSet(); - if (clientErrors.TryGetValue(key, out var clientVal)) - { - newErrors.UnionWith(val.Intersect(clientVal)); - newErrors.UnionWith(clientVal.Where(n => n.AlwaysRelevant)); - } - if (newErrors.Count == 0) continue; - allErrors[key] = newErrors; + // We include sometimes-relevant errors if they exist both for the client & server + if (clientErrors.TryGetValue(key, out var clientVal)) + newErrors.UnionWith(val.Intersect(clientVal)); + + if (newErrors.Count != 0) + allErrors[key] = newErrors; + } + + // Finally add any always-relevant client errors. + foreach (var (key, val) in clientErrors) + { + var newErrors = val.Where(n => n.AlwaysRelevant).ToHashSet(); + if (newErrors.Count == 0) + continue; + + if (allErrors.TryGetValue(key, out var errors)) + errors.UnionWith(val.Where(n => n.AlwaysRelevant)); + else + allErrors[key] = newErrors; } return allErrors; diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index e2c53944ad..fffafc48fd 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -23,8 +23,8 @@ - type: alert id: LowOxygen category: Breathing - icon: - sprite: /Textures/Interface/Alerts/breathing.rsi + icons: + - sprite: /Textures/Interface/Alerts/breathing.rsi state: not_enough_oxy name: "[color=red]Low Oxygen[/color]" description: "There is [color=red]not enough oxygen[/color] in the air you are breathing. Put on [color=green]internals[/color]." @@ -32,8 +32,8 @@ - type: alert id: Toxins category: Toxins - icon: - sprite: /Textures/Interface/Alerts/breathing.rsi + icons: + - sprite: /Textures/Interface/Alerts/breathing.rsi state: too_much_tox name: "[color=red]High Toxin Level[/color]" description: "There are [color=red]too many toxins[/color] in the air you are breathing. Put on [color=green]internals[/color] or get away." @@ -41,9 +41,11 @@ - type: alert id: LowPressure category: Pressure - icon: - sprite: /Textures/Interface/Alerts/pressure.rsi - state: lowpressure + icons: + - sprite: /Textures/Interface/Alerts/pressure.rsi + state: lowpressure1 + - sprite: /Textures/Interface/Alerts/pressure.rsi + state: lowpressure2 maxSeverity: 2 name: "[color=red]Low Pressure[/color]" description: "The air around you is [color=red]hazardously thin[/color]. A [color=green]space suit[/color] would protect you." @@ -51,16 +53,18 @@ - type: alert id: HighPressure category: Pressure - icon: - sprite: /Textures/Interface/Alerts/pressure.rsi - state: highpressure + icons: + - sprite: /Textures/Interface/Alerts/pressure.rsi + state: highpressure1 + - sprite: /Textures/Interface/Alerts/pressure.rsi + state: highpressure2 maxSeverity: 2 name: "[color=red]High Pressure[/color]" description: "The air around you is [color=red]hazardously thick[/color]. A [color=green]pressurized suit[/color] would be enough protect you" - type: alert id: Fire - icon: /Textures/Interface/Alerts/Fire/fire.png + icons: [ /Textures/Interface/Alerts/Fire/fire.png ] onClick: !type:ResistFire { } name: "[color=red]On Fire[/color]" description: "You're [color=red]on fire[/color]. Click the alert to stop, drop and roll to put the fire out or move to a vacuum area." @@ -69,9 +73,13 @@ - type: alert id: Cold category: Temperature - icon: - sprite: /Textures/Interface/Alerts/temperature.rsi - state: cold + icons: + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: cold1 + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: cold2 + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: cold3 maxSeverity: 3 name: "[color=cyan]Too Cold[/color]" description: "You're [color=cyan]freezing cold![/color] Get somewhere warmer and take off any insulating clothing like a space suit." @@ -79,16 +87,20 @@ - type: alert id: Hot category: Temperature - icon: - sprite: /Textures/Interface/Alerts/temperature.rsi - state: hot + icons: + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: hot1 + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: hot2 + - sprite: /Textures/Interface/Alerts/temperature.rsi + state: hot3 maxSeverity: 3 name: "[color=red]Too Hot[/color]" description: "It's [color=red]too hot![/color] Get somewhere colder, take off any insulating clothing like a space suit, or at least get away from the flames." - type: alert id: Weightless - icon: /Textures/Interface/Alerts/Weightless/weightless.png + icons: [ /Textures/Interface/Alerts/Weightless/weightless.png ] name: Weightless description: > Gravity has ceased affecting you, and you're floating around aimlessly. Find something sturdy to hold onto, or throw or shoot something in a direction opposite of you. @@ -96,14 +108,14 @@ - type: alert id: Stun - icon: /Textures/Objects/Weapons/Melee/stunbaton.rsi/stunbaton_off.png #Should probably draw a proper icon + icons: [ /Textures/Objects/Weapons/Melee/stunbaton.rsi/stunbaton_off.png ] #Should probably draw a proper icon name: "[color=yellow]Stunned[/color]" description: "You're [color=yellow]stunned[/color]! Something is impairing your ability to move or interact with objects" - type: alert id: Handcuffed onClick: !type:RemoveCuffs { } - icon: /Textures/Interface/Alerts/Handcuffed/Handcuffed.png + icons: [ /Textures/Interface/Alerts/Handcuffed/Handcuffed.png ] name: "[color=yellow]Handcuffed[/color]" description: "You're [color=yellow]handcuffed[/color] and can't use your hands. If anyone drags you, you won't be able to resist." @@ -111,15 +123,15 @@ id: Buckled category: Buckled onClick: !type:Unbuckle { } - icon: /Textures/Interface/Alerts/Buckle/buckled.png + icons: [ /Textures/Interface/Alerts/Buckle/buckled.png ] name: "[color=yellow]Buckled[/color]" description: "You've been [color=yellow]buckled[/color] to something. Click the alert to unbuckle unless you're [color=yellow]handcuffed.[/color]" - type: alert id: HumanCrit category: Health - icon: - sprite: /Textures/Interface/Alerts/human_health.rsi + icons: + - sprite: /Textures/Interface/Alerts/human_health.rsi state: health6 name: "[color=red]Critical Condition[/color]" description: "You're severely injured and unconscious." @@ -127,8 +139,8 @@ - type: alert id: HumanDead category: Health - icon: - sprite: /Textures/Interface/Alerts/human_health.rsi + icons: + - sprite: /Textures/Interface/Alerts/human_health.rsi state: health7 name: Dead description: You're dead, note that you can still be revived! @@ -136,9 +148,23 @@ - type: alert id: HumanHealth category: Health - icon: - sprite: /Textures/Interface/Alerts/human_health.rsi - state: health + icons: + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health0 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health1 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health2 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health3 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health4 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health5 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health6 + - sprite: /Textures/Interface/Alerts/human_health.rsi + state: health7 name: Health description: "[color=green]Green[/color] good. [color=red]Red[/color] bad." minSeverity: 0 @@ -148,9 +174,13 @@ id: Internals category: Internals onClick: !type:ToggleInternals {} - icon: - sprite: /Textures/Interface/Alerts/internals.rsi - state: internal + icons: + - sprite: /Textures/Interface/Alerts/internals.rsi + state: internal0 + - sprite: /Textures/Interface/Alerts/internals.rsi + state: internal1 + - sprite: /Textures/Interface/Alerts/internals.rsi + state: internal2 name: Toggle internals description: "Toggles your gas tank internals on or off." minSeverity: 0 @@ -160,23 +190,35 @@ id: PilotingShuttle category: Piloting onClick: !type:StopPiloting { } - icon: /Textures/Interface/Alerts/piloting.png + icons: [ /Textures/Interface/Alerts/piloting.png ] name: Piloting Shuttle description: You are piloting a shuttle. Click the alert to stop. - type: alert id: Peckish category: Hunger - icon: /Textures/Interface/Alerts/Hunger/Peckish.png + icons: [ /Textures/Interface/Alerts/Hunger/Peckish.png ] name: "[color=yellow]Peckish[/color]" description: Some food would be good right about now. - type: alert id: Stamina category: Stamina - icon: - sprite: /Textures/Interface/Alerts/stamina.rsi - state: stamina + icons: + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina0 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina1 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina2 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina3 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina4 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina5 + - sprite: /Textures/Interface/Alerts/stamina.rsi + state: stamina6 name: Stamina description: "Stuns you if it is too low." minSeverity: 0 @@ -185,90 +227,90 @@ - type: alert id: Starving category: Hunger - icon: /Textures/Interface/Alerts/Hunger/Starving.png + icons: [ /Textures/Interface/Alerts/Hunger/Starving.png ] name: "[color=red]Starving[/color]" description: You're severely malnourished. The hunger pains make moving around a chore. - type: alert id: Thirsty category: Thirst - icon: /Textures/Interface/Alerts/Thirst/Thirsty.png + icons: [ /Textures/Interface/Alerts/Thirst/Thirsty.png ] name: "[color=yellow]Thirsty[/color]" description: Something to drink would be good right about now. - type: alert id: Parched category: Thirst - icon: /Textures/Interface/Alerts/Thirst/Parched.png + icons: [ /Textures/Interface/Alerts/Thirst/Parched.png ] name: "[color=red]Parched[/color]" description: You're severely thirsty. The thirst makes moving around a chore. - type: alert id: Muted - icon: /Textures/Interface/Alerts/Abilities/silenced.png + icons: [ /Textures/Interface/Alerts/Abilities/silenced.png ] name: Muted description: You have lost the ability to speak. - type: alert id: VowOfSilence - icon: /Textures/Interface/Alerts/Abilities/silenced.png + icons: [ /Textures/Interface/Alerts/Abilities/silenced.png ] name: Vow of Silence onClick: !type:BreakVow { } description: You have taken a vow of silence as part of initiation into the Mystiko Tagma Mimon. Click to break your vow. - type: alert id: VowBroken - icon: /Textures/Interface/Actions/scream.png + icons: [ /Textures/Interface/Actions/scream.png ] name: Vow Broken onClick: !type:RetakeVow { } description: You've broken your vows to Mimes everywhere. You can speak, but you've lost your powers for at least 5 entire minutes!!! Click to try and retake your vow. - type: alert id: Pulled - icon: /Textures/Interface/Alerts/Pull/pulled.png + icons: [ /Textures/Interface/Alerts/Pull/pulled.png ] onClick: !type:StopBeingPulled { } name: Pulled description: You're being pulled. Move to break free. - type: alert id: Pulling - icon: /Textures/Interface/Alerts/Pull/pulling.png + icons: [ /Textures/Interface/Alerts/Pull/pulling.png ] onClick: !type:StopPulling { } name: Pulling description: You're pulling something. Click the alert to stop. - type: alert id: Debug1 - icon: /Textures/Interface/Alerts/human_health.rsi/health1.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health1.png ] name: Debug1 description: Debug - type: alert id: Debug2 - icon: /Textures/Interface/Alerts/human_health.rsi/health2.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health2.png ] name: Debug2 description: Debug - type: alert id: Debug3 - icon: /Textures/Interface/Alerts/human_health.rsi/health3.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health3.png ] name: Debug3 description: Debug - type: alert id: Debug4 - icon: /Textures/Interface/Alerts/human_health.rsi/health4.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health4.png ] name: Debug4 description: Debug - type: alert id: Debug5 - icon: /Textures/Interface/Alerts/human_health.rsi/health5.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health5.png ] name: Debug5 description: Debug - type: alert id: Debug6 - icon: /Textures/Interface/Alerts/human_health.rsi/health6.png + icons: [ /Textures/Interface/Alerts/human_health.rsi/health6.png ] name: Debug6 description: Debug diff --git a/Resources/Prototypes/Alerts/magboots.yml b/Resources/Prototypes/Alerts/magboots.yml index d2e7464184..ea7dfa7eb6 100644 --- a/Resources/Prototypes/Alerts/magboots.yml +++ b/Resources/Prototypes/Alerts/magboots.yml @@ -1,5 +1,7 @@ - type: alert id: Magboots - icon: { sprite: "/Textures/Clothing/Shoes/Boots/magboots.rsi", state: "icon-on" } + icons: + - sprite: /Textures/Clothing/Shoes/Boots/magboots.rsi + state: icon-on name: "Magboots" - description: You are immume to airflow, but slightly slower. + description: You are immune to airflow, but slightly slower. diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/cat_parts.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/cat_parts.yml index 1098c39fe8..d779d71bd3 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/cat_parts.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/cat_parts.yml @@ -4,9 +4,9 @@ markingCategory: HeadTop speciesRestriction: [Human] sprites: - - sprite: Mobs/Customization/cat_parts.rsi/ + - sprite: Mobs/Customization/cat_parts.rsi state: ears_cat_outer - - sprite: Mobs/Customization/cat_parts.rsi/ + - sprite: Mobs/Customization/cat_parts.rsi state: ears_cat_inner - type: marking @@ -15,5 +15,5 @@ markingCategory: Tail speciesRestriction: [Human] sprites: - - sprite: Mobs/Customization/cat_parts.rsi/ + - sprite: Mobs/Customization/cat_parts.rsi state: tail_cat