From 84b3d2297adf9729486c0ae04dd0813df73dfc8b Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Sun, 17 Mar 2024 18:16:32 -0400 Subject: [PATCH] added sidedness to different entities, both in general and depending on the zone where it matters --- .../objects/serverobject/doors/Door.scala | 8 + .../objects/serverobject/locks/IFFLock.scala | 2 + .../serverobject/pad/VehicleSpawnPad.scala | 3 + .../VehicleSpawnControlLoadVehicle.scala | 7 +- .../resourcesilo/ResourceSilo.scala | 6 +- .../serverobject/structures/Amenity.scala | 6 +- .../terminals/ProximityTerminal.scala | 3 + .../serverobject/turret/FacilityTurret.scala | 3 +- .../vehicles/control/VehicleControl.scala | 1 - .../net/psforever/objects/zones/Zone.scala | 331 +++++++++++++----- 10 files changed, 279 insertions(+), 91 deletions(-) diff --git a/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala b/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala index cd978d0d9..eb90ed91d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala +++ b/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.doors import net.psforever.objects.Player import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.UseItemMessage import net.psforever.types.Vector3 @@ -14,6 +15,13 @@ import net.psforever.types.Vector3 class Door(private val ddef: DoorDefinition) extends Amenity { private var openState: Option[Player] = None private var outwards: Option[Vector3] = None + /* + * Door sidedness does not reflect on the actual sidedness of anything that is in the door. + * While sidedness will be correct for internal doors - + * they are always passages between different between interior rooms in a facility - + * external doors cross between the outside world and a interior room of a facility. + */ + WhichSide = Sidedness.InsideOf def isOpen: Boolean = openState.isDefined diff --git a/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala b/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala index 12953d835..ca32e19ab 100644 --- a/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala +++ b/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala @@ -5,6 +5,7 @@ import akka.actor.ActorRef import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.TriggeredSound import net.psforever.types.Vector3 @@ -23,6 +24,7 @@ class IFFLock(private val idef: IFFLockDefinition) extends Amenity with Hackable HackSound = TriggeredSound.HackDoor HackEffectDuration = Array(60, 180, 300, 360) HackDuration = Array(5, 3, 1, 1) + WhichSide = Sidedness.InsideOf /** a vector in the direction of the "outside" of a room; * typically, any locking utility is on that same "outside" diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala index c125da0c1..4b9bb47c4 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.pad +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.{Player, Vehicle} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.terminals.Terminal @@ -18,6 +19,8 @@ import net.psforever.types.PlanetSideGUID * @param spDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ class VehicleSpawnPad(spDef: VehicleSpawnPadDefinition) extends Amenity { + WhichSide = Sidedness.OutsideOf + def Definition: VehicleSpawnPadDefinition = spDef } diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala index d9bdd3d94..8e4d07bc1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.pad.process -import akka.actor.Props +import akka.actor.{ActorRef, Props} import akka.pattern.ask import akka.util.Timeout import net.psforever.objects.GlobalDefinitions @@ -29,11 +29,11 @@ import scala.util.Success class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnControlBase(pad) { def LogId = "-loader" - val railJack = context.actorOf(Props(classOf[VehicleSpawnControlRailJack], pad), s"${context.parent.path.name}-rails") + val railJack: ActorRef = context.actorOf(Props(classOf[VehicleSpawnControlRailJack], pad), s"${context.parent.path.name}-rails") var temp: Option[VehicleSpawnControl.Order] = None - implicit val timeout = Timeout(3.seconds) + implicit val timeout: Timeout = Timeout(3.seconds) def receive: Receive = { case order @ VehicleSpawnControl.Order(driver, vehicle) => @@ -42,6 +42,7 @@ class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnC vehicle.Position = vehicle.Position - Vector3.z( if (GlobalDefinitions.isFlightVehicle(vehicle.Definition)) 9 else 5 ) //appear below the trench and doors + vehicle.WhichSide = pad.WhichSide vehicle.Cloaked = vehicle.Definition.CanCloak && driver.Cloaked temp = Some(order) diff --git a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala index 9f993229c..0a249676d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala +++ b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSilo.scala @@ -2,11 +2,14 @@ package net.psforever.objects.serverobject.resourcesilo import akka.actor.{ActorContext, Props} +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.{CommonNtuContainer, GlobalDefinitions, Player} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.packet.game.UseItemMessage import net.psforever.types.Vector3 +import scala.annotation.unused + class ResourceSilo extends Amenity with CommonNtuContainer { // For the flashing red light on top of the NTU silo on. @@ -16,6 +19,7 @@ class ResourceSilo extends Amenity with CommonNtuContainer { // For the NTU display bar private var capacitorDisplay: Long = 0 NtuCapacitor = Definition.MaxNtuCapacitor + WhichSide = Sidedness.OutsideOf def MaxNtuCapacitor : Float = Definition.MaxNtuCapacitor @@ -37,7 +41,7 @@ class ResourceSilo extends Amenity with CommonNtuContainer { def Definition: ResourceSiloDefinition = GlobalDefinitions.resource_silo - def Use(player: Player, msg: UseItemMessage): ResourceSilo.Exchange = { + def Use(@unused player: Player, @unused msg: UseItemMessage): ResourceSilo.Exchange = { ResourceSilo.ChargeEvent() } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala index 29dc28fab..d1de62824 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.structures import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.interior.{Sidedness, TraditionalInteriorAware} import net.psforever.objects.vital.resistance.StandardResistanceProfile import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.resolution.DamageAndResistance @@ -25,7 +26,8 @@ abstract class Amenity extends PlanetSideServerObject with Vitality with StandardResistanceProfile - with BlockMapEntity { + with BlockMapEntity + with TraditionalInteriorAware { private[this] val log = org.log4s.getLogger("Amenity") /** what other entity has authority over this amenity; usually either a building or a vehicle */ @@ -34,6 +36,8 @@ abstract class Amenity /** if the entity exists at a specific position relative to the owner's position */ private var offset: Option[Vector3] = None + WhichSide = Sidedness.InsideOf + def Faction: PlanetSideEmpire.Value = Owner.Faction /** diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala index ad8acfb88..ad700fbbe 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.terminals +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.{Default, Player} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.structures.Amenity @@ -17,6 +18,8 @@ import net.psforever.services.Service * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ class ProximityTerminal(tdef: ProximityTerminalDefinition) extends Terminal(tdef) with ProximityUnit { + WhichSide = Sidedness.StrictlyBetweenSides + override def Request(player: Player, msg: Any): Terminal.Exchange = { msg match { case message: CommonMessages.Use => diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala index 5ff5bd246..b3e81d0a2 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurret.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.turret import net.psforever.objects.equipment.JammableUnit +import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware import net.psforever.objects.serverobject.turret.auto.AutomatedTurret @@ -14,6 +15,7 @@ class FacilityTurret(tDef: FacilityTurretDefinition) with JammableUnit with CaptureTerminalAware { WeaponTurret.LoadDefinition(turret = this) + WhichSide = Sidedness.OutsideOf def TurretOwner: SourceEntry = { Seats @@ -36,7 +38,6 @@ class FacilityTurret(tDef: FacilityTurretDefinition) } object FacilityTurret { - /** * Overloaded constructor. * @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala index f4cec5a7d..3ede78821 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala @@ -319,7 +319,6 @@ class VehicleControl(vehicle: Vehicle) case Some(seatNumber) => val vsrc = VehicleSource(vehicle) user.LogActivity(VehicleMountActivity(vsrc, PlayerSource.inSeat(user, vsrc, seatNumber), vehicle.Zone.Number)) - obj.WhichSide = user.WhichSide //if the driver mount, change ownership if that is permissible for this vehicle if (seatNumber == 0 && !obj.OwnerName.contains(user.Name) && obj.Definition.CanBeOwned.nonEmpty) { //whatever vehicle was previously owned diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 38eba94ac..446465435 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -42,8 +42,10 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.interior.{InteriorAware, Sidedness} import net.psforever.objects.serverobject.locks.IFFLock +import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad +import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.sourcing.SourceEntry import net.psforever.objects.vehicles.{MountedWeapons, UtilityType} @@ -662,11 +664,11 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) { (buildings.get(building_id), guid(object_guid)) match { case (Some(building), Some(amenity: Amenity)) => building.Amenities = amenity - //amenity.History(EntitySpawn(SourceEntry(amenity), this)) - case (Some(_), _) | (None, _) | (_, None) => ; //let ZoneActor's sanity check catch this error + case (Some(_), _) | (None, _) | (_, None) => () //let ZoneActor's sanity check catch this error } }) - Zone.AssignDoors(zone = this) + Zone.AssignOutwardSideToDoors(zone = this) + Zone.AssignSidednessToAmenities(zone = this) //ntu management (eventually move to a generic building startup function) buildings.values .flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo)) @@ -880,11 +882,10 @@ object Zone { new Zone(id, map, number) } - private def AssignDoors(zone: Zone): Unit = { + private def AssignOutwardSideToDoors(zone: Zone): Unit = { //let ZoneActor's sanity check catch any missing entities - val map = zone.map - if (map.cavern) { - //cavern doors + //todo there are no doors in the training zones so we may skip that + if (zone.map.cavern) { //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 ( @@ -892,107 +893,269 @@ object Zone { .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) - } - } + AssignOutwardSideToSanctuaryDoors(zone) } 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 - } + AssignOutwardSidetoContinentDoors(zone) + } + } + + private def AssignOutwardSideToSanctuaryDoors(zone: Zone): Unit = { + val map = zone.map + val guid = zone.guid + AssignOutwardsToIFFLockedDoors(zone) + //doors with IFF locks belong to towers and are always between; the locks are always outside + map.doorToLock + .map { case (door, lock) => (guid(door), guid(lock)) } + .collect { case (Some(door: Door), Some(lock: IFFLock)) => + door.WhichSide = Sidedness.StrictlyBetweenSides + lock.WhichSide = Sidedness.OutsideOf + } + //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 isReallyADoor = door.asInstanceOf[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)) + isReallyADoor.WhichSide = Sidedness.StrictlyBetweenSides + isReallyADoor.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 isReallyADoor = door.asInstanceOf[Door] + val doorPosition = door.Position + val closestHartDoor = hartDoors.minBy(t => Vector3.DistanceSquared(doorPosition, t.Position)) + isReallyADoor.WhichSide = Sidedness.StrictlyBetweenSides + isReallyADoor.Outwards = Vector3.Unit(doorPosition.xy - closestHartDoor.Position.xy) } - var pairedDoors = Seq[(Door, Door)]() - unpairedDoors.foreach { buildingUnPairedDoors => - var volatileUnpairedDoors = buildingUnPairedDoors - while (volatileUnpairedDoors.size > 1) { - val sampleDoor = volatileUnpairedDoors.head + } + } + + private def AssignOutwardSidetoContinentDoors(zone: Zone): Unit = { + val map = zone.map + val guid = zone.guid + AssignOutwardsToIFFLockedDoors(zone) + val buildingsToDoors = zone.Buildings.values.map(b => (b, b.Amenities.collect { case d: Door => d })).toMap + //external doors with IFF locks are always between and outside, respectively + map.doorToLock + .map { case (door, lock) => (guid(door), guid(lock)) } + .collect { case (Some(door: Door), Some(lock: IFFLock)) + if door.Definition.geometryInteractionRadius.nonEmpty => + door.WhichSide = Sidedness.StrictlyBetweenSides + lock.WhichSide = Sidedness.OutsideOf + } + //for major facilities, external doors in the courtyard are paired, connected by a passage between ground and walls + //they are the only external doors that do not have iff locks + buildingsToDoors + .filter { case (b, _) => b.BuildingType == StructureType.Facility } + .foreach { case (_, doors) => + var unpairedDoors = doors.collect { + case d: Door + if d.Definition == GlobalDefinitions.gr_door_ext && !map.doorToLock.contains(d.GUID.guid) => + d + } + var pairedDoors = Seq[(Door, Door)]() + while (unpairedDoors.size > 1) { + val sampleDoor = unpairedDoors.head val sampleDoorPosition = sampleDoor.Position.xy - val distances = Float.MaxValue +: volatileUnpairedDoors + val distances = Float.MaxValue +: unpairedDoors .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) + val otherDoor = unpairedDoors(indexOfClosestDoor) + unpairedDoors = unpairedDoors.slice(1, indexOfClosestDoor) ++ unpairedDoors.drop(indexOfClosestDoor + 1) pairedDoors = pairedDoors :+ (sampleDoor, otherDoor) } + 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 + door1.WhichSide = Sidedness.StrictlyBetweenSides + door2.Outwards = Vector3.neg(outwards) + door2.WhichSide = Sidedness.StrictlyBetweenSides + } } - 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) + //bunkers do not define a formal interior, so their doors are solely exterior + buildingsToDoors + .filter { case (b, _) => b.BuildingType == StructureType.Bunker } + .foreach { case (_, doors) => + doors.foreach(_.WhichSide = Sidedness.OutsideOf) } - //bunker doors do not define an interior - } } - private def AssignIFFLockedDoors(zone: Zone): Unit = { - val map = zone.map + private def AssignOutwardsToIFFLockedDoors(zone: Zone): Unit = { 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 + zone.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 _ => () } } + private def AssignSidednessToAmenities(zone: Zone): Unit = { + //let ZoneActor's sanity check catch any missing entities + //todo training zones, where everything is outside + if (zone.map.cavern) { + //todo what do? + /* + quite a few amenities are disconnected from buildings + there are two orientations of terminal/spawn pad + as aforementioned, door outwards and sidedness is not assignable at the moment + */ + } else if ( + PlanetSideEmpire.values + .filterNot(_ == PlanetSideEmpire.NEUTRAL) + .exists(fac => Zones.sanctuaryZoneNumber(fac) == zone.Number) + ) { + AssignSidednessToSanctuaryAmenities(zone) + } else { + AssignSidednessToContinentAmenities(zone) + } + } + + private def AssignSidednessToSanctuaryAmenities(zone: Zone): Unit = { + val map = zone.map + val guid = zone.guid + //only tower doors possess locks and those are always external + map.doorToLock + .map { case (_, lock) => guid(lock) } + .collect { + case Some(lock: IFFLock) => + lock.WhichSide = Sidedness.OutsideOf + } + //medical terminals are always inside + zone.buildings + .values + .flatMap(_.Amenities) + .collect { + case pt: ProximityTerminal if pt.Definition == GlobalDefinitions.medical_terminal => + pt.WhichSide = Sidedness.InsideOf + } + //repair silos and landing pads have multiple components and all of these are outside + //we have to search all terminal entities because the repair silos are not installed anywhere + guid + .GetPool(name = "terminals") + .map(_.Numbers.flatMap(number => guid(number))) + .getOrElse(List()) + .collect { + case pt: ProximityTerminal + if pt.Definition == GlobalDefinitions.repair_silo => + val guid = pt.GUID.guid + Seq(guid, guid + 1, guid + 2, guid + 3) + case pt: ProximityTerminal + if pt.Definition.Name.startsWith("pad_landing_") => + val guid = pt.GUID.guid + Seq(guid, guid + 1, guid + 2) + } + .flatten[Int] + .map(guid(_)) + .collect { + case Some(pt: ProximityTerminal) => + pt.WhichSide = Sidedness.OutsideOf + } + //the following terminals are installed outside + map.terminalToSpawnPad + .keys + .flatMap(guid(_)) + .collect { + case terminal: Terminal => + terminal.WhichSide = Sidedness.OutsideOf + } + } + + private def AssignSidednessToContinentAmenities(zone: Zone): Unit = { + val map = zone.map + val guid = zone.guid + val buildingsMap = zone.buildings.values + //door locks on external doors are also external while the door is merely "between"; all other locks are internal + map.doorToLock + .map { case (door, lock) => (guid(door), guid(lock))} + .collect { + case (Some(door: Door), Some(lock: IFFLock)) + if door.Definition.geometryInteractionRadius.nonEmpty => + lock.WhichSide = Sidedness.OutsideOf + } + //medical terminals are always inside + buildingsMap + .flatMap(_.Amenities) + .collect { + case pt: ProximityTerminal + if pt.Definition == GlobalDefinitions.medical_terminal || pt.Definition == GlobalDefinitions.adv_med_terminal => + pt.WhichSide = Sidedness.InsideOf + } + //repair silos and landing pads have multiple components and all of these are outside + buildingsMap + .flatMap(_.Amenities) + .collect { + case pt: ProximityTerminal + if pt.Definition == GlobalDefinitions.repair_silo => + val guid = pt.GUID.guid + Seq(guid, guid + 1, guid + 2, guid + 3) + case pt: ProximityTerminal + if pt.Definition.Name.startsWith("pad_landing_") => + val guid = pt.GUID.guid + Seq(guid, guid + 1, guid + 2) + } + .toSeq + .flatten[Int] + .map(guid(_)) + .collect { + case Some(pt: ProximityTerminal) => + pt.WhichSide = Sidedness.OutsideOf + } + //all vehicle spawn pads are outside, save for the ground vehicle pad in the tech plants + buildingsMap.collect { + case b + if b.Definition == GlobalDefinitions.tech_plant => + b.Amenities + .collect { case pad: VehicleSpawnPad => pad } + .minBy(_.Position.z) + .WhichSide = Sidedness.InsideOf + } + //all vehicle terminals are outside of their owning facilities in the courtyard + //the only exceptions are vehicle terminals in tech plants and the dropship center air terminal + map.terminalToSpawnPad + .keys + .flatMap(guid(_)) + .collect { + case terminal: Terminal + if terminal.Definition != GlobalDefinitions.dropship_vehicle_terminal && + terminal.Owner.asInstanceOf[Building].Definition != GlobalDefinitions.tech_plant => + terminal.WhichSide = Sidedness.OutsideOf + } + } + object Population { /**