- add: Changeling antagonist (#2)

* Changeling WIP

* UI

* Pointers fix

* Moved out abilities

* Regenerate ability

* Fixed Regenerate ability
Prevent ghosting while regenerating

* Cleanup

* Base lesser form

* Finished Lesser Form && Transform

* Transform Sting

* Blind Sting

* Mute Sting
Added OnExamine on absorbed human

* Hallucination Sting
Changeling Absorb and transfer absorbed entities to absorber

* Cryogenic Sting

* Adrenaline Sacs

* Transform now uses Polymorph

* Armblade, Shield, Armor

* Tentacle Arm ability
Tentacle Gun system

* WIP with bugs

* WiP bugs

* fix implant transfer

* Fixed bugs with shop transfer and actions transfer

* Just in case

* Vi sitter i ventrilo och spelar DotA

* Fixes and proper LesserForm tracking

* !!!!!

* Fixed empty buttons

* WIP Gamerule
Ready - shop

* nerf stun time cause its sucks

* cleaning

* just in case

* Absorb DNA Objective.

* Partial objectives with bugs

* fix

* fix pointer

* Changeling objectives

* Changeling objectives №2

* Admin verb, game rule

* Fixed empty list check
Icons for objectives

* Changeling chat, changeling names etc.

* fix some merge errors

* - fix: Fixed all bugs with changeling

---------

Co-authored-by: Y-Parvus <yevhen.parvus@gmail.com>
Co-authored-by: Y-Parvus <61109031+Y-Parvus@users.noreply.github.com>
Co-authored-by: HitPanda <104197232+EnefFlow@users.noreply.github.com>
Co-authored-by: EnefFlow <regeto90@mail.ru>
This commit is contained in:
rhailrake
2024-01-31 14:01:35 +00:00
committed by GitHub
parent 7872502bf8
commit aa8e31fa7e
127 changed files with 3747 additions and 33 deletions

View File

@@ -0,0 +1,10 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Changeling;
[RegisterComponent, NetworkedComponent]
public sealed partial class AbsorbedComponent : Component
{
public EntityUid AbsorberMind;
}

View File

@@ -0,0 +1,70 @@
using Content.Shared.Humanoid;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Changeling;
[RegisterComponent, NetworkedComponent]
public sealed partial class ChangelingComponent : Component
{
[DataField("chemRegenRate")]
public int ChemicalRegenRate = 2;
[DataField("chemicalCap")]
public int ChemicalCapacity = 75;
[ViewVariables(VVAccess.ReadWrite), DataField("chemicalsBalance")]
public int ChemicalsBalance = 20;
[ViewVariables(VVAccess.ReadWrite), DataField("pointsBalance")]
public int StartingPointsBalance = 10;
[ViewVariables(VVAccess.ReadOnly)]
public float Accumulator;
[ViewVariables(VVAccess.ReadOnly)]
public float UpdateDelay = 6f;
[ViewVariables(VVAccess.ReadOnly)]
public bool IsRegenerating;
[ViewVariables(VVAccess.ReadOnly)]
public bool IsLesserForm;
[ViewVariables(VVAccess.ReadOnly)]
public string HiveName;
[ViewVariables(VVAccess.ReadOnly), DataField("absorbedEntities")]
public Dictionary<string, HumanoidData> AbsorbedEntities = new();
[ViewVariables(VVAccess.ReadWrite), DataField("AbsorbDNACost")]
public int AbsorbDnaCost;
[ViewVariables(VVAccess.ReadWrite), DataField("AbsorbDNADelay")]
public float AbsorbDnaDelay = 10f;
[ViewVariables(VVAccess.ReadWrite), DataField("TransformDelay")]
public float TransformDelay = 2f;
[ViewVariables(VVAccess.ReadWrite), DataField("RegenerateDelay")]
public float RegenerateDelay = 20f;
[ViewVariables(VVAccess.ReadWrite), DataField("LesserFormDelay")]
public float LesserFormDelay = 5f;
public bool IsInited;
}
public struct HumanoidData
{
public EntityPrototype EntityPrototype;
public MetaDataComponent? MetaDataComponent;
public HumanoidAppearanceComponent AppearanceComponent;
public string Name;
public string Dna;
}

View File

@@ -0,0 +1,63 @@
using System.Linq;
using Robust.Shared.Random;
namespace Content.Shared.Changeling;
public sealed class ChangelingNameGenerator
{
[Dependency] private readonly IRobustRandom _random = default!;
private List<string> _used = new();
private readonly List<string> _greekAlphabet = new()
{
"Alpha",
"Beta",
"Gamma",
"Delta",
"Epsilon",
"Zeta",
"Eta",
"Theta",
"Iota",
"Kappa",
"Lambda",
"Mu",
"Nu",
"Xi",
"Omicron",
"Pi",
"Rho",
"Sigma",
"Tau",
"Upsilon",
"Phi",
"Chi",
"Psi",
"Omega"
};
private string GenWhiteLabelName()
{
var number = _random.Next(0,10000);
return $"HiveMember-{number}";
}
public string GetName()
{
_random.Shuffle(_greekAlphabet);
foreach (var selected in _greekAlphabet.Where(selected => !_used.Contains(selected)))
{
_used.Add(selected);
return selected;
}
return GenWhiteLabelName();
}
public void ClearUsed()
{
_used.Clear();
}
}

View File

@@ -0,0 +1,88 @@
using Content.Shared.Alert;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Network;
namespace Content.Shared.Changeling;
public sealed class ChemicalsSystem : EntitySystem
{
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly INetManager _net = default!;
public bool AddChemicals(EntityUid uid, ChangelingComponent component, int quantity)
{
if (_mobStateSystem.IsDead(uid))
return false;
var toAdd = quantity;
if (component.ChemicalsBalance == component.ChemicalCapacity)
return false;
if (component.ChemicalsBalance + toAdd > component.ChemicalCapacity)
{
var overflow = component.ChemicalsBalance + toAdd - component.ChemicalCapacity;
toAdd -= overflow;
component.ChemicalsBalance += toAdd;
}
component.ChemicalsBalance += toAdd;
Dirty(uid, component);
UpdateAlert(uid, component);
return true;
}
public bool RemoveChemicals(EntityUid uid, ChangelingComponent component, int quantity)
{
if (_mobStateSystem.IsDead(uid) && !component.IsRegenerating)
return false;
var toRemove = quantity;
if (component.ChemicalsBalance == 0)
return false;
if (component.ChemicalsBalance - toRemove < 0)
return false;
component.ChemicalsBalance -= toRemove;
Dirty(uid, component);
UpdateAlert(uid, component);
return true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ChangelingComponent>();
while (query.MoveNext(out var uid, out var component))
{
component.Accumulator += frameTime;
if(component.Accumulator < component.UpdateDelay)
continue;
if (component.IsRegenerating)
continue;
component.Accumulator = 0;
AddChemicals(uid, component, component.ChemicalRegenRate);
}
}
public void UpdateAlert(EntityUid uid, ChangelingComponent component)
{
if(_net.IsServer)
{
_alertsSystem.ShowAlert(uid, AlertType.Chemicals,
(short) Math.Clamp(Math.Round(component.ChemicalsBalance / 10f), 0, 7));
}
}
}

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Changeling;
[RegisterComponent, NetworkedComponent]
public sealed partial class LesserFormRestrictedComponent : Component
{
}

View File

@@ -0,0 +1,105 @@
using Content.Shared.Actions;
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared.Changeling;
[Serializable, NetSerializable]
public sealed partial class AbsorbDnaDoAfterEvent : SimpleDoAfterEvent
{
}
public sealed partial class AbsorbDnaActionEvent : EntityTargetActionEvent
{
}
[Serializable, NetSerializable]
public sealed partial class TransformDoAfterEvent : SimpleDoAfterEvent
{
public string SelectedDna;
}
public sealed partial class TransformActionEvent : InstantActionEvent
{
}
[Serializable, NetSerializable]
public sealed partial class RegenerateDoAfterEvent : SimpleDoAfterEvent
{
}
public sealed partial class RegenerateActionEvent : InstantActionEvent
{
}
[Serializable, NetSerializable]
public sealed partial class LesserFormDoAfterEvent : SimpleDoAfterEvent
{
}
public sealed partial class LesserFormActionEvent : InstantActionEvent
{
}
public sealed partial class TransformStingActionEvent : EntityTargetActionEvent
{
}
public sealed partial class BlindStingActionEvent : EntityTargetActionEvent
{
}
public sealed partial class MuteStingActionEvent : EntityTargetActionEvent
{
}
public sealed partial class HallucinationStingActionEvent : EntityTargetActionEvent
{
}
public sealed partial class CryoStingActionEvent : EntityTargetActionEvent
{
}
public sealed partial class AdrenalineSacsActionEvent : InstantActionEvent
{
}
public sealed partial class FleshmendActionEvent : InstantActionEvent
{
}
public sealed partial class ArmbladeActionEvent : InstantActionEvent
{
}
public sealed partial class OrganicShieldActionEvent : InstantActionEvent
{
}
public sealed partial class ChitinousArmorActionEvent : InstantActionEvent
{
}
public sealed partial class TentacleArmActionEvent : InstantActionEvent
{
}
public sealed partial class ChangelingShopActionEvent : InstantActionEvent
{
}

View File

@@ -0,0 +1,33 @@
namespace Content.Shared.Changeling;
public sealed class SharedChangelingChat : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ChangelingComponent, ComponentStartup>(OnInit);
SubscribeLocalEvent<ChangelingComponent, ComponentShutdown>(OnShutdown);
}
private void OnInit(EntityUid uid, ChangelingComponent component, ComponentStartup args)
{
RaiseLocalEvent(new ChangelingUserStart(true));
}
private void OnShutdown(EntityUid uid, ChangelingComponent component, ComponentShutdown args)
{
RaiseLocalEvent(new ChangelingUserStart(false));
}
}
public sealed class ChangelingUserStart
{
public bool Created { get; }
public ChangelingUserStart(bool state)
{
Created = state;
}
}

View File

@@ -0,0 +1,155 @@
using System.Numerics;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Physics;
using Content.Shared.Projectiles;
using Content.Shared.Stunnable;
using Content.Shared.Throwing;
using Content.Shared.Weapons.Misc;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Input;
using Robust.Shared.Network;
using Robust.Shared.Physics.Components;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Shared.Changeling;
public abstract class SharedTentacleGun : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TentacleGunComponent, GunShotEvent>(OnTentacleShot);
SubscribeLocalEvent<TentacleProjectileComponent, ProjectileEmbedEvent>(OnTentacleCollide);
}
private void OnTentacleShot(EntityUid uid, TentacleGunComponent component, ref GunShotEvent args)
{
foreach (var (shotUid, _) in args.Ammo)
{
if (!HasComp<TentacleProjectileComponent>(shotUid))
continue;
Dirty(uid, component);
var visuals = EnsureComp<JointVisualsComponent>(shotUid.Value);
visuals.Sprite =
new SpriteSpecifier.Rsi(new ResPath("Objects/Weapons/Guns/Launchers/tentacle_gun.rsi"), "frope");
visuals.OffsetA = new Vector2(0f, 0.5f);
visuals.Target = uid;
Dirty(shotUid.Value, visuals);
}
TryComp<AppearanceComponent>(uid, out var appearance);
_appearance.SetData(uid, SharedTetherGunSystem.TetherVisualsStatus.Key, false, appearance);
}
private void OnTentacleCollide(EntityUid uid, TentacleProjectileComponent component, ref ProjectileEmbedEvent args)
{
if (!_timing.IsFirstTimePredicted)
return;
if (!HasComp<TentacleGunComponent>(args.Weapon))
{
QueueDel(uid);
return;
}
if (!TryComp<GunComponent>(args.Weapon, out var gun))
{
QueueDel(uid);
return;
}
if (!HasComp<HumanoidAppearanceComponent>(args.Embedded))
{
DeleteProjectile(uid);
return;
}
switch (gun.SelectedMode)
{
case SelectiveFire.PullMob when !PullMob(args):
DeleteProjectile(uid);
return;
case SelectiveFire.PullMob:
_timerManager.AddTimer(new Timer(1500, false, () =>
{
DeleteProjectile(uid);
}));
break;
case SelectiveFire.PullItem:
PullItem(args);
DeleteProjectile(uid);
break;
}
}
private void PullItem(ProjectileEmbedEvent args)
{
foreach (var activeItem in _handsSystem.EnumerateHeld(args.Embedded))
{
if(!TryComp<PhysicsComponent>(activeItem, out var physicsComponent))
return;
var coords = Transform(args.Embedded).Coordinates;
_handsSystem.TryDrop(args.Embedded, coords);
var force = physicsComponent.Mass * 2.5f / 2;
_throwingSystem.TryThrow(activeItem, Transform(args.Shooter!.Value).Coordinates, force);
break;
}
}
private bool PullMob(ProjectileEmbedEvent args)
{
var stunTime = _random.Next(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(8));
if (!_stunSystem.TryParalyze(args.Embedded, stunTime, true))
return false;
_throwingSystem.TryThrow(args.Embedded, Transform(args.Shooter!.Value).Coordinates, 5f);
return true;
}
private void DeleteProjectile(EntityUid projUid)
{
TryComp<AppearanceComponent>(projUid, out var appearance);
if (!Deleted(projUid))
{
if (_netManager.IsServer)
{
QueueDel(projUid);
}
}
_appearance.SetData(projUid, SharedTetherGunSystem.TetherVisualsStatus.Key, true, appearance);
}
[Serializable, NetSerializable]
protected sealed class RequestTentacleMessage : EntityEventArgs
{
public BoundKeyFunction Key;
public RequestTentacleMessage(BoundKeyFunction key)
{
Key = key;
}
}
}

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Changeling;
[RegisterComponent, NetworkedComponent]
public sealed partial class TentacleGunComponent : Component
{
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Changeling;
[RegisterComponent, NetworkedComponent]
public sealed partial class TentacleProjectileComponent : Component
{
}