mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
projectiles that, by definition, are allowed to damage targets through walls (radiation_cloud, with DamageThroughWalls) regardless of sidedness; any unflagged radiation_cloud must be on the side as its target; fixed chat bang-commands not being executed
This commit is contained in:
parent
b5d60a7f9e
commit
4508c1ae45
12 changed files with 266 additions and 60 deletions
|
|
@ -224,6 +224,10 @@ class ChatActor(
|
||||||
session.account.gm || Config.app.development.unprivilegedGmCommands.contains(message.messageType)
|
session.account.gm || Config.app.development.unprivilegedGmCommands.contains(message.messageType)
|
||||||
|
|
||||||
(message.messageType, message.recipient.trim, message.contents.trim) match {
|
(message.messageType, message.recipient.trim, message.contents.trim) match {
|
||||||
|
/** Messages starting with ! are custom chat commands */
|
||||||
|
case (_, _, contents) if contents.startsWith("!") &&
|
||||||
|
customCommandMessages(message, session, chatService, cluster, gmCommandAllowed) => ()
|
||||||
|
|
||||||
case (CMT_FLY, recipient, contents) if gmCommandAllowed =>
|
case (CMT_FLY, recipient, contents) if gmCommandAllowed =>
|
||||||
val flying = contents match {
|
val flying = contents match {
|
||||||
case "on" => true
|
case "on" => true
|
||||||
|
|
@ -899,10 +903,6 @@ class ChatActor(
|
||||||
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, 0, 138, 0, 138, contents.toInt)
|
ZonePopulationUpdateMessage(4, 414, 138, 0, 138, 0, 138, 0, 138, contents.toInt)
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Messages starting with ! are custom chat commands */
|
|
||||||
case (_, _, contents) if contents.startsWith("!") &&
|
|
||||||
customCommandMessages(message, session, chatService, cluster, gmCommandAllowed) => ;
|
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"Unhandled chat message $message")
|
log.warn(s"Unhandled chat message $message")
|
||||||
}
|
}
|
||||||
|
|
@ -1126,7 +1126,7 @@ class ChatActor(
|
||||||
)
|
)
|
||||||
true
|
true
|
||||||
|
|
||||||
} else if (contents.startsWith("!loc ")) {
|
} else if (contents.startsWith("!loc")) {
|
||||||
val continent = session.zone
|
val continent = session.zone
|
||||||
val player = session.player
|
val player = session.player
|
||||||
val loc =
|
val loc =
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package net.psforever.actors.session.support
|
||||||
|
|
||||||
import akka.actor.{ActorContext, typed}
|
import akka.actor.{ActorContext, typed}
|
||||||
import net.psforever.objects.definition.ProjectileDefinition
|
import net.psforever.objects.definition.ProjectileDefinition
|
||||||
|
import net.psforever.objects.serverobject.doors.InteriorDoorPassage
|
||||||
|
import net.psforever.objects.serverobject.interior.Sidedness
|
||||||
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
import net.psforever.objects.serverobject.turret.auto.{AutomatedTurret, AutomatedTurretBehavior}
|
||||||
import net.psforever.objects.zones.Zoning
|
import net.psforever.objects.zones.Zoning
|
||||||
import net.psforever.objects.serverobject.turret.VanuSentry
|
import net.psforever.objects.serverobject.turret.VanuSentry
|
||||||
|
|
@ -303,11 +305,11 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
//find target(s)
|
//find target(s)
|
||||||
(hit_info match {
|
(hit_info match {
|
||||||
case Some(hitInfo) =>
|
case Some(hitInfo) =>
|
||||||
val hitPos = hitInfo.hit_pos
|
val hitPos = hitInfo.hit_pos
|
||||||
sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
|
sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
|
||||||
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
|
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
|
||||||
val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius
|
val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius
|
||||||
val targets = Zone.findAllTargets(hitPos)(continent, player, projectile.profile)
|
val targets = Zone.findAllTargets(continent, player, hitPos, projectile.profile)
|
||||||
.filter { target =>
|
.filter { target =>
|
||||||
Vector3.DistanceSquared(target.Position, hitPos) <= radius
|
Vector3.DistanceSquared(target.Position, hitPos) <= radius
|
||||||
}
|
}
|
||||||
|
|
@ -361,7 +363,6 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
FindProjectileEntry(projectile_guid) match {
|
FindProjectileEntry(projectile_guid) match {
|
||||||
case Some(projectile) =>
|
case Some(projectile) =>
|
||||||
val profile = projectile.profile
|
val profile = projectile.profile
|
||||||
projectile.Position = explosion_pos
|
|
||||||
projectile.Velocity = projectile_vel
|
projectile.Velocity = projectile_vel
|
||||||
val (resolution1, resolution2) = profile.Aggravated match {
|
val (resolution1, resolution2) = profile.Aggravated match {
|
||||||
case Some(_) if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
|
case Some(_) if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
|
||||||
|
|
@ -372,7 +373,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
//direct_victim_uid
|
//direct_victim_uid
|
||||||
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
||||||
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, explosion_pos, target)
|
||||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position).collect { resprojectile =>
|
ResolveProjectileInteraction(projectile, resolution1, target, target.Position).collect { resprojectile =>
|
||||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||||
sessionData.handleDealingDamage(target, resprojectile)
|
sessionData.handleDealingDamage(target, resprojectile)
|
||||||
|
|
@ -563,6 +564,7 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
ProjectileQuality.Normal
|
ProjectileQuality.Normal
|
||||||
}
|
}
|
||||||
val qualityprojectile = projectile.quality(initialQuality)
|
val qualityprojectile = projectile.quality(initialQuality)
|
||||||
|
qualityprojectile.WhichSide = player.WhichSide
|
||||||
projectiles(projectileIndex) = Some(qualityprojectile)
|
projectiles(projectileIndex) = Some(qualityprojectile)
|
||||||
if (projectile_info.ExistsOnRemoteClients) {
|
if (projectile_info.ExistsOnRemoteClients) {
|
||||||
log.trace(
|
log.trace(
|
||||||
|
|
@ -1048,19 +1050,22 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
|
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
|
||||||
case Nil =>
|
case Nil =>
|
||||||
Nil
|
Nil
|
||||||
|
case list if list.isEmpty =>
|
||||||
|
Nil
|
||||||
case list =>
|
case list =>
|
||||||
HandleDamageProxySetupLittleBuddy(list, hitPos)
|
HandleDamageProxySetupLittleBuddy(list, hitPos)
|
||||||
|
UpdateProjectileSidednessAfterHit(projectile, hitPos)
|
||||||
|
val projectileSide = projectile.WhichSide
|
||||||
list.flatMap { proxy =>
|
list.flatMap { proxy =>
|
||||||
if (proxy.profile.ExistsOnRemoteClients) {
|
if (proxy.profile.ExistsOnRemoteClients) {
|
||||||
proxy.Position = hitPos
|
proxy.Position = hitPos
|
||||||
|
proxy.WhichSide = projectileSide
|
||||||
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
|
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
|
||||||
Nil
|
Nil
|
||||||
} else if (proxy.tool_def == GlobalDefinitions.maelstrom) {
|
} else if (proxy.tool_def == GlobalDefinitions.maelstrom) {
|
||||||
//server-side maelstrom grenade target selection
|
//server-side maelstrom grenade target selection
|
||||||
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
|
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
|
||||||
val targets = continent.blockMap
|
val targets = Zone.findAllTargets(continent, hitPos, proxy.profile.LashRadius, { _.livePlayerList })
|
||||||
.sector(hitPos, proxy.profile.LashRadius)
|
|
||||||
.livePlayerList
|
|
||||||
.filter { target =>
|
.filter { target =>
|
||||||
Vector3.DistanceSquared(target.Position, hitPos) <= radius
|
Vector3.DistanceSquared(target.Position, hitPos) <= radius
|
||||||
}
|
}
|
||||||
|
|
@ -1498,6 +1503,88 @@ private[support] class WeaponAndProjectileOperations(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def UpdateProjectileSidednessAfterHit(projectile: Projectile, hitPosition: Vector3): Unit = {
|
||||||
|
val origin = projectile.Position
|
||||||
|
val distance = Vector3.Magnitude(hitPosition - origin)
|
||||||
|
continent.blockMap
|
||||||
|
.sector(hitPosition, distance)
|
||||||
|
.environmentList
|
||||||
|
.collect { case o: InteriorDoorPassage =>
|
||||||
|
val door = o.door
|
||||||
|
val intersectTest = quickLineSphereIntersectionPoints(
|
||||||
|
origin,
|
||||||
|
hitPosition,
|
||||||
|
door.Position,
|
||||||
|
door.Definition.geometryInteractionRadius.get + 0.1f
|
||||||
|
)
|
||||||
|
(door, intersectTest)
|
||||||
|
}
|
||||||
|
.collect { case (door, intersectionTest) if intersectionTest.nonEmpty =>
|
||||||
|
(door, Vector3.Magnitude(hitPosition - door.Position), intersectionTest)
|
||||||
|
}
|
||||||
|
.minByOption { case (_, dist, _) => dist }
|
||||||
|
.foreach { case (door, _, intersects) =>
|
||||||
|
val strictly = if (Vector3.DotProduct(Vector3.Unit(hitPosition - door.Position), door.Outwards) > 0f) {
|
||||||
|
Sidedness.OutsideOf
|
||||||
|
} else {
|
||||||
|
Sidedness.InsideOf
|
||||||
|
}
|
||||||
|
projectile.WhichSide = if (intersects.size == 1) {
|
||||||
|
Sidedness.InBetweenSides(door, strictly)
|
||||||
|
} else {
|
||||||
|
strictly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does a line segment line intersect with a sphere?<br>
|
||||||
|
* This most likely belongs in `Geometry` or `GeometryForm` or somehow in association with the `\objects\geometry\` package.
|
||||||
|
* @param start first point of the line segment
|
||||||
|
* @param end second point of the line segment
|
||||||
|
* @param center center of the sphere
|
||||||
|
* @param radius radius of the sphere
|
||||||
|
* @return list of all points of intersection, if any
|
||||||
|
* @see `Vector3.DistanceSquared`
|
||||||
|
* @see `Vector3.MagnitudeSquared`
|
||||||
|
*/
|
||||||
|
private def quickLineSphereIntersectionPoints(
|
||||||
|
start: Vector3,
|
||||||
|
end: Vector3,
|
||||||
|
center: Vector3,
|
||||||
|
radius: Float
|
||||||
|
): Iterable[Vector3] = {
|
||||||
|
/*
|
||||||
|
Algorithm adapted from code found on https://paulbourke.net/geometry/circlesphere/index.html#linesphere,
|
||||||
|
because I kept messing up proper substitution of the line formula and the circle formula into the quadratic equation.
|
||||||
|
*/
|
||||||
|
val Vector3(cx, cy, cz) = center
|
||||||
|
val Vector3(sx, sy, sz) = start
|
||||||
|
val vector = end - start
|
||||||
|
//speed our way through a quadratic equation
|
||||||
|
val (a, b) = {
|
||||||
|
val Vector3(dx, dy, dz) = vector
|
||||||
|
(
|
||||||
|
dx * dx + dy * dy + dz * dz,
|
||||||
|
2f * (dx * (sx - cx) + dy * (sy - cy) + dz * (sz - cz))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val c = Vector3.MagnitudeSquared(center) + Vector3.MagnitudeSquared(start) - 2f * (cx * sx + cy * sy + cz * sz) - radius * radius
|
||||||
|
val result = b * b - 4 * a * c
|
||||||
|
if (result < 0f) {
|
||||||
|
//negative, no intersection
|
||||||
|
Seq()
|
||||||
|
} else if (result < 0.00001f) {
|
||||||
|
//zero-ish, one intersection point
|
||||||
|
Seq(start - vector * (b / (2f * a)))
|
||||||
|
} else {
|
||||||
|
//positive, two intersection points
|
||||||
|
val sqrt = math.sqrt(result).toFloat
|
||||||
|
val endStart = vector / (2f * a)
|
||||||
|
Seq(start + endStart * (sqrt - b), start + endStart * (b + sqrt) * -1f)
|
||||||
|
}.filter(p => Vector3.DistanceSquared(start, p) <= a)
|
||||||
|
}
|
||||||
|
|
||||||
override protected[session] def stop(): Unit = {
|
override protected[session] def stop(): Unit = {
|
||||||
if (player != null && player.HasGUID) {
|
if (player != null && player.HasGUID) {
|
||||||
(prefire ++ shooting).foreach { guid =>
|
(prefire ++ shooting).foreach { guid =>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class InteractWithRadiationClouds(
|
||||||
*/
|
*/
|
||||||
private var skipTargets: List[PlanetSideGUID] = List()
|
private var skipTargets: List[PlanetSideGUID] = List()
|
||||||
|
|
||||||
def Type = RadiationInteraction
|
def Type: ZoneInteractionType = RadiationInteraction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wander into a radiation cloud and suffer the consequences.
|
* Wander into a radiation cloud and suffer the consequences.
|
||||||
|
|
@ -40,12 +40,16 @@ class InteractWithRadiationClouds(
|
||||||
target match {
|
target match {
|
||||||
case t: Vitality =>
|
case t: Vitality =>
|
||||||
val position = target.Position
|
val position = target.Position
|
||||||
|
val targetList = List(target)
|
||||||
//collect all projectiles in sector/range
|
//collect all projectiles in sector/range
|
||||||
val projectiles = sector
|
val projectiles = sector
|
||||||
.projectileList
|
.projectileList
|
||||||
.filter { cloud =>
|
.filter { cloud =>
|
||||||
val radius = cloud.Definition.DamageRadius
|
val definition = cloud.Definition
|
||||||
cloud.Definition.radiation_cloud && Zone.distanceCheck(target, cloud, radius * radius)
|
val radius = definition.DamageRadius
|
||||||
|
definition.radiation_cloud &&
|
||||||
|
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
|
||||||
|
Zone.distanceCheck(target, cloud, radius * radius)
|
||||||
}
|
}
|
||||||
.distinct
|
.distinct
|
||||||
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.objects.ballistics
|
package net.psforever.objects.ballistics
|
||||||
|
|
||||||
|
import net.psforever.objects.serverobject.interior.TraditionalInteriorAware
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
@ -53,7 +54,8 @@ final case class Projectile(
|
||||||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||||
fire_time: Long = System.currentTimeMillis()
|
fire_time: Long = System.currentTimeMillis()
|
||||||
) extends PlanetSideGameObject
|
) extends PlanetSideGameObject
|
||||||
with BlockMapEntity {
|
with BlockMapEntity
|
||||||
|
with TraditionalInteriorAware {
|
||||||
Position = shot_origin
|
Position = shot_origin
|
||||||
Orientation = shot_angle
|
Orientation = shot_angle
|
||||||
Velocity = shot_velocity.getOrElse {
|
Velocity = shot_velocity.getOrElse {
|
||||||
|
|
@ -73,7 +75,8 @@ final case class Projectile(
|
||||||
* Create a copy of this projectile with all the same information
|
* Create a copy of this projectile with all the same information
|
||||||
* save for the quality.
|
* save for the quality.
|
||||||
* Used mainly for aggravated damage.
|
* Used mainly for aggravated damage.
|
||||||
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original.
|
* It is important to note that the new projectile shares the (otherwise) exclusive id of the original
|
||||||
|
* and that it is not added to a block map structure.
|
||||||
* @param value the new quality
|
* @param value the new quality
|
||||||
* @return a new `Projectile` entity
|
* @return a new `Projectile` entity
|
||||||
*/
|
*/
|
||||||
|
|
@ -94,6 +97,7 @@ final case class Projectile(
|
||||||
)
|
)
|
||||||
if(isMiss) projectile.Miss()
|
if(isMiss) projectile.Miss()
|
||||||
else if(isResolved) projectile.Resolve()
|
else if(isResolved) projectile.Resolve()
|
||||||
|
projectile.WhichSide = this.WhichSide
|
||||||
projectile
|
projectile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,6 @@ class ProjectileDefinition(objectId: Int)
|
||||||
/** projectile takes the form of a type of "grenade";
|
/** projectile takes the form of a type of "grenade";
|
||||||
* grenades arc with gravity rather than travel in a relatively straight path */
|
* grenades arc with gravity rather than travel in a relatively straight path */
|
||||||
private var grenade_projectile: Boolean = false
|
private var grenade_projectile: Boolean = false
|
||||||
/** radiation clouds create independent damage-dealing areas in a zone that last for the projectile's lifespan */
|
|
||||||
var radiation_cloud: Boolean = false
|
|
||||||
//derived calculations
|
//derived calculations
|
||||||
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
|
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
|
||||||
* typically handled as the projectile no longer performing damage;
|
* typically handled as the projectile no longer performing damage;
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,16 @@ object Segment {
|
||||||
Segment(p, Point(p.x + d.x, p.y + d.y, p.z + d.z))
|
Segment(p, Point(p.x + d.x, p.y + d.y, p.z + d.z))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overloaded constructor that uses individual coordinates.
|
||||||
|
* @param a origin
|
||||||
|
* @param b destination
|
||||||
|
* @return a `Segment` entity
|
||||||
|
*/
|
||||||
|
def apply(a: Vector3, b: Vector3): Segment = {
|
||||||
|
Segment(Point(a), Point(b))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An overloaded constructor that uses individual coordinates.
|
* An overloaded constructor that uses individual coordinates.
|
||||||
* @param x the 'x' coordinate of the position
|
* @param x the 'x' coordinate of the position
|
||||||
|
|
|
||||||
|
|
@ -1040,7 +1040,7 @@ object GlobalDefinitionsProjectile {
|
||||||
|
|
||||||
maelstrom_grenade_damager.Name = "maelstrom_grenade_damager"
|
maelstrom_grenade_damager.Name = "maelstrom_grenade_damager"
|
||||||
maelstrom_grenade_damager.ProjectileDamageType = DamageType.Direct
|
maelstrom_grenade_damager.ProjectileDamageType = DamageType.Direct
|
||||||
//the maelstrom_grenade_damage is something of a broken entity atm
|
//the maelstrom_grenade_damage will be treated as a broken entity
|
||||||
|
|
||||||
maelstrom_grenade_projectile.Name = "maelstrom_grenade_projectile"
|
maelstrom_grenade_projectile.Name = "maelstrom_grenade_projectile"
|
||||||
maelstrom_grenade_projectile.Damage0 = 32
|
maelstrom_grenade_projectile.Damage0 = 32
|
||||||
|
|
@ -1049,6 +1049,7 @@ object GlobalDefinitionsProjectile {
|
||||||
maelstrom_grenade_projectile.LashRadius = 5f
|
maelstrom_grenade_projectile.LashRadius = 5f
|
||||||
maelstrom_grenade_projectile.GrenadeProjectile = true
|
maelstrom_grenade_projectile.GrenadeProjectile = true
|
||||||
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
|
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
|
||||||
|
maelstrom_grenade_projectile.DamageThroughWalls = true
|
||||||
maelstrom_grenade_projectile.InitialVelocity = 30
|
maelstrom_grenade_projectile.InitialVelocity = 30
|
||||||
maelstrom_grenade_projectile.Lifespan = 2f
|
maelstrom_grenade_projectile.Lifespan = 2f
|
||||||
maelstrom_grenade_projectile.DamageProxy = 464 //maelstrom_grenade_damager
|
maelstrom_grenade_projectile.DamageProxy = 464 //maelstrom_grenade_damager
|
||||||
|
|
@ -1063,6 +1064,7 @@ object GlobalDefinitionsProjectile {
|
||||||
maelstrom_grenade_projectile_contact.LashRadius = 5f
|
maelstrom_grenade_projectile_contact.LashRadius = 5f
|
||||||
maelstrom_grenade_projectile_contact.GrenadeProjectile = true
|
maelstrom_grenade_projectile_contact.GrenadeProjectile = true
|
||||||
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
|
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
|
||||||
|
maelstrom_grenade_projectile_contact.DamageThroughWalls = true
|
||||||
maelstrom_grenade_projectile_contact.InitialVelocity = 30
|
maelstrom_grenade_projectile_contact.InitialVelocity = 30
|
||||||
maelstrom_grenade_projectile_contact.Lifespan = 15f
|
maelstrom_grenade_projectile_contact.Lifespan = 15f
|
||||||
maelstrom_grenade_projectile_contact.DamageProxy = 464 //maelstrom_grenade_damager
|
maelstrom_grenade_projectile_contact.DamageProxy = 464 //maelstrom_grenade_damager
|
||||||
|
|
@ -1533,6 +1535,7 @@ object GlobalDefinitionsProjectile {
|
||||||
radiator_cloud.DamageToHealthOnly = true
|
radiator_cloud.DamageToHealthOnly = true
|
||||||
radiator_cloud.radiation_cloud = true
|
radiator_cloud.radiation_cloud = true
|
||||||
radiator_cloud.ProjectileDamageType = DamageType.Radiation
|
radiator_cloud.ProjectileDamageType = DamageType.Radiation
|
||||||
|
radiator_cloud.DamageThroughWalls = true
|
||||||
//custom aggravated information
|
//custom aggravated information
|
||||||
radiator_cloud.ProjectileDamageTypeSecondary = DamageType.Aggravated
|
radiator_cloud.ProjectileDamageTypeSecondary = DamageType.Aggravated
|
||||||
radiator_cloud.Aggravated = AggravatedDamage(
|
radiator_cloud.Aggravated = AggravatedDamage(
|
||||||
|
|
@ -2017,6 +2020,7 @@ object GlobalDefinitionsProjectile {
|
||||||
aphelion_plasma_cloud.DamageRadius = 3f
|
aphelion_plasma_cloud.DamageRadius = 3f
|
||||||
aphelion_plasma_cloud.radiation_cloud = true
|
aphelion_plasma_cloud.radiation_cloud = true
|
||||||
aphelion_plasma_cloud.ProjectileDamageType = DamageType.Aggravated
|
aphelion_plasma_cloud.ProjectileDamageType = DamageType.Aggravated
|
||||||
|
//aphelion_plasma_cloud.DamageThroughWalls = true
|
||||||
aphelion_plasma_cloud.Aggravated = AggravatedDamage(
|
aphelion_plasma_cloud.Aggravated = AggravatedDamage(
|
||||||
AggravatedInfo(DamageType.Splash, 0.5f, 1000),
|
AggravatedInfo(DamageType.Splash, 0.5f, 1000),
|
||||||
Aura.Napalm,
|
Aura.Napalm,
|
||||||
|
|
@ -2229,6 +2233,7 @@ object GlobalDefinitionsProjectile {
|
||||||
peregrine_particle_cannon_radiation_cloud.DamageRadius = 3f
|
peregrine_particle_cannon_radiation_cloud.DamageRadius = 3f
|
||||||
peregrine_particle_cannon_radiation_cloud.radiation_cloud = true
|
peregrine_particle_cannon_radiation_cloud.radiation_cloud = true
|
||||||
peregrine_particle_cannon_radiation_cloud.ProjectileDamageType = DamageType.Radiation
|
peregrine_particle_cannon_radiation_cloud.ProjectileDamageType = DamageType.Radiation
|
||||||
|
peregrine_particle_cannon_radiation_cloud.DamageThroughWalls = true
|
||||||
peregrine_particle_cannon_radiation_cloud.Lifespan = 5.0f
|
peregrine_particle_cannon_radiation_cloud.Lifespan = 5.0f
|
||||||
ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_radiation_cloud)
|
ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_radiation_cloud)
|
||||||
peregrine_particle_cannon_radiation_cloud.registerAs = "rc-projectiles"
|
peregrine_particle_cannon_radiation_cloud.registerAs = "rc-projectiles"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import net.psforever.objects.serverobject.doors.Door
|
||||||
sealed trait SidenessComparison
|
sealed trait SidenessComparison
|
||||||
|
|
||||||
sealed trait Sidedness {
|
sealed trait Sidedness {
|
||||||
|
def opposite: Sidedness
|
||||||
|
|
||||||
protected def value: SidenessComparison
|
protected def value: SidenessComparison
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,14 +25,17 @@ object Sidedness {
|
||||||
|
|
||||||
/* Immutable value containers */
|
/* Immutable value containers */
|
||||||
case object InsideOf extends Inside with Sidedness {
|
case object InsideOf extends Inside with Sidedness {
|
||||||
|
def opposite: Sidedness = OutsideOf
|
||||||
protected def value: SidenessComparison = IsInside
|
protected def value: SidenessComparison = IsInside
|
||||||
}
|
}
|
||||||
|
|
||||||
case object OutsideOf extends Outside with Sidedness {
|
case object OutsideOf extends Outside with Sidedness {
|
||||||
|
def opposite: Sidedness = InsideOf
|
||||||
protected def value: SidenessComparison = IsOutside
|
protected def value: SidenessComparison = IsOutside
|
||||||
}
|
}
|
||||||
|
|
||||||
case object StrictlyBetweenSides extends Inside with Outside with Sidedness {
|
case object StrictlyBetweenSides extends Inside with Outside with Sidedness {
|
||||||
|
def opposite: Sidedness = this
|
||||||
protected def value: SidenessComparison = IsBetween
|
protected def value: SidenessComparison = IsBetween
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,6 +44,7 @@ object Sidedness {
|
||||||
private val door: Door,
|
private val door: Door,
|
||||||
private val strictly: Sidedness
|
private val strictly: Sidedness
|
||||||
) extends Inside with Outside with Sidedness {
|
) extends Inside with Outside with Sidedness {
|
||||||
|
def opposite: Sidedness = this
|
||||||
protected def value: SidenessComparison = {
|
protected def value: SidenessComparison = {
|
||||||
if (door.isOpen) {
|
if (door.isOpen) {
|
||||||
IsBetween
|
IsBetween
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.mount
|
||||||
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||||
import net.psforever.objects.sourcing.SourceEntry
|
import net.psforever.objects.sourcing.SourceEntry
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.vital.base.{DamageResolution, DamageType}
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.etc.RadiationReason
|
import net.psforever.objects.vital.etc.RadiationReason
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.resistance.StandardResistanceProfile
|
import net.psforever.objects.vital.resistance.StandardResistanceProfile
|
||||||
|
|
@ -38,17 +38,16 @@ class InteractWithRadiationCloudsSeatedInEntity(
|
||||||
*/
|
*/
|
||||||
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
|
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
|
||||||
val position = target.Position
|
val position = target.Position
|
||||||
|
val targetList = List(target)
|
||||||
//collect all projectiles in sector/range
|
//collect all projectiles in sector/range
|
||||||
val projectiles = sector
|
val projectiles = sector
|
||||||
.projectileList
|
.projectileList
|
||||||
.filter { cloud =>
|
.filter { cloud =>
|
||||||
val definition = cloud.Definition
|
val definition = cloud.Definition
|
||||||
|
val radius = definition.DamageRadius
|
||||||
definition.radiation_cloud &&
|
definition.radiation_cloud &&
|
||||||
definition.AllDamageTypes.contains(DamageType.Radiation) &&
|
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
|
||||||
{
|
Zone.distanceCheck(target, cloud, radius * radius)
|
||||||
val radius = definition.DamageRadius
|
|
||||||
Zone.distanceCheck(target, cloud, radius * radius)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.distinct
|
.distinct
|
||||||
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,11 @@ trait DamageProperties
|
||||||
private var charging: Option[ChargeDamage] = None
|
private var charging: Option[ChargeDamage] = None
|
||||||
/** a destroyed mine will detonate rather than fizzle-out */
|
/** a destroyed mine will detonate rather than fizzle-out */
|
||||||
private var sympathy: Boolean = false
|
private var sympathy: Boolean = false
|
||||||
|
/** radiation clouds create independent damage-dealing areas in a zone that last for the projectile's lifespan;
|
||||||
|
* not implicate a damage type (primary or secondary or aggravated) of `Radiation` */
|
||||||
|
private var radiationCloud: Boolean = false
|
||||||
|
/** server damage is applied when comparing against sided-ness of the source and the target */
|
||||||
|
private var damageThroughWalls: Boolean = false
|
||||||
|
|
||||||
def UseDamage1Subtract: Boolean = useDamage1Subtract
|
def UseDamage1Subtract: Boolean = useDamage1Subtract
|
||||||
|
|
||||||
|
|
@ -132,4 +137,18 @@ trait DamageProperties
|
||||||
sympathy = chain
|
sympathy = chain
|
||||||
SympatheticExplosion
|
SympatheticExplosion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def radiation_cloud: Boolean = radiationCloud
|
||||||
|
|
||||||
|
def radiation_cloud_=(isCloud: Boolean): Boolean = {
|
||||||
|
radiationCloud = isCloud
|
||||||
|
radiation_cloud
|
||||||
|
}
|
||||||
|
|
||||||
|
def DamageThroughWalls: Boolean = damageThroughWalls
|
||||||
|
|
||||||
|
def DamageThroughWalls_=(through: Boolean): Boolean = {
|
||||||
|
damageThroughWalls = through
|
||||||
|
DamageThroughWalls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import net.psforever.objects.vital.etc.ExplodingEntityReason
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
||||||
import net.psforever.objects.vital.prop.DamageWithPosition
|
import net.psforever.objects.vital.prop.DamageWithPosition
|
||||||
import net.psforever.objects.vital.Vitality
|
import net.psforever.objects.vital.Vitality
|
||||||
import net.psforever.objects.zones.blockmap.BlockMap
|
import net.psforever.objects.zones.blockmap.{BlockMap, SectorPopulation}
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.zones.Zones
|
import net.psforever.zones.Zones
|
||||||
|
|
||||||
|
|
@ -1437,6 +1437,48 @@ object Zone {
|
||||||
allAffectedTargets
|
allAffectedTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param source na
|
||||||
|
* @param damageProperties na
|
||||||
|
* @param targets na
|
||||||
|
* @return na
|
||||||
|
*/
|
||||||
|
def allOnSameSide(
|
||||||
|
source: PlanetSideGameObject,
|
||||||
|
damageProperties: DamageWithPosition,
|
||||||
|
targets: List[PlanetSideServerObject with Vitality]
|
||||||
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
source match {
|
||||||
|
case awareSource: InteriorAware if !damageProperties.DamageThroughWalls =>
|
||||||
|
allOnSameSide(awareSource.WhichSide, targets)
|
||||||
|
case _ if !damageProperties.DamageThroughWalls =>
|
||||||
|
val sourcePosition = source.Position
|
||||||
|
targets
|
||||||
|
.sortBy(t => Vector3.DistanceSquared(sourcePosition, t.Position))
|
||||||
|
.collectFirst { case awareSource: InteriorAware => allOnSameSide(awareSource.WhichSide, targets) }
|
||||||
|
.getOrElse(targets)
|
||||||
|
case _ =>
|
||||||
|
targets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param side na
|
||||||
|
* @param targets na
|
||||||
|
* @return na
|
||||||
|
*/
|
||||||
|
def allOnSameSide(
|
||||||
|
side: Sidedness,
|
||||||
|
targets: List[PlanetSideServerObject with Vitality]
|
||||||
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
targets.flatMap {
|
||||||
|
case awareTarget: InteriorAware if !Sidedness.equals(side, awareTarget.WhichSide) => None
|
||||||
|
case anyTarget => Some(anyTarget)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
* @see `DamageWithPosition`
|
* @see `DamageWithPosition`
|
||||||
|
|
@ -1451,9 +1493,13 @@ object Zone {
|
||||||
source: PlanetSideGameObject with Vitality,
|
source: PlanetSideGameObject with Vitality,
|
||||||
damagePropertiesBySource: DamageWithPosition
|
damagePropertiesBySource: DamageWithPosition
|
||||||
): List[PlanetSideServerObject with Vitality] = {
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
allOnSameSide(
|
findAllTargets(
|
||||||
|
zone,
|
||||||
source,
|
source,
|
||||||
findAllTargets(zone, source.Position, damagePropertiesBySource).filter { target => target ne source }
|
source.Position,
|
||||||
|
damagePropertiesBySource,
|
||||||
|
damagePropertiesBySource.DamageRadius,
|
||||||
|
getAllTargets
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1461,45 +1507,29 @@ object Zone {
|
||||||
* na
|
* na
|
||||||
* @see `DamageWithPosition`
|
* @see `DamageWithPosition`
|
||||||
* @see `Zone.blockMap.sector`
|
* @see `Zone.blockMap.sector`
|
||||||
|
* @param zone the zone in which the explosion should occur
|
||||||
* @param sourcePosition a custom position that is used as the origin of the explosion;
|
* @param sourcePosition a custom position that is used as the origin of the explosion;
|
||||||
* not necessarily related to source
|
* not necessarily related to source
|
||||||
* @param zone the zone in which the explosion should occur
|
|
||||||
* @param source a game entity that is treated as the origin and is excluded from results
|
* @param source a game entity that is treated as the origin and is excluded from results
|
||||||
* @param damagePropertiesBySource information about the effect/damage
|
* @param damagePropertiesBySource information about the effect/damage
|
||||||
* @return a list of affected entities
|
* @return a list of affected entities
|
||||||
*/
|
*/
|
||||||
def findAllTargets(
|
def findAllTargets(
|
||||||
sourcePosition: Vector3
|
|
||||||
)
|
|
||||||
(
|
|
||||||
zone: Zone,
|
zone: Zone,
|
||||||
source: PlanetSideGameObject with Vitality,
|
source: PlanetSideGameObject with Vitality,
|
||||||
|
sourcePosition: Vector3,
|
||||||
damagePropertiesBySource: DamageWithPosition
|
damagePropertiesBySource: DamageWithPosition
|
||||||
): List[PlanetSideServerObject with Vitality] = {
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
allOnSameSide(
|
findAllTargets(
|
||||||
|
zone,
|
||||||
source,
|
source,
|
||||||
findAllTargets(zone, sourcePosition, damagePropertiesBySource).filter { target => target ne source }
|
sourcePosition,
|
||||||
|
damagePropertiesBySource,
|
||||||
|
damagePropertiesBySource.DamageRadius,
|
||||||
|
getAllTargets
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def allOnSameSide(
|
|
||||||
source: PlanetSideGameObject with Vitality,
|
|
||||||
targets: List[PlanetSideServerObject with Vitality]
|
|
||||||
): List[PlanetSideServerObject with Vitality] = {
|
|
||||||
source match {
|
|
||||||
case awareSource: InteriorAware =>
|
|
||||||
val awareSide = awareSource.WhichSide
|
|
||||||
targets.flatMap {
|
|
||||||
case awareTarget: InteriorAware if !Sidedness.equals(awareSide, awareTarget.WhichSide) =>
|
|
||||||
None
|
|
||||||
case anyTarget =>
|
|
||||||
Some(anyTarget)
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
targets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* na
|
* na
|
||||||
* @see `DamageWithPosition`
|
* @see `DamageWithPosition`
|
||||||
|
|
@ -1507,24 +1537,66 @@ object Zone {
|
||||||
* @param zone the zone in which the explosion should occur
|
* @param zone the zone in which the explosion should occur
|
||||||
* @param sourcePosition a position that is used as the origin of the explosion
|
* @param sourcePosition a position that is used as the origin of the explosion
|
||||||
* @param damagePropertiesBySource information about the effect/damage
|
* @param damagePropertiesBySource information about the effect/damage
|
||||||
|
* @param getTargetsFromSector get this list of entities from a sector
|
||||||
* @return a list of affected entities
|
* @return a list of affected entities
|
||||||
*/
|
*/
|
||||||
def findAllTargets(
|
def findAllTargets(
|
||||||
zone: Zone,
|
zone: Zone,
|
||||||
|
source: PlanetSideGameObject with Vitality,
|
||||||
sourcePosition: Vector3,
|
sourcePosition: Vector3,
|
||||||
damagePropertiesBySource: DamageWithPosition
|
damagePropertiesBySource: DamageWithPosition,
|
||||||
|
radius: Float,
|
||||||
|
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
|
||||||
): List[PlanetSideServerObject with Vitality] = {
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
val sourcePositionXY = sourcePosition.xy
|
allOnSameSide(
|
||||||
val sectors = zone.blockMap.sector(sourcePositionXY, damagePropertiesBySource.DamageRadius)
|
source,
|
||||||
|
damagePropertiesBySource,
|
||||||
|
findAllTargets(zone, sourcePosition, radius, getTargetsFromSector).filter { target => target ne source }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def findAllTargets(
|
||||||
|
sector: SectorPopulation,
|
||||||
|
source: PlanetSideGameObject with Vitality,
|
||||||
|
damagePropertiesBySource: DamageWithPosition,
|
||||||
|
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
|
||||||
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
allOnSameSide(
|
||||||
|
source,
|
||||||
|
damagePropertiesBySource,
|
||||||
|
getTargetsFromSector(sector)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @see `DamageWithPosition`
|
||||||
|
* @see `Zone.blockMap.sector`
|
||||||
|
* @param zone the zone in which the explosion should occur
|
||||||
|
* @param sourcePosition a position that is used as the origin of the explosion
|
||||||
|
* @param radius idistance
|
||||||
|
* @param getTargetsFromSector get this list of entities from a sector
|
||||||
|
* @return a list of affected entities
|
||||||
|
*/
|
||||||
|
def findAllTargets(
|
||||||
|
zone: Zone,
|
||||||
|
sourcePosition: Vector3,
|
||||||
|
radius: Float,
|
||||||
|
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
|
||||||
|
): List[PlanetSideServerObject with Vitality] = {
|
||||||
|
getTargetsFromSector(zone.blockMap.sector(sourcePosition.xy, radius))
|
||||||
|
}
|
||||||
|
|
||||||
|
def getAllTargets(sector: SectorPopulation): List[PlanetSideServerObject with Vitality] = {
|
||||||
//collect all targets that can be damaged
|
//collect all targets that can be damaged
|
||||||
//players
|
//players
|
||||||
val playerTargets = sectors.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
|
val playerTargets = sector.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
|
||||||
//vehicles
|
//vehicles
|
||||||
val vehicleTargets = sectors.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
|
val vehicleTargets = sector.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
|
||||||
//deployables
|
//deployables
|
||||||
val deployableTargets = sectors.deployableList.filterNot { _.Destroyed }
|
val deployableTargets = sector.deployableList.filterNot { _.Destroyed }
|
||||||
//amenities
|
//amenities
|
||||||
val soiTargets = sectors.amenityList.collect { case amenity: Vitality if !amenity.Destroyed => amenity }
|
val soiTargets = sector.amenityList.collect { case amenity: Vitality if !amenity.Destroyed => amenity }
|
||||||
//altogether ...
|
//altogether ...
|
||||||
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,8 @@ final case class Vector3(x: Float, y: Float, z: Float) {
|
||||||
object Vector3 {
|
object Vector3 {
|
||||||
final val Zero: Vector3 = Vector3(0f, 0f, 0f)
|
final val Zero: Vector3 = Vector3(0f, 0f, 0f)
|
||||||
|
|
||||||
|
def unapply(v: Vector3): Option[(Float, Float, Float)] = Some((v.x, v.y, v.z))
|
||||||
|
|
||||||
private def closeToInsignificance(d: Float, epsilon: Float = 10f): Float = {
|
private def closeToInsignificance(d: Float, epsilon: Float = 10f): Float = {
|
||||||
val ulp = math.ulp(epsilon)
|
val ulp = math.ulp(epsilon)
|
||||||
math.signum(d) match {
|
math.signum(d) match {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue