mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-24 14:49:11 +00:00
the force dome exhibits a perimeter in which enemies will be destroyed when it energizes; the facility generator will become undestroyable when the force dome is energized
This commit is contained in:
parent
6a960ed5ac
commit
4b3f8ea6c0
5 changed files with 245 additions and 100 deletions
|
|
@ -6,6 +6,7 @@ import akka.actor.typed.{ActorRef, Behavior}
|
|||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
||||
import net.psforever.actors.commands.NtuCommand
|
||||
import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneActor}
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.dome.ForceDomePhysics
|
||||
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloControl
|
||||
|
|
@ -107,9 +108,12 @@ case object MajorFacilityLogic
|
|||
}
|
||||
// No map update needed - will be sent by `HackCaptureActor` when required
|
||||
case dome: ForceDomePhysics =>
|
||||
val building = details.building
|
||||
// The force dome being expanded modifies the NTU drain rate
|
||||
val multiplier: Float = calculateNtuDrainMultiplierFrom(details.building, domeOpt = Some(dome))
|
||||
details.building.NtuSource.foreach(_.Actor ! ResourceSiloControl.DrainMultiplier(multiplier))
|
||||
building.NtuSource.foreach(_.Actor ! ResourceSiloControl.DrainMultiplier(multiplier))
|
||||
// The force dome being expanded marks the generator as being invulnerable; it can be damaged otherwise
|
||||
building.Generator.foreach { _.Actor ! Damageable.Vulnerability(dome.Energized) }
|
||||
case _ =>
|
||||
details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage()))
|
||||
}
|
||||
|
|
@ -373,7 +377,10 @@ case object MajorFacilityLogic
|
|||
): Float = {
|
||||
// The force dome being expanded means all repairs are essentially for free
|
||||
dome
|
||||
.map { case d if d.Energized => 0f }
|
||||
.flatMap {
|
||||
case d if d.Energized => Some(0f)
|
||||
case _ => None
|
||||
}
|
||||
.orElse {
|
||||
mainTerminal.flatMap { _ => Some(2f) } //todo main terminal and viruses
|
||||
}
|
||||
|
|
|
|||
|
|
@ -959,17 +959,57 @@ object GlobalDefinitionsMiscellaneous {
|
|||
|
||||
force_dome_amp_physics.Name = "force_dome_amp_physics"
|
||||
force_dome_amp_physics.UseRadius = 142.26f
|
||||
force_dome_amp_physics.PerimeterOffsets = List(
|
||||
Vector3(83.05469f, 114.875f, 0f),
|
||||
Vector3(-90.328125f, 114.875f, 0f),
|
||||
Vector3(-90.328125f, -106.90625f, 0f),
|
||||
Vector3(83.05469f, -106.90625f, 0f)
|
||||
)
|
||||
|
||||
force_dome_comm_physics.Name = "force_dome_comm_physics"
|
||||
force_dome_comm_physics.UseRadius = 121.8149f
|
||||
force_dome_comm_physics.PerimeterOffsets = List(
|
||||
Vector3(35.1875f, -89.859375f, 0f),
|
||||
Vector3(80.96875f, -43.773438f, 0f),
|
||||
Vector3(80.96875f, 91.08594f, 0f),
|
||||
Vector3(-37.296875f, 91.08594f, 0f),
|
||||
Vector3(-83.640625f, 45.601562f, 0f),
|
||||
Vector3(-83.640625f, -89.859375f, 0f)
|
||||
)
|
||||
|
||||
force_dome_cryo_physics.Name = "force_dome_cryo_physics"
|
||||
force_dome_cryo_physics.UseRadius = 127.9241f //127.7963f
|
||||
force_dome_cryo_physics.PerimeterOffsets = List(
|
||||
Vector3(72.75476f, 39.902725f, 0),
|
||||
Vector3(24.505968f, 88.03482f, 0),
|
||||
Vector3(-74.73426f, 88.03482f, 0),
|
||||
Vector3(-74.73426f, -103.47f, 0),
|
||||
Vector3(72.75476f, -103.47f, 0)
|
||||
)
|
||||
|
||||
force_dome_dsp_physics.Name = "force_dome_dsp_physics"
|
||||
force_dome_dsp_physics.UseRadius = 175.8838f //175.7081f
|
||||
force_dome_dsp_physics.PerimeterOffsets = List(
|
||||
Vector3(35.03125f, -93.25f, 0f),
|
||||
Vector3(-83.1875f, -93.25f, 0f),
|
||||
Vector3(-83.1875f, 114.515625f, 0f),
|
||||
Vector3(-12.109375f, 188.26562f, 0f),
|
||||
Vector3(130.44531f, 188.26562f, 0f),
|
||||
Vector3(130.44531f, -93.28125f, 0f)
|
||||
)
|
||||
|
||||
force_dome_tech_physics.Name = "force_dome_tech_physics"
|
||||
force_dome_tech_physics.UseRadius = 150.1284f
|
||||
force_dome_tech_physics.PerimeterOffsets = List( //todo double-check eisa, esamir
|
||||
Vector3(130.14636f, -95.20665f, 0f),
|
||||
Vector3(130.14636f, 34.441734f, 0f),
|
||||
Vector3(103.98575f, 52.58408f, 0f),
|
||||
Vector3(16.405174f, 54.746464f, 0f),
|
||||
Vector3(14.256668f, 107.01521f, 0f),
|
||||
Vector3(-92.08687f, 107.01521f, 0f),
|
||||
Vector3(-92.08687f, -96.176155f, 0f),
|
||||
Vector3(-73.64424f, -114.65837f, 0f),
|
||||
Vector3(102.12191f, -114.65837f, 0f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations
|
|||
* All of these should be affected by the damage where applicable.
|
||||
*/
|
||||
trait Damageable {
|
||||
|
||||
/**
|
||||
* Contextual access to the object being the target of this damage.
|
||||
* Needs declaration in lowest implementing code.
|
||||
|
|
@ -25,19 +24,34 @@ trait Damageable {
|
|||
*/
|
||||
def DamageableObject: Damageable.Target
|
||||
|
||||
/** a local `canDamage` flag */
|
||||
private var isVulnerable: Boolean = true
|
||||
|
||||
/** the official mixin hook;
|
||||
* `orElse` onto the "control" `Actor` `receive`; or,
|
||||
* cite the `originalTakesDamage` protocol during inheritance overrides */
|
||||
val takesDamage: Receive = {
|
||||
case Damageable.MakeVulnerable =>
|
||||
isVulnerable = false
|
||||
|
||||
case Damageable.MakeInvulnerable =>
|
||||
isVulnerable = true
|
||||
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
if (isVulnerable && obj.CanDamage) {
|
||||
PerformDamage(obj, damage_func)
|
||||
}
|
||||
}
|
||||
|
||||
/** a duplicate of the core implementation for the default mixin hook, for use in overriding */
|
||||
final val originalTakesDamage: Receive = {
|
||||
case Damageable.MakeVulnerable =>
|
||||
isVulnerable = false
|
||||
|
||||
case Damageable.MakeInvulnerable =>
|
||||
isVulnerable = true
|
||||
|
||||
case Vitality.Damage(damage_func) =>
|
||||
val obj = DamageableObject
|
||||
if (obj.CanDamage) {
|
||||
|
|
@ -67,6 +81,20 @@ object Damageable {
|
|||
*/
|
||||
final val LogChannel: String = "DamageResolution"
|
||||
|
||||
trait PersonalVulnerability
|
||||
|
||||
final case object MakeVulnerable extends PersonalVulnerability
|
||||
|
||||
final case object MakeInvulnerable extends PersonalVulnerability
|
||||
|
||||
def Vulnerability(state: Boolean): PersonalVulnerability = {
|
||||
if (state) {
|
||||
MakeInvulnerable
|
||||
} else {
|
||||
MakeVulnerable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the possibility exist that the designated target can be affected by this projectile's damage?
|
||||
* @see `Hackable`
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import net.psforever.objects.PlanetSideGameObject
|
|||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl}
|
||||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
|
||||
import net.psforever.objects.serverobject.turret.FacilityTurret
|
||||
import net.psforever.objects.sourcing.SourceEntry
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.vital.etc.ForceDomeExposure
|
||||
|
|
@ -19,6 +18,8 @@ import net.psforever.services.Service
|
|||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGeneratorState, Vector3}
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
object ForceDomeControl {
|
||||
trait Command
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ object ForceDomeControl {
|
|||
|
||||
/**
|
||||
* Dispatch a message to update the state of the clients with the server state of the capitol force dome.
|
||||
* @param dome force dome
|
||||
* @param dome force dome
|
||||
* @param activationState new force dome status
|
||||
*/
|
||||
def ChangeDomeEnergizedState(dome: ForceDomePhysics, activationState: Boolean): Unit = {
|
||||
|
|
@ -49,7 +50,7 @@ object ForceDomeControl {
|
|||
* use the faction affinity, the generator status, and the resource silo's capacitance level
|
||||
* to determine if the capitol force dome should be active.
|
||||
* @param building building being evaluated
|
||||
* @param dome force dome
|
||||
* @param dome force dome
|
||||
* @return the condition of the capitol force dome;
|
||||
* `None`, if the facility is not a capitol building;
|
||||
* `Some(true|false)` to indicate the state of the force dome
|
||||
|
|
@ -78,7 +79,7 @@ object ForceDomeControl {
|
|||
* for capitol force dome expansion.
|
||||
* @param building target building
|
||||
* @return `true`, if the conditions for capitol force dome are not met;
|
||||
* `false`, otherwise
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def InvalidBuildingCapitolForceDomeConditions(building: Building): Boolean = {
|
||||
building.Faction == PlanetSideEmpire.NEUTRAL ||
|
||||
|
|
@ -87,59 +88,30 @@ object ForceDomeControl {
|
|||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* Apply a fixed point and a rotation value to a series of vertex offsets,
|
||||
* then daisy-chain the resulting vertices in such a way that
|
||||
* it creates a perimeter around the (building) owner of the capitol force dome.
|
||||
* The resulting capitol force dome barrier is a blocky pyramoid shape.
|
||||
* @param dome force dome
|
||||
* @return na
|
||||
* @return perimeter of the force dome barrier
|
||||
*/
|
||||
def GeneralFacilityPerimeter(dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
|
||||
val generatorTowerCenter = dome.Position.xy
|
||||
val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
|
||||
val pointsOfForceDomePerimeter = turretPoints.map { p =>
|
||||
val segmentFromTowerToTurret = p - generatorTowerCenter
|
||||
Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset
|
||||
def SetupForceDomePerimeter(dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
|
||||
val center = dome.Position.xy
|
||||
val rotation = math.toRadians(dome.Owner.Orientation.z).toFloat
|
||||
val perimeterOffsets = dome.Definition.PerimeterOffsets
|
||||
val perimeterPoints = perimeterOffsets.map {
|
||||
center + Vector3.PlanarRotateAroundPoint(_, Vector3(0, 0, 1), rotation)
|
||||
}
|
||||
pointsOfForceDomePerimeter
|
||||
.flatMap { point =>
|
||||
pointsOfForceDomePerimeter
|
||||
.sortBy(p => Vector3.DistanceSquared(p, point))
|
||||
.slice(1, 3)
|
||||
.map { otherPoint =>
|
||||
if (point.y > otherPoint.y || point.x < otherPoint.x) {
|
||||
(point, otherPoint)
|
||||
} else {
|
||||
(otherPoint, point)
|
||||
}
|
||||
}
|
||||
}
|
||||
.distinct
|
||||
}
|
||||
|
||||
import scala.annotation.unused
|
||||
def TechPlantFacilityPerimeter(@unused dome: ForceDomePhysics): List[(Vector3, Vector3)] = {
|
||||
// val generatorTowerCenter = dome.Position.xy
|
||||
// val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
|
||||
// val organizedByClosestToGarage = dome
|
||||
// .Owner
|
||||
// .Amenities
|
||||
// .find(_.Definition.Name.equals("gr_door_garage_ext"))
|
||||
// .map { garage =>
|
||||
// val doorPosition = garage.Position.xy
|
||||
// turretPoints.sortBy(point => Vector3.DistanceSquared(doorPosition, point))
|
||||
// }
|
||||
// .getOrElse(List[Vector3]())
|
||||
//
|
||||
// //val turretPoints = dome.Owner.Amenities.filter(_.isInstanceOf[FacilityTurret]).map(_.Position.xy)
|
||||
// val pointsOfForceDomePerimeter = turretPoints.map { p =>
|
||||
// val segmentFromTowerToTurret = p - generatorTowerCenter
|
||||
// Vector3.Unit(segmentFromTowerToTurret) * (Vector3.Magnitude(segmentFromTowerToTurret) + 20) //todo get correct distance offset
|
||||
// }
|
||||
Nil
|
||||
((0 until perimeterPoints.size - 1).map { index =>
|
||||
(perimeterPoints(index), perimeterPoints(index + 1))
|
||||
} :+ (perimeterPoints.last, perimeterPoints.head)).toList
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* The capitol force dome should have changed states but it will not!
|
||||
* Make certain everyone knows!
|
||||
* @param building target building
|
||||
* @param state na
|
||||
* @param state whether the force dome is energized or not
|
||||
*/
|
||||
def CustomDomeStateEnforcedMessage(
|
||||
building: Building,
|
||||
|
|
@ -156,7 +128,8 @@ object ForceDomeControl {
|
|||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* The capitol force dome will start changing states normally.
|
||||
* Make certain everyone knows.
|
||||
* @param building facility
|
||||
*/
|
||||
def NormalDomeStateMessage(building: Building): Unit = {
|
||||
|
|
@ -179,19 +152,19 @@ object ForceDomeControl {
|
|||
* pass a message onto that facility that it should check its own state alignment.
|
||||
* @param building facility with `dome`
|
||||
*/
|
||||
def AlignForceDomeStatusAndUpdate(building: Building, dome: ForceDomePhysics): Unit = {
|
||||
CheckForceDomeStatus(building, dome).foreach {
|
||||
case true =>
|
||||
if (!dome.Energized) {
|
||||
ChangeDomeEnergizedState(dome, activationState = true)
|
||||
ForceDomeKills(dome)
|
||||
dome.Owner.Actor ! BuildingActor.MapUpdate()
|
||||
}
|
||||
case false =>
|
||||
if (dome.Energized) {
|
||||
ChangeDomeEnergizedState(dome, activationState = false)
|
||||
dome.Owner.Actor ! BuildingActor.MapUpdate()
|
||||
}
|
||||
def AlignForceDomeStatusAndUpdate(building: Building, dome: ForceDomePhysics): Boolean = {
|
||||
val energizedState = dome.Energized
|
||||
CheckForceDomeStatus(building, dome).exists {
|
||||
case true if !energizedState =>
|
||||
dome.Owner.Actor ! BuildingActor.MapUpdate()
|
||||
ChangeDomeEnergizedState(dome, activationState = true)
|
||||
true
|
||||
case false if energizedState =>
|
||||
ChangeDomeEnergizedState(dome, activationState = false)
|
||||
dome.Owner.Actor ! BuildingActor.MapUpdate()
|
||||
false
|
||||
case _ =>
|
||||
energizedState
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,44 +177,44 @@ object ForceDomeControl {
|
|||
* pass a message onto that facility that it should check its own state alignment.
|
||||
* @param building facility with `dome`
|
||||
*/
|
||||
private def AlignForceDomeStatus(building: Building, dome: ForceDomePhysics): Unit = {
|
||||
CheckForceDomeStatus(building, dome).foreach {
|
||||
case true =>
|
||||
if (!dome.Energized) {
|
||||
ChangeDomeEnergizedState(dome, activationState = true)
|
||||
ForceDomeKills(dome)
|
||||
}
|
||||
case false =>
|
||||
if (dome.Energized) {
|
||||
ChangeDomeEnergizedState(dome, activationState = false)
|
||||
}
|
||||
private def AlignForceDomeStatus(building: Building, dome: ForceDomePhysics): Boolean = {
|
||||
val energizedState = dome.Energized
|
||||
CheckForceDomeStatus(building, dome).exists {
|
||||
case true if !energizedState =>
|
||||
ChangeDomeEnergizedState(dome, activationState = true)
|
||||
true
|
||||
case false if energizedState =>
|
||||
ChangeDomeEnergizedState(dome, activationState = false)
|
||||
false
|
||||
case _ =>
|
||||
energizedState
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Being too close to the force dome can destroy targets if they do not match the faction alignment of the dome.
|
||||
* This is the usual fate of opponents upon it being expanded (energeized).
|
||||
* This is the usual fate of opponents upon it being expanded (energized).
|
||||
* @see `Zone.serverSideDamage`
|
||||
* @param dome force dome
|
||||
* @return a list of affected entities
|
||||
*/
|
||||
def ForceDomeKills(dome: ForceDomePhysics): List[PlanetSideServerObject] = {
|
||||
def ForceDomeKills(dome: ForceDomePhysics, perimeter: List[(Vector3, Vector3)]): List[PlanetSideServerObject] = {
|
||||
Zone.serverSideDamage(
|
||||
dome.Zone,
|
||||
dome,
|
||||
contactWithForceDome,
|
||||
Zone.distanceCheck,
|
||||
makesContactWithForceDome,
|
||||
targetUnderForceDome(perimeter),
|
||||
forceDomeTargets(dome.Definition.UseRadius, dome.Faction)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* Prepare damage information related to being caugt underneath the capitol force dome when it expands.
|
||||
* @param source a game object that represents the source of the explosion
|
||||
* @param target a game object that is affected by the explosion
|
||||
* @return a `DamageInteraction` object
|
||||
*/
|
||||
private def contactWithForceDome(
|
||||
private def makesContactWithForceDome(
|
||||
source: PlanetSideGameObject with FactionAffinity with Vitality,
|
||||
target: PlanetSideGameObject with FactionAffinity with Vitality
|
||||
): DamageInteraction = {
|
||||
|
|
@ -254,6 +227,84 @@ object ForceDomeControl {
|
|||
|
||||
/**
|
||||
* na
|
||||
* @see `Zone.distanceCheck`
|
||||
* @param segments ground-level perimeter of the force dome is defined by these segments (as vertex pairs)
|
||||
* @param obj1 a game entity, should be the force dome
|
||||
* @param obj2 a game entity, should be a damageable target of the force dome's wrath
|
||||
* @param maxDistance ot applicable
|
||||
* @return `true`, if target is detected within the force dome kill region
|
||||
* `false`, otherwise
|
||||
*/
|
||||
private def targetUnderForceDome(
|
||||
segments: List[(Vector3, Vector3)]
|
||||
)
|
||||
(
|
||||
obj1: PlanetSideGameObject,
|
||||
obj2: PlanetSideGameObject,
|
||||
@unused maxDistance: Float
|
||||
): Boolean = {
|
||||
val centerPos @ Vector3(centerX, centerY, centerZ) = obj1.Position
|
||||
val Vector3(targetX, targetY, targetZ) = obj2.Position - centerPos //deltas of segment of target to dome
|
||||
val checkForIntersection = segments.exists { case (point1, point2) =>
|
||||
//want targets within the perimeter; if there's an intersection, target is outside of the perimeter
|
||||
segmentIntersectionTestPerSegment(centerX, centerY, targetX, targetY, point1.x, point1.y, point2.x, point2.y)
|
||||
}
|
||||
!checkForIntersection && (targetZ < centerZ || Zone.distanceCheck(obj1, obj2, math.pow(obj1.Definition.UseRadius, 2).toFloat))
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to assist line segment intersection tests.
|
||||
* The important frame of reference is checking whether a hypothetical segment between a point and a target
|
||||
* intersects with an established line segment between two other points.
|
||||
* For our purposes, the resulting line segments will never be collinear, so there is no reason to test that.
|
||||
* @param pointX x-coordinate used to create a test segment
|
||||
* @param pointY y-coordinate used to create a test segment
|
||||
* @param targetX x-coordinate of an important point for a test segment
|
||||
* @param targetY y-coordinate of an important point for a test segment
|
||||
* @param segmentPoint1x x-coordinate of one point from a segment
|
||||
* @param segmentPoint1y y-coordinate of one point from a segment
|
||||
* @param segmentPoint2x x-coordinate of a different point from a segment
|
||||
* @param segmentPoint2y y-coordinate of a different point from a segment
|
||||
* @return `true`, if the points form into two segments that intersect;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
private def segmentIntersectionTestPerSegment(
|
||||
pointX: Float,
|
||||
pointY: Float,
|
||||
targetX: Float,
|
||||
targetY: Float,
|
||||
segmentPoint1x: Float,
|
||||
segmentPoint1y: Float,
|
||||
segmentPoint2x: Float,
|
||||
segmentPoint2y: Float
|
||||
): Boolean = {
|
||||
//based on Franklin Antonio's "Faster Line Segment Intersection" topic "in Graphics Gems III" book (http://www.graphicsgems.org/)
|
||||
//compare, java.awt.geom.Line2D.linesIntersect
|
||||
val bx = segmentPoint1x - segmentPoint2x //delta-x of segment
|
||||
val by = segmentPoint1y - segmentPoint2y //delta-y of segment
|
||||
val cx = pointX - segmentPoint1x //delta-x of hypotenuse of triangle formed by center, segment endpoint, and intersection point
|
||||
val cy = pointY - segmentPoint1y //delta-y of hypotenuse of triangle formed by center, segment endpoint, and intersection point
|
||||
val alphaNumerator = by * cx - bx * cy
|
||||
val commonDenominator = targetY * bx - targetX * by
|
||||
val betaNumerator = targetX * cy - targetY * cx
|
||||
if (
|
||||
commonDenominator > 0 &&
|
||||
(alphaNumerator < 0 || alphaNumerator > commonDenominator || betaNumerator < 0 || betaNumerator > commonDenominator)
|
||||
) {
|
||||
false
|
||||
} else if (
|
||||
commonDenominator < 0 &&
|
||||
(alphaNumerator > 0 || alphaNumerator < commonDenominator || betaNumerator > 0 || betaNumerator < commonDenominator)
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
//a collinear line test could go here, but we don't need it
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all enemy players, vehicles, and combat engineering deployables in a sector.
|
||||
* @see `DamageWithPosition`
|
||||
* @see `Zone.blockMap.sector`
|
||||
* @param zone the zone in which the explosion should occur
|
||||
|
|
@ -292,8 +343,13 @@ class ForceDomeControl(dome: ForceDomePhysics)
|
|||
def CaptureTerminalAwareObject: Amenity with CaptureTerminalAware = dome
|
||||
def FactionObject: FactionAffinity = dome
|
||||
|
||||
/** a capitol force dome's owner should always be a facility, preferably the capitol facility of the continent;
|
||||
* to save time, casted this entity and cache it for repeated use once;
|
||||
* force dome is not immediately owned (by correct facility) so delay determination */
|
||||
private lazy val domeOwnerAsABuilding = dome.Owner.asInstanceOf[Building]
|
||||
|
||||
/** ground-level perimeter of the force dome is defined by these segments (as vertex pairs) */
|
||||
private val perimeterSegments: List[(Vector3, Vector3)] = ForceDomeControl.SetupForceDomePerimeter(dome)
|
||||
/** force the dome into a certain state regardless of what conditions would normally transition it into that state */
|
||||
private var customState: Option[Boolean] = None
|
||||
|
||||
def commonBehavior: Receive = checkBehavior
|
||||
|
|
@ -325,6 +381,7 @@ class ForceDomeControl(dome: ForceDomePhysics)
|
|||
customState = None
|
||||
ForceDomeControl.NormalDomeStateMessage(domeOwnerAsABuilding)
|
||||
ForceDomeControl.AlignForceDomeStatusAndUpdate(domeOwnerAsABuilding, dome)
|
||||
ForceDomeControl.ForceDomeKills(dome, perimeterSegments)
|
||||
}
|
||||
|
||||
def poweredStateLogic: Receive = {
|
||||
|
|
@ -363,6 +420,10 @@ class ForceDomeControl(dome: ForceDomePhysics)
|
|||
deenergizeUnlessSuppressedDueToCustomState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Power down the force dome if it was previously being powered and
|
||||
* as long as a custom state of being energized is not being enforced.
|
||||
*/
|
||||
private def deenergizeUnlessSuppressedDueToCustomState(): Unit = {
|
||||
if (dome.Energized) {
|
||||
if (customState.isEmpty) {
|
||||
|
|
@ -374,25 +435,22 @@ class ForceDomeControl(dome: ForceDomePhysics)
|
|||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* Yield to a custom value enforcing a certain force dome state - energized or powered down.
|
||||
* If the custom state is not declared, run the function and analyze any change in the force dome's natural state.
|
||||
* @param func function to run if not blocked
|
||||
* @return next behavior for an actor state
|
||||
* @return current energized state of the dome
|
||||
*/
|
||||
private def blockedByCustomStateOr(func: (Building, ForceDomePhysics) => Unit): Unit = {
|
||||
blockedByCustomStateOr(func, domeOwnerAsABuilding, dome)
|
||||
}
|
||||
/**
|
||||
* na
|
||||
* @param func function to run if not blocked
|
||||
* @param building facility to operate upon (parameter to `func`)
|
||||
* @return next behavior for an actor state
|
||||
*/
|
||||
private def blockedByCustomStateOr(func: (Building, ForceDomePhysics) => Unit, building: Building, dome: ForceDomePhysics): Unit = {
|
||||
private def blockedByCustomStateOr(func: (Building, ForceDomePhysics) => Boolean): Boolean = {
|
||||
customState match {
|
||||
case None =>
|
||||
func(building, dome)
|
||||
val newState = func(domeOwnerAsABuilding, dome)
|
||||
if (!dome.Energized && newState) {
|
||||
ForceDomeControl.ForceDomeKills(dome, perimeterSegments)
|
||||
}
|
||||
newState
|
||||
case Some(state) =>
|
||||
ForceDomeControl.CustomDomeStateEnforcedMessage(building, state)
|
||||
ForceDomeControl.CustomDomeStateEnforcedMessage(domeOwnerAsABuilding, state)
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,20 @@
|
|||
package net.psforever.objects.serverobject.dome
|
||||
|
||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class ForceDomeDefinition(objectId: Int)
|
||||
extends AmenityDefinition(objectId) {
|
||||
Name = "force_dome"
|
||||
/** offsets that define the perimeter of the pyramidal force "dome" barrier;
|
||||
* these points are the closest to where the dome interacts with the ground at a corner;
|
||||
* should be sequential, either clockwise or counterclockwise */
|
||||
private var perimeter: List[Vector3] = List()
|
||||
|
||||
def PerimeterOffsets: List[Vector3] = perimeter
|
||||
|
||||
def PerimeterOffsets_=(points: List[Vector3]): List[Vector3] = {
|
||||
perimeter = points
|
||||
PerimeterOffsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue