diff --git a/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs b/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs index c323caac2a..6c3a17d1b7 100644 --- a/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs +++ b/OpenDreamClient/Interface/DebugWindows/IconDebugWindow.xaml.cs @@ -58,6 +58,7 @@ private void Update() { AddPropertyIfNotDefault("Plane", appearance.Plane, MutableAppearance.Default.Plane); AddPropertyIfNotDefault("Blend Mode", appearance.BlendMode, MutableAppearance.Default.BlendMode); AddPropertyIfNotDefault("Appearance Flags", appearance.AppearanceFlags, MutableAppearance.Default.AppearanceFlags); + AddPropertyIfNotDefault("Vis Flags", appearance.VisFlags, MutableAppearance.Default.VisFlags); AddPropertyIfNotDefault("Invisibility", appearance.Invisibility, MutableAppearance.Default.Invisibility); AddPropertyIfNotDefault("Opacity", appearance.Opacity, MutableAppearance.Default.Opacity); AddPropertyIfNotDefault("Override", appearance.Override, MutableAppearance.Default.Override); diff --git a/OpenDreamClient/Rendering/DreamIcon.cs b/OpenDreamClient/Rendering/DreamIcon.cs index 322a42212c..26e4498cce 100644 --- a/OpenDreamClient/Rendering/DreamIcon.cs +++ b/OpenDreamClient/Rendering/DreamIcon.cs @@ -28,13 +28,6 @@ private set { private DMIResource? _dmi; - public int AnimationFrame { - get { - UpdateAnimation(); - return _animationFrame; - } - } - [ViewVariables] public ImmutableAppearance? Appearance { get => CalculateAnimatedAppearance(); @@ -95,9 +88,9 @@ public void Dispose() { var dmi = flick?.Icon ?? DMI; var iconState = flick?.IconState ?? _iconState; - var animationFrame = flick?.GetAnimationFrame(gameTiming) ?? AnimationFrame; + var animationFrame = flick?.GetAnimationFrame(gameTiming) ?? GetAnimationFrame(); if (animationFrame == -1) // A flick returns -1 for a finished animation - animationFrame = AnimationFrame; + animationFrame = GetAnimationFrame(); if (CachedTexture != null && !_textureDirty && flick == null) return CachedTexture.Texture; @@ -221,16 +214,19 @@ public void GetWorldAABB(Vector2 worldPos, ref Box2? aabb) { } } - private void UpdateAnimation() { + public int GetAnimationFrame() => GetAnimationFrame(_iconState, _direction); + + public int GetAnimationFrame(string? iconState, AtomDirection dir) { if(DMI == null || Appearance == null || _animationComplete) - return; + return _animationFrame; - DMIParser.ParsedDMIState? dmiState = DMI.Description.GetStateOrDefault(_iconState); + DMIParser.ParsedDMIState? dmiState = DMI.Description.GetStateOrDefault(iconState); if(dmiState == null) - return; - DMIParser.ParsedDMIFrame[] frames = dmiState.GetFrames(_direction); + return _animationFrame; + DMIParser.ParsedDMIFrame[] frames = dmiState.GetFrames(dir); - if (frames.Length <= 1) return; + if (frames.Length <= 1) + return 0; var oldFrame = _animationFrame; var currentGameTicks = gameTiming.CurTime.Ticks; @@ -253,6 +249,8 @@ private void UpdateAnimation() { if (oldFrame != _animationFrame) DirtyTexture(); + + return _animationFrame; } private ImmutableAppearance? CalculateAnimatedAppearance() { diff --git a/OpenDreamClient/Rendering/DreamPlane.cs b/OpenDreamClient/Rendering/DreamPlane.cs index 3d6fc05fe3..c6ce329fda 100644 --- a/OpenDreamClient/Rendering/DreamPlane.cs +++ b/OpenDreamClient/Rendering/DreamPlane.cs @@ -102,7 +102,9 @@ public void DrawMouseMap(DrawingHandleWorld handle, DreamViewOverlay overlay, Ve texture = Texture.White; } - var pos = (sprite.Position - worldAABB.BottomLeft) * overlay.IconSize; + var posOffset = -worldAABB.BottomLeft + sprite.RenderPosOffset; + + var pos = (sprite.Position + posOffset) * overlay.IconSize; if (sprite.MainIcon != null) pos += sprite.MainIcon.TextureRenderOffset; diff --git a/OpenDreamClient/Rendering/DreamViewOverlay.cs b/OpenDreamClient/Rendering/DreamViewOverlay.cs index 92d59a2a88..05530b8c70 100644 --- a/OpenDreamClient/Rendering/DreamViewOverlay.cs +++ b/OpenDreamClient/Rendering/DreamViewOverlay.cs @@ -197,9 +197,24 @@ private void DrawAll(OverlayDrawArgs args, EntityUid eye, Vector2i viewportSize) } //handles underlays, overlays, appearance flags, images. Adds them to the result list, so they can be sorted and drawn with DrawIcon() - private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid uid, bool isScreen, ref int tieBreaker, List result, sbyte seeVis, RendererMetaData? parentIcon = null, bool keepTogether = false, Vector3? turfCoords = null, ClientAppearanceSystem.Flick? flick = null) { + private void ProcessIconComponents( + DreamIcon icon, + Vector2 position, + EntityUid uid, + bool isScreen, + bool isVisChild, // DIRECT vis_contents child + ref int tieBreaker, + List result, + sbyte seeVis, + RendererMetaData? parentIcon = null, + bool keepTogether = false, + Vector3? turfCoords = null, + ClientAppearanceSystem.Flick? flick = null + ) { if (icon.Appearance is null) //in the event that appearance hasn't loaded yet return; + if ((isVisChild || (parentIcon?.IsVisContent ?? false)) && (icon.Appearance.VisFlags & VisFlags.Hide) != 0) + return; result.EnsureCapacity(result.Count + icon.Underlays.Count + icon.Overlays.Count + 1); RendererMetaData current = RentRendererMetaData(); @@ -211,6 +226,8 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.TieBreaker = tieBreaker; current.RenderSource = icon.Appearance.RenderSource; current.RenderTarget = icon.Appearance.RenderTarget; + current.VisFlags = icon.Appearance.VisFlags; + current.IsVisContent = parentIcon?.IsVisContent ?? false; current.AppearanceFlags = icon.Appearance.AppearanceFlags; current.BlendMode = icon.Appearance.BlendMode; current.Flick = flick; @@ -223,8 +240,24 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u ); if (parentIcon != null) { - current.ClickUid = parentIcon.ClickUid; - current.MouseOpacity = parentIcon.MouseOpacity; + if(isVisChild) { + current.IsVisContent = true; + var visFlags = current.VisFlags; + current.ClickUid = (visFlags & VisFlags.InheritId) != 0 ? parentIcon.ClickUid : current.ClickUid; + current.MouseOpacity = (visFlags & VisFlags.InheritId) != 0 ? parentIcon.MouseOpacity : icon.Appearance.MouseOpacity; + + if((visFlags & (VisFlags.InheritIcon | VisFlags.InheritIconState | VisFlags.InheritDir)) != 0) { + var usedIcon = (visFlags & VisFlags.InheritIcon) != 0 ? parentIcon.MainIcon! : current.MainIcon; + var usedIconState = (visFlags & VisFlags.InheritIconState) != 0 ? parentIcon.MainIcon!.Appearance!.IconState : current.MainIcon.Appearance.IconState; + var usedDir = (visFlags & VisFlags.InheritDir) != 0 ? parentIcon.MainIcon!.Appearance!.Direction : current.MainIcon.Appearance.Direction; + + current.TextureOverride = usedIcon.DMI?.GetState(usedIconState)?.GetFrames(usedDir).ElementAtOrDefault(usedIcon.GetAnimationFrame(usedIconState, usedDir)); + } + } else { + current.ClickUid = parentIcon.ClickUid; + current.MouseOpacity = parentIcon.MouseOpacity; + } + if ((icon.Appearance.AppearanceFlags & AppearanceFlags.ResetColor) != 0 || keepTogether) { //RESET_COLOR current.ColorToApply = icon.Appearance.Color; current.ColorMatrixToApply = icon.Appearance.ColorMatrix; @@ -243,13 +276,16 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u else current.TransformToApply = iconAppearanceTransformMatrix * parentIcon.TransformToApply; - if ((icon.Appearance.Plane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit. - current.Plane = parentIcon.Plane + (icon.Appearance.Plane + 32767); + var effectivePlane = (isVisChild && (current.VisFlags & VisFlags.InheritPlane) != 0) ? parentIcon.Plane : icon.Appearance.Plane; + var effectiveLayer = (isVisChild && (current.VisFlags & VisFlags.InheritLayer) != 0) ? parentIcon.Layer : icon.Appearance.Layer; + + if ((effectivePlane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit. + current.Plane = parentIcon.Plane + (effectivePlane + 32767); else - current.Plane = icon.Appearance.Plane; + current.Plane = effectivePlane; //FLOAT_LAYER - if this icon's layer is negative, it's a float layer so set it's layer equal to the parent object and sort through the float_layer shit later - current.Layer = (icon.Appearance.Layer < 0) ? parentIcon.Layer : icon.Appearance.Layer; + current.Layer = (effectiveLayer < 0) ? parentIcon.Layer : effectiveLayer; if (current.BlendMode == BlendMode.Default) current.BlendMode = parentIcon.BlendMode; @@ -294,6 +330,35 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u result.Add(renderTargetPlaceholder); } + // vis_contents are split with VIS_UNDERLAY, so we need to do this early + var visContents = icon.Appearance.VisContents; + List? underContents = null; + List? overContents = null; + foreach(var visContent in visContents) { + EntityUid visContentEntity = _entityManager.GetEntity(visContent); + if (!_spriteQuery.TryGetComponent(visContentEntity, out var sprite)) + continue; + + var appearance = sprite.Icon.Appearance; + if(appearance is null || (appearance.VisFlags & VisFlags.Hide) != 0) // don't waste our time + continue; + if (!_spriteSystem.IsVisible(sprite, null, seeVis, null)) + continue; + + if((appearance.VisFlags & VisFlags.Underlay) == 0) + (overContents ??= new(visContents.Length)).Add(visContentEntity); + else + (underContents ??= new(visContents.Length)).Add(visContentEntity); + } + + //underlay vis_contents are rendered first, before the underlays even + if(underContents is not null) { + foreach(var visContentEntity in underContents) { + var sprite = _spriteQuery.GetComponent(visContentEntity); + ProcessIconComponents(sprite.Icon, position, visContentEntity, false, true, ref tieBreaker, result, seeVis, current, keepTogether); + } + } + //underlays - colour, alpha, and transform are inherited, but filters aren't //underlays are sorted in reverse order to overlays for(int underlayIndex = icon.Underlays.Count-1; underlayIndex >= 0; underlayIndex--) { @@ -308,10 +373,10 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u (underlay.Appearance.AppearanceFlags & AppearanceFlags.KeepApart) != 0; if (!keepTogether || keepApart) { //KEEP_TOGETHER wasn't set on our parent, or KEEP_APART - ProcessIconComponents(underlay, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current); + ProcessIconComponents(underlay, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current); } else { current.KeepTogetherGroup ??= new(); - ProcessIconComponents(underlay, current.Position, uid, isScreen, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether); + ProcessIconComponents(underlay, current.Position, uid, isScreen, false, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether); } } @@ -330,10 +395,10 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u (overlay.Appearance.AppearanceFlags & AppearanceFlags.KeepApart) != 0; if (!keepTogether || keepApart) { //KEEP_TOGETHER wasn't set on our parent, or KEEP_APART - ProcessIconComponents(overlay, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current); + ProcessIconComponents(overlay, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current); } else { current.KeepTogetherGroup ??= new(); - ProcessIconComponents(overlay, current.Position, uid, isScreen, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether); + ProcessIconComponents(overlay, current.Position, uid, isScreen, false, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether); } } @@ -351,21 +416,16 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u current.MainIcon = sprite.Icon; current.Position += (sprite.Icon.Appearance.TotalPixelOffset / (float)IconSize); } else - ProcessIconComponents(sprite.Icon, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current); + ProcessIconComponents(sprite.Icon, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current); } } - foreach (var visContent in icon.Appearance.VisContents) { - EntityUid visContentEntity = _entityManager.GetEntity(visContent); - if (!_spriteQuery.TryGetComponent(visContentEntity, out var sprite)) - continue; - if (!_spriteSystem.IsVisible(sprite, null, seeVis, null)) - continue; - - ProcessIconComponents(sprite.Icon, position, visContentEntity, false, ref tieBreaker, result, seeVis, current, keepTogether); - - // TODO: click uid should be set to current.uid again - // TODO: vis_flags + // overlay vis_contents are rendered last + if(overContents is not null) { + foreach(var visContentEntity in overContents) { + var sprite = _spriteQuery.GetComponent(visContentEntity); + ProcessIconComponents(sprite.Icon, position, visContentEntity, false, true, ref tieBreaker, result, seeVis, current, keepTogether); + } } //maptext is basically just an image of rendered text added as an overlay @@ -453,7 +513,7 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende // For now, just generate a buffer of 128 pixels around the main icon... Vector2i ktSize = (256, 256) + iconMetaData.MainIcon?.DMI?.IconSize ?? (0,0); iconMetaData.TextureOverride = ProcessKeepTogether(handle, iconMetaData, ktSize); - positionOffset -= ((ktSize/IconSize) - Vector2.One) * new Vector2(0.5f); //correct for KT group texture offset + iconMetaData.RenderPosOffset -= ((ktSize/IconSize) - Vector2.One) * new Vector2(0.5f); //correct for KT group texture offset } //Maptext @@ -472,7 +532,7 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende } var frame = iconMetaData.GetTexture(this, handle); - var pixelPosition = (iconMetaData.Position + positionOffset) * IconSize; + var pixelPosition = (iconMetaData.Position + (positionOffset + iconMetaData.RenderPosOffset)) * IconSize; //if frame is null, this doesn't require a draw, so return NOP if (frame == null) @@ -648,7 +708,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU tValue = 0; //pass the turf coords for client.images lookup Vector3 turfCoords = new Vector3(tileRef.X, tileRef.Y, (int) worldPos.MapId); - ProcessIconComponents(_appearanceSystem.GetTurfIcon((uint)tileRef.Tile.TypeId), worldPos.Position - Vector2.One, EntityUid.Invalid, false, ref tValue, _spriteContainer, seeVis, turfCoords: turfCoords, flick: flick); + ProcessIconComponents(_appearanceSystem.GetTurfIcon((uint)tileRef.Tile.TypeId), worldPos.Position - Vector2.One, EntityUid.Invalid, false, false, ref tValue, _spriteContainer, seeVis, turfCoords: turfCoords, flick: flick); } // Visible entities @@ -679,7 +739,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU var flick = _appearanceSystem.GetMovableFlick(entity); tValue = 0; - ProcessIconComponents(sprite.Icon, worldPos - new Vector2(0.5f), entity, false, ref tValue, _spriteContainer, seeVis, flick: flick); + ProcessIconComponents(sprite.Icon, worldPos - new Vector2(0.5f), entity, false, false, ref tValue, _spriteContainer, seeVis, flick: flick); } } @@ -701,7 +761,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU for (int x = 0; x < sprite.ScreenLocation.RepeatX; x++) { for (int y = 0; y < sprite.ScreenLocation.RepeatY; y++) { tValue = 0; - ProcessIconComponents(sprite.Icon, position + iconSize * new Vector2(x, y), uid, true, ref tValue, _spriteContainer, seeVis); + ProcessIconComponents(sprite.Icon, position + iconSize * new Vector2(x, y), uid, true, false, ref tValue, _spriteContainer, seeVis); } } } diff --git a/OpenDreamClient/Rendering/RendererMetaData.cs b/OpenDreamClient/Rendering/RendererMetaData.cs index 8d50ea57bc..25a54d98d9 100644 --- a/OpenDreamClient/Rendering/RendererMetaData.cs +++ b/OpenDreamClient/Rendering/RendererMetaData.cs @@ -17,9 +17,12 @@ internal sealed class RendererMetaData : IComparable { public ColorMatrix ColorMatrixToApply; public float AlphaToApply; public Matrix3x2 TransformToApply; + public Vector2 RenderPosOffset; public string? RenderSource; public string? RenderTarget; public List? KeepTogetherGroup; + public VisFlags VisFlags; + public bool IsVisContent; public AppearanceFlags AppearanceFlags; public BlendMode BlendMode; public MouseOpacity MouseOpacity; @@ -50,9 +53,12 @@ public void Reset() { ColorMatrixToApply = ColorMatrix.Identity; AlphaToApply = 1.0f; TransformToApply = Matrix3x2.Identity; + RenderPosOffset = Vector2.Zero; RenderSource = ""; RenderTarget = ""; KeepTogetherGroup = null; //don't actually need to allocate this 90% of the time + VisFlags = VisFlags.None; + IsVisContent = false; AppearanceFlags = AppearanceFlags.None; BlendMode = BlendMode.Default; MouseOpacity = MouseOpacity.Transparent; diff --git a/OpenDreamRuntime/AtomManager.cs b/OpenDreamRuntime/AtomManager.cs index fcf5ede69d..d48066c019 100644 --- a/OpenDreamRuntime/AtomManager.cs +++ b/OpenDreamRuntime/AtomManager.cs @@ -142,6 +142,7 @@ public bool IsValidAppearanceVar(string name) { case "plane": case "blend_mode": case "appearance_flags": + case "vis_flags": case "alpha": case "glide_size": case "render_source": @@ -252,8 +253,12 @@ public void SetAppearanceVar(MutableAppearance appearance, string varName, Dream appearance.BlendMode = Enum.IsDefined((BlendMode)blendMode) ? (BlendMode)blendMode : BlendMode.Default; break; case "appearance_flags": - value.TryGetValueAsInteger(out int flagsVar); - appearance.AppearanceFlags = (AppearanceFlags) flagsVar; + value.TryGetValueAsInteger(out int appearanceFlagsVar); + appearance.AppearanceFlags = (AppearanceFlags) appearanceFlagsVar; + break; + case "vis_flags": + value.TryGetValueAsInteger(out int visFlagsVar); + appearance.VisFlags = (VisFlags) visFlagsVar; break; case "alpha": value.TryGetValueAsFloat(out float floatAlpha); @@ -408,6 +413,8 @@ public DreamValue GetAppearanceVar(ImmutableAppearance appearance, string varNam return new((int) appearance.BlendMode); case "appearance_flags": return new((int) appearance.AppearanceFlags); + case "vis_flags": + return new((int) appearance.VisFlags); case "alpha": return new(appearance.Alpha); case "glide_size": @@ -641,6 +648,7 @@ public MutableAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) def.TryGetVariable("render_target", out var renderTargetVar); def.TryGetVariable("blend_mode", out var blendModeVar); def.TryGetVariable("appearance_flags", out var appearanceFlagsVar); + def.TryGetVariable("vis_flags", out var visFlagsVar); def.TryGetVariable("maptext", out var maptextVar); def.TryGetVariable("maptext_width", out var maptextWidthVar); def.TryGetVariable("maptext_height", out var maptextHeightVar); @@ -671,6 +679,7 @@ public MutableAppearance GetAppearanceFromDefinition(DreamObjectDefinition def) SetAppearanceVar(appearance, "render_target", renderTargetVar); SetAppearanceVar(appearance, "blend_mode", blendModeVar); SetAppearanceVar(appearance, "appearance_flags", appearanceFlagsVar); + SetAppearanceVar(appearance, "vis_flags", visFlagsVar); SetAppearanceVar(appearance, "maptext", maptextVar); SetAppearanceVar(appearance, "maptext_width", maptextWidthVar); SetAppearanceVar(appearance, "maptext_height", maptextHeightVar); diff --git a/OpenDreamShared/Dream/ImmutableAppearance.cs b/OpenDreamShared/Dream/ImmutableAppearance.cs index 12c9fe35e8..2bc1d73d1d 100644 --- a/OpenDreamShared/Dream/ImmutableAppearance.cs +++ b/OpenDreamShared/Dream/ImmutableAppearance.cs @@ -42,6 +42,7 @@ public sealed class ImmutableAppearance : IEquatable { [ViewVariables] public readonly int Plane = MutableAppearance.Default.Plane; [ViewVariables] public readonly BlendMode BlendMode = MutableAppearance.Default.BlendMode; [ViewVariables] public readonly AppearanceFlags AppearanceFlags = MutableAppearance.Default.AppearanceFlags; + [ViewVariables] public readonly VisFlags VisFlags = MutableAppearance.Default.VisFlags; [ViewVariables] public readonly sbyte Invisibility = MutableAppearance.Default.Invisibility; [ViewVariables] public readonly bool Opacity = MutableAppearance.Default.Opacity; [ViewVariables] public readonly bool Override = MutableAppearance.Default.Override; @@ -96,6 +97,7 @@ public ImmutableAppearance(MutableAppearance appearance, SharedAppearanceSystem? RenderTarget = appearance.RenderTarget; BlendMode = appearance.BlendMode; AppearanceFlags = appearance.AppearanceFlags; + VisFlags = appearance.VisFlags; Invisibility = appearance.Invisibility; Opacity = appearance.Opacity; MouseOpacity = appearance.MouseOpacity; @@ -169,6 +171,7 @@ public bool Equals(ImmutableAppearance? immutableAppearance) { if (immutableAppearance.RenderTarget != RenderTarget) return false; if (immutableAppearance.BlendMode != BlendMode) return false; if (immutableAppearance.AppearanceFlags != AppearanceFlags) return false; + if (immutableAppearance.VisFlags != VisFlags) return false; if (immutableAppearance.Invisibility != Invisibility) return false; if (immutableAppearance.Opacity != Opacity) return false; if (immutableAppearance.MouseOpacity != MouseOpacity) return false; @@ -253,6 +256,7 @@ public override int GetHashCode() { hashCode.Add(RenderTarget); hashCode.Add(BlendMode); hashCode.Add(AppearanceFlags); + hashCode.Add(VisFlags); hashCode.Add(Maptext); hashCode.Add(MaptextOffset); hashCode.Add(MaptextSize); @@ -347,6 +351,9 @@ public ImmutableAppearance(NetBuffer buffer, IRobustSerializer serializer) { case IconAppearanceProperty.AppearanceFlags: AppearanceFlags = (AppearanceFlags)buffer.ReadInt32(); break; + case IconAppearanceProperty.VisFlags: + VisFlags = (VisFlags)buffer.ReadInt32(); + break; case IconAppearanceProperty.Invisibility: Invisibility = buffer.ReadSByte(); break; @@ -507,6 +514,7 @@ public MutableAppearance ToMutable() { result.RenderTarget = RenderTarget; result.BlendMode = BlendMode; result.AppearanceFlags = AppearanceFlags; + result.VisFlags = VisFlags; result.Invisibility = Invisibility; result.Opacity = Opacity; result.MouseOpacity = MouseOpacity; @@ -629,6 +637,11 @@ public void WriteToBuffer(NetBuffer buffer, IRobustSerializer serializer) { buffer.Write((int)AppearanceFlags); } + if (VisFlags != MutableAppearance.Default.VisFlags) { + buffer.Write((byte)IconAppearanceProperty.VisFlags); + buffer.Write((int)VisFlags); + } + if (Invisibility != MutableAppearance.Default.Invisibility) { buffer.Write((byte)IconAppearanceProperty.Invisibility); buffer.Write(Invisibility); diff --git a/OpenDreamShared/Dream/MutableAppearance.cs b/OpenDreamShared/Dream/MutableAppearance.cs index d6879ddefe..7aded10a13 100644 --- a/OpenDreamShared/Dream/MutableAppearance.cs +++ b/OpenDreamShared/Dream/MutableAppearance.cs @@ -40,6 +40,7 @@ public sealed class MutableAppearance : IEquatable, IDisposab [ViewVariables] public int Plane = -32767; [ViewVariables] public BlendMode BlendMode = BlendMode.Default; [ViewVariables] public AppearanceFlags AppearanceFlags = AppearanceFlags.None; + [ViewVariables] public VisFlags VisFlags = VisFlags.None; [ViewVariables] public sbyte Invisibility; [ViewVariables] public bool Opacity; [ViewVariables] public bool Override; @@ -134,6 +135,7 @@ public void CopyFrom(MutableAppearance appearance) { RenderTarget = appearance.RenderTarget; BlendMode = appearance.BlendMode; AppearanceFlags = appearance.AppearanceFlags; + VisFlags = appearance.VisFlags; Invisibility = appearance.Invisibility; Opacity = appearance.Opacity; MouseOpacity = appearance.MouseOpacity; @@ -189,6 +191,7 @@ public bool Equals(MutableAppearance? appearance) { if (appearance.RenderTarget != RenderTarget) return false; if (appearance.BlendMode != BlendMode) return false; if (appearance.AppearanceFlags != AppearanceFlags) return false; + if (appearance.VisFlags != VisFlags) return false; if (appearance.Invisibility != Invisibility) return false; if (appearance.Opacity != Opacity) return false; if (appearance.MouseOpacity != MouseOpacity) return false; @@ -291,6 +294,7 @@ public override int GetHashCode() { hashCode.Add(RenderTarget); hashCode.Add(BlendMode); hashCode.Add(AppearanceFlags); + hashCode.Add(VisFlags); hashCode.Add(Maptext); hashCode.Add(MaptextOffset); hashCode.Add(MaptextSize); @@ -382,6 +386,19 @@ public enum AppearanceFlags { TileMover = 2048 } +[Flags] +public enum VisFlags { + None = 0, + InheritIcon = 1, + InheritIconState = 2, + InheritDir = 4, + InheritLayer = 8, + InheritPlane = 16, + InheritId = 32, + Underlay = 64, + Hide = 128, +} + [Flags] //kinda, but only EASE_IN and EASE_OUT are used as bitflags, everything else is an enum public enum AnimationEasing { Linear = 0, @@ -437,6 +454,7 @@ public enum IconAppearanceProperty : byte { Plane, BlendMode, AppearanceFlags, + VisFlags, Invisibility, Opacity, Override,