Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update 12/28/2024 #122

Merged
merged 11 commits into from
Dec 29, 2024
2 changes: 1 addition & 1 deletion Content.Client/Jittering/JitteringSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
61 changes: 61 additions & 0 deletions Content.Server/Traits/TraitSystem.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
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 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;

Expand All @@ -20,6 +29,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()
{
Expand All @@ -31,6 +45,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<TraitPrototype>(traitId, out var traitPrototype))
Expand All @@ -47,8 +64,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);
}

/// <summary>
Expand All @@ -59,4 +83,41 @@ public void AddTrait(EntityUid uid, TraitPrototype traitPrototype)
foreach (var function in traitPrototype.Functions)
function.OnPlayerSpawn(uid, _componentFactory, EntityManager, _serialization);
}

/// <summary>
/// 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.
/// </summary>
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));
}

/// <summary>
/// https://www.youtube.com/watch?v=X2QMN0a_TrA
/// </summary>
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);
}
}
30 changes: 30 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,13 @@ public static readonly CVarDef<bool>
public static readonly CVarDef<int> GameTraitsDefaultPoints =
CVarDef.Create("game.traits_default_points", 10, CVar.REPLICATED);

/// <summary>
/// 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.
/// </summary>
public static readonly CVarDef<bool> TraitsPunishCheaters =
CVarDef.Create("game.traits_punish_cheaters", true, CVar.REPLICATED);

/// <summary>
/// Whether to allow characters to select loadout items.
Expand Down Expand Up @@ -2813,5 +2820,28 @@ public static readonly CVarDef<float>
/// </summary>
public static readonly CVarDef<bool> UseDynamicHostname =
CVarDef.Create("game.use_dynamic_hostname", false, CVar.SERVERONLY);

#region SoftCrit

/// <summary>
/// Used for basic Soft-Crit implementation. Entities are allowed to crawl when in crit, as this CVar intercepts the mover controller check for incapacitation,
/// and prevents it from stopping movement if this CVar is set to true and the user is Crit but Not Dead. This is only for movement,
/// you still can't stand up while crit, and you're still more or less helpless.
/// </summary>
public static readonly CVarDef<bool> AllowMovementWhileCrit =
CVarDef.Create("mobstate.allow_movement_while_crit", true, CVar.REPLICATED);

public static readonly CVarDef<bool> AllowTalkingWhileCrit =
CVarDef.Create("mobstate.allow_talking_while_crit", true, CVar.REPLICATED);

/// <summary>
/// Currently does nothing because I would have to figure out WHERE I would even put this check, and the mover controller is fairly complicated.
/// The goal is to make it so that attempting to move while in 'soft crit' can potentially cause further injury, causing you to die faster. Ideally there would be special
/// actions that can be performed in soft crit, such as applying pressure to your own injuries to slow down the bleedout, or other varieties of "Will To Live".
/// </summary>
public static readonly CVarDef<bool> DamageWhileCritMove =
CVarDef.Create("mobstate.damage_while_crit_move", false, CVar.REPLICATED);

#endregion
}
}
28 changes: 26 additions & 2 deletions Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Shared.Bed.Sleep;
using Content.Shared.Buckle.Components;
using Content.Shared.CCVar;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage.ForceSay;
using Content.Shared.Emoting;
Expand All @@ -16,17 +17,20 @@
using Content.Shared.Standing;
using Content.Shared.Strip.Components;
using Content.Shared.Throwing;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components;

namespace Content.Shared.Mobs.Systems;

public partial class MobStateSystem
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;

//General purpose event subscriptions. If you can avoid it register these events inside their own systems
private void SubscribeEvents()
{
SubscribeLocalEvent<MobStateComponent, BeforeGettingStrippedEvent>(OnGettingStripped);
SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(OnDirectionAttempt);
SubscribeLocalEvent<MobStateComponent, UseAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, AttackAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, ConsciousAttemptEvent>(CheckAct);
Expand All @@ -38,7 +42,7 @@ private void SubscribeEvents()
SubscribeLocalEvent<MobStateComponent, DropAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PickupAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, StartPullAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(OnMoveAttempt);
SubscribeLocalEvent<MobStateComponent, StandAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, PointAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, TryingToSleepEvent>(OnSleepAttempt);
Expand All @@ -48,6 +52,23 @@ private void SubscribeEvents()
SubscribeLocalEvent<MobStateComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
}

private void OnDirectionAttempt(Entity<MobStateComponent> ent, ref ChangeDirectionAttemptEvent args)
{
if (ent.Comp.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

CheckAct(ent.Owner, ent.Comp, args);
}

private void OnMoveAttempt(Entity<MobStateComponent> ent, ref UpdateCanMoveEvent args)
{
if (ent.Comp.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

CheckAct(ent.Owner, ent.Comp, args);
}


private void OnUnbuckleAttempt(Entity<MobStateComponent> ent, ref UnbuckleAttemptEvent args)
{
// TODO is this necessary?
Expand Down Expand Up @@ -145,6 +166,9 @@ private void OnSpeakAttempt(EntityUid uid, MobStateComponent component, SpeakAtt
return;
}

if (component.CurrentState is MobState.Critical && _configurationManager.GetCVar(CCVars.AllowTalkingWhileCrit))
return;

CheckAct(uid, component, args);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,11 @@ private void HandleDirChange(EntityUid entity, Direction dir, ushort subTick, bo
if (MoverQuery.TryGetComponent(entity, out var mover))
SetMoveInput(mover, MoveButtons.None);

if (!_mobState.IsIncapacitated(entity))
HandleDirChange(relayMover.RelayEntity, dir, subTick, state);
if (_mobState.IsDead(entity)
|| _mobState.IsCritical(entity) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
return;

HandleDirChange(relayMover.RelayEntity, dir, subTick, state);

return;
}
Expand Down
7 changes: 4 additions & 3 deletions Content.Shared/Movement/Systems/SharedMoverController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ protected void HandleMobMovement(
var canMove = mover.CanMove;
if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget))
{
if (_mobState.IsIncapacitated(relayTarget.Source) ||
TryComp<SleepingComponent>(relayTarget.Source, out _) ||
!MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover))
if (_mobState.IsDead(relayTarget.Source)
|| TryComp<SleepingComponent>(relayTarget.Source, out _)
|| !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover)
|| _mobState.IsCritical(relayTarget.Source) && !_configManager.GetCVar(CCVars.AllowMovementWhileCrit))
{
canMove = false;
}
Expand Down
40 changes: 40 additions & 0 deletions Resources/Changelog/Changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8757,3 +8757,43 @@ 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
- author: VMSolidus
changes:
- type: Add
message: >-
Added server config options for basic "Soft-Crit". When enabled,
characters who are critically injured can still slowly crawl, but are
otherwise still helpless and dying.
id: 6592
time: '2024-12-26T03:50:10.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1370
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
- PlushieTrystan
- PlushieSlips
- PlushieJester
- PlushieHarpy
- PlushieMort
chance: 0.5
offset: 0.2

Expand Down
60 changes: 60 additions & 0 deletions Resources/Prototypes/Entities/Objects/Fun/toys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
- !type:CharacterTraitRequirement
traits:
- ShadowkinBlackeye
special:
- !type:AddComponentSpecial
components:
- type: Pacified

- type: startingGear
id: PrisonerGear
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions Resources/Textures/Objects/Fun/toys.rsi/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,12 @@
},
{
"name": "shadowkin"
},
{
"name": "mortplush"
},
{
"name": "harpyplushie"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading