Добавлена новая раса Кицунэ Co-authored-by: Daniel <77834935+Orvex07@users.noreply.github.com>
208 lines
7.9 KiB
C#
208 lines
7.9 KiB
C#
using Content.Server.Actions;
|
|
using Content.Server.DoAfter;
|
|
using Content.Server.Polymorph.Components;
|
|
using Content.Server.Polymorph.Systems;
|
|
using Content.Shared._Sunrise.Kitsune;
|
|
using Content.Shared._Sunrise.SpriteColor;
|
|
using Content.Shared._Sunrise.TTS;
|
|
using Content.Shared.Actions;
|
|
using Content.Shared.Actions.Components;
|
|
using Content.Shared.Damage;
|
|
using Content.Shared.Damage.Systems;
|
|
using Content.Shared.DoAfter;
|
|
using Content.Shared.FixedPoint;
|
|
using Content.Shared.Humanoid;
|
|
using Content.Shared.Polymorph;
|
|
using Content.Shared.Popups;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Localization;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Prototypes;
|
|
|
|
namespace Content.Server._Sunrise.Kitsune;
|
|
|
|
public sealed class KitsuneTransformSystem : EntitySystem
|
|
{
|
|
private const float TransformDurationSeconds = 240f; // 4 minutes
|
|
private const float TransformDoAfterDurationSeconds = 3f;
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
|
[Dependency] private readonly PolymorphSystem _polymorph = default!;
|
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly ILogManager _logManager = default!;
|
|
[Dependency] private readonly DamageableSystem _damage = default!;
|
|
[Dependency] private readonly SpriteColorSystem _spriteColor = default!;
|
|
|
|
private ISawmill _sawmill = default!;
|
|
|
|
// Dictionary to track when each transformed entity should auto-revert
|
|
private Dictionary<EntityUid, float> _transformDurations = new();
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<KitsuneTransformComponent, KitsuneTransformActionEvent>(OnKitsuneTransform);
|
|
SubscribeLocalEvent<KitsuneTransformComponent, KitsuneTransformDoAfterEvent>(OnKitsuneTransformDoAfter);
|
|
SubscribeLocalEvent<KitsuneTransformComponent, KitsuneRevertActionEvent>(OnKitsuneRevert);
|
|
SubscribeLocalEvent<KitsuneTransformComponent, KitsuneRevertDoAfterEvent>(OnKitsuneRevertDoAfter);
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
// Check for expired transforms
|
|
var expired = new List<EntityUid>();
|
|
var toUpdate = new Dictionary<EntityUid, float>();
|
|
|
|
foreach (var (uid, timeLeft) in _transformDurations)
|
|
{
|
|
var newTimeLeft = timeLeft - frameTime;
|
|
if (newTimeLeft <= 0)
|
|
expired.Add(uid);
|
|
else
|
|
toUpdate[uid] = newTimeLeft;
|
|
}
|
|
|
|
// Update the timers
|
|
_transformDurations = toUpdate;
|
|
|
|
// Auto-revert expired transforms
|
|
foreach (var uid in expired)
|
|
{
|
|
if (TryComp<KitsuneTransformComponent>(uid, out var component) &&
|
|
TryComp<PolymorphedEntityComponent>(uid, out var morphComp))
|
|
{
|
|
_polymorph.Revert((uid, morphComp));
|
|
_popup.PopupEntity(Loc.GetString("kitsune-transform-expired"), uid, uid, PopupType.MediumCaution);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnKitsuneTransform(EntityUid uid, KitsuneTransformComponent component, KitsuneTransformActionEvent args)
|
|
{
|
|
args.Handled = true;
|
|
|
|
if (TryComp<PolymorphedEntityComponent>(uid, out _))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("kitsune-transform-already-transformed"), uid, uid, PopupType.MediumCaution);
|
|
return;
|
|
}
|
|
|
|
// Start the do-after
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(TransformDoAfterDurationSeconds),
|
|
new KitsuneTransformDoAfterEvent(),
|
|
uid)
|
|
{
|
|
BreakOnMove = true,
|
|
BreakOnDamage = true,
|
|
MovementThreshold = 1f,
|
|
NeedHand = false,
|
|
};
|
|
|
|
if (_doAfter.TryStartDoAfter(doAfterArgs))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("kitsune-transform-starting"), uid, uid, PopupType.MediumCaution);
|
|
_audio.PlayPvs(new SoundPathSpecifier("/Audio/_Sunrise/BloodCult/butcher.ogg"), uid);
|
|
}
|
|
}
|
|
|
|
private void OnKitsuneTransformDoAfter(EntityUid uid, KitsuneTransformComponent component, ref KitsuneTransformDoAfterEvent args)
|
|
{
|
|
if (args.Cancelled)
|
|
return;
|
|
|
|
// Transform into fox
|
|
if (!_prototypeManager.TryIndex<PolymorphPrototype>(new ProtoId<PolymorphPrototype>("KitsuneTransform"), out var prototype))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("kitsune-transform-failed"), uid, uid, PopupType.MediumCaution);
|
|
_sawmill.Warning($"Kitsune transform failed: could not find 'KitsuneTransform' polymorph prototype");
|
|
return;
|
|
}
|
|
|
|
// Apply 9 slash damage to self
|
|
var damage = new DamageSpecifier()
|
|
{
|
|
DamageDict = new Dictionary<string, FixedPoint2>
|
|
{
|
|
{ "Slash", FixedPoint2.New(9) }
|
|
}
|
|
};
|
|
_damage.TryChangeDamage(uid, damage);
|
|
// Store the original entity reference before polymorph
|
|
component.StashedHumanoid = uid;
|
|
|
|
// Set transform duration timer
|
|
_transformDurations[uid] = TransformDurationSeconds;
|
|
|
|
// Perform polymorph
|
|
var newUid = _polymorph.PolymorphEntity(uid, prototype) ?? throw new ArgumentNullException("_polymorph.PolymorphEntity(uid, prototype)");
|
|
|
|
// Transfer TTS voice to the fox form from the original humanoid's voice
|
|
if (TryComp<TTSComponent>(uid, out var originalTts))
|
|
{
|
|
if (TryComp<TTSComponent>(newUid, out var foxTts))
|
|
{
|
|
foxTts.VoicePrototypeId = originalTts.VoicePrototypeId;
|
|
}
|
|
}
|
|
|
|
// Apply the humanoid's hair color to the colored fur layer
|
|
if (TryComp<HumanoidAppearanceComponent>(uid, out var humanoidAppearance))
|
|
{
|
|
// Use CachedHairColor if available, otherwise fallback to SkinColor
|
|
var hairColor = humanoidAppearance.CachedHairColor ?? humanoidAppearance.EyeColor;
|
|
_spriteColor.SetStateColor(newUid, "nine-tail_fox_gray_color", hairColor);
|
|
}
|
|
|
|
_popup.PopupEntity(Loc.GetString("kitsune-transform-success"), newUid, newUid, PopupType.MediumCaution);
|
|
_audio.PlayPvs(new SoundPathSpecifier("/Audio/_Sunrise/BloodCult/enter_blood.ogg"), newUid);
|
|
}
|
|
|
|
private void OnKitsuneRevert(EntityUid uid, KitsuneTransformComponent component, KitsuneRevertActionEvent args)
|
|
{
|
|
args.Handled = true;
|
|
|
|
if (!TryComp<PolymorphedEntityComponent>(uid, out _))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("kitsune-revert-not-transformed"), uid, uid, PopupType.MediumCaution);
|
|
return;
|
|
}
|
|
|
|
// Start the do-after for revert
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(3),
|
|
new KitsuneRevertDoAfterEvent(),
|
|
uid)
|
|
{
|
|
BreakOnMove = true,
|
|
BreakOnDamage = true,
|
|
MovementThreshold = 1f,
|
|
NeedHand = false,
|
|
};
|
|
|
|
if (_doAfter.TryStartDoAfter(doAfterArgs))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("kitsune-revert-starting"), uid, uid, PopupType.MediumCaution);
|
|
}
|
|
}
|
|
|
|
private void OnKitsuneRevertDoAfter(EntityUid uid, KitsuneTransformComponent component, ref KitsuneRevertDoAfterEvent args)
|
|
{
|
|
if (args.Cancelled)
|
|
return;
|
|
|
|
// Clear the duration timer
|
|
_transformDurations.Remove(uid);
|
|
|
|
// Revert the polymorph
|
|
if (!TryComp<PolymorphedEntityComponent>(uid, out var morphComp))
|
|
return;
|
|
_polymorph.Revert((uid, morphComp));
|
|
|
|
_popup.PopupEntity(Loc.GetString("kitsune-revert-success"), uid, uid, PopupType.MediumCaution);
|
|
}
|
|
}
|