mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-25 23:29:11 +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)
|
||||
|
||||
(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 =>
|
||||
val flying = contents match {
|
||||
case "on" => true
|
||||
|
|
@ -899,10 +903,6 @@ class ChatActor(
|
|||
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 _ =>
|
||||
log.warn(s"Unhandled chat message $message")
|
||||
}
|
||||
|
|
@ -1126,7 +1126,7 @@ class ChatActor(
|
|||
)
|
||||
true
|
||||
|
||||
} else if (contents.startsWith("!loc ")) {
|
||||
} else if (contents.startsWith("!loc")) {
|
||||
val continent = session.zone
|
||||
val player = session.player
|
||||
val loc =
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
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.zones.Zoning
|
||||
import net.psforever.objects.serverobject.turret.VanuSentry
|
||||
|
|
@ -303,11 +305,11 @@ private[support] class WeaponAndProjectileOperations(
|
|||
//find target(s)
|
||||
(hit_info match {
|
||||
case Some(hitInfo) =>
|
||||
val hitPos = hitInfo.hit_pos
|
||||
val hitPos = hitInfo.hit_pos
|
||||
sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
|
||||
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
|
||||
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 =>
|
||||
Vector3.DistanceSquared(target.Position, hitPos) <= radius
|
||||
}
|
||||
|
|
@ -361,7 +363,6 @@ private[support] class WeaponAndProjectileOperations(
|
|||
FindProjectileEntry(projectile_guid) match {
|
||||
case Some(projectile) =>
|
||||
val profile = projectile.profile
|
||||
projectile.Position = explosion_pos
|
||||
projectile.Velocity = projectile_vel
|
||||
val (resolution1, resolution2) = profile.Aggravated match {
|
||||
case Some(_) if profile.ProjectileDamageTypes.contains(DamageType.Aggravated) =>
|
||||
|
|
@ -372,7 +373,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
//direct_victim_uid
|
||||
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
||||
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 =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
|
|
@ -563,6 +564,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
ProjectileQuality.Normal
|
||||
}
|
||||
val qualityprojectile = projectile.quality(initialQuality)
|
||||
qualityprojectile.WhichSide = player.WhichSide
|
||||
projectiles(projectileIndex) = Some(qualityprojectile)
|
||||
if (projectile_info.ExistsOnRemoteClients) {
|
||||
log.trace(
|
||||
|
|
@ -1048,19 +1050,22 @@ private[support] class WeaponAndProjectileOperations(
|
|||
GlobalDefinitions.getDamageProxy(projectile, hitPos) match {
|
||||
case Nil =>
|
||||
Nil
|
||||
case list if list.isEmpty =>
|
||||
Nil
|
||||
case list =>
|
||||
HandleDamageProxySetupLittleBuddy(list, hitPos)
|
||||
UpdateProjectileSidednessAfterHit(projectile, hitPos)
|
||||
val projectileSide = projectile.WhichSide
|
||||
list.flatMap { proxy =>
|
||||
if (proxy.profile.ExistsOnRemoteClients) {
|
||||
proxy.Position = hitPos
|
||||
proxy.WhichSide = projectileSide
|
||||
continent.Projectile ! ZoneProjectile.Add(player.GUID, proxy)
|
||||
Nil
|
||||
} else if (proxy.tool_def == GlobalDefinitions.maelstrom) {
|
||||
//server-side maelstrom grenade target selection
|
||||
val radius = proxy.profile.LashRadius * proxy.profile.LashRadius
|
||||
val targets = continent.blockMap
|
||||
.sector(hitPos, proxy.profile.LashRadius)
|
||||
.livePlayerList
|
||||
val targets = Zone.findAllTargets(continent, hitPos, proxy.profile.LashRadius, { _.livePlayerList })
|
||||
.filter { target =>
|
||||
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 = {
|
||||
if (player != null && player.HasGUID) {
|
||||
(prefire ++ shooting).foreach { guid =>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class InteractWithRadiationClouds(
|
|||
*/
|
||||
private var skipTargets: List[PlanetSideGUID] = List()
|
||||
|
||||
def Type = RadiationInteraction
|
||||
def Type: ZoneInteractionType = RadiationInteraction
|
||||
|
||||
/**
|
||||
* Wander into a radiation cloud and suffer the consequences.
|
||||
|
|
@ -40,12 +40,16 @@ class InteractWithRadiationClouds(
|
|||
target match {
|
||||
case t: Vitality =>
|
||||
val position = target.Position
|
||||
val targetList = List(target)
|
||||
//collect all projectiles in sector/range
|
||||
val projectiles = sector
|
||||
.projectileList
|
||||
.filter { cloud =>
|
||||
val radius = cloud.Definition.DamageRadius
|
||||
cloud.Definition.radiation_cloud && Zone.distanceCheck(target, cloud, radius * radius)
|
||||
val definition = cloud.Definition
|
||||
val radius = definition.DamageRadius
|
||||
definition.radiation_cloud &&
|
||||
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
|
||||
Zone.distanceCheck(target, cloud, radius * radius)
|
||||
}
|
||||
.distinct
|
||||
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.ballistics
|
||||
|
||||
import net.psforever.objects.serverobject.interior.TraditionalInteriorAware
|
||||
import net.psforever.objects.sourcing.SourceEntry
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
|
@ -53,7 +54,8 @@ final case class Projectile(
|
|||
id: Long = Projectile.idGenerator.getAndIncrement(),
|
||||
fire_time: Long = System.currentTimeMillis()
|
||||
) extends PlanetSideGameObject
|
||||
with BlockMapEntity {
|
||||
with BlockMapEntity
|
||||
with TraditionalInteriorAware {
|
||||
Position = shot_origin
|
||||
Orientation = shot_angle
|
||||
Velocity = shot_velocity.getOrElse {
|
||||
|
|
@ -73,7 +75,8 @@ final case class Projectile(
|
|||
* Create a copy of this projectile with all the same information
|
||||
* save for the quality.
|
||||
* 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
|
||||
* @return a new `Projectile` entity
|
||||
*/
|
||||
|
|
@ -94,6 +97,7 @@ final case class Projectile(
|
|||
)
|
||||
if(isMiss) projectile.Miss()
|
||||
else if(isResolved) projectile.Resolve()
|
||||
projectile.WhichSide = this.WhichSide
|
||||
projectile
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ class ProjectileDefinition(objectId: Int)
|
|||
/** projectile takes the form of a type of "grenade";
|
||||
* grenades arc with gravity rather than travel in a relatively straight path */
|
||||
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
|
||||
/** the calculated distance at which the projectile have traveled far enough to despawn (m);
|
||||
* 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))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param x the 'x' coordinate of the position
|
||||
|
|
|
|||
|
|
@ -1040,7 +1040,7 @@ object GlobalDefinitionsProjectile {
|
|||
|
||||
maelstrom_grenade_damager.Name = "maelstrom_grenade_damager"
|
||||
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.Damage0 = 32
|
||||
|
|
@ -1049,6 +1049,7 @@ object GlobalDefinitionsProjectile {
|
|||
maelstrom_grenade_projectile.LashRadius = 5f
|
||||
maelstrom_grenade_projectile.GrenadeProjectile = true
|
||||
maelstrom_grenade_projectile.ProjectileDamageType = DamageType.Direct
|
||||
maelstrom_grenade_projectile.DamageThroughWalls = true
|
||||
maelstrom_grenade_projectile.InitialVelocity = 30
|
||||
maelstrom_grenade_projectile.Lifespan = 2f
|
||||
maelstrom_grenade_projectile.DamageProxy = 464 //maelstrom_grenade_damager
|
||||
|
|
@ -1063,6 +1064,7 @@ object GlobalDefinitionsProjectile {
|
|||
maelstrom_grenade_projectile_contact.LashRadius = 5f
|
||||
maelstrom_grenade_projectile_contact.GrenadeProjectile = true
|
||||
maelstrom_grenade_projectile_contact.ProjectileDamageType = DamageType.Direct
|
||||
maelstrom_grenade_projectile_contact.DamageThroughWalls = true
|
||||
maelstrom_grenade_projectile_contact.InitialVelocity = 30
|
||||
maelstrom_grenade_projectile_contact.Lifespan = 15f
|
||||
maelstrom_grenade_projectile_contact.DamageProxy = 464 //maelstrom_grenade_damager
|
||||
|
|
@ -1533,6 +1535,7 @@ object GlobalDefinitionsProjectile {
|
|||
radiator_cloud.DamageToHealthOnly = true
|
||||
radiator_cloud.radiation_cloud = true
|
||||
radiator_cloud.ProjectileDamageType = DamageType.Radiation
|
||||
radiator_cloud.DamageThroughWalls = true
|
||||
//custom aggravated information
|
||||
radiator_cloud.ProjectileDamageTypeSecondary = DamageType.Aggravated
|
||||
radiator_cloud.Aggravated = AggravatedDamage(
|
||||
|
|
@ -2017,6 +2020,7 @@ object GlobalDefinitionsProjectile {
|
|||
aphelion_plasma_cloud.DamageRadius = 3f
|
||||
aphelion_plasma_cloud.radiation_cloud = true
|
||||
aphelion_plasma_cloud.ProjectileDamageType = DamageType.Aggravated
|
||||
//aphelion_plasma_cloud.DamageThroughWalls = true
|
||||
aphelion_plasma_cloud.Aggravated = AggravatedDamage(
|
||||
AggravatedInfo(DamageType.Splash, 0.5f, 1000),
|
||||
Aura.Napalm,
|
||||
|
|
@ -2229,6 +2233,7 @@ object GlobalDefinitionsProjectile {
|
|||
peregrine_particle_cannon_radiation_cloud.DamageRadius = 3f
|
||||
peregrine_particle_cannon_radiation_cloud.radiation_cloud = true
|
||||
peregrine_particle_cannon_radiation_cloud.ProjectileDamageType = DamageType.Radiation
|
||||
peregrine_particle_cannon_radiation_cloud.DamageThroughWalls = true
|
||||
peregrine_particle_cannon_radiation_cloud.Lifespan = 5.0f
|
||||
ProjectileDefinition.CalculateDerivedFields(peregrine_particle_cannon_radiation_cloud)
|
||||
peregrine_particle_cannon_radiation_cloud.registerAs = "rc-projectiles"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import net.psforever.objects.serverobject.doors.Door
|
|||
sealed trait SidenessComparison
|
||||
|
||||
sealed trait Sidedness {
|
||||
def opposite: Sidedness
|
||||
|
||||
protected def value: SidenessComparison
|
||||
}
|
||||
|
||||
|
|
@ -23,14 +25,17 @@ object Sidedness {
|
|||
|
||||
/* Immutable value containers */
|
||||
case object InsideOf extends Inside with Sidedness {
|
||||
def opposite: Sidedness = OutsideOf
|
||||
protected def value: SidenessComparison = IsInside
|
||||
}
|
||||
|
||||
case object OutsideOf extends Outside with Sidedness {
|
||||
def opposite: Sidedness = InsideOf
|
||||
protected def value: SidenessComparison = IsOutside
|
||||
}
|
||||
|
||||
case object StrictlyBetweenSides extends Inside with Outside with Sidedness {
|
||||
def opposite: Sidedness = this
|
||||
protected def value: SidenessComparison = IsBetween
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +44,7 @@ object Sidedness {
|
|||
private val door: Door,
|
||||
private val strictly: Sidedness
|
||||
) extends Inside with Outside with Sidedness {
|
||||
def opposite: Sidedness = this
|
||||
protected def value: SidenessComparison = {
|
||||
if (door.isOpen) {
|
||||
IsBetween
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.mount
|
|||
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||
import net.psforever.objects.sourcing.SourceEntry
|
||||
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.interaction.DamageInteraction
|
||||
import net.psforever.objects.vital.resistance.StandardResistanceProfile
|
||||
|
|
@ -38,17 +38,16 @@ class InteractWithRadiationCloudsSeatedInEntity(
|
|||
*/
|
||||
override def interaction(sector: SectorPopulation, target: InteractsWithZone): Unit = {
|
||||
val position = target.Position
|
||||
val targetList = List(target)
|
||||
//collect all projectiles in sector/range
|
||||
val projectiles = sector
|
||||
.projectileList
|
||||
.filter { cloud =>
|
||||
val definition = cloud.Definition
|
||||
val radius = definition.DamageRadius
|
||||
definition.radiation_cloud &&
|
||||
definition.AllDamageTypes.contains(DamageType.Radiation) &&
|
||||
{
|
||||
val radius = definition.DamageRadius
|
||||
Zone.distanceCheck(target, cloud, radius * radius)
|
||||
}
|
||||
Zone.allOnSameSide(cloud, definition, targetList).nonEmpty &&
|
||||
Zone.distanceCheck(target, cloud, radius * radius)
|
||||
}
|
||||
.distinct
|
||||
val notSkipped = projectiles.filterNot { t => skipTargets.contains(t.GUID) }
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ trait DamageProperties
|
|||
private var charging: Option[ChargeDamage] = None
|
||||
/** a destroyed mine will detonate rather than fizzle-out */
|
||||
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
|
||||
|
||||
|
|
@ -132,4 +137,18 @@ trait DamageProperties
|
|||
sympathy = chain
|
||||
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.prop.DamageWithPosition
|
||||
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.zones.Zones
|
||||
|
||||
|
|
@ -1437,6 +1437,48 @@ object Zone {
|
|||
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
|
||||
* @see `DamageWithPosition`
|
||||
|
|
@ -1451,9 +1493,13 @@ object Zone {
|
|||
source: PlanetSideGameObject with Vitality,
|
||||
damagePropertiesBySource: DamageWithPosition
|
||||
): List[PlanetSideServerObject with Vitality] = {
|
||||
allOnSameSide(
|
||||
findAllTargets(
|
||||
zone,
|
||||
source,
|
||||
findAllTargets(zone, source.Position, damagePropertiesBySource).filter { target => target ne source }
|
||||
source.Position,
|
||||
damagePropertiesBySource,
|
||||
damagePropertiesBySource.DamageRadius,
|
||||
getAllTargets
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1461,45 +1507,29 @@ object Zone {
|
|||
* na
|
||||
* @see `DamageWithPosition`
|
||||
* @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;
|
||||
* 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 damagePropertiesBySource information about the effect/damage
|
||||
* @return a list of affected entities
|
||||
*/
|
||||
def findAllTargets(
|
||||
sourcePosition: Vector3
|
||||
)
|
||||
(
|
||||
zone: Zone,
|
||||
source: PlanetSideGameObject with Vitality,
|
||||
sourcePosition: Vector3,
|
||||
damagePropertiesBySource: DamageWithPosition
|
||||
): List[PlanetSideServerObject with Vitality] = {
|
||||
allOnSameSide(
|
||||
findAllTargets(
|
||||
zone,
|
||||
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
|
||||
* @see `DamageWithPosition`
|
||||
|
|
@ -1507,24 +1537,66 @@ object Zone {
|
|||
* @param zone the zone in which the explosion should occur
|
||||
* @param sourcePosition a position that is used as the origin of the explosion
|
||||
* @param damagePropertiesBySource information about the effect/damage
|
||||
* @param getTargetsFromSector get this list of entities from a sector
|
||||
* @return a list of affected entities
|
||||
*/
|
||||
def findAllTargets(
|
||||
zone: Zone,
|
||||
source: PlanetSideGameObject with Vitality,
|
||||
sourcePosition: Vector3,
|
||||
damagePropertiesBySource: DamageWithPosition
|
||||
damagePropertiesBySource: DamageWithPosition,
|
||||
radius: Float,
|
||||
getTargetsFromSector: SectorPopulation => List[PlanetSideServerObject with Vitality]
|
||||
): List[PlanetSideServerObject with Vitality] = {
|
||||
val sourcePositionXY = sourcePosition.xy
|
||||
val sectors = zone.blockMap.sector(sourcePositionXY, damagePropertiesBySource.DamageRadius)
|
||||
allOnSameSide(
|
||||
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
|
||||
//players
|
||||
val playerTargets = sectors.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
|
||||
val playerTargets = sector.livePlayerList.filterNot { _.VehicleSeated.nonEmpty }
|
||||
//vehicles
|
||||
val vehicleTargets = sectors.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
|
||||
val vehicleTargets = sector.vehicleList.filterNot { v => v.Destroyed || v.MountedIn.nonEmpty }
|
||||
//deployables
|
||||
val deployableTargets = sectors.deployableList.filterNot { _.Destroyed }
|
||||
val deployableTargets = sector.deployableList.filterNot { _.Destroyed }
|
||||
//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 ...
|
||||
playerTargets ++ vehicleTargets ++ deployableTargets ++ soiTargets
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ final case class Vector3(x: Float, y: Float, z: Float) {
|
|||
object Vector3 {
|
||||
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 = {
|
||||
val ulp = math.ulp(epsilon)
|
||||
math.signum(d) match {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue