logic for declaring door occupants/users as inside or outside or within an open door using door declarations, geometry and environment fields, and math; divided the remaining object type definitions into their own files and removed those from the main global definitions file

This commit is contained in:
Fate-JH 2024-03-09 09:37:40 -05:00
parent d17c16fd9b
commit b0d3c63b83
21 changed files with 1423 additions and 772 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
import net.psforever.objects.avatar.interaction.{WithGantry, WithLava, WithWater}
import net.psforever.objects.avatar.interaction.{WithEntrance, WithGantry, WithLava, WithWater}
import net.psforever.objects.avatar.{Avatar, LoadoutManager, SpecialCarry}
import net.psforever.objects.ballistics.InteractWithRadiationClouds
import net.psforever.objects.ce.{Deployable, InteractWithMines, InteractWithTurrets}
@ -12,6 +12,7 @@ import net.psforever.objects.serverobject.{PlanetSideServerObject, environment}
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.serverobject.environment.interaction.common.{WithDeath, WithMovementTrigger}
import net.psforever.objects.serverobject.interior.InteriorAwareFromInteraction
import net.psforever.objects.serverobject.mount.MountableEntity
import net.psforever.objects.vital.resistance.ResistanceProfile
import net.psforever.objects.vital.{HealFromEquipment, InGameActivity, RepairFromEquipment, Vitality}
@ -35,9 +36,11 @@ class Player(var avatar: Avatar)
with Container
with JammableUnit
with ZoneAware
with InteriorAwareFromInteraction
with AuraContainer
with MountableEntity {
interaction(environment.interaction.InteractWithEnvironment(Seq(
new WithEntrance(avatar.name),
new WithWater(avatar.name),
new WithLava(),
new WithDeath(),

View file

@ -0,0 +1,79 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.avatar.interaction
import net.psforever.objects.serverobject.doors.{Door, InteriorDoorPassage}
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentTrait, PieceOfEnvironment, interaction}
import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment}
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.types.Vector3
import scala.concurrent.duration._
class WithEntrance(val channel: String)
extends InteractionWith {
val attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
private var stopTest: Boolean = false
private var sideAware: Sidedness = Sidedness.InBetweenSides
def doInteractingWith(
obj: InteractsWithZone,
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
if (stopTest && data.contains("bellybutton")) {
stopTest = false
} else {
val door = body.asInstanceOf[InteriorDoorPassage].door
if (door.isOpen) {
sideAware = Sidedness.InBetweenSides
} else {
performInteriorCheck(obj, door)
}
obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 250 milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("bellybutton")))
}
}
override def stopInteractingWith(
obj: InteractsWithZone,
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
performInteriorCheck(obj, body.asInstanceOf[InteriorDoorPassage].door)
stopTest = true
}
private def performInteriorCheck(
obj: InteractsWithZone,
door: Door
): Sidedness = {
import net.psforever.packet.game.ChatMsg
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideGUID}
val result = Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f
if (result && sideAware != Sidedness.OutsideOf) {
//outside
sideAware = Sidedness.OutsideOf
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now outside"))
)
} else if (!result && sideAware != Sidedness.InsideOf) {
//inside
sideAware = Sidedness.InsideOf
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now inside"))
)
}
sideAware
}
def ThisSide: Sidedness = sideAware
def ThisSide_=(thisSide: Sidedness): Unit = {
sideAware = thisSide
ThisSide
}
}

View file

@ -80,7 +80,7 @@ class WithWater(val channel: String)
override def recoverFromInteracting(obj: InteractsWithZone): Unit = {
super.recoverFromInteracting(obj)
if (condition.exists(_.state == OxygenState.Suffocation)) {
if (condition.exists(info => info.state == OxygenState.Suffocation && info.progress < 99f)) {
stopInteractingWith(obj, condition.map(_.body).get, None)
}
waterInteractionTime = 0L

View file

@ -0,0 +1,34 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.geometry
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.geometry.d2.Rectangle
import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentCollision, EnvironmentTrait, PieceOfEnvironment}
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
case class VolumetricEnvironmentCollision(door: Door)
extends EnvironmentCollision {
private lazy val geometry = door.Definition.Geometry.apply(door)
private lazy val bound: Rectangle = {
val g = geometry
Rectangle(
g.pointOnOutside(Vector3(0, 1,0)).y,
g.pointOnOutside(Vector3(-1,0,0)).x,
g.pointOnOutside(Vector3(0,-1,0)).y,
g.pointOnOutside(Vector3( 1,0,0)).x
)
}
def Geometry: VolumetricGeometry = geometry
def altitude: Float = geometry.pointOnOutside(Vector3(0,0,1)).z
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
Zone.distanceCheck(obj.Definition.Geometry(obj), geometry) <= varDepth
}
def bounding: Rectangle = bound
}

View file

@ -0,0 +1,176 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.SpawnPoint
import net.psforever.types.LatticeBenefit
object GlobalDefinitionsBuilding {
import GlobalDefinitions._
/**
* Initialize `BuildingDefinition` globals.
*/
def init(): Unit = {
amp_station.Name = "amp_station"
amp_station.SOIRadius = 300
amp_station.LatticeLinkBenefit = LatticeBenefit.AmpStation
comm_station.Name = "comm_station"
comm_station.SOIRadius = 300
comm_station.LatticeLinkBenefit = LatticeBenefit.InterlinkFacility
comm_station_dsp.Name = "comm_station_dsp"
comm_station_dsp.SOIRadius = 300
comm_station_dsp.LatticeLinkBenefit = LatticeBenefit.DropshipCenter
cryo_facility.Name = "cryo_facility"
cryo_facility.SOIRadius = 300
cryo_facility.LatticeLinkBenefit = LatticeBenefit.BioLaboratory
tech_plant.Name = "tech_plant"
tech_plant.SOIRadius = 300
tech_plant.LatticeLinkBenefit = LatticeBenefit.TechnologyPlant
building.Name = "building"
vanu_core.Name = "vanu_core"
ground_bldg_a.Name = "ground_bldg_a"
ground_bldg_b.Name = "ground_bldg_b"
ground_bldg_c.Name = "ground_bldg_c"
ground_bldg_d.Name = "ground_bldg_d"
ground_bldg_e.Name = "ground_bldg_e"
ground_bldg_f.Name = "ground_bldg_f"
ground_bldg_g.Name = "ground_bldg_g"
ground_bldg_h.Name = "ground_bldg_h"
ground_bldg_i.Name = "ground_bldg_i"
ground_bldg_j.Name = "ground_bldg_j"
ground_bldg_z.Name = "ground_bldg_z"
ceiling_bldg_a.Name = "ceiling_bldg_a"
ceiling_bldg_b.Name = "ceiling_bldg_b"
ceiling_bldg_c.Name = "ceiling_bldg_c"
ceiling_bldg_d.Name = "ceiling_bldg_d"
ceiling_bldg_e.Name = "ceiling_bldg_e"
ceiling_bldg_f.Name = "ceiling_bldg_f"
ceiling_bldg_g.Name = "ceiling_bldg_g"
ceiling_bldg_h.Name = "ceiling_bldg_h"
ceiling_bldg_i.Name = "ceiling_bldg_i"
ceiling_bldg_j.Name = "ceiling_bldg_j"
ceiling_bldg_z.Name = "ceiling_bldg_z"
mainbase1.Name = "mainbase1"
mainbase2.Name = "mainbase2"
mainbase3.Name = "mainbase3"
meeting_center_nc.Name = "meeting_center_nc"
meeting_center_tr.Name = "meeting_center_tr"
meeting_center_vs.Name = "meeting_center_vs"
minibase1.Name = "minibase1"
minibase2.Name = "minibase2"
minibase3.Name = "minibase3"
redoubt.Name = "redoubt"
redoubt.SOIRadius = 187
tower_a.Name = "tower_a"
tower_a.SOIRadius = 50
tower_b.Name = "tower_b"
tower_b.SOIRadius = 50
tower_c.Name = "tower_c"
tower_c.SOIRadius = 50
vanu_control_point.Name = "vanu_control_point"
vanu_control_point.SOIRadius = 187
vanu_vehicle_station.Name = "vanu_vehicle_station"
vanu_vehicle_station.SOIRadius = 187
hst.Name = "hst"
hst.UseRadius = 44.96882005f
hst.SOIRadius = 82
hst.VehicleAllowance = true
hst.NoWarp += dropship
hst.NoWarp += galaxy_gunship
hst.NoWarp += lodestar
hst.NoWarp += aphelion_gunner
hst.NoWarp += aphelion_flight
hst.NoWarp += colossus_gunner
hst.NoWarp += colossus_flight
hst.NoWarp += peregrine_gunner
hst.NoWarp += peregrine_flight
hst.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 6f)
warpgate.Name = "warpgate"
warpgate.UseRadius = 67.81070029f
warpgate.SOIRadius = 302 //301.8713f
warpgate.VehicleAllowance = true
warpgate.SpecificPointFunc = SpawnPoint.Gate
warpgate_cavern.Name = "warpgate_cavern"
warpgate_cavern.UseRadius = 19.72639434f
warpgate_cavern.SOIRadius = 41
warpgate_cavern.VehicleAllowance = true
warpgate_cavern.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 4.5f)
warpgate_small.Name = "warpgate_small"
warpgate_small.UseRadius = 69.03687655f
warpgate_small.SOIRadius = 103
warpgate_small.VehicleAllowance = true
warpgate_small.SpecificPointFunc = SpawnPoint.SmallGate(innerRadius = 27.60654127f, flightlessZOffset = 0.5f)
bunker_gauntlet.Name = "bunker_gauntlet"
bunker_lg.Name = "bunker_lg"
bunker_sm.Name = "bunker_sm"
orbital_building_nc.Name = "orbital_building_nc"
orbital_building_tr.Name = "orbital_building_tr"
orbital_building_vs.Name = "orbital_building_vs"
VT_building_nc.Name = "VT_building_nc"
VT_building_tr.Name = "VT_building_tr"
VT_building_vs.Name = "VT_building_vs"
vt_dropship.Name = "vt_dropship"
vt_spawn.Name = "vt_spawn"
vt_vehicle.Name = "vt_vehicle"
}
}

View file

@ -0,0 +1,61 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.global
import net.psforever.objects.GlobalDefinitions
import net.psforever.types.ExoSuitType
object GlobalDefinitionsImplant {
import GlobalDefinitions._
/**
* Initialize `ImplantDefinition` globals.
*/
def init(): Unit = {
advanced_regen.Name = "advanced_regen"
advanced_regen.InitializationDuration = 120
advanced_regen.StaminaCost = 2
advanced_regen.CostIntervalDefault = 500
targeting.Name = "targeting"
targeting.InitializationDuration = 60
audio_amplifier.Name = "audio_amplifier"
audio_amplifier.InitializationDuration = 60
audio_amplifier.StaminaCost = 1
audio_amplifier.CostIntervalDefault = 1000
darklight_vision.Name = "darklight_vision"
darklight_vision.InitializationDuration = 60
darklight_vision.ActivationStaminaCost = 3
darklight_vision.StaminaCost = 1
darklight_vision.CostIntervalDefault = 500
melee_booster.Name = "melee_booster"
melee_booster.InitializationDuration = 120
melee_booster.StaminaCost = 10
personal_shield.Name = "personal_shield"
personal_shield.InitializationDuration = 120
personal_shield.StaminaCost = 1
personal_shield.CostIntervalDefault = 600
range_magnifier.Name = "range_magnifier"
range_magnifier.InitializationDuration = 60
second_wind.Name = "second_wind"
second_wind.InitializationDuration = 180
silent_run.Name = "silent_run"
silent_run.InitializationDuration = 90
silent_run.StaminaCost = 1
silent_run.CostIntervalDefault = 333
silent_run.CostIntervalByExoSuitHashMap(ExoSuitType.Infiltration) = 1000
surge.Name = "surge"
surge.InitializationDuration = 90
surge.StaminaCost = 1
surge.CostIntervalDefault = 1000
surge.CostIntervalByExoSuitHashMap(ExoSuitType.Agile) = 500
surge.CostIntervalByExoSuitHashMap(ExoSuitType.Reinforced) = 333
}
}

View file

@ -483,7 +483,7 @@ object GlobalDefinitionsMiscellaneous {
repair_silo.TargetValidation += EffectTarget.Category.Vehicle -> EffectTarget.Validation.RepairSilo
repair_silo.Damageable = false
repair_silo.Repairable = false
recharge_terminal.Name = "recharge_terminal"
recharge_terminal.Interval = 1000
recharge_terminal.UseRadius = 20
@ -598,21 +598,91 @@ object GlobalDefinitionsMiscellaneous {
lock_external.Damageable = false
lock_external.Repairable = false
amp_cap_door.Name = "amp_cap_door"
ancient_door.Name = "ancient_door"
ancient_door.geometryInteractionRadius = Some(1)
ancient_garage_door.Name = "ancient_garage_door"
ancient_garage_door.geometryInteractionRadius = Some(1)
cryo_med_door.Name = "cryo_med_door"
cryo_room_door.Name = "cryo_room_door"
door.Name = "door"
door.Damageable = false
door.Repairable = false
door_airlock.Name = "door_airlock"
door_airlock_orb.Name = "door_airlock_orb"
door_dsp.Name = "door_dsp"
door_garage.Name = "door_garage"
door_interior.Name = "door_interior"
door_mb.Name = "door_mb"
door_mb_garage.Name = "door_mb_garage"
door_mb_main.Name = "door_mb_main"
door_mb_orb.Name = "door_mb_orb"
door_mb_side.Name = "door_mb_side"
door_nc_garage.Name = "door_nc_garage"
door_nc_rotating.Name = "door_nc_rotating"
door_ncside.Name = "door_ncside"
door_orbspawn.Name = "door_orbspawn"
door_spawn_mb.Name = "door_spawn_mb"
door_spawn_mb.Damageable = true
door_spawn_mb.Repairable = false
garage_door.Name = "garage_door"
gr_door_airlock.Name = "gr_door_airlock"
gr_door_ext.Name = "gr_door_ext"
gr_door_ext.geometryInteractionRadius = Some(1)
gr_door_garage_ext.Name = "gr_door_garage_ext"
gr_door_garage_ext.geometryInteractionRadius = Some(1)
gr_door_garage_int.Name = "gr_door_garage_int"
gr_door_int.Name = "gr_door_int"
gr_door_main.Name = "gr_door_main"
gr_door_main.geometryInteractionRadius = Some(1)
gr_door_mb_ext.Name = "gr_door_mb_ext"
gr_door_mb_ext.geometryInteractionRadius = Some(1)
gr_door_mb_int.Name = "gr_door_mb_int"
gr_door_mb_lrg.Name = "gr_door_mb_lrg"
gr_door_mb_lrg.geometryInteractionRadius = Some(1)
gr_door_mb_obsd.Name = "gr_door_mb_obsd"
gr_door_mb_orb.Name = "gr_door_mb_orb"
gr_door_mb_orb.Damageable = false
gr_door_mb_orb.Repairable = false
gr_door_med.Name = "gr_door_med"
main_door.Name = "main_door"
shield_door.Name = "shield_door"
spawn_tube_door.Name = "spawn_tube_door"
spawn_tube_door.Damageable = true
spawn_tube_door.Repairable = false
spawn_tube_door_coffin.Name = "spawn_tube_door_coffin"
spawn_tube_door_coffin.Damageable = true
resource_silo.Name = "resource_silo"
resource_silo.Damageable = false

View file

@ -9,7 +9,7 @@ import net.psforever.objects.definition.converter._
import net.psforever.objects.equipment._
import net.psforever.objects.inventory.InventoryTile
object GlobalDefinitionsTools {
object GlobalDefinitionsTool {
import GlobalDefinitions._
/**

View file

@ -5,6 +5,7 @@ import net.psforever.objects.Player
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.UseItemMessage
import net.psforever.types.Vector3
/**
* A structure-owned server object that is a "door" that can open and can close.
@ -12,6 +13,7 @@ import net.psforever.packet.game.UseItemMessage
*/
class Door(private val ddef: DoorDefinition) extends Amenity {
private var openState: Option[Player] = None
private var outwards: Option[Vector3] = None
def isOpen: Boolean = openState.isDefined
@ -26,6 +28,17 @@ class Door(private val ddef: DoorDefinition) extends Amenity {
Open
}
def Outwards: Vector3 = outwards.getOrElse(Orientation)
def Outwards_=(out: Vector3): Vector3 = {
Outwards_=(Some(out))
}
def Outwards_=(out: Option[Vector3]): Vector3 = {
outwards = out
Outwards
}
/** Doors do not have health, so only check if they are damageable. */
override def CanDamage : Boolean = Definition.Damageable

View file

@ -0,0 +1,17 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.doors
import net.psforever.objects.geometry.VolumetricEnvironmentCollision
import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, EnvironmentCollision, EnvironmentTrait, PieceOfEnvironment}
final case class InteriorDoorPassage(door: Door)
extends PieceOfEnvironment {
assert(door.Definition.geometryInteractionRadius.nonEmpty, s"door ${door.GUID} needs an interaction radius to be volumetric")
//assert(door.Outwards != Vector3.Zero, s"door ${door.GUID} does not have an outwards direction")
/** a general description of this environment */
override def attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
/** a special representation of the region that qualifies as "this environment" */
override def collision: EnvironmentCollision = VolumetricEnvironmentCollision(door)
}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2020-2024 PSForever
package net.psforever.objects.serverobject.environment
import enumeratum.{Enum, EnumEntry}
import net.psforever.objects.serverobject.interior.InteriorAware
import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle}
import net.psforever.objects.vital.Vitality
import net.psforever.types.Vector3
@ -9,14 +9,11 @@ import net.psforever.types.Vector3
/**
* A general description of environment and its interactive possibilities.
*/
sealed abstract class EnvironmentTrait extends EnumEntry {
abstract class EnvironmentTrait {
def canInteractWith(obj: PlanetSideGameObject): Boolean
}
object EnvironmentAttribute extends Enum[EnvironmentTrait] {
/** glue connecting `EnumEntry` to `Enumeration` */
val values: IndexedSeq[EnvironmentTrait] = findValues
object EnvironmentAttribute {
case object Water extends EnvironmentTrait {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
@ -71,4 +68,14 @@ object EnvironmentAttribute extends Enum[EnvironmentTrait] {
}
}
}
case object InteriorField
extends EnvironmentTrait {
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
obj match {
case _: InteriorAware => true
case _ => false
}
}
}
}

View file

@ -1,6 +1,7 @@
// Copyright (c) 2020 PSForever
package net.psforever.objects.serverobject.environment
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.geometry.d2.Rectangle
import net.psforever.types.Vector3
@ -13,14 +14,22 @@ trait EnvironmentCollision {
/** in general, the highest point in this geometry */
def altitude: Float
/**
* Is the test point "within" the bounds of the represented environment?
* @param obj entity to test
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(obj: PlanetSideGameObject): Boolean = testInteraction(obj, varDepth = 0)
/**
* Is the test point "within" the bounds of the represented environment?
* @param pos the test point
* @param obj entity to test
* @param varDepth how far "into" the environment the point must be
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(pos: Vector3, varDepth: Float): Boolean
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean
def bounding: Rectangle
}
@ -32,8 +41,8 @@ trait EnvironmentCollision {
*/
final case class DeepPlane(altitude: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
pos.z + varDepth < altitude
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
obj.Position.z + varDepth < altitude
}
def bounding: Rectangle = {
@ -55,7 +64,8 @@ final case class DeepPlane(altitude: Float)
*/
final case class DeepSquare(altitude: Float, north: Float, east: Float, south: Float, west: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z + varDepth < altitude && north > pos.y && pos.y >= south && east > pos.x && pos.x >= west
}
@ -76,7 +86,8 @@ final case class DeepSquare(altitude: Float, north: Float, east: Float, south: F
*/
final case class DeepSurface(altitude: Float, north: Float, east: Float, south: Float, west: Float)
extends EnvironmentCollision {
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z < altitude && north > pos.y && pos.y >= south && east > pos.x && pos.x >= west
}
@ -95,7 +106,8 @@ final case class DeepCircularSurface(center: Vector3, radius: Float)
def bounding: Rectangle = Rectangle(center.y + radius, center.x + radius, center.y - radius, center.x - radius)
def testInteraction(pos: Vector3, varDepth: Float): Boolean = {
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = {
val pos = obj.Position
pos.z < center.z && Vector3.DistanceSquared(pos.xy, center.xy) < radius * radius
}
}

View file

@ -18,12 +18,12 @@ trait PieceOfEnvironment
/**
* Is the test point "within" the bounds of the represented environment?
* @param pos the test point
* @param obj entity to test
* @param varDepth how far "into" the environment the point must be
* @return `true`, if the point is sufficiently "deep";
* `false`, otherwise
*/
def testInteraction(pos: Vector3, varDepth: Float): Boolean = collision.testInteraction(pos, varDepth)
def testInteraction(obj: PlanetSideGameObject, varDepth: Float): Boolean = collision.testInteraction(obj, varDepth)
/**
* Did the test point move into or leave the bounds of the represented environment since its previous test?
@ -34,8 +34,8 @@ trait PieceOfEnvironment
* `Some(false)`, if the point has left the sufficiently "deep" region;
* `None`, otherwise
*/
def testStepIntoInteraction(pos: Vector3, previousPos: Vector3, varDepth: Float): Option[Boolean] =
PieceOfEnvironment.testStepIntoInteraction(body = this, pos, previousPos, varDepth)
def testStepIntoInteraction(pos: Vector3, obj: PlanetSideGameObject, previousPos: Vector3, varDepth: Float): Option[Boolean] =
PieceOfEnvironment.testStepIntoInteraction(body = this, obj, pos, previousPos, varDepth)
def Position: Vector3 = collision.bounding.center.asVector3 + Vector3.z(collision.altitude)
@ -61,9 +61,19 @@ object PieceOfEnvironment {
* `Some(false)`, if the point has left the sufficiently "deep" region;
* `None`, if the described points only exist outside of or only exists inside of the critical region
*/
def testStepIntoInteraction(body: PieceOfEnvironment, pos: Vector3, previousPos: Vector3, varDepth: Float): Option[Boolean] = {
val isEncroaching = body.collision.testInteraction(pos, varDepth)
val wasEncroaching = body.collision.testInteraction(previousPos, varDepth)
def testStepIntoInteraction(
body: PieceOfEnvironment,
obj: PlanetSideGameObject,
pos: Vector3,
previousPos: Vector3,
varDepth: Float
): Option[Boolean] = {
val originalPosition = obj.Position
obj.Position = pos
val isEncroaching = body.collision.testInteraction(obj, varDepth)
obj.Position = previousPos
val wasEncroaching = body.collision.testInteraction(obj, varDepth)
obj.Position = originalPosition
if (isEncroaching != wasEncroaching) {
Some(isEncroaching)
} else {

View file

@ -117,10 +117,9 @@ object InteractWithEnvironment {
obj: PlanetSideServerObject,
sector: SectorPopulation
): Set[PieceOfEnvironment] = {
val position = obj.Position
val depth = GlobalDefinitions.MaxDepth(obj)
sector.environmentList
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(position, depth))
.filter(body => body.attribute.canInteractWith(obj) && body.testInteraction(obj, depth))
.distinctBy(_.attribute)
.toSet
}
@ -137,7 +136,7 @@ object InteractWithEnvironment {
body: PieceOfEnvironment,
obj: PlanetSideServerObject
): Option[PieceOfEnvironment] = {
if ((obj.Zone eq zone) && body.testInteraction(obj.Position, GlobalDefinitions.MaxDepth(obj))) {
if ((obj.Zone eq zone) && body.testInteraction(obj, GlobalDefinitions.MaxDepth(obj))) {
Some(body)
} else {
None

View file

@ -0,0 +1,34 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.interior
import net.psforever.objects.avatar.interaction.WithEntrance
import net.psforever.objects.serverobject.environment.interaction.InteractWithEnvironment
import net.psforever.objects.zones.InteractsWithZone
import scala.annotation.unused
trait InteriorAware {
def WhichSide: Sidedness
def WhichSide_=(@unused thisSide: Sidedness): Sidedness
}
trait InteriorAwareFromInteraction
extends InteriorAware {
awareness: InteractsWithZone =>
private lazy val withEntrance: Option[WithEntrance] = {
awareness
.interaction()
.collect { case i: InteractWithEnvironment => i.Interactions.values }
.flatten
.collectFirst { case i: WithEntrance => i }
}
def WhichSide: Sidedness = {
withEntrance.map(_.ThisSide).getOrElse(Sidedness.InBetweenSides)
}
def WhichSide_=(thisSide: Sidedness): Sidedness = {
withEntrance.foreach(_.ThisSide = thisSide)
WhichSide
}
}

View file

@ -0,0 +1,22 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.interior
sealed trait Sidedness
sealed trait Inside
sealed trait Outside
sealed trait InBetween extends Inside with Outside
object Sidedness {
case object InsideOf extends Inside with Sidedness
case object OutsideOf extends Outside with Sidedness
case object InBetweenSides extends InBetween with Sidedness
def equals(a: Sidedness, b: Sidedness): Boolean = {
(a eq b) || a == Sidedness.InBetweenSides || b == Sidedness.InBetweenSides
}
}

View file

@ -52,6 +52,7 @@ import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.blockmap.BlockMap
import net.psforever.services.Service
import net.psforever.zones.Zones
import scala.annotation.tailrec
import scala.collection.mutable
@ -664,13 +665,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
case (Some(_), _) | (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error
}
})
//doors with nearby locks use those locks as their unlocking mechanism
//let ZoneActor's sanity check catch missing entities
map.doorToLock
.map { case(doorGUID: Int, lockGUID: Int) => (guid(doorGUID), guid(lockGUID)) }
.collect { case (Some(door: Door), Some(lock: IFFLock)) =>
door.Actor ! Door.UpdateMechanism(IFFLock.testLock(lock))
}
Zone.AssignDoors(zone = this)
//ntu management (eventually move to a generic building startup function)
buildings.values
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
@ -884,6 +879,124 @@ object Zone {
new Zone(id, map, number)
}
private def AssignDoors(zone: Zone): Unit = {
//let ZoneActor's sanity check catch any missing entities
val map = zone.map
val guid = zone.guid
val invalidOutwards = Vector3(0,0,-1) //down
if (map.cavern) {
//cavern doors
//todo what do?
//almost all are type ancient_door and don't have many hints to determine outward-ness; there are no IFF locks
} else if (
PlanetSideEmpire.values
.filterNot(_ == PlanetSideEmpire.NEUTRAL)
.exists(fac => Zones.sanctuaryZoneNumber(fac) == zone.Number)
) {
//sanctuary doors
AssignIFFLockedDoors(zone)
//spawn building doors
val buildings = zone.Buildings.values
val amenityList = buildings
.collect {
case b
if b.Definition.Name.startsWith("VT_building_") =>
val amenities = b.Amenities
(
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_ext),
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_lrg),
amenities.filter(_.Definition == GlobalDefinitions.order_terminal),
amenities.filter(_.Definition == GlobalDefinitions.respawn_tube_sanctuary)
)
}
amenityList.foreach { case (entranceDoors, _, terminals, tubes) =>
entranceDoors.foreach { door =>
val doorPosition = door.Position
val closestTerminal = terminals.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
val closestTube = tubes.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
door.asInstanceOf[Door].Outwards = Vector3.Unit(closestTerminal.Position.xy - closestTube.Position.xy)
}
//todo training zone warp chamber doors
}
//hart building doors
buildings
.collect {
case b
if b.Definition.Name.startsWith("orbital_building_") =>
val amenities = b.Amenities
(
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_ext),
amenities.filter(_.Definition == GlobalDefinitions.gr_door_mb_orb)
)
}
.foreach { case (entranceDoors, hartDoors) =>
entranceDoors.foreach { door =>
val doorPosition = door.Position
val closestHartDoor = hartDoors.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position))
door.asInstanceOf[Door].Outwards = Vector3.Unit(doorPosition.xy - closestHartDoor.Position.xy)
}
}
} else {
//above ground zone doors
AssignIFFLockedDoors(zone)
//for major facilities, external doors in the courtyard are without locks but are paired in opposing directions
val unpairedDoors = zone.Buildings
.values
.collect {
case b
if b.BuildingType == StructureType.Facility && b.Amenities.nonEmpty =>
b.Amenities.collect {
case d: Door
if d.Definition == GlobalDefinitions.gr_door_ext && d.Outwards == Vector3.Zero =>
d
}
}
var pairedDoors = Seq[(Door, Door)]()
unpairedDoors.foreach { buildingUnPairedDoors =>
var volatileUnpairedDoors = buildingUnPairedDoors
while (volatileUnpairedDoors.size > 1) {
val sampleDoor = volatileUnpairedDoors.head
val sampleDoorPosition = sampleDoor.Position.xy
val distances = Float.MaxValue +: volatileUnpairedDoors
.map(d => Vector3.DistanceSquared(d.Position.xy, sampleDoorPosition))
.drop(1)
val min = distances.min
val indexOfClosestDoor = distances.indexWhere(_ == min)
val otherDoor = volatileUnpairedDoors(indexOfClosestDoor)
volatileUnpairedDoors = volatileUnpairedDoors.slice(1, indexOfClosestDoor) ++ volatileUnpairedDoors.drop(indexOfClosestDoor + 1)
pairedDoors = pairedDoors :+ (sampleDoor, otherDoor)
}
volatileUnpairedDoors.foreach { door =>
door.Outwards = invalidOutwards
}
}
pairedDoors.foreach { case (door1, door2) =>
//give each paired courtyard door an outward-ness
val outwards = Vector3.Unit(door1.Position.xy - door2.Position.xy)
door1.Outwards = outwards
door2.Outwards = Vector3.neg(outwards)
}
//bunker doors do not define an interior
}
}
private def AssignIFFLockedDoors(zone: Zone): Unit = {
val map = zone.map
val guid = zone.guid
val invalidOutwards = Vector3(0,0,-1) //down
//doors with nearby locks use those locks as their unlocking mechanism and their outwards indication
map.doorToLock
.map { case (doorGUID: Int, lockGUID: Int) => (guid(doorGUID), guid(lockGUID)) }
.collect {
case (Some(door: Door), Some(lock: IFFLock)) =>
door.Outwards = lock.Outwards
door.Actor ! Door.UpdateMechanism(IFFLock.testLock(lock))
case (Some(door: Door), _) =>
door.Outwards = invalidOutwards
case _ => ()
}
}
object Population {
/**

View file

@ -4,6 +4,7 @@ package net.psforever.objects.zones.blockmap
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.ce.Deployable
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.doors.{Door, InteriorDoorPassage}
import net.psforever.objects.serverobject.environment.PieceOfEnvironment
import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.{Player, Vehicle}
@ -199,6 +200,12 @@ class Sector(val longitude: Int, val latitude: Int, val span: Int)
deployables.list.size < deployables.addTo(d).size
case b: Building =>
buildings.list.size < buildings.addTo(b).size
case d: Door =>
val doorAdded = amenities.list.size < amenities.addTo(d).size
d.Definition.geometryInteractionRadius.collect {
case _ if doorAdded => environment.addTo(InteriorDoorPassage(d))
}
doorAdded
case a: Amenity =>
amenities.list.size < amenities.addTo(a).size
case e: PieceOfEnvironment =>

View file

@ -431,16 +431,15 @@ object Zones {
.addLocalObject(obj.guid, Locker.Constructor(obj.position), owningBuildingGuid = ownerGuid)
case "lock_external" | "lock_garage" | "lock_small" =>
val closestDoor = doors.minBy(d => Vector3.Distance(d.position, obj.position))
// Since tech plant garage locks are the only type where the lock does not face the same direction as the door we need to apply an offset for those, otherwise the door won't operate properly when checking inside/outside angles.
val yawOffset = if (obj.objectType == "lock_garage") 90 else 0
val position = obj.position
val closestDoor = doors.minBy(d => Vector3.Distance(d.position, position))
// Ignore duplicate lock objects, for example Sobek (and other Dropship Centers) CC door has 2 locks stacked on top of each other
if (!zoneMap.doorToLock.keys.iterator.contains(closestDoor.guid)) {
// Since tech plant garage locks are the only type where the lock does not face the same direction as the door we need to apply an offset for those, otherwise the door won't operate properly when checking inside/outside angles.
val yawOffset = if (obj.objectType == "lock_garage") 90 else 0
zoneMap.addLocalObject(
obj.guid,
IFFLock.Constructor(obj.position, Vector3(0, 0, obj.yaw + yawOffset)),
IFFLock.Constructor(position, Vector3.z(obj.yaw + yawOffset)),
owningBuildingGuid = ownerGuid,
doorGuid = closestDoor.guid
)

View file

@ -2,8 +2,8 @@
package objects
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle}
import net.psforever.objects.definition.VehicleDefinition
import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, Tool, Vehicle}
import net.psforever.objects.definition.{ObjectDefinition, VehicleDefinition}
import net.psforever.objects.serverobject.environment._
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalDefinition}
import net.psforever.objects.vital.Vitality
@ -11,6 +11,10 @@ import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.types._
import org.specs2.mutable.Specification
private class PSGOTest() extends PlanetSideGameObject() {
override def Definition: ObjectDefinition = null
}
class EnvironmentCollisionTest extends Specification {
"DeepPlane" should {
val point: Float = 10f
@ -21,15 +25,23 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
plane.testInteraction(Vector3(0,0,10), varDepth = -1) mustEqual true
plane.testInteraction(Vector3(0,0, 9), varDepth = 0) mustEqual true
plane.testInteraction(Vector3(0,0, 8), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(0,0,9)
plane.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(0,0,8)
plane.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
plane.testInteraction(Vector3(0,0,11), varDepth = -1) mustEqual false
plane.testInteraction(Vector3(0,0,10), varDepth = 0) mustEqual false
plane.testInteraction(Vector3(0,0, 9), varDepth = 1) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(0,0,11)
plane.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,0,9)
plane.testInteraction(obj, varDepth = 1) mustEqual false
}
}
@ -42,23 +54,39 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
square.testInteraction(Vector3(1,1, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,8, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(8,8, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(8,1, 0), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,1,10), varDepth = -1) mustEqual true
square.testInteraction(Vector3(1,1, 9), varDepth = 0) mustEqual true
square.testInteraction(Vector3(1,1, 8), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(1,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,8, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,8, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,10)
square.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(1,1, 9)
square.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1, 8)
square.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
square.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual false
square.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual false
square.testInteraction(Vector3(1,1, 9), varDepth = 1) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,9, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,9, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,1, 0)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1,11)
square.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(1,1,10)
square.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1, 9)
square.testInteraction(obj, varDepth = 1) mustEqual false
}
}
@ -71,22 +99,37 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
surface.testInteraction(Vector3(1,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,8,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(8,8,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(8,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = -1) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,1,9), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(1,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,8,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,8,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(8,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,1,9)
surface.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
surface.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual false
surface.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,9, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,9, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,1, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(1,1,11)
surface.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(1,1,10)
surface.testInteraction(obj, varDepth = 0) mustEqual false
}
}
@ -100,22 +143,37 @@ class EnvironmentCollisionTest extends Specification {
}
"must have interaction that passes" in {
surface.testInteraction(Vector3(3,1,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(1,3,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(3,5,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(5,3,0), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = -1) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = 0) mustEqual true
surface.testInteraction(Vector3(2,2,9), varDepth = 1) mustEqual true
val obj = new PSGOTest()
obj.Position = Vector3(3,1,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(1,3,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(3,5,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(5,3,0)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = -1) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = 0) mustEqual true
obj.Position = Vector3(2,2,9)
surface.testInteraction(obj, varDepth = 1) mustEqual true
}
"must have interaction that fails" in {
surface.testInteraction(Vector3(3,0, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(0,3, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(3,6, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(6,3, 0), varDepth = 0) mustEqual false
surface.testInteraction(Vector3(2,2,11), varDepth = -1) mustEqual false
surface.testInteraction(Vector3(2,2,10), varDepth = 0) mustEqual false
val obj = new PSGOTest()
obj.Position = Vector3(3,0, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(0,3, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(3,6, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(6,3, 0)
surface.testInteraction(obj, varDepth = 0) mustEqual false
obj.Position = Vector3(2,2,11)
surface.testInteraction(obj, varDepth = -1) mustEqual false
obj.Position = Vector3(2,2,10)
surface.testInteraction(obj, varDepth = 0) mustEqual false
}
}
}
@ -237,21 +295,23 @@ class SeaLevelTest extends Specification {
}
"must have interaction that passes (same as DeepPlane)" in {
plane.testInteraction(Vector3(0,0,10), varDepth = -1) mustEqual
level.testInteraction(Vector3(0,0,10), varDepth = -1)
plane.testInteraction(Vector3(0,0, 9), varDepth = 0) mustEqual
level.testInteraction(Vector3(0,0, 9), varDepth = 0)
plane.testInteraction(Vector3(0,0, 8), varDepth = 1) mustEqual
level.testInteraction(Vector3(0,0, 8), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = -1) mustEqual level.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(0,0, 9)
plane.testInteraction(obj, varDepth = 0) mustEqual level.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,0, 8)
plane.testInteraction(obj, varDepth = 1) mustEqual level.testInteraction(obj, varDepth = 1)
}
"must have interaction that fails (same as DeepPlane)" in {
plane.testInteraction(Vector3(0,0,11), varDepth = -1) mustEqual
level.testInteraction(Vector3(0,0,11), varDepth = -1)
plane.testInteraction(Vector3(0,0,10), varDepth = 0) mustEqual
level.testInteraction(Vector3(0,0,10), varDepth = 0)
plane.testInteraction(Vector3(0,0, 9), varDepth = 1) mustEqual
level.testInteraction(Vector3(0,0, 9), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(0,0,11)
plane.testInteraction(obj, varDepth = -1) mustEqual level.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(0,0,10)
plane.testInteraction(obj, varDepth = 0) mustEqual level.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,0, 9)
plane.testInteraction(obj, varDepth = 1) mustEqual level.testInteraction(obj, varDepth = 1)
}
}
}
@ -267,43 +327,45 @@ class PoolTest extends Specification {
}
"must have interaction that passes (same as DeepSquare)" in {
pool.testInteraction(Vector3(1,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,8, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,8, 0), varDepth = 0)
pool.testInteraction(Vector3(8,8, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(8,8, 0), varDepth = 0)
pool.testInteraction(Vector3(8,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(8,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,1,10), varDepth = -1) mustEqual
square.testInteraction(Vector3(1,1,10), varDepth = -1)
pool.testInteraction(Vector3(1,1, 9), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1, 9), varDepth = 0)
pool.testInteraction(Vector3(1,1, 8), varDepth = 1) mustEqual
square.testInteraction(Vector3(1,1, 8), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(1,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,8, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(8,8, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(8,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1,10)
pool.testInteraction(obj, varDepth = -1) mustEqual square.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(1,1, 9)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1, 8)
pool.testInteraction(obj, varDepth = 1) mustEqual square.testInteraction(obj, varDepth = 1)
}
"must have interaction that fails (same as DeepSquare)" in {
pool.testInteraction(Vector3(1,0, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,0, 0), varDepth = 0)
pool.testInteraction(Vector3(1,9, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,9, 0), varDepth = 0)
pool.testInteraction(Vector3(0,9, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(0,9, 0), varDepth = 0)
pool.testInteraction(Vector3(0,1, 0), varDepth = 0) mustEqual
square.testInteraction(Vector3(0,1, 0), varDepth = 0)
pool.testInteraction(Vector3(1,1,11), varDepth = -1) mustEqual
square.testInteraction(Vector3(1,1,11), varDepth = -1)
pool.testInteraction(Vector3(1,1,10), varDepth = 0) mustEqual
square.testInteraction(Vector3(1,1,10), varDepth = 0)
pool.testInteraction(Vector3(1,1, 9), varDepth = 1) mustEqual
square.testInteraction(Vector3(1,1, 9), varDepth = 1)
val obj = new PSGOTest()
obj.Position = Vector3(1,0, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,9, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,9, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(0,1, 0)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1,11)
pool.testInteraction(obj, varDepth = -1) mustEqual square.testInteraction(obj, varDepth = -1)
obj.Position = Vector3(1,1,10)
pool.testInteraction(obj, varDepth = 0) mustEqual square.testInteraction(obj, varDepth = 0)
obj.Position = Vector3(1,1, 9)
pool.testInteraction(obj, varDepth = 1) mustEqual square.testInteraction(obj, varDepth = 1)
}
}
}
class GantryDenialField extends Specification {
val square = DeepSquare(0, 1, 10, 10, 1)
val square: DeepSquare = DeepSquare(0, 1, 10, 10, 1)
"GantryDenialField" should {
"always has the environmental attribute of 'GantryDenialField'" in {
@ -319,19 +381,19 @@ class PieceOfEnvironmentTest extends Specification {
val level = SeaLevel(10f)
"detect entering a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,9), Vector3(0,0,11), varDepth = 0).contains(true) mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,9), Vector3(0,0,11), varDepth = 0).contains(true) mustEqual true
}
"detect leaving a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,11), Vector3(0,0,9), varDepth = 0).contains(false) mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,11), Vector3(0,0,9), varDepth = 0).contains(false) mustEqual true
}
"not detect moving outside of a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,12), Vector3(0,0,11), varDepth = 0).isEmpty mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,12), Vector3(0,0,11), varDepth = 0).isEmpty mustEqual true
}
"not detect moving within a critical region" in {
testStepIntoInteraction(level, Vector3(0,0,9), Vector3(0,0,8), varDepth = 0).isEmpty mustEqual true
testStepIntoInteraction(level, new PSGOTest(), Vector3(0,0,9), Vector3(0,0,8), varDepth = 0).isEmpty mustEqual true
}
}
}