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

Rainlizard #403

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
61fde0d
Update xenopet.yml
fenndragon Dec 12, 2024
e403ce5
Create folder
fenndragon Dec 12, 2024
c9ef286
Add files via upload
fenndragon Dec 12, 2024
2e58f62
Merge branch 'Fansana:master' into rainlizard
fenndragon Dec 12, 2024
5a08f7f
Add files via upload
fenndragon Dec 12, 2024
c9326f8
Add files via upload
fenndragon Dec 12, 2024
0b9b729
Update xenopet.yml
fenndragon Dec 12, 2024
6ea3561
Update xenopet.yml
fenndragon Dec 12, 2024
33717d3
sprites
fenndragon Dec 12, 2024
f9bf85b
Update xenopet.yml
fenndragon Dec 12, 2024
820b53e
Create folder
fenndragon Dec 12, 2024
5281a5c
Add files via upload
fenndragon Dec 12, 2024
fd84d36
Create folder
fenndragon Dec 12, 2024
97d3602
Add files via upload
fenndragon Dec 12, 2024
018e1a9
Update xenopet.yml
fenndragon Dec 14, 2024
0d88c0c
Merge branch 'Fansana:master' into rainlizard
fenndragon Dec 16, 2024
f5bd542
Delete Content.Client/DeltaV/Lamiae/folder
fenndragon Dec 16, 2024
712cf23
Delete Content.Shared/SegmentedEntity/folder
fenndragon Dec 16, 2024
7d68994
fixes and tweaks
fenndragon Dec 16, 2024
e0b46d1
Merge branch 'rainlizard' of https://github.com/fenndragon/floofstati…
fenndragon Dec 16, 2024
b859789
setup
fenndragon Dec 16, 2024
d4a9e07
morelizards
fenndragon Dec 16, 2024
09361f7
fix
fenndragon Dec 16, 2024
085598a
fix
fenndragon Dec 16, 2024
0c82a61
additions
fenndragon Dec 17, 2024
2a2d7c5
fix
fenndragon Dec 21, 2024
46b5e7e
Merge branch 'Fansana:master' into rainlizard
fenndragon Dec 30, 2024
3cd9bca
r
fenndragon Dec 30, 2024
96b605a
f
fenndragon Dec 30, 2024
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
178 changes: 178 additions & 0 deletions Content.Client/DeltaV/Lamiae/SnakeOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using Content.Shared.SegmentedEntity;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Client.Resources;
using Robust.Client.ResourceManagement;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using System.Numerics;
using System.Linq;


namespace Content.Client.Lamiae;

/// <summary>
/// This draws lamia segments directly from polygons instead of sprites. This is a very novel approach as of the time this is being written (August 2024) but it wouldn't surprise me
/// if there's a better way to do this at some point. Currently we have a very heavy restriction on the tools we can make, forcing me to make several helpers that may be redundant later.
/// This will be overcommented because I know you haven't seen code like this before and you might want to copy it.
/// This is an expansion on some techniques I discovered in (https://github.com/Elijahrane/Delta-v/blob/49d76c437740eab79fc622ab50d628b926e6ddcb/Content.Client/DeltaV/Arcade/S3D/Renderer/S3DRenderer.cs)
/// </summary>
public sealed class SnakeOverlay : Overlay
{
private readonly IResourceCache _resourceCache;
private readonly IEntityManager _entManager;
private readonly SharedTransformSystem _transform;
private readonly SharedHumanoidAppearanceSystem _humanoid = default!;

// Look through these carefully. WorldSpace is useful for debugging. Note that this defaults to "screen space" which breaks when you try and get the world handle.
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;

// Overlays are strange and you need this pattern where you define readonly deps above, and then make a constructor with this pattern. Anything that creates this overlay will then
// have to provide all the deps.
public SnakeOverlay(IEntityManager entManager, IResourceCache resourceCache)
{
_resourceCache = resourceCache;
// we get ent manager from SnakeOverlaySystem turning this on and passing it
_entManager = entManager;
// with ent manager we can fetch our other entity systems
_transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
_humanoid = _entManager.EntitySysManager.GetEntitySystem<SharedHumanoidAppearanceSystem>();

// draw at drawdepth 3
ZIndex = 3;
}

// This step occurs each frame. For some overlays you may want to conisder limiting how often they update, but for player entities that move around fast we'll just do it every frame.
protected override void Draw(in OverlayDrawArgs args)
{
// load the handle, the "pen" we draw with
var handle = args.WorldHandle;

// Get all lamiae the client knows of and their transform in a way we can enumerate over
var enumerator = _entManager.AllEntityQueryEnumerator<SegmentedEntityComponent, TransformComponent>();

// I go over the collection above, pulling out an EntityUid and the two components I need for each.
while (enumerator.MoveNext(out var uid, out var lamia, out var xform))
{
// Skip ones that are off-map. "Map" in this context means interconnected stuff you can travel between by moving, rather than needing e.g. FTL to load a new map.
if (xform.MapID != args.MapId)
continue;

// Skip ones where they are not loaded properly, uninitialized, or w/e
if (lamia.Segments.Count < lamia.NumberOfSegments)
{
_entManager.Dirty(uid, lamia); // pls give me an update...
continue;
}

// By the way, there's a hack to mitigate overdraw somewhat. Check out whatever is going on with the variable called "bounds" in DoAfterOverlay.
// I won't do it here because (1) it's ugly and (2) theoretically these entities can be fucking huge and you'll see the tail end of them when they are way off screen.
// On a PVS level I think segmented entities should be all-or-nothing when it comes to PVS range, that is you either load all of their segments or none.

// Color.White is drawing without modifying color. For clothed tails, we should use White. For skin, we should use the color of the marking.
// TODO: Better way to cache this
if (_entManager.TryGetComponent<HumanoidAppearanceComponent>(uid, out var humanoid))
{
if (humanoid.MarkingSet.TryGetCategory(MarkingCategories.Tail, out var tailMarkings))
{
var col = tailMarkings.First().MarkingColors.First();
DrawLamia(handle, lamia, col);
}
}
else
{
DrawLamia(handle, lamia, Color.White);
}
}
}

// This is where we do the actual drawing.
private void DrawLamia(DrawingHandleWorld handle, SegmentedEntityComponent lamia, Color color)
{
// We're going to store all our verticies in here and then draw them
List<DrawVertexUV2D> verts = new List<DrawVertexUV2D>();

// Radius of the initial segment
float radius = lamia.InitialRadius;

// We're storing the left and right verticies of the last segment so we can start drawing from there without gaps
Vector2? lastPtCW = null;
Vector2? lastPtCCW = null;

var tex = _resourceCache.GetTexture(lamia.TexturePath);

int i = 1;
// do each segment except the last one normally
while (i < lamia.Segments.Count - 1)
{
// get centerpoints of last segment and this one
var origin = _transform.GetWorldPosition(_entManager.GetEntity(lamia.Segments[i - 1]));
var destination = _transform.GetWorldPosition(_entManager.GetEntity(lamia.Segments[i]));

// get direction between the two points and normalize it
var connectorVec = destination - origin;
connectorVec = connectorVec.Normalized();

//get one rotated 90 degrees clockwise
var offsetVecCW = new Vector2(connectorVec.Y, 0 - connectorVec.X);

//and counterclockwise
var offsetVecCCW = new Vector2(0 - connectorVec.Y, connectorVec.X);

/// tri 1: line across first segment and corner of second
if (lastPtCW == null)
{
verts.Add(new DrawVertexUV2D(origin + offsetVecCW * radius, Vector2.Zero));
}
else
{
verts.Add(new DrawVertexUV2D((Vector2) lastPtCW, Vector2.Zero));
}

if (lastPtCCW == null)
{
verts.Add(new DrawVertexUV2D(origin + offsetVecCCW * radius, new Vector2(1, 0)));
}
else
{
verts.Add(new DrawVertexUV2D((Vector2) lastPtCCW, new Vector2(1, 0)));
}

verts.Add(new DrawVertexUV2D(destination + offsetVecCW * radius, new Vector2(0, 1)));

// tri 2: line across second segment and corner of first
if (lastPtCCW == null)
{
verts.Add(new DrawVertexUV2D(origin + offsetVecCCW * radius, new Vector2(1, 0)));
}
else
{
verts.Add(new DrawVertexUV2D((Vector2) lastPtCCW, new Vector2(1, 0)));
}

lastPtCW = destination + offsetVecCW * radius;
verts.Add(new DrawVertexUV2D((Vector2) lastPtCW, new Vector2(0, 1)));
lastPtCCW = destination + offsetVecCCW * radius;
verts.Add(new DrawVertexUV2D((Vector2) lastPtCCW, new Vector2(1, 1)));

// slim down a bit for next segment
radius *= lamia.SlimFactor;

i++;
}

// draw tail (1 tri)
if (lastPtCW != null && lastPtCCW != null)
{
verts.Add(new DrawVertexUV2D((Vector2) lastPtCW, new Vector2(0, 0)));
verts.Add(new DrawVertexUV2D((Vector2) lastPtCCW, new Vector2(1, 0)));

var destination = _transform.GetWorldPosition(_entManager.GetEntity(lamia.Segments.Last()));

verts.Add(new DrawVertexUV2D(destination, new Vector2(0.5f, 1f)));
}

// Draw all of the triangles we just pit in at once
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, texture: tex, verts.ToArray().AsSpan(), color);
}
}
26 changes: 26 additions & 0 deletions Content.Client/DeltaV/Lamiae/SnakeOverlaySystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;

namespace Content.Client.Lamiae;

/// <summary>
/// This system turns on our always-on overlay. I have no opinion on this design pattern or the existence of this file.
/// It also fetches the deps it needs.
/// </summary>
public sealed class SnakeOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;

public override void Initialize()
{
base.Initialize();
_overlay.AddOverlay(new SnakeOverlay(EntityManager, _resourceCache));
}

public override void Shutdown()
{
base.Shutdown();
_overlay.RemoveOverlay<SnakeOverlay>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Content.Shared.Ghost;
using Content.Shared.Maps;
using Content.Shared.Parallax;
using Content.Shared.SegmentedEntity;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Content.Shared.StatusEffect;
Expand Down Expand Up @@ -81,6 +82,8 @@ public sealed partial class ShuttleSystem
private void InitializeFTL()
{
SubscribeLocalEvent<StationPostInitEvent>(OnStationPostInit);
SubscribeLocalEvent<FTLComponent, ComponentShutdown>(OnFtlShutdown);

_bodyQuery = GetEntityQuery<BodyComponent>();
_buckleQuery = GetEntityQuery<BuckleComponent>();
_beaconQuery = GetEntityQuery<FTLBeaconComponent>();
Expand All @@ -97,6 +100,12 @@ private void InitializeFTL()
_cfg.OnValueChanged(CCVars.HyperspaceKnockdownTime, time => _hyperspaceKnockdownTime = TimeSpan.FromSeconds(time), true);
}

private void OnFtlShutdown(Entity<FTLComponent> ent, ref ComponentShutdown args)
{
Del(ent.Comp.VisualizerEntity);
ent.Comp.VisualizerEntity = null;
}

private void OnStationPostInit(ref StationPostInitEvent ev)
{
// Add all grid maps as ftl destinations that anyone can FTL to.
Expand Down Expand Up @@ -343,7 +352,11 @@ private bool TrySetupFTL(EntityUid uid, ShuttleComponent shuttle, [NotNullWhen(t
component = AddComp<FTLComponent>(uid);
component.State = FTLState.Starting;
var audio = _audio.PlayPvs(_startupSound, uid);
audio.Value.Component.Flags |= AudioFlags.GridAudio;

if (audio == null)
return false;

audio!.Value.Component.Flags |= AudioFlags.GridAudio;

if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
{
Expand Down Expand Up @@ -422,8 +435,16 @@ private void UpdateFTLTravelling(Entity<FTLComponent, ShuttleComponent> entity)
var comp = entity.Comp1;
comp.StateTime = StartEndTime.FromCurTime(_gameTiming, DefaultArrivalTime);
comp.State = FTLState.Arriving;
// TODO: Arrival effects
// For now we'll just use the ss13 bubbles but we can do fancier.

if (entity.Comp1.VisualizerProto != null)
{
comp.VisualizerEntity = SpawnAtPosition(entity.Comp1.VisualizerProto, entity.Comp1.TargetCoordinates);
var visuals = Comp<FtlVisualizerComponent>(comp.VisualizerEntity.Value);
visuals.Grid = entity.Owner;
Dirty(comp.VisualizerEntity.Value, visuals);
_transform.SetLocalRotation(comp.VisualizerEntity.Value, entity.Comp1.TargetAngle);
_pvs.AddGlobalOverride(comp.VisualizerEntity.Value);
}

_thruster.DisableLinearThrusters(shuttle);
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
Expand Down Expand Up @@ -509,7 +530,11 @@ private void UpdateFTLArriving(Entity<FTLComponent, ShuttleComponent> entity)

comp.TravelStream = _audio.Stop(comp.TravelStream);
var audio = _audio.PlayPvs(_arrivalSound, uid);
audio.Value.Component.Flags |= AudioFlags.GridAudio;

if (audio == null)
return;

audio!.Value.Component.Flags |= AudioFlags.GridAudio;
// TODO: Shitcode til engine fix

if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
Expand Down Expand Up @@ -615,7 +640,9 @@ private void KnockOverKids(TransformComponent xform, ref ValueList<EntityUid> to
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
if (!_buckleQuery.TryGetComponent(child, out var buckle) || buckle.Buckled)
if (!_buckleQuery.TryGetComponent(child, out var buckle) || buckle.Buckled
|| HasComp<SegmentedEntityComponent>(child)
|| HasComp<SegmentedEntitySegmentComponent>(child))
continue;

toKnock.Add(child);
Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Teleportation/PortalSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Content.Shared.Administration.Logs;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Ghost;
using Content.Shared.Mind.Components;
Expand Down
8 changes: 7 additions & 1 deletion Content.Server/Weapons/Ranged/Systems/GunSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,13 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid?
if (!rayCastResults.Any())
break;

var result = rayCastResults[0];
var raycastEvent = new HitScanAfterRayCastEvent(rayCastResults);
RaiseLocalEvent(lastUser, ref raycastEvent);

if (raycastEvent.RayCastResults == null)
break;

var result = raycastEvent.RayCastResults[0];
var hit = result.HitEntity;
lastHit = hit;

Expand Down
1 change: 1 addition & 0 deletions Content.Shared/Roles/StartingGearPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public string GetGear(string slot, HumanoidCharacterProfile? profile)
{
case "jumpsuit" when profile.Clothing == ClothingPreference.Jumpskirt && !string.IsNullOrEmpty(InnerClothingSkirt):
case "jumpsuit" when profile.Species == "Harpy" && !string.IsNullOrEmpty(InnerClothingSkirt):
case "jumpsuit" when profile.Species == "Lamia" && !string.IsNullOrEmpty(InnerClothingSkirt):
return InnerClothingSkirt;
case "back" when profile.Backpack == BackpackPreference.Satchel && !string.IsNullOrEmpty(Satchel):
return Satchel;
Expand Down
11 changes: 11 additions & 0 deletions Content.Shared/SegmentedEntity/SegmentSpawnedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Shared.SegmentedEntity;

public sealed class SegmentSpawnedEvent : EntityEventArgs
{
public EntityUid Lamia = default!;

public SegmentSpawnedEvent(EntityUid lamia)
{
Lamia = lamia;
}
}
Loading
Loading