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

The Newest Furry Race [Skeletons] #7825

Merged
merged 36 commits into from
May 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1c46b73
Merge branch 'space-wizards-master'
EmoGarbage404 Apr 25, 2022
8bd4c6e
first pass
EmoGarbage404 Apr 27, 2022
8e5f711
color fix
EmoGarbage404 Apr 27, 2022
404cb6d
Merge branch 'space-wizards:master' into master
EmoGarbage404 Apr 27, 2022
bc28856
Merge branch 'space-wizards:master' into master
EmoGarbage404 Apr 28, 2022
a72eba6
syndie bundle
EmoGarbage404 Apr 28, 2022
45f48a6
Update duffelbag.yml
EmoGarbage404 Apr 29, 2022
eed7ef7
Update noninfectious.yml
EmoGarbage404 Apr 29, 2022
9a53aaa
more work
EmoGarbage404 Apr 30, 2022
51e57f8
skeleton reassembly fix
EmoGarbage404 Apr 30, 2022
0fc9bbb
sfx plus sprite fixes
EmoGarbage404 Apr 30, 2022
aa46ec6
Merge branch 'master' into skeletons
EmoGarbage404 Apr 30, 2022
ede4e40
Update species.yml
EmoGarbage404 May 1, 2022
7f6bd4d
feud 1
EmoGarbage404 May 1, 2022
ef51962
Update species.yml
EmoGarbage404 May 1, 2022
a9b55e1
Update species.yml
EmoGarbage404 May 1, 2022
0e53191
Update Content.Server/Body/Systems/TransferMindOnGibSystem.cs
EmoGarbage404 May 4, 2022
b614f7b
Update Content.Server/Body/Systems/SkeletonBodyManagerSystem.cs
EmoGarbage404 May 4, 2022
26f7824
Update Content.Server/Body/Systems/SkeletonBodyManagerSystem.cs
EmoGarbage404 May 4, 2022
88753bf
Update Content.Server/Body/Systems/SkeletonBodyManagerSystem.cs
EmoGarbage404 May 4, 2022
77ab04d
Merge remote-tracking branch 'origin/zombie' into skeletons
EmoGarbage404 May 4, 2022
464058f
Merge remote-tracking branch 'origin/skeletons' into skeletons
EmoGarbage404 May 4, 2022
ed74485
mirror's fixes pt 1
EmoGarbage404 May 4, 2022
8af9804
mirror fixes pt. 2
EmoGarbage404 May 8, 2022
474aac6
Merge branch 'master' into skeletons
EmoGarbage404 May 8, 2022
bce4114
mirror's fixes pt 3. (the finale) (yeehaw)
EmoGarbage404 May 8, 2022
6bf0cf5
Update species.yml
EmoGarbage404 May 8, 2022
da822a3
Merge branch 'master' into skeletons
EmoGarbage404 May 9, 2022
667f1e0
Skeleton man
metalgearsloth May 12, 2022
f1c6791
a
metalgearsloth May 12, 2022
bfbdb30
Merge remote-tracking branch 'upstream/master' into skeletons
metalgearsloth May 12, 2022
ab9a9de
Too spooped
metalgearsloth May 12, 2022
a665713
Update licenses.txt
EmoGarbage404 May 12, 2022
b659679
Create license.txt
EmoGarbage404 May 12, 2022
5dece18
Update licenses.txt
EmoGarbage404 May 12, 2022
7586cb2
Update license.txt
EmoGarbage404 May 12, 2022
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
1 change: 1 addition & 0 deletions Content.Client/Entry/IgnoredComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ public static class IgnoredComponents
"DoorRemote",
"InteractionPopup",
"HealthAnalyzer",
"BodyReassemble",
"Thirst",
"CanEscapeInventory",
"Wires"
Expand Down
29 changes: 29 additions & 0 deletions Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,23 @@ private void OnSkinColorOnValueChanged()
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
case SpeciesSkinColor.TintedHues:
{
if (!_rgbSkinColorContainer.Visible)
{
_skinColor.Visible = false;
_rgbSkinColorContainer.Visible = true;
}

// a little hacky in order to convert rgb --> hsv --> rgb
var color = new Color(_rgbSkinColorSelector.Color.R, _rgbSkinColorSelector.Color.G, _rgbSkinColorSelector.Color.B);
var newColor = Color.ToHsv(color);
newColor.Y = .1f;
color = Color.FromHsv(newColor);

Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
}

IsDirty = true;
Expand Down Expand Up @@ -753,6 +770,18 @@ private void UpdateSkinColor()
_rgbSkinColorContainer.Visible = true;
}

// set the RGB values to the direct values otherwise
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
break;
}
case SpeciesSkinColor.TintedHues:
{
if (!_rgbSkinColorContainer.Visible)
{
_skinColor.Visible = false;
_rgbSkinColorContainer.Visible = true;
}

// set the RGB values to the direct values otherwise
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
break;
Expand Down
42 changes: 37 additions & 5 deletions Content.Server/Body/Components/BodyComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,20 @@ protected override void Startup()
}
}

public override void Gib(bool gibParts = false)
public override HashSet<EntityUid> Gib(bool gibParts = false)
{
base.Gib(gibParts);
var gibs = base.Gib(gibParts);

SoundSystem.Play(Filter.Pvs(Owner), _gibSound.GetSound(), _entMan.GetComponent<TransformComponent>(Owner).Coordinates, AudioHelpers.WithVariation(0.025f));
var xform = _entMan.GetComponent<TransformComponent>(Owner);
var coordinates = xform.Coordinates;

// These have already been forcefully removed from containers so run it here.
foreach (var part in gibs)
{
_entMan.EventBus.RaiseLocalEvent(part, new PartGibbedEvent(Owner, gibs));
}

SoundSystem.Play(Filter.Pvs(Owner, entityManager: _entMan), _gibSound.GetSound(), coordinates, AudioHelpers.WithVariation(0.025f));

if (_entMan.TryGetComponent(Owner, out ContainerManagerComponent? container))
{
Expand All @@ -96,18 +105,41 @@ public override void Gib(bool gibParts = false)
foreach (var ent in cont.ContainedEntities)
{
cont.ForceRemove(ent);
_entMan.GetComponent<TransformComponent>(ent).Coordinates = _entMan.GetComponent<TransformComponent>(Owner).Coordinates;
_entMan.GetComponent<TransformComponent>(ent).Coordinates = coordinates;
ent.RandomOffset(0.25f);
}
}
}

_entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(), false);
_entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(gibs), false);
_entMan.QueueDeleteEntity(Owner);

return gibs;
}
}

public sealed class BeingGibbedEvent : EntityEventArgs
{
public readonly HashSet<EntityUid> GibbedParts;

public BeingGibbedEvent(HashSet<EntityUid> gibbedParts)
{
GibbedParts = gibbedParts;
}
}

/// <summary>
/// An event raised on all the parts of an entity when it's gibbed
/// </summary>
public sealed class PartGibbedEvent : EntityEventArgs
{
public EntityUid EntityToGib;
public readonly HashSet<EntityUid> GibbedParts;

public PartGibbedEvent(EntityUid entityToGib, HashSet<EntityUid> gibbedParts)
{
EntityToGib = entityToGib;
GibbedParts = gibbedParts;
}
}
}
35 changes: 35 additions & 0 deletions Content.Server/Body/Components/BodyReassembleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Threading;
using Content.Server.Cloning;
using Content.Shared.Actions.ActionTypes;

namespace Content.Server.Body.Components
{
[RegisterComponent]
public sealed class BodyReassembleComponent : Component
{
/// <summary>
/// The dna entry used for reassembling the skeleton
/// updated before the entity is gibbed.
/// </summary>
[ViewVariables]
public ClonerDNAEntry? DNA = null;

/// <summary>
/// The default time it takes to reassemble itself
/// </summary>
[ViewVariables]
[DataField("delay")]
public float DoAfterTime = 5f;

/// <summary>
/// The list of body parts that are needed for reassembly
/// </summary>
[ViewVariables]
public HashSet<EntityUid>? BodyParts = null;

[DataField("action")]
public InstantAction? ReassembleAction = null;

public CancellationTokenSource? CancelToken = null;
}
}
234 changes: 234 additions & 0 deletions Content.Server/Body/Systems/BodyReassembleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
using System.Threading;
using Content.Server.Body.Components;
using Content.Server.Cloning;
using Content.Server.DoAfter;
using Content.Server.Mind.Components;
using Content.Server.Popups;
using Content.Server.Preferences.Managers;
using Content.Shared.Actions;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Preferences;
using Content.Shared.Species;
using Content.Shared.Verbs;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;

/// <remarks>
/// Fair warning, this is all kinda shitcode, but it'll have to wait for a major
/// refactor until proper body systems get added. The current implementation is
/// definitely not ideal and probably will be prone to weird bugs.
/// </remarks>

namespace Content.Server.Body.Systems
{
public sealed class BodyReassembleSystem : EntitySystem
{
[Dependency] private readonly IServerPreferencesManager _prefsManager = null!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidAppearance = default!;

private const float SelfReassembleMultiplier = 2f;

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

SubscribeLocalEvent<BodyReassembleComponent, PartGibbedEvent>(OnPartGibbed);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleActionEvent>(StartReassemblyAction);

SubscribeLocalEvent<BodyReassembleComponent, GetVerbsEvent<AlternativeVerb>>(AddReassembleVerbs);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleCompleteEvent>(ReassembleComplete);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleCancelledEvent>(ReassembleCancelled);
}

private void StartReassemblyAction(EntityUid uid, BodyReassembleComponent component, ReassembleActionEvent args)
{
args.Handled = true;
StartReassembly(uid, component, SelfReassembleMultiplier);
}

private void ReassembleCancelled(EntityUid uid, BodyReassembleComponent component, ReassembleCancelledEvent args)
{
component.CancelToken = null;
}

private void OnPartGibbed(EntityUid uid, BodyReassembleComponent component, PartGibbedEvent args)
{
if (!TryComp<MindComponent>(args.EntityToGib, out var mindComp) || mindComp?.Mind == null)
return;

component.BodyParts = args.GibbedParts;
UpdateDNAEntry(uid, args.EntityToGib);
mindComp.Mind.TransferTo(uid);

if (component.ReassembleAction == null)
return;

_actions.AddAction(uid, component.ReassembleAction, null);
}

private void StartReassembly(EntityUid uid, BodyReassembleComponent component, float multiplier = 1f)
{
if (component.CancelToken != null)
return;

if (!GetNearbyParts(uid, component, out var partList))
return;

if (partList == null)
return;

var doAfterTime = component.DoAfterTime * multiplier;
var cancelToken = new CancellationTokenSource();
component.CancelToken = cancelToken;

var doAfterEventArgs = new DoAfterEventArgs(component.Owner, doAfterTime, cancelToken.Token, component.Owner)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = false,
TargetCancelledEvent = new ReassembleCancelledEvent(),
TargetFinishedEvent = new ReassembleCompleteEvent(uid, uid, partList),
};

_doAfterSystem.DoAfter(doAfterEventArgs);
}

/// <summary>
/// Adds the custom verb for reassembling body parts
/// </summary>
private void AddReassembleVerbs(EntityUid uid, BodyReassembleComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;

if (!TryComp<MindComponent>(uid, out var mind) ||
!mind.HasMind ||
component.CancelToken != null)
return;

// doubles the time if you reconstruct yourself
var multiplier = args.User == uid ? SelfReassembleMultiplier : 1f;

// Custom verb
AlternativeVerb custom = new()
{
Text = Loc.GetString("reassemble-action"),
Act = () =>
{
StartReassembly(uid, component, multiplier);
},
IconEntity = uid,
Priority = 1
};
args.Verbs.Add(custom);
}

private bool GetNearbyParts(EntityUid uid, BodyReassembleComponent component, out HashSet<EntityUid>? partList)
{
partList = new HashSet<EntityUid>();

if (component.BodyParts == null)
return false;

// Ensures all of the old body part pieces are there
var xformQuery = GetEntityQuery<TransformComponent>();
var notFound = true;
var bodyXform = xformQuery.GetComponent(uid);

foreach (var part in component.BodyParts)
{
if (!xformQuery.TryGetComponent(part, out var xform) ||
!bodyXform.Coordinates.InRange(EntityManager, xform.Coordinates, 2f)) continue;

notFound = false;
partList.Add(part);
}

if (notFound)
{
_popupSystem.PopupEntity(Loc.GetString("reassemble-fail"), uid, Filter.Entities(uid));
return false;
}

return true;
}

private void ReassembleComplete(EntityUid uid, BodyReassembleComponent component, ReassembleCompleteEvent args)
{
component.CancelToken = null;

if (component.DNA == null)
return;

// Creates the new entity and transfers the mind component
var speciesProto = _prototype.Index<SpeciesPrototype>(component.DNA.Value.Profile.Species).Prototype;
var mob = EntityManager.SpawnEntity(speciesProto, EntityManager.GetComponent<TransformComponent>(component.Owner).MapPosition);

_humanoidAppearance.UpdateFromProfile(mob, component.DNA.Value.Profile);
MetaData(mob).EntityName = component.DNA.Value.Profile.Name;

if (TryComp<MindComponent>(uid, out var mindcomp) && mindcomp.Mind != null)
mindcomp.Mind.TransferTo(mob);

// Cleans up all the body part pieces
foreach (var entity in args.PartList)
{
EntityManager.DeleteEntity(entity);
}

_popupSystem.PopupEntity(Loc.GetString("reassemble-success", ("user", mob)), mob, Filter.Entities(mob));
}

/// <summary>
/// Called before the skeleton entity is gibbed in order to save
/// the dna for reassembly later
/// </summary>
/// <param name="uid"> the entity that the player will transfer to</param>
/// <param name="body"> the entity whose DNA is being saved</param>
private void UpdateDNAEntry(EntityUid uid, EntityUid body)
{
if (!TryComp<BodyReassembleComponent>(uid, out var skelBodyComp) || !TryComp<MindComponent>(body, out var mindcomp))
return;

if (mindcomp.Mind == null)
return;

if (mindcomp.Mind.UserId == null)
return;

var profile = (HumanoidCharacterProfile) _prefsManager.GetPreferences(mindcomp.Mind.UserId.Value).SelectedCharacter;
skelBodyComp.DNA = new ClonerDNAEntry(mindcomp.Mind, profile);
}

private sealed class ReassembleCompleteEvent : EntityEventArgs
{
/// <summary>
/// The entity being reassembled
/// </summary>
public readonly EntityUid Uid;

/// <summary>
/// The user performing the reassembly
/// </summary>
public readonly EntityUid User;
public readonly HashSet<EntityUid> PartList;

public ReassembleCompleteEvent(EntityUid uid, EntityUid user, HashSet<EntityUid> partList)
{
Uid = uid;
User = user;
PartList = partList;
}
}

private sealed class ReassembleCancelledEvent : EntityEventArgs {}
}
}

public sealed class ReassembleActionEvent : InstantActionEvent { }
2 changes: 1 addition & 1 deletion Content.Server/Cloning/CloningSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void Reset(RoundRestartCleanupEvent ev)
// For example, GameTicker should be using this, and this should be using ICharacterProfile rather than HumanoidCharacterProfile.
// It should carry a reference or copy of itself with the mobs that it affects.
// See TODO in MedicalScannerComponent.
struct ClonerDNAEntry {
public struct ClonerDNAEntry {
public Mind.Mind Mind;
public HumanoidCharacterProfile Profile;

Expand Down
Loading