diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala
index 9eba63b82..60eed356a 100644
--- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala
+++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala
@@ -641,6 +641,7 @@ class SessionData(
val dObj: Deployable = Deployables.Make(ammoType)()
dObj.Position = pos
dObj.Orientation = orient
+ dObj.WhichSide = player.WhichSide
dObj.Faction = player.Faction
dObj.AssignOwnership(player)
val tasking: TaskBundle = dObj match {
diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
index f61853fe6..54b00181b 100644
--- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
@@ -10,6 +10,7 @@ import net.psforever.login.WorldSession
import net.psforever.objects.avatar.BattleRank
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
import net.psforever.objects.inventory.InventoryItem
+import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.mount.Seat
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
@@ -2630,15 +2631,19 @@ class ZoningOperations(
} else {
Zones.zones.find { _.id.equals(zoneId) }.orElse(Some(Zone.Nowhere)).get.Number
}
+ val toSide = physSpawnPoint.map(_.Owner) match {
+ case Some(_: WarpGate) => Sidedness.OutsideOf
+ case Some(_: Building) => Sidedness.InsideOf
+ case Some(v: Vehicle) => v.WhichSide //though usually OutsideOf
+ case _ => Sidedness.StrictlyBetweenSides //todo needs better determination
+ }
val toSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity => SourceEntry(o) }
respawnTimer = context.system.scheduler.scheduleOnce(respawnTime) {
if (player.isBackpack) { // if the player is dead, he is handled as dead infantry, even if he died in a vehicle
// new player is spawning
val newPlayer = RespawnClone(player)
- newPlayer.Position = pos
- newPlayer.Orientation = ori
newPlayer.LogActivity(SpawningActivity(PlayerSource(newPlayer), toZoneNumber, toSpawnPoint))
- LoadZoneAsPlayer(newPlayer, zoneId)
+ LoadZoneAsPlayUsing(newPlayer, pos, ori, toSide, zoneId)
} else {
avatarActor ! AvatarActor.DeactivateActiveImplants()
val betterSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity with InGameHistory => o }
@@ -2646,6 +2651,7 @@ class ZoningOperations(
case Some(vehicle: Vehicle) => // driver or passenger in vehicle using a warp gate, or a droppod
InGameHistory.SpawnReconstructionActivity(vehicle, toZoneNumber, betterSpawnPoint)
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
+ vehicle.WhichSide = toSide
LoadZoneInVehicle(vehicle, pos, ori, zoneId)
case _ if player.HasGUID => // player is deconstructing self or instant action
@@ -2655,21 +2661,38 @@ class ZoningOperations(
continent.id,
AvatarAction.ObjectDelete(player_guid, player_guid, 4)
)
- player.Position = pos
- player.Orientation = ori
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
- LoadZoneAsPlayer(player, zoneId)
+ LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
case _ => //player is logging in
- player.Position = pos
- player.Orientation = ori
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
- LoadZoneAsPlayer(player, zoneId)
+ LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
}
}
}
}
+ /**
+ * na
+ * @param target player being spawned
+ * @param position where player is being placed in the game wqrld
+ * @param orientation in what direction the player is facing in the game world
+ * @param onThisSide description of the containing environment
+ * @param goingToZone common designation for the zone
+ */
+ private def LoadZoneAsPlayUsing(
+ target: Player,
+ position: Vector3,
+ orientation: Vector3,
+ onThisSide: Sidedness,
+ goingToZone: String
+ ): Unit = {
+ target.Position = position
+ target.Orientation = orientation
+ target.WhichSide = onThisSide
+ LoadZoneAsPlayer(target, goingToZone)
+ }
+
/**
* The user is either already in the current zone and merely transporting from one location to another,
* also called "dying", or occasionally "deconstructing,"
diff --git a/src/main/scala/net/psforever/objects/Player.scala b/src/main/scala/net/psforever/objects/Player.scala
index abe4d0d7c..94f5136a5 100644
--- a/src/main/scala/net/psforever/objects/Player.scala
+++ b/src/main/scala/net/psforever/objects/Player.scala
@@ -40,7 +40,7 @@ class Player(var avatar: Avatar)
with AuraContainer
with MountableEntity {
interaction(environment.interaction.InteractWithEnvironment(Seq(
- new WithEntrance(avatar.name),
+ new WithEntrance(),
new WithWater(avatar.name),
new WithLava(),
new WithDeath(),
diff --git a/src/main/scala/net/psforever/objects/Vehicle.scala b/src/main/scala/net/psforever/objects/Vehicle.scala
index 5bd310564..2c067a698 100644
--- a/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects
+import net.psforever.objects.avatar.interaction.WithEntrance
import net.psforever.objects.ce.{InteractWithMines, InteractWithTurrets}
import net.psforever.objects.definition.{ToolDefinition, VehicleDefinition}
import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit}
@@ -12,9 +13,10 @@ import net.psforever.objects.serverobject.aura.AuraContainer
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.environment.interaction.common.{WithDeath, WithMovementTrigger}
import net.psforever.objects.serverobject.hackable.Hackable
+import net.psforever.objects.serverobject.interior.{InteriorAwareFromInteraction, Sidedness}
import net.psforever.objects.serverobject.structures.AmenityOwner
import net.psforever.objects.vehicles._
-import net.psforever.objects.vehicles.interaction.{WithLava, WithWater}
+import net.psforever.objects.vehicles.interaction.{WithEntranceInVehicle, WithLava, WithWater}
import net.psforever.objects.vital.resistance.StandardResistanceProfile
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.DamageResistanceModel
@@ -90,8 +92,10 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
with CommonNtuContainer
with Container
with AuraContainer
- with MountableEntity {
+ with MountableEntity
+ with InteriorAwareFromInteraction {
interaction(environment.interaction.InteractWithEnvironment(Seq(
+ new WithEntrance(),
new WithWater(),
new WithLava(),
new WithDeath(),
@@ -513,6 +517,12 @@ class Vehicle(private val vehicleDef: VehicleDefinition)
!Definition.CanFly && super.BailProtection_=(protect)
}
+ override def WhichSide_=(thisSide: Sidedness): Sidedness = {
+ val toSide = super.WhichSide_=(thisSide)
+ (Seats.values ++ CargoHolds.values).flatMap(_.occupants).foreach(_.WhichSide = toSide)
+ toSide
+ }
+
/**
* This is the definition entry that is used to store and unload pertinent information about the `Vehicle`.
* @return the vehicle's definition entry
diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala
index 84daad7cc..6385d140c 100644
--- a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala
+++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala
@@ -4,18 +4,18 @@ 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.serverobject.interior.{Sidedness, TraditionalInteriorAware}
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.types.Vector3
import scala.concurrent.duration._
-class WithEntrance(val channel: String)
- extends InteractionWith {
+class WithEntrance()
+ extends InteractionWith
+ with TraditionalInteriorAware {
val attribute: EnvironmentTrait = EnvironmentAttribute.InteriorField
private var stopTest: Boolean = false
- private var sideAware: Sidedness = Sidedness.InBetweenSides
def doInteractingWith(
obj: InteractsWithZone,
@@ -26,12 +26,14 @@ class WithEntrance(val channel: String)
stopTest = false
} else {
val door = body.asInstanceOf[InteriorDoorPassage].door
- if (door.isOpen) {
- sideAware = Sidedness.InBetweenSides
+ val strictly = performInteriorCheck(obj, door)
+ val value = if (door.isOpen) {
+ Sidedness.InBetweenSides(door, strictly)
} else {
- performInteriorCheck(obj, door)
+ strictly
}
- obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 250 milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("bellybutton")))
+ WhichSide_=(value)
+ obj.Actor ! RespondsToZoneEnvironment.Timer(attribute, delay = 250.milliseconds, obj.Actor, interaction.InteractingWithEnvironment(body, Some("bellybutton")))
}
}
@@ -40,7 +42,7 @@ class WithEntrance(val channel: String)
body: PieceOfEnvironment,
data: Option[Any]
): Unit = {
- performInteriorCheck(obj, body.asInstanceOf[InteriorDoorPassage].door)
+ WhichSide_=(performInteriorCheck(obj, body.asInstanceOf[InteriorDoorPassage].door))
stopTest = true
}
@@ -48,32 +50,44 @@ class WithEntrance(val channel: String)
obj: InteractsWithZone,
door: Door
): Sidedness = {
+ debugInteriorCheck(obj, door)
+// if (Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f) {
+// Sidedness.OutsideOf
+// } else {
+// Sidedness.InsideOf
+// }
+ }
+
+ private def debugInteriorCheck(
+ obj: InteractsWithZone,
+ door: Door
+ ): Sidedness = {
+ import net.psforever.objects.{Player, Vehicle}
import net.psforever.packet.game.ChatMsg
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideGUID}
+ val channel = obj match {
+ case p: Player => p.Name
+ case v: Vehicle => v.Actor.toString()
+ case _ => ""
+ }
val result = Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f
- if (result && sideAware != Sidedness.OutsideOf) {
+ if (result && WhichSide != 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) {
+ Sidedness.OutsideOf
+ } else if (!result && WhichSide != Sidedness.InsideOf) {
//inside
- sideAware = Sidedness.InsideOf
obj.Zone.AvatarEvents ! AvatarServiceMessage(
channel,
AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now inside"))
)
+ Sidedness.InsideOf
+ } else {
+ WhichSide
}
- sideAware
- }
-
- def ThisSide: Sidedness = sideAware
-
- def ThisSide_=(thisSide: Sidedness): Unit = {
- sideAware = thisSide
- ThisSide
}
}
diff --git a/src/main/scala/net/psforever/objects/ce/Deployable.scala b/src/main/scala/net/psforever/objects/ce/Deployable.scala
index d9025f8c9..65387285b 100644
--- a/src/main/scala/net/psforever/objects/ce/Deployable.scala
+++ b/src/main/scala/net/psforever/objects/ce/Deployable.scala
@@ -5,6 +5,7 @@ import net.psforever.objects._
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.interior.TraditionalInteriorAware
import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.resolution.DamageResistanceModel
import net.psforever.objects.zones.ZoneAware
@@ -18,7 +19,8 @@ trait BaseDeployable
with BlockMapEntity
with Vitality
with OwnableByPlayer
- with ZoneAware {
+ with ZoneAware
+ with TraditionalInteriorAware {
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var shields: Int = 0
diff --git a/src/main/scala/net/psforever/objects/geometry/GeometryForm.scala b/src/main/scala/net/psforever/objects/geometry/GeometryForm.scala
index 83a6e1ace..40a9afa54 100644
--- a/src/main/scala/net/psforever/objects/geometry/GeometryForm.scala
+++ b/src/main/scala/net/psforever/objects/geometry/GeometryForm.scala
@@ -112,6 +112,22 @@ object GeometryForm {
}
}
+ /**
+ * The geometric representation is a cylinder
+ * offset with respects to the height of the entity's base.
+ * @param radius half the distance across
+ * @param height how tall the cylinder is (the distance of the top to the base)
+ * @param o the entity
+ * @return the representation
+ */
+ def representByRaisedCylinder(radius: Float, height: Float)(o: Any): VolumetricGeometry = {
+ o match {
+ case p: PlanetSideGameObject => Cylinder(p.Position + Vector3.z(height * 0.5f), Vector3.relativeUp(p.Orientation), radius, math.abs(height))
+ case s: SourceEntry => Cylinder(s.Position + Vector3.z(height * 0.5f), Vector3.relativeUp(s.Orientation), radius, math.abs(height))
+ case _ => invalidCylinder
+ }
+ }
+
/**
* The geometric representation is a cylinder around the entity's base
* if the target represents a player entity.
diff --git a/src/main/scala/net/psforever/objects/geometry/VolumetricEnvironmentCollision.scala b/src/main/scala/net/psforever/objects/geometry/VolumetricEnvironmentCollision.scala
index d09a5166d..e2ae08ec6 100644
--- a/src/main/scala/net/psforever/objects/geometry/VolumetricEnvironmentCollision.scala
+++ b/src/main/scala/net/psforever/objects/geometry/VolumetricEnvironmentCollision.scala
@@ -5,7 +5,7 @@ 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.serverobject.environment.EnvironmentCollision
import net.psforever.objects.zones.Zone
import net.psforever.types.Vector3
diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala
index c01293ba6..bdd4b888f 100644
--- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala
+++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsMiscellaneous.scala
@@ -648,25 +648,31 @@ object GlobalDefinitionsMiscellaneous {
gr_door_airlock.Name = "gr_door_airlock"
gr_door_ext.Name = "gr_door_ext"
- gr_door_ext.geometryInteractionRadius = Some(1)
+ gr_door_ext.geometryInteractionRadius = Some(1.9f)
gr_door_garage_ext.Name = "gr_door_garage_ext"
- gr_door_garage_ext.geometryInteractionRadius = Some(1)
+ gr_door_garage_ext.initialOpeningDistance = 8f
+ gr_door_garage_ext.continuousOpenDistance = 9f
+ gr_door_garage_ext.geometryInteractionRadius = Some(11)
+ gr_door_garage_ext.geometryInteractionHeight = Some(-11)
+ gr_door_garage_ext.geometryInteractionCenterOn = true
gr_door_garage_int.Name = "gr_door_garage_int"
+ gr_door_garage_int.initialOpeningDistance = 8f
+ gr_door_garage_int.continuousOpenDistance = 9f
gr_door_int.Name = "gr_door_int"
gr_door_main.Name = "gr_door_main"
- gr_door_main.geometryInteractionRadius = Some(1)
+ gr_door_main.geometryInteractionRadius = Some(2.75f)
gr_door_mb_ext.Name = "gr_door_mb_ext"
- gr_door_mb_ext.geometryInteractionRadius = Some(1)
+ gr_door_mb_ext.geometryInteractionRadius = Some(2)
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_lrg.geometryInteractionRadius = Some(2.5f)
gr_door_mb_obsd.Name = "gr_door_mb_obsd"
diff --git a/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
index ca03cbcdf..f75d409dc 100644
--- a/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
@@ -2,6 +2,7 @@
package net.psforever.objects.serverobject.doors
import net.psforever.objects.geometry.GeometryForm
+import net.psforever.objects.geometry.d3.VolumetricGeometry
import net.psforever.objects.serverobject.structures.AmenityDefinition
/**
@@ -18,12 +19,15 @@ class DoorDefinition(objectId: Int)
var geometryInteractionRadius: Option[Float] = None
var geometryInteractionHeight: Option[Float] = None
+ var geometryInteractionCenterOn: Boolean = false
- Geometry = {
- (geometryInteractionRadius, geometryInteractionHeight) match {
- case (Some(r), Some(h)) => GeometryForm.representByCylinder(r, h)
- case (Some(r), None) => GeometryForm.representBySphereOnBase(r)
- case _ => super.Geometry
+ override def Geometry: Any => VolumetricGeometry = {
+ (geometryInteractionRadius, geometryInteractionHeight, geometryInteractionCenterOn) match {
+ case (Some(r), Some(h), false) => GeometryForm.representByCylinder(r, h)
+ case (Some(r), Some(h), true) => GeometryForm.representByRaisedCylinder(r, h)
+ case (Some(r), None, false) => GeometryForm.representBySphereOnBase(r)
+ case (Some(r), None, true) => GeometryForm.representBySphere(r)
+ case _ => super.Geometry
}
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala b/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
index 9fb5c7e66..37641049e 100644
--- a/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
@@ -1,7 +1,6 @@
// Copyright (c) 2020-2024 PSForever
package net.psforever.objects.serverobject.environment
-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
@@ -18,32 +17,25 @@ object EnvironmentAttribute {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj.Definition.DrownAtMaxDepth || obj.Definition.DisableAtMaxDepth || (obj match {
- case p: Player => p.VehicleSeated.isEmpty
- case v: Vehicle => v.MountedIn.isEmpty
- case _ => true
- })
+ obj.Definition.DrownAtMaxDepth ||
+ obj.Definition.DisableAtMaxDepth ||
+ canInteractWithPlayersAndVehicles(obj) ||
+ (obj match {
+ case p: Player => p.VehicleSeated.isEmpty
+ case v: Vehicle => v.MountedIn.isEmpty
+ case _ => false
+ })
}
}
case object Lava extends EnvironmentTrait {
/** lava can only interact with anything capable of registering damage */
- def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj match {
- case o: Vitality => o.Definition.Damageable
- case _ => false
- }
- }
+ def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
}
case object Death extends EnvironmentTrait {
/** death can only interact with anything capable of registering damage */
- def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj match {
- case o: Vitality => o.Definition.Damageable
- case _ => false
- }
- }
+ def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithDamagingFields(obj)
}
case object GantryDenialField
@@ -60,22 +52,39 @@ object EnvironmentAttribute {
case object MovementFieldTrigger
extends EnvironmentTrait {
/** only interact with living player characters or vehicles */
- def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj match {
- case p: Player => p.isAlive && p.Position != Vector3.Zero
- case v: Vehicle => !v.Destroyed && v.Position != Vector3.Zero
- case _ => false
- }
- }
+ def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
}
case object InteriorField
extends EnvironmentTrait {
- def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj match {
- case _: InteriorAware => true
- case _ => false
- }
+ /** only interact with living player characters or vehicles */
+ def canInteractWith(obj: PlanetSideGameObject): Boolean = canInteractWithPlayersAndVehicles(obj)
+ }
+
+ /**
+ * This environment field only interacts with anything capable of registering damage.
+ * Also, exclude targets that are located at the game world origin.
+ * @param obj target entity
+ * @return whether or not this field affects the target entity
+ */
+ def canInteractWithPlayersAndVehicles(obj: PlanetSideGameObject): Boolean = {
+ (obj.Position != Vector3.Zero) ||
+ (obj match {
+ case p: Player => p.isAlive
+ case v: Vehicle => !v.Destroyed
+ case _ => false
+ })
+ }
+
+ /**
+ * This environment field only interacts with living player characters or not-destroyed vehicles.
+ * @param obj target entity
+ * @return whether or not this field affects the target entity
+ */
+ def canInteractWithDamagingFields(obj: PlanetSideGameObject): Boolean = {
+ obj match {
+ case o: Vitality => !o.Destroyed && o.Definition.Damageable
+ case _ => false
}
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala b/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala
index a687bd818..f29e85874 100644
--- a/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/interior/InteriorAware.scala
@@ -12,6 +12,18 @@ trait InteriorAware {
def WhichSide_=(@unused thisSide: Sidedness): Sidedness
}
+trait TraditionalInteriorAware
+ extends InteriorAware {
+ private var side: Sidedness = Sidedness.StrictlyBetweenSides
+
+ def WhichSide: Sidedness = side
+
+ def WhichSide_=(thisSide: Sidedness): Sidedness = {
+ side = thisSide
+ WhichSide
+ }
+}
+
trait InteriorAwareFromInteraction
extends InteriorAware {
awareness: InteractsWithZone =>
@@ -24,11 +36,11 @@ trait InteriorAwareFromInteraction
}
def WhichSide: Sidedness = {
- withEntrance.map(_.ThisSide).getOrElse(Sidedness.InBetweenSides)
+ withEntrance.map(_.WhichSide).getOrElse(Sidedness.StrictlyBetweenSides)
}
def WhichSide_=(thisSide: Sidedness): Sidedness = {
- withEntrance.foreach(_.ThisSide = thisSide)
+ withEntrance.foreach(_.WhichSide = thisSide)
WhichSide
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/interior/Sidedness.scala b/src/main/scala/net/psforever/objects/serverobject/interior/Sidedness.scala
index c7a36f2a6..bec498331 100644
--- a/src/main/scala/net/psforever/objects/serverobject/interior/Sidedness.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/interior/Sidedness.scala
@@ -1,22 +1,60 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.interior
-sealed trait Sidedness
+import net.psforever.objects.serverobject.doors.Door
-sealed trait Inside
+sealed trait SidenessComparison
-sealed trait Outside
-
-sealed trait InBetween extends Inside with Outside
+sealed trait Sidedness {
+ protected def value: SidenessComparison
+}
object Sidedness {
- case object InsideOf extends Inside with Sidedness
+ sealed trait Inside
- case object OutsideOf extends Outside with Sidedness
+ sealed trait Outside
- case object InBetweenSides extends InBetween with Sidedness
+ /* Comparison values */
+ private case object IsInside extends SidenessComparison
+
+ private case object IsOutside extends SidenessComparison
+
+ private case object IsBetween extends SidenessComparison
+
+ /* Immutable value containers */
+ case object InsideOf extends Inside with Sidedness {
+ protected def value: SidenessComparison = IsInside
+ }
+
+ case object OutsideOf extends Outside with Sidedness {
+ protected def value: SidenessComparison = IsOutside
+ }
+
+ case object StrictlyBetweenSides extends Inside with Outside with Sidedness {
+ protected def value: SidenessComparison = IsBetween
+ }
+
+ /* Mutable value container */
+ class InBetweenSides(
+ private val door: Door,
+ private val strictly: Sidedness
+ ) extends Inside with Outside with Sidedness {
+ protected def value: SidenessComparison = {
+ if (door.isOpen) {
+ IsBetween
+ } else {
+ strictly.value
+ }
+ }
+ }
+
+ object InBetweenSides {
+ def apply(door: Door, strictly: Sidedness): InBetweenSides = new InBetweenSides(door, strictly)
+ }
def equals(a: Sidedness, b: Sidedness): Boolean = {
- (a eq b) || a == Sidedness.InBetweenSides || b == Sidedness.InBetweenSides
+ val avalue = a.value
+ val bvalue = b.value
+ (avalue eq bvalue) || avalue == IsBetween || bvalue == IsBetween
}
}
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 8cc6389b0..f4cec5a7d 100644
--- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
@@ -319,11 +319,12 @@ 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
vehicle.Zone.GUID(user.avatar.vehicle) match {
- case Some(v : Vehicle) =>
+ case Some(v: Vehicle) =>
v.Actor ! Vehicle.Ownership(None)
case _ =>
user.avatar.vehicle = None
diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala
new file mode 100644
index 000000000..75bf9749e
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala
@@ -0,0 +1,50 @@
+// Copyright (c) 2024 PSForever
+package net.psforever.objects.vehicles.interaction
+
+import net.psforever.objects.Vehicle
+import net.psforever.objects.avatar.interaction.WithEntrance
+import net.psforever.objects.serverobject.doors.InteriorDoorPassage
+import net.psforever.objects.serverobject.environment.PieceOfEnvironment
+import net.psforever.objects.zones.InteractsWithZone
+
+class WithEntranceInVehicle
+ extends WithEntrance() {
+ private var warningLevel: Int = 0
+ private var lastWarning: Long = 0L
+
+ override def doInteractingWith(obj: InteractsWithZone, body: PieceOfEnvironment, data: Option[Any]): Unit = {
+ super.doInteractingWith(obj, body, data)
+ if (warningLevel == -1) {
+ warnAboutProximity(obj, msg = "@InvalidTerrain_VehicleNowSafe")
+ warningLevel = 0
+ } else if (!body.asInstanceOf[InteriorDoorPassage].door.Definition.Name.contains("garage")) {
+ val curr = System.currentTimeMillis()
+ if (curr - lastWarning >= 5000L) {
+ if (warningLevel > 3) {
+ import scala.concurrent.duration._
+ obj.Actor ! Vehicle.Deconstruct(Some(2.seconds))
+ } else if (warningLevel > 0) {
+ warnAboutProximity(obj, msg = "@InvalidTerrain_VehicleWillDeconstruct")
+ }
+ lastWarning = curr
+ warningLevel += 1
+ }
+ }
+ }
+
+ override def stopInteractingWith(obj: InteractsWithZone, body: PieceOfEnvironment, data: Option[Any]): Unit = {
+ super.stopInteractingWith(obj, body, data)
+ warningLevel = -1
+ }
+
+ private def warnAboutProximity(obj: InteractsWithZone, msg: String): Unit = {
+ import net.psforever.packet.game.ChatMsg
+ import net.psforever.services.Service
+ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
+ import net.psforever.types.ChatMessageType
+ obj.Zone.AvatarEvents ! AvatarServiceMessage(
+ obj.Actor.toString(),
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg))
+ )
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala
index 71f2cf285..fbf0289f9 100644
--- a/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -40,6 +40,7 @@ import net.psforever.objects.guid.pool.NumberPool
import net.psforever.objects.serverobject.PlanetSideServerObject
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.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
@@ -882,8 +883,6 @@ object Zone {
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?
@@ -966,9 +965,6 @@ object Zone {
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
@@ -1455,7 +1451,10 @@ object Zone {
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
- findAllTargets(zone, source.Position, damagePropertiesBySource).filter { target => target ne source }
+ allOnSameSide(
+ source,
+ findAllTargets(zone, source.Position, damagePropertiesBySource).filter { target => target ne source }
+ )
}
/**
@@ -1477,7 +1476,28 @@ object Zone {
source: PlanetSideGameObject with Vitality,
damagePropertiesBySource: DamageWithPosition
): List[PlanetSideServerObject with Vitality] = {
- findAllTargets(zone, sourcePosition, damagePropertiesBySource).filter { target => target ne source }
+ allOnSameSide(
+ source,
+ findAllTargets(zone, sourcePosition, damagePropertiesBySource).filter { target => target ne source }
+ )
+ }
+
+ private def allOnSameSide(
+ source: PlanetSideGameObject with Vitality,
+ targets: List[PlanetSideServerObject with Vitality]
+ ): List[PlanetSideServerObject with Vitality] = {
+ source match {
+ case awareSource: InteriorAware =>
+ val awareSide = awareSource.WhichSide
+ targets.flatMap {
+ case awareTarget: InteriorAware if !Sidedness.equals(awareSide, awareTarget.WhichSide) =>
+ None
+ case anyTarget =>
+ Some(anyTarget)
+ }
+ case _ =>
+ targets
+ }
}
/**
@@ -1524,7 +1544,9 @@ object Zone {
target: PlanetSideGameObject with FactionAffinity with Vitality
): DamageInteraction = {
explosionDamage(instigation, target.Position)(source, target)
- }/**
+ }
+
+ /**
* na
* @param instigation what previous event happened, if any, that caused this explosion
* @param explosionPosition the coordinates of the detected explosion
diff --git a/src/main/scala/net/psforever/packet/game/InvalidTerrainMessage.scala b/src/main/scala/net/psforever/packet/game/InvalidTerrainMessage.scala
index 383836426..d90101496 100644
--- a/src/main/scala/net/psforever/packet/game/InvalidTerrainMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/InvalidTerrainMessage.scala
@@ -1,10 +1,12 @@
// Copyright (c) 2021 PSForever
package net.psforever.packet.game
+import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scodec.Attempt.Successful
-import scodec.Codec
+import scodec.bits.BitVector
+import scodec.{Attempt, Codec}
import scodec.codecs._
import shapeless.{::, HNil}
@@ -15,7 +17,7 @@ object TerrainCondition extends Enumeration {
type Type = Value
val Safe, Unsafe = Value
- implicit val codec = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 1))
+ implicit val codec: Codec[TerrainCondition.Value] = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 1))
}
/**
@@ -34,8 +36,8 @@ final case class InvalidTerrainMessage(
pos: Vector3
) extends PlanetSideGamePacket {
type Packet = InvalidTerrainMessage
- def opcode = GamePacketOpcode.InvalidTerrainMessage
- def encode = InvalidTerrainMessage.encode(this)
+ def opcode: Type = GamePacketOpcode.InvalidTerrainMessage
+ def encode: Attempt[BitVector] = InvalidTerrainMessage.encode(this)
}
object InvalidTerrainMessage extends Marshallable[InvalidTerrainMessage] {
diff --git a/src/main/scala/net/psforever/types/ChatMessageType.scala b/src/main/scala/net/psforever/types/ChatMessageType.scala
index dd0d9654c..589062be6 100644
--- a/src/main/scala/net/psforever/types/ChatMessageType.scala
+++ b/src/main/scala/net/psforever/types/ChatMessageType.scala
@@ -247,7 +247,7 @@ object ChatMessageType extends Enum[ChatMessageType] {
case object CMT_ADD_VANUMODULE extends ChatMessageType // /moduleadd
case object CMT_REMOVE_VANUMODULE extends ChatMessageType // /moduleremove OR /modulerm
case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive
- case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ???
+ case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // The "Next" button on the /debugmassive UI produces this message.
case object UNK_222 extends ChatMessageType // "CTF Flag stolen"
// Plays a trumpet-like sound and displays the message: " has spawned a LLU"
// "It must be taken to 's Control Console within 15 minutes or the hack will fail!"