mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-12 06:55:10 +00:00
mechanism for server-driven emp caused by projectiles with emp properties; finalization of geometry elements and tests for geometric tests
This commit is contained in:
parent
e41e7e7cfa
commit
6e81ee7e95
11 changed files with 706 additions and 52 deletions
|
|
@ -5252,8 +5252,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
|
CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target)
|
||||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
|
ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match {
|
||||||
case Some(projectile) =>
|
case Some(_projectile) =>
|
||||||
HandleDealingDamage(target, projectile)
|
HandleDealingDamage(target, _projectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -5264,13 +5264,25 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||||
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
||||||
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
|
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match {
|
||||||
case Some(projectile) =>
|
case Some(_projectile) =>
|
||||||
HandleDealingDamage(target, projectile)
|
HandleDealingDamage(target, _projectile)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (
|
||||||
|
projectile.profile.HasJammedEffectDuration ||
|
||||||
|
projectile.profile.JammerProjectile ||
|
||||||
|
projectile.profile.SympatheticExplosion
|
||||||
|
) {
|
||||||
|
Zone.causeSpecialEmp(
|
||||||
|
continent,
|
||||||
|
player,
|
||||||
|
explosion_pos,
|
||||||
|
GlobalDefinitions.special_emp.innateDamage.get
|
||||||
|
)
|
||||||
|
}
|
||||||
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
|
||||||
//cleanup
|
//cleanup
|
||||||
val localIndex = projectile_guid.guid - Projectile.baseUID
|
val localIndex = projectile_guid.guid - Projectile.baseUID
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
|
||||||
mine,
|
mine,
|
||||||
DamageInteraction(
|
DamageInteraction(
|
||||||
SourceEntry(mine),
|
SourceEntry(mine),
|
||||||
TriggerUsedReason(PlayerSource(player), trigger),
|
TriggerUsedReason(PlayerSource(player), trigger.GUID),
|
||||||
mine.Position
|
mine.Position
|
||||||
).calculate()(mine),
|
).calculate()(mine),
|
||||||
damage = 0
|
damage = 0
|
||||||
|
|
@ -239,6 +239,10 @@ object ExplosiveDeployableControl {
|
||||||
val scalar = Vector3.ScalarProjection(dir, up)
|
val scalar = Vector3.ScalarProjection(dir, up)
|
||||||
val point1 = g1.pointOnOutside(dir).asVector3
|
val point1 = g1.pointOnOutside(dir).asVector3
|
||||||
val point2 = g2.pointOnOutside(Vector3.neg(dir)).asVector3
|
val point2 = g2.pointOnOutside(Vector3.neg(dir)).asVector3
|
||||||
(scalar >= 0 || Vector3.MagnitudeSquared(up * scalar) < 0.35f) && Vector3.DistanceSquared(point1, point2) <= maxDistance
|
(scalar >= 0 || Vector3.MagnitudeSquared(up * scalar) < 0.35f) &&
|
||||||
|
math.min(
|
||||||
|
Vector3.DistanceSquared(g1.center.asVector3, g2.center.asVector3),
|
||||||
|
Vector3.DistanceSquared(point1, point2)
|
||||||
|
) <= maxDistance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -972,6 +972,8 @@ object GlobalDefinitions {
|
||||||
|
|
||||||
val router_telepad_deployable = SimpleDeployableDefinition(DeployedItem.router_telepad_deployable)
|
val router_telepad_deployable = SimpleDeployableDefinition(DeployedItem.router_telepad_deployable)
|
||||||
|
|
||||||
|
val special_emp = ExplosiveDeployableDefinition(DeployedItem.jammer_mine)
|
||||||
|
|
||||||
//this is only treated like a deployable
|
//this is only treated like a deployable
|
||||||
val internal_router_telepad_deployable = InternalTelepadDefinition() //objectId: 744
|
val internal_router_telepad_deployable = InternalTelepadDefinition() //objectId: 744
|
||||||
init_deployables()
|
init_deployables()
|
||||||
|
|
@ -7293,6 +7295,22 @@ object GlobalDefinitions {
|
||||||
internal_router_telepad_deployable.DeployTime = Duration.create(1, "ms")
|
internal_router_telepad_deployable.DeployTime = Duration.create(1, "ms")
|
||||||
internal_router_telepad_deployable.DeployCategory = DeployableCategory.Telepads
|
internal_router_telepad_deployable.DeployCategory = DeployableCategory.Telepads
|
||||||
internal_router_telepad_deployable.Packet = new InternalTelepadDeployableConverter
|
internal_router_telepad_deployable.Packet = new InternalTelepadDeployableConverter
|
||||||
|
|
||||||
|
special_emp.Name = "emp"
|
||||||
|
special_emp.MaxHealth = 1
|
||||||
|
special_emp.Damageable = false
|
||||||
|
special_emp.Repairable = false
|
||||||
|
special_emp.DeployCategory = DeployableCategory.Mines
|
||||||
|
special_emp.explodes = true
|
||||||
|
special_emp.innateDamage = new DamageWithPosition {
|
||||||
|
CausesDamageType = DamageType.Splash
|
||||||
|
SympatheticExplosion = true
|
||||||
|
Damage0 = 0
|
||||||
|
DamageAtEdge = 1.0f
|
||||||
|
DamageRadius = 5f
|
||||||
|
AdditionalEffect = true
|
||||||
|
Modifiers = MaxDistanceCutoff
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -7844,12 +7862,11 @@ object GlobalDefinitions {
|
||||||
generator.innateDamage = new DamageWithPosition {
|
generator.innateDamage = new DamageWithPosition {
|
||||||
CausesDamageType = DamageType.One
|
CausesDamageType = DamageType.One
|
||||||
Damage0 = 99999
|
Damage0 = 99999
|
||||||
//DamageRadius should be 14, but 14 is insufficient for hitting the whole chamber; hence, ...
|
|
||||||
DamageRadius = 15.75f
|
|
||||||
DamageRadiusMin = 14
|
DamageRadiusMin = 14
|
||||||
|
DamageRadius = 14.5f
|
||||||
DamageAtEdge = 0.00002f
|
DamageAtEdge = 0.00002f
|
||||||
Modifiers = ExplodingRadialDegrade
|
Modifiers = ExplodingRadialDegrade
|
||||||
//damage is 99999 at 14m, dropping rapidly to ~1 at 15.75m
|
//damage is 99999 at 14m, dropping rapidly to ~1 at 14.5m
|
||||||
}
|
}
|
||||||
generator.Geometry = GeometryForm.representByCylinder(radius = 1.2617f, height = 9.14063f)
|
generator.Geometry = GeometryForm.representByCylinder(radius = 1.2617f, height = 9.14063f)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import net.psforever.types.Vector3
|
||||||
object Geometry {
|
object Geometry {
|
||||||
def equalFloats(value1: Float, value2: Float, off: Float = 0.001f): Boolean = {
|
def equalFloats(value1: Float, value2: Float, off: Float = 0.001f): Boolean = {
|
||||||
val diff = value1 - value2
|
val diff = value1 - value2
|
||||||
(diff >= 0 && diff <= off) || diff > -off
|
if (diff >= 0) diff <= off else diff > -off
|
||||||
}
|
}
|
||||||
|
|
||||||
def equalVectors(value1: Vector3, value2: Vector3, off: Float = 0.001f): Boolean = {
|
def equalVectors(value1: Vector3, value2: Vector3, off: Float = 0.001f): Boolean = {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,22 @@ package net.psforever.objects.geometry
|
||||||
|
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic interface for all geometry.
|
||||||
|
*/
|
||||||
trait PrimitiveGeometry {
|
trait PrimitiveGeometry {
|
||||||
|
/**
|
||||||
|
* The centroid of the geometry.
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
def center: Point
|
def center: Point
|
||||||
|
|
||||||
def pointOnOutside(line: Line) : Point = pointOnOutside(line.d)
|
/**
|
||||||
|
* Find a point on the exterior of the geometry if a line was drawn outwards from the centroid.
|
||||||
|
* What counts as "the exterior" is limited to the complexity of the geometry.
|
||||||
|
* @param v the vector in the direction of the point on the exterior
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
def pointOnOutside(v: Vector3) : Point
|
def pointOnOutside(v: Vector3) : Point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,43 +28,103 @@ trait PrimitiveGeometry {
|
||||||
// def pointOnOutside(v: Vector3): Point2D = center
|
// def pointOnOutside(v: Vector3): Point2D = center
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic interface of all three-dimensional geometry.
|
||||||
|
* For the only real requirement for a hree-dimensional geometric figure is that it has three components of position
|
||||||
|
* and an equal number of components demonstrating equal that said dimensionality.
|
||||||
|
*/
|
||||||
trait Geometry3D extends PrimitiveGeometry {
|
trait Geometry3D extends PrimitiveGeometry {
|
||||||
def center: Point3D
|
def center: Point3D
|
||||||
|
|
||||||
def pointOnOutside(v: Vector3): Point3D = center
|
def pointOnOutside(v: Vector3): Point3D = center
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Characteristics of a geometric figure with only three coordinates to define a position.
|
||||||
|
*/
|
||||||
trait Point {
|
trait Point {
|
||||||
|
/**
|
||||||
|
* Transform the point into the common interchangeable format for coordinates.
|
||||||
|
* They're very similar, anyway.
|
||||||
|
* @return a `Vector3` entity of the same denomination
|
||||||
|
*/
|
||||||
def asVector3: Vector3
|
def asVector3: Vector3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Characteristics of a geometric figure defining a direction or a progressive change in coordinates.
|
||||||
|
*/
|
||||||
trait Slope {
|
trait Slope {
|
||||||
|
/**
|
||||||
|
* The slope itself.
|
||||||
|
* @return a `Vector3` entity
|
||||||
|
*/
|
||||||
def d: Vector3
|
def d: Vector3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long the slope goes on for.
|
||||||
|
* @return The length of the slope
|
||||||
|
*/
|
||||||
def length: Float
|
def length: Float
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Slope {
|
||||||
|
/**
|
||||||
|
* On occasions, the defined slope should have a length of one unit.
|
||||||
|
* It is a unit vector.
|
||||||
|
* @param v the input slope as a `Vector3` entity
|
||||||
|
* @throws `AssertionError` if the length is more or less than 1.
|
||||||
|
*/
|
||||||
|
def assertUnitVector(v: Vector3): Unit = {
|
||||||
|
assert({
|
||||||
|
val mag = Vector3.Magnitude(v)
|
||||||
|
mag - 0.05f < 1f && mag + 0.05f > 1f
|
||||||
|
}, "not a unit vector")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Characteristics of a geometric figure indicating an infinite slope - a mathematical line.
|
||||||
|
* The slope is always a unit vector.
|
||||||
|
* The point that assists to define the line is a constraint that the line must pass through.
|
||||||
|
*/
|
||||||
trait Line extends Slope {
|
trait Line extends Slope {
|
||||||
assert({
|
Slope.assertUnitVector(d)
|
||||||
val mag = Vector3.Magnitude(d)
|
|
||||||
mag - 0.05f < 1f && mag + 0.05f > 1f
|
|
||||||
}, "not a unit vector")
|
|
||||||
|
|
||||||
def p: Point
|
def p: Point
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of a mathematical line is infinite.
|
||||||
|
* @return The length of the slope
|
||||||
|
*/
|
||||||
def length: Float = Float.PositiveInfinity
|
def length: Float = Float.PositiveInfinity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Characteristics of a geometric figure that have two endpoints, defining a fixed-length slope.
|
||||||
|
*/
|
||||||
trait Segment extends Slope {
|
trait Segment extends Slope {
|
||||||
|
/** The first point, considered the "start". */
|
||||||
def p1: Point
|
def p1: Point
|
||||||
|
/** The second point, considered the "end". */
|
||||||
def p2: Point
|
def p2: Point
|
||||||
|
|
||||||
def length: Float = Vector3.Magnitude(d)
|
def length: Float = Vector3.Magnitude(d)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the segment into a matheatical line of the same slope.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
def asLine: PrimitiveGeometry
|
def asLine: PrimitiveGeometry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance of a geometric coordinate position.
|
||||||
|
* @see `Vector3`
|
||||||
|
* @param x the 'x' coordinate of the position
|
||||||
|
* @param y the 'y' coordinate of the position
|
||||||
|
* @param z the 'z' coordinate of the position
|
||||||
|
*/
|
||||||
final case class Point3D(x: Float, y: Float, z: Float) extends Geometry3D with Point {
|
final case class Point3D(x: Float, y: Float, z: Float) extends Geometry3D with Point {
|
||||||
def center: Point3D = this
|
def center: Point3D = this
|
||||||
|
|
||||||
|
|
@ -61,39 +132,119 @@ final case class Point3D(x: Float, y: Float, z: Float) extends Geometry3D with P
|
||||||
}
|
}
|
||||||
|
|
||||||
object Point3D {
|
object Point3D {
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that assigns world origin coordinates.
|
||||||
|
* @return a `Point3D` entity
|
||||||
|
*/
|
||||||
def apply(): Point3D = Point3D(0,0,0)
|
def apply(): Point3D = Point3D(0,0,0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses the same coordinates from a `Vector3` entity.
|
||||||
|
* @param v the entity with the corresponding points
|
||||||
|
* @return a `Point3D` entity
|
||||||
|
*/
|
||||||
def apply(v: Vector3): Point3D = Point3D(v.x, v.y, v.z)
|
def apply(v: Vector3): Point3D = Point3D(v.x, v.y, v.z)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance of a geometric coordinate position and a specific direction from that position.
|
||||||
|
* Rays are like mathematical lines in that they have infinite length;
|
||||||
|
* but, that infinite length is only expressed in a single direction,
|
||||||
|
* rather than proceeding in both a direction and its opposite direction from a target point.
|
||||||
|
* Infinity just be like that.
|
||||||
|
* Additionally, the point is not merely any point on the ray used to assist defining it
|
||||||
|
* and is instead considered the clearly-defined origin of the ray.
|
||||||
|
* @param p the point of origin
|
||||||
|
* @param d the direction
|
||||||
|
*/
|
||||||
final case class Ray3D(p: Point3D, d: Vector3) extends Geometry3D with Line {
|
final case class Ray3D(p: Point3D, d: Vector3) extends Geometry3D with Line {
|
||||||
def center: Point3D = p
|
def center: Point3D = p
|
||||||
}
|
}
|
||||||
|
|
||||||
object Ray3D {
|
object Ray3D {
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses individual coordinates.
|
||||||
|
* @param x the 'x' coordinate of the position
|
||||||
|
* @param y the 'y' coordinate of the position
|
||||||
|
* @param z the 'z' coordinate of the position
|
||||||
|
* @param d the direction
|
||||||
|
* @return a `Ray3D` entity
|
||||||
|
*/
|
||||||
def apply(x: Float, y: Float, z: Float, d: Vector3): Ray3D = Ray3D(Point3D(x,y,z), d)
|
def apply(x: Float, y: Float, z: Float, d: Vector3): Ray3D = Ray3D(Point3D(x,y,z), d)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses a `Vector3` entity to express coordinates.
|
||||||
|
* @param v the coordinates of the position
|
||||||
|
* @param d the direction
|
||||||
|
* @return a `Ray3D` entity
|
||||||
|
*/
|
||||||
|
def apply(v: Vector3, d: Vector3): Ray3D = Ray3D(Point3D(v.x, v.y, v.z), d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance of a geometric coordinate position and a specific direction from that position.
|
||||||
|
* Mathematical lines have infinite length and their slope is represented as a unit vector.
|
||||||
|
* The point is merely a point used to assist in defining the line.
|
||||||
|
* @param p the point of origin
|
||||||
|
* @param d the direction
|
||||||
|
*/
|
||||||
final case class Line3D(p: Point3D, d: Vector3) extends Geometry3D with Line {
|
final case class Line3D(p: Point3D, d: Vector3) extends Geometry3D with Line {
|
||||||
def center: Point3D = p
|
def center: Point3D = p
|
||||||
}
|
}
|
||||||
|
|
||||||
object Line3D {
|
object Line3D {
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses individual coordinates.
|
||||||
|
* @param x the 'x' coordinate of the position
|
||||||
|
* @param y the 'y' coordinate of the position
|
||||||
|
* @param z the 'z' coordinate of the position
|
||||||
|
* @param d the direction
|
||||||
|
* @return a `Line3D` entity
|
||||||
|
*/
|
||||||
def apply(x: Float, y: Float, z: Float, d: Vector3): Line3D = {
|
def apply(x: Float, y: Float, z: Float, d: Vector3): Line3D = {
|
||||||
Line3D(Point3D(x,y,z), d)
|
Line3D(Point3D(x,y,z), d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses a pair of individual coordinates
|
||||||
|
* and uses their difference to produce a unit vector to define a direction.
|
||||||
|
* @param ax the 'x' coordinate of the position
|
||||||
|
* @param ay the 'y' coordinate of the position
|
||||||
|
* @param az the 'z' coordinate of the position
|
||||||
|
* @param bx the 'x' coordinate of a destination position
|
||||||
|
* @param by the 'y' coordinate of a destination position
|
||||||
|
* @param bz the 'z' coordinate of a destination position
|
||||||
|
* @return a `Line3D` entity
|
||||||
|
*/
|
||||||
def apply(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float): Line3D = {
|
def apply(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float): Line3D = {
|
||||||
Line3D(Point3D(ax, ay, az), Vector3.Unit(Vector3(bx-ax, by-ay, bz-az)))
|
Line3D(Point3D(ax, ay, az), Vector3.Unit(Vector3(bx-ax, by-ay, bz-az)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses a pair of points
|
||||||
|
* and uses their difference to produce a unit vector to define a direction.
|
||||||
|
* @param p1 the coordinates of the position
|
||||||
|
* @param p2 the coordinates of a destination position
|
||||||
|
* @return a `Line3D` entity
|
||||||
|
*/
|
||||||
def apply(p1: Point3D, p2: Point3D): Line3D = {
|
def apply(p1: Point3D, p2: Point3D): Line3D = {
|
||||||
Line3D(p1, Vector3.Unit(Vector3(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z)))
|
Line3D(p1, Vector3.Unit(Vector3(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance of a limited span between two geometric coordinate positions, called "endpoints".
|
||||||
|
* Unlike mathematical lines, slope is treated the same as the vector leading from one point to the other
|
||||||
|
* and is the length of the segment.
|
||||||
|
* @param p1 a point
|
||||||
|
* @param p2 another point
|
||||||
|
*/
|
||||||
final case class Segment3D(p1: Point3D, p2: Point3D) extends Geometry3D with Segment {
|
final case class Segment3D(p1: Point3D, p2: Point3D) extends Geometry3D with Segment {
|
||||||
def center: Point3D = Point3D(d * 0.5f)
|
/**
|
||||||
|
* The center point of a segment is a position that is equally in between both endpoints.
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
|
def center: Point3D = Point3D((p2.asVector3 + p1.asVector3) * 0.5f)
|
||||||
|
|
||||||
def d: Vector3 = p2.asVector3 - p1.asVector3
|
def d: Vector3 = p2.asVector3 - p1.asVector3
|
||||||
|
|
||||||
|
|
@ -101,65 +252,182 @@ final case class Segment3D(p1: Point3D, p2: Point3D) extends Geometry3D with Seg
|
||||||
}
|
}
|
||||||
|
|
||||||
object Segment3D {
|
object Segment3D {
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses a pair of individual coordinates
|
||||||
|
* and uses their difference to define a direction.
|
||||||
|
* @param ax the 'x' coordinate of the position
|
||||||
|
* @param ay the 'y' coordinate of the position
|
||||||
|
* @param az the 'z' coordinate of the position
|
||||||
|
* @param bx the 'x' coordinate of a destination position
|
||||||
|
* @param by the 'y' coordinate of a destination position
|
||||||
|
* @param bz the 'z' coordinate of a destination position
|
||||||
|
* @return a `Segment3D` entity
|
||||||
|
*/
|
||||||
def apply(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float): Segment3D = {
|
def apply(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float): Segment3D = {
|
||||||
Segment3D(Point3D(ax, ay, az), Point3D(bx, by, bz))
|
Segment3D(Point3D(ax, ay, az), Point3D(bx, by, bz))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor.
|
||||||
|
* @param p the point of origin
|
||||||
|
* @param d the direction and distance (of the second point)
|
||||||
|
*/
|
||||||
|
def apply(p: Point3D, d: Vector3): Segment3D = {
|
||||||
|
Segment3D(p, Point3D(p.x + d.x, p.y + d.y, p.z + d.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses individual coordinates.
|
||||||
|
* @param x the 'x' coordinate of the position
|
||||||
|
* @param y the 'y' coordinate of the position
|
||||||
|
* @param z the 'z' coordinate of the position
|
||||||
|
* @param d the direction
|
||||||
|
* @return a `Segment3D` entity
|
||||||
|
*/
|
||||||
def apply(x: Float, y: Float, z: Float, d: Vector3): Segment3D = {
|
def apply(x: Float, y: Float, z: Float, d: Vector3): Segment3D = {
|
||||||
Segment3D(Point3D(x, y, z), Point3D(x + d.x, y + d.y, z + d.z))
|
Segment3D(Point3D(x, y, z), Point3D(x + d.x, y + d.y, z + d.z))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance of a volumetric region that encapsulates all points within a certain distance of a central point.
|
||||||
|
* (That's what a sphere is.)
|
||||||
|
* A sphere has no real "top", "base", or "side" as all directions are described the same.
|
||||||
|
* @param p the point
|
||||||
|
* @param radius a distance that spans all points in any direction from the central point
|
||||||
|
*/
|
||||||
final case class Sphere(p: Point3D, radius: Float) extends Geometry3D {
|
final case class Sphere(p: Point3D, radius: Float) extends Geometry3D {
|
||||||
def center: Point3D = p
|
def center: Point3D = p
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a point on the exterior of the geometry if a line was drawn outwards from the centroid.
|
||||||
|
* All points that exist on the exterior of a sphere are on the surface of that sphere
|
||||||
|
* and are equally distant from the central point.
|
||||||
|
* @param v the vector in the direction of the point on the exterior
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
override def pointOnOutside(v: Vector3): Point3D = {
|
override def pointOnOutside(v: Vector3): Point3D = {
|
||||||
val slope = Vector3.Unit(v)
|
val slope = Vector3.Unit(v)
|
||||||
val mult = radius / math.sqrt(slope.x * slope.x + slope.y * slope.y + slope.z * slope.z)
|
val mult = radius / Vector3.Magnitude(slope)
|
||||||
val pointOnSurface = center.asVector3 + slope * mult.toFloat
|
Point3D(center.asVector3 + slope * mult)
|
||||||
Point3D(pointOnSurface.x, pointOnSurface.y, pointOnSurface.z)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Sphere {
|
object Sphere {
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that only defines the radius of the sphere
|
||||||
|
* and places it at the world origin.
|
||||||
|
* @param radius a distance around the world origin coordinates
|
||||||
|
* @return a `Sphere` entity
|
||||||
|
*/
|
||||||
def apply(radius: Float): Sphere = Sphere(Point3D(), radius)
|
def apply(radius: Float): Sphere = Sphere(Point3D(), radius)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses individual coordinates to define the central point.
|
||||||
|
* * @param x the 'x' coordinate of the position
|
||||||
|
* * @param y the 'y' coordinate of the position
|
||||||
|
* * @param z the 'z' coordinate of the position
|
||||||
|
* @param radius a distance around the world origin coordinates
|
||||||
|
* @return a `Sphere` entity
|
||||||
|
*/
|
||||||
def apply(x: Float, y: Float, z: Float, radius: Float): Sphere = Sphere(Point3D(x,y,z), radius)
|
def apply(x: Float, y: Float, z: Float, radius: Float): Sphere = Sphere(Point3D(x,y,z), radius)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses vector coordinates to define the central point.
|
||||||
|
* @param v the coordinates of the position
|
||||||
|
* @param radius a distance around the world origin coordinates
|
||||||
|
* @return a `Sphere` entity
|
||||||
|
*/
|
||||||
def apply(v: Vector3, radius: Float): Sphere = Sphere(Point3D(v), radius)
|
def apply(v: Vector3, radius: Float): Sphere = Sphere(Point3D(v), radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Cylinder(position: Vector3, relativeUp: Vector3, radius: Float, height: Float) extends Geometry3D {
|
/**
|
||||||
def center: Point3D = Point3D(position + relativeUp * height * 0.5f)
|
* The instance of a volumetric region that encapsulates all points within a certain distance of a central point.
|
||||||
|
* The region is characterized by a regular circular cross-section when observed from above or below
|
||||||
|
* and a flat top and a flat base when viewed from the side.
|
||||||
|
* The "base" is where the origin point is defined (at the center of a circular cross-section)
|
||||||
|
* and the "top" is discovered a `height` from the base along what the cylinder considers its `relativeUp` direction.
|
||||||
|
* @param p the point
|
||||||
|
* @param relativeUp what the cylinder considers its "up" direction
|
||||||
|
* @param radius a distance expressed in all circular cross-sections along the `relativeUp` direction
|
||||||
|
* @param height the distance between the "base" and the "top"
|
||||||
|
*/
|
||||||
|
final case class Cylinder(p: Point3D, relativeUp: Vector3, radius: Float, height: Float) extends Geometry3D {
|
||||||
|
Slope.assertUnitVector(relativeUp)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The center point of a cylinder is halfway between the "top" and the "base" along the direction of `relativeUp`.
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
|
def center: Point3D = Point3D(p.asVector3 + relativeUp * height * 0.5f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a point on the exterior of the geometry if a line was drawn outwards from the centroid.
|
||||||
|
* A cylinder is composed of three clearly-defined regions on its exterior -
|
||||||
|
* two flat but circular surfaces that are the "top" and the "base"
|
||||||
|
* and a wrapped "sides" surface that defines all points connecting the "base" to the "top"
|
||||||
|
* along the `relativeUp` direction.
|
||||||
|
* The requested point may exist on any of these surfaces.
|
||||||
|
* @param v the vector in the direction of the point on the exterior
|
||||||
|
* @return a point
|
||||||
|
*/
|
||||||
override def pointOnOutside(v: Vector3): Point3D = {
|
override def pointOnOutside(v: Vector3): Point3D = {
|
||||||
val centerVector = center.asVector3
|
val centerVector = center.asVector3
|
||||||
val slope = Vector3.Unit(v)
|
val slope = Vector3.Unit(v)
|
||||||
val acrossTopAndBase = slope - relativeUp
|
val dotProdOfSlopeAndUp = Vector3.DotProduct(slope, relativeUp)
|
||||||
val pointOnSide = centerVector + slope * (radius / Vector3.Magnitude(acrossTopAndBase))
|
if (Geometry.equalFloats(dotProdOfSlopeAndUp, value2 = 1) || Geometry.equalFloats(dotProdOfSlopeAndUp, value2 = -1)) {
|
||||||
val pointOnBase = position + acrossTopAndBase * radius
|
// very rare condition: 'slope' and 'relativeUp' are parallel or antiparallel
|
||||||
val pointOnTop = pointOnBase + relativeUp * height
|
Point3D(centerVector + slope * height * 0.5f)
|
||||||
val fromPointOnTopToSide = Vector3.Unit(pointOnTop - pointOnSide)
|
|
||||||
val fromPointOnSideToBase = Vector3.Unit(pointOnSide - pointOnBase)
|
|
||||||
val target = if(fromPointOnTopToSide == Vector3.Zero ||
|
|
||||||
fromPointOnSideToBase == Vector3.Zero ||
|
|
||||||
Geometry.equalVectors(fromPointOnTopToSide, fromPointOnSideToBase)) {
|
|
||||||
//on side, including top rim or base rim
|
|
||||||
pointOnSide
|
|
||||||
} else {
|
} else {
|
||||||
//on top or base
|
val acrossTopAndBase = slope - relativeUp * dotProdOfSlopeAndUp
|
||||||
// the full equation would be 'centerVector + slope * (height * 0.5f / Vector3.Magnitude(relativeUp))'
|
val pointOnSide = centerVector + slope * (radius / Vector3.Magnitude(acrossTopAndBase))
|
||||||
// 'relativeUp` is already a unit vector (magnitude of 1)
|
val pointOnBase = p.asVector3 + acrossTopAndBase * radius
|
||||||
centerVector + slope * height * 0.5f
|
val pointOnTop = pointOnBase + relativeUp * height
|
||||||
|
val fromPointOnTopToSide = Vector3.Unit(pointOnTop - pointOnSide)
|
||||||
|
val fromPointOnSideToBase = Vector3.Unit(pointOnSide - pointOnBase)
|
||||||
|
val target = if(Geometry.equalVectors(fromPointOnTopToSide, Vector3.Zero) ||
|
||||||
|
Geometry.equalVectors(fromPointOnSideToBase, Vector3.Zero) ||
|
||||||
|
Geometry.equalVectors(fromPointOnTopToSide, fromPointOnSideToBase)) {
|
||||||
|
//on side, including top rim or base rim
|
||||||
|
pointOnSide
|
||||||
|
} else {
|
||||||
|
//on top or base
|
||||||
|
// the full equation would be 'centerVector + slope * (height * 0.5f / Vector3.Magnitude(relativeUp))'
|
||||||
|
// 'relativeUp` is already a unit vector (magnitude of 1)
|
||||||
|
centerVector + slope * height * 0.5f
|
||||||
|
}
|
||||||
|
Point3D(target)
|
||||||
}
|
}
|
||||||
Point3D(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Cylinder {
|
object Cylinder {
|
||||||
def apply(v: Vector3, radius: Float, height: Float): Cylinder = Cylinder(v, Vector3(0,0,1), radius, height)
|
/**
|
||||||
|
* An overloaded constructor where the 'relativeUp' of the cylinder is perpendicular to the xy-plane.
|
||||||
|
* @param p the point
|
||||||
|
* @param radius a distance expressed in all circular cross-sections along the `relativeUp` direction
|
||||||
|
* @param height the distance between the "base" and the "top"
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def apply(p: Point3D, radius: Float, height: Float): Cylinder = Cylinder(p, Vector3(0,0,1), radius, height)
|
||||||
|
|
||||||
def apply(p: Point3D, radius: Float, height: Float): Cylinder = Cylinder(p.asVector3, Vector3(0,0,1), radius, height)
|
/**
|
||||||
|
* An overloaded constructor where the origin point is expressed as a vector
|
||||||
|
* and the 'relativeUp' of the cylinder is perpendicular to the xy-plane.
|
||||||
|
* @param p the point
|
||||||
|
* @param radius a distance expressed in all circular cross-sections along the `relativeUp` direction
|
||||||
|
* @param height the distance between the "base" and the "top"
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def apply(p: Vector3, radius: Float, height: Float): Cylinder = Cylinder(Point3D(p), Vector3(0,0,1), radius, height)
|
||||||
|
|
||||||
def apply(p: Point3D, v: Vector3, radius: Float, height: Float): Cylinder = Cylinder(p.asVector3, v, radius, height)
|
/**
|
||||||
|
* An overloaded constructor the origin point is expressed as a vector.
|
||||||
|
* @param p the point
|
||||||
|
* @param v what the cylinder considers its "up" direction
|
||||||
|
* @param radius a distance expressed in all circular cross-sections along the `relativeUp` direction
|
||||||
|
* @param height the distance between the "base" and the "top"
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def apply(p: Vector3, v: Vector3, radius: Float, height: Float): Cylinder = Cylinder(Point3D(p), v, radius, height)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) 2021 PSForever
|
||||||
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
|
import net.psforever.objects.PlanetSideGameObject
|
||||||
|
import net.psforever.objects.ballistics.SourceEntry
|
||||||
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
|
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||||
|
import net.psforever.objects.vital.Vitality
|
||||||
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
||||||
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
|
import net.psforever.objects.vital.resolution.DamageAndResistance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for a "damage source" in damage calculations
|
||||||
|
* that parameterizes information necessary to explain a server-driven electromagnetic pulse occurring.
|
||||||
|
* @see `VitalityDefinition.explodes`
|
||||||
|
* @see `VitalityDefinition.innateDamage`
|
||||||
|
* @see `Zone.causesSpecialEmp`
|
||||||
|
* @param entity the source of the explosive yield
|
||||||
|
* @param damageModel the model to be utilized in these calculations;
|
||||||
|
* typically, but not always, defined by the target
|
||||||
|
*/
|
||||||
|
final case class EmpReason(
|
||||||
|
entity: SourceEntry,
|
||||||
|
source: DamageWithPosition,
|
||||||
|
damageModel: DamageAndResistance,
|
||||||
|
override val attribution: Int
|
||||||
|
) extends DamageReason {
|
||||||
|
def resolution: DamageResolution.Value = DamageResolution.Splash
|
||||||
|
|
||||||
|
def same(test: DamageReason): Boolean = test match {
|
||||||
|
case eer: ExplodingEntityReason => eer.entity eq entity
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** lay the blame on that which caused this emp to occur */
|
||||||
|
def adversary: Option[SourceEntry] = Some(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
object EmpReason {
|
||||||
|
def apply(
|
||||||
|
owner: PlanetSideGameObject with FactionAffinity,
|
||||||
|
source: DamageWithPosition,
|
||||||
|
target: PlanetSideServerObject with Vitality
|
||||||
|
): EmpReason = {
|
||||||
|
EmpReason(SourceEntry(owner), source, target.DamageModel, owner.Definition.ObjectId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -73,12 +73,12 @@ case object ExplodingRadialDegrade extends ExplodingDamageModifiers.Mod {
|
||||||
def calculate(damage: Int, data: DamageInteraction, cause: ExplodingEntityReason): Int = {
|
def calculate(damage: Int, data: DamageInteraction, cause: ExplodingEntityReason): Int = {
|
||||||
cause.source match {
|
cause.source match {
|
||||||
case withPosition: DamageWithPosition =>
|
case withPosition: DamageWithPosition =>
|
||||||
val distance = math.sqrt(Zone.distanceCheck(
|
val radius = withPosition.DamageRadius
|
||||||
|
val radiusMin = withPosition.DamageRadiusMin
|
||||||
|
val distance = math.sqrt(Zone.distanceCheck(
|
||||||
cause.entity.Definition.asInstanceOf[ObjectDefinition].Geometry(cause.entity),
|
cause.entity.Definition.asInstanceOf[ObjectDefinition].Geometry(cause.entity),
|
||||||
data.target.Definition.Geometry(data.target)
|
data.target.Definition.Geometry(data.target)
|
||||||
))
|
))
|
||||||
val radius = withPosition.DamageRadius
|
|
||||||
val radiusMin = withPosition.DamageRadiusMin
|
|
||||||
if (distance <= radiusMin) {
|
if (distance <= radiusMin) {
|
||||||
damage
|
damage
|
||||||
} else if (distance <= radius) {
|
} else if (distance <= radius) {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
// Copyright (c) 2020 PSForever
|
// Copyright (c) 2020 PSForever
|
||||||
package net.psforever.objects.vital.etc
|
package net.psforever.objects.vital.etc
|
||||||
|
|
||||||
import net.psforever.objects.BoomerTrigger
|
import net.psforever.objects.GlobalDefinitions
|
||||||
import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
|
import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
|
import net.psforever.objects.vital.{NoResistanceSelection, SimpleResolutions}
|
||||||
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
import net.psforever.objects.vital.base.{DamageReason, DamageResolution}
|
||||||
import net.psforever.objects.vital.damage.DamageCalculations.AgainstExoSuit
|
import net.psforever.objects.vital.damage.DamageCalculations.AgainstExoSuit
|
||||||
import net.psforever.objects.vital.prop.DamageProperties
|
import net.psforever.objects.vital.prop.DamageProperties
|
||||||
import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
|
import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResistanceModel}
|
||||||
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper for a "damage source" in damage calculations
|
* A wrapper for a "damage source" in damage calculations
|
||||||
|
|
@ -23,16 +24,16 @@ import net.psforever.objects.vital.resolution.{DamageAndResistance, DamageResist
|
||||||
* @see `DamageCalculations`
|
* @see `DamageCalculations`
|
||||||
* @see `VitalityDefinition.DamageableByFriendlyFire`
|
* @see `VitalityDefinition.DamageableByFriendlyFire`
|
||||||
* @param user the player who is holding the trigger
|
* @param user the player who is holding the trigger
|
||||||
* @param item the trigger
|
* @param item_guid the trigger
|
||||||
*/
|
*/
|
||||||
final case class TriggerUsedReason(user: PlayerSource, item: BoomerTrigger)
|
final case class TriggerUsedReason(user: PlayerSource, item_guid: PlanetSideGUID)
|
||||||
extends DamageReason {
|
extends DamageReason {
|
||||||
def source: DamageProperties = TriggerUsedReason.triggered
|
def source: DamageProperties = TriggerUsedReason.triggered
|
||||||
|
|
||||||
def resolution: DamageResolution.Value = DamageResolution.Resolved
|
def resolution: DamageResolution.Value = DamageResolution.Resolved
|
||||||
|
|
||||||
def same(test: DamageReason): Boolean = test match {
|
def same(test: DamageReason): Boolean = test match {
|
||||||
case tur: TriggerUsedReason => tur.item eq item
|
case tur: TriggerUsedReason => tur.item_guid == item_guid && tur.user.Name.equals(user.Name)
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +44,7 @@ final case class TriggerUsedReason(user: PlayerSource, item: BoomerTrigger)
|
||||||
|
|
||||||
/** while weird, the trigger was accredited as the method of death on Gemini Live;
|
/** while weird, the trigger was accredited as the method of death on Gemini Live;
|
||||||
* even though its icon looks like an misshapen AMS */
|
* even though its icon looks like an misshapen AMS */
|
||||||
override def attribution: Int = item.Definition.ObjectId
|
override def attribution: Int = GlobalDefinitions.boomer_trigger.ObjectId
|
||||||
}
|
}
|
||||||
|
|
||||||
object TriggerUsedReason {
|
object TriggerUsedReason {
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ import net.psforever.objects.geometry.Geometry3D
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||||
import net.psforever.objects.vehicles.UtilityType
|
import net.psforever.objects.vehicles.UtilityType
|
||||||
import net.psforever.objects.vital.etc.ExplodingEntityReason
|
import net.psforever.objects.vital.etc.{EmpReason, ExplodingEntityReason}
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A server object representing the one-landmass planets as well as the individual subterranean caverns.<br>
|
* A server object representing the one-landmass planets as well as the individual subterranean caverns.<br>
|
||||||
|
|
@ -1172,6 +1173,64 @@ object Zone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates `Damageable` targets within the radius of a server-prepared electromagnetic pulse
|
||||||
|
* and informs those entities that they have affected by the aforementioned pulse.
|
||||||
|
* Targets within the effect radius within other rooms are affected, unlike with normal damage.
|
||||||
|
* The only affected target is Boomer deployables.
|
||||||
|
* @see `Amenity.Owner`
|
||||||
|
* @see `BoomerDeployable`
|
||||||
|
* @see `DamageInteraction`
|
||||||
|
* @see `DamageResult`
|
||||||
|
* @see `DamageWithPosition`
|
||||||
|
* @see `EmpReason`
|
||||||
|
* @see `Zone.DeployableList`
|
||||||
|
* @param zone the zone in which the emp should occur
|
||||||
|
* @param obj the entity that triggered the emp (information)
|
||||||
|
* @param sourcePosition where the emp physically originates
|
||||||
|
* @param effect characteristics of the emp produced
|
||||||
|
* @param detectionTest a custom test to determine if any given target is affected;
|
||||||
|
* defaults to an internal test for simple radial proximity
|
||||||
|
* @return a list of affected entities
|
||||||
|
*/
|
||||||
|
def causeSpecialEmp(
|
||||||
|
zone: Zone,
|
||||||
|
obj: PlanetSideServerObject with Vitality,
|
||||||
|
sourcePosition: Vector3,
|
||||||
|
effect: DamageWithPosition,
|
||||||
|
detectionTest: (PlanetSideGameObject, PlanetSideGameObject, Float) => Boolean = distanceCheck
|
||||||
|
): List[PlanetSideServerObject] = {
|
||||||
|
val proxy: ExplosiveDeployable = {
|
||||||
|
//construct a proxy unit to represent the pulse
|
||||||
|
val o = new ExplosiveDeployable(GlobalDefinitions.special_emp)
|
||||||
|
o.Owner = Some(obj.GUID)
|
||||||
|
o.OwnerName = obj match {
|
||||||
|
case p: Player => p.Name
|
||||||
|
case o: OwnableByPlayer => o.OwnerName.getOrElse("")
|
||||||
|
case _ => ""
|
||||||
|
}
|
||||||
|
o.Position = sourcePosition
|
||||||
|
o.Faction = obj.Faction
|
||||||
|
o
|
||||||
|
}
|
||||||
|
val radius = effect.DamageRadius * effect.DamageRadius
|
||||||
|
//only boomers can be affected (that's why it's special)
|
||||||
|
val allAffectedTargets = zone.DeployableList
|
||||||
|
.collect { case o: BoomerDeployable if !o.Destroyed && (o ne obj) && detectionTest(proxy, o, radius) => o }
|
||||||
|
//inform targets that they have suffered the effects of the emp
|
||||||
|
allAffectedTargets
|
||||||
|
.foreach { target =>
|
||||||
|
target.Actor ! Vitality.Damage(
|
||||||
|
DamageInteraction(
|
||||||
|
SourceEntry(target),
|
||||||
|
EmpReason(obj, effect, target),
|
||||||
|
sourcePosition
|
||||||
|
).calculate()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
allAffectedTargets
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two game entities are considered "near" each other if they are within a certain distance of one another.
|
* Two game entities are considered "near" each other if they are within a certain distance of one another.
|
||||||
* A default function literal mainly used for `causesExplosion`.
|
* A default function literal mainly used for `causesExplosion`.
|
||||||
|
|
@ -1197,6 +1256,7 @@ object Zone {
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def distanceCheck(g1: Geometry3D, g2: Geometry3D, maxDistance: Float): Boolean = {
|
def distanceCheck(g1: Geometry3D, g2: Geometry3D, maxDistance: Float): Boolean = {
|
||||||
|
Vector3.DistanceSquared(g1.center.asVector3, g2.center.asVector3) <= maxDistance ||
|
||||||
distanceCheck(g1, g2) <= maxDistance
|
distanceCheck(g1, g2) <= maxDistance
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ object PointOfInterest {
|
||||||
"anguta" -> Vector3(3999, 4170, 266),
|
"anguta" -> Vector3(3999, 4170, 266),
|
||||||
"igaluk" -> Vector3(3241, 5658, 235),
|
"igaluk" -> Vector3(3241, 5658, 235),
|
||||||
"keelut" -> Vector3(3630, 1904, 265),
|
"keelut" -> Vector3(3630, 1904, 265),
|
||||||
"nerrivik" -> Vector3(3522, 3703, 322),
|
"nerrivik" -> Vector3(3522, 3703, 222),
|
||||||
"pinga" -> Vector3(5938, 3545, 96),
|
"pinga" -> Vector3(5938, 3545, 96),
|
||||||
"sedna" -> Vector3(3932, 5160, 232),
|
"sedna" -> Vector3(3932, 5160, 232),
|
||||||
"tarqaq" -> Vector3(2980, 2155, 237),
|
"tarqaq" -> Vector3(2980, 2155, 237),
|
||||||
|
|
|
||||||
244
src/test/scala/objects/GeometryTest.scala
Normal file
244
src/test/scala/objects/GeometryTest.scala
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
// Copyright (c) 2021 PSForever
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import net.psforever.objects.geometry._
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import org.specs2.mutable.Specification
|
||||||
|
|
||||||
|
class GeometryTest extends Specification {
|
||||||
|
"Point3D" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Point3D(1,2,3.5f)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Point3D() mustEqual Point3D(0,0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Point3D(Vector3(1,2,3)) mustEqual Point3D(1,2,3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"be its own center point" in {
|
||||||
|
val obj = Point3D(1,2,3.5f)
|
||||||
|
obj.center mustEqual obj
|
||||||
|
}
|
||||||
|
|
||||||
|
"define its own exterior" in {
|
||||||
|
val obj = Point3D(1,2,3.5f)
|
||||||
|
obj.pointOnOutside(Vector3(1,0,0)) mustEqual obj
|
||||||
|
obj.pointOnOutside(Vector3(0,1,0)) mustEqual obj
|
||||||
|
obj.pointOnOutside(Vector3(0,0,1)) mustEqual obj
|
||||||
|
}
|
||||||
|
|
||||||
|
"convert to Vector3" in {
|
||||||
|
val obj = Point3D(1,2,3.5f)
|
||||||
|
obj.asVector3 mustEqual Vector3(1,2,3.5f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Ray3D" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Ray3D(Point3D(1,2,3.5f), Vector3(1,0,0))
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Ray3D(1,2,3.5f, Vector3(1,0,0)) mustEqual Ray3D(Point3D(1,2,3.5f), Vector3(1,0,0))
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Ray3D(Vector3(1,2,3.5f), Vector3(1,0,0)) mustEqual Ray3D(Point3D(1,2,3.5f), Vector3(1,0,0))
|
||||||
|
}
|
||||||
|
|
||||||
|
"have a unit vector as its direction vector" in {
|
||||||
|
Ray3D(1,2,3.5f, Vector3(1,1,1)) must throwA[AssertionError]
|
||||||
|
}
|
||||||
|
|
||||||
|
"have its target point as the center point" in {
|
||||||
|
val obj = Ray3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
obj.center mustEqual Point3D(1,2,3.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
"define its own exterior" in {
|
||||||
|
val obj1 = Ray3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
val obj2 = Point3D(1,2,3.5f)
|
||||||
|
obj1.pointOnOutside(Vector3(1,0,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,1,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,0,1)) mustEqual obj2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Line3D" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Line3D(Point3D(1,2,3.5f), Vector3(1,0,0))
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Line3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Line3D(1,2,3.5f, 2,2,3.5f) mustEqual Line3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
}
|
||||||
|
|
||||||
|
"have a unit vector as its direction vector" in {
|
||||||
|
Line3D(1,2,3.5f, Vector3(1,1,1)) must throwA[AssertionError]
|
||||||
|
}
|
||||||
|
|
||||||
|
"have its target point as the center point" in {
|
||||||
|
val obj = Line3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
obj.center mustEqual Point3D(1,2,3.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
"define its own exterior" in {
|
||||||
|
val obj1 = Line3D(1,2,3.5f, Vector3(1,0,0))
|
||||||
|
val obj2 = Point3D(1,2,3.5f)
|
||||||
|
obj1.pointOnOutside(Vector3(1,0,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,1,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,0,1)) mustEqual obj2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Segment3D" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Segment3D(Point3D(1,2,3), Point3D(3,2,3))
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Segment3D(1,2,3, 3,2,3) mustEqual Segment3D(Point3D(1,2,3), Point3D(3,2,3))
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Segment3D(Point3D(1,2,3), Vector3(1,0,0)) mustEqual Segment3D(Point3D(1,2,3), Point3D(2,2,3))
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (4)" in {
|
||||||
|
Segment3D(1,2,3, Vector3(1,0,0)) mustEqual Segment3D(Point3D(1,2,3), Point3D(2,2,3))
|
||||||
|
}
|
||||||
|
|
||||||
|
"does not need to have unit vector as its direction vector" in {
|
||||||
|
val obj1 = Segment3D(1,2,3, Vector3(5,1,1))
|
||||||
|
val obj2 = Segment3D(Point3D(1,2,3), Point3D(6,3,4))
|
||||||
|
obj1 mustEqual obj2
|
||||||
|
obj1.d mustEqual obj2.d
|
||||||
|
}
|
||||||
|
|
||||||
|
"have a midway point between its two endpoints" in {
|
||||||
|
Segment3D(Point3D(1,2,3), Point3D(3,4,5)).center mustEqual Point3D(2,3,4)
|
||||||
|
}
|
||||||
|
|
||||||
|
"report the point on the outside as its center point" in {
|
||||||
|
val obj1 = Segment3D(Point3D(1,2,3), Point3D(3,4,5))
|
||||||
|
val obj2 = obj1.center
|
||||||
|
obj1.pointOnOutside(Vector3(1,0,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,1,0)) mustEqual obj2
|
||||||
|
obj1.pointOnOutside(Vector3(0,0,1)) mustEqual obj2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Sphere3D" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Sphere(Point3D(1,2,3), 3)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Sphere(3) mustEqual Sphere(Point3D(0,0,0), 3)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Sphere(1,2,3, 3) mustEqual Sphere(Point3D(1,2,3), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (4)" in {
|
||||||
|
Sphere(Vector3(1,2,3), 3) mustEqual Sphere(Point3D(1,2,3), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"the center point is self-evident" in {
|
||||||
|
Sphere(Point3D(1,2,3), 3).center mustEqual Point3D(1,2,3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"report the point on the outside depending on the requested direction" in {
|
||||||
|
val obj1 = Sphere(1,2,3, 3)
|
||||||
|
obj1.pointOnOutside(Vector3( 1, 0, 0)) mustEqual Point3D( 4, 2,3) //east
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 1, 0)) mustEqual Point3D( 1, 5,3) //north
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0, 1)) mustEqual Point3D( 1, 2,6) //up
|
||||||
|
obj1.pointOnOutside(Vector3(-1, 0, 0)) mustEqual Point3D(-2, 2,3) //west
|
||||||
|
obj1.pointOnOutside(Vector3( 0,-1, 0)) mustEqual Point3D( 1,-1,3) //south
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0,-1)) mustEqual Point3D( 1, 2,0) //down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Cylinder (normal)" should {
|
||||||
|
"construct (1)" in {
|
||||||
|
Cylinder(Point3D(1,2,3), Vector3(0,0,1), 2, 3)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (2)" in {
|
||||||
|
Cylinder(Point3D(1,2,3), 2, 3) mustEqual Cylinder(Point3D(1,2,3), Vector3(0,0,1), 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (3)" in {
|
||||||
|
Cylinder(Vector3(1,2,3), 2, 3) mustEqual Cylinder(Point3D(1,2,3), Vector3(0,0,1), 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"construct (4)" in {
|
||||||
|
Cylinder(Vector3(1,2,3), Vector3(0,0,1), 2, 3) mustEqual Cylinder(Point3D(1,2,3), Vector3(0,0,1), 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"report the center point as the center of the cylinder" in {
|
||||||
|
Cylinder(Point3D(1,2,3), 2, 3).center mustEqual Point3D(1,2,4.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
"the point on the outside is different depending on the requested direction" in {
|
||||||
|
val obj1 = Cylinder(Point3D(1,2,3), 2, 3)
|
||||||
|
obj1.pointOnOutside(Vector3( 1, 0, 0)) mustEqual Point3D( 3, 2, 4.5f) //east
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 1, 0)) mustEqual Point3D( 1, 4, 4.5f) //north
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0, 1)) mustEqual Point3D( 1, 2, 6f) //up
|
||||||
|
obj1.pointOnOutside(Vector3(-1, 0, 0)) mustEqual Point3D(-1, 2, 4.5f) //west
|
||||||
|
obj1.pointOnOutside(Vector3( 0,-1, 0)) mustEqual Point3D( 1, 0, 4.5f) //south
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0,-1)) mustEqual Point3D( 1, 2, 3f) //down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Cylinder (side tilt)" should {
|
||||||
|
"not require a specific direction to be relative up" in {
|
||||||
|
Cylinder(Point3D(1,2,3), Vector3(1,0,0), 2, 3)
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
|
||||||
|
"require its specific relative up direction to be expressed as a unit vector" in {
|
||||||
|
Cylinder(Point3D(1,2,3), Vector3(4,0,0), 2, 3) must throwA[AssertionError]
|
||||||
|
}
|
||||||
|
|
||||||
|
"report the center point as the center of the cylinder, as if rotated about its base" in {
|
||||||
|
Cylinder(Point3D(1,2,3), Vector3(1,0,0), 2, 3).center mustEqual Point3D(2.5f, 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
"report the point on the outside as different depending on the requested direction and the relative up direction" in {
|
||||||
|
val obj1 = Cylinder(Point3D(1,2,3), Vector3(1,0,0), 2, 3)
|
||||||
|
obj1.pointOnOutside(Vector3( 1, 0, 0)) mustEqual Point3D(4, 2, 3) //east
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 1, 0)) mustEqual Point3D(2.5f, 4, 3) //north
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0, 1)) mustEqual Point3D(2.5f, 2, 5) //up
|
||||||
|
obj1.pointOnOutside(Vector3(-1, 0, 0)) mustEqual Point3D(1, 2, 3) //west
|
||||||
|
obj1.pointOnOutside(Vector3( 0,-1, 0)) mustEqual Point3D(2.5f, 0, 3) //south
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0,-1)) mustEqual Point3D(2.5f, 2, 1) //down
|
||||||
|
|
||||||
|
val obj2 = Cylinder(Point3D(1,2,3), Vector3(0,0,1), 2, 3)
|
||||||
|
obj1.pointOnOutside(Vector3( 1, 0, 0)) mustNotEqual obj2.pointOnOutside(Vector3( 1, 0, 0))
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 1, 0)) mustNotEqual obj2.pointOnOutside(Vector3( 1, 1, 0))
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0, 1)) mustNotEqual obj2.pointOnOutside(Vector3( 1, 0, 1))
|
||||||
|
obj1.pointOnOutside(Vector3(-1, 0, 0)) mustNotEqual obj2.pointOnOutside(Vector3(-1, 0, 0))
|
||||||
|
obj1.pointOnOutside(Vector3( 0,-1, 0)) mustNotEqual obj2.pointOnOutside(Vector3( 0,-1, 0))
|
||||||
|
obj1.pointOnOutside(Vector3( 0, 0,-1)) mustNotEqual obj2.pointOnOutside(Vector3( 0, 0,-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue