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

shadekin update #364

Merged
merged 9 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
31 changes: 31 additions & 0 deletions Content.Server/ADT/EntityEffects/Effects/RandomTeleport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Robust.Shared.Prototypes;
using Content.Shared.StatusEffect;
using Content.Shared.EntityEffects;
using Content.Server.ADT.Shadekin;

namespace Content.Server.Chemistry.ReagentEffects
{
/// <summary>
/// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
/// adding one if not there and to change the movespeed
/// </summary>
public sealed partial class RandomTeleport : EntityEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
return Loc.GetString("reagent-effect-guidebook-teleport",
("chance", Probability));
}

public override void Effect(EntityEffectBaseArgs ev)
{
if (ev is not EntityEffectReagentArgs args)
return;

var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem<StatusEffectsSystem>();
var shadekin = args.EntityManager.EntitySysManager.GetEntitySystem<ShadekinSystem>();

shadekin.TeleportRandomly(args.TargetEntity, 2f);
}
}
}
229 changes: 229 additions & 0 deletions Content.Server/ADT/Shadekin/ShadekinSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
using Content.Shared.ADT.Shadekin.Components;
using Robust.Shared.Timing;
using Content.Shared.Damage.Systems;
using Content.Shared.Humanoid;
using Content.Server.Humanoid;
using Content.Shared.ADT.Shadekin;
using System.Numerics;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Robust.Server.GameObjects;
using Content.Shared.Effects;
using Robust.Shared.Player;
using Robust.Server.Audio;
using Robust.Shared.Map;
using Robust.Shared.Random;
using Content.Shared.Examine;
using Content.Server.Actions;
using Content.Server.Station.Systems;
using Content.Shared.Alert;
using Robust.Shared.Prototypes;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Movement.Pulling.Components;

namespace Content.Server.ADT.Shadekin;

public sealed partial class ShadekinSystem : EntitySystem
{
[Dependency] protected readonly IGameTiming _timing = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedColorFlashEffectSystem _colorFlash = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
[Dependency] private readonly AlertsSystem _alert = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly PullingSystem _pulling = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ShadekinComponent, ShadekinTeleportActionEvent>(OnTeleport);
SubscribeLocalEvent<ShadekinComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<ShadekinComponent, ComponentInit>(OnMapInit);
SubscribeLocalEvent<ShadekinComponent, ComponentShutdown>(OnShutdown);

SubscribeLocalEvent<ShadekinComponent, HumanoidProfileLoadedEvent>(OnProfileLoaded);
}

public override void Update(float frameTime)
{
base.Update(frameTime);

var query = EntityQueryEnumerator<ShadekinComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.NextSecond > _timing.CurTime)
continue;
if (comp.Blackeye)
continue;
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 4));
comp.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);

if (comp.PowerLevel >= comp.PowerLevelMax)
comp.MaxedPowerAccumulator += 1f;
else
{
comp.PowerLevel += comp.PowerLevelGain * comp.PowerLevelGainMultiplier;
comp.MaxedPowerAccumulator = 0f;
}

if (comp.PowerLevel < comp.PowerLevelMin)
comp.MinPowerAccumulator += 1f;
else
comp.MinPowerAccumulator = 0f;

if (comp.MinPowerAccumulator >= comp.MinPowerRoof)
BlackEye(uid);
if (comp.MaxedPowerAccumulator >= comp.MaxedPowerRoof)
TeleportRandomly(uid, comp);
}
}

private void OnMapInit(EntityUid uid, ShadekinComponent comp, ComponentInit args)
{
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 4));
}

private void OnShutdown(EntityUid uid, ShadekinComponent comp, ComponentShutdown args)
{
_alert.ClearAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"));
if (comp.ActionEntity != null)
_action.RemoveAction(uid, comp.ActionEntity);
}

private void OnProfileLoaded(EntityUid uid, ShadekinComponent comp, ref HumanoidProfileLoadedEvent args)
{
// if ( // Оно очень странно получается, работает только при позднем подключении
// args.Profile.Appearance.EyeColor.B < 100f &&
// args.Profile.Appearance.EyeColor.R < 100f &&
// args.Profile.Appearance.EyeColor.G < 100f)
// {
// comp.Blackeye = true;
// comp.PowerLevelGainEnabled = false;
// comp.PowerLevel = 0f;
// return;
// }
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 4));
_action.AddAction(uid, ref comp.ActionEntity, comp.ActionProto);
}

private void OnTeleport(EntityUid uid, ShadekinComponent comp, ShadekinTeleportActionEvent args)
{
if (args.Handled)
return;
// if (_interaction.InRangeUnobstructed(uid, args.Target, -1f))
// return;
if (!TryUseAbility(uid, 50))
return;
args.Handled = true;
if (TryComp<PullerComponent>(uid, out var puller) && puller.Pulling != null && TryComp<PullableComponent>(puller.Pulling, out var pullable))
_pulling.TryStopPull(puller.Pulling.Value, pullable);
_transform.SetCoordinates(uid, args.Target);
_colorFlash.RaiseEffect(Color.DarkCyan, new List<EntityUid>() { uid }, Filter.Pvs(uid, entityManager: EntityManager));
_audio.PlayPvs("/Audio/ADT/Shadekin/shadekin-transition.ogg", uid);
}

private void OnExamine(EntityUid uid, ShadekinComponent comp, ExaminedEvent args)
{
var level = "max";
if (comp.PowerLevel == 250f)
level = "max";
if (comp.PowerLevel < 250f)
level = "good";
if (comp.PowerLevel <= 200f)
level = "okay";
if (comp.PowerLevel <= 100f)
level = "bad";
if (comp.PowerLevel <= 50f)
level = "worst";

if (args.Examiner == uid)
args.PushMarkup(Loc.GetString("shadekin-examine-self-" + level, ("power", comp.PowerLevel.ToString())));
}

public void TeleportRandomly(EntityUid uid, ShadekinComponent? comp)
{
if (!Resolve(uid, ref comp))
return;
var coordsValid = false;
EntityCoordinates coords = Transform(uid).Coordinates;

while (!coordsValid)
{
var newCoords = new EntityCoordinates(Transform(uid).ParentUid, coords.X + _random.NextFloat(-5f, 5f), coords.Y + _random.NextFloat(-5f, 5f));
if (_interaction.InRangeUnobstructed(uid, newCoords, -1f))
{
TryUseAbility(uid, 40, false);
if (TryComp<PullerComponent>(uid, out var puller) && puller.Pulling != null && TryComp<PullableComponent>(puller.Pulling, out var pullable))
_pulling.TryStopPull(puller.Pulling.Value, pullable);
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 4));
_transform.SetCoordinates(uid, newCoords);
_transform.AttachToGridOrMap(uid, Transform(uid));
_colorFlash.RaiseEffect(Color.DarkCyan, new List<EntityUid>() { uid }, Filter.Pvs(uid, entityManager: EntityManager));
_audio.PlayPvs("/Audio/ADT/Shadekin/shadekin-transition.ogg", uid);
comp.MaxedPowerAccumulator = 0f;
coordsValid = true;
break;
}
}
}

public void TeleportRandomly(EntityUid uid, float range = 5f)
{
var coordsValid = false;
EntityCoordinates coords = Transform(uid).Coordinates;

while (!coordsValid)
{
var newCoords = new EntityCoordinates(Transform(uid).ParentUid, coords.X + _random.NextFloat(-range, range), coords.Y + _random.NextFloat(-range, range));
if (_interaction.InRangeUnobstructed(uid, newCoords, -1f))
{
if (TryComp<PullerComponent>(uid, out var puller) && puller.Pulling != null && TryComp<PullableComponent>(puller.Pulling, out var pullable))
_pulling.TryStopPull(puller.Pulling.Value, pullable);
_transform.SetCoordinates(uid, newCoords);
_transform.AttachToGridOrMap(uid, Transform(uid));
_colorFlash.RaiseEffect(Color.DarkCyan, new List<EntityUid>() { uid }, Filter.Pvs(uid, entityManager: EntityManager));
_audio.PlayPvs("/Audio/ADT/Shadekin/shadekin-transition.ogg", uid);
coordsValid = true;
break;
}
}
}

public bool TryUseAbility(EntityUid uid, FixedPoint2 cost, bool allowBlackeye = true, ShadekinComponent? comp = null)
{
if (!Resolve(uid, ref comp))
return false;
if (comp.PowerLevel <= cost && allowBlackeye)
{
BlackEye(uid);
return false;
}
comp.PowerLevel -= cost.Float();
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 4));
return true;
}

public void BlackEye(EntityUid uid, ShadekinComponent? comp = null)
{
if (!Resolve(uid, ref comp))
return;

comp.Blackeye = true;
comp.PowerLevelGainEnabled = false;
comp.PowerLevel = 0f;
_stamina.TakeStaminaDamage(uid, 150f);
if (TryComp<HumanoidAppearanceComponent>(uid, out var humanoid))
{
humanoid.EyeColor = Color.Black;
Dirty(uid, humanoid);
}
_alert.ShowAlert(uid, _proto.Index<AlertPrototype>("ShadekinPower"), (short) Math.Clamp(Math.Round(comp.PowerLevel / 50f), 0, 5));
_action.RemoveAction(comp.ActionEntity);
}
}
2 changes: 1 addition & 1 deletion Content.Server/Body/Components/RespiratorComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public sealed partial class RespiratorComponent : Component
/// The emote when gasps
/// </summary>
[DataField]
public ProtoId<EmotePrototype> GaspEmote = "Gasp";
public ProtoId<EmotePrototype>? GaspEmote = "Gasp"; // ADT tweak

/// <summary>
/// How many cycles in a row has the mob been under-saturated?
Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Body/Systems/RespiratorSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public override void Update(float frameTime)

if (respirator.Saturation < respirator.SuffocationThreshold)
{
if (_gameTiming.CurTime >= respirator.LastGaspEmoteTime + respirator.GaspEmoteCooldown)
if (_gameTiming.CurTime >= respirator.LastGaspEmoteTime + respirator.GaspEmoteCooldown && respirator.GaspEmote != null) // ADT tweak
{
respirator.LastGaspEmoteTime = _gameTiming.CurTime;
_chat.TryEmoteWithChat(uid, respirator.GaspEmote, ChatTransmitRange.HideChat, ignoreActionBlocker: true);
Expand Down
53 changes: 33 additions & 20 deletions Content.Shared/ADT/Shadekin/Components/ShadekinComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,29 @@ namespace Content.Shared.ADT.Shadekin.Components;
public sealed partial class ShadekinComponent : Component
{
#region Random occurrences
/// <summary>
/// Accumulator that indicates how long shadekin were with max energy level
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float MaxedPowerAccumulator = 0f;

/// <summary>
/// Teleport randomly if MaxedPowerAccumulator is greater
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float MaxedPowerRoof = 0f;

[ViewVariables(VVAccess.ReadWrite)]
public float MaxedPowerRateMin = 45f;

[ViewVariables(VVAccess.ReadWrite)]
public float MaxedPowerRateMax = 150f;

public float MaxedPowerRoof = 45f;

/// <summary>
/// Accumulator that indicates how long shadekin were with min energy level
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float MinPowerAccumulator = 0f;

/// <summary>
/// Blackeye if MinPowerAccumulator is greater
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float MinPowerRoof = 0f;

[ViewVariables(VVAccess.ReadWrite)]
public float MinPowerMin = 15f;

[ViewVariables(VVAccess.ReadWrite)]
public float MinPowerMax = 60f;
public float MinPowerRoof = 30f;
#endregion


Expand All @@ -57,7 +56,7 @@ public sealed partial class ShadekinComponent : Component
public float PowerLevel
{
get => _powerLevel;
set => _powerLevel = Math.Clamp(value, PowerLevelMin, PowerLevelMax);
set => _powerLevel = Math.Clamp(value, PowerThresholds[ShadekinPowerThreshold.Min], PowerThresholds[ShadekinPowerThreshold.Max]);
}
public float _powerLevel = 150f;

Expand All @@ -68,16 +67,16 @@ public float PowerLevel
public float PowerLevelMax = PowerThresholds[ShadekinPowerThreshold.Max];

/// <summary>
/// Blackeyes if PowerLevel is this value.
/// Blackeye chance if PowerLevel less than this value.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public float PowerLevelMin = PowerThresholds[ShadekinPowerThreshold.Min];
public float PowerLevelMin = PowerThresholds[ShadekinPowerThreshold.Tired];

/// <summary>
/// How much energy is gained per second.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public float PowerLevelGain = 0.75f;
public float PowerLevelGain = 0.5f;

/// <summary>
/// Power gain multiplier
Expand All @@ -97,8 +96,17 @@ public float PowerLevel
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public bool Blackeye = false;

[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public bool RoundstartBlackeyeChecked = false;

/// <summary>
/// Next second to gain energy
/// </summary>
[ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public TimeSpan NextSecond = TimeSpan.Zero;

public static readonly Dictionary<ShadekinPowerThreshold, float> PowerThresholds = new()

public static Dictionary<ShadekinPowerThreshold, float> PowerThresholds = new()
{
{ ShadekinPowerThreshold.Max, 250.0f },
{ ShadekinPowerThreshold.Great, 200.0f },
Expand All @@ -108,6 +116,11 @@ public float PowerLevel
{ ShadekinPowerThreshold.Min, 0.0f },
};
#endregion

#region Actions
public string ActionProto = "ActionShadekinTeleport";
public EntityUid? ActionEntity;
#endregion
}

public enum ShadekinPowerThreshold : byte
Expand Down
9 changes: 9 additions & 0 deletions Content.Shared/ADT/Shadekin/SharedShadekin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Content.Shared.Actions;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;

namespace Content.Shared.ADT.Shadekin;

public sealed partial class ShadekinTeleportActionEvent : WorldTargetActionEvent
{
}
Loading
Loading