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 MountedWeapons
with Deployment with Deployment
with Vitality with Vitality
with OwnableByPlayer
with StandardResistanceProfile with StandardResistanceProfile
with Container { with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
private var health : Int = 1 private var health : Int = 1
private var shields : Int = 0 private var shields : Int = 0
private var decal : Int = 0 private var decal : Int = 0
@ -139,26 +139,6 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
MountedIn 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 = { def Health : Int = {
health health
} }
@ -499,7 +479,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
if(trunkAccess.isEmpty || trunkAccess.contains(player.GUID)) { if(trunkAccess.isEmpty || trunkAccess.contains(player.GUID)) {
groupPermissions(3) match { groupPermissions(3) match {
case VehicleLockState.Locked => //only the owner 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 case VehicleLockState.Group => //anyone in the owner's squad or platoon
faction == player.Faction //TODO this is not correct faction == player.Faction //TODO this is not correct
case VehicleLockState.Empire => //anyone of the owner's faction 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 import net.psforever.types.PlanetSideEmpire
trait Deployable extends FactionAffinity trait Deployable extends FactionAffinity
with Vitality { with Vitality
with OwnableByPlayer {
this : PlanetSideGameObject => this : PlanetSideGameObject =>
private var health : Int = 1 private var health : Int = 1
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var owner : Option[PlanetSideGUID] = None
private var ownerName : Option[String] = None
def Health : Int = health def Health : Int = health
@ -32,38 +31,6 @@ trait Deployable extends FactionAffinity
Faction 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 DamageModel : DamageResistanceModel = Definition.asInstanceOf[DamageResistanceModel]
def Definition : ObjectDefinition with BaseDeployableDefinition def Definition : ObjectDefinition with BaseDeployableDefinition

View file

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

View file

@ -4,6 +4,7 @@ package services.vehicle.support
import net.psforever.objects.Vehicle import net.psforever.objects.Vehicle
import net.psforever.objects.guid.{GUIDTask, TaskResolver} import net.psforever.objects.guid.{GUIDTask, TaskResolver}
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.types.DriveState import net.psforever.types.DriveState
import services.{RemoverActor, Service} import services.{RemoverActor, Service}
import services.vehicle.{VehicleAction, VehicleServiceMessage} import services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -26,6 +27,21 @@ class VehicleRemover extends RemoverActor {
val vehicleGUID = vehicle.GUID val vehicleGUID = vehicle.GUID
val zoneId = entry.zone.Id val zoneId = entry.zone.Id
vehicle.Actor ! Vehicle.PrepareForDeletion 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 //kick out all passengers
vehicle.Seats.values.foreach(seat => { vehicle.Seats.values.foreach(seat => {
seat.Occupant match { seat.Occupant match {
@ -37,6 +53,12 @@ class VehicleRemover extends RemoverActor {
} }
case None => ; 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))
}
}) })
} }

View file

@ -64,7 +64,6 @@ import services.local.support.{HackCaptureActor, RouterTelepadActivation}
import services.support.SupportActor import services.support.SupportActor
class WorldSessionActor extends Actor with MDCContextAware { class WorldSessionActor extends Actor with MDCContextAware {
import WorldSessionActor._ import WorldSessionActor._
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
@ -132,7 +131,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
*/ */
implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0 implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0
override def postStop() = { override def postStop() : Unit = {
//TODO normally, player avatar persists a minute or so after disconnect; we are subject to the SessionReaper //TODO normally, player avatar persists a minute or so after disconnect; we are subject to the SessionReaper
clientKeepAlive.cancel clientKeepAlive.cancel
reviveTimer.cancel reviveTimer.cancel
@ -160,21 +159,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(player.isAlive) { if(player.isAlive) {
//actually being alive or manually deconstructing //actually being alive or manually deconstructing
player.Position = Vector3.Zero player.Position = Vector3.Zero
if(player.VehicleSeated.nonEmpty) { //if seated, dismount
//quickly and briefly kill player to avoid disembark animation player.VehicleSeated match {
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0)) case Some(_) =>
DismountVehicleOnLogOut() //quickly and briefly kill player to avoid disembark animation?
DisownVehicle() avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
DismountVehicleOnLogOut()
case _ => ;
} }
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid))
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID) taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
//TODO normally, the actual player avatar persists a minute or so after the user disconnects //TODO normally, the actual player avatar persists a minute or so after the user disconnects
} }
else if(continent.LivePlayers.contains(player) && !continent.Corpses.contains(player)) { else if(continent.LivePlayers.contains(player) && !continent.Corpses.contains(player)) {
//player disconnected while waiting for a revive //player disconnected while waiting for a revive, maybe
//similar to handling ReleaseAvatarRequestMessage //similar to handling ReleaseAvatarRequestMessage
player.Release player.Release
DisownVehicle()
player.VehicleSeated match { player.VehicleSeated match {
case None => case None =>
FriskCorpse(player) //TODO eliminate dead letters FriskCorpse(player) //TODO eliminate dead letters
@ -191,7 +191,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID) taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
} }
case Some(vehicle_guid) => case Some(_) =>
val player_guid = player.GUID val player_guid = player.GUID
player.Position = Vector3.Zero //save character before doing this player.Position = Vector3.Zero //save character before doing this
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid))
@ -199,7 +199,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
DismountVehicleOnLogOut() DismountVehicleOnLogOut()
} }
} }
DisownVehicle() //disassociate and start the deconstruction timer for any currently owned vehicle
SpecialCaseDisownVehicle()
continent.Population ! Zone.Population.Leave(avatar) continent.Population ! Zone.Population.Leave(avatar)
} }
} }
@ -221,6 +222,29 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
/**
* If a vehicle is owned by a character, disassociate the vehicle, then schedule it for deconstruction.
* @see `DisownVehicle()`
* @return the vehicle previously owned, if any
*/
def SpecialCaseDisownVehicle() : Option[Vehicle] = {
DisownVehicle() match {
case out @ Some(vehicle) =>
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), continent))
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent,
if(vehicle.Flying) {
Some(0 seconds) //immediate deconstruction
}
else {
vehicle.Definition.DeconstructionTime //normal deconstruction
}
))
out
case None =>
None
}
}
def receive = Initializing def receive = Initializing
def Initializing : Receive = { def Initializing : Receive = {
@ -373,7 +397,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
} }
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //ownership sendResponse(PlanetsideAttributeMessage(player.GUID, 21, vehicle_guid)) //ownership
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) => case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, pad) =>
val vehicle_guid = vehicle.GUID val vehicle_guid = vehicle.GUID
@ -540,8 +564,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(obj : BoomerDeployable) => case Some(obj : BoomerDeployable) =>
val guid = obj.GUID val guid = obj.GUID
val factionOnContinentChannel = s"${continent.Id}/${player.Faction}" val factionOnContinentChannel = s"${continent.Id}/${player.Faction}"
obj.Owner = None obj.AssignOwnership(None)
obj.OwnerName = None
avatar.Deployables.Remove(obj) avatar.Deployables.Remove(obj)
UpdateDeployableUIElements(avatar.Deployables.UpdateUIElement(obj.Definition.Item)) UpdateDeployableUIElements(avatar.Deployables.UpdateUIElement(obj.Definition.Item))
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent)) localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(obj, continent))
@ -580,8 +603,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
val playerGUID = player.GUID val playerGUID = player.GUID
val faction = player.Faction val faction = player.Faction
val factionOnContinentChannel = s"${continent.Id}/${faction}" val factionOnContinentChannel = s"${continent.Id}/${faction}"
obj.Owner = playerGUID obj.AssignOwnership(player)
obj.OwnerName = player.Name
obj.Faction = faction obj.Faction = faction
avatar.Deployables.Add(obj) avatar.Deployables.Add(obj)
UpdateDeployableUIElements(avatar.Deployables.UpdateUIElement(obj.Definition.Item)) UpdateDeployableUIElements(avatar.Deployables.UpdateUIElement(obj.Definition.Item))
@ -635,8 +657,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
TryDropConstructionTool(tool, index, obj.Position) TryDropConstructionTool(tool, index, obj.Position)
sendResponse(ObjectDeployedMessage.Failure(obj.Definition.Name)) sendResponse(ObjectDeployedMessage.Failure(obj.Definition.Name))
obj.Position = Vector3.Zero obj.Position = Vector3.Zero
obj.Owner = None obj.AssignOwnership(None)
obj.OwnerName = None
continent.Deployables ! Zone.Deployable.Dismiss(obj) continent.Deployables ! Zone.Deployable.Dismiss(obj)
} }
@ -780,6 +801,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
localService ! Service.Leave(Some(continentId)) localService ! Service.Leave(Some(continentId))
localService ! Service.Leave(Some(factionOnContinentChannel)) localService ! Service.Leave(Some(factionOnContinentChannel))
vehicleService ! Service.Leave(Some(continentId)) vehicleService ! Service.Leave(Some(continentId))
vehicleService ! Service.Leave(Some(factionOnContinentChannel))
player.Continent = zoneId player.Continent = zoneId
continent = zone continent = zone
continent.Population ! Zone.Population.Join(avatar) continent.Population ! Zone.Population.Join(avatar)
@ -1348,8 +1370,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case None => ; case None => ;
} }
tplayer.VehicleOwned = Some(obj_guid) OwnVehicle(obj, tplayer)
obj.Owner = Some(tplayer.GUID)
} }
AccessContents(obj) AccessContents(obj)
UpdateWeaponAtSeatPosition(obj, seat_num) UpdateWeaponAtSeatPosition(obj, seat_num)
@ -1764,11 +1785,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications += cert avatar.Certifications += cert
StartBundlingPackets() StartBundlingPackets()
AddToDeployableQuantities(cert, player.Certifications) AddToDeployableQuantities(cert, player.Certifications)
sendResponse(PlanetsideAttributeMessage(guid, 24, cert.id.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 24, cert.id))
tplayer.Certifications.intersect(Certification.Dependencies.Like(cert)).foreach(entry => { tplayer.Certifications.intersect(Certification.Dependencies.Like(cert)).foreach(entry => {
log.info(s"$cert replaces the learned certification $entry that cost ${Certification.Cost.Of(entry)} points") log.info(s"$cert replaces the learned certification $entry that cost ${Certification.Cost.Of(entry)} points")
avatar.Certifications -= entry avatar.Certifications -= entry
sendResponse(PlanetsideAttributeMessage(guid, 25, entry.id.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 25, entry.id))
}) })
StopBundlingPackets() StopBundlingPackets()
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true)) sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, true))
@ -1787,12 +1808,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
avatar.Certifications -= cert avatar.Certifications -= cert
StartBundlingPackets() StartBundlingPackets()
RemoveFromDeployablesQuantities(cert, player.Certifications) RemoveFromDeployablesQuantities(cert, player.Certifications)
sendResponse(PlanetsideAttributeMessage(guid, 25, cert.id.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 25, cert.id))
tplayer.Certifications.intersect(Certification.Dependencies.FromAll(cert)).foreach(entry => { tplayer.Certifications.intersect(Certification.Dependencies.FromAll(cert)).foreach(entry => {
log.info(s"$name is also forgetting the ${Certification.Cost.Of(entry)}-point $entry certification which depends on $cert") log.info(s"$name is also forgetting the ${Certification.Cost.Of(entry)}-point $entry certification which depends on $cert")
avatar.Certifications -= entry avatar.Certifications -= entry
RemoveFromDeployablesQuantities(entry, player.Certifications) RemoveFromDeployablesQuantities(entry, player.Certifications)
sendResponse(PlanetsideAttributeMessage(guid, 25, entry.id.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 25, entry.id))
}) })
StopBundlingPackets() StopBundlingPackets()
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Sell, true)) sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Sell, true))
@ -2023,7 +2044,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case VehicleResponse.Ownership(vehicle_guid) => case VehicleResponse.Ownership(vehicle_guid) =>
sendResponse(PlanetsideAttributeMessage(guid, 21, vehicle_guid.guid)) sendResponse(PlanetsideAttributeMessage(tplayer_guid, 21, vehicle_guid))
case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) => case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
@ -2098,7 +2119,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
case VehicleResponse.KickCargo(vehicle, speed) => case VehicleResponse.ForceDismountVehicleCargo(vehicle_guid, bailed, requestedByPassenger, kicked) =>
DismountVehicleCargo(tplayer_guid, vehicle_guid, bailed, requestedByPassenger, kicked)
case VehicleResponse.KickCargo(vehicle, speed, delay) =>
if(player.VehicleSeated.nonEmpty && deadState == DeadState.Alive) { if(player.VehicleSeated.nonEmpty && deadState == DeadState.Alive) {
if(speed > 0) { if(speed > 0) {
val strafe = if(CargoOrientation(vehicle) == 1) 2 else 1 val strafe = if(CargoOrientation(vehicle) == 1) 2 else 1
@ -2107,7 +2130,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
controlled = Some(reverseSpeed) controlled = Some(reverseSpeed)
sendResponse(ServerVehicleOverrideMsg(true, true, true, false, 0, strafe, reverseSpeed, Some(0))) sendResponse(ServerVehicleOverrideMsg(true, true, true, false, 0, strafe, reverseSpeed, Some(0)))
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(4500 milliseconds, self, VehicleServiceResponse(toChannel, tplayer_guid, VehicleResponse.KickCargo(vehicle, 0))) context.system.scheduler.scheduleOnce(delay milliseconds, self, VehicleServiceResponse(toChannel, tplayer_guid, VehicleResponse.KickCargo(vehicle, 0, delay)))
} }
else { else {
controlled = None controlled = None
@ -2678,12 +2701,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(SetCurrentAvatarMessage(guid, 0, 0)) sendResponse(SetCurrentAvatarMessage(guid, 0, 0))
sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on //TODO once per respawn? sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, true, "", "1 on", None)) //CC on //TODO once per respawn?
sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z))) sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z)))
//transfer vehicle ownership
player.VehicleOwned match {
case Some(vehicle_guid) if player.VehicleSeated.contains(vehicle_guid) =>
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.Ownership(guid, vehicle_guid))
case _ => ;
}
if(spectator) { if(spectator) {
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None)) sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
} }
@ -2734,6 +2751,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
}) })
StopBundlingPackets() StopBundlingPackets()
drawDeloyableIcon = DontRedrawIcons drawDeloyableIcon = DontRedrawIcons
//assert or transfer vehicle ownership
continent.GUID(player.VehicleOwned) match {
case Some(vehicle : Vehicle) if vehicle.OwnerName.contains(tplayer.Name) =>
vehicle.Owner = guid
vehicleService ! VehicleServiceMessage(s"${continent.Id}/${tplayer.Faction}", VehicleAction.Ownership(guid, vehicle.GUID))
case _ =>
player.VehicleOwned = None
}
//if driver of a vehicle, summon any passengers and cargo vehicles left behind on previous continent //if driver of a vehicle, summon any passengers and cargo vehicles left behind on previous continent
GetVehicleAndSeat() match { GetVehicleAndSeat() match {
case (Some(vehicle), Some(0)) => case (Some(vehicle), Some(0)) =>
@ -2863,16 +2888,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ DismountVehicleCargoMsg(player_guid, vehicle_guid, bailed, requestedByPassenger, kicked) => case msg @ DismountVehicleCargoMsg(player_guid, vehicle_guid, bailed, requestedByPassenger, kicked) =>
log.info(msg.toString) log.info(msg.toString)
if(!requestedByPassenger) { if(!requestedByPassenger) {
continent.GUID(vehicle_guid) match { DismountVehicleCargo(player_guid, vehicle_guid, bailed, requestedByPassenger, kicked)
case Some(cargo : Vehicle) =>
continent.GUID(cargo.MountedIn) match {
case Some(ferry : Vehicle) =>
HandleDismountVehicleCargo(player_guid, vehicle_guid, cargo, ferry.GUID, ferry, bailed, requestedByPassenger, kicked)
case _ =>
log.warn(s"DismountVehicleCargoMsg: target ${cargo.Definition.Name} does not know what treats it as cargo")
}
case _ => ;
}
} }
case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) => case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) =>
@ -2908,6 +2924,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
localService ! Service.Join(continentId) localService ! Service.Join(continentId)
localService ! Service.Join(factionOnContinentChannel) localService ! Service.Join(factionOnContinentChannel)
vehicleService ! Service.Join(continentId) vehicleService ! Service.Join(continentId)
vehicleService ! Service.Join(factionOnContinentChannel)
galaxyService ! Service.Join("galaxy") galaxyService ! Service.Join("galaxy")
configZone(continent) configZone(continent)
sendResponse(TimeOfDayMessage(1191182336)) sendResponse(TimeOfDayMessage(1191182336))
@ -3342,7 +3359,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
deadState = DeadState.Release //cancel movement updates deadState = DeadState.Release //cancel movement updates
PlayerActionsToCancel() PlayerActionsToCancel()
continent.GUID(player.VehicleSeated) match { continent.GUID(player.VehicleSeated) match {
case Some(vehicle : Vehicle) => case Some(vehicle : Vehicle) if vehicle.MountedIn.isEmpty =>
vehicle.PassengerInSeat(player) match { vehicle.PassengerInSeat(player) match {
case Some(0) => case Some(0) =>
vehicle.Position = pos vehicle.Position = pos
@ -3354,7 +3371,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Position = pos player.Position = pos
//avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, player.GUID)) //avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, player.GUID))
LoadZonePhysicalSpawnPoint(zone, pos, Vector3.Zero, 0) LoadZonePhysicalSpawnPoint(zone, pos, Vector3.Zero, 0)
case _ => //seated in something that is not a vehicle, or we're dead, in which case we can't move case _ => //seated in something that is not a vehicle or the vehicle is cargo, in which case we can't move
deadState = DeadState.Alive deadState = DeadState.Alive
} }
@ -3366,7 +3383,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
deadState = DeadState.Release //cancel movement updates deadState = DeadState.Release //cancel movement updates
PlayerActionsToCancel() PlayerActionsToCancel()
continent.GUID(player.VehicleSeated) match { continent.GUID(player.VehicleSeated) match {
case Some(vehicle : Vehicle) if player.isAlive => case Some(vehicle : Vehicle) if vehicle.MountedIn.isEmpty =>
vehicle.PassengerInSeat(player) match { vehicle.PassengerInSeat(player) match {
case Some(0) => case Some(0) =>
vehicle.Position = pos vehicle.Position = pos
@ -3378,7 +3395,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Position = pos player.Position = pos
sendResponse(PlayerStateShiftMessage(ShiftState(0, pos, player.Orientation.z, None))) sendResponse(PlayerStateShiftMessage(ShiftState(0, pos, player.Orientation.z, None)))
deadState = DeadState.Alive //must be set here deadState = DeadState.Alive //must be set here
case _ => //seated in something that is not a vehicle, or we're dead, in which case we can't move case _ => //seated in something that is not a vehicle or the vehicle is cargo, in which case we can't move
deadState = DeadState.Alive deadState = DeadState.Alive
} }
@ -4276,8 +4293,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
dObj.Position = pos dObj.Position = pos
dObj.Orientation = orient dObj.Orientation = orient
dObj.Faction = player.Faction dObj.Faction = player.Faction
dObj.Owner = player.GUID dObj.AssignOwnership(player)
dObj.OwnerName = player.Name
val tasking : TaskResolver.GiveTask = dObj match { val tasking : TaskResolver.GiveTask = dObj match {
case turret : TurretDeployable => case turret : TurretDeployable =>
GUIDTask.RegisterDeployableTurret(turret)(continent.GUID) GUIDTask.RegisterDeployableTurret(turret)(continent.GUID)
@ -4617,7 +4633,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ DeployRequestMessage(player_guid, vehicle_guid, deploy_state, unk2, unk3, pos) => case msg @ DeployRequestMessage(player_guid, vehicle_guid, deploy_state, unk2, unk3, pos) =>
log.info(s"DeployRequest: $msg") log.info(s"DeployRequest: $msg")
if(player.VehicleOwned == Some(vehicle_guid) && player.VehicleOwned == player.VehicleSeated) { if(player.VehicleOwned.contains(vehicle_guid) && player.VehicleOwned == player.VehicleSeated) {
continent.GUID(vehicle_guid) match { continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) => case Some(obj : Vehicle) =>
obj.Actor ! Deployment.TryDeploymentChange(deploy_state) obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
@ -5038,7 +5054,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
def Execute(resolver : ActorRef) : Unit = { def Execute(resolver : ActorRef) : Unit = {
localDriver.VehicleSeated = localVehicle.GUID localDriver.VehicleSeated = localVehicle.GUID
localVehicle.Owner = localDriver.GUID OwnVehicle(localVehicle, localDriver)
localAnnounce ! NewPlayerLoaded(localDriver) //alerts WSA localAnnounce ! NewPlayerLoaded(localDriver) //alerts WSA
resolver ! scala.util.Success(this) resolver ! scala.util.Success(this)
} }
@ -5322,12 +5338,39 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param vehicle the `Vehicle` * @param vehicle the `Vehicle`
*/ */
def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = { def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = {
val vehicle_guid = vehicle.GUID if(vehicle.Faction == player.Faction) {
(0 to 3).foreach(group => { val vehicle_guid = vehicle.GUID
sendResponse( (0 to 3).foreach(group => {
PlanetsideAttributeMessage(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id) sendResponse(
) PlanetsideAttributeMessage(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id)
}) )
})
}
}
/**
* na
* @param vehicle na
* @param tplayer na
* @return na
*/
def OwnVehicle(vehicle : Vehicle, tplayer : Player) : Option[Vehicle] = OwnVehicle(vehicle, Some(tplayer))
/**
* na
* @param vehicle na
* @param playerOpt na
* @return na
*/
def OwnVehicle(vehicle : Vehicle, playerOpt : Option[Player]) : Option[Vehicle] = {
playerOpt match {
case Some(tplayer) =>
tplayer.VehicleOwned = vehicle.GUID
vehicle.AssignOwnership(playerOpt)
Some(vehicle)
case None =>
None
}
} }
/** /**
@ -5340,7 +5383,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
* The vehicle must exist in the game world on the current continent. * The vehicle must exist in the game world on the current continent.
* This is similar but unrelated to the natural exchange of ownership when someone else sits in the vehicle's driver seat. * This is similar but unrelated to the natural exchange of ownership when someone else sits in the vehicle's driver seat.
* This is the player side of vehicle ownership removal. * This is the player side of vehicle ownership removal.
* @see `SpecialCaseVehicleDespawn(Player, Vehicle)`
* @param tplayer the player * @param tplayer the player
*/ */
def DisownVehicle(tplayer : Player) : Option[Vehicle] = { def DisownVehicle(tplayer : Player) : Option[Vehicle] = {
@ -5349,7 +5391,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
tplayer.VehicleOwned = None tplayer.VehicleOwned = None
continent.GUID(vehicle_guid) match { continent.GUID(vehicle_guid) match {
case Some(vehicle : Vehicle) => case Some(vehicle : Vehicle) =>
SpecialCaseVehicleDespawn(tplayer, vehicle) DisownVehicle(tplayer, vehicle)
case _ => case _ =>
None None
} }
@ -5359,35 +5401,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
/** /**
* Disassociate a vehicle from the player that owns it, if that player really was the previous owner. * Disassociate a player from a vehicle that he owns without associating a different player as the owner.
* Set the vehicle's driver seat permissions and passenger and gunner seat permissions to "allow empire,"
* then reload them for all clients.
* This is the vehicle side of vehicle ownership removal. * This is the vehicle side of vehicle ownership removal.
* Additionally, start the vehicle deconstruction timer if conditions are valid.
* @see `DisownVehicle(Player)`
* @param tplayer the player * @param tplayer the player
* @param vehicle the discovered vehicle
*/ */
private def SpecialCaseVehicleDespawn(tplayer : Player, vehicle : Vehicle) : Option[Vehicle] = { private def DisownVehicle(tplayer : Player, vehicle : Vehicle) : Option[Vehicle] = {
if(vehicle.Owner.contains(tplayer.GUID)) { val pguid = tplayer.GUID
vehicle.Owner = None if(vehicle.Owner.contains(pguid)) {
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), continent)) vehicle.AssignOwnership(None)
vehicle.CargoHolds.values val factionOnContinent = s"${continent.Id}/${vehicle.Faction}"
.collect { case hold if hold.isOccupied => SpecialCaseVehicleDespawn(player, hold.Occupant.get) } vehicleService ! VehicleServiceMessage(factionOnContinent, VehicleAction.Ownership(pguid, PlanetSideGUID(0)))
continent.GUID(vehicle.MountedIn) match { val vguid = vehicle.GUID
case Some(ferry : Vehicle) => val empire = VehicleLockState.Empire.id
HandleDismountVehicleCargo( (0 to 2).foreach(group => {
PlanetSideGUID(0), vehicle.PermissionGroup(group, empire)
vehicle.GUID, vehicleService ! VehicleServiceMessage(factionOnContinent, VehicleAction.SeatPermissions(pguid, vguid, group, empire))
vehicle, })
ferry.GUID, ReloadVehicleAccessPermissions(vehicle)
ferry,
false,
false,
false
)
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, Some(0 seconds))) //instant vehicle decay
case _ =>
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, vehicle.Definition.DeconstructionTime)) //normal vehicle decay
}
Some(vehicle) Some(vehicle)
} }
else { else {
@ -6629,8 +6661,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.VehicleSeated = guid player.VehicleSeated = guid
if(seat == 0) { if(seat == 0) {
//if driver //if driver
player.VehicleOwned = guid OwnVehicle(vehicle, player)
vehicle.Owner = player.GUID
vehicle.CargoHolds.values vehicle.CargoHolds.values
.collect { case hold if hold.isOccupied => hold.Occupant.get } .collect { case hold if hold.isOccupied => hold.Occupant.get }
.foreach { _.MountedIn = guid } .foreach { _.MountedIn = guid }
@ -7521,13 +7552,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
removed match { removed match {
case Some(telepad : TelepadDeployable) => case Some(telepad : TelepadDeployable) =>
telepad.Owner = None telepad.AssignOwnership(None)
telepad.OwnerName = None
localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), continent)) localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(telepad), continent))
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, continent, Some(0 seconds))) //normal decay localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(telepad, continent, Some(0 seconds))) //normal decay
case Some(old) => case Some(old) =>
old.Owner = None old.AssignOwnership(None)
old.OwnerName = None
localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(old), continent)) localService ! LocalServiceMessage.Deployables(RemoverActor.ClearSpecific(List(old), continent))
localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(old, continent, Some(0 seconds))) localService ! LocalServiceMessage.Deployables(RemoverActor.AddTask(old, continent, Some(0 seconds)))
if(msg) { //max test if(msg) { //max test
@ -7959,7 +7988,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param tplayer the target player being moved around; * @param tplayer the target player being moved around;
* not necessarily the same player as the `WorldSessionActor`-global `player` * not necessarily the same player as the `WorldSessionActor`-global `player`
* @param zone_id the zone in which the player will be placed * @param zone_id the zone in which the player will be placed
* @return a tuple composed of an ActorRef` destination and a message to send to that destination * @return a tuple composed of an `ActorRef` destination and a message to send to that destination
*/ */
def LoadZoneAsPlayer(tplayer : Player, zone_id : String) : (ActorRef, Any) = { def LoadZoneAsPlayer(tplayer : Player, zone_id : String) : (ActorRef, Any) = {
if(zone_id == continent.Id) { if(zone_id == continent.Id) {
@ -8058,12 +8087,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
hold.Occupant.get hold.Occupant.get
} }
occupiedCargoHolds.foreach{ cargo => occupiedCargoHolds.foreach{ cargo =>
cargo.Seat(0) match { cargo.Seats(0).Occupant match {
case Some(seat) if seat.isOccupied => case Some(occupant) =>
vehicleService ! VehicleServiceMessage(s"${seat.Occupant.get.Name}", VehicleAction.TransferPassengerChannel(pguid, s"${cargo.Actor}", toChannel, cargo)) vehicleService ! VehicleServiceMessage(s"${occupant.Name}", VehicleAction.TransferPassengerChannel(pguid, s"${cargo.Actor}", toChannel, cargo))
case _ => case _ =>
log.error("LoadZoneInVehicleAsDriver: abort; vehicle in cargo hold missing driver") log.error("LoadZoneInVehicleAsDriver: abort; vehicle in cargo hold missing driver")
SpecialCaseVehicleDespawn(player, cargo) HandleDismountVehicleCargo(player.GUID, cargo.GUID, cargo, vehicle.GUID, vehicle, false, false, true)
} }
} }
// //
@ -8486,6 +8515,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some((mountPoint, hold)) => case Some((mountPoint, hold)) =>
cargo.MountedIn = None cargo.MountedIn = None
hold.Occupant = None hold.Occupant = None
val driverOpt = cargo.Seats(0).Occupant
val rotation : Vector3 = if(CargoOrientation(cargo) == 1) { //TODO: BFRs will likely also need this set val rotation : Vector3 = if(CargoOrientation(cargo) == 1) { //TODO: BFRs will likely also need this set
//dismount router "sideways" in a lodestar //dismount router "sideways" in a lodestar
carrier.Orientation.xy + Vector3.z((carrier.Orientation.z - 90) % 360) carrier.Orientation.xy + Vector3.z((carrier.Orientation.z - 90) % 360)
@ -8511,11 +8541,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.Empty, 0) val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.Empty, 0)
sendResponse(ejectCargoMsg) //dismount vehicle on UI and disable "shield" effect on lodestar sendResponse(ejectCargoMsg) //dismount vehicle on UI and disable "shield" effect on lodestar
sendResponse(detachCargoMsg) sendResponse(detachCargoMsg)
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, ejectCargoMsg)) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SendResponse(player_guid, ejectCargoMsg))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, detachCargoMsg)) vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SendResponse(player_guid, detachCargoMsg))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(PlanetSideGUID(0), resetCargoMsg)) //lazy vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SendResponse(PlanetSideGUID(0), resetCargoMsg)) //lazy
log.debug(ejectCargoMsg.toString) log.debug(ejectCargoMsg.toString)
log.debug(detachCargoMsg.toString) log.debug(detachCargoMsg.toString)
if(driverOpt.isEmpty) {
//TODO cargo should drop like a rock like normal; until then, deconstruct it
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(cargo), continent))
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(cargo, continent, Some(0 seconds)))
}
} }
else { else {
//the carrier vehicle is not flying; just open the door and let the cargo vehicle back out; force it out if necessary //the carrier vehicle is not flying; just open the door and let the cargo vehicle back out; force it out if necessary
@ -8525,19 +8560,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(cargoDetachMessage) sendResponse(cargoDetachMessage)
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, cargoStatusMessage)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, cargoStatusMessage))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, cargoDetachMessage)) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.SendResponse(player_guid, cargoDetachMessage))
if(kicked) { driverOpt match {
cargo.Seat(0).get.Occupant match { case Some(driver) =>
case Some(driver) => if(kicked) {
vehicleService ! VehicleServiceMessage(s"${driver.Name}", VehicleAction.KickCargo(player_guid, cargo, cargo.Definition.AutoPilotSpeed2)) vehicleService ! VehicleServiceMessage(s"${driver.Name}", VehicleAction.KickCargo(player_guid, cargo, cargo.Definition.AutoPilotSpeed2, 4500))
case None => }
//driverless vehicle will get cleaned up import scala.concurrent.duration._
} import scala.concurrent.ExecutionContext.Implicits.global
//check every quarter second if the vehicle has moved far enough away to be considered dismounted
cargoDismountTimer.cancel
cargoDismountTimer = context.system.scheduler.scheduleOnce(250 milliseconds, self, CheckCargoDismount(cargoGUID, carrierGUID, mountPoint, iteration = 0))
case None =>
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.Empty, 0)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SendResponse(PlanetSideGUID(0), resetCargoMsg)) //lazy
//TODO cargo should back out like normal; until then, deconstruct it
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(cargo), continent))
vehicleService ! VehicleServiceMessage.Decon(RemoverActor.AddTask(cargo, continent, Some(0 seconds)))
} }
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
//check every quarter second if the vehicle has moved far enough away to be considered dismounted
cargoDismountTimer.cancel
cargoDismountTimer = context.system.scheduler.scheduleOnce(250 milliseconds, self, CheckCargoDismount(cargoGUID, carrierGUID, mountPoint, iteration = 0))
} }
StopBundlingPackets() StopBundlingPackets()
@ -8546,6 +8585,28 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
} }
/**
* na
* @param player_guid na
* @param vehicle_guid na
* @param bailed na
* @param requestedByPassenger na
* @param kicked na
*/
def DismountVehicleCargo(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) : Unit = {
continent.GUID(vehicle_guid) match {
case Some(cargo : Vehicle) =>
continent.GUID(cargo.MountedIn) match {
case Some(ferry : Vehicle) =>
HandleDismountVehicleCargo(player_guid, vehicle_guid, cargo, ferry.GUID, ferry, bailed, requestedByPassenger, kicked)
case _ =>
log.warn(s"DismountVehicleCargo: target ${cargo.Definition.Name}@$vehicle_guid does not know what treats it as cargo")
}
case _ =>
log.warn(s"DismountVehicleCargo: target $vehicle_guid either is not a vehicle in ${continent.Id} or does not exist")
}
}
def GetPlayerHackSpeed(obj: PlanetSideServerObject with Hackable): Float = { def GetPlayerHackSpeed(obj: PlanetSideServerObject with Hackable): Float = {
val playerHackLevel = GetPlayerHackLevel() val playerHackLevel = GetPlayerHackLevel()
val timeToHack = obj.HackDuration(playerHackLevel) val timeToHack = obj.HackDuration(playerHackLevel)