Cargo Bail (#258)

* deconstructing a vehicle that has cargo will eject the cargo before despawning the vehicle

* cargo that dismounts without a driver is scheduled for deconstruction; any player that disconnects while in a vehicle that is flying has the vehicle scheduled for immediate deconstruction instead of the standard deconstruction time

* modified routine for disowning vehicles outside of the conventional 'driver is owner' system

* standardized player-driven ownership fields; adjusted how vehicles transfer and announce ownership on zone join events (SetCurrentAvatar)

* stop /zone and /warp if in a cargo vehicle (even if driver)

* fix for landed-dismount cargo logic not starting distance checking loop

* concatenated Owner and OwnerName assignment to a singular object method, and the vehicle use of assignment to a singular WSA function
This commit is contained in:
Fate-JH 2019-05-06 09:10:39 -04:00 committed by GitHub
parent fca14e6b1b
commit a467a79c71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 284 additions and 179 deletions

View file

@ -0,0 +1,65 @@
// Copyright (c) 2019 PSForever
package net.psforever.objects
import net.psforever.packet.game.PlanetSideGUID
trait OwnableByPlayer {
private var owner : Option[PlanetSideGUID] = None
private var ownerName : Option[String] = None
def Owner : Option[PlanetSideGUID] = owner
def Owner_=(owner : PlanetSideGUID) : Option[PlanetSideGUID] = Owner_=(Some(owner))
def Owner_=(owner : Player) : Option[PlanetSideGUID] = Owner_=(Some(owner.GUID))
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
owner match {
case Some(_) =>
this.owner = owner
case None =>
this.owner = None
}
Owner
}
def OwnerName : Option[String] = ownerName
def OwnerName_=(owner : String) : Option[String] = OwnerName_=(Some(owner))
def OwnerName_=(owner : Player) : Option[String] = OwnerName_=(Some(owner.Name))
def OwnerName_=(owner : Option[String]) : Option[String] = {
owner match {
case Some(_) =>
ownerName = owner
case None =>
ownerName = None
}
OwnerName
}
/**
* na
* @param player na
* @return na
*/
def AssignOwnership(player : Player) : OwnableByPlayer = AssignOwnership(Some(player))
/**
* na
* @param playerOpt na
* @return na
*/
def AssignOwnership(playerOpt : Option[Player]) : OwnableByPlayer = {
playerOpt match {
case Some(player) =>
Owner = player
OwnerName = player
case None =>
Owner = None
OwnerName = None
}
this
}
}

View file

@ -72,10 +72,10 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
with MountedWeapons
with Deployment
with Vitality
with OwnableByPlayer
with StandardResistanceProfile
with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
private var health : Int = 1
private var shields : Int = 0
private var decal : Int = 0
@ -139,26 +139,6 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
MountedIn
}
def Owner : Option[PlanetSideGUID] = {
this.owner
}
def Owner_=(owner : PlanetSideGUID) : Option[PlanetSideGUID] = Owner_=(Some(owner))
def Owner_=(owner : Player) : Option[PlanetSideGUID] = Owner_=(Some(owner.GUID))
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
owner match {
case Some(_) =>
if(Definition.CanBeOwned) {
this.owner = owner
}
case None =>
this.owner = None
}
Owner
}
def Health : Int = {
health
}
@ -499,7 +479,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
if(trunkAccess.isEmpty || trunkAccess.contains(player.GUID)) {
groupPermissions(3) match {
case VehicleLockState.Locked => //only the owner
owner.isEmpty || (owner.isDefined && player.GUID == owner.get)
Owner.isEmpty || (Owner.isDefined && player.GUID == Owner.get)
case VehicleLockState.Group => //anyone in the owner's squad or platoon
faction == player.Faction //TODO this is not correct
case VehicleLockState.Empire => //anyone of the owner's faction

View file

@ -9,12 +9,11 @@ import net.psforever.packet.game.{DeployableIcon, PlanetSideGUID}
import net.psforever.types.PlanetSideEmpire
trait Deployable extends FactionAffinity
with Vitality {
with Vitality
with OwnableByPlayer {
this : PlanetSideGameObject =>
private var health : Int = 1
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var owner : Option[PlanetSideGUID] = None
private var ownerName : Option[String] = None
def Health : Int = health
@ -32,38 +31,6 @@ trait Deployable extends FactionAffinity
Faction
}
def Owner : Option[PlanetSideGUID] = owner
def Owner_=(owner : PlanetSideGUID) : Option[PlanetSideGUID] = Owner_=(Some(owner))
def Owner_=(owner : Player) : Option[PlanetSideGUID] = Owner_=(Some(owner.GUID))
def Owner_=(owner : Option[PlanetSideGUID]) : Option[PlanetSideGUID] = {
owner match {
case Some(_) =>
this.owner = owner
case None =>
this.owner = None
}
Owner
}
def OwnerName : Option[String] = ownerName
def OwnerName_=(owner : String) : Option[String] = OwnerName_=(Some(owner))
def OwnerName_=(owner : Player) : Option[String] = OwnerName_=(Some(owner.Name))
def OwnerName_=(owner : Option[String]) : Option[String] = {
owner match {
case Some(_) =>
ownerName = owner
case None =>
ownerName = None
}
OwnerName
}
def DamageModel : DamageResistanceModel = Definition.asInstanceOf[DamageResistanceModel]
def Definition : ObjectDefinition with BaseDeployableDefinition

View file

@ -155,11 +155,11 @@ import scodec.codecs._
* `81 - ???`<br>
* `113 - Vehicle capacitor - e.g. Leviathan EMP charge`
*
* @param player_guid the player
* @param attribute_type na
* @param attribute_value na
* @param guid the object
* @param attribute_type the field
* @param attribute_value the value
*/
final case class PlanetsideAttributeMessage(player_guid : PlanetSideGUID,
final case class PlanetsideAttributeMessage(guid : PlanetSideGUID,
attribute_type : Int,
attribute_value : Long)
extends PlanetSideGamePacket {
@ -169,12 +169,16 @@ final case class PlanetsideAttributeMessage(player_guid : PlanetSideGUID,
}
object PlanetsideAttributeMessage extends Marshallable[PlanetsideAttributeMessage] {
def apply(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Int) : PlanetsideAttributeMessage = {
PlanetsideAttributeMessage(player_guid, attribute_type, attribute_value.toLong)
def apply(guid : PlanetSideGUID, attribute_type : Int, attribute_value : Int) : PlanetsideAttributeMessage = {
PlanetsideAttributeMessage(guid, attribute_type, attribute_value.toLong)
}
def apply(guid : PlanetSideGUID, attribute_type : Int, attribute_value : PlanetSideGUID) : PlanetsideAttributeMessage = {
PlanetsideAttributeMessage(guid, attribute_type, attribute_value.guid)
}
implicit val codec : Codec[PlanetsideAttributeMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
("guid" | PlanetSideGUID.codec) ::
("attribute_type" | uint8L) ::
("attribute_value" | uint32L)
).as[PlanetsideAttributeMessage]

View file

@ -36,5 +36,6 @@ object VehicleAction {
final case class TransferPassengerChannel(player_guid : PlanetSideGUID, temp_channel : String, new_channel : String, vehicle : Vehicle) extends Action
final case class TransferPassenger(player_guid : PlanetSideGUID, temp_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Action
final case class KickCargo(player_guid : PlanetSideGUID, cargo : Vehicle, speed : Int) extends Action
final case class ForceDismountVehicleCargo(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) extends Action
final case class KickCargo(player_guid : PlanetSideGUID, cargo : Vehicle, speed : Int, delay : Long) extends Action
}

View file

@ -40,5 +40,6 @@ object VehicleResponse {
final case class TransferPassengerChannel(old_channel : String, temp_channel : String, vehicle : Vehicle) extends Response
final case class TransferPassenger(temp_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Response
final case class KickCargo(cargo : Vehicle, speed : Int) extends Response
final case class ForceDismountVehicleCargo(vehicle_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) extends Response
final case class KickCargo(cargo : Vehicle, speed : Int, delay : Long) extends Response
}

View file

@ -140,9 +140,13 @@ class VehicleService extends Actor {
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete))
)
case VehicleAction.KickCargo(player_guid, cargo, speed) =>
case VehicleAction.ForceDismountVehicleCargo(player_guid, vehicle_guid, bailed, requestedByPassenger, kicked) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed))
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ForceDismountVehicleCargo(vehicle_guid, bailed, requestedByPassenger, kicked))
)
case VehicleAction.KickCargo(player_guid, cargo, speed, delay) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed, delay))
)
case _ => ;
}

View file

@ -4,6 +4,7 @@ package services.vehicle.support
import net.psforever.objects.Vehicle
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.DriveState
import services.{RemoverActor, Service}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -26,6 +27,21 @@ class VehicleRemover extends RemoverActor {
val vehicleGUID = vehicle.GUID
val zoneId = entry.zone.Id
vehicle.Actor ! Vehicle.PrepareForDeletion
//escape being someone else's cargo
(vehicle.MountedIn match {
case Some(carrierGUID) =>
entry.zone.Vehicles.find(v => v.GUID == carrierGUID)
case None =>
None
}) match {
case Some(carrier : Vehicle) =>
val driverName = carrier.Seats(0).Occupant match {
case Some(driver) => driver.Name
case _ => zoneId
}
context.parent ! VehicleServiceMessage(s"$driverName", VehicleAction.ForceDismountVehicleCargo(PlanetSideGUID(0), vehicleGUID, true, false, false))
case _ => ;
}
//kick out all passengers
vehicle.Seats.values.foreach(seat => {
seat.Occupant match {
@ -37,6 +53,12 @@ class VehicleRemover extends RemoverActor {
}
case None => ;
}
//abandon all cargo
vehicle.CargoHolds.values
.collect { case hold if hold.isOccupied =>
val cargo = hold.Occupant.get
context.parent ! VehicleServiceMessage(zoneId, VehicleAction.ForceDismountVehicleCargo(PlanetSideGUID(0), cargo.GUID, true, false, false))
}
})
}