Skip to content

Commit

Permalink
Improve the behavior of death planes (#257)
Browse files Browse the repository at this point in the history
* Add ObjectScript for death planes

* Fix size of death barrier objects

* Switch death plane to use SmashAsync

* Add initial water death plane script

* Switch smasher for death planes to death plane

* Remove comment in DeathPlane

* Merge and improve object script loading for LuaScriptComponent and custom scripts.

* Add player fall death script.

* Move death plane scripts.

* Add death (teleport) plane for properties.

* Correct player sphere physics to be at center instead of bottom. (Fix Portabello waters being too high)

* Disable check for hasCustomClientScript

* Disable fallback, replace FV death plane objectscript with nativescript

* Reformatting

* Add CapsuleBody (for cylinders)

Fixes NT big assembly teleporters

* Capsule-sphere collision test

* Clean up some code

* Replace client script names with server ones

* Run native DeathPlane script in multiple worlds

* Move PrimitiveModel cube origins from center to bottom

* Fix scripts linked to client script names

* Correctly split PATH env variable on *nix systems

* Apply binoculars script to all binoculars

* Always give all rewards for achievements

* Fix for ghost players after dying to death plane

* Fix Instantiate when item is not in Objects table

* Fix not dying after falling near Tortoise Terrace

* Add LOT-specific scripts.

Co-authored-by: TheNexusAvenger <[email protected]>
Co-authored-by: enteryournamehere <[email protected]>
  • Loading branch information
3 people authored Oct 1, 2021
1 parent c700cbc commit b5f877a
Show file tree
Hide file tree
Showing 25 changed files with 493 additions and 75 deletions.
2 changes: 1 addition & 1 deletion Uchu.Master/ServerInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void Start(string location, string dotnet)
var useDotNet = !string.IsNullOrWhiteSpace(dotnet);
if (useDotNet && dotnet?.ToLower() == "dotnet" && !File.Exists(dotnet))
{
var pathDirectories = (Environment.GetEnvironmentVariable("PATH") ?? "").Split(";");
var pathDirectories = (Environment.GetEnvironmentVariable("PATH") ?? "").Split(new[] { ';', ':' });
var dotNetInPath = pathDirectories.Any(pathDirectory => File.Exists(Path.Combine(pathDirectory, dotnet)));
if (!dotNetInPath)
{
Expand Down
34 changes: 34 additions & 0 deletions Uchu.Physics.Test/CollisionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,39 @@ public void BoxBoxCollision()
Assert.IsFalse(PhysicsSimulation.BoxBoxCollision(smallBox5, box7));
Assert.IsTrue(PhysicsSimulation.BoxBoxCollision(box9, box8Rotated));
}

[Test]
public void CapsuleSphereCollision()
{
// r=1.5 at (0,8,0)
var sphere = SphereBody.Create(_simulation,
new Vector3(0, 8, 0),
1.5f);

// pointing straight up, should hit sphere
var capsule1 = CapsuleBody.Create(this._simulation,
new Vector3(0, 0, 0),
Quaternion.Identity,
1f,
6f);

// rotated 45deg, should miss sphere
var capsule2 = CapsuleBody.Create(this._simulation,
new Vector3(0, 0, 0),
Quaternion.CreateFromAxisAngle(Vector3.UnitX, (float) (0.125 * Math.Tau)),
1f,
6f);

// floating, rotated 90deg, should hit sphere
var capsule3 = CapsuleBody.Create(this._simulation,
new Vector3(8, 8, 0),
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (float) (0.25 * Math.Tau)),
1f,
6f);

Assert.IsTrue(PhysicsSimulation.CapsuleSphereCollision(capsule1, sphere));
Assert.IsFalse(PhysicsSimulation.CapsuleSphereCollision(capsule2, sphere));
Assert.IsTrue(PhysicsSimulation.CapsuleSphereCollision(capsule3, sphere));
}
}
}
51 changes: 51 additions & 0 deletions Uchu.Physics/Objects/CapsuleBody.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Numerics;

namespace Uchu.Physics
{
public class CapsuleBody : PhysicsObject
{
/// <summary>
/// Value used to determine the order in which objects are passed to the collision detection functions.
/// </summary>
public override int CollisionPrecedence { get; } = 2;

/// <summary>
/// Creates a capsule body.
/// </summary>
/// <param name="simulation">The simulation to use.</param>
public CapsuleBody(PhysicsSimulation simulation) : base(simulation)
{
}

/// <summary>
/// Creates a capsule body.
/// </summary>
/// <param name="simulation">The simulation to use.</param>
/// <param name="position">The position of the body.</param>
/// <param name="rotation">The rotation of the body.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the cylinder part of the capsule.</param>
/// <returns>The capsule body that was created.</returns>
public static CapsuleBody Create(PhysicsSimulation simulation, Vector3 position, Quaternion rotation,
float radius, float height)
{
var obj = new CapsuleBody(simulation);
obj.Position = position;
obj.Rotation = rotation;
obj.Radius = radius;
obj.Height = height;
simulation.Register(obj);
return obj;
}

/// <summary>
/// Radius of the capsule.
/// </summary>
public float Radius;

/// <summary>
/// Height of the cylinder part of the capsule.
/// </summary>
public float Height;
}
}
70 changes: 62 additions & 8 deletions Uchu.Physics/PhysicsSimulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,20 @@ public static bool Collides(PhysicsObject firstObject, PhysicsObject secondObjec
}

// First precedence value >= second precedence value
// Cube = 1, sphere = 0

if (first is BoxBody box && second is SphereBody sphere)
return BoxSphereCollision(box, sphere);
if (first is SphereBody sphere1 && second is SphereBody sphere2)
return SphereSphereCollision(sphere1, sphere2);
if (first is BoxBody box1 && second is BoxBody box2)
return BoxBoxCollision(box1, box2);
// Capsule = 2, cube = 1, sphere = 0

if (first is BoxBody boxSphereFirst && second is SphereBody boxSphereSecond)
return BoxSphereCollision(boxSphereFirst, boxSphereSecond);
if (first is SphereBody sphereSphereFirst && second is SphereBody sphereSphereSecond)
return SphereSphereCollision(sphereSphereFirst, sphereSphereSecond);
if (first is BoxBody boxBoxFirst && second is BoxBody boxBoxSecond)
return BoxBoxCollision(boxBoxFirst, boxBoxSecond);
if (first is CapsuleBody capsuleSphereFirst && second is SphereBody capsuleSphereSecond)
return CapsuleSphereCollision(capsuleSphereFirst, capsuleSphereSecond);
if (first is CapsuleBody capsuleBoxFirst && second is BoxBody capsuleBoxSecond)
return CapsuleBoxCollision(capsuleBoxFirst, capsuleBoxSecond);
if (first is CapsuleBody capsuleCapsuleFirst && second is CapsuleBody capsuleCapsuleSecond)
return CapsuleCapsuleCollision(capsuleCapsuleFirst, capsuleCapsuleSecond);

throw new NotSupportedException();
}
Expand Down Expand Up @@ -144,6 +150,54 @@ public static bool SphereSphereCollision(SphereBody firstSphere, SphereBody seco
return distanceSquared < maxDistanceSquared;
}

/// <summary>
/// Determine whether a capsule and a sphere intersect.
/// </summary>
/// <param name="capsule">Capsule physics object</param>
/// <param name="sphere">Sphere physics object</param>
public static bool CapsuleSphereCollision(CapsuleBody capsule, SphereBody sphere)
{
// reference frame: capsule at (0, 0, 0), pointing in positive y direction
var sphereCoordsRelative = sphere.Position - capsule.Position;

// to transform coordinate system to have capsule be axis-aligned, we apply
// the reverse of the rotation to the sphere coordinates
var inverse = new Quaternion(capsule.Rotation.X, capsule.Rotation.Y, capsule.Rotation.Z, -capsule.Rotation.W);
var sphereCoordsTransformed = Vector3.Transform(sphereCoordsRelative, inverse);

// coordinates of the line in the centre of the cylinder part
const int capsuleMinY = 0;
var capsuleMaxY = capsule.Height;

const int closestPointToSphereX = 0;
var closestPointToSphereY = Math.Clamp(sphereCoordsTransformed.Y, capsuleMinY, capsuleMaxY);
const int closestPointToSphereZ = 0;

return Vector3.DistanceSquared(sphereCoordsTransformed,
new Vector3(closestPointToSphereX, closestPointToSphereY, closestPointToSphereZ))
< Math.Pow(capsule.Radius + sphere.Radius, 2);
}

/// <summary>
/// Determine whether two capsules intersect.
/// </summary>
/// <param name="firstCapsule">The first capsule</param>
/// <param name="secondCapsule">The second capsule</param>
public static bool CapsuleCapsuleCollision(CapsuleBody firstCapsule, CapsuleBody secondCapsule)
{
throw new NotImplementedException("Capsule-capsule collision checks are not implemented.");
}

/// <summary>
/// Determine whether a capsule and a box intersect.
/// </summary>
/// <param name="capsule">The capsule</param>
/// <param name="box">The box</param>
public static bool CapsuleBoxCollision(CapsuleBody capsule, BoxBody box)
{
throw new NotImplementedException("Capsule-box collision checks are not implemented.");
}

/// <summary>
/// Registers a physics object.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions Uchu.StandardScripts/AvantGardens/MaelstromSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Uchu.StandardScripts.AvantGardens
{
/// <summary>
/// Native implementation of scripts/02_client/map/ag/l_ag_maelstrom_sample.lua
/// Script to show/hide maelstrom samples based on whether the player has the relevant mission
/// </summary>
[ScriptName("l_ag_maelstrom_sample.lua")]
[LotSpecific(14718)]
public class MaelstromSample : ObjectScript
{
/// <summary>
Expand All @@ -21,4 +21,4 @@ public MaelstromSample(GameObject gameObject) : base(gameObject)
missionFilter.AddMissionIdToFilter(MissionId.SampleforScience);
}
}
}
}
6 changes: 3 additions & 3 deletions Uchu.StandardScripts/AvantGardens/MaelstromVacuum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
namespace Uchu.StandardScripts.AvantGardens
{
/// <summary>
/// Native implementation of scripts/02_client/Equipment/l_maelstrom_extracticator_client.lua
/// Script for spawned maelstrom vacuum (https://lu.lcdruniverse.org/explorer/objects/14596)
/// </summary>
[ScriptName("l_maelstrom_extracticator_client.lua")]
[ScriptName("ScriptComponent_1582_script_name__removed")]
public class MaelstromVacuum : ObjectScript
{
/// <summary>
Expand Down Expand Up @@ -57,4 +57,4 @@ public MaelstromVacuum(GameObject gameObject) : base(gameObject)
});
}
}
}
}
6 changes: 3 additions & 3 deletions Uchu.StandardScripts/AvantGardens/TargetFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Uchu.StandardScripts.AvantGardens
{
/// <summary>
/// Native implementation of scripts/02_client/map/ag/l_ag_plunger_target.lua
/// Script to show/hide targets based on whether the player has the relevant mission
/// </summary>
[ScriptName("l_ag_plunger_target.lua")]
[LotSpecific(14380)]
public class TargetFilter : ObjectScript
{
/// <summary>
Expand All @@ -20,4 +20,4 @@ public TargetFilter(GameObject gameObject) : base(gameObject)
missionFilter.AddMissionIdToFilter(MissionId.SixShooter);
}
}
}
}
11 changes: 8 additions & 3 deletions Uchu.StandardScripts/General/Binoculars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
namespace Uchu.StandardScripts.General
{
/// <summary>
/// Native implementation of scripts/02_client/map/general/l_binoculars_client.lua
/// Script to set flags for binoculars (e.g. https://lu.lcdruniverse.org/explorer/objects/6700) the player uses
/// </summary>
[ScriptName("l_binoculars_client.lua")]
[ScriptName("ScriptComponent_1002_script_name__removed")] // Binoculars, id 6700
[ScriptName("ScriptComponent_952_script_name__removed")] // AG - Spaceship Binoculars, id 6842
[ScriptName("ScriptComponent_975_script_name__removed")] // GF - Binoculars, id 6958
[ScriptName("ScriptComponent_1020_script_name__removed")] // PR - Binoculars, id 7607
[ScriptName("ScriptComponent_1021_script_name__removed")] // NS - Binoculars, id 7608
[ScriptName("ScriptComponent_1335_script_name__removed")] // FB - Binoculars, id 12306
public class Binoculars : ObjectScript
{
/// <summary>
Expand Down Expand Up @@ -40,4 +45,4 @@ public Binoculars(GameObject gameObject) : base(gameObject)
});
}
}
}
}
31 changes: 31 additions & 0 deletions Uchu.StandardScripts/General/DeathPlane/DeathPlane.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Threading.Tasks;
using Uchu.World;
using Uchu.World.Scripting.Native;

namespace Uchu.StandardScripts.General.DeathPlane
{
/// <summary>
/// Native implementation of scripts/ai/act/l_act_player_death_trigger.lua
/// </summary>
[ScriptName("l_act_player_death_trigger.lua")]
public class DeathPlane : ObjectScript
{
/// <summary>
/// Creates the object script.
/// </summary>
/// <param name="gameObject">Game object to control with the script.</param>
public DeathPlane(GameObject gameObject) : base(gameObject)
{
var physics = gameObject.GetComponent<PhysicsComponent>();
if (physics == default) return;
Listen(physics.OnEnter, other =>
{
if (!(other.GameObject is Player player)) return;
Task.Run(async () =>
{
await player.GetComponent<DestructibleComponent>().SmashAsync(gameObject);
});
});
}
}
}
32 changes: 32 additions & 0 deletions Uchu.StandardScripts/General/DeathPlane/PlayerFallDeath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Uchu.World;
using Uchu.World.Scripting.Native;

namespace Uchu.StandardScripts.General.DeathPlane
{
/// <summary>
/// Native implementation of scripts/ai/gf/l_player_fall_death.lua
/// </summary>
[ScriptName("l_player_fall_death.lua")]
public class PlayerFallDeath : ObjectScript
{
/// <summary>
/// Creates the object script.
/// </summary>
/// <param name="gameObject">Game object to control with the script.</param>
public PlayerFallDeath(GameObject gameObject) : base(gameObject)
{
var physics = gameObject.GetComponent<PhysicsComponent>();
if (physics == default) return;
Listen(physics.OnEnter, other =>
{
if (!(other.GameObject is Player player)) return;
Task.Run(async () =>
{
await Task.Delay(2000);
await player.GetComponent<DestructibleComponent>().SmashAsync(gameObject);
});
});
}
}
}
28 changes: 28 additions & 0 deletions Uchu.StandardScripts/General/DeathPlane/PropertyDeathPlane.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Uchu.World;
using Uchu.World.Scripting.Native;

namespace Uchu.StandardScripts.General.DeathPlane
{
/// <summary>
/// Native implementation of scripts/ai/ns/ns_pp_01/l_ns_pp_01_teleport.lua
/// </summary>
[ScriptName("l_ns_pp_01_teleport.lua")]
public class PropertyDeathPlane : ObjectScript
{
/// <summary>
/// Creates the object script.
/// </summary>
/// <param name="gameObject">Game object to control with the script.</param>
public PropertyDeathPlane(GameObject gameObject) : base(gameObject)
{
var physics = gameObject.GetComponent<PhysicsComponent>();
if (physics == default) return;
Listen(physics.OnEnter, other =>
{
if (!(other.GameObject is Player player)) return;
var teleportObject = this.GetGroup("Teleport")[0];
player.Teleport(teleportObject.Transform.Position, ignore: false);
});
}
}
}
Loading

0 comments on commit b5f877a

Please sign in to comment.