Implant Terminals:

Implant terminals (mech) are now properly mountable and implant terminals (interface) are also properly interactive.  Player can select to equip or to remove implants properly.

Mountable:

Vehicles and implant terminal mechs now use common Mountable logic.

home3 Hart C:

All doors, save for those to the shuttle, and all implant terminals in this building are now rigged to operate.
This commit is contained in:
FateJH 2017-11-29 22:30:25 -05:00
parent 47a0aa3e0c
commit f9beb47073
27 changed files with 1134 additions and 176 deletions

View file

@ -14,7 +14,7 @@ import com.typesafe.config.ConfigFactory
import net.psforever.crypto.CryptoInterface
import net.psforever.objects.zones._
import net.psforever.objects.guid.TaskResolver
import net.psforever.objects.serverobject.builders.{DoorObjectBuilder, IFFLockObjectBuilder, TerminalObjectBuilder, VehicleSpawnPadObjectBuilder}
import net.psforever.objects.serverobject.builders._
import net.psforever.types.Vector3
import org.slf4j
import org.fusesource.jansi.Ansi._
@ -233,10 +233,29 @@ object PsLogin {
LocalObject(DoorObjectBuilder(door, 330))
LocalObject(DoorObjectBuilder(door, 332))
LocalObject(DoorObjectBuilder(door, 362))
LocalObject(DoorObjectBuilder(door, 370))
LocalObject(DoorObjectBuilder(door, 371))
LocalObject(DoorObjectBuilder(door, 372))
LocalObject(DoorObjectBuilder(door, 373))
LocalObject(DoorObjectBuilder(door, 374))
LocalObject(DoorObjectBuilder(door, 375))
LocalObject(DoorObjectBuilder(door, 394))
LocalObject(DoorObjectBuilder(door, 395))
LocalObject(DoorObjectBuilder(door, 396))
LocalObject(DoorObjectBuilder(door, 397))
LocalObject(DoorObjectBuilder(door, 398))
LocalObject(DoorObjectBuilder(door, 462))
LocalObject(DoorObjectBuilder(door, 463))
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 520)) //Hart B
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 522)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 523)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 524)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 525)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 526)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 527)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 528)) //Hart C
LocalObject(ImplantTerminalMechObjectBuilder(implant_terminal_mech, 529)) //Hart C
LocalObject(IFFLockObjectBuilder(lock_external, 556))
LocalObject(IFFLockObjectBuilder(lock_external, 558))
LocalObject(TerminalObjectBuilder(cert_terminal, 186))
@ -245,6 +264,15 @@ object PsLogin {
LocalObject(TerminalObjectBuilder(order_terminal, 853))
LocalObject(TerminalObjectBuilder(order_terminal, 855))
LocalObject(TerminalObjectBuilder(order_terminal, 860))
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1081)) //tube 520
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1082)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1083)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1084)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1085)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1086)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1087)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1088)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(implant_terminal_interface, 1089)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(ground_vehicle_terminal, 1063))
LocalObject(VehicleSpawnPadObjectBuilder(spawn_pad, 500)) //TODO guid not correct
LocalObject(TerminalObjectBuilder(dropship_vehicle_terminal, 304))
@ -254,16 +282,36 @@ object PsLogin {
ObjectToBase(330, 29)
ObjectToBase(332, 29)
//ObjectToBase(520, 29)
ObjectToBase(522, 29)
ObjectToBase(523, 29)
ObjectToBase(524, 29)
ObjectToBase(525, 29)
ObjectToBase(526, 29)
ObjectToBase(527, 29)
ObjectToBase(528, 29)
ObjectToBase(529, 29)
ObjectToBase(556, 29)
ObjectToBase(558, 29)
ObjectToBase(1063, 29) //TODO unowned courtyard terminal?
ObjectToBase(500, 29) //TODO unowned courtyard spawnpad?
ObjectToBase(304, 29) //TODO unowned courtyard terminal?
ObjectToBase(501, 29) //TODO unowned courtyard spawnpad?
ObjectToBase(1081, 29)
ObjectToBase(1063, 2) //TODO unowned courtyard terminal?
ObjectToBase(500, 2) //TODO unowned courtyard spawnpad?
ObjectToBase(304, 2) //TODO unowned courtyard terminal?
ObjectToBase(501, 2) //TODO unowned courtyard spawnpad?
DoorToLock(330, 558)
DoorToLock(332, 556)
TerminalToSpawnPad(1063, 500)
TerminalToSpawnPad(304, 501)
TerminalToInterface(520, 1081)
TerminalToInterface(522, 1082)
TerminalToInterface(523, 1083)
TerminalToInterface(524, 1084)
TerminalToInterface(525, 1085)
TerminalToInterface(526, 1086)
TerminalToInterface(527, 1087)
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
}
val home3 = new Zone("home3", map13, 13) {
override def Init(implicit context : ActorContext) : Unit = {

View file

@ -14,14 +14,16 @@ import net.psforever.objects._
import net.psforever.objects.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.mount.Mountable
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.packet.game.objectcreate._
import net.psforever.packet.game.objectcreate.{DetailedCharacterData, _}
import net.psforever.types._
import services._
import services.avatar._
@ -342,6 +344,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Door.NoEvent() => ;
}
case Mountable.MountMessages(tplayer, reply) =>
reply match {
case Mountable.CanMount(obj : ImplantTerminalMech, seat_num) =>
val player_guid : PlanetSideGUID = tplayer.GUID
val obj_guid : PlanetSideGUID = obj.GUID
log.info(s"MountVehicleMsg: $player_guid mounts $obj @ $seat_num")
tplayer.VehicleSeated = Some(obj_guid)
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(obj_guid, 0, 1000L))) //health of mech
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(obj_guid, player_guid, seat_num)))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num))
case Mountable.CanMount(obj : Vehicle, seat_num) =>
val obj_guid : PlanetSideGUID = obj.GUID
val player_guid : PlanetSideGUID = tplayer.GUID
log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num")
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(obj_guid) //clear all deconstruction timers
tplayer.VehicleSeated = Some(obj_guid)
if(seat_num == 0) { //simplistic vehicle ownership management
obj.Owner match {
case Some(owner_guid) =>
continent.GUID(owner_guid) match {
case Some(previous_owner : Player) =>
if(previous_owner.VehicleOwned.contains(obj_guid)) {
previous_owner.VehicleOwned = None //simplistic ownership management, player loses vehicle ownership
}
case _ => ;
}
case None => ;
}
tplayer.VehicleOwned = Some(obj_guid)
obj.Owner = Some(player_guid)
}
obj.WeaponControlledFromSeat(seat_num) match {
case Some(weapon : Tool) =>
//update mounted weapon belonging to seat
val magazine = weapon.AmmoSlots(weapon.FireModeIndex).Box //update the magazine in the weapon, specifically
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, weapon.Magazine.toLong)))
//update all related ammunition objects in trunk
obj.Trunk.Items
.filter({ case ((_, item)) => item.obj.isInstanceOf[AmmoBox] && item.obj.asInstanceOf[AmmoBox].AmmoType == weapon.AmmoType })
.foreach({ case ((_, item)) =>
val box = item.obj.asInstanceOf[AmmoBox]
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, 0, obj_guid, box.Capacity.toLong)))
})
case _ => ; //no weapons to update
}
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(obj_guid, player_guid, seat_num)))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seat_num))
case Mountable.CanMount(obj : Mountable, seat_num) =>
log.warn(s"MountVehicleMsg: $obj is some generic mountable object and nothing will happen")
case Mountable.CanNotMount(obj, seat_num) =>
log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed")
}
case Terminal.TerminalMessage(tplayer, msg, order) =>
order match {
case Terminal.BuyExosuit(exosuit, subtype) =>
@ -545,6 +603,84 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Learn, false)))
}
case Terminal.LearnImplant(implant) =>
val terminal_guid = msg.terminal_guid
val implant_type = implant.Type
val message = s"Implants: $tplayer wants to learn $implant_type"
val (interface, slotNumber) = tplayer.VehicleSeated match {
case Some(mech_guid) =>
(
continent.Map.TerminalToInterface.get(mech_guid.guid),
if(!tplayer.Implants.exists({slot => slot.Implant == implant_type})) { //no duplicates
tplayer.InstallImplant(implant)
}
else {
None
}
)
case _ =>
(None, None)
}
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get
log.info(s"$message - put in slot $slot")
sendResponse(PacketCoding.CreateGamePacket(0, AvatarImplantMessage(tplayer.GUID, 0, slot, implant_type.id)))
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Learn, true)))
}
else {
if(interface.isEmpty) {
log.warn(s"$message - not interacting with a terminal")
}
else if(!interface.contains(terminal_guid.guid)) {
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
}
else if(slotNumber.isEmpty) {
log.warn(s"$message - already knows that implant")
}
else {
log.warn(s"$message - forgot to sit at a terminal")
}
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Learn, false)))
}
case Terminal.SellImplant(implant) =>
val terminal_guid = msg.terminal_guid
val implant_type = implant.Type
val (interface, slotNumber) = tplayer.VehicleSeated match {
case Some(mech_guid) =>
(
continent.Map.TerminalToInterface.get(mech_guid.guid),
tplayer.UninstallImplant(implant_type)
)
case None =>
(None, None)
}
if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) {
val slot = slotNumber.get
log.info(s"$tplayer is selling $implant_type - take from slot $slot")
sendResponse(PacketCoding.CreateGamePacket(0, AvatarImplantMessage(tplayer.GUID, 1, slot, 0)))
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, true)))
}
else {
val message = s"$tplayer can not sell $implant_type"
if(interface.isEmpty) {
log.warn(s"$message - not interacting with a terminal")
}
else if(!interface.contains(terminal_guid.guid)) {
log.warn(s"$message - interacting with the wrong terminal, ${interface.get}")
}
else if(slotNumber.isEmpty) {
log.warn(s"$message - does not know that implant")
}
else {
log.warn(s"$message - forgot to sit at a terminal")
}
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, false)))
}
case Terminal.BuyVehicle(vehicle, loadout) =>
continent.Map.TerminalToSpawnPad.get(msg.terminal_guid.guid) match {
case Some(pad_guid) =>
@ -563,52 +699,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false)))
}
case Vehicle.VehicleMessages(tplayer, reply) =>
reply match {
case Vehicle.CanSeatPlayer(vehicle, seat_num) =>
log.info(s"MountVehicleMsg: ${player.GUID} mounts ${vehicle.GUID} @ $seat_num")
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle.GUID) //clear all deconstruction timers
val vehicle_guid : PlanetSideGUID = vehicle.GUID
tplayer.VehicleSeated = Some(vehicle_guid)
if(seat_num == 0) { //simplistic vehicle ownership management
vehicle.Owner match {
case Some(owner_guid) =>
continent.GUID(owner_guid) match {
case Some(previous_owner : Player) =>
if(previous_owner.VehicleOwned.contains(vehicle_guid)) {
previous_owner.VehicleOwned = None //simplistic ownership management, player loses vehicle ownership
}
case _ => ;
}
case None => ;
}
player.VehicleOwned = Some(vehicle_guid)
vehicle.Owner = Some(player.GUID)
}
vehicle.WeaponControlledFromSeat(seat_num) match {
case Some(weapon : Tool) =>
//update mounted weapon belonging to seat
val magazine = weapon.AmmoSlots(weapon.FireModeIndex).Box //update the magazine in the weapon, specifically
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(magazine.GUID, 0, weapon.GUID, weapon.Magazine.toLong)))
//update all related ammunition objects in trunk
vehicle.Trunk.Items
.filter({ case ((_, item)) => item.obj.isInstanceOf[AmmoBox] && item.obj.asInstanceOf[AmmoBox].AmmoType == weapon.AmmoType })
.foreach({ case ((_, item)) =>
val box = item.obj.asInstanceOf[AmmoBox]
sendResponse(PacketCoding.CreateGamePacket(0, InventoryStateMessage(box.GUID, 0, vehicle_guid, box.Capacity.toLong)))
})
case _ => ; //no weapons to update
}
val player_guid : PlanetSideGUID = tplayer.GUID
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, player_guid, seat_num)))
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, vehicle_guid, seat_num))
case Vehicle.CannotSeatPlayer(vehicle, seat_num) =>
log.warn(s"MountVehicleMsg: player $tplayer attempted to board vehicle ${vehicle.GUID}'s seat $seat_num, but was not allowed")
case _ => ;
}
case VehicleSpawnPad.ConcealPlayer =>
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectActionMessage(player.GUID, 36)))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ConcealPlayer(player.GUID))
@ -627,7 +717,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle_guid, player_guid, 0)))
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //temporary drive away from pad delay
vehicle.Actor ! Vehicle.TrySeatPlayer(0, player)
vehicle.Actor ! Mountable.TryMount(player, 0)
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) =>
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //sitting in the vehicle clears the drive away delay
@ -906,7 +996,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Certifications += CertificationType.AirSupport
player.Certifications += CertificationType.GalaxyGunship
player.Certifications += CertificationType.Phantasm
//player.ExoSuit = ExoSuitType.Infiltrator
AwardBattleExperiencePoints(player, 1000000L)
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
player.Slot(0).Equipment = Tool(beamer)
player.Slot(2).Equipment = Tool(suppressor)
player.Slot(4).Equipment = Tool(forceblade)
@ -919,7 +1010,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Slot(5).Equipment.get.asInstanceOf[LockerContainer].Inventory += 0 -> SimpleItem(remote_electronics_kit)
//TODO end temp player character auto-loading
self ! ListAccountCharacters
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
clientKeepAlive.cancel
@ -982,25 +1072,58 @@ class WorldSessionActor extends Actor with MDCContextAware {
//load active vehicles in zone
continent.Vehicles.foreach(vehicle => {
val definition = vehicle.Definition
sendResponse(
PacketCoding.CreateGamePacket(0,
ObjectCreateMessage(
definition.ObjectId,
vehicle.GUID,
definition.Packet.ConstructorData(vehicle).get
)
sendResponse(PacketCoding.CreateGamePacket(0,
ObjectCreateMessage(
definition.ObjectId,
vehicle.GUID,
definition.Packet.ConstructorData(vehicle).get
)
)
))
//seat vehicle occupants
vehicle.Definition.MountPoints.values.foreach(seat_num => {
vehicle.Seat(seat_num).get.Occupant match {
case Some(tplayer) =>
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle.GUID, tplayer.GUID, seat_num)))
if(tplayer.HasGUID) {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(vehicle.GUID, tplayer.GUID, seat_num)))
}
case None => ;
}
})
ReloadVehicleAccessPermissions(vehicle)
})
//implant terminals
continent.Map.TerminalToInterface.foreach({ case((terminal_guid, interface_guid)) =>
val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match {
case Some(obj : Terminal) =>
val obj_def = obj.Definition
val obj_uid = obj_def.ObjectId
val obj_data = obj_def.Packet.ConstructorData(obj).get
sendResponse(PacketCoding.CreateGamePacket(0,
ObjectCreateMessage(
obj_uid,
PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1),
obj_data
)
))
case _ => ;
}
//seat terminal occupants
continent.GUID(terminal_guid) match {
case Some(obj : Mountable) =>
obj.MountPoints.foreach({ case((_, seat_num)) =>
obj.Seat(seat_num).get.Occupant match {
case Some(tplayer) =>
if(tplayer.HasGUID) {
sendResponse(PacketCoding.CreateGamePacket(0, ObjectAttachMessage(parent_guid, tplayer.GUID, seat_num)))
}
case None => ;
}
})
case _ => ;
}
})
avatarService ! Service.Join(player.Continent)
localService ! Service.Join(player.Continent)
vehicleService ! Service.Join(player.Continent)
@ -1423,18 +1546,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ WarpgateRequest(continent_guid, building_guid, dest_building_guid, dest_continent_guid, unk1, unk2) =>
log.info("WarpgateRequest: " + msg)
case msg @ MountVehicleMsg(player_guid, vehicle_guid, unk) =>
//log.info("MountVehicleMsg: "+msg)
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
case msg @ MountVehicleMsg(player_guid, mountable_guid, unk) =>
log.info("MountVehicleMsg: "+msg)
continent.GUID(mountable_guid) match {
case Some(obj : Mountable) =>
obj.GetSeatFromMountPoint(unk) match {
case Some(seat_num) =>
obj.Actor ! Vehicle.TrySeatPlayer(seat_num, player)
obj.Actor ! Mountable.TryMount(player, seat_num)
case None =>
log.warn(s"MountVehicleMsg: attempted to board vehicle $vehicle_guid's seat $unk, but no seat exists there")
log.warn(s"MountVehicleMsg: attempted to board mountable $mountable_guid's seat $unk, but no seat exists there")
}
case None | Some(_) =>
log.warn(s"MountVehicleMsg: not a vehicle")
log.warn(s"MountVehicleMsg: not a mountable thing")
}
case msg @ DismountVehicleMsg(player_guid, unk1, unk2) =>
@ -1443,31 +1566,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(player.GUID == player_guid) {
//common warning for this section
def dismountWarning(msg : String) : Unit = {
log.warn(s"$msg; a vehicle may not know that a player is no longer sitting it in")
log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it")
}
//normally disembarking from a seat
player.VehicleSeated match {
case Some(vehicle_guid) =>
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
obj.Seats.find(seat => seat.Occupant.contains(player)) match {
case Some(obj_guid) =>
continent.GUID(obj_guid) match {
case Some(obj : Mountable) =>
val seats = obj.Seats.values
seats.find(seat => seat.Occupant.contains(player)) match {
case Some(seat) =>
val vel = obj.Velocity.getOrElse(Vector3(0f, 0f, 0f))
val has_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt
if(seat.Bailable || obj.Velocity.isEmpty || has_vel == 0) { //ugh, float comparison
seat.Occupant = None
if(obj.Seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
//special actions
obj match {
case (veh : Vehicle) =>
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(veh, continent, 600L) //start vehicle decay (10m)
}
case _ => ;
}
}
case None =>
dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid")
dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable $obj_guid")
}
case _ =>
dismountWarning(s"DismountVehicleMsg: can not find vehicle $vehicle_guid")
dismountWarning(s"DismountVehicleMsg: can not find mountable entity $obj_guid")
}
case None =>
dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle")
dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a mountable entity")
}
//should be safe
player.VehicleSeated = None
@ -1483,12 +1612,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
if(tplayer.VehicleSeated.contains(vehicle_guid)) {
continent.GUID(vehicle_guid) match {
case Some(obj : Vehicle) =>
obj.Seats.find(seat => seat.Occupant.contains(tplayer)) match {
val seats = obj.Seats.values
seats.find(seat => seat.Occupant.contains(tplayer)) match {
case Some(seat) =>
seat.Occupant = None
tplayer.VehicleSeated = None
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, unk1, unk2))
if(obj.Seats.count(seat => seat.isOccupied) == 0) {
if(seats.count(seat => seat.isOccupied) == 0) {
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(obj, continent, 600L) //start vehicle decay (10m)
}
case None =>
@ -1984,6 +2114,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
}
/**
* Gives a target player positive battle experience points only.
* If the player has access to more implant slots as a result of changing battle experience points, unlock those slots.
* @param tplayer the player
* @param bep the change in experience points, positive by assertion
* @return the player's current battle experience points
*/
def AwardBattleExperiencePoints(tplayer : Player, bep : Long) : Long = {
val oldBep = tplayer.BEP
if(bep <= 0) {
log.error(s"trying to set $bep battle experience points on $tplayer; value can not be negative")
oldBep
}
else {
val oldSlots = DetailedCharacterData.numberOfImplantSlots(oldBep)
val newBep = oldBep + bep
val newSlots = DetailedCharacterData.numberOfImplantSlots(newBep)
tplayer.BEP = newBep
if(newSlots > oldSlots) {
(oldSlots until newSlots).foreach(slotNumber => {
tplayer.Implants(slotNumber).Unlocked = true
log.info(s"unlocking implant slot $slotNumber for $tplayer")
})
}
newBep
}
}
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))