mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-02-22 08:03:39 +00:00
integrating inside/outside considerations into server-calculated damage; clarifying the sidedness comparison rules; extending inside outside considerations for deployables and vehicles
This commit is contained in:
parent
b0d3c63b83
commit
b5d60a7f9e
18 changed files with 310 additions and 100 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,"
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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] {
|
||||
|
|
|
|||
|
|
@ -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: "<Base Type> <Base Name> has spawned a LLU"
|
||||
// "It must be taken to <Base Type> <Base Name>'s Control Console within 15 minutes or the hack will fail!"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue