diff --git a/Content.Server/HeightAdjust/BloodstreamAdjustSystem.cs b/Content.Server/HeightAdjust/BloodstreamAdjustSystem.cs new file mode 100644 index 0000000000..92e03e0c11 --- /dev/null +++ b/Content.Server/HeightAdjust/BloodstreamAdjustSystem.cs @@ -0,0 +1,45 @@ +using Content.Server.Body.Components; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Contests; +using Content.Shared.HeightAdjust; +using Robust.Shared.Configuration; + +namespace Content.Server.HeightAdjust; + +public sealed class BloodstreamAdjustSystem : EntitySystem +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly ContestsSystem _contests = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + + public override void Initialize() + { + SubscribeLocalEvent((uid, comp, _) => TryAdjustBloodstream((uid, comp))); + SubscribeLocalEvent((uid, comp, _) => TryAdjustBloodstream((uid, comp))); + } + + /// + /// Adjusts the bloodstream of the specified entity based on the settings provided by the component. + /// + public bool TryAdjustBloodstream(Entity ent) + { + if (!TryComp(ent, out var bloodstream) + || !_solutionContainer.TryGetSolution(ent.Owner, bloodstream.BloodSolutionName, out var bloodSolutionEnt) + || !_config.GetCVar(CCVars.HeightAdjustModifiesBloodstream)) + return false; + + var bloodSolution = bloodSolutionEnt.Value.Comp.Solution; + + var factor = Math.Pow(_contests.MassContest(ent, bypassClamp: true, rangeFactor: 4f), ent.Comp.Power); + factor = Math.Clamp(factor, ent.Comp.Min, ent.Comp.Max); + + var newVolume = bloodstream.BloodMaxVolume * factor; + var newBloodLevel = bloodSolution.FillFraction * newVolume; + bloodSolution.MaxVolume = newVolume; + bloodSolution.SetContents([new ReagentQuantity(bloodstream.BloodReagent, newBloodLevel, null)], false); + + return true; + } +} diff --git a/Content.Server/HeightAdjust/BloodstreamAffectedByMassComponent.cs b/Content.Server/HeightAdjust/BloodstreamAffectedByMassComponent.cs new file mode 100644 index 0000000000..f6c3a0e250 --- /dev/null +++ b/Content.Server/HeightAdjust/BloodstreamAffectedByMassComponent.cs @@ -0,0 +1,26 @@ +using Content.Server.Body.Components; + +namespace Content.Server.HeightAdjust; + +/// +/// When applied to a humanoid or any mob, adjusts their blood level based on the mass contest between them +/// and an average humanoid. +///
+/// The formula for the resulting bloodstream volume is V = BloodMaxVolume * MassContest^Power +/// clamped between the specified Min and Max values. +///
+[RegisterComponent] +public sealed partial class BloodstreamAffectedByMassComponent : Component +{ + /// + /// Minimum and maximum resulting volume factors. A minimum value of 0.5 means that the resulting volume will be at least 50% of the original. + /// + [DataField] + public float Min = 1 / 3f, Max = 3f; + + /// + /// The power to which the outcome of the mass contest will be risen. + /// + [DataField] + public float Power = 1f; +} diff --git a/Content.Server/InteractionVerbs/Actions/MoodAction.cs b/Content.Server/InteractionVerbs/Actions/MoodAction.cs new file mode 100644 index 0000000000..5b53c6ca18 --- /dev/null +++ b/Content.Server/InteractionVerbs/Actions/MoodAction.cs @@ -0,0 +1,42 @@ +using Content.Shared.InteractionVerbs; +using Content.Shared.Mood; +using Robust.Shared.Prototypes; + +namespace Content.Server.InteractionVerbs.Actions; + +/// +/// An action that adds a moodlet to the target, or removes one. +/// +[Serializable] +public sealed partial class MoodAction : InteractionAction +{ + [DataField(required: true)] + public ProtoId Effect; + + /// + /// Parameters for the . Only used if is false. + /// + [DataField] + public float Modifier = 1f, Offset = 0f; + + /// + /// If true, the moodlet will be removed. Otherwise, it will be added. + /// + [DataField] + public bool Remove = false; + + public override bool CanPerform(InteractionArgs args, InteractionVerbPrototype proto, bool isBefore, VerbDependencies deps) + { + return true; + } + + public override bool Perform(InteractionArgs args, InteractionVerbPrototype proto, VerbDependencies deps) + { + if (Remove) + deps.EntMan.EventBus.RaiseLocalEvent(args.Target, new MoodRemoveEffectEvent(Effect)); + else + deps.EntMan.EventBus.RaiseLocalEvent(args.Target, new MoodEffectEvent(Effect, Modifier, Offset)); + + return true; // Mood system is shitcode so we can't even know if the effect was added or anything + } +} diff --git a/Content.Server/Language/TranslatorSystem.cs b/Content.Server/Language/TranslatorSystem.cs index 24f4cb1729..6cdca12440 100644 --- a/Content.Server/Language/TranslatorSystem.cs +++ b/Content.Server/Language/TranslatorSystem.cs @@ -8,13 +8,13 @@ using Content.Shared.Language.Systems; using Content.Shared.PowerCell; using Content.Shared.Language.Components.Translators; +using Robust.Shared.Containers; namespace Content.Server.Language; -// This does not support holding multiple translators at once. -// That shouldn't be an issue for now, but it needs to be fixed later. public sealed class TranslatorSystem : SharedTranslatorSystem { + [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly LanguageSystem _language = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; @@ -24,65 +24,64 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnDetermineLanguages); - SubscribeLocalEvent(OnDetermineLanguages); - SubscribeLocalEvent(OnDetermineLanguages); + SubscribeLocalEvent(OnProxyDetermineLanguages); + SubscribeLocalEvent(OnTranslatorInserted); + SubscribeLocalEvent(OnTranslatorRemoved); SubscribeLocalEvent(OnTranslatorToggle); SubscribeLocalEvent(OnPowerCellSlotEmpty); - - SubscribeLocalEvent(OnTranslatorInteract); - SubscribeLocalEvent(OnTranslatorDropped); } private void OnDetermineLanguages(EntityUid uid, IntrinsicTranslatorComponent component, DetermineEntityLanguagesEvent ev) { - if (!component.Enabled || !TryComp(uid, out var speaker)) + if (!component.Enabled + || component.LifeStage >= ComponentLifeStage.Removing + || !TryComp(uid, out var knowledge) + || !_powerCell.HasActivatableCharge(uid)) return; - if (!_powerCell.HasActivatableCharge(uid)) + CopyLanguages(component, ev, knowledge); + } + + private void OnProxyDetermineLanguages(EntityUid uid, HoldsTranslatorComponent component, DetermineEntityLanguagesEvent ev) + { + if (!TryComp(uid, out var knowledge)) return; - // The idea here is as follows: - // Required languages are languages that are required to operate the translator. - // The translator has a limited number of languages it can translate to and translate from. - // If the wielder understands the language of the translator, they will be able to understand translations provided by it - // If the wielder also speaks that language, they will be able to use it to translate their own speech by "speaking" in that language - var addSpoken = CheckLanguagesMatch(component.RequiredLanguages, speaker.SpokenLanguages, component.RequiresAllLanguages); - var addUnderstood = CheckLanguagesMatch(component.RequiredLanguages, speaker.UnderstoodLanguages, component.RequiresAllLanguages); + foreach (var (translator, translatorComp) in component.Translators.ToArray()) + { + if (!translatorComp.Enabled || !_powerCell.HasActivatableCharge(uid)) + continue; - if (addSpoken) - foreach (var language in component.SpokenLanguages) - ev.SpokenLanguages.Add(language); + if (!_containers.TryGetContainingContainer(translator, out var container) || container.Owner != uid) + { + component.Translators.RemoveWhere(it => it.Owner == translator); + continue; + } - if (addUnderstood) - foreach (var language in component.UnderstoodLanguages) - ev.UnderstoodLanguages.Add(language); + CopyLanguages(translatorComp, ev, knowledge); + } } - private void OnTranslatorInteract(EntityUid translator, HandheldTranslatorComponent component, InteractHandEvent args) + private void OnTranslatorInserted(EntityUid translator, HandheldTranslatorComponent component, EntGotInsertedIntoContainerMessage args) { - var holder = args.User; - if (!EntityManager.HasComponent(holder)) + if (args.Container.Owner is not {Valid: true} holder + || !EntityManager.HasComponent(holder)) return; var intrinsic = EnsureComp(holder); - UpdateBoundIntrinsicComp(component, intrinsic, component.Enabled); + intrinsic.Translators.Add((translator, component)); _language.UpdateEntityLanguages(holder); } - private void OnTranslatorDropped(EntityUid translator, HandheldTranslatorComponent component, DroppedEvent args) + private void OnTranslatorRemoved(EntityUid translator, HandheldTranslatorComponent component, EntGotRemovedFromContainerMessage args) { - var holder = args.User; - if (!EntityManager.TryGetComponent(holder, out var intrinsic)) + if (args.Container.Owner is not {Valid: true} holder + || !EntityManager.TryGetComponent(holder, out var intrinsic)) return; - if (intrinsic.Issuer == component) - { - intrinsic.Enabled = false; - RemCompDeferred(holder, intrinsic); - } - + intrinsic.Translators.RemoveWhere(it => it.Owner == translator); _language.UpdateEntityLanguages(holder); } @@ -93,53 +92,31 @@ private void OnTranslatorToggle(EntityUid translator, HandheldTranslatorComponen // This will show a popup if false var hasPower = _powerCell.HasDrawCharge(translator); + var isEnabled = !translatorComp.Enabled && hasPower; - if (Transform(args.Target).ParentUid is { Valid: true } holder + translatorComp.Enabled = isEnabled; + _powerCell.SetPowerCellDrawEnabled(translator, isEnabled); + + if (_containers.TryGetContainingContainer(translator, out var holderCont) + && holderCont.Owner is var holder && TryComp(holder, out var languageComp)) { - // This translator is held by a language speaker and thus has an intrinsic counterpart bound to it. - // Make sure it's up-to-date. - var intrinsic = EnsureComp(holder); - var isEnabled = !translatorComp.Enabled; - if (intrinsic.Issuer != translatorComp) - { - // The intrinsic comp wasn't owned by this handheld translator, so this wasn't the active translator. - // Thus, the intrinsic comp needs to be turned on regardless of its previous state. - intrinsic.Issuer = translatorComp; - isEnabled = true; - } - isEnabled &= hasPower; - - UpdateBoundIntrinsicComp(translatorComp, intrinsic, isEnabled); - translatorComp.Enabled = isEnabled; - _powerCell.SetPowerCellDrawEnabled(translator, isEnabled); - // 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); // Update the current language of the entity if necessary if (isEnabled && translatorComp.SetLanguageOnInteract && firstNewLanguage is {}) _language.SetLanguage(holder, firstNewLanguage, languageComp); } - else - { - // This is a standalone translator (e.g. lying on the ground), toggle its state. - translatorComp.Enabled = !translatorComp.Enabled && hasPower; - _powerCell.SetPowerCellDrawEnabled(translator, !translatorComp.Enabled && hasPower); - } OnAppearanceChange(translator, translatorComp); if (hasPower) { - var message = Loc.GetString( - translatorComp.Enabled - ? "translator-component-turnon" - : "translator-component-shutoff", - ("translator", translatorComp.Owner)); - _popup.PopupEntity(message, translatorComp.Owner, args.User); + var loc = isEnabled ? "translator-component-turnon" : "translator-component-shutoff"; + var message = Loc.GetString(loc, ("translator", translator)); + _popup.PopupEntity(message, translator, args.User); } } @@ -148,43 +125,20 @@ private void OnPowerCellSlotEmpty(EntityUid translator, HandheldTranslatorCompon component.Enabled = false; _powerCell.SetPowerCellDrawEnabled(translator, false); OnAppearanceChange(translator, component); - - if (Transform(translator).ParentUid is { Valid: true } holder - && TryComp(holder, out var languageComp)) - { - if (!EntityManager.TryGetComponent(holder, out var intrinsic)) - return; - - if (intrinsic.Issuer == component) - { - intrinsic.Enabled = false; - RemComp(holder, intrinsic); - } - - _language.UpdateEntityLanguages(holder, languageComp); - } } - /// - /// Copies the state from the handheld to the intrinsic component - /// - private void UpdateBoundIntrinsicComp(HandheldTranslatorComponent comp, HoldsTranslatorComponent intrinsic, bool isEnabled) + private void CopyLanguages(BaseTranslatorComponent from, DetermineEntityLanguagesEvent to, LanguageKnowledgeComponent knowledge) { - if (isEnabled) - { - intrinsic.SpokenLanguages = [..comp.SpokenLanguages]; - intrinsic.UnderstoodLanguages = [..comp.UnderstoodLanguages]; - intrinsic.RequiredLanguages = [..comp.RequiredLanguages]; - } - else - { - intrinsic.SpokenLanguages.Clear(); - intrinsic.UnderstoodLanguages.Clear(); - intrinsic.RequiredLanguages.Clear(); - } + var addSpoken = CheckLanguagesMatch(from.RequiredLanguages, knowledge.SpokenLanguages, from.RequiresAllLanguages); + var addUnderstood = CheckLanguagesMatch(from.RequiredLanguages, knowledge.UnderstoodLanguages, from.RequiresAllLanguages); + + if (addSpoken) + foreach (var language in from.SpokenLanguages) + to.SpokenLanguages.Add(language); - intrinsic.Enabled = isEnabled; - intrinsic.Issuer = comp; + if (addUnderstood) + foreach (var language in from.UnderstoodLanguages) + to.UnderstoodLanguages.Add(language); } /// diff --git a/Content.Server/Psionics/PsionicsCommands.cs b/Content.Server/Psionics/PsionicsCommands.cs index 5c273461f1..2894343c66 100644 --- a/Content.Server/Psionics/PsionicsCommands.cs +++ b/Content.Server/Psionics/PsionicsCommands.cs @@ -3,6 +3,9 @@ using Content.Shared.Abilities.Psionics; using Robust.Shared.Console; using Robust.Shared.Player; +using Content.Server.Abilities.Psionics; +using Robust.Shared.Prototypes; +using Content.Shared.Psionics; namespace Content.Server.Psionics; @@ -25,3 +28,38 @@ public async void Execute(IConsoleShell shell, string argStr, string[] args) } } } + +[AdminCommand(AdminFlags.Fun)] +public sealed class AddPsionicPowerCommand : IConsoleCommand +{ + public string Command => "addpsionicpower"; + public string Description => Loc.GetString("command-addpsionicpower-description"); + public string Help => Loc.GetString("command-addpsionicpower-help"); + public async void Execute(IConsoleShell shell, string argStr, string[] args) + { + var entMan = IoCManager.Resolve(); + var psionicPowers = IoCManager.Resolve(); + var protoMan = IoCManager.Resolve(); + + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-need-exactly-one-argument")); + return; + } + + if (!EntityUid.TryParse(args[0], out var uid)) + { + shell.WriteError(Loc.GetString("addpsionicpower-args-one-error")); + return; + } + + if (!protoMan.TryIndex(args[1], out var powerProto)) + { + shell.WriteError(Loc.GetString("addpsionicpower-args-two-error")); + return; + } + + entMan.EnsureComponent(uid, out var psionic); + psionicPowers.InitializePsionicPower(uid, powerProto, psionic); + } +} diff --git a/Content.Server/StationEvents/Components/OscillatingStationEventSchedulerComponent.cs b/Content.Server/StationEvents/Components/OscillatingStationEventSchedulerComponent.cs new file mode 100644 index 0000000000..7f28401a5a --- /dev/null +++ b/Content.Server/StationEvents/Components/OscillatingStationEventSchedulerComponent.cs @@ -0,0 +1,69 @@ +namespace Content.Server.StationEvents.Components; + +/// +/// A station event scheduler that emits events at irregular intervals, with occasional chaos and occasional calmness. +/// +[RegisterComponent] +public sealed partial class OscillatingStationEventSchedulerComponent : Component +{ + // TODO cvars? + [DataField] + public float MinChaos = 0.1f, MaxChaos = 15f; + + /// + /// The amount of chaos at the beginning of the round. + /// + [DataField] + public float StartingChaosRatio = 0f; + + /// + /// The value of the first derivative of the event delay function at the beginning of the shift. + /// Must be between 1 and -1. + /// + [DataField] + public float StartingSlope = 1f; + + /// + /// Biases that determine how likely the event rate is to go up or down, and how fast it's going to happen. + /// + /// + /// Downwards bias must always be negative, and upwards must be positive. Otherwise, you'll get odd behavior or errors. + /// + [DataField] + public float DownwardsBias = -1f, UpwardsBias = 1f; + + /// + /// Limits that define how large the chaos slope can become. + /// + /// + /// Downwards limit must always be negative, and upwards must be positive. Otherwise, you'll get odd behavior or errors. + /// + [DataField] + public float DownwardsLimit = -1f, UpwardsLimit = 1f; + + /// + /// A value between 0 and 1 that determines how slowly the chaos and its first derivative change in time. + /// + /// + /// Changing these values will have a great impact on how fast the event rate changes. + /// + [DataField] + public float ChaosStickiness = 0.93f, SlopeStickiness = 0.96f; + + /// + /// Actual chaos data at the current moment. Those are overridden at runtime. + /// + [DataField] + public float CurrentChaos, CurrentSlope, LastAcceleration; + + + [DataField] + public TimeSpan NextUpdate = TimeSpan.Zero, LastEventTime = TimeSpan.Zero; + + /// + /// Update interval, which determines how often current chaos is recalculated. + /// Modifying this value does not directly impact the event rate, but changes how stable the slope is. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(5f); +} diff --git a/Content.Server/StationEvents/OscillatingStationEventScheduler.cs b/Content.Server/StationEvents/OscillatingStationEventScheduler.cs new file mode 100644 index 0000000000..2cbf861386 --- /dev/null +++ b/Content.Server/StationEvents/OscillatingStationEventScheduler.cs @@ -0,0 +1,102 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.StationEvents.Components; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Robust.Shared.Random; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.StationEvents; + +public sealed class OscillatingStationEventSchedulerSystem : GameRuleSystem +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EventManagerSystem _event = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + [Conditional("DEBUG")] + private void DebugValidateParams(OscillatingStationEventSchedulerComponent c) + { + // This monstrousity is necessary because if someone fucks up one of these parameters, + // it will likely either crash the game (in debug), or cause the event scheduler to stop working and spam the server console (in prod) + DebugTools.Assert(c.DownwardsBias <= 0f && c.UpwardsBias >= 0f, "Fix your scheduler bias"); + DebugTools.Assert(c.DownwardsLimit <= 0f && c.UpwardsLimit >= 0f, "Fix your scheduler slope limits"); + DebugTools.Assert(c.UpdateInterval > TimeSpan.Zero, "Scheduler update interval must be positive"); + DebugTools.Assert(c.ChaosStickiness >= 0f && c.ChaosStickiness <= 1f, "Scheduler stickiness must be between 0 and 1"); + DebugTools.Assert(c.SlopeStickiness >= 0f && c.SlopeStickiness <= 1f, "Scheduler stickiness must be between 0 and 1"); + DebugTools.Assert(c.MinChaos < c.MaxChaos, "Don't set the minimum above the maximum"); + } + + private TimeSpan CalculateAverageEventTime(OscillatingStationEventSchedulerComponent comp) + { + //TODO Those cvars are bad + var min = _cfg.GetCVar(CCVars.GameEventsOscillatingMinimumTime); + var max = _cfg.GetCVar(CCVars.GameEventsOscillatingAverageTime); + + return TimeSpan.FromSeconds(min + (max - min) / comp.CurrentChaos); // Why does C# have no math.lerp?????????????? + } + + protected override void Started(EntityUid uid, OscillatingStationEventSchedulerComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + DebugValidateParams(comp); + + comp.CurrentChaos = comp.MinChaos + comp.StartingChaosRatio * (comp.MaxChaos - comp.MinChaos); + comp.CurrentSlope = comp.StartingSlope; + + comp.NextUpdate = _timing.CurTime + CalculateAverageEventTime(comp); + comp.LastEventTime = _timing.CurTime; // Just so we don't run an event the very moment this scheduler gets added + } + + protected override void ActiveTick(EntityUid uid, OscillatingStationEventSchedulerComponent comp, GameRuleComponent gameRule, float frameTime) + { + if (comp.NextUpdate > _timing.CurTime) + return; + comp.NextUpdate = _timing.CurTime + comp.UpdateInterval; + DebugValidateParams(comp); + + // Slope is the first derivative of chaos, and acceleration is the second + // We randomize acceleration on each tick and simulate its effect on the slope and base function + // But we spread the effect across a longer time span to achieve a smooth and pleasant result + var delta = (float) comp.UpdateInterval.TotalSeconds; + var newAcceleration = _random.NextFloat(comp.DownwardsBias, comp.UpwardsBias); + var newSlope = + Math.Clamp(comp.CurrentSlope + newAcceleration * delta, comp.DownwardsLimit, comp.UpwardsLimit) * (1 - comp.SlopeStickiness) + + comp.CurrentSlope * comp.SlopeStickiness; + var newChaos = + Math.Clamp(comp.CurrentChaos + newSlope * delta, comp.MinChaos, comp.MaxChaos) * (1 - comp.ChaosStickiness) + + comp.CurrentChaos * comp.ChaosStickiness; + + comp.CurrentChaos = newChaos; + comp.CurrentSlope = newSlope; + comp.LastAcceleration = newAcceleration; + + // We do not use fixed "next event" times because that can cause us to skip over chaos spikes due to periods of low chaos + // Instead we recalculate the time until next event every time, so it can change before the event is even started + var targetDelay = CalculateAverageEventTime(comp); + if (_timing.CurTime > comp.LastEventTime + targetDelay && TryRunNextEvent(uid, comp, out _)) + { + #if DEBUG + var passed = _timing.CurTime - comp.LastEventTime; + Log.Debug($"Running an event after {passed.TotalSeconds} sec since last event. Next event scheduled in {CalculateAverageEventTime(comp).TotalSeconds} sec."); + #endif + + comp.LastEventTime = _timing.CurTime; + } + } + + public bool TryRunNextEvent(EntityUid uid, OscillatingStationEventSchedulerComponent comp, [NotNullWhen(true)] out string? runEvent) + { + runEvent = _event.PickRandomEvent(); + if (runEvent == null) + return false; + + _gameTicker.AddGameRule(runEvent); + return true; + } +} diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index ae8f803853..d6f13cb52a 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -24,6 +24,7 @@ public sealed class TraitSystem : EntitySystem [Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; public override void Initialize() { @@ -60,11 +61,34 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) /// public void AddTrait(EntityUid uid, TraitPrototype traitPrototype) { + RemoveTraitComponents(uid, traitPrototype); AddTraitComponents(uid, traitPrototype); AddTraitActions(uid, traitPrototype); AddTraitPsionics(uid, traitPrototype); } + /// + /// Removes all components defined by a Trait. It's not possible to validate component removals, + /// so if an incorrect string is given, it's basically a skill issue. + /// + /// + /// This comes before AddTraitComponents for a good reason. + /// It allows for a component to optionally be fully wiped and replaced with a new component. + /// + public void RemoveTraitComponents(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.ComponentRemovals is null) + return; + + foreach (var entry in traitPrototype.ComponentRemovals) + { + if (!_componentFactory.TryGetRegistration(entry, out var comp)) + continue; + + EntityManager.RemoveComponent(uid, comp.Type); + } + } + /// /// Adds all Components included with a Trait. /// diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 2fe183a606..4b39d9f936 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -178,28 +178,37 @@ public static readonly CVarDef /// Minimum time between Basic station events in seconds /// public static readonly CVarDef // 5 Minutes - GameEventsBasicMinimumTime = CVarDef.Create("game.events_basic_minimum_time", 300, CVar.SERVERONLY); + GameEventsBasicMinimumTime = CVarDef.Create("game.events_basic_minimum_time", 300, CVar.SERVERONLY | CVar.ARCHIVE); /// /// Maximum time between Basic station events in seconds /// public static readonly CVarDef // 25 Minutes - GameEventsBasicMaximumTime = CVarDef.Create("game.events_basic_maximum_time", 1500, CVar.SERVERONLY); + GameEventsBasicMaximumTime = CVarDef.Create("game.events_basic_maximum_time", 1500, CVar.SERVERONLY | CVar.ARCHIVE); /// /// Minimum time between Ramping station events in seconds /// public static readonly CVarDef // 6 Minutes - GameEventsRampingMinimumTime = CVarDef.Create("game.events_ramping_minimum_time", 360, CVar.SERVERONLY); + GameEventsRampingMinimumTime = CVarDef.Create("game.events_ramping_minimum_time", 360, CVar.SERVERONLY | CVar.ARCHIVE); /// /// Maximum time between Ramping station events in seconds /// public static readonly CVarDef // 12 Minutes - GameEventsRampingMaximumTime = CVarDef.Create("game.events_ramping_maximum_time", 720, CVar.SERVERONLY); + GameEventsRampingMaximumTime = CVarDef.Create("game.events_ramping_maximum_time", 720, CVar.SERVERONLY | CVar.ARCHIVE); /// - /// + /// Minimum time between Oscillating station events in seconds. This is the bare minimum which will never be violated, unlike with ramping events. + /// + public static readonly CVarDef // 40 seconds + GameEventsOscillatingMinimumTime = CVarDef.Create("game.events_oscillating_minimum_time", 40, CVar.SERVERONLY | CVar.ARCHIVE); + + /// + /// Time between Oscillating station events in seconds at 1x chaos level. Events may occur at larger intervals if current chaos is lower than that. + /// + public static readonly CVarDef // 20 Minutes - which constitutes a minimum of 120 seconds between events in Irregular and 280 seconds in Extended Irregular + GameEventsOscillatingAverageTime = CVarDef.Create("game.events_oscillating_average_time", 1200, CVar.SERVERONLY | CVar.ARCHIVE); /// /// Controls the maximum number of character slots a player is allowed to have. @@ -402,6 +411,12 @@ public static readonly CVarDef public static readonly CVarDef GamePressToSprint = CVarDef.Create("game.press_to_sprint", true, CVar.REPLICATED); + /// + /// Whether item slots, such as power cell slots or AME fuel cell slots, should support quick swap if it is not otherwise specified in their YAML prototype. + /// + public static readonly CVarDef AllowSlotQuickSwap = + CVarDef.Create("game.slot_quick_swap", true, CVar.REPLICATED); + #if EXCEPTION_TOLERANCE /// /// Amount of times round start must fail before the server is shut down. @@ -2234,12 +2249,6 @@ public static readonly CVarDef public static readonly CVarDef WorldgenConfig = CVarDef.Create("worldgen.worldgen_config", "Default", CVar.SERVERONLY); - /// - /// The amount of time between NPC Silicons draining their battery in seconds. - Estacao Pirata - /// - public static readonly CVarDef SiliconNpcUpdateTime = - CVarDef.Create("silicon.npcupdatetime", 1.5f, CVar.SERVERONLY); - /// /// The maximum amount of time the entity GC can process, in ms. /// @@ -2286,6 +2295,13 @@ public static readonly CVarDef CVarDef.Create("replay.auto_record_temp_dir", "", CVar.SERVERONLY); + /// + /// The amount of time between NPC Silicons draining their battery in seconds. + /// + public static readonly CVarDef SiliconNpcUpdateTime = + CVarDef.Create("silicon.npcupdatetime", 1.5f, CVar.SERVERONLY); + + /* * Miscellaneous */ @@ -2340,6 +2356,15 @@ public static readonly CVarDef public static readonly CVarDef HeightAdjustModifiesZoom = CVarDef.Create("heightadjust.modifies_zoom", false, CVar.SERVERONLY); + /// + /// Whether height & width sliders adjust a player's bloodstream volume. + /// + /// + /// This can be configured more precisely by modifying BloodstreamAffectedByMassComponent. + /// + public static readonly CVarDef HeightAdjustModifiesBloodstream = + CVarDef.Create("heightadjust.modifies_bloodstream", true, CVar.SERVERONLY); + /// /// Enables station goals /// @@ -2517,5 +2542,15 @@ public static readonly CVarDef CVarDef.Create("mood.decreases_speed", true, CVar.SERVER); #endregion + + #region Material Reclaimer + + /// + /// Whether or not a Material Reclaimer is allowed to eat people when emagged. + /// + public static readonly CVarDef ReclaimerAllowGibbing = + CVarDef.Create("reclaimer.allow_gibbing", true, CVar.SERVER); + + #endregion } } diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 42e7f721b3..ba8a9a934e 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -214,6 +214,7 @@ public ItemSlot(ItemSlot other) /// /// If the user interacts with an entity with an already-filled item slot, should they attempt to swap out the item? + /// If set to null, will be deduced based on the relevant config variable. /// /// /// Useful for things like chem dispensers, but undesirable for things like the ID card console, where you @@ -221,7 +222,7 @@ public ItemSlot(ItemSlot other) /// [DataField] [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] - public bool Swap = true; + public bool? Swap = null; public string? ID => ContainerSlot?.ID; diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 9cb21e882e..c7932ef8f7 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; +using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Destructible; using Content.Shared.Hands.Components; @@ -10,6 +11,7 @@ using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Utility; @@ -27,11 +29,14 @@ public sealed class ItemSlotsSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + private bool _defaultQuickSwap; + public override void Initialize() { base.Initialize(); @@ -53,6 +58,8 @@ public override void Initialize() SubscribeLocalEvent(HandleItemSlotsState); SubscribeLocalEvent(HandleButtonPressed); + + _config.OnValueChanged(CCVars.AllowSlotQuickSwap, b => _defaultQuickSwap = b, true); } #region ComponentManagement @@ -202,7 +209,7 @@ private void OnInteractUsing(EntityUid uid, ItemSlotsComponent itemSlots, Intera if (!slot.InsertOnInteract) continue; - if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap, popup: args.User)) + if (!CanInsert(uid, args.Used, args.User, slot, swap: slot.Swap ?? _defaultQuickSwap, popup: args.User)) continue; // Drop the held item onto the floor. Return if the user cannot drop. diff --git a/Content.Shared/HeightAdjust/HeightAdjustSystem.cs b/Content.Shared/HeightAdjust/HeightAdjustSystem.cs index 46b2d9b656..8bfdaccfd1 100644 --- a/Content.Shared/HeightAdjust/HeightAdjustSystem.cs +++ b/Content.Shared/HeightAdjust/HeightAdjustSystem.cs @@ -25,27 +25,7 @@ public sealed class HeightAdjustSystem : EntitySystem /// True if all operations succeeded public bool SetScale(EntityUid uid, float scale) { - var succeeded = true; - if (_config.GetCVar(CCVars.HeightAdjustModifiesZoom) && EntityManager.TryGetComponent(uid, out var eye)) - _eye.SetMaxZoom(uid, eye.MaxZoom * scale); - else - succeeded = false; - - if (_config.GetCVar(CCVars.HeightAdjustModifiesHitbox) && EntityManager.TryGetComponent(uid, out var fixtures)) - foreach (var fixture in fixtures.Fixtures) - _physics.SetRadius(uid, fixture.Key, fixture.Value, fixture.Value.Shape, MathF.MinMagnitude(fixture.Value.Shape.Radius * scale, 0.49f)); - else - succeeded = false; - - if (EntityManager.HasComponent(uid)) - { - _appearance.SetHeight(uid, scale); - _appearance.SetWidth(uid, scale); - } - else - succeeded = false; - - return succeeded; + return SetScale(uid, new Vector2(scale, scale)); } /// @@ -75,6 +55,8 @@ public bool SetScale(EntityUid uid, Vector2 scale) else succeeded = false; + RaiseLocalEvent(uid, new HeightAdjustedEvent { NewScale = scale }); + return succeeded; } } diff --git a/Content.Shared/HeightAdjust/HeightAdjustedEvent.cs b/Content.Shared/HeightAdjust/HeightAdjustedEvent.cs new file mode 100644 index 0000000000..3db856e0d8 --- /dev/null +++ b/Content.Shared/HeightAdjust/HeightAdjustedEvent.cs @@ -0,0 +1,11 @@ +using System.Numerics; + +namespace Content.Shared.HeightAdjust; + +/// +/// Raised on a humanoid after their scale has been adjusted in accordance with their profile and their physics have been updated. +/// +public sealed class HeightAdjustedEvent : EntityEventArgs +{ + public Vector2 NewScale; +} diff --git a/Content.Shared/InteractionVerbs/Requirements/AssortedRequirements.cs b/Content.Shared/InteractionVerbs/Requirements/AssortedRequirements.cs index 50c5c77d2c..69193a9212 100644 --- a/Content.Shared/InteractionVerbs/Requirements/AssortedRequirements.cs +++ b/Content.Shared/InteractionVerbs/Requirements/AssortedRequirements.cs @@ -13,11 +13,12 @@ namespace Content.Shared.InteractionVerbs.Requirements; [Serializable, NetSerializable] public sealed partial class EntityWhitelistRequirement : InteractionRequirement { - [DataField] public EntityWhitelist Whitelist = new(), Blacklist = new(); + [DataField] public EntityWhitelist? Whitelist, Blacklist; public override bool IsMet(InteractionArgs args, InteractionVerbPrototype proto, InteractionAction.VerbDependencies deps) { - return Whitelist.IsValid(args.Target, deps.EntMan) && !Blacklist.IsValid(args.Target, deps.EntMan); + return Whitelist?.IsValid(args.Target, deps.EntMan) != false + && Blacklist?.IsValid(args.Target, deps.EntMan) != true; } } diff --git a/Content.Shared/Language/Components/Translators/HoldsTranslatorComponent.cs b/Content.Shared/Language/Components/Translators/HoldsTranslatorComponent.cs index caea9b9a94..b35cc7fd03 100644 --- a/Content.Shared/Language/Components/Translators/HoldsTranslatorComponent.cs +++ b/Content.Shared/Language/Components/Translators/HoldsTranslatorComponent.cs @@ -1,11 +1,11 @@ namespace Content.Shared.Language.Components.Translators; /// -/// Applied internally to the holder of [HandheldTranslatorComponent]. -/// Do not use directly. Use [HandheldTranslatorComponent] instead. +/// Applied internally to the holder of an entity with [HandheldTranslatorComponent]. /// [RegisterComponent] -public sealed partial class HoldsTranslatorComponent : IntrinsicTranslatorComponent +public sealed partial class HoldsTranslatorComponent : Component { - public Component? Issuer = null; + [NonSerialized] + public HashSet> Translators = new(); } diff --git a/Content.Shared/Language/Components/Translators/ImplantedTranslatorComponent.cs b/Content.Shared/Language/Components/Translators/ImplantedTranslatorComponent.cs deleted file mode 100644 index d1d72e83ed..0000000000 --- a/Content.Shared/Language/Components/Translators/ImplantedTranslatorComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Shared.Language.Components.Translators; - -/// -/// Applied to entities who were injected with a translator implant. -/// -[RegisterComponent] -public sealed partial class ImplantedTranslatorComponent : IntrinsicTranslatorComponent -{ -} diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index 50dce3c766..cd4ae2e767 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Audio; using Content.Shared.Body.Components; +using Content.Shared.CCVar; using Content.Shared.Coordinates; using Content.Shared.Database; using Content.Shared.Emag.Components; @@ -11,6 +12,7 @@ using Content.Shared.Stacks; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Physics.Events; @@ -29,6 +31,7 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem [Dependency] protected readonly SharedAmbientSoundSystem AmbientSound = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] protected readonly SharedContainerSystem Container = default!; + [Dependency] private readonly IConfigurationManager _config = default!; public const string ActiveReclaimerContainerId = "active-material-reclaimer-container"; @@ -193,16 +196,16 @@ public bool CanStart(EntityUid uid, MaterialReclaimerComponent component) } /// - /// Whether or not the reclaimer satisfies the conditions - /// allowing it to gib/reclaim a living creature. + /// Whether or not the reclaimer satisfies the conditions + /// allowing it to gib/reclaim a living creature. /// public bool CanGib(EntityUid uid, EntityUid victim, MaterialReclaimerComponent component) { - return false; // DeltaV - Kinda LRP - // return component.Powered && - // component.Enabled && - // HasComp(victim) && - // HasComp(uid); + return _config.GetCVar(CCVars.ReclaimerAllowGibbing) + && component.Powered + && component.Enabled + && HasComp(victim) + && HasComp(uid); } /// diff --git a/Content.Shared/Traits/Assorted/Components/NormalVisionComponent.cs b/Content.Shared/Traits/Assorted/Components/NormalVisionComponent.cs deleted file mode 100644 index 442bb6f008..0000000000 --- a/Content.Shared/Traits/Assorted/Components/NormalVisionComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Traits.Assorted.Components; - -/// -/// This is used for removing species specific vision traits -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class NormalVisionComponent : Component -{ -} - diff --git a/Content.Shared/Traits/Assorted/Systems/NormalVisionSystem.cs b/Content.Shared/Traits/Assorted/Systems/NormalVisionSystem.cs deleted file mode 100644 index ee25bf364b..0000000000 --- a/Content.Shared/Traits/Assorted/Systems/NormalVisionSystem.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Shared.Abilities; -using Content.Shared.Traits.Assorted.Components; - -namespace Content.Shared.Traits.Assorted.Systems; - -/// -/// This handles removing species-specific vision traits. -/// -public sealed class NormalVisionSystem : EntitySystem -{ - /// - public override void Initialize() - { - SubscribeLocalEvent(OnStartup); - } - - - private void OnStartup(EntityUid uid, NormalVisionComponent component, ComponentInit args) - { - RemComp(uid); - RemComp(uid); - } -} diff --git a/Content.Shared/Traits/Prototypes/TraitPrototype.cs b/Content.Shared/Traits/Prototypes/TraitPrototype.cs index 8aa3e4442f..7bdff058ee 100644 --- a/Content.Shared/Traits/Prototypes/TraitPrototype.cs +++ b/Content.Shared/Traits/Prototypes/TraitPrototype.cs @@ -38,6 +38,13 @@ public sealed partial class TraitPrototype : IPrototype [DataField] public ComponentRegistry? Components { get; private set; } = default!; + /// + /// The components that will be removed from a player when they pick this trait. + /// Primarily used to remove species innate traits. + /// + [DataField] + public List? ComponentRemovals { get; private set; } = default!; + /// /// The list of each Action that this trait adds in the form of ActionId and ActionEntity /// diff --git a/Resources/Audio/Admin/adminhelp_old.ogg b/Resources/Audio/Admin/adminhelp_old.ogg new file mode 100644 index 0000000000..704c0fd6d2 Binary files /dev/null and b/Resources/Audio/Admin/adminhelp_old.ogg differ diff --git a/Resources/Audio/StationEvents/attributions.yml b/Resources/Audio/StationEvents/attributions.yml index e63b18a627..9b3ad13484 100644 --- a/Resources/Audio/StationEvents/attributions.yml +++ b/Resources/Audio/StationEvents/attributions.yml @@ -6,4 +6,9 @@ - files: ["clearly_nuclear.ogg"] license: "CC-BY-3.0" copyright: "Created by mryikes" - source: "https://www.youtube.com/watch?v=chix8uz-oUQ" \ No newline at end of file + source: "https://www.youtube.com/watch?v=chix8uz-oUQ" + +- files: ["chip_nightmare.ogg"] + license: "CC-BY-SA-4.0" + copyright: "Created by BasedUser on top of chip-meltdown.mod by Reanimator (Thom)." + source: "http://ygg.baseduser.eu.org/chip-nightmare_ss14.ogg" diff --git a/Resources/Audio/StationEvents/chip_nightmare.ogg b/Resources/Audio/StationEvents/chip_nightmare.ogg new file mode 100644 index 0000000000..c721c0ca24 Binary files /dev/null and b/Resources/Audio/StationEvents/chip_nightmare.ogg differ diff --git a/Resources/Audio/Voice/Slime/slime_scream_f2.ogg b/Resources/Audio/Voice/Slime/slime_scream_f2.ogg index 71ea5f4bdc..923da50166 100644 Binary files a/Resources/Audio/Voice/Slime/slime_scream_f2.ogg and b/Resources/Audio/Voice/Slime/slime_scream_f2.ogg differ diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3a64146cdb..817aebe5eb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -5985,3 +5985,83 @@ Entries: id: 6325 time: '2024-09-07T03:59:44.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/861 +- author: Mnemotechnician + changes: + - type: Add + message: >- + Your character size now affects your blood level. Smaller characters + will have less blood, and larger characters will have more. + id: 6326 + time: '2024-09-08T14:39:19.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/858 +- author: Mnemotechnician + changes: + - type: Remove + message: >- + Quick swap has been disabled for most item slots. This primarily means + you will have to eject power cells/magazines from items/weapons/borgs + before replacing them with different ones. + id: 6327 + time: '2024-09-08T14:42:30.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/856 +- author: router + changes: + - type: Tweak + message: Female slimes no longer have movie screams. + id: 6328 + time: '2024-09-08T14:44:29.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/849 +- author: Mnemotechnician + changes: + - type: Add + message: Chat bubbles now use the font & color of the language of the message. + id: 6329 + time: '2024-09-08T14:51:20.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/812 +- author: Mnemotechnician + changes: + - type: Add + message: >- + A shrimp morphotype was added to the failure pool of the metempsychotic + machine. + id: 6330 + time: '2024-09-08T14:56:13.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/882 +- author: Mnemotechnician + changes: + - type: Fix + message: >- + Multiple issues with translators were fixed. Additionally, you can now + hold multiple handheld translators at once without issues. + id: 6331 + time: '2024-09-08T15:08:37.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/834 +- author: VMSolidus + changes: + - type: Add + message: Recyclers can now once again eat people when emagged. + - type: Add + message: >- + The ability for emagged Recyclers to eat people is now controlled by the + CVar "reclaimer.allow_gibbing". + - type: Add + message: >- + Recyclers require power to eat people. No more dragging emagged + recyclers into crowds. + id: 6332 + time: '2024-09-08T15:15:56.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/822 +- author: BlueHNT + changes: + - type: Add + message: Added bwoink hammer with bwoink sound + id: 6333 + time: '2024-09-08T15:16:09.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/759 +- author: VMSolidus + changes: + - type: Add + message: 'A new console command, AddPsionicPower has been added. ' + id: 6334 + time: '2024-09-08T15:16:29.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/807 diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 589d79e6ea..034f38f854 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -21,11 +21,11 @@ chat-manager-whisper-headset-on-message = You can't whisper on the radio! chat-manager-server-wrap-message = [bold]{$message}[/bold] chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] {$message}[/bold][/font] -chat-manager-entity-say-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font="{$fontType}" size={$fontSize}]"[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/font] -chat-manager-entity-say-bold-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font="{$fontType}" size={$fontSize}]"[color={$color}][BubbleContent][bold]{$message}[/bold][/BubbleContent][/color]"[/font] +chat-manager-entity-say-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, "[BubbleContent][font="{$fontType}" size={$fontSize}][color={$color}]{$message}[/color][/font][/BubbleContent]" +chat-manager-entity-say-bold-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, "[BubbleContent][font="{$fontType}" size={$fontSize}][color={$color}][bold]{$message}[/bold][/color][/font][/BubbleContent]" -chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/italic][/font] -chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/italic][/font] +chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers, "[BubbleContent][font="{$fontType}"][color={$color}]{$message}[/color][/font][/BubbleContent]"[/italic][/font] +chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[BubbleContent][font="{$fontType}"][color={$color}]{$message}[/color][/font][/BubbleContent]"[/italic][/font] # THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed... chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) -> diff --git a/Resources/Locale/en-US/devices/device-network.ftl b/Resources/Locale/en-US/devices/device-network.ftl index d88b1f719d..2740143d51 100644 --- a/Resources/Locale/en-US/devices/device-network.ftl +++ b/Resources/Locale/en-US/devices/device-network.ftl @@ -31,7 +31,7 @@ device-address-prefix-freezer = FZR- device-address-prefix-volume-pump = VPP- device-address-prefix-smes = SMS- -#PDAs and terminals +# PDAs and terminals device-address-prefix-console = CLS- device-address-prefix-fire-alarm = FIR- device-address-prefix-air-alarm = AIR- @@ -40,7 +40,7 @@ device-address-prefix-sensor-monitor = MON- device-address-examine-message = The device's address is {$address}. -#Device net ID names +# Device net ID names device-net-id-private = Private device-net-id-wired = Wired device-net-id-wireless = Wireless diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-irregular.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-irregular.ftl new file mode 100644 index 0000000000..35fc7957cf --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-irregular.ftl @@ -0,0 +1,5 @@ +irregular-title = Irregular +irregular-description = Threat level varies throughout the shift. Sometimes it's a paradise, sometimes it's a disaster. + +irregular-extended-title = Irregular Extended +irregular-extended-description = A rather calm experience with occasional spikes of threats. diff --git a/Resources/Locale/en-US/interaction/verbs/noop.ftl b/Resources/Locale/en-US/interaction/verbs/noop.ftl index e8b2e85389..37e8508c8d 100644 --- a/Resources/Locale/en-US/interaction/verbs/noop.ftl +++ b/Resources/Locale/en-US/interaction/verbs/noop.ftl @@ -16,6 +16,12 @@ interaction-Pet-success-self-popup = You pet {THE($target)} on {POSS-ADJ($target interaction-Pet-success-target-popup = {THE($user)} pets you on {POSS-ADJ($target)} head. interaction-Pet-success-others-popup = {THE($user)} pets {THE($target)}. +interaction-PetAnimal-name = {interaction-Pet-name} +interaction-PetAnimal-description = Pet an animal. +interaction-PetAnimal-success-self-popup = {interaction-Pet-success-self-popup} +interaction-PetAnimal-success-target-popup = {interaction-Pet-success-target-popup} +interaction-PetAnimal-success-others-popup = {interaction-Pet-success-others-popup} + interaction-KnockOn-name = Knock interaction-KnockOn-description = Knock on the target to attract attention. interaction-KnockOn-success-self-popup = You knock on {THE($target)}. diff --git a/Resources/Locale/en-US/mood/mood.ftl b/Resources/Locale/en-US/mood/mood.ftl index c12ec7246e..bd64d52582 100644 --- a/Resources/Locale/en-US/mood/mood.ftl +++ b/Resources/Locale/en-US/mood/mood.ftl @@ -37,6 +37,8 @@ mood-effect-Dead = You are dead. mood-effect-BeingHugged = Hugs are nice. +mood-effect-BeingPet = Someone pet me! + mood-effect-ArcadePlay = I had fun playing an interesting arcade game. mood-effect-GotBlessed = I was blessed. @@ -51,4 +53,4 @@ mood-effect-RevolutionFocused = VIVA LA REVOLUTION!!! mood-effect-CultFocused = Dark Gods, grant me strength! -mood-effect-TraitSanguine = I have nothing to worry about. I'm sure everything will turn out well in the end! \ No newline at end of file +mood-effect-TraitSanguine = I have nothing to worry about. I'm sure everything will turn out well in the end! diff --git a/Resources/Locale/en-US/psionics/psionic-commands.ftl b/Resources/Locale/en-US/psionics/psionic-commands.ftl index e1110ef285..34d12981de 100644 --- a/Resources/Locale/en-US/psionics/psionic-commands.ftl +++ b/Resources/Locale/en-US/psionics/psionic-commands.ftl @@ -6,3 +6,8 @@ command-glimmerset-help = glimmerset (integer) command-lspsionic-description = List psionics. command-lspsionic-help = No arguments. + +command-addpsionicpower-description = Initialize an entity as Psionic with a given PowerPrototype +command-addpsionicpower-help = Argument 1 must be an EntityUid, and argument 2 must be a string matching the PrototypeId of a PsionicPower. +addpsionicpower-args-one-error = Argument 1 must be an EntityUid +addpsionicpower-args-two-error = Argument 2 must match the PrototypeId of a PsionicPower diff --git a/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl b/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl index 421dfe1484..682e03ff77 100644 --- a/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl +++ b/Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl @@ -37,8 +37,8 @@ reagent-desc-poison-wine = Is this even wine? Toxic! Hallucinogenic! Probably co reagent-name-rum = rum reagent-desc-rum = Distilled alcoholic drink made from sugarcane byproducts. -#reagent-name-sake = sake -#reagent-desc-sake = Alcoholic beverage made by fermenting rice that has been polished. +# reagent-name-sake = sake +# reagent-desc-sake = Alcoholic beverage made by fermenting rice that has been polished. reagent-name-tequila = tequila reagent-desc-tequila = A strong and mildly flavoured, mexican produced spirit. diff --git a/Resources/Locale/en-US/revenant/revenant.ftl b/Resources/Locale/en-US/revenant/revenant.ftl index f4ea6d79ff..1d6bae7b34 100644 --- a/Resources/Locale/en-US/revenant/revenant.ftl +++ b/Resources/Locale/en-US/revenant/revenant.ftl @@ -16,7 +16,7 @@ revenant-soul-yield-low = {CAPITALIZE(THE($target))} has a below average soul. revenant-soul-begin-harvest = {CAPITALIZE(THE($target))} suddenly rises slightly into the air, {POSS-ADJ($target)} skin turning an ashy gray. revenant-soul-finish-harvest = {CAPITALIZE(THE($target))} slumps onto the ground! -#UI +# UI revenant-user-interface-title = Ability Shop revenant-user-interface-essence-amount = [color=plum]{$amount}[/color] Stolen Essence diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 64b442647f..219d725c17 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -216,3 +216,9 @@ trait-description-NaturalTelepath = whether or not you possess any notable psychic powers. This offers all of the same benefits and drawbacks of Latent Psychic, except that you are guaranteed to start with full Telepathy. You may still gain powers as normal for a Latent Psychic. + +trait-name-AnomalousPositronics = Anomalous Positronics +trait-description-AnomalousPositronics = + Whether by intentional design from the manufacturer, black market modifications, or accidental omission, + your positronic brain lacks its standard psionic insulation. As a being that can be argued to have a soul, + this by extension means that it is possible for you to be influenced by the Noosphere. diff --git a/Resources/Locale/en-US/weapons/melee/melee.ftl b/Resources/Locale/en-US/weapons/melee/melee.ftl index 871d142504..d3318ea244 100644 --- a/Resources/Locale/en-US/weapons/melee/melee.ftl +++ b/Resources/Locale/en-US/weapons/melee/melee.ftl @@ -3,5 +3,5 @@ melee-inject-failed-hardsuit = Your {$weapon} cannot inject through hardsuits! melee-balloon-pop = {CAPITALIZE(THE($balloon))} popped! -#BatteryComponent +# BatteryComponent melee-battery-examine = It has enough charge for [color={$color}]{$count}[/color] hits. diff --git a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-irregular.ftl b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-irregular.ftl new file mode 100644 index 0000000000..a4aad7cf2d --- /dev/null +++ b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-irregular.ftl @@ -0,0 +1,4 @@ +irregular-title = Непостоянный +irregular-description = Уровень угрозы меняется в течение смены. Иногда это благодать, иногда - катастрофа. +irregular-extended-title = Нерегулярно Расширенный +irregular-extended-description = Довольно спокойный опыт с редкими всплесками угроз. diff --git a/Resources/Locale/ru-RU/interaction/verbs/noop.ftl b/Resources/Locale/ru-RU/interaction/verbs/noop.ftl index b020e6cd2f..8dd3b64a4f 100644 --- a/Resources/Locale/ru-RU/interaction/verbs/noop.ftl +++ b/Resources/Locale/ru-RU/interaction/verbs/noop.ftl @@ -13,6 +13,11 @@ interaction-Pet-description = Погладьте своего коллегу, ч interaction-Pet-success-self-popup = Вы гладите { THE($target) } по { POSS-ADJ($target) } голове. interaction-Pet-success-target-popup = { THE($user) } гладит вас по { POSS-ADJ($target) } головке. interaction-Pet-success-others-popup = { THE($user) } гладит { THE($target) }. +interaction-PetAnimal-name = { interaction-Pet-name } +interaction-PetAnimal-description = Погладить животное. +interaction-PetAnimal-success-self-popup = { interaction-Pet-success-self-popup } +interaction-PetAnimal-success-target-popup = { interaction-Pet-success-target-popup } +interaction-PetAnimal-success-others-popup = { interaction-Pet-success-others-popup } interaction-KnockOn-name = постучать interaction-KnockOn-description = Постучите по цели, чтобы привлечь к себе внимание. interaction-KnockOn-success-self-popup = Вы постучали по { THE($target) }. diff --git a/Resources/Locale/ru-RU/mood/mood.ftl b/Resources/Locale/ru-RU/mood/mood.ftl index b72b74dd47..ef60aad484 100644 --- a/Resources/Locale/ru-RU/mood/mood.ftl +++ b/Resources/Locale/ru-RU/mood/mood.ftl @@ -22,6 +22,7 @@ mood-effect-MobHighPressure = У меня такое чувство, будто mood-effect-TraitSaturnine = Все как-то отстойно. Ненавижу эту работу. mood-effect-Dead = Ты мертв. mood-effect-BeingHugged = Объятия — это приятно. +mood-effect-BeingPet = Кто-то погладил меня! mood-effect-ArcadePlay = Мне было весело играть в интересную аркадную игру. mood-effect-GotBlessed = Я был благословлен. mood-effect-PetAnimal = Животные такие милые, я не могу перестать их гладить! diff --git a/Resources/Locale/ru-RU/psionics/psionic-commands.ftl b/Resources/Locale/ru-RU/psionics/psionic-commands.ftl index 234462f0de..0a8283e50c 100644 --- a/Resources/Locale/ru-RU/psionics/psionic-commands.ftl +++ b/Resources/Locale/ru-RU/psionics/psionic-commands.ftl @@ -4,3 +4,7 @@ command-glimmerset-description = Установите глиммер на опр command-glimmerset-help = Установить глиммер (целое число) command-lspsionic-description = Список псиоников. command-lspsionic-help = Да поебать мне xD +command-addpsionicpower-description = Инициализируйте объект как псионический с заданным прототипом мощности +command-addpsionicpower-help = Аргументом 1 должен быть Uid объекта, а аргументом 2 должна быть строка, соответствующая идентификатору прототипа псионической силы. +addpsionicpower-args-one-error = Аргументом 1 должен быть Uid объекта +addpsionicpower-args-two-error = Аргумент 2 должен соответствовать идентификатору прототипа псионической силы diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/deltav/entities/mobs/npcs/fun.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/deltav/entities/mobs/npcs/fun.ftl new file mode 100644 index 0000000000..c59bda3597 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/deltav/entities/mobs/npcs/fun.ftl @@ -0,0 +1,2 @@ +ent-MobSpaceShrimp = космическая креветка + .desc = Креветочно проклятый... diff --git a/Resources/Locale/ru-RU/traits/traits.ftl b/Resources/Locale/ru-RU/traits/traits.ftl index b95606913e..02fb17d73f 100644 --- a/Resources/Locale/ru-RU/traits/traits.ftl +++ b/Resources/Locale/ru-RU/traits/traits.ftl @@ -166,3 +166,8 @@ trait-description-NaturalTelepath = обладаете ли вы какими-либо заметными экстрасенсорными способностями. Это дает все те же преимущества и недостатки, что и экстрасенс, за исключением того, что вы гарантированно начнете с телепатической связью. Вы все еще можете обрести способности, так же, как и обычный экстрасенс. +trait-name-AnomalousPositronics = Аномальная позитроника +trait-description-AnomalousPositronics = + Будь то преднамеренный дизайн от производителя, модификации на черном рынке или случайное упущение, + вашему позитронному мозгу не хватает стандартной псионической изоляции. Как существо, у которого, как можно утверждать, есть душа, + это, в более широком смысле, означает, что вы можете подвергаться влиянию Ноосферы. diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/fun.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/fun.yml new file mode 100644 index 0000000000..96008395b3 --- /dev/null +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/fun.yml @@ -0,0 +1,63 @@ +- type: entity + name: space shrimp + parent: [ SimpleMobBase, FlyingMobBase, MobCombat ] + id: MobSpaceShrimp + description: Shrimply cursed... + components: + - type: MeleeWeapon + soundHit: + path: /Audio/Effects/pop.ogg + - type: MovementSpeedModifier + baseWalkSpeed : 6 + baseSprintSpeed : 6 + - type: Sprite + sprite: DeltaV/Mobs/Animals/shrimp.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: shrimp + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 100 + mask: + - FlyingMobMask + layer: + - FlyingMobLayer + - type: Physics + - type: DamageStateVisuals + states: + Alive: + Base: shrimp + Dead: + Base: dead + - type: Butcherable + spawned: + - id: FoodMeatCrab + amount: 3 + - type: Bloodstream + bloodMaxVolume: 100 + bloodReagent: BbqSauce # Australia reference + - type: InteractionPopup + successChance: 0.5 + interactSuccessString: petting-success-possum + interactFailureString: petting-failure-possum + interactSuccessSpawn: EffectHearts + interactSuccessSound: + path: /Audio/Animals/raccoon_chatter.ogg + - type: Speech + speechSounds: Slime + - type: Puller + needsHands: false + - type: MindContainer + showExamineInfo: true + - type: NpcFactionMember + factions: + - Passive + - type: Body + prototype: Animal + - type: HTN + rootTask: + task: SimpleHostileCompound diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index b5f7e62612..5381dbabae 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -312,6 +312,8 @@ - type: OfferItem - type: LayingDown - type: Shoving + - type: BloodstreamAffectedByMass + power: 0.6 # A minimum size felinid will have 30% blood, a minimum size vulp will have 60%, a maximum size oni will have ~200% - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml index ced65ed92f..d6d86c08b2 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml @@ -392,4 +392,6 @@ - id: ThronglerToy prob: 0.30 orGroup: Prize - + - id: BwoinkHammer + prob: 0.30 + orGroup: Prize diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 1fea59a926..9e9740a0a8 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1745,6 +1745,34 @@ types: Blunt: 0 +- type: entity + parent: ToyHammer + id: BwoinkHammer + name: bwoink hammer + description: A toy hammer which makes a wildly terrifying sound. It is missing it's old spirit of bangammer and is still unraisinable. + suffix: DO NOT MAP + components: + - type: Sprite + sprite: Objects/Fun/bwoink_hammer.rsi + state: icon + - type: Item + size: Small + sprite: Objects/Fun/bwoink_hammer.rsi + - type: MeleeWeapon + soundHit: + path: /Audio/Admin/adminhelp_old.ogg + params: + variation: 0.03 + volume: 3 + soundNoDamage: + path: /Audio/Admin/adminhelp_old.ogg + params: + variation: 0.03 + volume: 3 + damage: + types: + Blunt: 0 + - type: entity parent: BaseItem id: WhoopieCushion diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index d0432815cb..dd8182b24f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -1416,6 +1416,7 @@ - SingularityToy - TeslaToy - ToySword + - BwoinkHammer - ThronglerToy - type: MaterialStorage whitelist: diff --git a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml index 138795cbf1..91eb0335e6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/recycler.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/recycler.yml @@ -116,4 +116,5 @@ interactSuccessString: petting-success-recycler interactFailureString: petting-failure-generic interactSuccessSound: - path: /Audio/Items/drill_hit.ogg \ No newline at end of file + path: /Audio/Items/drill_hit.ogg + - type: ApcPowerReceiver diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 0af55a7f9d..0a9daeb4a8 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -142,6 +142,33 @@ startingChaosRatio: 0.025 # Starts as slow as survival, but quickly ramps up shiftLengthModifier: 2.5 +- type: entity + id: IrregularStationEventScheduler + parent: BaseGameRule + noSpawn: true + components: + - type: OscillatingStationEventScheduler + minChaos: 0.8 + maxChaos: 14 + startingSlope: 0.2 + downwardsLimit: -0.35 + upwardsLimit: 0.4 + +# More likely to go down than up, so calmness prevails +- type: entity + id: IrregularExtendedStationEventScheduler + parent: BaseGameRule + noSpawn: true + components: + - type: OscillatingStationEventScheduler + minChaos: 0.8 + maxChaos: 8 + startingSlope: -1 + downwardsLimit: -0.4 + upwardsLimit: 0.3 + downwardsBias: -1.1 + upwardsBias: 0.9 + # variation passes - type: entity id: BasicRoundstartVariation diff --git a/Resources/Prototypes/Interactions/mood_interactions.yml b/Resources/Prototypes/Interactions/mood_interactions.yml new file mode 100644 index 0000000000..6a50704b03 --- /dev/null +++ b/Resources/Prototypes/Interactions/mood_interactions.yml @@ -0,0 +1,60 @@ +# Hugging - improves the mood of the user +- type: Interaction + id: Hug + parent: [BaseGlobal, BaseHands] + priority: 2 + #icon: /Textures/Interface/Actions/hug.png + delay: 0.7 + range: {max: 1} + hideByRequirement: true + requirement: + !type:MobStateRequirement + inverted: true + action: + # TODO: this should pull the target closer or sumth, but I need to code that action first + !type:MoodAction + effect: BeingHugged + +# Petting someone (people) - improves the mood of the target +- type: Interaction + id: Pet + parent: [BaseGlobal, BaseHands] + priority: 1 + #icon: /Textures/Interface/Actions/hug.png + delay: 0.4 + range: {max: 1} + hideByRequirement: true + requirement: + !type:ComplexRequirement + requirements: + - !type:MobStateRequirement + inverted: true + - !type:EntityWhitelistRequirement + whitelist: + components: [HumanoidAppearance] + action: + !type:MoodAction + effect: BeingPet + +# Petting someone (animals) - improves the mood of the user and the target +- type: Interaction + id: PetAnimal + parent: Pet + requirement: + !type:ComplexRequirement + requirements: + - !type:MobStateRequirement + allowedStates: [Alive] + - !type:EntityWhitelistRequirement + blacklist: + components: [HumanoidAppearance] + action: + !type:ComplexAction # TODO might wanna make a multiplexer action for situations like this + actions: + - !type:MoodAction + effect: BeingPet + - !type:OnUserAction + action: + !type:MoodAction + effect: PetAnimal + diff --git a/Resources/Prototypes/Interactions/noop_interactions.yml b/Resources/Prototypes/Interactions/noop_interactions.yml index 573f1f7791..6729b36e75 100644 --- a/Resources/Prototypes/Interactions/noop_interactions.yml +++ b/Resources/Prototypes/Interactions/noop_interactions.yml @@ -33,35 +33,6 @@ action: !type:NoOpAction -- type: Interaction - id: Hug - parent: [BaseGlobal, BaseHands] - priority: 2 - #icon: /Textures/Interface/Actions/hug.png - delay: 0.7 - range: {max: 1} - hideByRequirement: true - requirement: - !type:MobStateRequirement - inverted: true - action: - # TODO: this should pull the target closer or sumth, but I need to code that action first - !type:NoOpAction - -- type: Interaction - id: Pet - parent: [BaseGlobal, BaseHands] - priority: 1 - #icon: /Textures/Interface/Actions/hug.png - delay: 0.4 - range: {max: 1} - hideByRequirement: true - requirement: - !type:MobStateRequirement - inverted: true - action: - !type:NoOpAction - # Knocking on the target - windows, doors, etc. - type: Interaction id: KnockOn diff --git a/Resources/Prototypes/Mood/genericPositiveEffects.yml b/Resources/Prototypes/Mood/genericPositiveEffects.yml index 8ac5b25dc1..a4d5ae6ce0 100644 --- a/Resources/Prototypes/Mood/genericPositiveEffects.yml +++ b/Resources/Prototypes/Mood/genericPositiveEffects.yml @@ -2,6 +2,13 @@ id: BeingHugged moodChange: 3 timeout: 120 + category: PositiveInteraction + +- type: moodEffect + id: BeingPet + moodChange: 3 + timeout: 120 + category: PositiveInteraction - type: moodEffect id: ArcadePlay @@ -37,4 +44,4 @@ - type: moodEffect id: TraitSanguine - moodChange: 15 \ No newline at end of file + moodChange: 15 diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index 23001efef4..8ae56baff9 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -9,6 +9,15 @@ - !type:DepartmentTimeRequirement department: Epistemics # DeltaV - Epistemics Department replacing Science min: 3600 + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics startingGear: ForensicMantisGear icon: "JobIconForensicMantis" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Nyanotrasen/metempsychoticNonHumanoids.yml b/Resources/Prototypes/Nyanotrasen/metempsychoticNonHumanoids.yml index feabd9977b..0011464441 100644 --- a/Resources/Prototypes/Nyanotrasen/metempsychoticNonHumanoids.yml +++ b/Resources/Prototypes/Nyanotrasen/metempsychoticNonHumanoids.yml @@ -7,3 +7,4 @@ MobXenoQueen: 0.01 MobCrab: 0.01 MobPenguin: 1 + MobSpaceShrimp: 1 diff --git a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml index e893c17835..c600702b20 100644 --- a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml +++ b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml @@ -720,6 +720,14 @@ materials: PrizeTicket: 200 +- type: latheRecipe + id: BwoinkHammer + result: BwoinkHammer + applyMaterialDiscount: false + completetime: 0.1 + materials: + PrizeTicket: 200 + - type: latheRecipe id: ThronglerToy result: ThronglerToy diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index 2a57d582d8..8e4605909d 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -5,8 +5,17 @@ playTimeTracker: JobChaplain requirements: - !type:CharacterDepartmentTimeRequirement - department: Civilian - min: 14400 #Lost Paradise 4 hour + department: Civilian # DeltaV - Epistemics Department replacing Science + min: 14400 #DeltaV 4 hours + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics startingGear: ChaplainGear icon: "JobIconChaplain" supervisors: job-supervisors-hop diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml index 45c9d2932b..4baf719606 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml @@ -7,6 +7,15 @@ - !type:CharacterDepartmentTimeRequirement department: Epistemics min: 14400 + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics startingGear: LibrarianGear icon: "JobIconLibrarian" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index c903f24aaf..3bbb536b90 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -10,6 +10,15 @@ min: 54000 # DeltaV - 15 hours - !type:CharacterOverallTimeRequirement min: 72000 # DeltaV - 20 hours + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics weight: 10 startingGear: ResearchDirectorGear icon: "JobIconResearchDirector" diff --git a/Resources/Prototypes/SoundCollections/NukeMusic.yml b/Resources/Prototypes/SoundCollections/NukeMusic.yml index 51782d8bd5..b101719139 100644 --- a/Resources/Prototypes/SoundCollections/NukeMusic.yml +++ b/Resources/Prototypes/SoundCollections/NukeMusic.yml @@ -3,4 +3,5 @@ files: - /Audio/StationEvents/running_out.ogg - /Audio/StationEvents/countdown.ogg - - /Audio/StationEvents/clearly_nuclear.ogg \ No newline at end of file + - /Audio/StationEvents/clearly_nuclear.ogg + - /Audio/StationEvents/chip_nightmare.ogg diff --git a/Resources/Prototypes/Traits/neutral.yml b/Resources/Prototypes/Traits/neutral.yml index f4001f4fd0..a4e449f1c0 100644 --- a/Resources/Prototypes/Traits/neutral.yml +++ b/Resources/Prototypes/Traits/neutral.yml @@ -45,8 +45,9 @@ species: - Harpy - Vulpkanin - components: - - type: NormalVision + componentRemovals: + - UltraVision + - DogVision - type: trait id: Saturnine diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 577ce9b746..a8d0dd6ecd 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -203,10 +203,15 @@ - ForensicMantis - Chaplain - Librarian - - !type:CharacterSpeciesRequirement - inverted: true - species: - - IPC + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics - !type:CharacterTraitRequirement inverted: true traits: @@ -229,14 +234,18 @@ - ForensicMantis - Chaplain - Librarian - - !type:CharacterSpeciesRequirement - inverted: true - species: - - IPC + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics - !type:CharacterTraitRequirement inverted: true traits: - - Uncloneable - LatentPsychic - type: trait @@ -246,13 +255,37 @@ psionicPowers: - TelepathyPower requirements: - - !type:CharacterTraitRequirement - traits: - - LatentPsychic - !type:CharacterJobRequirement inverted: true jobs: - ResearchDirector - ForensicMantis - - Chaplain - - Librarian + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterTraitRequirement + traits: + - LatentPsychic + - !type:CharacterJobRequirement + jobs: + - Chaplain + - Librarian + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + - !type:CharacterTraitRequirement + traits: + - AnomalousPositronics + +- type: trait + id: AnomalousPositronics + category: Mental + points: -4 + componentRemovals: + - PsionicInsulation + requirements: + - !type:CharacterSpeciesRequirement + species: + - IPC diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index 22ec83177c..7af57ded37 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -22,17 +22,37 @@ - BasicRoundstartVariation - SubGamemodesRule -#- type: gamePreset -# id: AllAtOnce -# name: all-at-once-title -# description: all-at-once-description -# showInVote: false -# rules: -# - Nukeops -# - Traitor -# - Revolutionary -# - Zombie -# - RampingStationEventScheduler +- type: gamePreset + id: SurvivalIrregular + alias: [irregular] + showInVote: false + name: irregular-title + description: irregular-description + rules: + - IrregularStationEventScheduler + - BasicRoundstartVariation + +- type: gamePreset + id: SurvivalIrregularExtended + alias: [irregular-extended] + showInVote: false + name: irregular-extended-title + description: irregular-extended-description + rules: + - IrregularExtendedStationEventScheduler + - BasicRoundstartVariation + +- type: gamePreset + id: AllAtOnce + name: all-at-once-title + description: all-at-once-description + showInVote: false + rules: + - Nukeops + - Traitor + - Revolutionary + - Zombie + - RampingStationEventScheduler - type: gamePreset id: Extended diff --git a/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/dead.png b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/dead.png new file mode 100644 index 0000000000..f6f1cff31f Binary files /dev/null and b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/dead.png differ diff --git a/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/meta.json b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/meta.json new file mode 100644 index 0000000000..0bf79dc66f --- /dev/null +++ b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/meta.json @@ -0,0 +1,24 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprite by Leonardo-DaBepis", + "states": [ + { + "name": "shrimp", + "directions": 4, + "delays": [ + [ 0.3, 0.4, 0.5, 0.3 ], + [ 0.3, 0.4, 0.5, 0.3 ], + [ 0.3, 0.4, 0.5, 0.3 ], + [ 0.3, 0.4, 0.5, 0.3 ] + ] + }, + { + "name": "dead" + } + ] +} diff --git a/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/shrimp.png b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/shrimp.png new file mode 100644 index 0000000000..c6a19ec13a Binary files /dev/null and b/Resources/Textures/DeltaV/Mobs/Animals/shrimp.rsi/shrimp.png differ diff --git a/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/icon.png b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/icon.png new file mode 100644 index 0000000000..74702e12ef Binary files /dev/null and b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-left.png new file mode 100644 index 0000000000..2fdf4e67a3 Binary files /dev/null and b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-right.png new file mode 100644 index 0000000000..48d2596718 Binary files /dev/null and b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/meta.json b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/meta.json new file mode 100644 index 0000000000..a022711cc3 --- /dev/null +++ b/Resources/Textures/Objects/Fun/bwoink_hammer.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "inhands made by Blu , Icon taken from beestation and belonged to original ss13 https://github.com/BeeStation/BeeStation-Hornet", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +}