diff --git a/Content.Client/AnthroSystem/AnthroSystem.cs b/Content.Client/AnthroSystem/AnthroSystem.cs new file mode 100644 index 0000000000..c8991e8b2c --- /dev/null +++ b/Content.Client/AnthroSystem/AnthroSystem.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Shared.AnthroSystem; +using Content.Shared.CharacterAppearance; +using Content.Shared.CharacterAppearance.Systems; +using Content.Shared.Preferences; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Utility; + +namespace Content.Client.AnthroSystem +{ + public class AnthroEntitySystem : EntitySystem + { + [Dependency] private readonly AnthroMarkingManager _markingManager = default!; + [Dependency] private readonly AnthroSpeciesManager _speciesManager = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnAnthroSystemInit); + SubscribeLocalEvent(UpdateMarkings); + } + + static HumanoidVisualLayers[] actualBodyParts = + { + HumanoidVisualLayers.Chest, + HumanoidVisualLayers.Head, + HumanoidVisualLayers.RArm, + HumanoidVisualLayers.LArm, + HumanoidVisualLayers.RHand, + HumanoidVisualLayers.LHand, + HumanoidVisualLayers.RLeg, + HumanoidVisualLayers.LLeg, + HumanoidVisualLayers.RFoot, + HumanoidVisualLayers.LFoot + }; + public static string DefaultBase = "human"; + + + private void OnAnthroSystemInit(EntityUid uid, AnthroComponent component, ComponentInit __) + { + foreach (HumanoidVisualLayers layer in actualBodyParts) + { + Logger.DebugS("AnthroSystem", $"Activating marking tracking for {layer}"); + component.ActiveMarkings.Add(layer, new List()); + } + + } + + public void ToggleMarkingVisibility(EntityUid uid, SpriteComponent body, HumanoidVisualLayers layer, bool toggle) + { + var owner = EntityManager.GetEntity(uid); + if(!owner.TryGetComponent(out AnthroComponent? anthroSystem)) return; + + if (anthroSystem.ActiveMarkings.TryGetValue(layer, out List? layerMarkings)) + foreach (AnthroMarking marking in layerMarkings) + body.LayerSetVisible(marking.MarkingId, toggle); + } + // we use HumanoidCharacterProfile because it already exists - + // we just grab the inner Appearance from it since we've tied it in + // severely to that + // + // Probably can untie it, though + // + // TODO: Untie Appearance.Markings, Appearance.SpeciesBase from + // HumanoidCharacterAppearance, inline with the whole SQL model + // thing as well probably + public void UpdateMarkings(EntityUid uid, AnthroComponent anthroSystem, SharedHumanoidAppearanceSystem.ChangedHumanoidAppearanceEvent args) + { + var appearance = args.Appearance; + var owner = EntityManager.GetEntity(uid); + if (!EntityManager.TryGetComponent(uid, out SpriteComponent? sprite)) return; + + if (appearance.SpeciesBase != anthroSystem.LastBase + && _speciesManager.SpeciesHasSprites(appearance.SpeciesBase, + out IReadOnlyCollection>? speciesParts)) + { + Logger.DebugS("AnthroSystem", "Rerendering body sprites due to species difference."); + anthroSystem.LastBase = appearance.SpeciesBase; + foreach (var (layer, speciesSprite) in speciesParts) + if (speciesSprite is not null) sprite.LayerSetSprite(layer, speciesSprite); + } + + Logger.DebugS("AnthroSystem", "Recoloring body now."); + foreach (var part in actualBodyParts) + { + if (!sprite.LayerMapTryGet(part, out int targetLayer)) + { + Logger.DebugS("AnthroSystem", $"Could not get layer {part}"); + continue; + } + + sprite.LayerSetColor(targetLayer, appearance.SkinColor); + } + + Logger.DebugS("AnthroSystem", "Rendering markings now."); + Logger.DebugS("AnthroSystem", $"Marking count: {appearance.Markings.Count}"); + + + // Top -> Bottom ordering + foreach (var marking in appearance.Markings.Reverse()) + { + if (!_markingManager.IsValidMarking(marking, out AnthroMarkingPrototype? markingPrototype)) + { + Logger.DebugS("AnthroSystem", $"Invalid marking {marking.MarkingId}"); + continue; + } + + + if (!sprite.LayerMapTryGet(markingPrototype.BodyPart, out int targetLayer)) + { + Logger.DebugS("AnthroSystem", "Could not get the target layer"); + continue; + } + + Logger.DebugS("AnthroSystem", $"Adding {markingPrototype.Sprites.Count()} markings from {markingPrototype.ID} to layer {targetLayer}"); + + for (int i = 0; i < markingPrototype.Sprites.Count(); i++) + { + string layerId = markingPrototype.ID + markingPrototype.MarkingPartNames[i]; + + if (sprite.LayerMapTryGet(layerId, out var existingLayer)) + { + Logger.DebugS("AnthroSystem", $"Deduplicating {markingPrototype.MarkingPartNames[i]} now from {existingLayer}"); + sprite.RemoveLayer(existingLayer); + sprite.LayerMapRemove(marking.MarkingId); + } + Logger.DebugS("AnthroSystem", $"Adding part {markingPrototype.MarkingPartNames[i]} now to {targetLayer + i + 1}"); + + int layer = sprite.AddLayer(markingPrototype.Sprites[i], targetLayer + i + 1); + sprite.LayerMapSet(layerId, layer); + sprite.LayerSetColor(layerId, marking.MarkingColors[i]); + } + + Logger.DebugS("AnthroSystem", $"Marking added: {markingPrototype.ID}"); + // _activeMarkings[markingPrototype.BodyPart].Add(marking); + } + } + + /* + public void UpdateMarkings(EntityUid uid, HumanoidCharacterProfile profile) + { + UpdateMarkings(uid, profile.Appearance); + } + */ + } +} diff --git a/Content.Shared/AnthroSystem/AnthroComponent.cs b/Content.Shared/AnthroSystem/AnthroComponent.cs new file mode 100644 index 0000000000..f76c5be399 --- /dev/null +++ b/Content.Shared/AnthroSystem/AnthroComponent.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Content.Shared.CharacterAppearance; +using Robust.Shared.GameObjects; + +namespace Content.Shared.AnthroSystem +{ + [RegisterComponent] + public class AnthroComponent : Component + { + public override string Name => "AnthroSystem"; + + public List CurrentMarkingSet = new(); + public List LastMarkingSet = new(); + public string LastBase = "human"; + public Dictionary> ActiveMarkings = new(); + } +} diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index d26b84579b..73ec1c4986 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -193,6 +193,7 @@ types: Heat : 1 #per second, scales with temperature & other constants - type: HumanoidAppearance + - type: AnthroSystem # ECS!!! - type: Body template: HumanoidTemplate preset: HumanPreset @@ -383,6 +384,7 @@ layer: - Opaque - type: HumanoidAppearance + - type: AnthroSystem # ECS!!! - type: Body template: HumanoidTemplate preset: HumanPreset