fix metempsychosis (#2156)

* this is an insult to my sanity

* this is an insult to my sanity

* Revert "Merge branch 'shitcode' of https://github.com/MilonPL/Delta-v into shitcode"

This reverts commit 3de8ffc704, reversing
changes made to b22c053376.

* FUCKING GITSHIT

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* docs and shit
This commit is contained in:
Milon 2024-11-09 11:47:17 +01:00 committed by GitHub
parent a4755ff66e
commit 38d6e87dd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 236 additions and 194 deletions

View File

@ -1,12 +1,10 @@
using Content.Server.Nyanotrasen.Cloning;
using Content.Server.DeltaV.Cloning;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Random;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.DeltaV;
[TestFixture]
[TestOf(typeof(MetempsychoticMachineSystem))]
public sealed class MetempsychosisTest
{
[Test]
@ -23,18 +21,22 @@ public sealed class MetempsychosisTest
await server.WaitAssertion(() =>
{
prototypeManager.TryIndex<WeightedRandomPrototype>(metemComponent.MetempsychoticHumanoidPool,
prototypeManager.TryIndex(metemComponent.MetempsychoticHumanoidPool,
out var humanoidPool);
prototypeManager.TryIndex<WeightedRandomPrototype>(metemComponent.MetempsychoticNonHumanoidPool,
prototypeManager.TryIndex(metemComponent.MetempsychoticNonHumanoidPool,
out var nonHumanoidPool);
Assert.That(humanoidPool, Is.Not.Null, "MetempsychoticHumanoidPool is null!");
Assert.That(nonHumanoidPool, Is.Not.Null, "MetempsychoticNonHumanoidPool is null!");
Assert.That(humanoidPool.Weights, Is.Not.Empty,
"MetempsychoticHumanoidPool has no valid prototypes!");
Assert.That(nonHumanoidPool.Weights, Is.Not.Empty,
"MetempsychoticNonHumanoidPool has no valid prototypes!");
Assert.Multiple(() =>
{
Assert.That(humanoidPool, Is.Not.Null, "MetempsychoticHumanoidPool is null!");
Assert.That(nonHumanoidPool, Is.Not.Null, "MetempsychoticNonHumanoidPool is null!");
Assert.That(humanoidPool.Weights,
Is.Not.Empty,
"MetempsychoticHumanoidPool has no valid prototypes!");
Assert.That(nonHumanoidPool.Weights,
Is.Not.Empty,
"MetempsychoticNonHumanoidPool has no valid prototypes!");
});
foreach (var key in humanoidPool.Weights.Keys)
{

View File

@ -9,6 +9,8 @@ using Content.Server.Jobs;
using Content.Server.Materials;
using Content.Server.Popups;
using Content.Server.Power.EntitySystems;
using Content.Server.Psionics; // DeltaV
using Content.Server.Traits.Assorted; // DeltaV
using Content.Shared.Atmos;
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Components;
@ -33,26 +35,10 @@ using Robust.Shared.Containers;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Content.Server.Traits.Assorted; //Nyano - Summary: allows the potential psionic ability to be written to the character.
using Content.Server.Psionics; //DeltaV needed for Psionic Systems
using Content.Shared.Speech; //DeltaV Start Metem Usings
using Content.Shared.Tag;
using Content.Shared.Preferences;
using Content.Shared.Emoting;
using Content.Server.Speech.Components;
using Content.Server.StationEvents.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Nyanotrasen.Cloning;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.GameObjects.Components.Localization; //DeltaV End Metem Usings
using Content.Server.EntityList;
using Content.Shared.SSDIndicator;
using Content.Shared.Damage.ForceSay;
using Content.Server.Polymorph.Components;
namespace Content.Server.Cloning
{
public sealed class CloningSystem : EntitySystem
public sealed partial class CloningSystem : EntitySystem // DeltaV - Set to partial, see CloningSystem.Metempsychosis.cs
{
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly IPlayerManager _playerManager = null!;
@ -76,8 +62,6 @@ namespace Content.Server.Cloning
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly MetempsychoticMachineSystem _metem = default!; //DeltaV
[Dependency] private readonly TagSystem _tag = default!; //DeltaV
public readonly Dictionary<MindComponent, EntityUid> ClonesWaitingForMind = new();
public const float EasyModeCloningCost = 0.7f;
@ -158,6 +142,10 @@ namespace Content.Server.Cloning
if (!Resolve(uid, ref clonePod))
return false;
// DeltaV - This method should use Entity<CloningPodComponent> pod instead
// But I don't want to completely mangle it so we do this here
var podEnt = new Entity<CloningPodComponent>(uid, clonePod);
if (HasComp<ActiveCloningPodComponent>(uid))
return false;
@ -244,13 +232,13 @@ namespace Content.Server.Cloning
AddComp<ActiveCloningPodComponent>(uid);
return true;
}
// End Nyano-code.
}
// end of genetic damage checks
var mob = FetchAndSpawnMob(clonePod, pref, speciesPrototype, humanoid, bodyToClone, karmaBonus); //DeltaV Replaces CloneAppearance with Metem/Clone via FetchAndSpawnMob
// DeltaV - Replaces CloneAppearance with Metem/Clone via FetchAndSpawnMob
var mob = FetchAndSpawnMob(podEnt, pref, speciesPrototype, humanoid, bodyToClone, karmaBonus);
///Nyano - Summary: adds the potential psionic trait to the reanimated mob.
// Nyano - Summary: adds the potential psionic trait to the reanimated mob.
EnsureComp<PotentialPsionicComponent>(mob);
var ev = new CloningEvent(bodyToClone, mob);
@ -348,6 +336,7 @@ namespace Content.Server.Cloning
var transform = Transform(uid);
var indices = _transformSystem.GetGridTilePositionOrDefault((uid, transform));
var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
if (HasComp<EmaggedComponent>(uid))
{
_audio.PlayPvs(clonePod.ScreamSound, uid);
@ -375,84 +364,6 @@ namespace Content.Server.Cloning
RemCompDeferred<ActiveCloningPodComponent>(uid);
}
/// <summary>
/// Start Nyano Code: Handles fetching the mob and any appearance stuff...
/// </summary>
private EntityUid FetchAndSpawnMob(CloningPodComponent clonePod, HumanoidCharacterProfile pref, SpeciesPrototype speciesPrototype, HumanoidAppearanceComponent humanoid, EntityUid bodyToClone, float karmaBonus)
{
List<Sex> sexes = new();
bool switchingSpecies = false;
bool applyKarma = false;
var toSpawn = speciesPrototype.Prototype;
TryComp<MetempsychosisKarmaComponent>(bodyToClone, out var oldKarma);
if (TryComp<MetempsychoticMachineComponent>(clonePod.Owner, out var metem))
{
toSpawn = _metem.GetSpawnEntity(clonePod.Owner, karmaBonus, metem, speciesPrototype, out var newSpecies, oldKarma?.Score);
applyKarma = true;
if (newSpecies != null)
{
sexes = newSpecies.Sexes;
if (speciesPrototype.ID != newSpecies.ID)
switchingSpecies = true;
speciesPrototype = newSpecies;
}
}
var mob = Spawn(toSpawn, _transformSystem.GetMapCoordinates(clonePod.Owner));
if (TryComp<HumanoidAppearanceComponent>(mob, out var newHumanoid))
{
if (switchingSpecies || HasComp<MetempsychosisKarmaComponent>(bodyToClone))
{
pref = HumanoidCharacterProfile.RandomWithSpecies(newHumanoid.Species);
if (sexes.Contains(humanoid.Sex))
pref = pref.WithSex(humanoid.Sex);
pref = pref.WithGender(humanoid.Gender);
pref = pref.WithAge(humanoid.Age);
}
_humanoidSystem.LoadProfile(mob, pref);
}
if (applyKarma)
{
var karma = EnsureComp<MetempsychosisKarmaComponent>(mob);
karma.Score++;
if (oldKarma != null)
karma.Score += oldKarma.Score;
}
var ev = new CloningEvent(bodyToClone, mob);
RaiseLocalEvent(bodyToClone, ref ev);
if (!ev.NameHandled)
_metaSystem.SetEntityName(mob, MetaData(bodyToClone).EntityName);
var grammar = EnsureComp<GrammarComponent>(mob);
grammar.ProperNoun = true;
grammar.Gender = humanoid.Gender;
Dirty(mob, grammar);
EnsureComp<PotentialPsionicComponent>(mob);
EnsureComp<SpeechComponent>(mob);
EnsureComp<DamageForceSayComponent>(mob);
EnsureComp<EmotingComponent>(mob);
EnsureComp<MindContainerComponent>(mob);
EnsureComp<SSDIndicatorComponent>(mob);
RemComp<ReplacementAccentComponent>(mob);
RemComp<MonkeyAccentComponent>(mob);
RemComp<SentienceTargetComponent>(mob);
RemComp<GhostTakeoverAvailableComponent>(mob);
_tag.AddTag(mob, "DoorBumpOpener");
return mob;
}
//End Nyano Code
public void Reset(RoundRestartCleanupEvent ev)
{
ClonesWaitingForMind.Clear();

View File

@ -1,5 +1,5 @@
using Content.Server.Bible.Components;
using Content.Server.Nyanotrasen.Cloning;
using Content.Server.DeltaV.Cloning;
using Content.Shared.Abilities.Psionics;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;

View File

@ -0,0 +1,172 @@
using Content.Server.DeltaV.Cloning;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Content.Shared.Speech;
using Content.Shared.Emoting;
using Content.Shared.Damage.ForceSay;
using Content.Shared.SSDIndicator;
using Content.Server.Speech.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.StationEvents.Components;
using Content.Server.Psionics;
using Robust.Shared.Random;
using Content.Shared.Mind.Components;
using Content.Shared.Tag;
using Content.Shared.Cloning;
using Content.Shared.Random.Helpers;
using Robust.Shared.GameObjects.Components.Localization;
namespace Content.Server.Cloning;
public sealed partial class CloningSystem
{
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly GrammarSystem _grammar = default!;
/// <summary>
/// Gets the entity prototype to spawn for a clone based on karma and chance calculations.
/// </summary>
private string GetSpawnEntity(Entity<MetempsychoticMachineComponent> ent, float karmaBonus, SpeciesPrototype oldSpecies, out SpeciesPrototype? species, int karma = 0)
{
// First time being cloned - return original species
if (karma == 0)
{
species = oldSpecies;
return oldSpecies.Prototype;
}
var chance = ent.Comp.HumanoidBaseChance + karmaBonus;
chance -= (1 - ent.Comp.HumanoidBaseChance) * karma;
// Perfect clone chance
if (chance > 1 && _robustRandom.Prob(chance - 1))
{
species = oldSpecies;
return oldSpecies.Prototype;
}
// Roll for humanoid vs non-humanoid
chance = Math.Clamp(chance, 0, 1);
if (_robustRandom.Prob(chance))
{
if (_prototype.TryIndex(ent.Comp.MetempsychoticHumanoidPool, out var humanoidPool))
{
var protoId = humanoidPool.Pick();
if (_prototype.TryIndex<SpeciesPrototype>(protoId, out var speciesPrototype))
{
species = speciesPrototype;
return speciesPrototype.Prototype;
}
}
}
else if (_prototype.TryIndex(ent.Comp.MetempsychoticNonHumanoidPool, out var nonHumanoidPool))
{
// For non-humanoids, return the entity prototype directly
species = null;
return nonHumanoidPool.Pick();
}
// Fallback to original species if prototype indexing fails
Log.Error("Failed to get valid clone type - falling back to original species");
species = oldSpecies;
return oldSpecies.Prototype;
}
/// <summary>
/// Handles fetching the mob and managing appearance for cloning with metempsychosis mechanics
/// </summary>
private EntityUid FetchAndSpawnMob(
Entity<CloningPodComponent> pod,
HumanoidCharacterProfile pref,
SpeciesPrototype speciesPrototype,
HumanoidAppearanceComponent humanoid,
EntityUid bodyToClone,
float karmaBonus)
{
List<Sex> sexes = [];
var switchingSpecies = false;
var applyKarma = false;
var toSpawn = speciesPrototype.Prototype;
// Get existing karma score or start at 0
var karmaScore = 0;
if (TryComp<MetempsychosisKarmaComponent>(bodyToClone, out var oldKarma))
{
karmaScore = oldKarma.Score;
}
if (TryComp<MetempsychoticMachineComponent>(pod.Owner, out var metem))
{
var metemEntity = new Entity<MetempsychoticMachineComponent>(pod.Owner, metem);
toSpawn = GetSpawnEntity(metemEntity, karmaBonus, speciesPrototype, out var newSpecies, karmaScore);
applyKarma = true;
if (newSpecies != null)
{
sexes = newSpecies.Sexes;
speciesPrototype = newSpecies;
if (speciesPrototype.ID != newSpecies.ID)
switchingSpecies = true;
}
}
var mob = Spawn(toSpawn, _transformSystem.GetMapCoordinates(pod.Owner));
// Only try to handle humanoid appearance if we have a humanoid component
if (TryComp<HumanoidAppearanceComponent>(mob, out var newHumanoid))
{
if (switchingSpecies || HasComp<MetempsychosisKarmaComponent>(bodyToClone))
{
pref = HumanoidCharacterProfile.RandomWithSpecies(newHumanoid.Species);
if (sexes.Contains(humanoid.Sex))
pref = pref.WithSex(humanoid.Sex);
pref = pref.WithGender(humanoid.Gender);
pref = pref.WithAge(humanoid.Age);
}
_humanoidSystem.LoadProfile(mob, pref);
}
if (applyKarma)
{
var karma = EnsureComp<MetempsychosisKarmaComponent>(mob);
karma.Score = karmaScore + 1; // Increment karma score
}
var ev = new CloningEvent(bodyToClone, mob);
RaiseLocalEvent(bodyToClone, ref ev);
if (!ev.NameHandled)
_metaSystem.SetEntityName(mob, MetaData(bodyToClone).EntityName);
var grammar = EnsureComp<GrammarComponent>(mob);
var grammarEnt = new Entity<GrammarComponent>(mob, grammar);
_grammar.SetProperNoun(grammarEnt, true);
_grammar.SetGender(grammarEnt, humanoid.Gender);
Dirty(mob, grammar);
SetupBasicComponents(mob);
return mob;
}
// I hate this
private void SetupBasicComponents(EntityUid mob)
{
EnsureComp<PotentialPsionicComponent>(mob);
EnsureComp<SpeechComponent>(mob);
EnsureComp<DamageForceSayComponent>(mob);
EnsureComp<EmotingComponent>(mob);
EnsureComp<MindContainerComponent>(mob);
EnsureComp<SSDIndicatorComponent>(mob);
RemComp<ReplacementAccentComponent>(mob);
RemComp<MonkeyAccentComponent>(mob);
RemComp<SentienceTargetComponent>(mob);
RemComp<GhostTakeoverAvailableComponent>(mob);
_tag.AddTag(mob, "DoorBumpOpener");
}
}

View File

@ -0,0 +1,11 @@
namespace Content.Server.DeltaV.Cloning;
/// <summary>
/// This tracks how many times you have already been cloned and lowers your chance of getting a humanoid each time.
/// </summary>
[RegisterComponent]
public sealed partial class MetempsychosisKarmaComponent : Component
{
[DataField]
public int Score;
}

View File

@ -0,0 +1,27 @@
using Content.Shared.Random;
using Robust.Shared.Prototypes;
namespace Content.Server.DeltaV.Cloning;
[RegisterComponent]
public sealed partial class MetempsychoticMachineComponent : Component
{
/// <summary>
/// Base probability of remaining humanoid during cloning. Higher karma reduces this chance.
/// </summary>
[DataField]
public float HumanoidBaseChance = 0.75f;
/// <summary>
/// Species prototypes pool to use for humanoids.
/// </summary>
[DataField]
public ProtoId<WeightedRandomPrototype> MetempsychoticHumanoidPool = "MetempsychoticHumanoidPool";
/// <summary>
/// Entitiy prototypes pool to use for non-humanoids.
/// </summary>
[DataField]
public ProtoId<WeightedRandomPrototype> MetempsychoticNonHumanoidPool = "MetempsychoticNonhumanoidPool";
}

View File

@ -1,12 +0,0 @@
namespace Content.Server.Nyanotrasen.Cloning
{
/// <summary>
/// This tracks how many times you have already been cloned and lowers your chance of getting a humanoid each time.
/// </summary>
[RegisterComponent]
public sealed partial class MetempsychosisKarmaComponent : Component
{
[DataField("score")]
public int Score = 0;
}
}

View File

@ -1,22 +0,0 @@
using Content.Shared.Random;
namespace Content.Server.Nyanotrasen.Cloning
{
[RegisterComponent]
public sealed partial class MetempsychoticMachineComponent : Component
{
/// <summary>
/// Chance you will spawn as a humanoid instead of a non humanoid.
/// </summary>
[DataField("humanoidBaseChance")]
public float HumanoidBaseChance = 0.75f;
[ValidatePrototypeId<WeightedRandomPrototype>]
[DataField("metempsychoticHumanoidPool")]
public string MetempsychoticHumanoidPool = "MetempsychoticHumanoidPool";
[ValidatePrototypeId<WeightedRandomPrototype>]
[DataField("metempsychoticNonHumanoidPool")]
public string MetempsychoticNonHumanoidPool = "MetempsychoticNonhumanoidPool";
}
}

View File

@ -1,47 +0,0 @@
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
namespace Content.Server.Nyanotrasen.Cloning
{
public sealed class MetempsychoticMachineSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private ISawmill _sawmill = default!;
public string GetSpawnEntity(EntityUid uid, float karmaBonus, MetempsychoticMachineComponent component, SpeciesPrototype oldSpecies, out SpeciesPrototype? species, int? karma = null)
{
var chance = component.HumanoidBaseChance + karmaBonus;
if (karma != null)
chance -= ((1 - component.HumanoidBaseChance) * (float) karma);
if (chance > 1 && _random.Prob(chance - 1))
{
species = oldSpecies;
return oldSpecies.Prototype;
}
else
chance = 1;
chance = Math.Clamp(chance, 0, 1);
if (_random.Prob(chance) &&
_prototypeManager.TryIndex<WeightedRandomPrototype>(component.MetempsychoticHumanoidPool, out var humanoidPool) &&
_prototypeManager.TryIndex<SpeciesPrototype>(humanoidPool.Pick(), out var speciesPrototype))
{
species = speciesPrototype;
return speciesPrototype.Prototype;
}
else
{
species = null;
_sawmill.Error("Could not index species for metempsychotic machine...");
return "MobHuman";
}
}
}
}