* Content side new physics structure

* BroadPhase outline done

* But we need to fix WorldAABB

* Fix static pvs AABB

* Fix import

* Rando fixes

* B is for balloon

* Change human mob hitbox to circle

* Decent movement

* Start adding friction to player controller

I think it's the best way to go about it to keep other objects somewhat consistent for physics.

* This baby can fit so many physics bugs in it.

* Slight mob mover optimisations.

* Player mover kinda works okay.

* Beginnings of testbed

* More testbed

* Circlestack bed

* Namespaces

* BB fixes

* Pull WorldAABB

* Joint pulling

* Semi-decent movement I guess.

* Pulling better

* Bullet controller + old movement

* im too dumb for this shit

* Use kinematic mob controller again

It's probably for the best TBH

* Stashed shitcode

* Remove SlipController

* In which movement code is entirely refactored

* Singularity fix

* Fix ApplyLinearImpulse

* MoveRelay fix

* Fix door collisions

* Disable subfloor collisions

Saves on broadphase a fair bit

* Re-implement ClimbController

* Zumzum's pressure

* Laggy item throwing

* Minor atmos change

* Some caching

* Optimise controllers

* Optimise CollideWith to hell and back

* Re-do throwing and tile friction

* Landing too

* Optimise controllers

* Move CCVars and other stuff swept is beautiful

* Cleanup a bunch of controllers

* Fix shooting and high pressure movement controller

* Flashing improvements

* Stuff and things

* Combat collisions

* Combat mode collisions

* Pulling distance joint again

* Cleanup physics interfaces

* More like scuffedularity

* Shit's fucked

* Haha tests go green

* Bigmoneycrab

* Fix dupe pulling

* Zumzum's based fix

* Don't run tile friction for non-predicted bodies

* Experimental pulling improvement

* Everything's a poly now

* Optimise AI region debugging a bit

Could still be better but should improve default performance a LOT

* Mover no updater

* Crazy kinematic body idea

* Good collisions

* KinematicController

* Fix aghost

* Throwing refactor

* Pushing cleanup

* Fix throwing and footstep sounds

* Frametime in ICollideBehavior

* Fix stuff

* Actually fix weightlessness

* Optimise collision behaviors a lot

* Make open lockers still collide with walls

* powwweeerrrrr

* Merge master proper

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* Ch ch ch changesss

* SHIP IT

* Fix #if DEBUG

* Fix vaulting and item locker collision

* Fix throwing

* Editing yaml by hand what can go wrong

* on

* Last yaml fixes

* Okay now it's fixed

* Linter

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
Co-authored-by: Vera Aguilera Puerto <zddm@outlook.es>
This commit is contained in:
metalgearsloth
2021-03-08 04:09:59 +11:00
committed by GitHub
parent 217e8c0ba2
commit 4d064abcd7
237 changed files with 3365 additions and 2880 deletions

View File

@@ -11,14 +11,13 @@ namespace Content.Client.GameObjects.Components.Movement
{ {
base.HandleComponentState(curState, nextState); base.HandleComponentState(curState, nextState);
if (curState is not ClimbModeComponentState climbModeState || Body == null) if (curState is not ClimbModeComponentState climbModeState)
{ {
return; return;
} }
IsClimbing = climbModeState.Climbing; IsClimbing = climbModeState.Climbing;
OwnerIsTransitioning = climbModeState.IsTransitioning;
} }
public override bool IsClimbing { get; set; }
} }
} }

View File

@@ -1,15 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Client.GameObjects.Components.Movement
{
[RegisterComponent]
[ComponentReference(typeof(IMoverComponent))]
public class PlayerInputMoverComponent : SharedPlayerInputMoverComponent
{
public override EntityCoordinates LastPosition { get; set; }
public override float StepSoundDistance { get; set; }
}
}

View File

@@ -1,12 +0,0 @@
using Content.Shared.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Projectiles
{
[RegisterComponent]
public class ThrownItemComponent : ProjectileComponent
{
public override string Name => "ThrownItem";
public override uint? NetID => ContentNetIDs.THROWN_ITEM;
}
}

View File

@@ -6,6 +6,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Client.GameObjects.Components.Suspicion namespace Content.Client.GameObjects.Components.Suspicion
{ {
@@ -62,7 +63,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
continue; continue;
} }
if (!ally.TryGetComponent(out IPhysicsComponent physics)) if (!ally.TryGetComponent(out IPhysBody physics))
{ {
continue; continue;
} }
@@ -82,7 +83,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
continue; continue;
} }
var worldBox = physics.WorldAABB; var worldBox = physics.GetWorldAABB();
// if not on screen, or too small, continue // if not on screen, or too small, continue
if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty()) if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty())
@@ -90,7 +91,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
continue; continue;
} }
var screenCoordinates = _eyeManager.WorldToScreen(physics.WorldAABB.TopLeft + (0, 0.5f)); var screenCoordinates = _eyeManager.WorldToScreen(physics.GetWorldAABB().TopLeft + (0, 0.5f));
DrawString(screen, _font, screenCoordinates, _traitorText, Color.OrangeRed); DrawString(screen, _font, screenCoordinates, _traitorText, Color.OrangeRed);
} }
} }

View File

@@ -128,8 +128,12 @@ namespace Content.Client.GameObjects.EntitySystems.AI
if (tooltip == PathfindingDebugMode.Graph) if (tooltip == PathfindingDebugMode.Graph)
{ {
var systemMessage = new SharedAiDebug.RequestPathfindingGraphMessage(); EntityManager.EntityNetManager.SendSystemNetworkMessage(new SharedAiDebug.RequestPathfindingGraphMessage());
EntityManager.EntityNetManager.SendSystemNetworkMessage(systemMessage); }
if (tooltip == PathfindingDebugMode.Regions)
{
EntityManager.EntityNetManager.SendSystemNetworkMessage(new SharedAiDebug.SubscribeReachableMessage());
} }
// TODO: Request region graph, although the client system messages didn't seem to be going through anymore // TODO: Request region graph, although the client system messages didn't seem to be going through anymore
@@ -138,6 +142,11 @@ namespace Content.Client.GameObjects.EntitySystems.AI
private void DisableMode(PathfindingDebugMode mode) private void DisableMode(PathfindingDebugMode mode)
{ {
if (mode == PathfindingDebugMode.Regions && (_modes & PathfindingDebugMode.Regions) != 0)
{
EntityManager.EntityNetManager.SendSystemNetworkMessage(new SharedAiDebug.UnsubscribeReachableMessage());
}
_modes &= ~mode; _modes &= ~mode;
if (_modes == 0) if (_modes == 0)
{ {

View File

@@ -1,43 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems;
using JetBrains.Annotations;
using Robust.Client.Physics;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
[UsedImplicitly]
public class MoverSystem : SharedMoverSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override void Initialize()
{
base.Initialize();
UpdatesBefore.Add(typeof(PhysicsSystem));
}
public override void FrameUpdate(float frameTime)
{
var playerEnt = _playerManager.LocalPlayer?.ControlledEntity;
if (playerEnt == null || !playerEnt.TryGetComponent(out IMoverComponent? mover) || !playerEnt.TryGetComponent(out IPhysicsComponent? physics))
{
return;
}
physics.Predict = true;
UpdateKinematics(playerEnt.Transform, mover, physics);
}
public override void Update(float frameTime)
{
FrameUpdate(frameTime);
}
}
}

View File

@@ -85,13 +85,22 @@ namespace Content.Client.GameObjects.EntitySystems
foreach (var snapGridComponent in grid.GetSnapGridCell(position, SnapGridOffset.Center)) foreach (var snapGridComponent in grid.GetSnapGridCell(position, SnapGridOffset.Center))
{ {
var entity = snapGridComponent.Owner; var entity = snapGridComponent.Owner;
if (!entity.TryGetComponent(out SubFloorHideComponent subFloorComponent) || if (!entity.TryGetComponent(out SubFloorHideComponent subFloorComponent))
!entity.TryGetComponent(out ISpriteComponent spriteComponent))
{ {
continue; continue;
} }
spriteComponent.Visible = EnableAll || !subFloorComponent.Running || tileDef.IsSubFloor; var enabled = EnableAll || !subFloorComponent.Running || tileDef.IsSubFloor;
if (entity.TryGetComponent(out ISpriteComponent spriteComponent))
{
spriteComponent.Visible = enabled;
}
if (entity.TryGetComponent(out PhysicsComponent physicsComponent))
{
physicsComponent.CanCollide = enabled;
}
} }
} }
} }

View File

@@ -0,0 +1,36 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics.Controllers;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics.Dynamics;
namespace Content.Client.Physics.Controllers
{
public sealed class MoverController : SharedMoverController
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
var player = _playerManager.LocalPlayer?.ControlledEntity;
if (player == null ||
!player.TryGetComponent(out IMoverComponent? mover) ||
!player.TryGetComponent(out PhysicsComponent? body)) return;
body.Predict = true; // TODO: equal prediction instead of true?
// Server-side should just be handled on its own so we'll just do this shizznit
if (player.TryGetComponent(out IMobMoverComponent? mobMover))
{
HandleMobMovement(mover, body, mobMover);
return;
}
HandleKinematicMovement(mover, body);
}
}
}

View File

@@ -82,7 +82,7 @@ namespace Content.IntegrationTests.Tests.Disposal
- type: Anchorable - type: Anchorable
- type: PowerReceiver - type: PowerReceiver
- type: Physics - type: Physics
anchored: true bodyType: Static
- type: entity - type: entity
name: DisposalTrunkDummy name: DisposalTrunkDummy

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Doors; using Content.Server.GameObjects.Components.Doors;
using Content.Shared.GameObjects.Components.Doors; using Content.Shared.GameObjects.Components.Doors;
using NUnit.Framework; using NUnit.Framework;
@@ -19,10 +20,11 @@ namespace Content.IntegrationTests.Tests.Doors
id: PhysicsDummy id: PhysicsDummy
components: components:
- type: Physics - type: Physics
anchored: false bodyType: Dynamic
shapes: fixtures:
- !type:PhysShapeAabb - shape:
bounds: ""-0.49,-0.49,0.49,0.49"" !type:PhysShapeCircle
bounds: ""-0.49,-0.49,0.49,0.49""
layer: layer:
- Impassable - Impassable
@@ -33,9 +35,11 @@ namespace Content.IntegrationTests.Tests.Doors
- type: Door - type: Door
- type: Airlock - type: Airlock
- type: Physics - type: Physics
shapes: bodyType: Static
- !type:PhysShapeAabb fixtures:
bounds: ""-0.49,-0.49,0.49,0.49"" - shape:
!type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
mask: mask:
- Impassable - Impassable
"; ";
@@ -111,9 +115,9 @@ namespace Content.IntegrationTests.Tests.Doors
var mapManager = server.ResolveDependency<IMapManager>(); var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
IPhysBody physBody = null;
IEntity physicsDummy = null; IEntity physicsDummy = null;
IEntity airlock = null; IEntity airlock = null;
TestController controller = null;
ServerDoorComponent doorComponent = null; ServerDoorComponent doorComponent = null;
var physicsDummyStartingX = -1; var physicsDummyStartingX = -1;
@@ -128,9 +132,7 @@ namespace Content.IntegrationTests.Tests.Doors
airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates((0, 0), mapId)); airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates((0, 0), mapId));
Assert.True(physicsDummy.TryGetComponent(out IPhysicsComponent physics)); Assert.True(physicsDummy.TryGetComponent(out physBody));
controller = physics.EnsureController<TestController>();
Assert.True(airlock.TryGetComponent(out doorComponent)); Assert.True(airlock.TryGetComponent(out doorComponent));
Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed)); Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed));
@@ -139,12 +141,13 @@ namespace Content.IntegrationTests.Tests.Doors
await server.WaitIdleAsync(); await server.WaitIdleAsync();
// Push the human towards the airlock // Push the human towards the airlock
controller.LinearVelocity = (0.5f, 0); Assert.That(physBody != null);
physBody.LinearVelocity = (0.5f, 0);
for (var i = 0; i < 240; i += 10) for (var i = 0; i < 240; i += 10)
{ {
// Keep the airlock awake so they collide // Keep the airlock awake so they collide
airlock.GetComponent<IPhysicsComponent>().WakeBody(); airlock.GetComponent<IPhysBody>().WakeBody();
// Ensure that it is still closed // Ensure that it is still closed
Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed)); Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed));
@@ -154,12 +157,12 @@ namespace Content.IntegrationTests.Tests.Doors
} }
// Sanity check // Sanity check
Assert.That(physicsDummy.Transform.MapPosition.X, Is.GreaterThan(physicsDummyStartingX)); // Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor
// If you see this yell at me in discord so I can continue to pretend this didn't happen.
// Assert.That(physicsDummy.Transform.MapPosition.X, Is.GreaterThan(physicsDummyStartingX));
// Blocked by the airlock // Blocked by the airlock
Assert.That(physicsDummy.Transform.MapPosition.X, Is.Negative.Or.Zero); Assert.That(Math.Abs(physicsDummy.Transform.MapPosition.X - 1) > 0.01f);
} }
private class TestController : VirtualController { }
} }
} }

View File

@@ -7,6 +7,7 @@ using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics;
namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement
{ {
@@ -59,15 +60,12 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement
// Now let's make the player enter a climbing transitioning state. // Now let's make the player enter a climbing transitioning state.
climbing.IsClimbing = true; climbing.IsClimbing = true;
climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition); climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition);
var body = human.GetComponent<IPhysicsComponent>(); var body = human.GetComponent<IPhysBody>();
// TODO: Check it's climbing
Assert.That(body.HasController<ClimbController>(), "Player has no ClimbController");
// Force the player out of climb state. It should immediately remove the ClimbController. // Force the player out of climb state. It should immediately remove the ClimbController.
climbing.IsClimbing = false; climbing.IsClimbing = false;
Assert.That(!body.HasController<ClimbController>(), "Player wrongly has a ClimbController");
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();

View File

@@ -0,0 +1,270 @@
/*
MIT License
Copyright (c) 2019 Erin Catto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
These tests are derived from box2d's testbed tests but done in a way as to be automated and useful for CI.
*/
using System.Collections.Generic;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Dynamics.Shapes;
using Robust.UnitTesting;
namespace Content.IntegrationTests.Tests.Physics
{
[TestFixture]
public class PhysicsTestBedTest : ContentIntegrationTest
{
[Test]
public async Task TestBoxStack()
{
var server = StartServer();
await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var physicsSystem = entitySystemManager.GetEntitySystem<SharedPhysicsSystem>();
MapId mapId;
var columnCount = 1;
var rowCount = 15;
PhysicsComponent[] bodies = new PhysicsComponent[columnCount * rowCount];
Vector2 firstPos = Vector2.Zero;
await server.WaitPost(() =>
{
mapId = mapManager.CreateMap();
physicsSystem.Maps[mapId].Gravity = new Vector2(0, -9.8f);
var entityManager = IoCManager.Resolve<IEntityManager>();
// TODO: Need a blank entity we can spawn for testbed.
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
var horizontalFixture = new Fixture(ground, horizontal)
{
CollisionLayer = 1,
CollisionMask = 1,
Hard = true
};
ground.AddFixture(horizontalFixture);
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
var verticalFixture = new Fixture(ground, vertical)
{
CollisionLayer = 1,
CollisionMask = 1,
Hard = true
};
ground.AddFixture(verticalFixture);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
PolygonShape shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var box = entityManager.SpawnEntity("BlankEntity",
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
box.BodyType = BodyType.Dynamic;
box.SleepingAllowed = false;
shape = new PolygonShape(0.001f) {Vertices = new List<Vector2>()
{
new(0.5f, -0.5f),
new(0.5f, 0.5f),
new(-0.5f, 0.5f),
new(-0.5f, -0.5f),
}};
box.FixedRotation = true;
// TODO: Need to detect shape and work out if we need to use fixedrotation
var fixture = new Fixture(box, shape)
{
CollisionMask = 1,
CollisionLayer = 1,
Hard = true,
};
box.AddFixture(fixture);
bodies[j * rowCount + i] = box;
}
}
firstPos = bodies[0].Owner.Transform.WorldPosition;
});
await server.WaitRunTicks(1);
// Check that gravity workin
await server.WaitAssertion(() =>
{
Assert.That(firstPos != bodies[0].Owner.Transform.WorldPosition);
});
// Assert
await server.WaitRunTicks(150);
// Assert settled, none below 0, etc.
await server.WaitAssertion(() =>
{
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < bodies.Length; i++)
{
var body = bodies[j * columnCount + i];
var worldPos = body.Owner.Transform.WorldPosition;
// TODO: Multi-column support but I cbf right now
// Can't be more exact as some level of sinking is allowed.
Assert.That(worldPos.EqualsApprox(new Vector2(0.0f, i + 0.5f), 0.1f), $"Expected y-value of {i + 0.5f} but found {worldPos.Y}");
}
}
});
}
[Test]
public async Task TestCircleStack()
{
var server = StartServer();
await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var physicsSystem = entitySystemManager.GetEntitySystem<SharedPhysicsSystem>();
MapId mapId;
var columnCount = 1;
var rowCount = 15;
PhysicsComponent[] bodies = new PhysicsComponent[columnCount * rowCount];
Vector2 firstPos = Vector2.Zero;
await server.WaitPost(() =>
{
mapId = mapManager.CreateMap();
physicsSystem.Maps[mapId].Gravity = new Vector2(0, -9.8f);
var entityManager = IoCManager.Resolve<IEntityManager>();
// TODO: Need a blank entity we can spawn for testbed.
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
var horizontalFixture = new Fixture(ground, horizontal)
{
CollisionLayer = 1,
CollisionMask = 1,
Hard = true
};
ground.AddFixture(horizontalFixture);
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
var verticalFixture = new Fixture(ground, vertical)
{
CollisionLayer = 1,
CollisionMask = 1,
Hard = true
};
ground.AddFixture(verticalFixture);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
PhysShapeCircle shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var circle = entityManager.SpawnEntity("BlankEntity",
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
circle.BodyType = BodyType.Dynamic;
circle.SleepingAllowed = false;
shape = new PhysShapeCircle {Radius = 0.5f};
var fixture = new Fixture(circle, shape)
{
CollisionMask = 1,
CollisionLayer = 1,
Hard = true,
};
circle.AddFixture(fixture);
bodies[j * rowCount + i] = circle;
}
}
firstPos = bodies[0].Owner.Transform.WorldPosition;
});
await server.WaitRunTicks(1);
// Check that gravity workin
await server.WaitAssertion(() =>
{
Assert.That(firstPos != bodies[0].Owner.Transform.WorldPosition);
});
// Assert
await server.WaitRunTicks(150);
// Assert settled, none below 0, etc.
await server.WaitAssertion(() =>
{
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < bodies.Length; i++)
{
var body = bodies[j * columnCount + i];
var worldPos = body.Owner.Transform.WorldPosition;
// TODO: Multi-column support but I cbf right now
// Can't be more exact as some level of sinking is allowed.
Assert.That(worldPos.EqualsApprox(new Vector2(0.0f, i + 0.5f), 0.1f), $"Expected y-value of {i + 0.5f} but found {worldPos.Y}");
}
}
});
}
}
}

View File

@@ -1,79 +0,0 @@
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Pulling;
using Content.Shared.GameObjects.Components.Pulling;
using Content.Shared.Physics.Pull;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Pulling
{
[TestFixture]
[TestOf(typeof(SharedPullableComponent))]
[TestOf(typeof(SharedPullerComponent))]
[TestOf(typeof(PullController))]
public class PullTest : ContentIntegrationTest
{
private const string Prototypes = @"
- type: entity
name: PullTestPullerDummy
id: PullTestPullerDummy
components:
- type: Puller
- type: Physics
- type: entity
name: PullTestPullableDummy
id: PullTestPullableDummy
components:
- type: Pullable
- type: Physics
";
[Test]
public async Task AnchoredNoPullTest()
{
var options = new ServerContentIntegrationOption {ExtraPrototypes = Prototypes};
var server = StartServerDummyTicker(options);
await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
await server.WaitAssertion(() =>
{
mapManager.CreateNewMapEntity(MapId.Nullspace);
var pullerEntity = entityManager.SpawnEntity("PullTestPullerDummy", MapCoordinates.Nullspace);
var pullableEntity = entityManager.SpawnEntity("PullTestPullableDummy", MapCoordinates.Nullspace);
var puller = pullerEntity.GetComponent<SharedPullerComponent>();
var pullable = pullableEntity.GetComponent<PullableComponent>();
var pullablePhysics = pullableEntity.GetComponent<PhysicsComponent>();
pullablePhysics.Anchored = false;
Assert.That(pullable.TryStartPull(puller.Owner));
Assert.That(pullable.Puller, Is.EqualTo(puller.Owner));
Assert.That(pullable.BeingPulled);
Assert.That(puller.Pulling, Is.EqualTo(pullable.Owner));
Assert.That(pullable.TryStopPull);
Assert.That(pullable.Puller, Is.Null);
Assert.That(pullable.BeingPulled, Is.False);
Assert.That(puller.Pulling, Is.Null);
pullablePhysics.Anchored = true;
Assert.That(pullable.TryStartPull(puller.Owner), Is.False);
Assert.That(pullable.Puller, Is.Null);
Assert.That(pullable.BeingPulled, Is.False);
Assert.That(puller.Pulling, Is.Null);
});
}
}
}

View File

@@ -1,61 +0,0 @@
#nullable enable
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
public sealed class SpriteTest : ContentIntegrationTest
{
/// <summary>
/// Test RSIs and textures are valid
/// </summary>
[Test]
public async Task TestSpritePaths()
{
var (client, server) = await StartConnectedServerClientPair();
await client.WaitIdleAsync();
await server.WaitIdleAsync();
var resc = client.ResolveDependency<IResourceCache>();
var entityManager = client.ResolveDependency<IEntityManager>();
var mapManager = client.ResolveDependency<IMapManager>();
var prototypeManager = client.ResolveDependency<IPrototypeManager>();
await client.WaitIdleAsync();
client.Assert(() =>
{
var mapId = mapManager.CreateMap();
string filePath;
var map = mapManager.GetMapEntity(mapId);
foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
{
if (proto.Abstract || !proto.Components.ContainsKey("Sprite"))
continue;
var entity = entityManager.SpawnEntity(proto.ID, map.Transform.MapPosition);
var spriteComponent = entity.GetComponent<ISpriteComponent>();
foreach (var layer in spriteComponent.AllLayers)
{
if (layer.RsiState != null && layer.Rsi != null)
{
filePath = layer.Rsi.Path + "/" + layer.RsiState + ".png";
Assert.That(resc.ContentFileExists(filePath), $"Unable to find {filePath}");
}
}
}
});
await client.WaitIdleAsync();
}
}
}

View File

@@ -5,6 +5,7 @@ using Content.Shared.Utility;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics.Broadphase;
namespace Content.IntegrationTests.Tests.Utility namespace Content.IntegrationTests.Tests.Utility
{ {
@@ -20,9 +21,10 @@ namespace Content.IntegrationTests.Tests.Utility
name: {BlockerDummyId} name: {BlockerDummyId}
components: components:
- type: Physics - type: Physics
shapes: fixtures:
- !type:PhysShapeAabb - shape:
bounds: ""-0.49,-0.49,0.49,0.49"" !type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
mask: mask:
- Impassable - Impassable
"; ";
@@ -37,6 +39,7 @@ namespace Content.IntegrationTests.Tests.Utility
var sMapManager = server.ResolveDependency<IMapManager>(); var sMapManager = server.ResolveDependency<IMapManager>();
var sEntityManager = server.ResolveDependency<IEntityManager>(); var sEntityManager = server.ResolveDependency<IEntityManager>();
var broady = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedBroadPhaseSystem>();
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
@@ -58,6 +61,7 @@ namespace Content.IntegrationTests.Tests.Utility
// Spawn a blocker with an Impassable mask // Spawn a blocker with an Impassable mask
sEntityManager.SpawnEntity(BlockerDummyId, entityCoordinates); sEntityManager.SpawnEntity(BlockerDummyId, entityCoordinates);
broady.Update(0.016f);
// Cannot spawn something with an Impassable layer // Cannot spawn something with an Impassable layer
Assert.Null(sEntityManager.SpawnIfUnobstructed(null, entityCoordinates, CollisionGroup.Impassable)); Assert.Null(sEntityManager.SpawnIfUnobstructed(null, entityCoordinates, CollisionGroup.Impassable));

View File

@@ -8,6 +8,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Server.AI.Utils namespace Content.Server.AI.Utils
{ {
@@ -40,7 +41,7 @@ namespace Content.Server.AI.Utils
angle.ToVec(), angle.ToVec(),
(int)(CollisionGroup.Opaque | CollisionGroup.Impassable | CollisionGroup.MobImpassable)); (int)(CollisionGroup.Opaque | CollisionGroup.Impassable | CollisionGroup.MobImpassable));
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(owner.Transform.MapID, ray, range, owner).ToList(); var rayCastResults = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(owner.Transform.MapID, ray, range, owner).ToList();
return rayCastResults.Count > 0 && rayCastResults[0].HitEntity == target; return rayCastResults.Count > 0 && rayCastResults[0].HitEntity == target;
} }

View File

@@ -8,6 +8,8 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -113,9 +115,9 @@ namespace Content.Server.Administration.Commands
if (found.GetGridId(entityManager) != GridId.Invalid) if (found.GetGridId(entityManager) != GridId.Invalid)
{ {
player.AttachedEntity.Transform.Coordinates = found; player.AttachedEntity.Transform.Coordinates = found;
if (player.AttachedEntity.TryGetComponent(out IPhysicsComponent physics)) if (player.AttachedEntity.TryGetComponent(out IPhysBody physics))
{ {
physics.Stop(); physics.LinearVelocity = Vector2.Zero;
} }
} }
else else

View File

@@ -1,70 +0,0 @@
#nullable enable
using System;
using Content.Server.GameObjects.Components.Atmos;
using Content.Shared.Atmos;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Atmos
{
public class HighPressureMovementController : FrictionController
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
public override IPhysicsComponent? ControlledComponent { protected get; set; }
private const float MoveForcePushRatio = 1f;
private const float MoveForceForcePushRatio = 1f;
private const float ProbabilityOffset = 25f;
private const float ProbabilityBasePercent = 10f;
private const float ThrowForce = 100f;
public void ExperiencePressureDifference(int cycle, float pressureDifference, AtmosDirection direction,
float pressureResistanceProbDelta, EntityCoordinates throwTarget)
{
if (ControlledComponent == null)
return;
// TODO ATMOS stuns?
var transform = ControlledComponent.Owner.Transform;
var pressureComponent = ControlledComponent.Owner.GetComponent<MovedByPressureComponent>();
var maxForce = MathF.Sqrt(pressureDifference) * 2.25f;
var moveProb = 100f;
if (pressureComponent.PressureResistance > 0)
moveProb = MathF.Abs((pressureDifference / pressureComponent.PressureResistance * ProbabilityBasePercent) -
ProbabilityOffset);
if (moveProb > ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f))
&& !float.IsPositiveInfinity(pressureComponent.MoveResist)
&& (!ControlledComponent.Anchored
&& (maxForce >= (pressureComponent.MoveResist * MoveForcePushRatio)))
|| (ControlledComponent.Anchored && (maxForce >= (pressureComponent.MoveResist * MoveForceForcePushRatio))))
{
if (maxForce > ThrowForce)
{
if (throwTarget != EntityCoordinates.Invalid)
{
var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f;
var pos = ((throwTarget.Position - transform.Coordinates.Position).Normalized + direction.ToDirection().ToVec()).Normalized;
LinearVelocity = pos * moveForce;
}
else
{
var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f);
LinearVelocity = direction.ToDirection().ToVec() * moveForce;
}
pressureComponent.LastHighPressureMovementAirCycle = cycle;
}
}
}
}
}

View File

@@ -17,6 +17,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -193,14 +194,12 @@ namespace Content.Server.Atmos
foreach (var entity in _gridTileLookupSystem.GetEntitiesIntersecting(GridIndex, GridIndices)) foreach (var entity in _gridTileLookupSystem.GetEntitiesIntersecting(GridIndex, GridIndices))
{ {
if (!entity.TryGetComponent(out IPhysicsComponent physics) if (!entity.TryGetComponent(out IPhysBody physics)
|| !entity.IsMovedByPressure(out var pressure) || !entity.IsMovedByPressure(out var pressure)
|| entity.IsInContainer()) || entity.IsInContainer())
continue; continue;
physics.WakeBody(); var pressureMovements = physics.Entity.EnsureComponent<MovedByPressureComponent>();
var pressureMovements = physics.EnsureController<HighPressureMovementController>();
if (pressure.LastHighPressureMovementAirCycle < _gridAtmosphereComponent.UpdateCounter) if (pressure.LastHighPressureMovementAirCycle < _gridAtmosphereComponent.UpdateCounter)
{ {
pressureMovements.ExperiencePressureDifference(_gridAtmosphereComponent.UpdateCounter, PressureDifference, _pressureDirection, 0, PressureSpecificTarget?.GridIndices.ToEntityCoordinates(GridIndex, _mapManager) ?? EntityCoordinates.Invalid); pressureMovements.ExperiencePressureDifference(_gridAtmosphereComponent.UpdateCounter, PressureDifference, _pressureDirection, 0, PressureSpecificTarget?.GridIndices.ToEntityCoordinates(GridIndex, _mapManager) ?? EntityCoordinates.Invalid);

View File

@@ -5,6 +5,7 @@ using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.Components.Movement;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.GameObjects.Components.Mobs.Speech; using Content.Shared.GameObjects.Components.Mobs.Speech;
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -55,7 +56,8 @@ namespace Content.Server.Commands
Timer.Spawn(100, () => Timer.Spawn(100, () =>
{ {
entity.EnsureComponent<MindComponent>(); entity.EnsureComponent<MindComponent>();
entity.EnsureComponent<PlayerInputMoverComponent>(); entity.EnsureComponent<SharedPlayerInputMoverComponent>();
entity.EnsureComponent<SharedPlayerMobMoverComponent>();
entity.EnsureComponent<SharedSpeechComponent>(); entity.EnsureComponent<SharedSpeechComponent>();
entity.EnsureComponent<SharedEmotingComponent>(); entity.EnsureComponent<SharedEmotingComponent>();
}); });

View File

@@ -0,0 +1,234 @@
// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Physics;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Dynamics.Shapes;
using Robust.Shared.Timing;
#nullable enable
namespace Content.Server.Commands.Physics
{
/*
* I didn't use blueprints because this is way easier to iterate upon as I can shit out testbed upon testbed on new maps
* and never have to leave my debugger.
*/
/// <summary>
/// Copies of Box2D's physics testbed for debugging.
/// </summary>
[AdminCommand(AdminFlags.Mapping)]
public class TestbedCommand : IConsoleCommand
{
public string Command => "testbed";
public string Description => "Loads a physics testbed and teleports your player there";
public string Help => $"{Command} <mapid> <test>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteLine("Require 2 args for testbed!");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
if (!int.TryParse(args[0], out var mapInt))
{
shell.WriteLine($"Unable to parse map {args[0]}");
return;
}
var mapId = new MapId(mapInt);
if (!mapManager.MapExists(mapId))
{
shell.WriteLine("Unable to find map {mapId}");
return;
}
if (shell.Player == null)
{
shell.WriteLine("No player found");
return;
}
var player = (IPlayerSession) shell.Player;
switch (args[1])
{
case "boxstack":
SetupPlayer(mapId, shell, player, mapManager);
CreateBoxStack(mapId);
break;
case "circlestack":
SetupPlayer(mapId, shell, player, mapManager);
CreateCircleStack(mapId);
break;
default:
shell.WriteLine($"testbed {args[0]} not found!");
return;
}
shell.WriteLine($"Testbed on map {mapId}");
}
private void SetupPlayer(MapId mapId, IConsoleShell shell, IPlayerSession? player, IMapManager mapManager)
{
var pauseManager = IoCManager.Resolve<IPauseManager>();
pauseManager.SetMapPaused(mapId, false);
var map = EntitySystem.Get<SharedPhysicsSystem>().Maps[mapId].Gravity = new Vector2(0, -4.9f);
return;
}
private void CreateBoxStack(MapId mapId)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
var horizontalFixture = new Fixture(ground, horizontal)
{
CollisionLayer = (int) CollisionGroup.Impassable,
CollisionMask = (int) CollisionGroup.Impassable,
Hard = true
};
ground.AddFixture(horizontalFixture);
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
var verticalFixture = new Fixture(ground, vertical)
{
CollisionLayer = (int) CollisionGroup.Impassable,
CollisionMask = (int) CollisionGroup.Impassable,
Hard = true
};
ground.AddFixture(verticalFixture);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
var columnCount = 1;
var rowCount = 15;
PolygonShape shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var box = entityManager.SpawnEntity("BlankEntity",
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
box.BodyType = BodyType.Dynamic;
box.SleepingAllowed = false;
shape = new PolygonShape();
shape.SetAsBox(0.5f, 0.5f);
box.FixedRotation = false;
// TODO: Need to detect shape and work out if we need to use fixedrotation
var fixture = new Fixture(box, shape)
{
CollisionMask = (int) CollisionGroup.Impassable,
CollisionLayer = (int) CollisionGroup.Impassable,
Hard = true,
};
box.AddFixture(fixture);
}
}
}
private void CreateCircleStack(MapId mapId)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
// TODO: Need a blank entity we can spawn for testbed.
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
var horizontalFixture = new Fixture(ground, horizontal)
{
CollisionLayer = (int) CollisionGroup.Impassable,
CollisionMask = (int) CollisionGroup.Impassable,
Hard = true
};
ground.AddFixture(horizontalFixture);
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
var verticalFixture = new Fixture(ground, vertical)
{
CollisionLayer = (int) CollisionGroup.Impassable,
CollisionMask = (int) CollisionGroup.Impassable,
Hard = true
};
ground.AddFixture(verticalFixture);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
var columnCount = 1;
var rowCount = 15;
PhysShapeCircle shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var box = entityManager.SpawnEntity("BlankEntity",
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
box.BodyType = BodyType.Dynamic;
box.SleepingAllowed = false;
shape = new PhysShapeCircle {Radius = 0.5f};
box.FixedRotation = false;
// TODO: Need to detect shape and work out if we need to use fixedrotation
var fixture = new Fixture(box, shape)
{
CollisionMask = (int) CollisionGroup.Impassable,
CollisionLayer = (int) CollisionGroup.Impassable,
Hard = true,
};
box.AddFixture(fixture);
}
}
}
}
}

View File

@@ -4,6 +4,8 @@ using Content.Shared.Construction;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions namespace Content.Server.Construction.Completions
{ {
@@ -15,9 +17,9 @@ namespace Content.Server.Construction.Completions
public async Task PerformAction(IEntity entity, IEntity? user) public async Task PerformAction(IEntity entity, IEntity? user)
{ {
if (!entity.TryGetComponent(out IPhysicsComponent? physics)) return; if (!entity.TryGetComponent(out IPhysBody? physics)) return;
physics.Anchored = Value; physics.BodyType = Value ? BodyType.Static : BodyType.Dynamic;
} }
} }
} }

View File

@@ -3,6 +3,8 @@ using Content.Shared.Construction;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Construction.Conditions namespace Content.Server.Construction.Conditions
@@ -15,21 +17,21 @@ namespace Content.Server.Construction.Conditions
public async Task<bool> Condition(IEntity entity) public async Task<bool> Condition(IEntity entity)
{ {
if (!entity.TryGetComponent(out IPhysicsComponent physics)) return false; if (!entity.TryGetComponent(out IPhysBody physics)) return false;
return physics.Anchored == Anchored; return (physics.BodyType == BodyType.Static && Anchored) || (physics.BodyType != BodyType.Static && !Anchored);
} }
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange) public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
{ {
if (!entity.TryGetComponent(out IPhysicsComponent physics)) return false; if (!entity.TryGetComponent(out IPhysBody physics)) return false;
switch (Anchored) switch (Anchored)
{ {
case true when !physics.Anchored: case true when physics.BodyType != BodyType.Static:
message.AddMarkup("First, anchor it.\n"); message.AddMarkup("First, anchor it.\n");
return true; return true;
case false when physics.Anchored: case false when physics.BodyType == BodyType.Static:
message.AddMarkup("First, unanchor it.\n"); message.AddMarkup("First, unanchor it.\n");
return true; return true;
} }

View File

@@ -77,6 +77,7 @@ namespace Content.Server.Explosions
var impassableEntities = new List<Tuple<IEntity, float>>(); var impassableEntities = new List<Tuple<IEntity, float>>();
var nonImpassableEntities = new List<Tuple<IEntity, float>>(); var nonImpassableEntities = new List<Tuple<IEntity, float>>();
// TODO: Given this seems to rely on physics it should just query directly like everything else.
// The entities are paired with their distance to the epicenter // The entities are paired with their distance to the epicenter
// and splitted into two lists based on if they are Impassable or not // and splitted into two lists based on if they are Impassable or not
@@ -92,7 +93,7 @@ namespace Content.Server.Explosions
continue; continue;
} }
if (!entity.TryGetComponent(out IPhysicsComponent? body) || body.PhysicsShapes.Count < 1) if (!entity.TryGetComponent(out PhysicsComponent? body) || body.Fixtures.Count < 1)
{ {
continue; continue;
} }

View File

@@ -8,6 +8,8 @@ using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components namespace Content.Server.GameObjects.Components
@@ -37,7 +39,7 @@ namespace Content.Server.GameObjects.Components
/// <returns>true if it is valid, false otherwise</returns> /// <returns>true if it is valid, false otherwise</returns>
private async Task<bool> Valid(IEntity user, IEntity? utilizing, [NotNullWhen(true)] bool force = false) private async Task<bool> Valid(IEntity user, IEntity? utilizing, [NotNullWhen(true)] bool force = false)
{ {
if (!Owner.HasComponent<IPhysicsComponent>()) if (!Owner.HasComponent<IPhysBody>())
{ {
return false; return false;
} }
@@ -69,8 +71,8 @@ namespace Content.Server.GameObjects.Components
return false; return false;
} }
var physics = Owner.GetComponent<IPhysicsComponent>(); var physics = Owner.GetComponent<IPhysBody>();
physics.Anchored = true; physics.BodyType = BodyType.Static;
if (Owner.TryGetComponent(out PullableComponent? pullableComponent)) if (Owner.TryGetComponent(out PullableComponent? pullableComponent))
{ {
@@ -100,8 +102,8 @@ namespace Content.Server.GameObjects.Components
return false; return false;
} }
var physics = Owner.GetComponent<IPhysicsComponent>(); var physics = Owner.GetComponent<IPhysBody>();
physics.Anchored = false; physics.BodyType = BodyType.Dynamic;
return true; return true;
} }
@@ -115,12 +117,12 @@ namespace Content.Server.GameObjects.Components
/// <returns>true if toggled, false otherwise</returns> /// <returns>true if toggled, false otherwise</returns>
private async Task<bool> TryToggleAnchor(IEntity user, IEntity? utilizing = null, bool force = false) private async Task<bool> TryToggleAnchor(IEntity user, IEntity? utilizing = null, bool force = false)
{ {
if (!Owner.TryGetComponent(out IPhysicsComponent? physics)) if (!Owner.TryGetComponent(out IPhysBody? physics))
{ {
return false; return false;
} }
return physics.Anchored ? return physics.BodyType == BodyType.Static ?
await TryUnAnchor(user, utilizing, force) : await TryUnAnchor(user, utilizing, force) :
await TryAnchor(user, utilizing, force); await TryAnchor(user, utilizing, force);
} }

View File

@@ -16,12 +16,15 @@ using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.GameObjects.Components.Atmos
{ {
[RegisterComponent] [RegisterComponent]
public class FlammableComponent : SharedFlammableComponent, ICollideBehavior, IFireAct, IReagentReaction public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IReagentReaction
{ {
private bool _resisting = false; private bool _resisting = false;
private readonly List<EntityUid> _collided = new(); private readonly List<EntityUid> _collided = new();
@@ -129,19 +132,19 @@ namespace Content.Server.GameObjects.Components.Atmos
} }
var entity = Owner.EntityManager.GetEntity(uid); var entity = Owner.EntityManager.GetEntity(uid);
var physics = Owner.GetComponent<IPhysicsComponent>(); var physics = Owner.GetComponent<IPhysBody>();
var otherPhysics = entity.GetComponent<IPhysicsComponent>(); var otherPhysics = entity.GetComponent<IPhysBody>();
if (!physics.WorldAABB.Intersects(otherPhysics.WorldAABB)) if (!physics.GetWorldAABB().Intersects(otherPhysics.GetWorldAABB()))
{ {
_collided.Remove(uid); _collided.Remove(uid);
} }
} }
} }
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable)) if (!otherBody.Entity.TryGetComponent(out FlammableComponent otherFlammable))
return; return;
if (!FireSpread || !otherFlammable.FireSpread) if (!FireSpread || !otherFlammable.FireSpread)

View File

@@ -13,6 +13,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.GameObjects.Components.Atmos
{ {
@@ -41,7 +42,7 @@ namespace Content.Server.GameObjects.Components.Atmos
public GasMixture Air { get; set; } = new (DefaultVolume); public GasMixture Air { get; set; } = new (DefaultVolume);
[ViewVariables] [ViewVariables]
public bool Anchored => !Owner.TryGetComponent<IPhysicsComponent>(out var physics) || physics.Anchored; public bool Anchored => !Owner.TryGetComponent<IPhysBody>(out var physics) || physics.BodyType == BodyType.Static;
/// <summary> /// <summary>
/// The floor connector port that the canister is attached to. /// The floor connector port that the canister is attached to.
@@ -71,7 +72,7 @@ namespace Content.Server.GameObjects.Components.Atmos
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
if (Owner.TryGetComponent<IPhysicsComponent>(out var physics)) if (Owner.TryGetComponent<IPhysBody>(out var physics))
{ {
AnchorUpdate(); AnchorUpdate();
} }

View File

@@ -1,6 +1,14 @@
#nullable enable #nullable enable
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Shared.Atmos;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.Physics;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -9,8 +17,16 @@ namespace Content.Server.GameObjects.Components.Atmos
[RegisterComponent] [RegisterComponent]
public class MovedByPressureComponent : Component public class MovedByPressureComponent : Component
{ {
[Dependency] private readonly IRobustRandom _robustRandom = default!;
public override string Name => "MovedByPressure"; public override string Name => "MovedByPressure";
private const float MoveForcePushRatio = 1f;
private const float MoveForceForcePushRatio = 1f;
private const float ProbabilityOffset = 25f;
private const float ProbabilityBasePercent = 10f;
private const float ThrowForce = 100f;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("enabled")] [DataField("enabled")]
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
@@ -22,6 +38,77 @@ namespace Content.Server.GameObjects.Components.Atmos
public float MoveResist { get; set; } = 100f; public float MoveResist { get; set; } = 100f;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public int LastHighPressureMovementAirCycle { get; set; } = 0; public int LastHighPressureMovementAirCycle { get; set; } = 0;
public void ExperiencePressureDifference(int cycle, float pressureDifference, AtmosDirection direction,
float pressureResistanceProbDelta, EntityCoordinates throwTarget)
{
if (!Owner.TryGetComponent(out PhysicsComponent? physics))
return;
physics.WakeBody();
// TODO ATMOS stuns?
var transform = physics.Owner.Transform;
var maxForce = MathF.Sqrt(pressureDifference) * 2.25f;
var moveProb = 100f;
if (PressureResistance > 0)
moveProb = MathF.Abs((pressureDifference / PressureResistance * ProbabilityBasePercent) -
ProbabilityOffset);
if (moveProb > ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f))
&& !float.IsPositiveInfinity(MoveResist)
&& (!physics.Anchored
&& (maxForce >= (MoveResist * MoveForcePushRatio)))
|| (physics.Anchored && (maxForce >= (MoveResist * MoveForceForcePushRatio))))
{
if (physics.Owner.HasComponent<IMobStateComponent>())
{
physics.BodyStatus = BodyStatus.InAir;
foreach (var fixture in physics.Fixtures)
{
fixture.CollisionMask &= ~(int) CollisionGroup.VaultImpassable;
}
Owner.SpawnTimer(2000, () =>
{
if (Deleted || !Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return;
// Uhh if you get race conditions good luck buddy.
if (physicsComponent.Owner.HasComponent<IMobStateComponent>())
{
physicsComponent.BodyStatus = BodyStatus.OnGround;
}
foreach (var fixture in physics.Fixtures)
{
fixture.CollisionMask |= (int) CollisionGroup.VaultImpassable;
}
});
}
if (maxForce > ThrowForce)
{
// Vera please fix ;-;
if (throwTarget != EntityCoordinates.Invalid)
{
var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 15f;
var pos = ((throwTarget.Position - transform.Coordinates.Position).Normalized + direction.ToDirection().ToVec()).Normalized;
physics.ApplyLinearImpulse(pos * moveForce);
}
else
{
var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f);
physics.ApplyLinearImpulse(direction.ToDirection().ToVec() * moveForce);
}
LastHighPressureMovementAirCycle = cycle;
}
}
}
} }
public static class MovedByPressureExtensions public static class MovedByPressureExtensions

View File

@@ -7,6 +7,8 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -15,7 +17,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Chemistry namespace Content.Server.GameObjects.Components.Chemistry
{ {
[RegisterComponent] [RegisterComponent]
class VaporComponent : SharedVaporComponent, ICollideBehavior class VaporComponent : SharedVaporComponent, IStartCollide
{ {
public const float ReactTime = 0.125f; public const float ReactTime = 0.125f;
@@ -31,8 +33,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
private float _timer; private float _timer;
private EntityCoordinates _target; private EntityCoordinates _target;
private bool _running; private bool _running;
private Vector2 _direction;
private float _velocity;
private float _aliveTime; private float _aliveTime;
public override void Initialize() public override void Initialize()
@@ -42,18 +42,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
Owner.EnsureComponentWarn(out SolutionContainerComponent _); Owner.EnsureComponentWarn(out SolutionContainerComponent _);
} }
public void Start(Vector2 dir, float velocity, EntityCoordinates target, float aliveTime) public void Start(Vector2 dir, float speed, EntityCoordinates target, float aliveTime)
{ {
_running = true; _running = true;
_target = target; _target = target;
_direction = dir;
_velocity = velocity;
_aliveTime = aliveTime; _aliveTime = aliveTime;
// Set Move // Set Move
if (Owner.TryGetComponent(out IPhysicsComponent physics)) if (Owner.TryGetComponent(out PhysicsComponent physics))
{ {
var controller = physics.EnsureController<VaporController>(); physics.BodyStatus = BodyStatus.InAir;
controller.Move(_direction, _velocity); physics.ApplyLinearImpulse(dir * speed);
} }
} }
@@ -68,7 +66,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
_timer += frameTime; _timer += frameTime;
_reactTimer += frameTime; _reactTimer += frameTime;
if (_reactTimer >= ReactTime && Owner.TryGetComponent(out IPhysicsComponent physics)) if (_reactTimer >= ReactTime)
{ {
_reactTimer = 0; _reactTimer = 0;
var mapGrid = _mapManager.GetGrid(Owner.Transform.GridID); var mapGrid = _mapManager.GetGrid(Owner.Transform.GridID);
@@ -86,12 +84,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
if(!_reached && _target.TryDistance(Owner.EntityManager, Owner.Transform.Coordinates, out var distance) && distance <= 0.5f) if(!_reached && _target.TryDistance(Owner.EntityManager, Owner.Transform.Coordinates, out var distance) && distance <= 0.5f)
{ {
_reached = true; _reached = true;
if (Owner.TryGetComponent(out IPhysicsComponent coll))
{
var controller = coll.EnsureController<VaporController>();
controller.Stop();
}
} }
if (contents.CurrentVolume == 0 || _timer > _aliveTime) if (contents.CurrentVolume == 0 || _timer > _aliveTime)
@@ -123,26 +115,17 @@ namespace Content.Server.GameObjects.Components.Chemistry
return true; return true;
} }
void ICollideBehavior.CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (!Owner.TryGetComponent(out SolutionContainerComponent contents)) if (!Owner.TryGetComponent(out SolutionContainerComponent contents))
return; return;
contents.Solution.DoEntityReaction(collidedWith, ReactionMethod.Touch); contents.Solution.DoEntityReaction(otherBody.Entity, ReactionMethod.Touch);
// Check for collision with a impassable object (e.g. wall) and stop // Check for collision with a impassable object (e.g. wall) and stop
if (collidedWith.TryGetComponent(out IPhysicsComponent physics)) if ((otherBody.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && otherBody.Hard)
{ {
if ((physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && physics.Hard) Owner.Delete();
{
if (Owner.TryGetComponent(out IPhysicsComponent coll))
{
var controller = coll.EnsureController<VaporController>();
controller.Stop();
}
Owner.Delete();
}
} }
} }
} }

View File

@@ -16,6 +16,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Physics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -430,10 +431,10 @@ namespace Content.Server.GameObjects.Components.Construction
} }
} }
if (Owner.TryGetComponent(out IPhysicsComponent? physics) && if (Owner.TryGetComponent(out IPhysBody? physics) &&
entity.TryGetComponent(out IPhysicsComponent? otherPhysics)) entity.TryGetComponent(out IPhysBody? otherPhysics))
{ {
otherPhysics.Anchored = physics.Anchored; otherPhysics.BodyType = physics.BodyType;
} }
Owner.Delete(); Owner.Delete();

View File

@@ -4,12 +4,14 @@ using Content.Server.GameObjects.Components.MachineLinking;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Shared.GameObjects.Components.Conveyor; using Content.Shared.GameObjects.Components.Conveyor;
using Content.Shared.GameObjects.Components.MachineLinking; using Content.Shared.GameObjects.Components.MachineLinking;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics; using Content.Shared.Physics;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -30,6 +32,8 @@ namespace Content.Server.GameObjects.Components.Conveyor
[DataField("angle")] [DataField("angle")]
private Angle _angle; private Angle _angle;
public float Speed => _speed;
/// <summary> /// <summary>
/// The amount of units to move the entity by per second. /// The amount of units to move the entity by per second.
/// </summary> /// </summary>
@@ -90,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
/// <returns> /// <returns>
/// The angle when taking into account if the conveyor is reversed /// The angle when taking into account if the conveyor is reversed
/// </returns> /// </returns>
private Angle GetAngle() public Angle GetAngle()
{ {
var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0; var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0;
var radians = MathHelper.DegreesToRadians(_angle); var radians = MathHelper.DegreesToRadians(_angle);
@@ -98,7 +102,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment); return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment);
} }
private bool CanRun() public bool CanRun()
{ {
if (State == ConveyorState.Off) if (State == ConveyorState.Off)
{ {
@@ -119,15 +123,16 @@ namespace Content.Server.GameObjects.Components.Conveyor
return true; return true;
} }
private bool CanMove(IEntity entity) public bool CanMove(IEntity entity)
{ {
// TODO We should only check status InAir or Static or MapGrid or /mayber/ container
if (entity == Owner) if (entity == Owner)
{ {
return false; return false;
} }
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
physics.Anchored) physics.BodyType == BodyType.Static)
{ {
return false; return false;
} }
@@ -150,31 +155,6 @@ namespace Content.Server.GameObjects.Components.Conveyor
return true; return true;
} }
public void Update(float frameTime)
{
if (!CanRun())
{
return;
}
var intersecting = Owner.EntityManager.GetEntitiesIntersecting(Owner, true);
var direction = GetAngle().ToVec();
foreach (var entity in intersecting)
{
if (!CanMove(entity))
{
continue;
}
if (entity.TryGetComponent(out IPhysicsComponent? physics))
{
var controller = physics.EnsureController<ConveyedController>();
controller.Move(direction, _speed, entity.Transform.WorldPosition - Owner.Transform.WorldPosition);
}
}
}
public void TriggerSignal(TwoWayLeverSignal signal) public void TriggerSignal(TwoWayLeverSignal signal)
{ {
State = signal switch State = signal switch

View File

@@ -6,6 +6,8 @@ using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -15,7 +17,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.GameObjects.Components.Damage namespace Content.Server.GameObjects.Components.Damage
{ {
[RegisterComponent] [RegisterComponent]
public class DamageOnHighSpeedImpactComponent : Component, ICollideBehavior public class DamageOnHighSpeedImpactComponent : Component, IStartCollide
{ {
[Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -42,16 +44,16 @@ namespace Content.Server.GameObjects.Components.Damage
public float DamageCooldown { get; set; } = 2f; public float DamageCooldown { get; set; } = 2f;
private TimeSpan _lastHit = TimeSpan.Zero; private TimeSpan _lastHit = TimeSpan.Zero;
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (!Owner.TryGetComponent(out IPhysicsComponent physics) || !Owner.TryGetComponent(out IDamageableComponent damageable)) return; if (!Owner.TryGetComponent(out IDamageableComponent damageable)) return;
var speed = physics.LinearVelocity.Length; var speed = ourBody.LinearVelocity.Length;
if (speed < MinimumSpeed) return; if (speed < MinimumSpeed) return;
if(!string.IsNullOrEmpty(SoundHit)) if(!string.IsNullOrEmpty(SoundHit))
EntitySystem.Get<AudioSystem>().PlayFromEntity(SoundHit, collidedWith, AudioHelpers.WithVariation(0.125f).WithVolume(-0.125f)); EntitySystem.Get<AudioSystem>().PlayFromEntity(SoundHit, otherBody.Entity, AudioHelpers.WithVariation(0.125f).WithVolume(-0.125f));
if ((_gameTiming.CurTime - _lastHit).TotalSeconds < DamageCooldown) if ((_gameTiming.CurTime - _lastHit).TotalSeconds < DamageCooldown)
return; return;
@@ -63,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Damage
if (Owner.TryGetComponent(out StunnableComponent stun) && _robustRandom.Prob(StunChance)) if (Owner.TryGetComponent(out StunnableComponent stun) && _robustRandom.Prob(StunChance))
stun.Stun(StunSeconds); stun.Stun(StunSeconds);
damageable.ChangeDamage(Damage, damage, false, collidedWith); damageable.ChangeDamage(Damage, damage, false, otherBody.Entity);
} }
} }
} }

View File

@@ -10,6 +10,7 @@ using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -76,7 +77,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false; return false;
} }
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide) !physics.CanCollide)
{ {
return false; return false;
@@ -93,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false; return false;
} }
if (entity.TryGetComponent(out IPhysicsComponent? physics)) if (entity.TryGetComponent(out IPhysBody? physics))
{ {
physics.CanCollide = false; physics.CanCollide = false;
} }
@@ -125,7 +126,7 @@ namespace Content.Server.GameObjects.Components.Disposal
foreach (var entity in _contents.ContainedEntities.ToArray()) foreach (var entity in _contents.ContainedEntities.ToArray())
{ {
if (entity.TryGetComponent(out IPhysicsComponent? physics)) if (entity.TryGetComponent(out IPhysBody? physics))
{ {
physics.CanCollide = true; physics.CanCollide = true;
} }

View File

@@ -27,6 +27,8 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timing.Timer; using Timer = Robust.Shared.Timing.Timer;
@@ -147,7 +149,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false; return false;
} }
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide) !physics.CanCollide)
{ {
return false; return false;

View File

@@ -16,6 +16,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent; using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
@@ -33,8 +34,8 @@ namespace Content.Server.GameObjects.Components.Disposal
[ViewVariables] [ViewVariables]
public bool Anchored => public bool Anchored =>
!Owner.TryGetComponent(out IPhysicsComponent? physics) || !Owner.TryGetComponent(out IPhysBody? physics) ||
physics.Anchored; physics.BodyType == BodyType.Static;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalRouterUiKey.Key); [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalRouterUiKey.Key);

View File

@@ -28,6 +28,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Physics;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -144,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Disposal
if (!base.CanInsert(entity)) if (!base.CanInsert(entity))
return false; return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide) !physics.CanCollide)
{ {
if (entity.TryGetComponent(out IMobStateComponent? state) && state.IsDead()) if (entity.TryGetComponent(out IMobStateComponent? state) && state.IsDead())

View File

@@ -1,4 +1,4 @@
#nullable enable #nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@@ -26,6 +26,9 @@ using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timing.Timer; using Timer = Robust.Shared.Timing.Timer;
@@ -34,7 +37,7 @@ namespace Content.Server.GameObjects.Components.Doors
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(SharedDoorComponent))] [ComponentReference(typeof(SharedDoorComponent))]
public class ServerDoorComponent : SharedDoorComponent, IActivate, ICollideBehavior, IInteractUsing, IMapInit public class ServerDoorComponent : SharedDoorComponent, IActivate, IStartCollide, IInteractUsing, IMapInit
{ {
[ComponentDependency] [ComponentDependency]
private readonly IDoorCheck? _doorCheck = null; private readonly IDoorCheck? _doorCheck = null;
@@ -200,7 +203,7 @@ namespace Content.Server.GameObjects.Components.Doors
} }
} }
void ICollideBehavior.CollideWith(IEntity entity) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (State != DoorState.Closed) if (State != DoorState.Closed)
{ {
@@ -214,9 +217,9 @@ namespace Content.Server.GameObjects.Components.Doors
// Disabled because it makes it suck hard to walk through double doors. // Disabled because it makes it suck hard to walk through double doors.
if (entity.HasComponent<IBody>()) if (otherBody.Entity.HasComponent<IBody>())
{ {
if (!entity.TryGetComponent<IMoverComponent>(out var mover)) return; if (!otherBody.Entity.TryGetComponent<IMoverComponent>(out var mover)) return;
/* /*
// TODO: temporary hack to fix the physics system raising collision events akwardly. // TODO: temporary hack to fix the physics system raising collision events akwardly.
@@ -229,7 +232,7 @@ namespace Content.Server.GameObjects.Components.Doors
TryOpen(entity); TryOpen(entity);
*/ */
TryOpen(entity); TryOpen(otherBody.Entity);
} }
} }
@@ -410,18 +413,14 @@ namespace Content.Server.GameObjects.Components.Doors
{ {
var safety = SafetyCheck(); var safety = SafetyCheck();
if (safety && PhysicsComponent != null) if (safety && Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
{ {
var physics = IoCManager.Resolve<IPhysicsManager>(); var broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
foreach(var e in physics.GetCollidingEntities(Owner.Transform.MapID, PhysicsComponent.WorldAABB)) // Use this version so we can ignore the CanCollide being false
foreach(var e in broadPhaseSystem.GetCollidingEntities(physicsComponent.Entity.Transform.MapID, physicsComponent.GetWorldAABB()))
{ {
if (e.CanCollide && if ((physicsComponent.CollisionMask & e.CollisionLayer) != 0 && broadPhaseSystem.IntersectionPercent(physicsComponent, e) > 0.01f) return true;
((PhysicsComponent.CollisionMask & e.CollisionLayer) != 0x0 ||
(PhysicsComponent.CollisionLayer & e.CollisionMask) != 0x0))
{
return true;
}
} }
} }
return false; return false;
@@ -493,26 +492,25 @@ namespace Content.Server.GameObjects.Components.Doors
return false; return false;
} }
var doorAABB = PhysicsComponent.WorldAABB; var doorAABB = PhysicsComponent.GetWorldAABB();
var hitsomebody = false; var hitsomebody = false;
// Crush // Crush
foreach (var e in collidingentities) foreach (var e in collidingentities)
{ {
if (!e.TryGetComponent(out StunnableComponent? stun) if (!e.Entity.TryGetComponent(out StunnableComponent? stun)
|| !e.TryGetComponent(out IDamageableComponent? damage) || !e.Entity.TryGetComponent(out IDamageableComponent? damage))
|| !e.TryGetComponent(out IPhysicsComponent? otherBody))
{ {
continue; continue;
} }
var percentage = otherBody.WorldAABB.IntersectPercentage(doorAABB); var percentage = e.GetWorldAABB().IntersectPercentage(doorAABB);
if (percentage < 0.1f) if (percentage < 0.1f)
continue; continue;
hitsomebody = true; hitsomebody = true;
CurrentlyCrushing.Add(e.Uid); CurrentlyCrushing.Add(e.Entity.Uid);
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner); damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
stun.Paralyze(DoorStunTime); stun.Paralyze(DoorStunTime);

View File

@@ -3,14 +3,13 @@ using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Trigger.TimerTrigger; using Content.Server.GameObjects.Components.Trigger.TimerTrigger;
using Content.Server.Throw; using Content.Server.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Explosion; using Content.Shared.GameObjects.Components.Explosion;
using Robust.Shared.Containers;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -108,10 +107,13 @@ namespace Content.Server.GameObjects.Components.Explosion
var angleMin = segmentAngle * thrownCount; var angleMin = segmentAngle * thrownCount;
var angleMax = segmentAngle * (thrownCount + 1); var angleMax = segmentAngle * (thrownCount + 1);
var angle = Angle.FromDegrees(random.Next(angleMin, angleMax)); var angle = Angle.FromDegrees(random.Next(angleMin, angleMax));
var distance = (float)random.NextFloat() * _throwDistance; // var distance = random.NextFloat() * _throwDistance;
var target = new EntityCoordinates(Owner.Uid, angle.ToVec().Normalized * distance);
grenade.Throw(0.5f, target, grenade.Transform.Coordinates); delay += random.Next(550, 900);
thrownCount++;
// TODO: Suss out throw strength
grenade.TryThrow(angle.ToVec().Normalized * 50);
grenade.SpawnTimer(delay, () => grenade.SpawnTimer(delay, () =>
{ {
@@ -123,9 +125,6 @@ namespace Content.Server.GameObjects.Components.Explosion
useTimer.Trigger(eventArgs.User); useTimer.Trigger(eventArgs.User);
} }
}); });
delay += random.Next(550, 900);
thrownCount++;
} }
Owner.Delete(); Owner.Delete();
@@ -140,7 +139,7 @@ namespace Content.Server.GameObjects.Components.Explosion
if (_unspawnedCount > 0) if (_unspawnedCount > 0)
{ {
_unspawnedCount--; _unspawnedCount--;
grenade = Owner.EntityManager.SpawnEntity(_fillPrototype, Owner.Transform.Coordinates); grenade = Owner.EntityManager.SpawnEntity(_fillPrototype, Owner.Transform.MapPosition);
return true; return true;
} }

View File

@@ -15,6 +15,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -368,7 +369,7 @@ namespace Content.Server.GameObjects.Components.Fluids
foreach (var entity in _snapGrid.GetInDir(direction)) foreach (var entity in _snapGrid.GetInDir(direction))
{ {
if (entity.TryGetComponent(out IPhysicsComponent physics) && if (entity.TryGetComponent(out IPhysBody physics) &&
(physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0) (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0)
{ {
puddle = default; puddle = default;

View File

@@ -27,6 +27,9 @@ using Robust.Shared.Maths;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.GUI namespace Content.Server.GameObjects.Components.GUI
{ {
@@ -718,13 +721,13 @@ namespace Content.Server.GameObjects.Components.GUI
Dirty(); Dirty();
if (!message.Entity.TryGetComponent(out IPhysicsComponent? physics)) if (!message.Entity.TryGetComponent(out IPhysBody? physics))
{ {
return; return;
} }
// set velocity to zero // set velocity to zero
physics.Stop(); physics.LinearVelocity = Vector2.Zero;
return; return;
} }
} }

View File

@@ -12,6 +12,7 @@ using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -19,6 +20,8 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -38,6 +41,11 @@ namespace Content.Server.GameObjects.Components.Items.Storage
private static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5); private static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
private TimeSpan _lastInternalOpenAttempt; private TimeSpan _lastInternalOpenAttempt;
private const int OpenMask = (int) (
CollisionGroup.MobImpassable |
CollisionGroup.VaultImpassable |
CollisionGroup.SmallImpassable);
[ViewVariables] [ViewVariables]
[DataField("Capacity")] [DataField("Capacity")]
private int _storageCapacityMax = 30; private int _storageCapacityMax = 30;
@@ -229,15 +237,21 @@ namespace Content.Server.GameObjects.Components.Items.Storage
private void ModifyComponents() private void ModifyComponents()
{ {
if (!_isCollidableWhenOpen && Owner.TryGetComponent<IPhysicsComponent>(out var physics)) if (!_isCollidableWhenOpen && Owner.TryGetComponent<IPhysBody>(out var physics))
{ {
if (Open) if (Open)
{ {
physics.Hard = false; foreach (var fixture in physics.Fixtures)
{
fixture.CollisionLayer &= ~OpenMask;
}
} }
else else
{ {
physics.Hard = true; foreach (var fixture in physics.Fixtures)
{
fixture.CollisionLayer |= OpenMask;
}
} }
} }
@@ -255,10 +269,10 @@ namespace Content.Server.GameObjects.Components.Items.Storage
protected virtual bool AddToContents(IEntity entity) protected virtual bool AddToContents(IEntity entity)
{ {
if (entity == Owner) return false; if (entity == Owner) return false;
if (entity.TryGetComponent(out IPhysicsComponent? entityPhysicsComponent)) if (entity.TryGetComponent(out IPhysBody? entityPhysicsComponent))
{ {
if(MaxSize < entityPhysicsComponent.WorldAABB.Size.X if(MaxSize < entityPhysicsComponent.GetWorldAABB().Size.X
|| MaxSize < entityPhysicsComponent.WorldAABB.Size.Y) || MaxSize < entityPhysicsComponent.GetWorldAABB().Size.Y)
{ {
return false; return false;
} }
@@ -285,10 +299,10 @@ namespace Content.Server.GameObjects.Components.Items.Storage
{ {
foreach (var contained in Contents.ContainedEntities.ToArray()) foreach (var contained in Contents.ContainedEntities.ToArray())
{ {
if(Contents.Remove(contained)) if (Contents.Remove(contained))
{ {
contained.Transform.WorldPosition = ContentsDumpPosition(); contained.Transform.WorldPosition = ContentsDumpPosition();
if (contained.TryGetComponent<IPhysicsComponent>(out var physics)) if (contained.TryGetComponent<IPhysBody>(out var physics))
{ {
physics.CanCollide = true; physics.CanCollide = true;
} }

View File

@@ -1,6 +1,5 @@
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.Components.Storage;
@@ -8,6 +7,7 @@ using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using Content.Shared.Utility; using Content.Shared.Utility;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -15,6 +15,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -24,7 +25,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
[ComponentReference(typeof(StorableComponent))] [ComponentReference(typeof(StorableComponent))]
[ComponentReference(typeof(SharedStorableComponent))] [ComponentReference(typeof(SharedStorableComponent))]
[ComponentReference(typeof(IItemComponent))] [ComponentReference(typeof(IItemComponent))]
public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent, IThrown, ILand
{ {
public override string Name => "Item"; public override string Name => "Item";
public override uint? NetID => ContentNetIDs.ITEM; public override uint? NetID => ContentNetIDs.ITEM;
@@ -83,8 +84,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return false; return false;
} }
if (Owner.TryGetComponent(out IPhysicsComponent physics) && if (Owner.TryGetComponent(out IPhysBody physics) &&
physics.Anchored) physics.BodyType == BodyType.Static)
{ {
return false; return false;
} }
@@ -137,22 +138,43 @@ namespace Content.Server.GameObjects.Components.Items.Storage
var targetLocation = eventArgs.Target.Transform.Coordinates; var targetLocation = eventArgs.Target.Transform.Coordinates;
var dirVec = (targetLocation.ToMapPos(Owner.EntityManager) - sourceLocation.ToMapPos(Owner.EntityManager)).Normalized; var dirVec = (targetLocation.ToMapPos(Owner.EntityManager) - sourceLocation.ToMapPos(Owner.EntityManager)).Normalized;
var throwForce = 1.0f; float throwForce;
switch (eventArgs.Severity) switch (eventArgs.Severity)
{ {
case ExplosionSeverity.Destruction: case ExplosionSeverity.Destruction:
throwForce = 3.0f; throwForce = 30.0f;
break; break;
case ExplosionSeverity.Heavy: case ExplosionSeverity.Heavy:
throwForce = 2.0f; throwForce = 20.0f;
break; break;
case ExplosionSeverity.Light: default:
throwForce = 1.0f; throwForce = 10.0f;
break; break;
} }
Owner.Throw(throwForce, targetLocation, sourceLocation, true); Owner.TryThrow(dirVec * throwForce);
}
// TODO: Predicted
void IThrown.Thrown(ThrownEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out PhysicsComponent physicsComponent)) return;
foreach (var fixture in physicsComponent.Fixtures)
{
fixture.CollisionLayer |= (int) CollisionGroup.MobImpassable;
}
}
void ILand.Land(LandEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out PhysicsComponent physicsComponent)) return;
foreach (var fixture in physicsComponent.Fixtures)
{
fixture.CollisionLayer &= ~(int) CollisionGroup.MobImpassable;
}
} }
} }
} }

View File

@@ -0,0 +1,50 @@
#nullable enable
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Mobs.State;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Items
{
internal static class ThrowHelper
{
/// <summary>
/// Tries to throw the entity if it has a physics component, otherwise does nothing.
/// </summary>
/// <param name="entity"></param>
/// <param name="direction">Will use the vector's magnitude as the strength of the impulse</param>
internal static void TryThrow(this IEntity entity, Vector2 direction, IEntity? user = null)
{
if (direction == Vector2.Zero || !entity.TryGetComponent(out PhysicsComponent? physicsComponent))
{
return;
}
if (physicsComponent.BodyType == BodyType.Static)
{
Logger.Warning("Tried to throw entity {entity} but can't throw static bodies!");
return;
}
if (entity.HasComponent<IMobStateComponent>())
{
Logger.Warning("Throwing not supported for mobs!");
return;
}
if (entity.HasComponent<ItemComponent>())
{
entity.EnsureComponent<ThrownItemComponent>().Thrower = user;
if (user != null)
EntitySystem.Get<InteractionSystem>().ThrownInteraction(user, entity);
}
physicsComponent.ApplyLinearImpulse(direction);
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Mobs.State namespace Content.Server.GameObjects.Components.Mobs.State
{ {
@@ -30,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Mobs.State
EntitySystem.Get<StandingStateSystem>().Down(entity); EntitySystem.Get<StandingStateSystem>().Down(entity);
if (entity.TryGetComponent(out IPhysicsComponent physics)) if (entity.TryGetComponent(out IPhysBody physics))
{ {
physics.CanCollide = false; physics.CanCollide = false;
} }
@@ -40,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Mobs.State
{ {
base.ExitState(entity); base.ExitState(entity);
if (entity.TryGetComponent(out IPhysicsComponent physics)) if (entity.TryGetComponent(out IPhysBody physics))
{ {
physics.CanCollide = true; physics.CanCollide = true;
} }

View File

@@ -12,8 +12,9 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
{ {
[RegisterComponent, ComponentReference(typeof(IMoverComponent))] [RegisterComponent]
public class AiControllerComponent : Component, IMoverComponent [ComponentReference(typeof(IMobMoverComponent))]
public class AiControllerComponent : Component, IMobMoverComponent, IMoverComponent
{ {
[DataField("logic")] private float _visionRadius = 8.0f; [DataField("logic")] private float _visionRadius = 8.0f;
@@ -88,13 +89,12 @@ namespace Content.Server.GameObjects.Components.Movement
} }
/// <inheritdoc /> /// <inheritdoc />
[ViewVariables] [ViewVariables(VVAccess.ReadWrite)]
public float CurrentPushSpeed => 5.0f; public float PushStrength { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[ViewVariables] [ViewVariables(VVAccess.ReadWrite)]
public float GrabRange => 0.2f; public float GrabRange { get; set; } = 0.2f;
/// <summary> /// <summary>
/// Is the entity Sprinting (running)? /// Is the entity Sprinting (running)?
@@ -113,7 +113,8 @@ namespace Content.Server.GameObjects.Components.Movement
public EntityCoordinates LastPosition { get; set; } public EntityCoordinates LastPosition { get; set; }
[ViewVariables(VVAccess.ReadWrite)] public float StepSoundDistance { get; set; } [ViewVariables(VVAccess.ReadWrite)]
public float StepSoundDistance { get; set; }
public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled) { } public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled) { }
public void SetSprinting(ushort subTick, bool walking) { } public void SetSprinting(ushort subTick, bool walking) { }

View File

@@ -14,6 +14,8 @@ using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
@@ -158,9 +160,11 @@ namespace Content.Server.GameObjects.Components.Movement
var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs); var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs);
if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1) if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out PhysicsComponent body) && body.Fixtures.Count >= 1)
{ {
var direction = (Owner.Transform.WorldPosition - entityToMove.Transform.WorldPosition).Normalized; var entityPos = entityToMove.Transform.WorldPosition;
var direction = (Owner.Transform.WorldPosition - entityPos).Normalized;
var endPoint = Owner.Transform.WorldPosition; var endPoint = Owner.Transform.WorldPosition;
var climbMode = entityToMove.GetComponent<ClimbingComponent>(); var climbMode = entityToMove.GetComponent<ClimbingComponent>();
@@ -168,14 +172,14 @@ namespace Content.Server.GameObjects.Components.Movement
if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line
{ {
endPoint = new Vector2(entityToMove.Transform.WorldPosition.X, endPoint.Y); endPoint = new Vector2(entityPos.X, endPoint.Y);
} }
else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line
{ {
endPoint = new Vector2(endPoint.X, entityToMove.Transform.WorldPosition.Y); endPoint = new Vector2(endPoint.X, entityPos.Y);
} }
climbMode.TryMoveTo(entityToMove.Transform.WorldPosition, endPoint); climbMode.TryMoveTo(entityPos, endPoint);
// we may potentially need additional logic since we're forcing a player onto a climbable // we may potentially need additional logic since we're forcing a player onto a climbable
// there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for // there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for
@@ -203,9 +207,12 @@ namespace Content.Server.GameObjects.Components.Movement
var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs); var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs);
if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1) if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out PhysicsComponent body) && body.Fixtures.Count >= 1)
{ {
var direction = (Owner.Transform.WorldPosition - user.Transform.WorldPosition).Normalized; // TODO: Remove the copy-paste code
var userPos = user.Transform.WorldPosition;
var direction = (Owner.Transform.WorldPosition - userPos).Normalized;
var endPoint = Owner.Transform.WorldPosition; var endPoint = Owner.Transform.WorldPosition;
var climbMode = user.GetComponent<ClimbingComponent>(); var climbMode = user.GetComponent<ClimbingComponent>();
@@ -220,7 +227,7 @@ namespace Content.Server.GameObjects.Components.Movement
endPoint = new Vector2(endPoint.X, user.Transform.WorldPosition.Y); endPoint = new Vector2(endPoint.X, user.Transform.WorldPosition.Y);
} }
climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint); climbMode.TryMoveTo(userPos, endPoint);
var othersMessage = Loc.GetString("{0:theName} jumps onto {1:theName}!", user, Owner); var othersMessage = Loc.GetString("{0:theName} jumps onto {1:theName}!", user, Owner);
user.PopupMessageOtherClients(othersMessage); user.PopupMessageOtherClients(othersMessage);

View File

@@ -1,10 +1,13 @@
#nullable enable #nullable enable
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Buckle; using Content.Shared.GameObjects.Components.Buckle;
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
{ {
@@ -12,23 +15,41 @@ namespace Content.Server.GameObjects.Components.Movement
[ComponentReference(typeof(SharedClimbingComponent))] [ComponentReference(typeof(SharedClimbingComponent))]
public class ClimbingComponent : SharedClimbingComponent public class ClimbingComponent : SharedClimbingComponent
{ {
private bool _isClimbing; [Dependency] private readonly IGameTiming _gameTiming = default!;
private ClimbController? _climbController;
public override bool IsClimbing public override bool IsClimbing
{ {
get => _isClimbing; get => base.IsClimbing;
set set
{ {
if (_isClimbing == value) if (_isClimbing == value)
return; return;
if (!value) base.IsClimbing = value;
if (value)
{ {
Body?.TryRemoveController<ClimbController>(); StartClimbTime = IoCManager.Resolve<IGameTiming>().CurTime;
EntitySystem.Get<ClimbSystem>().AddActiveClimber(this);
OwnerIsTransitioning = true;
}
else
{
EntitySystem.Get<ClimbSystem>().RemoveActiveClimber(this);
OwnerIsTransitioning = false;
} }
_isClimbing = value; Dirty();
}
}
protected override bool OwnerIsTransitioning
{
get => base.OwnerIsTransitioning;
set
{
if (value == base.OwnerIsTransitioning) return;
base.OwnerIsTransitioning = value;
Dirty(); Dirty();
} }
} }
@@ -51,38 +72,36 @@ namespace Content.Server.GameObjects.Components.Movement
/// </summary> /// </summary>
public void TryMoveTo(Vector2 from, Vector2 to) public void TryMoveTo(Vector2 from, Vector2 to)
{ {
if (Body == null) if (Body == null) return;
return;
_climbController = Body.EnsureController<ClimbController>(); var velocity = (to - from).Length;
_climbController.TryMoveTo(from, to);
if (velocity <= 0.0f) return;
Body.ApplyLinearImpulse((to - from).Normalized * velocity * 400);
OwnerIsTransitioning = true;
Owner.SpawnTimer((int) (BufferTime * 1000), () =>
{
if (Deleted) return;
OwnerIsTransitioning = false;
});
} }
public void Update() public void Update()
{ {
if (!IsClimbing || Body == null) if (!IsClimbing || _gameTiming.CurTime < TimeSpan.FromSeconds(BufferTime) + StartClimbTime)
return;
if (_climbController != null && (_climbController.IsBlocked || !_climbController.IsActive))
{ {
if (Body.TryRemoveController<ClimbController>()) return;
{
_climbController = null;
}
} }
if (IsClimbing) if (!IsOnClimbableThisFrame && IsClimbing)
Body.WakeBody();
if (!IsOnClimbableThisFrame && IsClimbing && _climbController == null)
IsClimbing = false; IsClimbing = false;
IsOnClimbableThisFrame = false;
} }
public override ComponentState GetComponentState(ICommonSession player) public override ComponentState GetComponentState(ICommonSession player)
{ {
return new ClimbModeComponentState(_isClimbing); return new ClimbModeComponentState(_isClimbing, OwnerIsTransitioning);
} }
} }
} }

View File

@@ -1,19 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.Components.Movement
{
/// <summary>
/// Moves the entity based on input from a KeyBindingInputComponent.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IMoverComponent))]
public class PlayerInputMoverComponent : SharedPlayerInputMoverComponent
{
public override EntityCoordinates LastPosition { get; set; }
public override float StepSoundDistance { get; set; }
}
}

View File

@@ -11,6 +11,8 @@ using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -21,8 +23,6 @@ namespace Content.Server.GameObjects.Components.Movement
[ComponentReference(typeof(IMoverComponent))] [ComponentReference(typeof(IMoverComponent))]
internal class ShuttleControllerComponent : Component, IMoverComponent internal class ShuttleControllerComponent : Component, IMoverComponent
{ {
[Dependency] private readonly IMapManager _mapManager = default!;
private bool _movingUp; private bool _movingUp;
private bool _movingDown; private bool _movingDown;
private bool _movingLeft; private bool _movingLeft;
@@ -42,43 +42,19 @@ namespace Content.Server.GameObjects.Components.Movement
public override string Name => "ShuttleController"; public override string Name => "ShuttleController";
public bool IgnorePaused => false;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float CurrentWalkSpeed { get; } = 8; public float CurrentWalkSpeed { get; } = 8;
public float CurrentSprintSpeed => 0; public float CurrentSprintSpeed => 0;
/// <inheritdoc />
[ViewVariables]
public float CurrentPushSpeed => 0.0f;
/// <inheritdoc />
[ViewVariables]
public float GrabRange => 0.0f;
public bool Sprinting => false; public bool Sprinting => false;
public (Vector2 walking, Vector2 sprinting) VelocityDir { get; } = (Vector2.Zero, Vector2.Zero); public (Vector2 walking, Vector2 sprinting) VelocityDir { get; set; } = (Vector2.Zero, Vector2.Zero);
public EntityCoordinates LastPosition { get; set; }
public float StepSoundDistance { get; set; }
public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled) public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled)
{ {
var gridId = Owner.Transform.GridID; VelocityDir = (CalcNewVelocity(direction, enabled), Vector2.Zero);
if (_mapManager.TryGetGrid(gridId, out var grid) &&
Owner.EntityManager.TryGetEntity(grid.GridEntityId, out var gridEntity))
{
//TODO: Switch to shuttle component
if (!gridEntity.TryGetComponent(out IPhysicsComponent? physics))
{
physics = gridEntity.AddComponent<PhysicsComponent>();
physics.Mass = 1;
physics.CanCollide = true;
physics.PhysicsShapes.Add(new PhysShapeGrid(grid));
}
var controller = physics.EnsureController<ShuttleController>();
controller.Push(CalcNewVelocity(direction, enabled), CurrentWalkSpeed);
}
} }
public void SetSprinting(ushort subTick, bool walking) public void SetSprinting(ushort subTick, bool walking)

View File

@@ -6,6 +6,8 @@ using Content.Server.GameObjects.Components.NodeContainer.NodeGroups;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.NodeContainer.Nodes namespace Content.Server.GameObjects.Components.NodeContainer.Nodes
@@ -40,7 +42,7 @@ namespace Content.Server.GameObjects.Components.NodeContainer.Nodes
/// </summary> /// </summary>
public bool Connectable => !_deleting && Anchored; public bool Connectable => !_deleting && Anchored;
private bool Anchored => !Owner.TryGetComponent<IPhysicsComponent>(out var physics) || physics.Anchored; private bool Anchored => !Owner.TryGetComponent<IPhysBody>(out var physics) || physics.BodyType == BodyType.Static;
/// <summary> /// <summary>
/// Prevents a node from being used by other nodes while midway through removal. /// Prevents a node from being used by other nodes while midway through removal.

View File

@@ -6,18 +6,20 @@ using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.PA namespace Content.Server.GameObjects.Components.PA
{ {
[RegisterComponent] [RegisterComponent]
public class ParticleProjectileComponent : Component, ICollideBehavior public class ParticleProjectileComponent : Component, IStartCollide
{ {
public override string Name => "ParticleProjectile"; public override string Name => "ParticleProjectile";
private ParticleAcceleratorPowerState _state; private ParticleAcceleratorPowerState _state;
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (collidedWith.TryGetComponent<SingularityComponent>(out var singularityComponent)) if (otherBody.Entity.TryGetComponent<SingularityComponent>(out var singularityComponent))
{ {
var multiplier = _state switch var multiplier = _state switch
{ {
@@ -30,8 +32,8 @@ namespace Content.Server.GameObjects.Components.PA
}; };
singularityComponent.Energy += 10 * multiplier; singularityComponent.Energy += 10 * multiplier;
Owner.Delete(); Owner.Delete();
}else if (collidedWith.TryGetComponent<SingularityGeneratorComponent>(out var singularityGeneratorComponent) }
) else if (otherBody.Entity.TryGetComponent<SingularityGeneratorComponent>(out var singularityGeneratorComponent))
{ {
singularityGeneratorComponent.Power += _state switch singularityGeneratorComponent.Power += _state switch
{ {
@@ -55,7 +57,7 @@ namespace Content.Server.GameObjects.Components.PA
Logger.Error("ParticleProjectile tried firing, but it was spawned without a CollidableComponent"); Logger.Error("ParticleProjectile tried firing, but it was spawned without a CollidableComponent");
return; return;
} }
physicsComponent.Status = BodyStatus.InAir; physicsComponent.BodyStatus = BodyStatus.InAir;
if (!Owner.TryGetComponent<ProjectileComponent>(out var projectileComponent)) if (!Owner.TryGetComponent<ProjectileComponent>(out var projectileComponent))
{ {
@@ -81,7 +83,6 @@ namespace Content.Server.GameObjects.Components.PA
spriteComponent.LayerSetState(0, $"particle{suffix}"); spriteComponent.LayerSetState(0, $"particle{suffix}");
physicsComponent physicsComponent
.EnsureController<BulletController>()
.LinearVelocity = angle.ToWorldVec() * 20f; .LinearVelocity = angle.ToWorldVec() * 20f;
Owner.Transform.LocalRotation = angle; Owner.Transform.LocalRotation = angle;

View File

@@ -6,12 +6,15 @@ using Content.Shared.GameObjects.Components.Tag;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Portal namespace Content.Server.GameObjects.Components.Portal
{ {
[RegisterComponent] [RegisterComponent]
public class PortalComponent : SharedPortalComponent, ICollideBehavior public class PortalComponent : SharedPortalComponent, IStartCollide
{ {
// Potential improvements: Different sounds, // Potential improvements: Different sounds,
// Add Gateways // Add Gateways
@@ -152,11 +155,11 @@ namespace Content.Server.GameObjects.Components.Portal
StartCooldown(); StartCooldown();
} }
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (_onCooldown == false) if (_onCooldown == false)
{ {
TryPortalEntity(collidedWith); TryPortalEntity(otherBody.Entity);
} }
} }
} }

View File

@@ -9,6 +9,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -95,7 +96,7 @@ namespace Content.Server.GameObjects.Components.Portal
{ {
// Added this component to avoid stacking portals and causing shenanigans // Added this component to avoid stacking portals and causing shenanigans
// TODO: Doesn't do a great job of stopping stacking portals for directed // TODO: Doesn't do a great job of stopping stacking portals for directed
if (entity.HasComponent<IPhysicsComponent>() || entity.HasComponent<TeleporterComponent>()) if (entity.HasComponent<IPhysBody>() || entity.HasComponent<TeleporterComponent>())
{ {
return; return;
} }
@@ -139,7 +140,7 @@ namespace Content.Server.GameObjects.Components.Portal
// TODO: Check the user's spot? Upside is no stacking TPs but downside is they can't unstuck themselves from walls. // TODO: Check the user's spot? Upside is no stacking TPs but downside is they can't unstuck themselves from walls.
foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(user.Transform.MapID, target)) foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(user.Transform.MapID, target))
{ {
if (entity.HasComponent<IPhysicsComponent>() || entity.HasComponent<PortalComponent>()) if (entity.HasComponent<IPhysBody>() || entity.HasComponent<PortalComponent>())
{ {
return false; return false;
} }

View File

@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -23,7 +24,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents
{ {
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!; [Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
[ViewVariables] [ComponentDependency] private readonly IPhysicsComponent? _physicsComponent = null; [ViewVariables] [ComponentDependency] private readonly IPhysBody? _physicsComponent = null;
public override string Name => "PowerReceiver"; public override string Name => "PowerReceiver";
@@ -53,7 +54,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents
/// </summary> /// </summary>
public bool Connectable => Anchored; public bool Connectable => Anchored;
private bool Anchored => _physicsComponent == null || _physicsComponent.Anchored; private bool Anchored => _physicsComponent == null || _physicsComponent.BodyType == BodyType.Static;
[ViewVariables] [ViewVariables]
public bool NeedsProvider { get; private set; } = true; public bool NeedsProvider { get; private set; } = true;

View File

@@ -7,11 +7,13 @@ using Robust.Shared.ViewVariables;
using System; using System;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Projectiles namespace Content.Server.GameObjects.Components.Projectiles
{ {
[RegisterComponent] [RegisterComponent]
public class ChemicalInjectionProjectileComponent : Component, ICollideBehavior public class ChemicalInjectionProjectileComponent : Component, IStartCollide
{ {
public override string Name => "ChemicalInjectionProjectile"; public override string Name => "ChemicalInjectionProjectile";
@@ -33,9 +35,9 @@ namespace Content.Server.GameObjects.Components.Projectiles
_solutionContainer = Owner.EnsureComponent<SolutionContainerComponent>(); _solutionContainer = Owner.EnsureComponent<SolutionContainerComponent>();
} }
void ICollideBehavior.CollideWith(IEntity entity) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (!entity.TryGetComponent<BloodstreamComponent>(out var bloodstream)) if (!otherBody.Entity.TryGetComponent<BloodstreamComponent>(out var bloodstream))
return; return;
var solution = _solutionContainer.Solution; var solution = _solutionContainer.Solution;

View File

@@ -1,10 +1,12 @@
using Content.Server.GameObjects.Components.Explosion; using Content.Server.GameObjects.Components.Explosion;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Projectiles namespace Content.Server.GameObjects.Components.Projectiles
{ {
[RegisterComponent] [RegisterComponent]
public class ExplosiveProjectileComponent : Component, ICollideBehavior public class ExplosiveProjectileComponent : Component, IStartCollide
{ {
public override string Name => "ExplosiveProjectile"; public override string Name => "ExplosiveProjectile";
@@ -15,18 +17,12 @@ namespace Content.Server.GameObjects.Components.Projectiles
Owner.EnsureComponent<ExplosiveComponent>(); Owner.EnsureComponent<ExplosiveComponent>();
} }
void ICollideBehavior.CollideWith(IEntity entity) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (Owner.TryGetComponent(out ExplosiveComponent explosive)) if (Owner.TryGetComponent(out ExplosiveComponent explosive))
{ {
explosive.Explosion(); explosive.Explosion();
} }
} }
// Projectile should handle the deleting
void ICollideBehavior.PostCollide(int collisionCount)
{
return;
}
} }
} }

View File

@@ -1,6 +1,9 @@
using Content.Server.GameObjects.Components.Weapon; using Content.Server.GameObjects.Components.Weapon;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Projectiles namespace Content.Server.GameObjects.Components.Projectiles
{ {
@@ -8,7 +11,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
/// Upon colliding with an object this will flash in an area around it /// Upon colliding with an object this will flash in an area around it
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public class FlashProjectileComponent : Component, ICollideBehavior public class FlashProjectileComponent : Component, IStartCollide
{ {
public override string Name => "FlashProjectile"; public override string Name => "FlashProjectile";
@@ -26,20 +29,15 @@ namespace Content.Server.GameObjects.Components.Projectiles
Owner.EnsureComponent<ProjectileComponent>(); Owner.EnsureComponent<ProjectileComponent>();
} }
void ICollideBehavior.CollideWith(IEntity entity) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (_flashed) if (_flashed)
{ {
return; return;
} }
FlashableComponent.FlashAreaHelper(Owner, _range, _duration); FlashableComponent.FlashAreaHelper(Owner, _range, _duration);
_flashed = true; _flashed = true;
} }
// Projectile should handle the deleting
void ICollideBehavior.PostCollide(int collisionCount)
{
return;
}
} }
} }

View File

@@ -6,6 +6,9 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;

View File

@@ -5,6 +5,8 @@ using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Projectiles; using Content.Shared.GameObjects.Components.Projectiles;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -13,7 +15,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Projectiles namespace Content.Server.GameObjects.Components.Projectiles
{ {
[RegisterComponent] [RegisterComponent]
public class ProjectileComponent : SharedProjectileComponent, ICollideBehavior public class ProjectileComponent : SharedProjectileComponent, IStartCollide
{ {
protected override EntityUid Shooter => _shooter; protected override EntityUid Shooter => _shooter;
@@ -52,39 +54,27 @@ namespace Content.Server.GameObjects.Components.Projectiles
Dirty(); Dirty();
} }
private bool _internalDeleteOnCollide;
/// <summary> /// <summary>
/// Applies the damage when our projectile collides with its victim /// Applies the damage when our projectile collides with its victim
/// </summary> /// </summary>
/// <param name="entity"></param> void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
void ICollideBehavior.CollideWith(IEntity entity)
{ {
if (_damagedEntity)
{
return;
}
// This is so entities that shouldn't get a collision are ignored. // This is so entities that shouldn't get a collision are ignored.
if (entity.TryGetComponent(out IPhysicsComponent otherPhysics) && otherPhysics.Hard == false) if (!otherBody.Hard || _damagedEntity)
{ {
_internalDeleteOnCollide = false;
return; return;
} }
else
if (otherBody.Entity.TryGetComponent(out IDamageableComponent damage) && _soundHitSpecies != null)
{ {
_internalDeleteOnCollide = true; EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundHitSpecies, otherBody.Entity.Transform.Coordinates);
}
else if (_soundHit != null)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundHit, otherBody.Entity.Transform.Coordinates);
} }
if (_soundHitSpecies != null && entity.HasComponent<IDamageableComponent>()) if (damage != null)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundHitSpecies, entity.Transform.Coordinates);
} else if (_soundHit != null)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundHit, entity.Transform.Coordinates);
}
if (entity.TryGetComponent(out IDamageableComponent damage))
{ {
Owner.EntityManager.TryGetEntity(_shooter, out var shooter); Owner.EntityManager.TryGetEntity(_shooter, out var shooter);
@@ -96,17 +86,14 @@ namespace Content.Server.GameObjects.Components.Projectiles
_damagedEntity = true; _damagedEntity = true;
} }
if (!entity.Deleted && entity.TryGetComponent(out CameraRecoilComponent recoilComponent) // Damaging it can delete it
&& Owner.TryGetComponent(out IPhysicsComponent ownPhysics)) if (!otherBody.Entity.Deleted && otherBody.Entity.TryGetComponent(out CameraRecoilComponent recoilComponent))
{ {
var direction = ownPhysics.LinearVelocity.Normalized; var direction = ourBody.LinearVelocity.Normalized;
recoilComponent.Kick(direction); recoilComponent.Kick(direction);
} }
}
void ICollideBehavior.PostCollide(int collideCount) Owner.Delete();
{
if (collideCount > 0 && DeleteOnCollide && _internalDeleteOnCollide) Owner.Delete();
} }
public override ComponentState GetComponentState(ICommonSession player) public override ComponentState GetComponentState(ICommonSession player)

View File

@@ -1,6 +1,9 @@
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Projectiles namespace Content.Server.GameObjects.Components.Projectiles
{ {
@@ -8,7 +11,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
/// Adds stun when it collides with an entity /// Adds stun when it collides with an entity
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed class StunnableProjectileComponent : Component, ICollideBehavior public sealed class StunnableProjectileComponent : Component, IStartCollide
{ {
public override string Name => "StunnableProjectile"; public override string Name => "StunnableProjectile";
@@ -27,16 +30,14 @@ namespace Content.Server.GameObjects.Components.Projectiles
Owner.EnsureComponentWarn(out ProjectileComponent _); Owner.EnsureComponentWarn(out ProjectileComponent _);
} }
void ICollideBehavior.CollideWith(IEntity entity) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (entity.TryGetComponent(out StunnableComponent stunnableComponent)) if (otherBody.Entity.TryGetComponent(out StunnableComponent stunnableComponent))
{ {
stunnableComponent.Stun(_stunAmount); stunnableComponent.Stun(_stunAmount);
stunnableComponent.Knockdown(_knockdownAmount); stunnableComponent.Knockdown(_knockdownAmount);
stunnableComponent.Slowdown(_slowdownAmount); stunnableComponent.Slowdown(_slowdownAmount);
} }
} }
void ICollideBehavior.PostCollide(int collidedCount) {}
} }
} }

View File

@@ -1,118 +0,0 @@
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.GameObjects;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Projectiles
{
[RegisterComponent]
internal class ThrownItemComponent : ProjectileComponent, ICollideBehavior
{
public const float DefaultThrowTime = 0.25f;
private bool _shouldCollide = true;
private bool _shouldStop = false;
public override string Name => "ThrownItem";
public override uint? NetID => ContentNetIDs.THROWN_ITEM;
/// <summary>
/// User who threw the item.
/// </summary>
public IEntity User { get; set; }
void ICollideBehavior.CollideWith(IEntity entity)
{
if (!_shouldCollide || entity.Deleted) return;
if (entity.TryGetComponent(out PhysicsComponent collid))
{
if (!collid.Hard) // ignore non hard
return;
_shouldStop = true; // hit something hard => stop after this collision
// Raise an event.
EntitySystem.Get<InteractionSystem>().ThrowCollideInteraction(User, Owner, entity, Owner.Transform.Coordinates);
}
// Stop colliding with mobs, this mimics not having enough velocity to do damage
// after impacting the first object.
// For realism this should actually be changed when the velocity of the object is less than a threshold.
// This would allow ricochets off walls, and weird gravity effects from slowing the object.
if (!Owner.Deleted && Owner.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
{
_shouldCollide = false;
}
}
private void StopThrow()
{
if (Deleted)
{
return;
}
if (Owner.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
{
body.PhysicsShapes[0].CollisionMask &= (int) ~CollisionGroup.ThrownItem;
if (body.TryGetController(out ThrownController controller))
{
controller.LinearVelocity = Vector2.Zero;
}
body.Status = BodyStatus.OnGround;
Owner.RemoveComponent<ThrownItemComponent>();
EntitySystem.Get<InteractionSystem>().LandInteraction(User, Owner, Owner.Transform.Coordinates);
}
}
void ICollideBehavior.PostCollide(int collideCount)
{
if (_shouldStop && collideCount > 0)
{
StopThrow();
}
}
public void StartThrow(Vector2 direction, float speed)
{
var comp = Owner.GetComponent<IPhysicsComponent>();
comp.Status = BodyStatus.InAir;
var controller = comp.EnsureController<ThrownController>();
controller.Push(direction, speed);
EntitySystem.Get<AudioSystem>()
.PlayFromEntity("/Audio/Effects/toss.ogg", Owner);
StartStopTimer();
}
private void StartStopTimer()
{
Owner.SpawnTimer((int) (DefaultThrowTime * 1000), MaybeStopThrow);
}
private void MaybeStopThrow()
{
if (Deleted)
{
return;
}
if (IoCManager.Resolve<IPhysicsManager>().IsWeightless(Owner.Transform.Coordinates))
{
StartStopTimer();
return;
}
StopThrow();
}
}
}

View File

@@ -5,6 +5,7 @@ using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Pulling namespace Content.Server.GameObjects.Components.Pulling
{ {
@@ -31,15 +32,15 @@ namespace Content.Server.GameObjects.Components.Pulling
} }
if (!user.HasComponent<ISharedHandsComponent>() || if (!user.HasComponent<ISharedHandsComponent>() ||
!user.TryGetComponent(out IPhysicsComponent? userPhysics) || !user.TryGetComponent(out IPhysBody? userPhysics) ||
!component.Owner.TryGetComponent(out IPhysicsComponent? targetPhysics) || !component.Owner.TryGetComponent(out IPhysBody? targetPhysics) ||
targetPhysics.Anchored) targetPhysics.BodyType == BodyType.Static)
{ {
return; return;
} }
data.Visibility = VerbVisibility.Visible; data.Visibility = VerbVisibility.Visible;
data.Text = component.Puller == userPhysics data.Text = component.Puller == userPhysics.Entity
? Loc.GetString("Stop pulling") ? Loc.GetString("Stop pulling")
: Loc.GetString("Pull"); : Loc.GetString("Pull");
} }

View File

@@ -20,6 +20,8 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -28,11 +30,11 @@ namespace Content.Server.GameObjects.Components.Recycling
{ {
// TODO: Add sound and safe beep // TODO: Add sound and safe beep
[RegisterComponent] [RegisterComponent]
public class RecyclerComponent : Component, ICollideBehavior, ISuicideAct public class RecyclerComponent : Component, IStartCollide, ISuicideAct
{ {
public override string Name => "Recycler"; public override string Name => "Recycler";
private readonly List<IEntity> _intersecting = new(); public List<IEntity> Intersecting { get; set; } = new();
/// <summary> /// <summary>
/// Whether or not sentient beings will be recycled /// Whether or not sentient beings will be recycled
@@ -74,9 +76,9 @@ namespace Content.Server.GameObjects.Components.Recycling
private void Recycle(IEntity entity) private void Recycle(IEntity entity)
{ {
if (!_intersecting.Contains(entity)) if (!Intersecting.Contains(entity))
{ {
_intersecting.Add(entity); Intersecting.Add(entity);
} }
// TODO: Prevent collision with recycled items // TODO: Prevent collision with recycled items
@@ -95,7 +97,7 @@ namespace Content.Server.GameObjects.Components.Recycling
recyclable.Recycle(_efficiency); recyclable.Recycle(_efficiency);
} }
private bool CanRun() public bool CanRun()
{ {
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) && if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) &&
!receiver.Powered) !receiver.Powered)
@@ -111,15 +113,15 @@ namespace Content.Server.GameObjects.Components.Recycling
return true; return true;
} }
private bool CanMove(IEntity entity) public bool CanMove(IEntity entity)
{ {
if (entity == Owner) if (entity == Owner)
{ {
return false; return false;
} }
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
physics.Anchored) physics.BodyType == BodyType.Static)
{ {
return false; return false;
} }
@@ -142,37 +144,9 @@ namespace Content.Server.GameObjects.Components.Recycling
return true; return true;
} }
public void Update(float frameTime) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (!CanRun()) Recycle(otherBody.Entity);
{
_intersecting.Clear();
return;
}
var direction = Vector2.UnitX;
for (var i = _intersecting.Count - 1; i >= 0; i--)
{
var entity = _intersecting[i];
if (entity.Deleted || !CanMove(entity) || !Owner.EntityManager.IsIntersecting(Owner, entity))
{
_intersecting.RemoveAt(i);
continue;
}
if (entity.TryGetComponent(out IPhysicsComponent? physics))
{
var controller = physics.EnsureController<ConveyedController>();
controller.Move(direction, frameTime, entity.Transform.WorldPosition - Owner.Transform.WorldPosition);
}
}
}
void ICollideBehavior.CollideWith(IEntity collidedWith)
{
Recycle(collidedWith);
} }
SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat) SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat)

View File

@@ -5,6 +5,7 @@ using Content.Shared.Interfaces;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -23,8 +24,8 @@ namespace Content.Server.GameObjects.Components.Rotatable
private void TryFlip(IEntity user) private void TryFlip(IEntity user)
{ {
if (Owner.TryGetComponent(out IPhysicsComponent? physics) && if (Owner.TryGetComponent(out IPhysBody? physics) &&
physics.Anchored) physics.BodyType == BodyType.Static)
{ {
Owner.PopupMessage(user, Loc.GetString("It's stuck.")); Owner.PopupMessage(user, Loc.GetString("It's stuck."));
return; return;

View File

@@ -5,6 +5,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -25,9 +26,9 @@ namespace Content.Server.GameObjects.Components.Rotatable
private void TryRotate(IEntity user, Angle angle) private void TryRotate(IEntity user, Angle angle)
{ {
if (!RotateWhileAnchored && Owner.TryGetComponent(out IPhysicsComponent physics)) if (!RotateWhileAnchored && Owner.TryGetComponent(out IPhysBody physics))
{ {
if (physics.Anchored) if (physics.BodyType == BodyType.Static)
{ {
Owner.PopupMessage(user, Loc.GetString("It's stuck.")); Owner.PopupMessage(user, Loc.GetString("It's stuck."));
return; return;
@@ -42,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{ {
protected override void GetData(IEntity user, RotatableComponent component, VerbData data) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored)) if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysBody physics) && physics.BodyType == BodyType.Static))
{ {
data.Visibility = VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return; return;
@@ -64,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{ {
protected override void GetData(IEntity user, RotatableComponent component, VerbData data) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored)) if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysBody physics) && physics.BodyType == BodyType.Static))
{ {
data.Visibility = VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return; return;

View File

@@ -1,15 +1,17 @@
#nullable enable #nullable enable
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Singularity namespace Content.Server.GameObjects.Components.Singularity
{ {
[RegisterComponent] [RegisterComponent]
public class ContainmentFieldComponent : Component, ICollideBehavior public class ContainmentFieldComponent : Component, IStartCollide
{ {
public override string Name => "ContainmentField"; public override string Name => "ContainmentField";
public ContainmentFieldConnection? Parent; public ContainmentFieldConnection? Parent;
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if (Parent == null) if (Parent == null)
{ {
@@ -17,7 +19,7 @@ namespace Content.Server.GameObjects.Components.Singularity
return; return;
} }
Parent.TryRepell(Owner, collidedWith); Parent.TryRepell(Owner, otherBody.Entity);
} }
} }
} }

View File

@@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Content.Shared.Physics;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Timer = Robust.Shared.Timing.Timer; using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.GameObjects.Components.Singularity namespace Content.Server.GameObjects.Components.Singularity
@@ -90,10 +90,12 @@ namespace Content.Server.GameObjects.Components.Singularity
/// <param name="toRepell">Entity to repell.</param> /// <param name="toRepell">Entity to repell.</param>
public void TryRepell(IEntity repellFrom, IEntity toRepell) public void TryRepell(IEntity repellFrom, IEntity toRepell)
{ {
if (!_fields.Contains(repellFrom) || !toRepell.TryGetComponent<IPhysicsComponent>(out var collidableComponent)) return; // TODO: Fix this also it's fucking repel
if (!_fields.Contains(repellFrom) || !toRepell.TryGetComponent<IPhysBody>(out var collidableComponent)) return;
return;
var speed = 5; var speed = 5;
var containmentFieldRepellController = collidableComponent.EnsureController<ContainmentFieldRepellController>(); //var containmentFieldRepellController = collidableComponent.EnsureController<ContainmentFieldRepellController>();
if (!CanRepell(toRepell)) if (!CanRepell(toRepell))
{ {
@@ -106,22 +108,22 @@ namespace Content.Server.GameObjects.Components.Singularity
{ {
if (repellFrom.Transform.WorldPosition.X.CompareTo(toRepell.Transform.WorldPosition.X) > 0) if (repellFrom.Transform.WorldPosition.X.CompareTo(toRepell.Transform.WorldPosition.X) > 0)
{ {
containmentFieldRepellController.Repell(Direction.West, speed); //containmentFieldRepellController.Repell(Direction.West, speed);
} }
else else
{ {
containmentFieldRepellController.Repell(Direction.East, speed); //containmentFieldRepellController.Repell(Direction.East, speed);
} }
} }
else else
{ {
if (repellFrom.Transform.WorldPosition.Y.CompareTo(toRepell.Transform.WorldPosition.Y) > 0) if (repellFrom.Transform.WorldPosition.Y.CompareTo(toRepell.Transform.WorldPosition.Y) > 0)
{ {
containmentFieldRepellController.Repell(Direction.South, speed); //containmentFieldRepellController.Repell(Direction.South, speed);
} }
else else
{ {
containmentFieldRepellController.Repell(Direction.North, speed); //containmentFieldRepellController.Repell(Direction.North, speed);
} }
} }

View File

@@ -10,13 +10,15 @@ using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Singularity namespace Content.Server.GameObjects.Components.Singularity
{ {
[RegisterComponent] [RegisterComponent]
public class ContainmentFieldGeneratorComponent : Component, ICollideBehavior public class ContainmentFieldGeneratorComponent : Component, IStartCollide
{ {
[Dependency] private readonly IPhysicsManager _physicsManager = null!; [Dependency] private readonly IPhysicsManager _physicsManager = null!;
@@ -117,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Singularity
var dirVec = direction.ToVec(); var dirVec = direction.ToVec();
var ray = new CollisionRay(Owner.Transform.WorldPosition, dirVec, (int) CollisionGroup.MobMask); var ray = new CollisionRay(Owner.Transform.WorldPosition, dirVec, (int) CollisionGroup.MobMask);
var rawRayCastResults = _physicsManager.IntersectRay(Owner.Transform.MapID, ray, 4.5f, Owner, false); var rawRayCastResults = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(Owner.Transform.MapID, ray, 4.5f, Owner, false);
var rayCastResults = rawRayCastResults as RayCastResults[] ?? rawRayCastResults.ToArray(); var rayCastResults = rawRayCastResults as RayCastResults[] ?? rawRayCastResults.ToArray();
if(!rayCastResults.Any()) continue; if(!rayCastResults.Any()) continue;
@@ -182,9 +184,9 @@ namespace Content.Server.GameObjects.Components.Singularity
} }
} }
public void CollideWith(IEntity collidedWith) void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{ {
if(collidedWith.HasComponent<EmitterBoltComponent>()) if (otherBody.Entity.HasComponent<EmitterBoltComponent>())
{ {
ReceivePower(4); ReceivePower(4);
} }

View File

@@ -226,7 +226,7 @@ namespace Content.Server.GameObjects.Components.Singularity
return; return;
} }
physicsComponent.Status = BodyStatus.InAir; physicsComponent.BodyStatus = BodyStatus.InAir;
if (!projectile.TryGetComponent<ProjectileComponent>(out var projectileComponent)) if (!projectile.TryGetComponent<ProjectileComponent>(out var projectileComponent))
{ {
@@ -237,7 +237,6 @@ namespace Content.Server.GameObjects.Components.Singularity
projectileComponent.IgnoreEntity(Owner); projectileComponent.IgnoreEntity(Owner);
physicsComponent physicsComponent
.EnsureController<BulletController>()
.LinearVelocity = Owner.Transform.WorldRotation.ToWorldVec() * 20f; .LinearVelocity = Owner.Transform.WorldRotation.ToWorldVec() * 20f;
projectile.Transform.WorldRotation = Owner.Transform.WorldRotation; projectile.Transform.WorldRotation = Owner.Transform.WorldRotation;

View File

@@ -14,13 +14,15 @@ using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Dynamics.Shapes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.Singularity namespace Content.Server.GameObjects.Components.Singularity
{ {
[RegisterComponent] [RegisterComponent]
public class SingularityComponent : Component, ICollideBehavior public class SingularityComponent : Component, IStartCollide
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
@@ -38,7 +40,6 @@ namespace Content.Server.GameObjects.Components.Singularity
_energy = value; _energy = value;
if (_energy <= 0) if (_energy <= 0)
{ {
if(_singularityController != null) _singularityController.LinearVelocity = Vector2.Zero;
_spriteComponent?.LayerSetVisible(0, false); _spriteComponent?.LayerSetVisible(0, false);
Owner.Delete(); Owner.Delete();
@@ -75,7 +76,7 @@ namespace Content.Server.GameObjects.Components.Singularity
_spriteComponent?.LayerSetRSI(0, "Constructible/Power/Singularity/singularity_" + _level + ".rsi"); _spriteComponent?.LayerSetRSI(0, "Constructible/Power/Singularity/singularity_" + _level + ".rsi");
_spriteComponent?.LayerSetState(0, "singularity_" + _level); _spriteComponent?.LayerSetState(0, "singularity_" + _level);
if(_collidableComponent != null && _collidableComponent.PhysicsShapes.Any() && _collidableComponent.PhysicsShapes[0] is PhysShapeCircle circle) if(_collidableComponent != null && _collidableComponent.Fixtures.Any() && _collidableComponent.Fixtures[0].Shape is PhysShapeCircle circle)
{ {
circle.Radius = _level - 0.5f; circle.Radius = _level - 0.5f;
} }
@@ -95,7 +96,6 @@ namespace Content.Server.GameObjects.Components.Singularity
_ => 0 _ => 0
}; };
private SingularityController? _singularityController;
private PhysicsComponent? _collidableComponent; private PhysicsComponent? _collidableComponent;
private SpriteComponent? _spriteComponent; private SpriteComponent? _spriteComponent;
private RadiationPulseComponent? _radiationPulseComponent; private RadiationPulseComponent? _radiationPulseComponent;
@@ -129,9 +129,6 @@ namespace Content.Server.GameObjects.Components.Singularity
Logger.Error("SingularityComponent was spawned without SpriteComponent"); Logger.Error("SingularityComponent was spawned without SpriteComponent");
} }
_singularityController = _collidableComponent?.EnsureController<SingularityController>();
if(_singularityController!=null)_singularityController.ControlledComponent = _collidableComponent;
if (!Owner.TryGetComponent(out _radiationPulseComponent)) if (!Owner.TryGetComponent(out _radiationPulseComponent))
{ {
Logger.Error("SingularityComponent was spawned without RadiationPulseComponent"); Logger.Error("SingularityComponent was spawned without RadiationPulseComponent");
@@ -140,60 +137,18 @@ namespace Content.Server.GameObjects.Components.Singularity
Level = 1; Level = 1;
} }
public void Update() public void Update(int seconds)
{ {
Energy -= EnergyDrain; Energy -= EnergyDrain * seconds;
if(Level == 1) return;
//pushing
var pushVector = new Vector2((_random.Next(-10, 10)), _random.Next(-10, 10));
while (pushVector.X == 0 && pushVector.Y == 0)
{
pushVector = new Vector2((_random.Next(-10, 10)), _random.Next(-10, 10));
}
_singularityController?.Push(pushVector.Normalized, 2);
} }
private readonly List<IEntity> _previousPulledEntities = new(); void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
public void CleanupPulledEntities()
{ {
foreach (var previousPulledEntity in _previousPulledEntities) var otherEntity = otherBody.Entity;
if (otherEntity.TryGetComponent<IMapGridComponent>(out var mapGridComponent))
{ {
if(previousPulledEntity.Deleted) continue; foreach (var tile in mapGridComponent.Grid.GetTilesIntersecting(ourBody.GetWorldAABB()))
if (!previousPulledEntity.TryGetComponent<PhysicsComponent>(out var collidableComponent)) continue;
var controller = collidableComponent.EnsureController<SingularityPullController>();
controller.StopPull();
}
_previousPulledEntities.Clear();
}
public void PullUpdate()
{
CleanupPulledEntities();
var entitiesToPull = Owner.EntityManager.GetEntitiesInRange(Owner.Transform.Coordinates, Level * 10);
foreach (var entity in entitiesToPull)
{
if (!entity.TryGetComponent<PhysicsComponent>(out var collidableComponent)) continue;
if (entity.HasComponent<GhostComponent>()) continue;
var controller = collidableComponent.EnsureController<SingularityPullController>();
if(Owner.Transform.Coordinates.EntityId != entity.Transform.Coordinates.EntityId) continue;
var vec = (Owner.Transform.Coordinates - entity.Transform.Coordinates).Position;
if (vec == Vector2.Zero) continue;
var speed = 10 / vec.Length * Level;
controller.Pull(vec.Normalized, speed);
_previousPulledEntities.Add(entity);
}
}
void ICollideBehavior.CollideWith(IEntity entity)
{
if (_collidableComponent == null) return; //how did it even collide then? :D
if (entity.TryGetComponent<IMapGridComponent>(out var mapGridComponent))
{
foreach (var tile in mapGridComponent.Grid.GetTilesIntersecting(((IPhysBody) _collidableComponent).WorldAABB))
{ {
mapGridComponent.Grid.SetTile(tile.GridIndices, Tile.Empty); mapGridComponent.Grid.SetTile(tile.GridIndices, Tile.Empty);
Energy++; Energy++;
@@ -201,14 +156,14 @@ namespace Content.Server.GameObjects.Components.Singularity
return; return;
} }
if (entity.HasComponent<ContainmentFieldComponent>() || (entity.TryGetComponent<ContainmentFieldGeneratorComponent>(out var component) && component.CanRepell(Owner))) if (otherEntity.HasComponent<ContainmentFieldComponent>() || (otherEntity.TryGetComponent<ContainmentFieldGeneratorComponent>(out var component) && component.CanRepell(Owner)))
{ {
return; return;
} }
if (entity.IsInContainer()) return; if (otherEntity.IsInContainer()) return;
entity.Delete(); otherEntity.Delete();
Energy++; Energy++;
} }
@@ -216,7 +171,6 @@ namespace Content.Server.GameObjects.Components.Singularity
{ {
_playingSound?.Stop(); _playingSound?.Stop();
_audioSystem.PlayAtCoords("/Audio/Effects/singularity_collapse.ogg", Owner.Transform.Coordinates); _audioSystem.PlayAtCoords("/Audio/Effects/singularity_collapse.ogg", Owner.Transform.Coordinates);
CleanupPulledEntities();
base.OnRemove(); base.OnRemove();
} }
} }

View File

@@ -6,6 +6,8 @@ using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Temperature namespace Content.Server.GameObjects.Components.Temperature
@@ -29,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Temperature
[ViewVariables] public float HeatCapacity { [ViewVariables] public float HeatCapacity {
get get
{ {
if (Owner.TryGetComponent<IPhysicsComponent>(out var physics)) if (Owner.TryGetComponent<IPhysBody>(out var physics))
{ {
return SpecificHeat * physics.Mass; return SpecificHeat * physics.Mass;
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using Content.Shared.GameObjects.Components.Weapons; using Content.Shared.GameObjects.Components.Weapons;
using Content.Shared.Physics;
using Content.Shared.Utility; using Content.Shared.Utility;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -31,18 +32,17 @@ namespace Content.Server.GameObjects.Components.Weapon
public static void FlashAreaHelper(IEntity source, float range, float duration, string sound = null) public static void FlashAreaHelper(IEntity source, float range, float duration, string sound = null)
{ {
foreach (var entity in IoCManager.Resolve<IEntityManager>().GetEntitiesInRange(source.Transform.Coordinates, range)) foreach (var entity in source.EntityManager.GetEntitiesInRange(source.Transform.Coordinates, range))
{ {
if (!source.InRangeUnobstructed(entity, range, popup: true)) if (!entity.TryGetComponent(out FlashableComponent flashable) ||
continue; !source.InRangeUnobstructed(entity, range, CollisionGroup.Opaque)) continue;
if(entity.TryGetComponent(out FlashableComponent flashable)) flashable.Flash(duration);
flashable.Flash(duration);
} }
if (!string.IsNullOrEmpty(sound)) if (!string.IsNullOrEmpty(sound))
{ {
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().PlayAtCoords(sound, source.Transform.Coordinates); EntitySystem.Get<AudioSystem>().PlayAtCoords(sound, source.Transform.Coordinates);
} }
} }
} }

View File

@@ -13,6 +13,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -191,10 +192,13 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
for (var i = 0; i < increments; i++) for (var i = 0; i < increments; i++)
{ {
var castAngle = new Angle(baseAngle + increment * i); var castAngle = new Angle(baseAngle + increment * i);
var res = _physicsManager.IntersectRay(mapId, new CollisionRay(position, castAngle.ToWorldVec(), (int) (CollisionGroup.Impassable|CollisionGroup.MobImpassable)), Range, ignore).FirstOrDefault(); var res = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(mapId,
if (res.HitEntity != null) new CollisionRay(position, castAngle.ToVec(),
(int) (CollisionGroup.Impassable | CollisionGroup.MobImpassable)), Range, ignore).ToList();
if (res.Count != 0)
{ {
resSet.Add(res.HitEntity); resSet.Add(res[0].HitEntity);
} }
} }

View File

@@ -22,6 +22,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -367,15 +368,14 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
projectileAngle = angle; projectileAngle = angle;
} }
var physics = projectile.GetComponent<IPhysicsComponent>(); var physics = projectile.GetComponent<IPhysBody>();
physics.Status = BodyStatus.InAir; physics.BodyStatus = BodyStatus.InAir;
var projectileComponent = projectile.GetComponent<ProjectileComponent>(); var projectileComponent = projectile.GetComponent<ProjectileComponent>();
projectileComponent.IgnoreEntity(shooter); projectileComponent.IgnoreEntity(shooter);
projectile projectile
.GetComponent<IPhysicsComponent>() .GetComponent<IPhysBody>()
.EnsureController<BulletController>()
.LinearVelocity = projectileAngle.ToVec() * velocity; .LinearVelocity = projectileAngle.ToVec() * velocity;
projectile.Transform.LocalRotation = projectileAngle + MathHelper.PiOver2; projectile.Transform.LocalRotation = projectileAngle + MathHelper.PiOver2;
@@ -405,7 +405,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
private void FireHitscan(IEntity shooter, HitscanComponent hitscan, Angle angle) private void FireHitscan(IEntity shooter, HitscanComponent hitscan, Angle angle)
{ {
var ray = new CollisionRay(Owner.Transform.Coordinates.ToMapPos(Owner.EntityManager), angle.ToVec(), (int) hitscan.CollisionMask); var ray = new CollisionRay(Owner.Transform.Coordinates.ToMapPos(Owner.EntityManager), angle.ToVec(), (int) hitscan.CollisionMask);
var physicsManager = IoCManager.Resolve<IPhysicsManager>(); var physicsManager = EntitySystem.Get<SharedBroadPhaseSystem>();
var rayCastResults = physicsManager.IntersectRay(Owner.Transform.MapID, ray, hitscan.MaxLength, shooter, false).ToList(); var rayCastResults = physicsManager.IntersectRay(Owner.Transform.MapID, ray, hitscan.MaxLength, shooter, false).ToList();
if (rayCastResults.Count >= 1) if (rayCastResults.Count >= 1)

View File

@@ -6,10 +6,13 @@ using Content.Shared.AI;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -73,6 +76,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
private readonly List<PathfindingRegion> _queuedCacheDeletions = new(); private readonly List<PathfindingRegion> _queuedCacheDeletions = new();
#if DEBUG #if DEBUG
private HashSet<IPlayerSession> _subscribedSessions = new();
private int _runningCacheIdx = 0; private int _runningCacheIdx = 0;
#endif #endif
@@ -81,7 +85,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_pathfindingSystem = Get<PathfindingSystem>(); _pathfindingSystem = Get<PathfindingSystem>();
SubscribeLocalEvent<PathfindingChunkUpdateMessage>(RecalculateNodeRegions); SubscribeLocalEvent<PathfindingChunkUpdateMessage>(RecalculateNodeRegions);
#if DEBUG #if DEBUG
SubscribeLocalEvent<PlayerAttachSystemMessage>(SendDebugMessage); SubscribeNetworkEvent<SharedAiDebug.SubscribeReachableMessage>(HandleSubscription);
SubscribeNetworkEvent<SharedAiDebug.UnsubscribeReachableMessage>(HandleUnsubscription);
#endif #endif
_mapManager.OnGridRemoved += GridRemoved; _mapManager.OnGridRemoved += GridRemoved;
} }
@@ -99,8 +104,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
GenerateRegions(chunk); GenerateRegions(chunk);
} }
// TODO: Only send diffs instead
#if DEBUG #if DEBUG
if (_queuedUpdates.Count > 0) if (_subscribedSessions.Count > 0 && _queuedUpdates.Count > 0)
{ {
foreach (var (gridId, regs) in _regions) foreach (var (gridId, regs) in _regions)
{ {
@@ -129,8 +135,28 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_cachedAccessible.Clear(); _cachedAccessible.Clear();
_queuedCacheDeletions.Clear(); _queuedCacheDeletions.Clear();
_mapManager.OnGridRemoved -= GridRemoved; _mapManager.OnGridRemoved -= GridRemoved;
UnsubscribeLocalEvent<PathfindingChunkUpdateMessage>();
UnsubscribeLocalEvent<PlayerAttachSystemMessage>();
UnsubscribeNetworkEvent<SharedAiDebug.SubscribeReachableMessage>();
UnsubscribeNetworkEvent<SharedAiDebug.UnsubscribeReachableMessage>();
} }
#if DEBUG
private void HandleSubscription(SharedAiDebug.SubscribeReachableMessage message, EntitySessionEventArgs eventArgs)
{
_subscribedSessions.Add((IPlayerSession) eventArgs.SenderSession);
foreach (var (gridId, _) in _regions)
{
SendRegionsDebugMessage(gridId);
}
}
private void HandleUnsubscription(SharedAiDebug.UnsubscribeReachableMessage message, EntitySessionEventArgs eventArgs)
{
_subscribedSessions.Remove((IPlayerSession) eventArgs.SenderSession);
}
#endif
private void RecalculateNodeRegions(PathfindingChunkUpdateMessage message) private void RecalculateNodeRegions(PathfindingChunkUpdateMessage message)
{ {
// TODO: Only need to do changed nodes ideally // TODO: Only need to do changed nodes ideally
@@ -154,7 +180,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
var targetNode = _pathfindingSystem.GetNode(targetTile); var targetNode = _pathfindingSystem.GetNode(targetTile);
var collisionMask = 0; var collisionMask = 0;
if (entity.TryGetComponent(out IPhysicsComponent physics)) if (entity.TryGetComponent(out IPhysBody physics))
{ {
collisionMask = physics.CollisionMask; collisionMask = physics.CollisionMask;
} }
@@ -681,15 +707,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
} }
#if DEBUG #if DEBUG
private void SendDebugMessage(PlayerAttachSystemMessage message)
{
var playerGrid = message.Entity.Transform.GridID;
if(playerGrid.IsValid())
SendRegionsDebugMessage(playerGrid);
}
private void SendRegionsDebugMessage(GridId gridId) private void SendRegionsDebugMessage(GridId gridId)
{ {
if (_subscribedSessions.Count == 0) return;
var grid = _mapManager.GetGrid(gridId); var grid = _mapManager.GetGrid(gridId);
// Chunk / Regions / Nodes // Chunk / Regions / Nodes
var debugResult = new Dictionary<int, Dictionary<int, List<Vector2>>>(); var debugResult = new Dictionary<int, Dictionary<int, List<Vector2>>>();
@@ -722,17 +742,23 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
chunkIdx++; chunkIdx++;
} }
RaiseNetworkEvent(new SharedAiDebug.ReachableChunkRegionsDebugMessage(gridId, debugResult));
foreach (var session in _subscribedSessions)
{
RaiseNetworkEvent(new SharedAiDebug.ReachableChunkRegionsDebugMessage(gridId, debugResult), session.ConnectedClient);
}
} }
/// <summary> /// <summary>
/// Sent whenever the reachable cache for a particular mob is built or retrieved /// Sent whenever the reachable cache for a particular mob is built or retrieved
/// </summary> /// </summary>
/// <param name="gridId"></param> /// <param name="gridId"></param>
/// <param name="regions"></param> /// <param name="regions"></param>
/// <param name="cached"></param> /// <param name="cached"></param>
private void SendRegionCacheMessage(GridId gridId, IEnumerable<PathfindingRegion> regions, bool cached) private void SendRegionCacheMessage(GridId gridId, IEnumerable<PathfindingRegion> regions, bool cached)
{ {
if (_subscribedSessions.Count == 0) return;
var grid = _mapManager.GetGrid(gridId); var grid = _mapManager.GetGrid(gridId);
var debugResult = new Dictionary<int, List<Vector2>>(); var debugResult = new Dictionary<int, List<Vector2>>();
@@ -750,7 +776,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_runningCacheIdx++; _runningCacheIdx++;
} }
RaiseNetworkEvent(new SharedAiDebug.ReachableCacheDebugMessage(gridId, debugResult, cached)); foreach (var session in _subscribedSessions)
{
RaiseNetworkEvent(new SharedAiDebug.ReachableCacheDebugMessage(gridId, debugResult, cached), session.ConnectedClient);
}
} }
#endif #endif
} }

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.Components.Movement;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{ {
@@ -27,7 +28,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
public static ReachableArgs GetArgs(IEntity entity) public static ReachableArgs GetArgs(IEntity entity)
{ {
var collisionMask = 0; var collisionMask = 0;
if (entity.TryGetComponent(out IPhysicsComponent? physics)) if (entity.TryGetComponent(out IPhysBody? physics))
{ {
collisionMask = physics.CollisionMask; collisionMask = physics.CollisionMask;
} }

View File

@@ -6,6 +6,7 @@ using Content.Server.GameObjects.Components.Doors;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
@@ -40,7 +41,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
GenerateMask(); GenerateMask();
} }
public static bool IsRelevant(IEntity entity, IPhysicsComponent physicsComponent) public static bool IsRelevant(IEntity entity, IPhysBody physicsComponent)
{ {
if (entity.Transform.GridID == GridId.Invalid || if (entity.Transform.GridID == GridId.Invalid ||
(PathfindingSystem.TrackedCollisionLayers & physicsComponent.CollisionLayer) == 0) (PathfindingSystem.TrackedCollisionLayers & physicsComponent.CollisionLayer) == 0)
@@ -256,7 +257,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
/// <param name="entity"></param> /// <param name="entity"></param>
/// TODO: These 2 methods currently don't account for a bunch of changes (e.g. airlock unpowered, wrenching, etc.) /// TODO: These 2 methods currently don't account for a bunch of changes (e.g. airlock unpowered, wrenching, etc.)
/// TODO: Could probably optimise this slightly more. /// TODO: Could probably optimise this slightly more.
public void AddEntity(IEntity entity, IPhysicsComponent physicsComponent) public void AddEntity(IEntity entity, IPhysBody physicsComponent)
{ {
// If we're a door // If we're a door
if (entity.HasComponent<AirlockComponent>() || entity.HasComponent<ServerDoorComponent>()) if (entity.HasComponent<AirlockComponent>() || entity.HasComponent<ServerDoorComponent>())
@@ -275,7 +276,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
DebugTools.Assert((PathfindingSystem.TrackedCollisionLayers & physicsComponent.CollisionLayer) != 0); DebugTools.Assert((PathfindingSystem.TrackedCollisionLayers & physicsComponent.CollisionLayer) != 0);
if (!physicsComponent.Anchored) if (physicsComponent.BodyType == BodyType.Static)
{ {
_physicsLayers.Add(entity, physicsComponent.CollisionLayer); _physicsLayers.Add(entity, physicsComponent.CollisionLayer);
} }

View File

@@ -11,6 +11,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
@@ -29,7 +30,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{ {
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
public IReadOnlyDictionary<GridId, Dictionary<Vector2i, PathfindingChunk>> Graph => _graph; public IReadOnlyDictionary<GridId, Dictionary<Vector2i, PathfindingChunk>> Graph => _graph;
private readonly Dictionary<GridId, Dictionary<Vector2i, PathfindingChunk>> _graph = new(); private readonly Dictionary<GridId, Dictionary<Vector2i, PathfindingChunk>> _graph = new();
@@ -81,7 +82,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
foreach (var update in _collidableUpdateQueue) foreach (var update in _collidableUpdateQueue)
{ {
var entity = EntityManager.GetEntity(update.Owner); if (!EntityManager.TryGetEntity(update.Owner, out var entity)) continue;
if (update.CanCollide) if (update.CanCollide)
{ {
HandleEntityAdd(entity); HandleEntityAdd(entity);
@@ -262,7 +264,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{ {
if (entity.Deleted || if (entity.Deleted ||
_lastKnownPositions.ContainsKey(entity) || _lastKnownPositions.ContainsKey(entity) ||
!entity.TryGetComponent(out IPhysicsComponent physics) || !entity.TryGetComponent(out IPhysBody physics) ||
!PathfindingNode.IsRelevant(entity, physics)) !PathfindingNode.IsRelevant(entity, physics))
{ {
return; return;
@@ -301,7 +303,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{ {
// If we've moved to space or the likes then remove us. // If we've moved to space or the likes then remove us.
if (moveEvent.Sender.Deleted || if (moveEvent.Sender.Deleted ||
!moveEvent.Sender.TryGetComponent(out IPhysicsComponent physics) || !moveEvent.Sender.TryGetComponent(out IPhysBody physics) ||
!PathfindingNode.IsRelevant(moveEvent.Sender, physics) || !PathfindingNode.IsRelevant(moveEvent.Sender, physics) ||
moveEvent.NewPosition.GetGridId(EntityManager) == GridId.Invalid) moveEvent.NewPosition.GetGridId(EntityManager) == GridId.Invalid)
{ {
@@ -366,7 +368,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
public bool CanTraverse(IEntity entity, PathfindingNode node) public bool CanTraverse(IEntity entity, PathfindingNode node)
{ {
if (entity.TryGetComponent(out IPhysicsComponent physics) && if (entity.TryGetComponent(out IPhysBody physics) &&
(physics.CollisionMask & node.BlockedCollisionMask) != 0) (physics.CollisionMask & node.BlockedCollisionMask) != 0)
{ {
return false; return false;

View File

@@ -13,6 +13,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -413,7 +414,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
var startTile = gridManager.GetTileRef(entity.Transform.Coordinates); var startTile = gridManager.GetTileRef(entity.Transform.Coordinates);
var endTile = gridManager.GetTileRef(steeringRequest.TargetGrid); var endTile = gridManager.GetTileRef(steeringRequest.TargetGrid);
var collisionMask = 0; var collisionMask = 0;
if (entity.TryGetComponent(out IPhysicsComponent physics)) if (entity.TryGetComponent(out IPhysBody physics))
{ {
collisionMask = physics.CollisionMask; collisionMask = physics.CollisionMask;
} }
@@ -599,7 +600,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
return Vector2.Zero; return Vector2.Zero;
} }
if (target.TryGetComponent(out IPhysicsComponent physics)) if (target.TryGetComponent(out IPhysBody physics))
{ {
var targetDistance = (targetPos.Position - entityPos.Position); var targetDistance = (targetPos.Position - entityPos.Position);
targetPos = targetPos.Offset(physics.LinearVelocity * targetDistance); targetPos = targetPos.Offset(physics.LinearVelocity * targetDistance);
@@ -617,7 +618,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
/// <returns></returns> /// <returns></returns>
private Vector2 CollisionAvoidance(IEntity entity, Vector2 direction, ICollection<IEntity> ignoredTargets) private Vector2 CollisionAvoidance(IEntity entity, Vector2 direction, ICollection<IEntity> ignoredTargets)
{ {
if (direction == Vector2.Zero || !entity.TryGetComponent(out IPhysicsComponent physics)) if (direction == Vector2.Zero || !entity.TryGetComponent(out IPhysBody physics))
{ {
return Vector2.Zero; return Vector2.Zero;
} }
@@ -658,7 +659,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
// if we're moving in the same direction then ignore // if we're moving in the same direction then ignore
// So if 2 entities are moving towards each other and both detect a collision they'll both move in the same direction // So if 2 entities are moving towards each other and both detect a collision they'll both move in the same direction
// i.e. towards the right // i.e. towards the right
if (physicsEntity.TryGetComponent(out IPhysicsComponent otherPhysics) && if (physicsEntity.TryGetComponent(out IPhysBody otherPhysics) &&
Vector2.Dot(otherPhysics.LinearVelocity, direction) > 0) Vector2.Dot(otherPhysics.LinearVelocity, direction) > 0)
{ {
continue; continue;

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
@@ -25,6 +26,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players; using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems.Click namespace Content.Server.GameObjects.EntitySystems.Click
@@ -568,61 +570,12 @@ namespace Content.Server.GameObjects.EntitySystems.Click
} }
var comps = thrown.GetAllComponents<IThrown>().ToList(); var comps = thrown.GetAllComponents<IThrown>().ToList();
var args = new ThrownEventArgs(user);
// Call Thrown on all components that implement the interface // Call Thrown on all components that implement the interface
foreach (var comp in comps) foreach (var comp in comps)
{ {
comp.Thrown(new ThrownEventArgs(user)); comp.Thrown(args);
}
}
/// <summary>
/// Calls Land on all components that implement the ILand interface
/// on an entity that has landed after being thrown.
/// </summary>
public void LandInteraction(IEntity user, IEntity landing, EntityCoordinates landLocation)
{
var landMsg = new LandMessage(user, landing, landLocation);
RaiseLocalEvent(landMsg);
if (landMsg.Handled)
{
return;
}
var comps = landing.GetAllComponents<ILand>().ToList();
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
comp.Land(new LandEventArgs(user, landLocation));
}
}
/// <summary>
/// Calls ThrowCollide on all components that implement the IThrowCollide interface
/// on a thrown entity and the target entity it hit.
/// </summary>
public void ThrowCollideInteraction(IEntity user, IEntity thrown, IEntity target, EntityCoordinates location)
{
var collideMsg = new ThrowCollideMessage(user, thrown, target, location);
RaiseLocalEvent(collideMsg);
if (collideMsg.Handled)
{
return;
}
var eventArgs = new ThrowCollideEventArgs(user, thrown, target, location);
foreach (var comp in thrown.GetAllComponents<IThrowCollide>().ToArray())
{
if (thrown.Deleted) break;
comp.DoHit(eventArgs);
}
foreach (var comp in target.GetAllComponents<IThrowCollide>().ToArray())
{
if (target.Deleted) break;
comp.HitBy(eventArgs);
} }
} }

View File

@@ -1,18 +1,39 @@
using Content.Server.GameObjects.Components.Movement; using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class ClimbSystem : EntitySystem internal sealed class ClimbSystem : EntitySystem, IResettingEntitySystem
{ {
private readonly HashSet<ClimbingComponent> _activeClimbers = new();
public void AddActiveClimber(ClimbingComponent climbingComponent)
{
_activeClimbers.Add(climbingComponent);
}
public void RemoveActiveClimber(ClimbingComponent climbingComponent)
{
_activeClimbers.Remove(climbingComponent);
}
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
foreach (var comp in ComponentManager.EntityQuery<ClimbingComponent>(true)) foreach (var climber in _activeClimbers.ToArray())
{ {
comp.Update(); climber.Update();
} }
} }
public void Reset()
{
_activeClimbers.Clear();
}
} }
} }

View File

@@ -1,18 +0,0 @@
using Content.Server.GameObjects.Components.Conveyor;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class ConveyorSystem : EntitySystem
{
public override void Update(float frameTime)
{
foreach (var comp in ComponentManager.EntityQuery<ConveyorComponent>(true))
{
comp.Update(frameTime);
}
}
}
}

View File

@@ -1,11 +1,11 @@
using System; using System;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
@@ -145,12 +145,12 @@ namespace Content.Server.GameObjects.EntitySystems
private bool HandleThrowItem(ICommonSession session, EntityCoordinates coords, EntityUid uid) private bool HandleThrowItem(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{ {
var plyEnt = ((IPlayerSession)session).AttachedEntity; var playerEnt = ((IPlayerSession)session).AttachedEntity;
if (plyEnt == null || !plyEnt.IsValid()) if (playerEnt == null || !playerEnt.IsValid())
return false; return false;
if (!plyEnt.TryGetComponent(out HandsComponent handsComp)) if (!playerEnt.TryGetComponent(out HandsComponent handsComp))
return false; return false;
if (!handsComp.CanDrop(handsComp.ActiveHand)) if (!handsComp.CanDrop(handsComp.ActiveHand))
@@ -169,14 +169,19 @@ namespace Content.Server.GameObjects.EntitySystems
else else
{ {
stackComp.Use(1); stackComp.Use(1);
throwEnt = throwEnt.EntityManager.SpawnEntity(throwEnt.Prototype.ID, plyEnt.Transform.Coordinates); throwEnt = throwEnt.EntityManager.SpawnEntity(throwEnt.Prototype.ID, playerEnt.Transform.Coordinates);
// can only throw one item at a time, regardless of what the prototype stack size is. // can only throw one item at a time, regardless of what the prototype stack size is.
if (throwEnt.TryGetComponent<StackComponent>(out var newStackComp)) if (throwEnt.TryGetComponent<StackComponent>(out var newStackComp))
newStackComp.Count = 1; newStackComp.Count = 1;
} }
throwEnt.ThrowTo(ThrowForce, coords, plyEnt.Transform.Coordinates, false, plyEnt); var direction = coords.ToMapPos(EntityManager) - playerEnt.Transform.WorldPosition;
if (direction == Vector2.Zero) return true;
direction = direction.Normalized * MathF.Min(direction.Length, 8.0f);
throwEnt.TryThrow(direction * ThrowForce * 15, playerEnt);
return true; return true;
} }

View File

@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -137,7 +138,7 @@ namespace Content.Server.GameObjects.EntitySystems
// Determine if the solar panel is occluded, and zero out coverage if so. // Determine if the solar panel is occluded, and zero out coverage if so.
// FIXME: The "Opaque" collision group doesn't seem to work right now. // FIXME: The "Opaque" collision group doesn't seem to work right now.
var ray = new CollisionRay(entity.Transform.WorldPosition, TowardsSun.ToVec(), (int) CollisionGroup.Opaque); var ray = new CollisionRay(entity.Transform.WorldPosition, TowardsSun.ToVec(), (int) CollisionGroup.Opaque);
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(entity.Transform.MapID, ray, SunOcclusionCheckDistance, entity); var rayCastResults = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(entity.Transform.MapID, ray, SunOcclusionCheckDistance, entity);
if (rayCastResults.Any()) if (rayCastResults.Any())
coverage = 0; coverage = 0;
} }

View File

@@ -1,18 +0,0 @@
using Content.Server.GameObjects.Components.Recycling;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class RecyclerSystem : EntitySystem
{
public override void Update(float frameTime)
{
foreach (var component in ComponentManager.EntityQuery<RecyclerComponent>(true))
{
component.Update(frameTime);
}
}
}
}

View File

@@ -7,36 +7,21 @@ namespace Content.Server.GameObjects.EntitySystems
[UsedImplicitly] [UsedImplicitly]
public class SingularitySystem : EntitySystem public class SingularitySystem : EntitySystem
{ {
private float curTimeSingulo; private float _accumulator;
private float curTimePull;
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
curTimeSingulo += frameTime; _accumulator += frameTime;
curTimePull += frameTime;
var shouldUpdate = curTimeSingulo >= 1f; while (_accumulator > 1.0f)
var shouldPull = curTimePull >= 0.2f;
if (!shouldUpdate && !shouldPull) return;
var singulos = ComponentManager.EntityQuery<SingularityComponent>(true);
if (curTimeSingulo >= 1f)
{ {
curTimeSingulo -= 1f; _accumulator -= 1.0f;
foreach (var singulo in singulos)
{
singulo.Update();
}
}
if (curTimePull >= 0.5f) foreach (var singularity in ComponentManager.EntityQuery<SingularityComponent>())
{
curTimePull -= 0.5f;
foreach (var singulo in singulos)
{ {
singulo.PullUpdate(); singularity.Update(1);
} }
} }
} }

View File

@@ -0,0 +1,118 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Conveyor;
using Content.Server.GameObjects.Components.Recycling;
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Controllers;
namespace Content.Server.Physics.Controllers
{
internal sealed class ConveyorController : VirtualController
{
public override List<Type> UpdatesAfter => new() {typeof(MoverController)};
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
foreach (var comp in ComponentManager.EntityQuery<ConveyorComponent>())
{
Convey(comp, frameTime);
}
// TODO: Uhh you can probably wrap the recycler's conveying properties into... conveyor
foreach (var comp in ComponentManager.EntityQuery<RecyclerComponent>())
{
ConveyRecycler(comp, frameTime);
}
}
private void Convey(ConveyorComponent comp, float frameTime)
{
// TODO: Use ICollideBehavior and cache intersecting
// Use an event for conveyors to know what needs to run
if (!comp.CanRun())
{
return;
}
var intersecting = EntityManager.GetEntitiesIntersecting(comp.Owner, true);
var direction = comp.GetAngle().ToVec();
Vector2? ownerPos = null;
foreach (var entity in intersecting)
{
if (!comp.CanMove(entity)) continue;
if (!entity.TryGetComponent(out IPhysBody? physics) || physics.BodyStatus == BodyStatus.InAir ||
entity.IsWeightless()) continue;
ownerPos ??= comp.Owner.Transform.WorldPosition;
var itemRelativeToConveyor = entity.Transform.WorldPosition - ownerPos.Value;
physics.LinearVelocity += Convey(direction * comp.Speed, frameTime, itemRelativeToConveyor);
}
}
// TODO Uhhh I did a shit job plz fix smug
private Vector2 Convey(Vector2 velocityDirection, float frameTime, Vector2 itemRelativeToConveyor)
{
//gravitating item towards center
//http://csharphelper.com/blog/2016/09/find-the-shortest-distance-between-a-point-and-a-line-segment-in-c/
Vector2 centerPoint;
var t = 0f;
if (velocityDirection.Length > 0) // if velocitydirection is 0, this calculation will divide by 0
{
t = Vector2.Dot(itemRelativeToConveyor, velocityDirection) /
Vector2.Dot(velocityDirection, velocityDirection);
}
if (t < 0)
{
centerPoint = new Vector2();
}
else if (t > 1)
{
centerPoint = velocityDirection;
}
else
{
centerPoint = velocityDirection * t;
}
var delta = centerPoint - itemRelativeToConveyor;
return delta * (400 * delta.Length) * frameTime;
}
private void ConveyRecycler(RecyclerComponent comp, float frameTime)
{
if (!comp.CanRun())
{
comp.Intersecting.Clear();
return;
}
var direction = Vector2.UnitX;
Vector2? ownerPos = null;
for (var i = comp.Intersecting.Count - 1; i >= 0; i--)
{
var entity = comp.Intersecting[i];
if (entity.Deleted || !comp.CanMove(entity) || !EntityManager.IsIntersecting(comp.Owner, entity))
{
comp.Intersecting.RemoveAt(i);
continue;
}
if (!entity.TryGetComponent(out IPhysBody? physics)) continue;
ownerPos ??= comp.Owner.Transform.WorldPosition;
physics.LinearVelocity += Convey(direction, frameTime, entity.Transform.WorldPosition - ownerPos.Value);
}
}
}
}

View File

@@ -1,15 +1,17 @@
#nullable enable #nullable enable
using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.Components.Sound; using Content.Server.GameObjects.Components.Sound;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Tag; using Content.Shared.GameObjects.Components.Tag;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Physics.Controllers;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -17,13 +19,15 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.Physics.Controllers
{ {
[UsedImplicitly] public class MoverController : SharedMoverController
internal class MoverSystem : SharedMoverSystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
@@ -35,54 +39,83 @@ namespace Content.Server.GameObjects.EntitySystems
private const float StepSoundMoveDistanceRunning = 2; private const float StepSoundMoveDistanceRunning = 2;
private const float StepSoundMoveDistanceWalking = 1.5f; private const float StepSoundMoveDistanceWalking = 1.5f;
/// <inheritdoc /> private HashSet<EntityUid> _excludedMobs = new();
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<PlayerDetachedSystemMessage>(PlayerDetached); _audioSystem = EntitySystem.Get<AudioSystem>();
_audioSystem = EntitySystemManager.GetEntitySystem<AudioSystem>();
UpdatesBefore.Add(typeof(PhysicsSystem));
} }
public override void Update(float frameTime) public override void UpdateBeforeSolve(bool prediction, float frameTime)
{ {
foreach (var (moverComponent, collidableComponent) in EntityManager.ComponentManager base.UpdateBeforeSolve(prediction, frameTime);
.EntityQuery<IMoverComponent, IPhysicsComponent>(false)) _excludedMobs.Clear();
foreach (var (mobMover, mover, physics) in ComponentManager.EntityQuery<IMobMoverComponent, IMoverComponent, PhysicsComponent>())
{ {
var entity = moverComponent.Owner; _excludedMobs.Add(mover.Owner.Uid);
UpdateKinematics(entity.Transform, moverComponent, collidableComponent); HandleMobMovement(mover, physics, mobMover);
}
foreach (var mover in ComponentManager.EntityQuery<ShuttleControllerComponent>())
{
_excludedMobs.Add(mover.Owner.Uid);
HandleShuttleMovement(mover);
}
foreach (var (mover, physics) in ComponentManager.EntityQuery<IMoverComponent, PhysicsComponent>(true))
{
if (_excludedMobs.Contains(mover.Owner.Uid)) continue;
HandleKinematicMovement(mover, physics);
} }
} }
private void PlayerDetached(PlayerDetachedSystemMessage ev) /*
* Some thoughts:
* Unreal actually doesn't predict vehicle movement at all, it's purely server-side which I thought was interesting
* The reason for this is that vehicles change direction very slowly compared to players so you don't really have the requirement for quick movement anyway
* As such could probably just look at applying a force / impulse to the shuttle server-side only so it controls like the titanic.
*/
private void HandleShuttleMovement(ShuttleControllerComponent mover)
{ {
if (ev.Entity.TryGetComponent(out IPhysicsComponent? physics) && var gridId = mover.Owner.Transform.GridID;
physics.TryGetController(out MoverController controller) &&
!ev.Entity.IsWeightless()) if (!_mapManager.TryGetGrid(gridId, out var grid) || !EntityManager.TryGetEntity(grid.GridEntityId, out var gridEntity)) return;
//TODO: Switch to shuttle component
if (!gridEntity.TryGetComponent(out PhysicsComponent? physics))
{ {
controller.StopMoving(); physics = gridEntity.AddComponent<PhysicsComponent>();
physics.BodyStatus = BodyStatus.InAir;
physics.Mass = 1;
physics.CanCollide = true;
physics.AddFixture(new Fixture(physics, new PhysShapeGrid(grid)));
} }
// TODO: Uhh this probably doesn't work but I still need to rip out the entity tree and make RenderingTreeSystem use grids so I'm not overly concerned about breaking shuttles.
physics.ApplyForce(mover.VelocityDir.walking + mover.VelocityDir.sprinting);
mover.VelocityDir = (Vector2.Zero, Vector2.Zero);
} }
protected override void HandleFootsteps(IMoverComponent mover) protected override void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover)
{ {
var transform = mover.Owner.Transform; var transform = mover.Owner.Transform;
// Handle footsteps. // Handle footsteps.
if (_mapManager.GridExists(mover.LastPosition.GetGridId(EntityManager))) if (_mapManager.GridExists(mobMover.LastPosition.GetGridId(EntityManager)))
{ {
// Can happen when teleporting between grids. // Can happen when teleporting between grids.
if (!transform.Coordinates.TryDistance(EntityManager, mover.LastPosition, out var distance)) if (!transform.Coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance))
{ {
mover.LastPosition = transform.Coordinates; mobMover.LastPosition = transform.Coordinates;
return; return;
} }
mover.StepSoundDistance += distance; mobMover.StepSoundDistance += distance;
} }
mover.LastPosition = transform.Coordinates; mobMover.LastPosition = transform.Coordinates;
float distanceNeeded; float distanceNeeded;
if (mover.Sprinting) if (mover.Sprinting)
{ {
@@ -93,11 +126,11 @@ namespace Content.Server.GameObjects.EntitySystems
distanceNeeded = StepSoundMoveDistanceWalking; distanceNeeded = StepSoundMoveDistanceWalking;
} }
if (mover.StepSoundDistance > distanceNeeded) if (mobMover.StepSoundDistance > distanceNeeded)
{ {
mover.StepSoundDistance = 0; mobMover.StepSoundDistance = 0;
if (!mover.Owner.HasTag("FootstepSound")) if (!mover.Owner.HasTag("FootstepSound") || mover.Owner.Transform.GridID == GridId.Invalid)
{ {
return; return;
} }
@@ -110,13 +143,14 @@ namespace Content.Server.GameObjects.EntitySystems
} }
else else
{ {
PlayFootstepSound(transform.Coordinates, mover.Sprinting); PlayFootstepSound(mover.Owner, mover.Sprinting);
} }
} }
} }
private void PlayFootstepSound(EntityCoordinates coordinates, bool sprinting) private void PlayFootstepSound(IEntity mover, bool sprinting)
{ {
var coordinates = mover.Transform.Coordinates;
// Step one: figure out sound collection prototype. // Step one: figure out sound collection prototype.
var grid = _mapManager.GetGrid(coordinates.GetGridId(EntityManager)); var grid = _mapManager.GetGrid(coordinates.GetGridId(EntityManager));
var tile = grid.GetTileRef(coordinates); var tile = grid.GetTileRef(coordinates);
@@ -159,5 +193,6 @@ namespace Content.Server.GameObjects.EntitySystems
Logger.ErrorS("sound", $"Unable to find sound collection for {soundCollectionName}"); Logger.ErrorS("sound", $"Unable to find sound collection for {soundCollectionName}");
} }
} }
} }
} }

View File

@@ -0,0 +1,106 @@
#nullable enable
using Content.Server.GameObjects.Components.Observer;
using Content.Server.GameObjects.Components.Singularity;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Random;
namespace Content.Server.Physics.Controllers
{
internal sealed class SingularityController : VirtualController
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
private float _pullAccumulator;
private float _moveAccumulator;
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
base.UpdateBeforeSolve(prediction, frameTime);
_moveAccumulator += frameTime;
_pullAccumulator += frameTime;
while (_pullAccumulator > 0.5f)
{
_pullAccumulator -= 0.5f;
foreach (var singularity in ComponentManager.EntityQuery<SingularityComponent>())
{
// TODO: Use colliders instead probably yada yada
PullEntities(singularity);
// Yeah look the collision with station wasn't working and I'm 15k lines in and not debugging this shit
DestroyTiles(singularity);
}
}
while (_moveAccumulator > 1.0f)
{
_moveAccumulator -= 1.0f;
foreach (var (singularity, physics) in ComponentManager.EntityQuery<SingularityComponent, PhysicsComponent>())
{
if (singularity.Owner.HasComponent<BasicActorComponent>()) continue;
// TODO: Need to essentially use a push vector in a random direction for us PLUS
// Any entity colliding with our larger circlebox needs to have an impulse applied to itself.
physics.BodyStatus = BodyStatus.InAir;
MoveSingulo(singularity, physics);
}
}
}
private void MoveSingulo(SingularityComponent singularity, PhysicsComponent physics)
{
if (singularity.Level <= 1) return;
// TODO: Could try gradual changes instead but for now just try to replicate
var pushVector = new Vector2(_robustRandom.Next(-10, 10), _robustRandom.Next(-10, 10));
if (pushVector == Vector2.Zero) return;
physics.LinearVelocity = Vector2.Zero;
physics.LinearVelocity = pushVector.Normalized * 2;
}
private void PullEntities(SingularityComponent component)
{
var singularityCoords = component.Owner.Transform.Coordinates;
// TODO: Maybe if we have named fixtures needs to pull out the outer circle collider (inner will be for deleting).
var entitiesToPull = EntityManager.GetEntitiesInRange(singularityCoords, component.Level * 10);
foreach (var entity in entitiesToPull)
{
if (!entity.TryGetComponent<PhysicsComponent>(out var collidableComponent) || collidableComponent.BodyType == BodyType.Static) continue;
if (entity.HasComponent<GhostComponent>()) continue;
if (singularityCoords.EntityId != entity.Transform.Coordinates.EntityId) continue;
var vec = (singularityCoords - entity.Transform.Coordinates).Position;
if (vec == Vector2.Zero) continue;
var speed = 10 / vec.Length * component.Level;
collidableComponent.ApplyLinearImpulse(vec.Normalized * speed);
}
}
private void DestroyTiles(SingularityComponent component)
{
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return;
var worldBox = physicsComponent.GetWorldAABB();
foreach (var grid in _mapManager.FindGridsIntersecting(component.Owner.Transform.MapID, worldBox))
{
foreach (var tile in grid.GetTilesIntersecting(worldBox))
{
grid.SetTile(tile.GridIndices, Tile.Empty);
}
}
}
}
}

View File

@@ -1,156 +0,0 @@
using System;
using Content.Server.GameObjects.Components.Projectiles;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Throw
{
public static class ThrowHelper
{
/// <summary>
/// Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>.
/// </summary>
/// <param name="thrownEnt">The entity to throw.</param>
/// <param name="throwForce">
/// The force to throw the entity with.
/// Total impulse applied is equal to this force applied for one second.
/// </param>
/// <param name="targetLoc">
/// The target location to throw at.
/// This is only used to calculate a direction,
/// actual distance is purely determined by <paramref name="throwForce"/>.
/// </param>
/// <param name="sourceLoc">
/// The position to start the throw from.
/// </param>
/// <param name="spread">
/// If true, slightly spread the actual throw angle.
/// </param>
/// <param name="throwSourceEnt">
/// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in.
/// </param>
public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
{
if (thrownEnt.Deleted)
{
return;
}
if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp))
return;
var entityManager = IoCManager.Resolve<IEntityManager>();
var direction_vector = targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager);
if (direction_vector.Length == 0)
{
return;
}
colComp.CanCollide = true;
// I can now collide with player, so that i can do damage.
if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp))
{
projComp = thrownEnt.AddComponent<ThrownItemComponent>();
if (colComp.PhysicsShapes.Count == 0)
colComp.PhysicsShapes.Add(new PhysShapeAabb());
colComp.PhysicsShapes[0].CollisionMask |= (int) CollisionGroup.ThrownItem;
colComp.Status = BodyStatus.InAir;
}
var angle = new Angle(direction_vector);
if (spread)
{
var spreadRandom = IoCManager.Resolve<IRobustRandom>();
angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3));
}
if (throwSourceEnt != null)
{
projComp.User = throwSourceEnt;
projComp.IgnoreEntity(throwSourceEnt);
if (ActionBlockerSystem.CanChangeDirection(throwSourceEnt))
{
throwSourceEnt.Transform.LocalRotation = (angle + MathHelper.PiOver2).GetCardinalDir().ToAngle();
}
}
// scaling is handled elsewhere, this is just multiplying by 60 independent of timing as a fix until elsewhere values are updated
var spd = throwForce * 60;
projComp.StartThrow(angle.ToVec(), spd);
if (throwSourceEnt != null &&
throwSourceEnt.TryGetComponent<IPhysicsComponent>(out var physics))
{
if (throwSourceEnt.IsWeightless())
{
// We don't check for surrounding entities,
// so you'll still get knocked around if you're hugging the station wall in zero g.
// I got kinda lazy is the reason why. Also it makes a bit of sense.
// If somebody wants they can come along and make it so magboots completely hold you still.
// Would be a cool incentive to use them.
const float throwFactor = 0.2f; // Break Newton's Third Law for better gameplay
var mover = physics.EnsureController<ThrowKnockbackController>();
mover.Push(-angle.ToVec(), spd * throwFactor);
}
}
}
/// <summary>
/// Throw an entity at the position of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>,
/// without overshooting.
/// </summary>cl
/// <param name="thrownEnt">The entity to throw.</param>
/// <param name="throwForceMax">
/// The MAXIMUM force to throw the entity with.
/// Throw force increases with distance to target, this is the maximum force allowed.
/// </param>
/// <param name="targetLoc">
/// The target location to throw at.
/// This function will try to land at this exact spot,
/// if <paramref name="throwForceMax"/> is large enough to allow for it to be reached.
/// </param>
/// <param name="sourceLoc">
/// The position to start the throw from.
/// </param>
/// <param name="spread">
/// If true, slightly spread the actual throw angle.
/// </param>
/// <param name="throwSourceEnt">
/// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in.
/// </param>
public static void ThrowTo(this IEntity thrownEnt, float throwForceMax, EntityCoordinates targetLoc,
EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
var timing = IoCManager.Resolve<IGameTiming>();
// Calculate the force necessary to land a throw based on throw duration, mass and distance.
if (!targetLoc.TryDistance(entityManager, sourceLoc, out var distance))
{
return;
}
var throwDuration = ThrownItemComponent.DefaultThrowTime;
// TODO: Mass isn't even used on the system side yet for controllers so do that someday
var velocityNecessary = distance / throwDuration;
var forceNecessary = velocityNecessary / timing.TickRate;
// Then clamp it to the max force allowed and call Throw().
thrownEnt.Throw(MathF.Min(forceNecessary, throwForceMax), targetLoc, sourceLoc, spread, throwSourceEnt);
}
}
}

View File

@@ -169,6 +169,18 @@ namespace Content.Shared.AI
Cached = cached; Cached = cached;
} }
} }
/// <summary>
/// Send if someone is subscribing to reachable regions for NPCs.
/// </summary>
[Serializable, NetSerializable]
public sealed class SubscribeReachableMessage : EntitySystemMessage {}
/// <summary>
/// Send if someone is unsubscribing to reachable regions for NPCs.
/// </summary>
[Serializable, NetSerializable]
public sealed class UnsubscribeReachableMessage : EntitySystemMessage {}
#endregion #endregion
} }
} }

View File

@@ -166,6 +166,15 @@ namespace Content.Shared
public static readonly CVarDef<bool> ParallaxDebug = public static readonly CVarDef<bool> ParallaxDebug =
CVarDef.Create("parallax.debug", false, CVar.CLIENTONLY); CVarDef.Create("parallax.debug", false, CVar.CLIENTONLY);
/*
* Physics
*/
public static readonly CVarDef<float> TileFrictionModifier =
CVarDef.Create("physics.tilefriction", 15.0f);
public static readonly CVarDef<float> StopSpeed =
CVarDef.Create("physics.stopspeed", 0.1f);
/* /*
* Ambience * Ambience

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.Construction.ConstructionConditions namespace Content.Shared.Construction.ConstructionConditions
{ {
@@ -33,7 +34,7 @@ namespace Content.Shared.Construction.ConstructionConditions
return false; return false;
// now we need to check that user actually tries to build wallmount on a wall // now we need to check that user actually tries to build wallmount on a wall
var physics = IoCManager.Resolve<IPhysicsManager>(); var physics = EntitySystem.Get<SharedBroadPhaseSystem>();
var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int) CollisionGroup.Impassable); var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int) CollisionGroup.Impassable);
var length = userToObject.Length; var length = userToObject.Length;
var userToObjRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rUserToObj, maxLength: length, var userToObjRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rUserToObj, maxLength: length,

View File

@@ -13,6 +13,9 @@ using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;

View File

@@ -19,7 +19,7 @@ namespace Content.Shared.GameObjects.Components.Buckle
public sealed override uint? NetID => ContentNetIDs.BUCKLE; public sealed override uint? NetID => ContentNetIDs.BUCKLE;
[ComponentDependency] protected readonly IPhysicsComponent? Physics; [ComponentDependency] protected readonly IPhysBody? Physics;
/// <summary> /// <summary>
/// The range from which this entity can buckle to a <see cref="SharedStrapComponent"/>. /// The range from which this entity can buckle to a <see cref="SharedStrapComponent"/>.

View File

@@ -21,8 +21,8 @@ namespace Content.Shared.GameObjects.Components.Disposal
[ViewVariables] [ViewVariables]
public bool Anchored => public bool Anchored =>
!Owner.TryGetComponent(out IPhysicsComponent? physics) || !Owner.TryGetComponent(out IPhysBody? physics) ||
physics.Anchored; physics.BodyType == BodyType.Static;
[Serializable, NetSerializable] [Serializable, NetSerializable]
public enum Visuals public enum Visuals
@@ -167,7 +167,7 @@ namespace Content.Shared.GameObjects.Components.Disposal
if (!Anchored) if (!Anchored)
return false; return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide) !physics.CanCollide)
{ {
if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) { if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) {

View File

@@ -20,7 +20,7 @@ namespace Content.Shared.GameObjects.Components.Doors
protected readonly SharedAppearanceComponent? AppearanceComponent = null; protected readonly SharedAppearanceComponent? AppearanceComponent = null;
[ComponentDependency] [ComponentDependency]
protected readonly IPhysicsComponent? PhysicsComponent = null; protected readonly IPhysBody? PhysicsComponent = null;
[ViewVariables] [ViewVariables]
private DoorState _state = DoorState.Closed; private DoorState _state = DoorState.Closed;

View File

@@ -0,0 +1,27 @@
#nullable enable
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Shared.GameObjects.Components.Items
{
[RegisterComponent]
public class ThrownItemComponent : Component, IStartCollide, ICollideSpecial
{
public override string Name => "ThrownItem";
public IEntity? Thrower { get; set; }
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
if (otherBody.Entity == Thrower) return;
EntitySystem.Get<ThrownItemSystem>().ThrowCollideInteraction(Thrower, ourBody, otherBody);
}
bool ICollideSpecial.PreventCollide(IPhysBody collidedwith)
{
return collidedwith.Entity == Thrower;
}
}
}

View File

@@ -21,8 +21,22 @@ namespace Content.Shared.GameObjects.Components.Mobs
get => _isInCombatMode; get => _isInCombatMode;
set set
{ {
if (_isInCombatMode == value) return;
_isInCombatMode = value; _isInCombatMode = value;
Dirty(); Dirty();
// Regenerate physics contacts -> Can probably just selectively check
/* Still a bit jank so left disabled for now.
if (Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
{
if (value)
{
physicsComponent.WakeBody();
}
physicsComponent.RegenerateContacts();
}
*/
} }
} }
@@ -32,6 +46,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
get => _activeZone; get => _activeZone;
set set
{ {
if (_activeZone == value) return;
_activeZone = value; _activeZone = value;
Dirty(); Dirty();
} }

View File

@@ -0,0 +1,17 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.GameObjects.Components.Movement
{
public interface IMobMoverComponent : IComponent
{
EntityCoordinates LastPosition { get; set; }
public float StepSoundDistance { get; set; }
float GrabRange { get; set; }
float PushStrength { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More