* 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);
if (curState is not ClimbModeComponentState climbModeState || Body == null)
if (curState is not ClimbModeComponentState climbModeState)
{
return;
}
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.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Client.GameObjects.Components.Suspicion
{
@@ -62,7 +63,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
continue;
}
if (!ally.TryGetComponent(out IPhysicsComponent physics))
if (!ally.TryGetComponent(out IPhysBody physics))
{
continue;
}
@@ -82,7 +83,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
continue;
}
var worldBox = physics.WorldAABB;
var worldBox = physics.GetWorldAABB();
// if not on screen, or too small, continue
if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty())
@@ -90,7 +91,7 @@ namespace Content.Client.GameObjects.Components.Suspicion
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);
}
}

View File

@@ -128,8 +128,12 @@ namespace Content.Client.GameObjects.EntitySystems.AI
if (tooltip == PathfindingDebugMode.Graph)
{
var systemMessage = new SharedAiDebug.RequestPathfindingGraphMessage();
EntityManager.EntityNetManager.SendSystemNetworkMessage(systemMessage);
EntityManager.EntityNetManager.SendSystemNetworkMessage(new SharedAiDebug.RequestPathfindingGraphMessage());
}
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
@@ -138,6 +142,11 @@ namespace Content.Client.GameObjects.EntitySystems.AI
private void DisableMode(PathfindingDebugMode mode)
{
if (mode == PathfindingDebugMode.Regions && (_modes & PathfindingDebugMode.Regions) != 0)
{
EntityManager.EntityNetManager.SendSystemNetworkMessage(new SharedAiDebug.UnsubscribeReachableMessage());
}
_modes &= ~mode;
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))
{
var entity = snapGridComponent.Owner;
if (!entity.TryGetComponent(out SubFloorHideComponent subFloorComponent) ||
!entity.TryGetComponent(out ISpriteComponent spriteComponent))
if (!entity.TryGetComponent(out SubFloorHideComponent subFloorComponent))
{
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: PowerReceiver
- type: Physics
anchored: true
bodyType: Static
- type: entity
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.Shared.GameObjects.Components.Doors;
using NUnit.Framework;
@@ -19,10 +20,11 @@ namespace Content.IntegrationTests.Tests.Doors
id: PhysicsDummy
components:
- type: Physics
anchored: false
shapes:
- !type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
bodyType: Dynamic
fixtures:
- shape:
!type:PhysShapeCircle
bounds: ""-0.49,-0.49,0.49,0.49""
layer:
- Impassable
@@ -33,9 +35,11 @@ namespace Content.IntegrationTests.Tests.Doors
- type: Door
- type: Airlock
- type: Physics
shapes:
- !type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
bodyType: Static
fixtures:
- shape:
!type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
mask:
- Impassable
";
@@ -111,9 +115,9 @@ namespace Content.IntegrationTests.Tests.Doors
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
IPhysBody physBody = null;
IEntity physicsDummy = null;
IEntity airlock = null;
TestController controller = null;
ServerDoorComponent doorComponent = null;
var physicsDummyStartingX = -1;
@@ -128,9 +132,7 @@ namespace Content.IntegrationTests.Tests.Doors
airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates((0, 0), mapId));
Assert.True(physicsDummy.TryGetComponent(out IPhysicsComponent physics));
controller = physics.EnsureController<TestController>();
Assert.True(physicsDummy.TryGetComponent(out physBody));
Assert.True(airlock.TryGetComponent(out doorComponent));
Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed));
@@ -139,12 +141,13 @@ namespace Content.IntegrationTests.Tests.Doors
await server.WaitIdleAsync();
// 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)
{
// Keep the airlock awake so they collide
airlock.GetComponent<IPhysicsComponent>().WakeBody();
airlock.GetComponent<IPhysBody>().WakeBody();
// Ensure that it is still closed
Assert.That(doorComponent.State, Is.EqualTo(SharedDoorComponent.DoorState.Closed));
@@ -154,12 +157,12 @@ namespace Content.IntegrationTests.Tests.Doors
}
// 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
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.IoC;
using Robust.Shared.Map;
using Robust.Shared.Physics;
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.
climbing.IsClimbing = true;
climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition);
var body = human.GetComponent<IPhysicsComponent>();
Assert.That(body.HasController<ClimbController>(), "Player has no ClimbController");
var body = human.GetComponent<IPhysBody>();
// TODO: Check it's climbing
// Force the player out of climb state. It should immediately remove the ClimbController.
climbing.IsClimbing = false;
Assert.That(!body.HasController<ClimbController>(), "Player wrongly has a ClimbController");
});
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 Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Physics.Broadphase;
namespace Content.IntegrationTests.Tests.Utility
{
@@ -20,9 +21,10 @@ namespace Content.IntegrationTests.Tests.Utility
name: {BlockerDummyId}
components:
- type: Physics
shapes:
- !type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
fixtures:
- shape:
!type:PhysShapeAabb
bounds: ""-0.49,-0.49,0.49,0.49""
mask:
- Impassable
";
@@ -37,6 +39,7 @@ namespace Content.IntegrationTests.Tests.Utility
var sMapManager = server.ResolveDependency<IMapManager>();
var sEntityManager = server.ResolveDependency<IEntityManager>();
var broady = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedBroadPhaseSystem>();
await server.WaitAssertion(() =>
{
@@ -58,6 +61,7 @@ namespace Content.IntegrationTests.Tests.Utility
// Spawn a blocker with an Impassable mask
sEntityManager.SpawnEntity(BlockerDummyId, entityCoordinates);
broady.Update(0.016f);
// Cannot spawn something with an Impassable layer
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.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Server.AI.Utils
{
@@ -40,7 +41,7 @@ namespace Content.Server.AI.Utils
angle.ToVec(),
(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;
}

View File

@@ -8,6 +8,8 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
namespace Content.Server.Administration.Commands
{
@@ -113,9 +115,9 @@ namespace Content.Server.Administration.Commands
if (found.GetGridId(entityManager) != GridId.Invalid)
{
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

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.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.ViewVariables;
@@ -193,14 +194,12 @@ namespace Content.Server.Atmos
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.IsInContainer())
continue;
physics.WakeBody();
var pressureMovements = physics.EnsureController<HighPressureMovementController>();
var pressureMovements = physics.Entity.EnsureComponent<MovedByPressureComponent>();
if (pressure.LastHighPressureMovementAirCycle < _gridAtmosphereComponent.UpdateCounter)
{
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.Shared.Administration;
using Content.Shared.GameObjects.Components.Mobs.Speech;
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -55,7 +56,8 @@ namespace Content.Server.Commands
Timer.Spawn(100, () =>
{
entity.EnsureComponent<MindComponent>();
entity.EnsureComponent<PlayerInputMoverComponent>();
entity.EnsureComponent<SharedPlayerInputMoverComponent>();
entity.EnsureComponent<SharedPlayerMobMoverComponent>();
entity.EnsureComponent<SharedSpeechComponent>();
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 Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
@@ -15,9 +17,9 @@ namespace Content.Server.Construction.Completions
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 Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.Construction.Conditions
@@ -15,21 +17,21 @@ namespace Content.Server.Construction.Conditions
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)
{
if (!entity.TryGetComponent(out IPhysicsComponent physics)) return false;
if (!entity.TryGetComponent(out IPhysBody physics)) return false;
switch (Anchored)
{
case true when !physics.Anchored:
case true when physics.BodyType != BodyType.Static:
message.AddMarkup("First, anchor it.\n");
return true;
case false when physics.Anchored:
case false when physics.BodyType == BodyType.Static:
message.AddMarkup("First, unanchor it.\n");
return true;
}

View File

@@ -77,6 +77,7 @@ namespace Content.Server.Explosions
var impassableEntities = 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
// and splitted into two lists based on if they are Impassable or not
@@ -92,7 +93,7 @@ namespace Content.Server.Explosions
continue;
}
if (!entity.TryGetComponent(out IPhysicsComponent? body) || body.PhysicsShapes.Count < 1)
if (!entity.TryGetComponent(out PhysicsComponent? body) || body.Fixtures.Count < 1)
{
continue;
}

View File

@@ -8,6 +8,8 @@ using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components
@@ -37,7 +39,7 @@ namespace Content.Server.GameObjects.Components
/// <returns>true if it is valid, false otherwise</returns>
private async Task<bool> Valid(IEntity user, IEntity? utilizing, [NotNullWhen(true)] bool force = false)
{
if (!Owner.HasComponent<IPhysicsComponent>())
if (!Owner.HasComponent<IPhysBody>())
{
return false;
}
@@ -69,8 +71,8 @@ namespace Content.Server.GameObjects.Components
return false;
}
var physics = Owner.GetComponent<IPhysicsComponent>();
physics.Anchored = true;
var physics = Owner.GetComponent<IPhysBody>();
physics.BodyType = BodyType.Static;
if (Owner.TryGetComponent(out PullableComponent? pullableComponent))
{
@@ -100,8 +102,8 @@ namespace Content.Server.GameObjects.Components
return false;
}
var physics = Owner.GetComponent<IPhysicsComponent>();
physics.Anchored = false;
var physics = Owner.GetComponent<IPhysBody>();
physics.BodyType = BodyType.Dynamic;
return true;
}
@@ -115,12 +117,12 @@ namespace Content.Server.GameObjects.Components
/// <returns>true if toggled, false otherwise</returns>
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 physics.Anchored ?
return physics.BodyType == BodyType.Static ?
await TryUnAnchor(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.Localization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos
{
[RegisterComponent]
public class FlammableComponent : SharedFlammableComponent, ICollideBehavior, IFireAct, IReagentReaction
public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IReagentReaction
{
private bool _resisting = false;
private readonly List<EntityUid> _collided = new();
@@ -129,19 +132,19 @@ namespace Content.Server.GameObjects.Components.Atmos
}
var entity = Owner.EntityManager.GetEntity(uid);
var physics = Owner.GetComponent<IPhysicsComponent>();
var otherPhysics = entity.GetComponent<IPhysicsComponent>();
var physics = Owner.GetComponent<IPhysBody>();
var otherPhysics = entity.GetComponent<IPhysBody>();
if (!physics.WorldAABB.Intersects(otherPhysics.WorldAABB))
if (!physics.GetWorldAABB().Intersects(otherPhysics.GetWorldAABB()))
{
_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;
if (!FireSpread || !otherFlammable.FireSpread)

View File

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

View File

@@ -1,6 +1,14 @@
#nullable enable
using System;
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.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -9,8 +17,16 @@ namespace Content.Server.GameObjects.Components.Atmos
[RegisterComponent]
public class MovedByPressureComponent : Component
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
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)]
[DataField("enabled")]
public bool Enabled { get; set; } = true;
@@ -22,6 +38,77 @@ namespace Content.Server.GameObjects.Components.Atmos
public float MoveResist { get; set; } = 100f;
[ViewVariables(VVAccess.ReadWrite)]
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

View File

@@ -7,6 +7,8 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -15,7 +17,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Chemistry
{
[RegisterComponent]
class VaporComponent : SharedVaporComponent, ICollideBehavior
class VaporComponent : SharedVaporComponent, IStartCollide
{
public const float ReactTime = 0.125f;
@@ -31,8 +33,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
private float _timer;
private EntityCoordinates _target;
private bool _running;
private Vector2 _direction;
private float _velocity;
private float _aliveTime;
public override void Initialize()
@@ -42,18 +42,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
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;
_target = target;
_direction = dir;
_velocity = velocity;
_aliveTime = aliveTime;
// Set Move
if (Owner.TryGetComponent(out IPhysicsComponent physics))
if (Owner.TryGetComponent(out PhysicsComponent physics))
{
var controller = physics.EnsureController<VaporController>();
controller.Move(_direction, _velocity);
physics.BodyStatus = BodyStatus.InAir;
physics.ApplyLinearImpulse(dir * speed);
}
}
@@ -68,7 +66,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
_timer += frameTime;
_reactTimer += frameTime;
if (_reactTimer >= ReactTime && Owner.TryGetComponent(out IPhysicsComponent physics))
if (_reactTimer >= ReactTime)
{
_reactTimer = 0;
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)
{
_reached = true;
if (Owner.TryGetComponent(out IPhysicsComponent coll))
{
var controller = coll.EnsureController<VaporController>();
controller.Stop();
}
}
if (contents.CurrentVolume == 0 || _timer > _aliveTime)
@@ -123,26 +115,17 @@ namespace Content.Server.GameObjects.Components.Chemistry
return true;
}
void ICollideBehavior.CollideWith(IEntity collidedWith)
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
if (!Owner.TryGetComponent(out SolutionContainerComponent contents))
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
if (collidedWith.TryGetComponent(out IPhysicsComponent physics))
if ((otherBody.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && otherBody.Hard)
{
if ((physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && physics.Hard)
{
if (Owner.TryGetComponent(out IPhysicsComponent coll))
{
var controller = coll.EnsureController<VaporController>();
controller.Stop();
}
Owner.Delete();
}
Owner.Delete();
}
}
}

View File

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

View File

@@ -4,12 +4,14 @@ using Content.Server.GameObjects.Components.MachineLinking;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Shared.GameObjects.Components.Conveyor;
using Content.Shared.GameObjects.Components.MachineLinking;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -30,6 +32,8 @@ namespace Content.Server.GameObjects.Components.Conveyor
[DataField("angle")]
private Angle _angle;
public float Speed => _speed;
/// <summary>
/// The amount of units to move the entity by per second.
/// </summary>
@@ -90,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
/// <returns>
/// The angle when taking into account if the conveyor is reversed
/// </returns>
private Angle GetAngle()
public Angle GetAngle()
{
var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0;
var radians = MathHelper.DegreesToRadians(_angle);
@@ -98,7 +102,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment);
}
private bool CanRun()
public bool CanRun()
{
if (State == ConveyorState.Off)
{
@@ -119,15 +123,16 @@ namespace Content.Server.GameObjects.Components.Conveyor
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)
{
return false;
}
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
physics.Anchored)
if (!entity.TryGetComponent(out IPhysBody? physics) ||
physics.BodyType == BodyType.Static)
{
return false;
}
@@ -150,31 +155,6 @@ namespace Content.Server.GameObjects.Components.Conveyor
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)
{
State = signal switch

View File

@@ -6,6 +6,8 @@ using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
@@ -15,7 +17,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.GameObjects.Components.Damage
{
[RegisterComponent]
public class DamageOnHighSpeedImpactComponent : Component, ICollideBehavior
public class DamageOnHighSpeedImpactComponent : Component, IStartCollide
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -42,16 +44,16 @@ namespace Content.Server.GameObjects.Components.Damage
public float DamageCooldown { get; set; } = 2f;
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(!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)
return;
@@ -63,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Damage
if (Owner.TryGetComponent(out StunnableComponent stun) && _robustRandom.Prob(StunChance))
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.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -76,7 +77,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false;
}
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide)
{
return false;
@@ -93,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false;
}
if (entity.TryGetComponent(out IPhysicsComponent? physics))
if (entity.TryGetComponent(out IPhysBody? physics))
{
physics.CanCollide = false;
}
@@ -125,7 +126,7 @@ namespace Content.Server.GameObjects.Components.Disposal
foreach (var entity in _contents.ContainedEntities.ToArray())
{
if (entity.TryGetComponent(out IPhysicsComponent? physics))
if (entity.TryGetComponent(out IPhysBody? physics))
{
physics.CanCollide = true;
}

View File

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

View File

@@ -16,6 +16,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.ViewVariables;
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
@@ -33,8 +34,8 @@ namespace Content.Server.GameObjects.Components.Disposal
[ViewVariables]
public bool Anchored =>
!Owner.TryGetComponent(out IPhysicsComponent? physics) ||
physics.Anchored;
!Owner.TryGetComponent(out IPhysBody? physics) ||
physics.BodyType == BodyType.Static;
[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.Localization;
using Robust.Shared.Log;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
@@ -144,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Disposal
if (!base.CanInsert(entity))
return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide)
{
if (entity.TryGetComponent(out IMobStateComponent? state) && state.IsDead())

View File

@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using System;
using System.Linq;
using System.Threading;
@@ -26,6 +26,9 @@ using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
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 Timer = Robust.Shared.Timing.Timer;
@@ -34,7 +37,7 @@ namespace Content.Server.GameObjects.Components.Doors
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(SharedDoorComponent))]
public class ServerDoorComponent : SharedDoorComponent, IActivate, ICollideBehavior, IInteractUsing, IMapInit
public class ServerDoorComponent : SharedDoorComponent, IActivate, IStartCollide, IInteractUsing, IMapInit
{
[ComponentDependency]
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)
{
@@ -214,9 +217,9 @@ namespace Content.Server.GameObjects.Components.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.
@@ -229,7 +232,7 @@ namespace Content.Server.GameObjects.Components.Doors
TryOpen(entity);
*/
TryOpen(entity);
TryOpen(otherBody.Entity);
}
}
@@ -410,18 +413,14 @@ namespace Content.Server.GameObjects.Components.Doors
{
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 &&
((PhysicsComponent.CollisionMask & e.CollisionLayer) != 0x0 ||
(PhysicsComponent.CollisionLayer & e.CollisionMask) != 0x0))
{
return true;
}
if ((physicsComponent.CollisionMask & e.CollisionLayer) != 0 && broadPhaseSystem.IntersectionPercent(physicsComponent, e) > 0.01f) return true;
}
}
return false;
@@ -493,26 +492,25 @@ namespace Content.Server.GameObjects.Components.Doors
return false;
}
var doorAABB = PhysicsComponent.WorldAABB;
var doorAABB = PhysicsComponent.GetWorldAABB();
var hitsomebody = false;
// Crush
foreach (var e in collidingentities)
{
if (!e.TryGetComponent(out StunnableComponent? stun)
|| !e.TryGetComponent(out IDamageableComponent? damage)
|| !e.TryGetComponent(out IPhysicsComponent? otherBody))
if (!e.Entity.TryGetComponent(out StunnableComponent? stun)
|| !e.Entity.TryGetComponent(out IDamageableComponent? damage))
{
continue;
}
var percentage = otherBody.WorldAABB.IntersectPercentage(doorAABB);
var percentage = e.GetWorldAABB().IntersectPercentage(doorAABB);
if (percentage < 0.1f)
continue;
hitsomebody = true;
CurrentlyCrushing.Add(e.Uid);
CurrentlyCrushing.Add(e.Entity.Uid);
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
stun.Paralyze(DoorStunTime);

View File

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

View File

@@ -15,6 +15,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
@@ -368,7 +369,7 @@ namespace Content.Server.GameObjects.Components.Fluids
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)
{
puddle = default;

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
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.Verbs;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using Content.Shared.Utility;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
@@ -15,6 +15,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -24,7 +25,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
[ComponentReference(typeof(StorableComponent))]
[ComponentReference(typeof(SharedStorableComponent))]
[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 uint? NetID => ContentNetIDs.ITEM;
@@ -83,8 +84,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return false;
}
if (Owner.TryGetComponent(out IPhysicsComponent physics) &&
physics.Anchored)
if (Owner.TryGetComponent(out IPhysBody physics) &&
physics.BodyType == BodyType.Static)
{
return false;
}
@@ -137,22 +138,43 @@ namespace Content.Server.GameObjects.Components.Items.Storage
var targetLocation = eventArgs.Target.Transform.Coordinates;
var dirVec = (targetLocation.ToMapPos(Owner.EntityManager) - sourceLocation.ToMapPos(Owner.EntityManager)).Normalized;
var throwForce = 1.0f;
float throwForce;
switch (eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
throwForce = 3.0f;
throwForce = 30.0f;
break;
case ExplosionSeverity.Heavy:
throwForce = 2.0f;
throwForce = 20.0f;
break;
case ExplosionSeverity.Light:
throwForce = 1.0f;
default:
throwForce = 10.0f;
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 Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Mobs.State
{
@@ -30,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Mobs.State
EntitySystem.Get<StandingStateSystem>().Down(entity);
if (entity.TryGetComponent(out IPhysicsComponent physics))
if (entity.TryGetComponent(out IPhysBody physics))
{
physics.CanCollide = false;
}
@@ -40,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Mobs.State
{
base.ExitState(entity);
if (entity.TryGetComponent(out IPhysicsComponent physics))
if (entity.TryGetComponent(out IPhysBody physics))
{
physics.CanCollide = true;
}

View File

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

View File

@@ -14,6 +14,8 @@ using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Movement
@@ -158,9 +160,11 @@ namespace Content.Server.GameObjects.Components.Movement
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 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
{
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
{
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
// 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);
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 climbMode = user.GetComponent<ClimbingComponent>();
@@ -220,7 +227,7 @@ namespace Content.Server.GameObjects.Components.Movement
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);
user.PopupMessageOtherClients(othersMessage);

View File

@@ -1,10 +1,13 @@
#nullable enable
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Buckle;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.Movement
{
@@ -12,23 +15,41 @@ namespace Content.Server.GameObjects.Components.Movement
[ComponentReference(typeof(SharedClimbingComponent))]
public class ClimbingComponent : SharedClimbingComponent
{
private bool _isClimbing;
private ClimbController? _climbController;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override bool IsClimbing
{
get => _isClimbing;
get => base.IsClimbing;
set
{
if (_isClimbing == value)
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();
}
}
@@ -51,38 +72,36 @@ namespace Content.Server.GameObjects.Components.Movement
/// </summary>
public void TryMoveTo(Vector2 from, Vector2 to)
{
if (Body == null)
return;
if (Body == null) return;
_climbController = Body.EnsureController<ClimbController>();
_climbController.TryMoveTo(from, to);
var velocity = (to - from).Length;
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()
{
if (!IsClimbing || Body == null)
return;
if (_climbController != null && (_climbController.IsBlocked || !_climbController.IsActive))
if (!IsClimbing || _gameTiming.CurTime < TimeSpan.FromSeconds(BufferTime) + StartClimbTime)
{
if (Body.TryRemoveController<ClimbController>())
{
_climbController = null;
}
return;
}
if (IsClimbing)
Body.WakeBody();
if (!IsOnClimbableThisFrame && IsClimbing && _climbController == null)
if (!IsOnClimbableThisFrame && IsClimbing)
IsClimbing = false;
IsOnClimbableThisFrame = false;
}
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.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -21,8 +23,6 @@ namespace Content.Server.GameObjects.Components.Movement
[ComponentReference(typeof(IMoverComponent))]
internal class ShuttleControllerComponent : Component, IMoverComponent
{
[Dependency] private readonly IMapManager _mapManager = default!;
private bool _movingUp;
private bool _movingDown;
private bool _movingLeft;
@@ -42,43 +42,19 @@ namespace Content.Server.GameObjects.Components.Movement
public override string Name => "ShuttleController";
public bool IgnorePaused => false;
[ViewVariables(VVAccess.ReadWrite)]
public float CurrentWalkSpeed { get; } = 8;
public float CurrentSprintSpeed => 0;
/// <inheritdoc />
[ViewVariables]
public float CurrentPushSpeed => 0.0f;
/// <inheritdoc />
[ViewVariables]
public float GrabRange => 0.0f;
public bool Sprinting => false;
public (Vector2 walking, Vector2 sprinting) VelocityDir { get; } = (Vector2.Zero, Vector2.Zero);
public EntityCoordinates LastPosition { get; set; }
public float StepSoundDistance { get; set; }
public (Vector2 walking, Vector2 sprinting) VelocityDir { get; set; } = (Vector2.Zero, Vector2.Zero);
public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled)
{
var gridId = Owner.Transform.GridID;
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);
}
VelocityDir = (CalcNewVelocity(direction, enabled), Vector2.Zero);
}
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.IoC;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.NodeContainer.Nodes
@@ -40,7 +42,7 @@ namespace Content.Server.GameObjects.Components.NodeContainer.Nodes
/// </summary>
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>
/// 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.Log;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.PA
{
[RegisterComponent]
public class ParticleProjectileComponent : Component, ICollideBehavior
public class ParticleProjectileComponent : Component, IStartCollide
{
public override string Name => "ParticleProjectile";
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
{
@@ -30,8 +32,8 @@ namespace Content.Server.GameObjects.Components.PA
};
singularityComponent.Energy += 10 * multiplier;
Owner.Delete();
}else if (collidedWith.TryGetComponent<SingularityGeneratorComponent>(out var singularityGeneratorComponent)
)
}
else if (otherBody.Entity.TryGetComponent<SingularityGeneratorComponent>(out var singularityGeneratorComponent))
{
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");
return;
}
physicsComponent.Status = BodyStatus.InAir;
physicsComponent.BodyStatus = BodyStatus.InAir;
if (!Owner.TryGetComponent<ProjectileComponent>(out var projectileComponent))
{
@@ -81,7 +83,6 @@ namespace Content.Server.GameObjects.Components.PA
spriteComponent.LayerSetState(0, $"particle{suffix}");
physicsComponent
.EnsureController<BulletController>()
.LinearVelocity = angle.ToWorldVec() * 20f;
Owner.Transform.LocalRotation = angle;

View File

@@ -6,12 +6,15 @@ using Content.Shared.GameObjects.Components.Tag;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Portal
{
[RegisterComponent]
public class PortalComponent : SharedPortalComponent, ICollideBehavior
public class PortalComponent : SharedPortalComponent, IStartCollide
{
// Potential improvements: Different sounds,
// Add Gateways
@@ -152,11 +155,11 @@ namespace Content.Server.GameObjects.Components.Portal
StartCooldown();
}
public void CollideWith(IEntity collidedWith)
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
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.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -95,7 +96,7 @@ namespace Content.Server.GameObjects.Components.Portal
{
// Added this component to avoid stacking portals and causing shenanigans
// 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;
}
@@ -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.
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;
}

View File

@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
@@ -23,7 +24,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents
{
[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";
@@ -53,7 +54,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents
/// </summary>
public bool Connectable => Anchored;
private bool Anchored => _physicsComponent == null || _physicsComponent.Anchored;
private bool Anchored => _physicsComponent == null || _physicsComponent.BodyType == BodyType.Static;
[ViewVariables]
public bool NeedsProvider { get; private set; } = true;

View File

@@ -7,11 +7,13 @@ using Robust.Shared.ViewVariables;
using System;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Projectiles
{
[RegisterComponent]
public class ChemicalInjectionProjectileComponent : Component, ICollideBehavior
public class ChemicalInjectionProjectileComponent : Component, IStartCollide
{
public override string Name => "ChemicalInjectionProjectile";
@@ -33,9 +35,9 @@ namespace Content.Server.GameObjects.Components.Projectiles
_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;
var solution = _solutionContainer.Solution;

View File

@@ -1,10 +1,12 @@
using Content.Server.GameObjects.Components.Explosion;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Projectiles
{
[RegisterComponent]
public class ExplosiveProjectileComponent : Component, ICollideBehavior
public class ExplosiveProjectileComponent : Component, IStartCollide
{
public override string Name => "ExplosiveProjectile";
@@ -15,18 +17,12 @@ namespace Content.Server.GameObjects.Components.Projectiles
Owner.EnsureComponent<ExplosiveComponent>();
}
void ICollideBehavior.CollideWith(IEntity entity)
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
if (Owner.TryGetComponent(out ExplosiveComponent explosive))
{
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 Robust.Shared.GameObjects;
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
{
@@ -8,7 +11,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
/// Upon colliding with an object this will flash in an area around it
/// </summary>
[RegisterComponent]
public class FlashProjectileComponent : Component, ICollideBehavior
public class FlashProjectileComponent : Component, IStartCollide
{
public override string Name => "FlashProjectile";
@@ -26,20 +29,15 @@ namespace Content.Server.GameObjects.Components.Projectiles
Owner.EnsureComponent<ProjectileComponent>();
}
void ICollideBehavior.CollideWith(IEntity entity)
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
if (_flashed)
{
return;
}
FlashableComponent.FlashAreaHelper(Owner, _range, _duration);
_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.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
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 Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -13,7 +15,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Projectiles
{
[RegisterComponent]
public class ProjectileComponent : SharedProjectileComponent, ICollideBehavior
public class ProjectileComponent : SharedProjectileComponent, IStartCollide
{
protected override EntityUid Shooter => _shooter;
@@ -52,39 +54,27 @@ namespace Content.Server.GameObjects.Components.Projectiles
Dirty();
}
private bool _internalDeleteOnCollide;
/// <summary>
/// Applies the damage when our projectile collides with its victim
/// Applies the damage when our projectile collides with its victim
/// </summary>
/// <param name="entity"></param>
void ICollideBehavior.CollideWith(IEntity entity)
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
if (_damagedEntity)
{
return;
}
// 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;
}
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>())
{
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))
if (damage != null)
{
Owner.EntityManager.TryGetEntity(_shooter, out var shooter);
@@ -96,17 +86,14 @@ namespace Content.Server.GameObjects.Components.Projectiles
_damagedEntity = true;
}
if (!entity.Deleted && entity.TryGetComponent(out CameraRecoilComponent recoilComponent)
&& Owner.TryGetComponent(out IPhysicsComponent ownPhysics))
// Damaging it can delete it
if (!otherBody.Entity.Deleted && otherBody.Entity.TryGetComponent(out CameraRecoilComponent recoilComponent))
{
var direction = ownPhysics.LinearVelocity.Normalized;
var direction = ourBody.LinearVelocity.Normalized;
recoilComponent.Kick(direction);
}
}
void ICollideBehavior.PostCollide(int collideCount)
{
if (collideCount > 0 && DeleteOnCollide && _internalDeleteOnCollide) Owner.Delete();
Owner.Delete();
}
public override ComponentState GetComponentState(ICommonSession player)

View File

@@ -1,6 +1,9 @@
using Content.Server.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
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
{
@@ -8,7 +11,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
/// Adds stun when it collides with an entity
/// </summary>
[RegisterComponent]
public sealed class StunnableProjectileComponent : Component, ICollideBehavior
public sealed class StunnableProjectileComponent : Component, IStartCollide
{
public override string Name => "StunnableProjectile";
@@ -27,16 +30,14 @@ namespace Content.Server.GameObjects.Components.Projectiles
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.Knockdown(_knockdownAmount);
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 Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Pulling
{
@@ -31,15 +32,15 @@ namespace Content.Server.GameObjects.Components.Pulling
}
if (!user.HasComponent<ISharedHandsComponent>() ||
!user.TryGetComponent(out IPhysicsComponent? userPhysics) ||
!component.Owner.TryGetComponent(out IPhysicsComponent? targetPhysics) ||
targetPhysics.Anchored)
!user.TryGetComponent(out IPhysBody? userPhysics) ||
!component.Owner.TryGetComponent(out IPhysBody? targetPhysics) ||
targetPhysics.BodyType == BodyType.Static)
{
return;
}
data.Visibility = VerbVisibility.Visible;
data.Text = component.Puller == userPhysics
data.Text = component.Puller == userPhysics.Entity
? Loc.GetString("Stop pulling")
: Loc.GetString("Pull");
}

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -25,9 +26,9 @@ namespace Content.Server.GameObjects.Components.Rotatable
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."));
return;
@@ -42,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{
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;
return;
@@ -64,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{
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;
return;

View File

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

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.GameObjects.Components.Singularity
@@ -90,10 +90,12 @@ namespace Content.Server.GameObjects.Components.Singularity
/// <param name="toRepell">Entity to repell.</param>
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 containmentFieldRepellController = collidableComponent.EnsureController<ContainmentFieldRepellController>();
//var containmentFieldRepellController = collidableComponent.EnsureController<ContainmentFieldRepellController>();
if (!CanRepell(toRepell))
{
@@ -106,22 +108,22 @@ namespace Content.Server.GameObjects.Components.Singularity
{
if (repellFrom.Transform.WorldPosition.X.CompareTo(toRepell.Transform.WorldPosition.X) > 0)
{
containmentFieldRepellController.Repell(Direction.West, speed);
//containmentFieldRepellController.Repell(Direction.West, speed);
}
else
{
containmentFieldRepellController.Repell(Direction.East, speed);
//containmentFieldRepellController.Repell(Direction.East, speed);
}
}
else
{
if (repellFrom.Transform.WorldPosition.Y.CompareTo(toRepell.Transform.WorldPosition.Y) > 0)
{
containmentFieldRepellController.Repell(Direction.South, speed);
//containmentFieldRepellController.Repell(Direction.South, speed);
}
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.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.ViewVariables;
using Robust.Server.GameObjects;
using Robust.Shared.Physics.Collision;
namespace Content.Server.GameObjects.Components.Singularity
{
[RegisterComponent]
public class ContainmentFieldGeneratorComponent : Component, ICollideBehavior
public class ContainmentFieldGeneratorComponent : Component, IStartCollide
{
[Dependency] private readonly IPhysicsManager _physicsManager = null!;
@@ -117,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Singularity
var dirVec = direction.ToVec();
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();
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);
}

View File

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

View File

@@ -14,13 +14,15 @@ using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Dynamics.Shapes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.GameObjects.Components.Singularity
{
[RegisterComponent]
public class SingularityComponent : Component, ICollideBehavior
public class SingularityComponent : Component, IStartCollide
{
[Dependency] private readonly IRobustRandom _random = default!;
@@ -38,7 +40,6 @@ namespace Content.Server.GameObjects.Components.Singularity
_energy = value;
if (_energy <= 0)
{
if(_singularityController != null) _singularityController.LinearVelocity = Vector2.Zero;
_spriteComponent?.LayerSetVisible(0, false);
Owner.Delete();
@@ -75,7 +76,7 @@ namespace Content.Server.GameObjects.Components.Singularity
_spriteComponent?.LayerSetRSI(0, "Constructible/Power/Singularity/singularity_" + _level + ".rsi");
_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;
}
@@ -95,7 +96,6 @@ namespace Content.Server.GameObjects.Components.Singularity
_ => 0
};
private SingularityController? _singularityController;
private PhysicsComponent? _collidableComponent;
private SpriteComponent? _spriteComponent;
private RadiationPulseComponent? _radiationPulseComponent;
@@ -129,9 +129,6 @@ namespace Content.Server.GameObjects.Components.Singularity
Logger.Error("SingularityComponent was spawned without SpriteComponent");
}
_singularityController = _collidableComponent?.EnsureController<SingularityController>();
if(_singularityController!=null)_singularityController.ControlledComponent = _collidableComponent;
if (!Owner.TryGetComponent(out _radiationPulseComponent))
{
Logger.Error("SingularityComponent was spawned without RadiationPulseComponent");
@@ -140,60 +137,18 @@ namespace Content.Server.GameObjects.Components.Singularity
Level = 1;
}
public void Update()
public void Update(int seconds)
{
Energy -= EnergyDrain;
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);
Energy -= EnergyDrain * seconds;
}
private readonly List<IEntity> _previousPulledEntities = new();
public void CleanupPulledEntities()
void IStartCollide.CollideWith(IPhysBody ourBody, IPhysBody otherBody, in Manifold manifold)
{
foreach (var previousPulledEntity in _previousPulledEntities)
var otherEntity = otherBody.Entity;
if (otherEntity.TryGetComponent<IMapGridComponent>(out var mapGridComponent))
{
if(previousPulledEntity.Deleted) continue;
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))
foreach (var tile in mapGridComponent.Grid.GetTilesIntersecting(ourBody.GetWorldAABB()))
{
mapGridComponent.Grid.SetTile(tile.GridIndices, Tile.Empty);
Energy++;
@@ -201,14 +156,14 @@ namespace Content.Server.GameObjects.Components.Singularity
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;
}
if (entity.IsInContainer()) return;
if (otherEntity.IsInContainer()) return;
entity.Delete();
otherEntity.Delete();
Energy++;
}
@@ -216,7 +171,6 @@ namespace Content.Server.GameObjects.Components.Singularity
{
_playingSound?.Stop();
_audioSystem.PlayAtCoords("/Audio/Effects/singularity_collapse.ogg", Owner.Transform.Coordinates);
CleanupPulledEntities();
base.OnRemove();
}
}

View File

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

View File

@@ -1,5 +1,6 @@
using System;
using Content.Shared.GameObjects.Components.Weapons;
using Content.Shared.Physics;
using Content.Shared.Utility;
using Robust.Server.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)
{
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))
continue;
if (!entity.TryGetComponent(out FlashableComponent flashable) ||
!source.InRangeUnobstructed(entity, range, CollisionGroup.Opaque)) continue;
if(entity.TryGetComponent(out FlashableComponent flashable))
flashable.Flash(duration);
flashable.Flash(duration);
}
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.Physics;
using Robust.Shared.Prototypes;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -191,10 +192,13 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
for (var i = 0; i < increments; 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();
if (res.HitEntity != null)
var res = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(mapId,
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.Physics;
using Robust.Shared.Player;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
@@ -367,15 +368,14 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
projectileAngle = angle;
}
var physics = projectile.GetComponent<IPhysicsComponent>();
physics.Status = BodyStatus.InAir;
var physics = projectile.GetComponent<IPhysBody>();
physics.BodyStatus = BodyStatus.InAir;
var projectileComponent = projectile.GetComponent<ProjectileComponent>();
projectileComponent.IgnoreEntity(shooter);
projectile
.GetComponent<IPhysicsComponent>()
.EnsureController<BulletController>()
.GetComponent<IPhysBody>()
.LinearVelocity = projectileAngle.ToVec() * velocity;
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)
{
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();
if (rayCastResults.Count >= 1)

View File

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

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
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)
{
var collisionMask = 0;
if (entity.TryGetComponent(out IPhysicsComponent? physics))
if (entity.TryGetComponent(out IPhysBody? physics))
{
collisionMask = physics.CollisionMask;
}

View File

@@ -6,6 +6,7 @@ using Content.Server.GameObjects.Components.Doors;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
@@ -40,7 +41,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
GenerateMask();
}
public static bool IsRelevant(IEntity entity, IPhysicsComponent physicsComponent)
public static bool IsRelevant(IEntity entity, IPhysBody physicsComponent)
{
if (entity.Transform.GridID == GridId.Invalid ||
(PathfindingSystem.TrackedCollisionLayers & physicsComponent.CollisionLayer) == 0)
@@ -256,7 +257,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
/// <param name="entity"></param>
/// 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.
public void AddEntity(IEntity entity, IPhysicsComponent physicsComponent)
public void AddEntity(IEntity entity, IPhysBody physicsComponent)
{
// If we're a door
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);
if (!physicsComponent.Anchored)
if (physicsComponent.BodyType == BodyType.Static)
{
_physicsLayers.Add(entity, physicsComponent.CollisionLayer);
}

View File

@@ -11,6 +11,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
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 IEntityManager _entityManager = default!;
public IReadOnlyDictionary<GridId, Dictionary<Vector2i, PathfindingChunk>> Graph => _graph;
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)
{
var entity = EntityManager.GetEntity(update.Owner);
if (!EntityManager.TryGetEntity(update.Owner, out var entity)) continue;
if (update.CanCollide)
{
HandleEntityAdd(entity);
@@ -262,7 +264,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{
if (entity.Deleted ||
_lastKnownPositions.ContainsKey(entity) ||
!entity.TryGetComponent(out IPhysicsComponent physics) ||
!entity.TryGetComponent(out IPhysBody physics) ||
!PathfindingNode.IsRelevant(entity, physics))
{
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 (moveEvent.Sender.Deleted ||
!moveEvent.Sender.TryGetComponent(out IPhysicsComponent physics) ||
!moveEvent.Sender.TryGetComponent(out IPhysBody physics) ||
!PathfindingNode.IsRelevant(moveEvent.Sender, physics) ||
moveEvent.NewPosition.GetGridId(EntityManager) == GridId.Invalid)
{
@@ -366,7 +368,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
public bool CanTraverse(IEntity entity, PathfindingNode node)
{
if (entity.TryGetComponent(out IPhysicsComponent physics) &&
if (entity.TryGetComponent(out IPhysBody physics) &&
(physics.CollisionMask & node.BlockedCollisionMask) != 0)
{
return false;

View File

@@ -13,6 +13,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -413,7 +414,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
var startTile = gridManager.GetTileRef(entity.Transform.Coordinates);
var endTile = gridManager.GetTileRef(steeringRequest.TargetGrid);
var collisionMask = 0;
if (entity.TryGetComponent(out IPhysicsComponent physics))
if (entity.TryGetComponent(out IPhysBody physics))
{
collisionMask = physics.CollisionMask;
}
@@ -599,7 +600,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
return Vector2.Zero;
}
if (target.TryGetComponent(out IPhysicsComponent physics))
if (target.TryGetComponent(out IPhysBody physics))
{
var targetDistance = (targetPos.Position - entityPos.Position);
targetPos = targetPos.Offset(physics.LinearVelocity * targetDistance);
@@ -617,7 +618,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
/// <returns></returns>
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;
}
@@ -658,7 +659,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
// 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
// i.e. towards the right
if (physicsEntity.TryGetComponent(out IPhysicsComponent otherPhysics) &&
if (physicsEntity.TryGetComponent(out IPhysBody otherPhysics) &&
Vector2.Dot(otherPhysics.LinearVelocity, direction) > 0)
{
continue;

View File

@@ -1,4 +1,5 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Items.Storage;
@@ -25,6 +26,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems.Click
@@ -568,61 +570,12 @@ namespace Content.Server.GameObjects.EntitySystems.Click
}
var comps = thrown.GetAllComponents<IThrown>().ToList();
var args = new ThrownEventArgs(user);
// Call Thrown on all components that implement the interface
foreach (var comp in comps)
{
comp.Thrown(new ThrownEventArgs(user));
}
}
/// <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);
comp.Thrown(args);
}
}

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 Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems
{
[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)
{
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.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input;
using Content.Shared.Interfaces;
@@ -145,12 +145,12 @@ namespace Content.Server.GameObjects.EntitySystems
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;
if (!plyEnt.TryGetComponent(out HandsComponent handsComp))
if (!playerEnt.TryGetComponent(out HandsComponent handsComp))
return false;
if (!handsComp.CanDrop(handsComp.ActiveHand))
@@ -169,14 +169,19 @@ namespace Content.Server.GameObjects.EntitySystems
else
{
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.
if (throwEnt.TryGetComponent<StackComponent>(out var newStackComp))
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;
}

View File

@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Random;
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.
// 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 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())
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]
public class SingularitySystem : EntitySystem
{
private float curTimeSingulo;
private float curTimePull;
private float _accumulator;
public override void Update(float frameTime)
{
base.Update(frameTime);
curTimeSingulo += frameTime;
curTimePull += frameTime;
_accumulator += frameTime;
var shouldUpdate = curTimeSingulo >= 1f;
var shouldPull = curTimePull >= 0.2f;
if (!shouldUpdate && !shouldPull) return;
var singulos = ComponentManager.EntityQuery<SingularityComponent>(true);
if (curTimeSingulo >= 1f)
while (_accumulator > 1.0f)
{
curTimeSingulo -= 1f;
foreach (var singulo in singulos)
{
singulo.Update();
}
}
_accumulator -= 1.0f;
if (curTimePull >= 0.5f)
{
curTimePull -= 0.5f;
foreach (var singulo in singulos)
foreach (var singularity in ComponentManager.EntityQuery<SingularityComponent>())
{
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
using System.Collections.Generic;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.Components.Sound;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Tag;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Physics.Controllers;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -17,13 +19,15 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.GameObjects.EntitySystems
namespace Content.Server.Physics.Controllers
{
[UsedImplicitly]
internal class MoverSystem : SharedMoverSystem
public class MoverController : SharedMoverController
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
@@ -35,54 +39,83 @@ namespace Content.Server.GameObjects.EntitySystems
private const float StepSoundMoveDistanceRunning = 2;
private const float StepSoundMoveDistanceWalking = 1.5f;
/// <inheritdoc />
private HashSet<EntityUid> _excludedMobs = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PlayerDetachedSystemMessage>(PlayerDetached);
_audioSystem = EntitySystemManager.GetEntitySystem<AudioSystem>();
UpdatesBefore.Add(typeof(PhysicsSystem));
_audioSystem = EntitySystem.Get<AudioSystem>();
}
public override void Update(float frameTime)
public override void UpdateBeforeSolve(bool prediction, float frameTime)
{
foreach (var (moverComponent, collidableComponent) in EntityManager.ComponentManager
.EntityQuery<IMoverComponent, IPhysicsComponent>(false))
base.UpdateBeforeSolve(prediction, frameTime);
_excludedMobs.Clear();
foreach (var (mobMover, mover, physics) in ComponentManager.EntityQuery<IMobMoverComponent, IMoverComponent, PhysicsComponent>())
{
var entity = moverComponent.Owner;
UpdateKinematics(entity.Transform, moverComponent, collidableComponent);
_excludedMobs.Add(mover.Owner.Uid);
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) &&
physics.TryGetController(out MoverController controller) &&
!ev.Entity.IsWeightless())
var gridId = mover.Owner.Transform.GridID;
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;
// Handle footsteps.
if (_mapManager.GridExists(mover.LastPosition.GetGridId(EntityManager)))
if (_mapManager.GridExists(mobMover.LastPosition.GetGridId(EntityManager)))
{
// 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;
}
mover.StepSoundDistance += distance;
mobMover.StepSoundDistance += distance;
}
mover.LastPosition = transform.Coordinates;
mobMover.LastPosition = transform.Coordinates;
float distanceNeeded;
if (mover.Sprinting)
{
@@ -93,11 +126,11 @@ namespace Content.Server.GameObjects.EntitySystems
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;
}
@@ -110,13 +143,14 @@ namespace Content.Server.GameObjects.EntitySystems
}
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.
var grid = _mapManager.GetGrid(coordinates.GetGridId(EntityManager));
var tile = grid.GetTileRef(coordinates);
@@ -159,5 +193,6 @@ namespace Content.Server.GameObjects.EntitySystems
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;
}
}
/// <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
}
}

View File

@@ -166,6 +166,15 @@ namespace Content.Shared
public static readonly CVarDef<bool> ParallaxDebug =
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

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.Construction.ConstructionConditions
{
@@ -33,7 +34,7 @@ namespace Content.Shared.Construction.ConstructionConditions
return false;
// 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 length = userToObject.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 Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

View File

@@ -19,7 +19,7 @@ namespace Content.Shared.GameObjects.Components.Buckle
public sealed override uint? NetID => ContentNetIDs.BUCKLE;
[ComponentDependency] protected readonly IPhysicsComponent? Physics;
[ComponentDependency] protected readonly IPhysBody? Physics;
/// <summary>
/// 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]
public bool Anchored =>
!Owner.TryGetComponent(out IPhysicsComponent? physics) ||
physics.Anchored;
!Owner.TryGetComponent(out IPhysBody? physics) ||
physics.BodyType == BodyType.Static;
[Serializable, NetSerializable]
public enum Visuals
@@ -167,7 +167,7 @@ namespace Content.Shared.GameObjects.Components.Disposal
if (!Anchored)
return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide)
{
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;
[ComponentDependency]
protected readonly IPhysicsComponent? PhysicsComponent = null;
protected readonly IPhysBody? PhysicsComponent = null;
[ViewVariables]
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;
set
{
if (_isInCombatMode == value) return;
_isInCombatMode = value;
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;
set
{
if (_activeZone == value) return;
_activeZone = value;
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