From 0aa49c14917355fecdd44cfc65d85c1f334e2d7e Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Thu, 19 Sep 2024 16:37:51 -0400 Subject: [PATCH 01/10] Fix Laying Down Bugs (#933) # Description This fixes prediction issues, both where pressing the Lay down key causes all entities on your screen to glitch and lay down, as well as the bug where upon receiving ANY movement input, a player would be visually changed on their screen to be laying down. # Changelog :cl: - fix: Fixed issues with the LayingDownSystem. Laying down no longer causes all entities on your screen to lay down at once. Using any movement input no longer causes the character to appear to the player as to be laying down. --- Content.Client/Standing/LayingDownSystem.cs | 8 +++++--- Content.Shared/Standing/SharedLayingDownSystem.cs | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Content.Client/Standing/LayingDownSystem.cs b/Content.Client/Standing/LayingDownSystem.cs index 6cc5f7921a..594883ac00 100644 --- a/Content.Client/Standing/LayingDownSystem.cs +++ b/Content.Client/Standing/LayingDownSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.ActionBlocker; +using Content.Shared.Buckle; using Content.Shared.Rotation; using Content.Shared.Standing; using Robust.Client.GameObjects; @@ -11,8 +11,9 @@ public sealed class LayingDownSystem : SharedLayingDownSystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IEyeManager _eyeManager = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly AnimationPlayerSystem _animation = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; + [Dependency] private readonly SharedBuckleSystem _buckle = default!; public override void Initialize() { @@ -26,7 +27,8 @@ public override void Initialize() private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveEvent args) { if (!_timing.IsFirstTimePredicted - || !_actionBlocker.CanMove(uid) + || !_standing.IsDown(uid) + || _buckle.IsBuckled(uid) || _animation.HasRunningAnimation(uid, "rotate") || !TryComp(uid, out var transform) || !TryComp(uid, out var sprite) diff --git a/Content.Shared/Standing/SharedLayingDownSystem.cs b/Content.Shared/Standing/SharedLayingDownSystem.cs index 47ad01949c..7a82d4b7aa 100644 --- a/Content.Shared/Standing/SharedLayingDownSystem.cs +++ b/Content.Shared/Standing/SharedLayingDownSystem.cs @@ -40,7 +40,8 @@ public override void Shutdown() private void ToggleStanding(ICommonSession? session) { - if (session?.AttachedEntity == null + if (session is not { AttachedEntity: { Valid: true } uid } _ + || !Exists(uid) || !HasComp(session.AttachedEntity) || _gravity.IsWeightless(session.AttachedEntity.Value)) return; From 7bce1e92a30359ff674a585925caf4884d01017c Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Thu, 19 Sep 2024 20:38:15 +0000 Subject: [PATCH 02/10] Automatic Changelog Update (#933) --- Resources/Changelog/Changelog.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d7568628f6..a233a61a2d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -6456,3 +6456,14 @@ Entries: id: 6371 time: '2024-09-19T03:29:41.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/929 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed issues with the LayingDownSystem. Laying down no longer causes all + entities on your screen to lay down at once. Using any movement input no + longer causes the character to appear to the player as to be laying + down. + id: 6372 + time: '2024-09-19T20:37:51.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/933 From 8b5dcd6efe52b7af65094a6cbf0de4cee40b84cc Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Thu, 19 Sep 2024 21:47:52 -0400 Subject: [PATCH 03/10] Non-Destructive Universal Language (#899) # Description UniversalLanguageSpeakerComponent has NUMEROUS issues, which previously were easy to ignore since it was a language only obtainable by Admins. But now that it's both a Psionic Power(And a trait that adds said power), the issues with it have been highlighted. Here's just a FEW 1. UniversalLanguageSpeaker overwrites all known languages, preventing you from speaking anything else 2. It overwrites the ability to use sign language. 3. It also overwrites the Mute trait. To fix that, I've made it follow *MOSTLY* the logic all the other languages use, so that it's less of a special snowflake case. Now if you have access to it, it will appear in your language list alongside other languages, rather than fully replacing the entire list. That way you can intentionally choose not to speak in a language understood by all. Fuck it, I also added the ability for the TraitSystem to just call LanguageSystem and directly add arbitrarily any desired language, rather than needing a component to do so. # Changelog :cl: - fix: UniversalLanguageSpeaker(And Xenoglossy by extension) will now appear in your language menu alongside other known languages, rather than replace all known languages. You can effectively now choose whether or not to speak it, or to use a normal language. - add: Traits can now add Languages directly. - add: Traits can now remove Languages directly. --------- Signed-off-by: VMSolidus Co-authored-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Co-authored-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Chemistry/ReagentEffects/MakeSentient.cs | 2 +- .../Language/LanguageSystem.Networking.cs | 7 +- Content.Server/Language/LanguageSystem.cs | 70 +++++++++---------- Content.Server/Language/TranslatorSystem.cs | 10 +-- .../Traits/Assorted/ForeignerTraitSystem.cs | 2 +- .../LanguageKnowledgeModifierComponent.cs | 23 ------ .../LanguageKnowledgeModifierSystem.cs | 35 ---------- Content.Server/Traits/TraitSystem.cs | 70 +++++++++++++++++++ .../Traits/Prototypes/TraitPrototype.cs | 24 +++++++ .../Entities/Structures/Specific/oracle.yml | 7 ++ .../Structures/Research/sophicscribe.yml | 7 ++ Resources/Prototypes/Traits/skills.yml | 10 ++- 12 files changed, 154 insertions(+), 113 deletions(-) delete mode 100644 Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs delete mode 100644 Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs diff --git a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs b/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs index 5654f9067b..a84e21b997 100644 --- a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs +++ b/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs @@ -41,7 +41,7 @@ public override void Effect(ReagentEffectArgs args) if (!knowledge.SpokenLanguages.Contains(fallback)) knowledge.SpokenLanguages.Add(fallback); - IoCManager.Resolve().GetEntitySystem().UpdateEntityLanguages(uid, speaker); + IoCManager.Resolve().GetEntitySystem().UpdateEntityLanguages(uid); // Stops from adding a ghost role to things like people who already have a mind if (entityManager.TryGetComponent(uid, out var mindContainer) && mindContainer.HasMind) diff --git a/Content.Server/Language/LanguageSystem.Networking.cs b/Content.Server/Language/LanguageSystem.Networking.cs index 572e2961fd..5f7f274273 100644 --- a/Content.Server/Language/LanguageSystem.Networking.cs +++ b/Content.Server/Language/LanguageSystem.Networking.cs @@ -64,12 +64,7 @@ private void SendLanguageStateToClient(ICommonSession session, LanguageSpeakerCo // TODO this is really stupid and can be avoided if we just make everything shared... private void SendLanguageStateToClient(EntityUid uid, ICommonSession session, LanguageSpeakerComponent? component = null) { - var isUniversal = HasComp(uid); - if (!isUniversal) - Resolve(uid, ref component, logMissing: false); - - // I really don't want to call 3 getter methods here, so we'll just have this slightly hardcoded solution - var message = isUniversal || component == null + var message = !Resolve(uid, ref component, logMissing: false) ? new LanguagesUpdatedMessage(UniversalPrototype, [UniversalPrototype], [UniversalPrototype]) : new LanguagesUpdatedMessage(component.CurrentLanguage, component.SpokenLanguages, component.UnderstoodLanguages); diff --git a/Content.Server/Language/LanguageSystem.cs b/Content.Server/Language/LanguageSystem.cs index e68489e9e2..a1c30997e2 100644 --- a/Content.Server/Language/LanguageSystem.cs +++ b/Content.Server/Language/LanguageSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Language.Events; using Content.Shared.Language; using Content.Shared.Language.Components; -using Content.Shared.Language.Events; using Content.Shared.Language.Systems; using UniversalLanguageSpeakerComponent = Content.Shared.Language.Components.UniversalLanguageSpeakerComponent; @@ -16,8 +15,19 @@ public override void Initialize() InitializeNet(); SubscribeLocalEvent(OnInitLanguageSpeaker); + SubscribeLocalEvent(OnUniversalInit); + SubscribeLocalEvent(OnUniversalShutdown); } + private void OnUniversalShutdown(EntityUid uid, UniversalLanguageSpeakerComponent component, ComponentShutdown args) + { + RemoveLanguage(uid, UniversalPrototype); + } + + private void OnUniversalInit(EntityUid uid, UniversalLanguageSpeakerComponent component, MapInitEvent args) + { + AddLanguage(uid, UniversalPrototype); + } #region public api @@ -48,10 +58,9 @@ public bool CanSpeak(EntityUid speaker, string language, LanguageSpeakerComponen /// public LanguagePrototype GetLanguage(EntityUid speaker, LanguageSpeakerComponent? component = null) { - if (HasComp(speaker) || !Resolve(speaker, ref component, logMissing: false)) - return Universal; // Serves both as a fallback and uhhh something (TODO: fix this comment) - - if (string.IsNullOrEmpty(component.CurrentLanguage) || !_prototype.TryIndex(component.CurrentLanguage, out var proto)) + if (!Resolve(speaker, ref component, logMissing: false) + || string.IsNullOrEmpty(component.CurrentLanguage) + || !_prototype.TryIndex(component.CurrentLanguage, out var proto)) return Universal; return proto; @@ -63,13 +72,10 @@ public LanguagePrototype GetLanguage(EntityUid speaker, LanguageSpeakerComponent /// Typically, checking is sufficient. public List GetSpokenLanguages(EntityUid uid) { - if (HasComp(uid)) - return [UniversalPrototype]; + if (!TryComp(uid, out var component)) + return []; - if (TryComp(uid, out var component)) - return component.SpokenLanguages; - - return []; + return component.SpokenLanguages; } /// @@ -78,21 +84,17 @@ public List GetSpokenLanguages(EntityUid uid) /// Typically, checking is sufficient. public List GetUnderstoodLanguages(EntityUid uid) { - if (HasComp(uid)) - return [UniversalPrototype]; // This one is tricky because... well, they understand all of them, not just one. - - if (TryComp(uid, out var component)) - return component.UnderstoodLanguages; + if (!TryComp(uid, out var component)) + return []; - return []; + return component.UnderstoodLanguages; } public void SetLanguage(EntityUid speaker, string language, LanguageSpeakerComponent? component = null) { - if (!CanSpeak(speaker, language) || (HasComp(speaker) && language != UniversalPrototype)) - return; - - if (!Resolve(speaker, ref component) || component.CurrentLanguage == language) + if (!CanSpeak(speaker, language) + || !Resolve(speaker, ref component) + || component.CurrentLanguage == language) return; component.CurrentLanguage = language; @@ -106,12 +108,10 @@ public void AddLanguage( EntityUid uid, string language, bool addSpoken = true, - bool addUnderstood = true, - LanguageKnowledgeComponent? knowledge = null, - LanguageSpeakerComponent? speaker = null) + bool addUnderstood = true) { - if (knowledge == null) - knowledge = EnsureComp(uid); + EnsureComp(uid, out var knowledge); + EnsureComp(uid); if (addSpoken && !knowledge.SpokenLanguages.Contains(language)) knowledge.SpokenLanguages.Add(language); @@ -119,7 +119,7 @@ public void AddLanguage( if (addUnderstood && !knowledge.UnderstoodLanguages.Contains(language)) knowledge.UnderstoodLanguages.Add(language); - UpdateEntityLanguages(uid, speaker); + UpdateEntityLanguages(uid); } /// @@ -129,12 +129,10 @@ public void RemoveLanguage( EntityUid uid, string language, bool removeSpoken = true, - bool removeUnderstood = true, - LanguageKnowledgeComponent? knowledge = null, - LanguageSpeakerComponent? speaker = null) + bool removeUnderstood = true) { - if (knowledge == null) - knowledge = EnsureComp(uid); + if (!TryComp(uid, out var knowledge)) + return; if (removeSpoken) knowledge.SpokenLanguages.Remove(language); @@ -142,7 +140,7 @@ public void RemoveLanguage( if (removeUnderstood) knowledge.UnderstoodLanguages.Remove(language); - UpdateEntityLanguages(uid, speaker); + UpdateEntityLanguages(uid); } /// @@ -168,9 +166,9 @@ public bool EnsureValidLanguage(EntityUid entity, LanguageSpeakerComponent? comp /// /// Immediately refreshes the cached lists of spoken and understood languages for the given entity. /// - public void UpdateEntityLanguages(EntityUid entity, LanguageSpeakerComponent? languages = null) + public void UpdateEntityLanguages(EntityUid entity) { - if (!Resolve(entity, ref languages)) + if (!TryComp(entity, out var languages)) return; var ev = new DetermineEntityLanguagesEvent(); @@ -205,7 +203,7 @@ private void OnInitLanguageSpeaker(EntityUid uid, LanguageSpeakerComponent compo if (string.IsNullOrEmpty(component.CurrentLanguage)) component.CurrentLanguage = component.SpokenLanguages.FirstOrDefault(UniversalPrototype); - UpdateEntityLanguages(uid, component); + UpdateEntityLanguages(uid); } #endregion diff --git a/Content.Server/Language/TranslatorSystem.cs b/Content.Server/Language/TranslatorSystem.cs index a893993e88..c48b93a393 100644 --- a/Content.Server/Language/TranslatorSystem.cs +++ b/Content.Server/Language/TranslatorSystem.cs @@ -85,8 +85,8 @@ private void OnTranslatorParentChanged(EntityUid translator, HandheldTranslatorC // If that is not the case, then OnProxyDetermineLanguages will remove this translator from HoldsTranslatorComponent.Translators. Timer.Spawn(0, () => { - if (Exists(args.OldParent) && TryComp(args.OldParent, out var speaker)) - _language.UpdateEntityLanguages(args.OldParent.Value, speaker); + if (Exists(args.OldParent) && HasComp(args.OldParent)) + _language.UpdateEntityLanguages(args.OldParent.Value); }); } @@ -108,7 +108,7 @@ private void OnTranslatorToggle(EntityUid translator, HandheldTranslatorComponen { // The first new spoken language added by this translator, or null var firstNewLanguage = translatorComp.SpokenLanguages.FirstOrDefault(it => !languageComp.SpokenLanguages.Contains(it)); - _language.UpdateEntityLanguages(holder, languageComp); + _language.UpdateEntityLanguages(holder); // Update the current language of the entity if necessary if (isEnabled && translatorComp.SetLanguageOnInteract && firstNewLanguage is {}) @@ -131,8 +131,8 @@ private void OnPowerCellSlotEmpty(EntityUid translator, HandheldTranslatorCompon _powerCell.SetPowerCellDrawEnabled(translator, false); OnAppearanceChange(translator, component); - if (_containers.TryGetContainingContainer(translator, out var holderCont) && TryComp(holderCont.Owner, out var languageComp)) - _language.UpdateEntityLanguages(holderCont.Owner, languageComp); + if (_containers.TryGetContainingContainer(translator, out var holderCont) && HasComp(holderCont.Owner)) + _language.UpdateEntityLanguages(holderCont.Owner); } private void CopyLanguages(BaseTranslatorComponent from, DetermineEntityLanguagesEvent to, LanguageKnowledgeComponent knowledge) diff --git a/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs index 58e974227c..2c7274a13d 100644 --- a/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs +++ b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs @@ -46,7 +46,7 @@ private void OnSpawn(Entity entity, ref ComponentInit a if (TryGiveTranslator(entity.Owner, entity.Comp.BaseTranslator, entity.Comp.BaseLanguage, alternateLanguage, out var translator)) { - _languages.RemoveLanguage(entity, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand, knowledge); + _languages.RemoveLanguage(entity, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand); } } diff --git a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs b/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs deleted file mode 100644 index 170dae40fa..0000000000 --- a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Shared.Language; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Server.Traits.Assorted; - -/// -/// Used for traits that modify entities' language knowledge. -/// -[RegisterComponent] -public sealed partial class LanguageKnowledgeModifierComponent : Component -{ - /// - /// List of languages this entity will learn to speak. - /// - [DataField("speaks")] - public List NewSpokenLanguages = new(); - - /// - /// List of languages this entity will learn to understand. - /// - [DataField("understands")] - public List NewUnderstoodLanguages = new(); -} diff --git a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs b/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs deleted file mode 100644 index 9053c9404f..0000000000 --- a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Linq; -using Content.Server.Language; -using Content.Shared.Language.Components; - -namespace Content.Server.Traits.Assorted; - -public sealed class LanguageKnowledgeModifierSystem : EntitySystem -{ - [Dependency] private readonly LanguageSystem _languages = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnStartup); - } - - private void OnStartup(Entity entity, ref ComponentInit args) - { - if (!TryComp(entity, out var knowledge)) - { - Log.Warning($"Entity {entity.Owner} does not have a LanguageKnowledge but has a LanguageKnowledgeModifier!"); - return; - } - - foreach (var spokenLanguage in entity.Comp.NewSpokenLanguages) - { - _languages.AddLanguage(entity, spokenLanguage, true, false, knowledge); - } - - foreach (var understoodLanguage in entity.Comp.NewUnderstoodLanguages) - { - _languages.AddLanguage(entity, understoodLanguage, false, true, knowledge); - } - } -} diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 39812f65b6..bd36b4ecef 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Utility; using Content.Server.Abilities.Psionics; using Content.Shared.Psionics; +using Content.Server.Language; using Content.Shared.Mood; namespace Content.Server.Traits; @@ -26,6 +27,7 @@ public sealed class TraitSystem : EntitySystem [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly LanguageSystem _languageSystem = default!; public override void Initialize() { @@ -66,6 +68,8 @@ public void AddTrait(EntityUid uid, TraitPrototype traitPrototype) AddTraitComponents(uid, traitPrototype); AddTraitActions(uid, traitPrototype); AddTraitPsionics(uid, traitPrototype); + AddTraitLanguage(uid, traitPrototype); + RemoveTraitLanguage(uid, traitPrototype); AddTraitMoodlets(uid, traitPrototype); } @@ -142,6 +146,72 @@ public void AddTraitPsionics(EntityUid uid, TraitPrototype traitPrototype) _psionicAbilities.InitializePsionicPower(uid, psionicPower, false); } + /// + /// Initialize languages given by a Trait. + /// + private void AddTraitLanguage(EntityUid uid, TraitPrototype traitPrototype) + { + AddTraitLanguagesSpoken(uid, traitPrototype); + AddTraitLanguagesUnderstood(uid, traitPrototype); + } + + /// + /// If a trait includes any Spoken Languages, this sends them to LanguageSystem to be initialized. + /// + public void AddTraitLanguagesSpoken(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.LanguagesSpoken is null) + return; + + foreach (var language in traitPrototype.LanguagesSpoken) + _languageSystem.AddLanguage(uid, language, true, false); + } + + /// + /// If a trait includes any Understood Languages, this sends them to LanguageSystem to be initialized. + /// + public void AddTraitLanguagesUnderstood(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.LanguagesUnderstood is null) + return; + + foreach (var language in traitPrototype.LanguagesUnderstood) + _languageSystem.AddLanguage(uid, language, false, true); + } + + /// + /// Remove Languages given by a Trait. + /// + private void RemoveTraitLanguage(EntityUid uid, TraitPrototype traitPrototype) + { + RemoveTraitLanguagesSpoken(uid, traitPrototype); + RemoveTraitLanguagesUnderstood(uid, traitPrototype); + } + + /// + /// Removes any Spoken Languages if defined by a trait. + /// + public void RemoveTraitLanguagesSpoken(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.RemoveLanguagesSpoken is null) + return; + + foreach (var language in traitPrototype.RemoveLanguagesSpoken) + _languageSystem.RemoveLanguage(uid, language, true, false); + } + + /// + /// Removes any Understood Languages if defined by a trait. + /// + public void RemoveTraitLanguagesUnderstood(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.RemoveLanguagesUnderstood is null) + return; + + foreach (var language in traitPrototype.RemoveLanguagesUnderstood) + _languageSystem.RemoveLanguage(uid, language, false, true); + } + /// /// If a trait includes any moodlets, this adds the moodlets to the receiving entity. /// While I can't stop you, you shouldn't use this to add temporary moodlets. diff --git a/Content.Shared/Traits/Prototypes/TraitPrototype.cs b/Content.Shared/Traits/Prototypes/TraitPrototype.cs index cd4b02a1e6..7c0e429a69 100644 --- a/Content.Shared/Traits/Prototypes/TraitPrototype.cs +++ b/Content.Shared/Traits/Prototypes/TraitPrototype.cs @@ -58,6 +58,30 @@ public sealed partial class TraitPrototype : IPrototype [DataField] public List? PsionicPowers { get; private set; } = default!; + /// + /// The list of all Spoken Languages that this trait adds. + /// + [DataField] + public List? LanguagesSpoken { get; private set; } = default!; + + /// + /// The list of all Understood Languages that this trait adds. + /// + [DataField] + public List? LanguagesUnderstood { get; private set; } = default!; + + /// + /// The list of all Spoken Languages that this trait removes. + /// + [DataField] + public List? RemoveLanguagesSpoken { get; private set; } = default!; + + /// + /// The list of all Understood Languages that this trait removes. + /// + [DataField] + public List? RemoveLanguagesUnderstood { get; private set; } = default!; + /// /// The list of all Moodlets that this trait adds. /// diff --git a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml index b60906b55e..2161c6d80d 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml @@ -67,6 +67,13 @@ demandWhitelist: components: - Item + - type: LanguageSpeaker + currentLanguage: GalacticCommon + - type: LanguageKnowledge + speaks: + - GalacticCommon + understands: + - GalacticCommon - type: weightedRandomEntity diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index 1a065b001c..533cf31499 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -48,3 +48,10 @@ - type: GuideHelp guides: - Psionics + - type: LanguageSpeaker + currentLanguage: GalacticCommon + - type: LanguageKnowledge + speaks: + - GalacticCommon + understands: + - GalacticCommon diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index fa79666c7a..015c9f5023 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -102,12 +102,10 @@ id: SignLanguage category: Visual points: -2 - components: - - type: LanguageKnowledgeModifier - speaks: - - Sign - understands: - - Sign + languagesSpoken: + - Sign + languagesUnderstood: + - Sign - type: trait id: Voracious From 80daa94e6a6e0875264053da48ed9e827a5dcbe0 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 20 Sep 2024 01:48:18 +0000 Subject: [PATCH 04/10] Automatic Changelog Update (#899) --- Resources/Changelog/Changelog.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a233a61a2d..a458042c4a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -6467,3 +6467,18 @@ Entries: id: 6372 time: '2024-09-19T20:37:51.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/933 +- author: VMSolidus + changes: + - type: Fix + message: >- + UniversalLanguageSpeaker(And Xenoglossy by extension) will now appear in + your language menu alongside other known languages, rather than replace + all known languages. You can effectively now choose whether or not to + speak it, or to use a normal language. + - type: Add + message: Traits can now add Languages directly. + - type: Add + message: Traits can now remove Languages directly. + id: 6373 + time: '2024-09-20T01:47:52.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/899 From 7119dd00376419f88eaa7d439529eb90daf99172 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Thu, 19 Sep 2024 22:48:43 -0400 Subject: [PATCH 05/10] NyanoCombat Part 4: MeleeSystem Refactor Part 2, Electric Boogaloo (#934) # Description Surprise motherfucker, here's a massive fucking refactor of SharedMeleeWeaponSystem. The MeleeWeaponComponent now allows for melee weapons to directly configure how and what members of the ContestsSystem they wish to utilize for influencing damage. Additionally, the damage of a melee weapon modified by Contests is now also visible when examined(Although this only reflects damage as per the current condition of the user). # Changelog :cl: - add: Melee Weapons can now individually define their interactions with the ContestsSystem. - add: Added the ContestArgs type, allowing arbitrarily any component to contain a list of arguments for ContestSystems. - add: Added the ContestConstructor, a new type of meta-Contest that enables other systems to use components to define all possible contest behaviors, rather than needing to hardcode specific interactions. --- .../Contests/ContestsSystem.Utilities.cs | 262 ++++++++++++++++++ .../Weapons/Melee/MeleeWeaponComponent.cs | 15 + .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 13 +- 3 files changed, 283 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Contests/ContestsSystem.Utilities.cs b/Content.Shared/Contests/ContestsSystem.Utilities.cs index 42a69bff47..0278d75e94 100644 --- a/Content.Shared/Contests/ContestsSystem.Utilities.cs +++ b/Content.Shared/Contests/ContestsSystem.Utilities.cs @@ -1,4 +1,5 @@ using Content.Shared.CCVar; +using Robust.Shared.Serialization; namespace Content.Shared.Contests; public sealed partial class ContestsSystem @@ -18,4 +19,265 @@ private bool ContestClampOverride(bool bypassClamp) { return _cfg.GetCVar(CCVars.AllowClampOverride) && bypassClamp; } + + /// + /// Constructor for feeding options from a given set of ContestArgs into the ContestsSystem. + /// Just multiply by this and give it a user EntityUid and a ContestArgs variable. That's all you need to know. + /// + public float ContestConstructor(EntityUid user, ContestArgs args) + { + if (!_cfg.GetCVar(CCVars.DoContestsSystem)) + return 1; + + if (!args.DoEveryInteraction) + return args.DoMassInteraction ? ((!args.MassDisadvantage + ? MassContest(user, args.MassBypassClamp, args.MassRangeModifier) + : 1 / MassContest(user, args.MassBypassClamp, args.MassRangeModifier)) + + args.MassOffset) + : 1 + * (args.DoStaminaInteraction ? ((!args.StaminaDisadvantage + ? StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier) + : 1 / StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier)) + + args.StaminaOffset) + : 1) + * (args.DoHealthInteraction ? ((!args.HealthDisadvantage + ? HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier) + : 1 / HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier)) + + args.HealthOffset) + : 1) + * (args.DoMindInteraction ? ((!args.MindDisadvantage + ? MindContest(user, args.MindBypassClamp, args.MindRangeModifier) + : 1 / MindContest(user, args.MindBypassClamp, args.MindRangeModifier)) + + args.MindOffset) + : 1) + * (args.DoMoodInteraction ? ((!args.MoodDisadvantage + ? MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier) + : 1 / MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier)) + + args.MoodOffset) + : 1); + + var everyContest = EveryContest(user, + args.MassBypassClamp, + args.StaminaBypassClamp, + args.HealthBypassClamp, + args.MindBypassClamp, + args.MoodBypassClamp, + args.MassRangeModifier, + args.StaminaRangeModifier, + args.HealthRangeModifier, + args.MindRangeModifier, + args.MoodRangeModifier, + args.EveryMassWeight, + args.EveryStaminaWeight, + args.EveryHealthWeight, + args.EveryMindWeight, + args.EveryMoodWeight, + args.EveryInteractionSumOrMultiply); + + return !args.EveryDisadvantage ? everyContest : 1 / everyContest; + } } + +[Serializable, NetSerializable, DataDefinition] +public sealed partial class ContestArgs +{ + /// + /// Controls whether this melee weapon allows for mass to factor into damage. + /// + [DataField] + public bool DoMassInteraction; + + /// + /// When true, mass provides a disadvantage. + /// + [DataField] + public bool MassDisadvantage; + + /// + /// When true, mass contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MassBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float MassRangeModifier = 1; + + /// + /// The output of a mass contest is increased by this amount. + /// + [DataField] + public float MassOffset; + + /// + /// Controls whether this melee weapon allows for stamina to factor into damage. + /// + [DataField] + public bool DoStaminaInteraction; + + /// + /// When true, stamina provides a disadvantage. + /// + [DataField] + public bool StaminaDisadvantage; + + /// + /// When true, stamina contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool StaminaBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float StaminaRangeModifier = 1; + + /// + /// The output of a stamina contest is increased by this amount. + /// + [DataField] + public float StaminaOffset; + + /// + /// Controls whether this melee weapon allows for health to factor into damage. + /// + [DataField] + public bool DoHealthInteraction; + + /// + /// When true, health contests provide a disadvantage. + /// + [DataField] + public bool HealthDisadvantage; + + /// + /// When true, health contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool HealthBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float HealthRangeModifier = 1; + + /// + /// The output of health contests is increased by this amount. + /// + [DataField] + public float HealthOffset; + + /// + /// Controls whether this melee weapon allows for psychic casting stats to factor into damage. + /// + [DataField] + public bool DoMindInteraction; + + /// + /// When true, high psychic casting stats provide a disadvantage. + /// + [DataField] + public bool MindDisadvantage; + + /// + /// When true, mind contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MindBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mind contests for melee. + /// + [DataField] + public float MindRangeModifier = 1; + + /// + /// The output of a mind contest is increased by this amount. + /// + [DataField] + public float MindOffset; + + /// + /// Controls whether this melee weapon allows mood to factor into damage. + /// + [DataField] + public bool DoMoodInteraction; + + /// + /// When true, mood provides a disadvantage. + /// + [DataField] + public bool MoodDisadvantage; + + /// + /// When true, mood contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MoodBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mood contests for melee. + /// + [DataField] + public float MoodRangeModifier = 1; + + /// + /// The output of mood contests is increased by this amount. + /// + [DataField] + public float MoodOffset; + + /// + /// Enables the EveryContest interaction for a melee weapon. + /// IF YOU PUT THIS ON ANY WEAPON OTHER THAN AN ADMEME, I WILL COME TO YOUR HOUSE AND SEND YOU TO MEET YOUR CREATOR WHEN THE PLAYERS COMPLAIN. + /// + [DataField] + public bool DoEveryInteraction; + + /// + /// When true, EveryContest provides a disadvantage. + /// + [DataField] + public bool EveryDisadvantage; + + /// + /// How much Mass is considered for an EveryContest. + /// + [DataField] + public float EveryMassWeight = 1; + + /// + /// How much Stamina is considered for an EveryContest. + /// + [DataField] + public float EveryStaminaWeight = 1; + + /// + /// How much Health is considered for an EveryContest. + /// + [DataField] + public float EveryHealthWeight = 1; + + /// + /// How much psychic casting stats are considered for an EveryContest. + /// + [DataField] + public float EveryMindWeight = 1; + + /// + /// How much mood is considered for an EveryContest. + /// + [DataField] + public float EveryMoodWeight = 1; + + /// + /// When true, the EveryContest sums the results of all contests rather than multiplying them, + /// probably giving you a very, very, very large multiplier... + /// + [DataField] + public bool EveryInteractionSumOrMultiply; +} \ No newline at end of file diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index c242d448f2..43e3096d15 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Robust.Shared.Audio; @@ -156,6 +157,20 @@ public sealed partial class MeleeWeaponComponent : Component /// [DataField, AutoNetworkedField] public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit"); + + /// + /// Arguments for the MeleeContestInteractions constructor + /// + [DataField] + public ContestArgs ContestArgs = new ContestArgs + { + DoStaminaInteraction = true, + StaminaDisadvantage = true, + StaminaRangeModifier = 2, + StaminaOffset = 0.25f, + DoHealthInteraction = true, + HealthRangeModifier = 1.5f, + }; } /// diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index b5a537b7e1..fd77ad31a4 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -32,7 +32,7 @@ namespace Content.Shared.Weapons.Melee; -public abstract class SharedMeleeWeaponSystem : EntitySystem +public abstract partial class SharedMeleeWeaponSystem : EntitySystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] protected readonly ActionBlockerSystem Blocker = default!; @@ -225,6 +225,9 @@ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponCompo var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user); RaiseLocalEvent(uid, ref ev); + if (component.ContestArgs is not null) + ev.Damage *= _contests.ContestConstructor(user, component.ContestArgs); + return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); } @@ -249,9 +252,7 @@ public FixedPoint2 GetHeavyDamageModifier(EntityUid uid, EntityUid user, MeleeWe return ev.DamageModifier * ev.Multipliers - * component.HeavyDamageBaseModifier - * _contests.StaminaContest(user, false, 2f) //Taking stamina damage reduces wide swing damage by up to 50% - / _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more wide swing damage + * component.HeavyDamageBaseModifier; } public bool TryGetWeapon(EntityUid entity, out EntityUid weaponUid, [NotNullWhen(true)] out MeleeWeaponComponent? melee) @@ -440,9 +441,7 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) { - var damage = GetDamage(meleeUid, user, component) - * _contests.StaminaContest(user) //Taking stamina damage reduces light attack damage by up to 25% - / _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more damage; + var damage = GetDamage(meleeUid, user, component); var target = GetEntity(ev.Target); // For consistency with wide attacks stuff needs damageable. From c1aef1b4394b76a936cf36e9a002aa0f63591669 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Fri, 20 Sep 2024 02:49:07 +0000 Subject: [PATCH 06/10] Automatic Changelog Update (#934) --- Resources/Changelog/Changelog.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a458042c4a..a3fe3c90d2 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -6482,3 +6482,21 @@ Entries: id: 6373 time: '2024-09-20T01:47:52.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/899 +- author: VMSolidus + changes: + - type: Add + message: >- + Melee Weapons can now individually define their interactions with the + ContestsSystem. + - type: Add + message: >- + Added the ContestArgs type, allowing arbitrarily any component to + contain a list of arguments for ContestSystems. + - type: Add + message: >- + Added the ContestConstructor, a new type of meta-Contest that enables + other systems to use components to define all possible contest + behaviors, rather than needing to hardcode specific interactions. + id: 6374 + time: '2024-09-20T02:48:43.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/934 From abbb7162e0235f00eb82583686b8a3b4e5527f57 Mon Sep 17 00:00:00 2001 From: Evgencheg <7064926@gmail.com> Date: Fri, 20 Sep 2024 12:40:42 +0300 Subject: [PATCH 07/10] =?UTF-8?q?=D1=86=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Prototypes/Traits/skills.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index a9cf3387ec..f1f133d0f8 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -280,7 +280,7 @@ - type: trait id: TrapAvoider category: Physical - points: -2 + points: -6 components: - type: StepTriggerImmune whitelist: From 76932ff5b84de40a1fc9b9dcc717b99d17c74337 Mon Sep 17 00:00:00 2001 From: Evgencheg <7064926@gmail.com> Date: Fri, 20 Sep 2024 12:52:02 +0300 Subject: [PATCH 08/10] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=B2=D1=80?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_LostParadise/Traits/languages.yml | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Resources/Prototypes/_LostParadise/Traits/languages.yml b/Resources/Prototypes/_LostParadise/Traits/languages.yml index fe75ee70ee..bf3aa4a098 100644 --- a/Resources/Prototypes/_LostParadise/Traits/languages.yml +++ b/Resources/Prototypes/_LostParadise/Traits/languages.yml @@ -2,12 +2,10 @@ id: ElfLanguage category: Speech points: 0 - components: - - type: LanguageKnowledgeModifier - speaks: - - ElfLang - understands: - - ElfLang + languagesSpoken: + - ElfLang + languagesUnderstood: + - ElfLang requirements: - !type:CharacterSpeciesRequirement inverted: false @@ -25,12 +23,10 @@ id: DwarfLanguage category: Speech points: 0 - components: - - type: LanguageKnowledgeModifier - speaks: - - DwarfLang - understands: - - DwarfLang + languagesSpoken: + - DwarfLang + languagesUnderstood: + - DwarfLang requirements: - !type:CharacterSpeciesRequirement inverted: false From d94e7d983e04ac16a2af7a3c94f8966e42e3071c Mon Sep 17 00:00:00 2001 From: Evgencheg <7064926@gmail.com> Date: Fri, 20 Sep 2024 13:05:30 +0300 Subject: [PATCH 09/10] =?UTF-8?q?=D0=BD=D1=83=20=D0=BD=D0=B0=D1=84=D0=B8?= =?UTF-8?q?=D0=B3=20=D0=B2=D0=B0=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Content.Shared/CCVar/CCVars.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index eeb35ad22e..183452ac5f 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -436,7 +436,7 @@ public static readonly CVarDef /// The prototype to use for secret weights. /// public static readonly CVarDef SecretWeightPrototype = - CVarDef.Create("game.secret_weight_prototype", "LPPSecret", CVar.SERVERONLY); + CVarDef.Create("game.secret_weight_prototype", "Secret", CVar.SERVERONLY); /// /// The id of the sound collection to randomly choose a sound from and play when the round ends. From f5dade18f665afafb3c44962ebabeadde5216d66 Mon Sep 17 00:00:00 2001 From: Evgencheg <7064926@gmail.com> Date: Fri, 20 Sep 2024 13:21:45 +0300 Subject: [PATCH 10/10] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D1=81=D1=8C=20=D0=BF=D0=BE=D0=B6=D0=B0=D0=BB=D1=83=D0=B9=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B0=D0=B0=D0=B0=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Prototypes/secret_weights.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Resources/Prototypes/secret_weights.yml b/Resources/Prototypes/secret_weights.yml index ad383a76d1..d0f437cfed 100644 --- a/Resources/Prototypes/secret_weights.yml +++ b/Resources/Prototypes/secret_weights.yml @@ -1,9 +1,8 @@ - type: weightedRandom id: Secret weights: - #Survival: 0.44 - Nukeops: 0.34 - Zombie: 0.17 - Traitor: 0.49 + Survival: 0.44 + Nukeops: 0.14 + Zombie: 0.03 + Traitor: 0.39 #Pirates: 0.15 #ahoy me bucko -