diff --git a/Content.Client/Jittering/JitteringSystem.cs b/Content.Client/Jittering/JitteringSystem.cs index 185bd490d3b..aafaf318bb9 100644 --- a/Content.Client/Jittering/JitteringSystem.cs +++ b/Content.Client/Jittering/JitteringSystem.cs @@ -45,7 +45,7 @@ private void OnShutdown(EntityUid uid, JitteringComponent jittering, ComponentSh private void OnAnimationCompleted(EntityUid uid, JitteringComponent jittering, AnimationCompletedEvent args) { - if(args.Key != _jitterAnimationKey) + if (args.Key != _jitterAnimationKey || jittering.LifeStage >= ComponentLifeStage.Stopping) return; if (TryComp(uid, out AnimationPlayerComponent? animationPlayer) diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index ecf4b10cbac..3e0866a2bab 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -1,15 +1,24 @@ using System.Linq; +using Content.Server.Administration.Logs; +using Content.Server.Administration.Systems; +using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.Players.PlayTimeTracking; +using Content.Shared.CCVar; +using Content.Shared.Chat; using Content.Shared.Customization.Systems; +using Content.Shared.Database; using Content.Shared.Players; using Content.Shared.Roles; using Content.Shared.Traits; +using Robust.Server.Player; using Robust.Shared.Configuration; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; +using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using Robust.Shared.Utility; +using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.Traits; @@ -21,6 +30,11 @@ public sealed class TraitSystem : EntitySystem [Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!; [Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly AdminSystem _adminSystem = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IChatManager _chatManager = default!; public override void Initialize() { @@ -32,6 +46,9 @@ public override void Initialize() // When the player is spawned in, add all trait components selected during character creation private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) { + var pointsTotal = _configuration.GetCVar(CCVars.GameTraitsDefaultPoints); + var traitSelections = _configuration.GetCVar(CCVars.GameTraitsMax); + foreach (var traitId in args.Profile.TraitPreferences) { if (!_prototype.TryIndex(traitId, out var traitPrototype)) @@ -48,8 +65,15 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) out _)) continue; + // To check for cheaters. :FaridaBirb.png: + pointsTotal += traitPrototype.Points; + --traitSelections; + AddTrait(args.Mob, traitPrototype); } + + if (pointsTotal < 0 || traitSelections < 0) + PunishCheater(args.Mob); } /// @@ -60,4 +84,41 @@ public void AddTrait(EntityUid uid, TraitPrototype traitPrototype) foreach (var function in traitPrototype.Functions) function.OnPlayerSpawn(uid, _componentFactory, EntityManager, _serialization); } + + /// + /// On a non-cheating client, it's not possible to save a character with a negative number of traits. This can however + /// trigger incorrectly if a character was saved, and then at a later point in time an admin changes the traits Cvars to reduce the points. + /// Or if the points costs of traits is increased. + /// + private void PunishCheater(EntityUid uid) + { + _adminLog.Add(LogType.AdminMessage, LogImpact.High, + $"{ToPrettyString(uid):entity} attempted to spawn with an invalid trait list. This might be a mistake, or they might be cheating"); + + if (!_configuration.GetCVar(CCVars.TraitsPunishCheaters) + || !_playerManager.TryGetSessionByEntity(uid, out var targetPlayer)) + return; + + // For maximum comedic effect, this is plenty of time for the cheater to get on station and start interacting with people. + var timeToDestroy = _random.NextFloat(120, 360); + + Timer.Spawn(TimeSpan.FromSeconds(timeToDestroy), () => VaporizeCheater(targetPlayer)); + } + + /// + /// https://www.youtube.com/watch?v=X2QMN0a_TrA + /// + private void VaporizeCheater (Robust.Shared.Player.ICommonSession targetPlayer) + { + _adminSystem.Erase(targetPlayer); + + var feedbackMessage = $"[font size=24][color=#ff0000]{"You have spawned in with an illegal trait point total. If this was a result of cheats, then your nonexistence is a skill issue. Otherwise, feel free to click 'Return To Lobby', and fix your trait selections."}[/color][/font]"; + _chatManager.ChatMessageToOne( + ChatChannel.Emotes, + feedbackMessage, + feedbackMessage, + EntityUid.Invalid, + false, + targetPlayer.Channel); + } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 218507ff4bb..0723517c2f4 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -430,6 +430,13 @@ public static readonly CVarDef public static readonly CVarDef GameTraitsDefaultPoints = CVarDef.Create("game.traits_default_points", 10, CVar.REPLICATED); + /// + /// Whether the game will SMITE people who used cheat engine to spawn with all of the traits. + /// Illegal trait totals will still be logged even if this is disabled. + /// If you are intending to decrease the trait points availability, or modify the costs of traits, consider temporarily disabling this. + /// + public static readonly CVarDef TraitsPunishCheaters = + CVarDef.Create("game.traits_punish_cheaters", true, CVar.REPLICATED); /// /// Whether to allow characters to select loadout items. diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 186a28f65b1..95604a1e21c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -8757,3 +8757,33 @@ Entries: id: 6587 time: '2024-12-22T10:26:41.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1366 +- author: juniwoofs + changes: + - type: Add + message: two new cuddly friends to the station! (harpy and morty plush) + id: 6588 + time: '2024-12-22T19:24:58.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1369 +- author: VMSolidus + changes: + - type: Add + message: >- + Implemented Anti-cheat for Traits. Attempting to join a round with an + illegal traits list will result in hilarious consequences. + id: 6589 + time: '2024-12-22T19:55:22.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1358 +- author: VMSolidus + changes: + - type: Add + message: Prisoners now spawn with a Pacifier Implant. + id: 6590 + time: '2024-12-22T19:56:21.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1341 +- author: sleepyyapril + changes: + - type: Fix + message: Fixed jittering displacing your character when shaken. + id: 6591 + time: '2024-12-22T19:57:10.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1334 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml index 96b5f7aa8c5..62c9e48ca65 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml @@ -69,6 +69,8 @@ - PlushieTrystan - PlushieSlips - PlushieJester + - PlushieHarpy + - PlushieMort chance: 0.5 offset: 0.2 diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index a547f33b59a..d5ee3e0b4d5 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1966,3 +1966,63 @@ - type: Sprite sprite: Objects/Fun/toys.rsi state: shadowkin + +- type: entity + parent: BasePlushie + id: PlushieMort + name: morty plushie + description: A plushie of the lovely Morty. It's a resilient, yet sensitive type of plush. + components: + - type: Sprite + sprite: Objects/Fun/toys.rsi + state: mortplush + +- type: entity + parent: BasePlushie + id: PlushieHarpy + name: harpy plushie + description: A soft plushie of a harpy! A small tag on it guarantees that all feathers are ethically sourced. + components: + - type: Sprite + sprite: Objects/Fun/toys.rsi + state: harpyplushie + - type: StaminaDamageOnHit + damage: 0.8 + - type: EmitSoundOnActivate + sound: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 + - type: EmitSoundOnUse + sound: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + - type: EmitSoundOnCollide + sound: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 + - type: EmitSoundOnLand + sound: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 + - type: UseDelay + delay: 0.8 + - type: MeleeWeapon + wideAnimationRotation: -135 + attackRate: 0.5 + damage: + types: + Blunt: 0 + soundHit: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 + soundSwing: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 + soundNoDamage: + path: /Audio/DeltaV/Voice/Harpy/caw1.ogg + params: + variation: 0.05 \ No newline at end of file diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml index 0ca17947425..638aaecd2cf 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml @@ -22,6 +22,10 @@ - !type:CharacterTraitRequirement traits: - ShadowkinBlackeye + special: + - !type:AddComponentSpecial + components: + - type: Pacified - type: startingGear id: PrisonerGear diff --git a/Resources/Textures/Objects/Fun/toys.rsi/harpyplushie.png b/Resources/Textures/Objects/Fun/toys.rsi/harpyplushie.png new file mode 100644 index 00000000000..d93178d3f7b Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/harpyplushie.png differ diff --git a/Resources/Textures/Objects/Fun/toys.rsi/meta.json b/Resources/Textures/Objects/Fun/toys.rsi/meta.json index cc03557e0b4..7dafd603638 100644 --- a/Resources/Textures/Objects/Fun/toys.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toys.rsi/meta.json @@ -389,6 +389,12 @@ }, { "name": "shadowkin" + }, + { + "name": "mortplush" + }, + { + "name": "harpyplushie" } ] } \ No newline at end of file diff --git a/Resources/Textures/Objects/Fun/toys.rsi/mortplush.png b/Resources/Textures/Objects/Fun/toys.rsi/mortplush.png new file mode 100644 index 00000000000..28b3f1c6ebb Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/mortplush.png differ