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

Mass Contests Remake #480

Merged
merged 15 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Content.Shared.Actions.Events;
using Content.Shared.Administration.Components;
using Content.Shared.CombatMode;
using Content.Shared.Contests;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
Expand Down Expand Up @@ -43,6 +44,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
[Dependency] private readonly SolutionContainerSystem _solutions = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly ContestsSystem _contests = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -138,7 +140,7 @@ protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid
if (attemptEvent.Cancelled)
return false;

var chance = CalculateDisarmChance(user, target, inTargetHand, combatMode);
var chance = CalculateDisarmChance(user, target, inTargetHand, combatMode) * _contests.MassContest(user, target);

if (_random.Prob(chance))
{
Expand Down
9 changes: 6 additions & 3 deletions Content.Server/Weapons/Ranged/Systems/GunSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Content.Server.Power.EntitySystems;
using Content.Server.Stunnable;
using Content.Server.Weapons.Ranged.Components;
using Content.Shared.Contests;
using Content.Shared.Damage;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
Expand Down Expand Up @@ -39,6 +40,7 @@ public sealed partial class GunSystem : SharedGunSystem
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly ContestsSystem _contests = default!;

public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation;
public const float GunClumsyChance = 0.5f;
Expand Down Expand Up @@ -97,7 +99,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid?
var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem);
var mapDirection = toMap - fromMap.Position;
var mapAngle = mapDirection.ToAngle();
var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle());
var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle(), user);

// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out var grid)
Expand Down Expand Up @@ -311,15 +313,16 @@ private Angle[] LinearSpread(Angle start, Angle end, int intervals)
return angles;
}

private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle direction)
private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle direction, EntityUid? shooter)
{
var timeSinceLastFire = (curTime - component.LastFire).TotalSeconds;
var newTheta = MathHelper.Clamp(component.CurrentAngle.Theta + component.AngleIncreaseModified.Theta - component.AngleDecayModified.Theta * timeSinceLastFire, component.MinAngleModified.Theta, component.MaxAngleModified.Theta);
component.CurrentAngle = new Angle(newTheta);
component.LastFire = component.NextFire;

// Convert it so angle can go either side.
var random = Random.NextFloat(-0.5f, 0.5f);

var random = Random.NextFloat(-0.5f, 0.5f) / _contests.MassContest(shooter);
var spread = component.CurrentAngle.Theta * random;
var angle = new Angle(direction.Theta + component.CurrentAngle.Theta * random);
DebugTools.Assert(spread <= component.MaxAngleModified.Theta);
Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2138,5 +2138,18 @@ public static readonly CVarDef<float>
/// </summary>
public static readonly CVarDef<float> StationGoalsChance =
CVarDef.Create("game.station_goals_chance", 0.1f, CVar.SERVERONLY);

/// <summary>
/// Toggles all MassContest functions. All mass contests output 1f when false
/// </summary>
public static readonly CVarDef<bool> DoMassContests =
CVarDef.Create("contests.do_mass_contests", true, CVar.REPLICATED | CVar.SERVER);

/// <summary>
/// The maximum amount that Mass Contests can modify a physics multiplier, given as a +/- percentage
/// Default of 0.25f outputs between * 0.75f and 1.25f
/// </summary>
public static readonly CVarDef<float> MassContestsMaxPercentage =
CVarDef.Create("contests.max_percentage", 0.25f, CVar.REPLICATED | CVar.SERVER);
}
}
19 changes: 19 additions & 0 deletions Content.Shared/Contests/ContestsSystem.CVars.cs
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Content.Shared.CCVar;
using Robust.Shared.Configuration;

namespace Content.Shared.Contests
{
public sealed partial class ContestsSystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;

public bool DoMassContests { get; private set; }
public float MassContestsMaxPercentage { get; private set; }

private void InitializeCVars()
{
Subs.CVar(_cfg, CCVars.DoMassContests, value => DoMassContests = value, true);
Subs.CVar(_cfg, CCVars.MassContestsMaxPercentage, value => MassContestsMaxPercentage = value, true);
}
}
}
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
116 changes: 116 additions & 0 deletions Content.Shared/Contests/ContestsSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using Robust.Shared.Physics.Components;

namespace Content.Shared.Contests
{
public sealed partial class ContestsSystem : EntitySystem
{
// Defaulted to the average mass of an adult human
/// <summary>
/// The presumed average mass of a player entity
/// </summary>
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
private const float AverageMass = 71f;
public override void Initialize()
{
base.Initialize();

InitializeCVars();

}
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
// REGION
// Mass Contests
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// Outputs the ratio of mass between a performer and the average human mass
/// </summary>
/// <param name="performerUid">Uid of Performer</param>
/// <returns></returns>
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
public float MassContest(EntityUid performerUid, float otherMass = AverageMass)
{
if (DoMassContests
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& performerPhysics.Mass != 0)
return Math.Clamp(performerPhysics.Mass / otherMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}

public float MassContest(EntityUid? performerUid, float otherMass = AverageMass)
{
if (DoMassContests)
{
var ratio = performerUid is { } uid ? MassContest(uid, otherMass) : 1f;
return ratio;
}

return 1f;
}

/// <summary>
/// Outputs the ratio of mass between a performer and the average human mass
/// If a function already has the performer's physics component, this is faster
/// </summary>
/// <param name="performerPhysics"></param>
/// <returns></returns>
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
public float MassContest(PhysicsComponent performerPhysics, float otherMass = AverageMass)
{
if (DoMassContests
&& performerPhysics.Mass != 0)
return Math.Clamp(performerPhysics.Mass / otherMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}

/// <summary>
/// Outputs the ratio of mass between a performer and a target, accepts either EntityUids or PhysicsComponents in any combination
/// If you have physics components already in your function, use those instead
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="performerUid"></param>
/// <param name="targetUid"></param>
/// <returns></returns>
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
public float MassContest(EntityUid performerUid, EntityUid targetUid)
{
if (DoMassContests
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& TryComp<PhysicsComponent>(targetUid, out var targetPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}

/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(EntityUid performerUid, PhysicsComponent targetPhysics)
{
if (DoMassContests
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}

/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(PhysicsComponent performerPhysics, EntityUid targetUid)
{
if (DoMassContests
&& TryComp<PhysicsComponent>(targetUid, out var targetPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}

/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(PhysicsComponent performerPhysics, PhysicsComponent targetPhysics)
{
if (DoMassContests
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - MassContestsMaxPercentage, 1 + MassContestsMaxPercentage);

return 1f;
}
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
}
}
35 changes: 21 additions & 14 deletions Content.Shared/Cuffs/Components/HandcuffComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,37 @@ public sealed partial class HandcuffComponent : Component
/// <summary>
/// The time it takes to cuff an entity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float CuffTime = 3.5f;

/// <summary>
/// The time it takes to uncuff an entity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float UncuffTime = 3.5f;

/// <summary>
/// The time it takes for a cuffed entity to uncuff itself.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float BreakoutTime = 15f;

/// <summary>
/// If an entity being cuffed is stunned, this amount of time is subtracted from the time it takes to add/remove their cuffs.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float StunBonus = 2f;

/// <summary>
/// Will the cuffs break when removed?
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool BreakOnRemove;

/// <summary>
/// Will the cuffs break when removed?
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public EntProtoId? BrokenPrototype;

/// <summary>
Expand All @@ -55,35 +55,42 @@ public sealed partial class HandcuffComponent : Component
/// <summary>
/// The path of the RSI file used for the player cuffed overlay.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public string? CuffedRSI = "Objects/Misc/handcuffs.rsi";

/// <summary>
/// The iconstate used with the RSI file for the player cuffed overlay.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
[DataField, AutoNetworkedField]
public string? BodyIconState = "body-overlay";

/// <summary>
/// An opptional color specification for <see cref="BodyIconState"/>
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public Color Color = Color.White;

[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public SoundSpecifier StartCuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_start.ogg");

[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public SoundSpecifier EndCuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_end.ogg");

[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public SoundSpecifier StartBreakoutSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_breakout_start.ogg");

[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public SoundSpecifier StartUncuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_takeoff_start.ogg");

[DataField, ViewVariables(VVAccess.ReadWrite)]
[DataField]
public SoundSpecifier EndUncuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_takeoff_end.ogg");

/// <summary>
/// Both a bool and a multiplier combined. If it is 0, handcuffs are unaffected by mass contests. The absolute value of any nonzero acts as a multiplier on how much mass affects uncuff speed.
/// A value of 1 provides the full modifier from MassContest. 0.5 is half the effect of mass contests, and so on.
/// </summary>
[DataField]
public float UncuffMassModifier = 0f;
VMSolidus marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion Content.Shared/Cuffs/SharedCuffableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Content.Shared.Alert;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Buckle.Components;
using Content.Shared.Contests;
using Content.Shared.Cuffs.Components;
using Content.Shared.Database;
using Content.Shared.DoAfter;
Expand Down Expand Up @@ -58,6 +59,7 @@ public abstract partial class SharedCuffableSystem : EntitySystem
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly UseDelaySystem _delay = default!;
[Dependency] private readonly ContestsSystem _contests = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -559,7 +561,7 @@ public void TryUncuff(EntityUid target, EntityUid user, EntityUid? cuffsToRemove
return;
}

var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime;
var uncuffTime = (isOwner ? cuff.BreakoutTime : cuff.UncuffTime) * (_contests.MassContest(user) * Math.Abs(cuff.UncuffMassModifier));

if (isOwner)
{
Expand Down
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
path: /Audio/Items/Handcuffs/rope_breakout.ogg
startBreakoutSound:
path: /Audio/Items/Handcuffs/rope_takeoff.ogg
uncuffMassModifier: 1
- type: Construction
graph: makeshifthandcuffs
node: cuffscable
Expand Down Expand Up @@ -93,6 +94,7 @@
path: /Audio/Items/Handcuffs/rope_breakout.ogg
startBreakoutSound:
path: /Audio/Items/Handcuffs/rope_takeoff.ogg
uncuffMassModifier: 1
- type: Sprite
sprite: Objects/Misc/zipties.rsi
state: cuff
Expand Down
Loading