mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +00:00
Misc Fixes (#1076)
* new paradigm for character creation detection of old characters by name; vehicle channel when seated in vehicle (ant); second wind activates as long as non-fatal damage n>=25; coordinated sequence of deployables whose UI is being updated * the max timer will assert itself through death and respawn * in theory, the tests are fixed; that may change from execution to execution, as is usual * adjusted how the mechanized exo-suit timer asserts itself when in conjunction with prior exo-suit purchase orders * players in seats have their mounted information shortened in a more straightforward, less fault-prone way; stamina recharge command shortened * fixed vehicles not loading when player has no GUID; deactivated squad features (may cause trouble for the Router, but we'll manage); removed lingering, unnecessary radiation tick * even if the player seems to be standing completely still, send an update packet once in a while (1500ms) * removing an active router will always clean up an active router telepad with which it is paired * better timing for refresh of the character select screen; potential to stop moving vehicles from anothers's perspectives * block mounting while vehicle in motion, or in control; if ejected early, end control early * block mounting while vehicle in motion, or in control (2)
This commit is contained in:
parent
66f45edcd3
commit
70c4393e9b
27 changed files with 593 additions and 392 deletions
|
|
@ -90,6 +90,9 @@ game {
|
||||||
# Purchases timers for the mechanized assault exo-suits all update at the same time when any of them would update
|
# Purchases timers for the mechanized assault exo-suits all update at the same time when any of them would update
|
||||||
shared-max-cooldown = no
|
shared-max-cooldown = no
|
||||||
|
|
||||||
|
# Purchases timers for the battleframe robotics vehicles all update at the same time when either of them would update
|
||||||
|
shared-bfr-cooldown = yes
|
||||||
|
|
||||||
# HART system, shuttles and facilities
|
# HART system, shuttles and facilities
|
||||||
hart {
|
hart {
|
||||||
# How long the shuttle is not boarding passengers (going through the motions)
|
# How long the shuttle is not boarding passengers (going through the motions)
|
||||||
|
|
@ -208,7 +211,7 @@ game {
|
||||||
# Use the index of that sample distance from this sequence in the sequence `delays` below.
|
# Use the index of that sample distance from this sequence in the sequence `delays` below.
|
||||||
ranges = [150, 300, 400]
|
ranges = [150, 300, 400]
|
||||||
# The absolute time delay before a successful packet must be dispatched regardless of distance. (s)
|
# The absolute time delay before a successful packet must be dispatched regardless of distance. (s)
|
||||||
delay-max = 1000
|
delay-max = 1500
|
||||||
# The time delays for each distance range before a successful packet must be dispatched. [s]
|
# The time delays for each distance range before a successful packet must be dispatched. [s]
|
||||||
# The index for an entry in this sequence is expected to be discovered using the `ranges` sequence above.
|
# The index for an entry in this sequence is expected to be discovered using the `ranges` sequence above.
|
||||||
# Delays between packets may not be as precise as desired
|
# Delays between packets may not be as precise as desired
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,17 @@
|
||||||
// Copyright (c) 2019 PSForever
|
// Copyright (c) 2019 PSForever
|
||||||
package net.psforever.actors.session
|
package net.psforever.actors.session
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
||||||
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
|
||||||
import net.psforever.objects.avatar.{ProgressDecoration, SpecialCarry}
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import net.psforever.objects.avatar.scoring.{Death, EquipmentStat, KDAStat, Kill}
|
|
||||||
import net.psforever.objects.sourcing.SourceWithHealthEntry
|
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
|
||||||
import net.psforever.objects.vital.{DamagingActivity, HealingActivity, SpawningActivity, Vitality}
|
|
||||||
import net.psforever.packet.game.objectcreate.BasicCharacterData
|
|
||||||
import net.psforever.types.ExperienceType
|
|
||||||
import org.joda.time.{LocalDateTime, Seconds}
|
import org.joda.time.{LocalDateTime, Seconds}
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.{ExecutionContextExecutor, Future, Promise}
|
import scala.concurrent.{ExecutionContextExecutor, Future, Promise}
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
//
|
//
|
||||||
|
import net.psforever.objects._
|
||||||
import net.psforever.objects.avatar.{
|
import net.psforever.objects.avatar.{
|
||||||
Avatar,
|
Avatar,
|
||||||
BattleRank,
|
BattleRank,
|
||||||
|
|
@ -29,25 +22,31 @@ import net.psforever.objects.avatar.{
|
||||||
Implant,
|
Implant,
|
||||||
MemberLists,
|
MemberLists,
|
||||||
PlayerControl,
|
PlayerControl,
|
||||||
Shortcut => AvatarShortcut
|
ProgressDecoration,
|
||||||
|
Shortcut => AvatarShortcut,
|
||||||
|
SpecialCarry
|
||||||
}
|
}
|
||||||
|
import net.psforever.objects.avatar.scoring.{Death, EquipmentStat, KDAStat, Kill}
|
||||||
import net.psforever.objects.definition._
|
import net.psforever.objects.definition._
|
||||||
import net.psforever.objects.definition.converter.CharacterSelectConverter
|
import net.psforever.objects.definition.converter.CharacterSelectConverter
|
||||||
import net.psforever.objects.inventory.Container
|
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.objects.inventory.InventoryItem
|
import net.psforever.objects.inventory.{Container, InventoryItem}
|
||||||
import net.psforever.objects.loadouts.{InfantryLoadout, Loadout, VehicleLoadout}
|
import net.psforever.objects.loadouts.{InfantryLoadout, Loadout, VehicleLoadout}
|
||||||
import net.psforever.objects._
|
|
||||||
import net.psforever.objects.locker.LockerContainer
|
import net.psforever.objects.locker.LockerContainer
|
||||||
import net.psforever.objects.sourcing.PlayerSource
|
import net.psforever.objects.sourcing.{PlayerSource,SourceWithHealthEntry}
|
||||||
import net.psforever.objects.vital.HealFromImplant
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
import net.psforever.objects.vital.{DamagingActivity, HealFromImplant, HealingActivity, SpawningActivity, Vitality}
|
||||||
|
import net.psforever.packet.game.objectcreate.{BasicCharacterData, ObjectClass, RibbonBars}
|
||||||
import net.psforever.packet.game.{Friend => GameFriend, _}
|
import net.psforever.packet.game.{Friend => GameFriend, _}
|
||||||
|
import net.psforever.persistence
|
||||||
|
import net.psforever.services.Service
|
||||||
|
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import net.psforever.types.{
|
import net.psforever.types.{
|
||||||
CharacterSex,
|
CharacterSex,
|
||||||
CharacterVoice,
|
CharacterVoice,
|
||||||
Cosmetic,
|
Cosmetic,
|
||||||
ExoSuitType,
|
ExoSuitType,
|
||||||
|
ExperienceType,
|
||||||
ImplantType,
|
ImplantType,
|
||||||
LoadoutType,
|
LoadoutType,
|
||||||
MemberAction,
|
MemberAction,
|
||||||
|
|
@ -57,11 +56,7 @@ import net.psforever.types.{
|
||||||
TransactionType
|
TransactionType
|
||||||
}
|
}
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
import net.psforever.persistence
|
|
||||||
import net.psforever.util.{Config, Database, DefinitionUtil}
|
import net.psforever.util.{Config, Database, DefinitionUtil}
|
||||||
import net.psforever.services.Service
|
|
||||||
//import org.log4s.Logger
|
|
||||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
|
||||||
|
|
||||||
object AvatarActor {
|
object AvatarActor {
|
||||||
def apply(sessionActor: ActorRef[SessionActor.Command]): Behavior[Command] =
|
def apply(sessionActor: ActorRef[SessionActor.Command]): Behavior[Command] =
|
||||||
|
|
@ -400,28 +395,33 @@ object AvatarActor {
|
||||||
def resolveSharedPurchaseTimeNames(pair: (BasicDefinition, String)): Seq[(BasicDefinition, String)] = {
|
def resolveSharedPurchaseTimeNames(pair: (BasicDefinition, String)): Seq[(BasicDefinition, String)] = {
|
||||||
val (definition, name) = pair
|
val (definition, name) = pair
|
||||||
if (name.matches("(tr|nc|vs)hev_.+") && Config.app.game.sharedMaxCooldown) {
|
if (name.matches("(tr|nc|vs)hev_.+") && Config.app.game.sharedMaxCooldown) {
|
||||||
|
//all mechanized exo-suits
|
||||||
val faction = name.take(2)
|
val faction = name.take(2)
|
||||||
(if (faction.equals("nc")) {
|
(if (faction.equals("nc")) {
|
||||||
Seq(GlobalDefinitions.nchev_scattercannon, GlobalDefinitions.nchev_falcon, GlobalDefinitions.nchev_sparrow)
|
Seq(GlobalDefinitions.nchev_scattercannon, GlobalDefinitions.nchev_falcon, GlobalDefinitions.nchev_sparrow)
|
||||||
} else if (faction.equals("vs")) {
|
} else if (faction.equals("vs")) {
|
||||||
Seq(GlobalDefinitions.vshev_quasar, GlobalDefinitions.vshev_comet, GlobalDefinitions.vshev_starfire)
|
Seq(GlobalDefinitions.vshev_quasar, GlobalDefinitions.vshev_comet, GlobalDefinitions.vshev_starfire)
|
||||||
} else {
|
} else {
|
||||||
Seq(GlobalDefinitions.trhev_dualcycler, GlobalDefinitions.trhev_pounder, GlobalDefinitions.trhev_burster)
|
Seq(GlobalDefinitions.trhev_dualcycler, GlobalDefinitions.trhev_pounder, GlobalDefinitions.trhev_burster)
|
||||||
}).zip(
|
}).map { tdef => (tdef, tdef.Descriptor) }
|
||||||
Seq(s"${faction}hev_antipersonnel", s"${faction}hev_antivehicular", s"${faction}hev_antiaircraft")
|
} else if ((name.matches(".+_gunner") || name.matches(".+_flight")) && Config.app.game.sharedBfrCooldown) {
|
||||||
)
|
//all battleframe robotics vehicles
|
||||||
} else {
|
|
||||||
definition match {
|
definition match {
|
||||||
case vdef: VehicleDefinition if GlobalDefinitions.isBattleFrameFlightVehicle(vdef) =>
|
case vdef: VehicleDefinition if GlobalDefinitions.isBattleFrameFlightVehicle(vdef) =>
|
||||||
val bframe = name.substring(0, name.indexOf('_'))
|
val bframe = name.substring(0, name.indexOf('_'))
|
||||||
val gunner = bframe + "_gunner"
|
val gunner = bframe + "_gunner"
|
||||||
Seq((DefinitionUtil.fromString(gunner), gunner), (vdef, name))
|
Seq((DefinitionUtil.fromString(gunner), gunner), pair)
|
||||||
|
|
||||||
case vdef: VehicleDefinition if GlobalDefinitions.isBattleFrameGunnerVehicle(vdef) =>
|
case vdef: VehicleDefinition if GlobalDefinitions.isBattleFrameGunnerVehicle(vdef) =>
|
||||||
val bframe = name.substring(0, name.indexOf('_'))
|
val bframe = name.substring(0, name.indexOf('_'))
|
||||||
val flight = bframe + "_flight"
|
val flight = bframe + "_flight"
|
||||||
Seq((vdef, name), (DefinitionUtil.fromString(flight), flight))
|
Seq(pair, (DefinitionUtil.fromString(flight), flight))
|
||||||
|
case _ =>
|
||||||
|
Seq.empty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
definition match {
|
||||||
|
case tdef: ToolDefinition if GlobalDefinitions.isMaxArms(tdef) =>
|
||||||
|
Seq((tdef, tdef.Descriptor))
|
||||||
case _ =>
|
case _ =>
|
||||||
Seq(pair)
|
Seq(pair)
|
||||||
}
|
}
|
||||||
|
|
@ -913,43 +913,8 @@ class AvatarActor(
|
||||||
AvatarActor.displayLookingForSquad(session.get, if (lfs) 1 else 0)
|
AvatarActor.displayLookingForSquad(session.get, if (lfs) 1 else 0)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case CreateAvatar(name, head, voice, gender, empire) =>
|
case CreateAvatar(name, head, voice, sex, empire) =>
|
||||||
import ctx._
|
createAvatar(account, name, sex, empire, head, voice)
|
||||||
ctx.run(query[persistence.Avatar].filter(_.name ilike lift(name)).filter(!_.deleted)).onComplete {
|
|
||||||
case Success(characters) =>
|
|
||||||
characters.headOption match {
|
|
||||||
case None =>
|
|
||||||
val result = for {
|
|
||||||
_ <- ctx.run(
|
|
||||||
query[persistence.Avatar]
|
|
||||||
.insert(
|
|
||||||
_.name -> lift(name),
|
|
||||||
_.accountId -> lift(account.id),
|
|
||||||
_.factionId -> lift(empire.id),
|
|
||||||
_.headId -> lift(head),
|
|
||||||
_.voiceId -> lift(voice.id),
|
|
||||||
_.genderId -> lift(gender.value),
|
|
||||||
_.bep -> lift(Config.app.game.newAvatar.br.experience),
|
|
||||||
_.cep -> lift(Config.app.game.newAvatar.cr.experience)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} yield ()
|
|
||||||
result.onComplete {
|
|
||||||
case Success(_) =>
|
|
||||||
log.debug(s"AvatarActor: created character $name for account ${account.name}")
|
|
||||||
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass)
|
|
||||||
sendAvatars(account)
|
|
||||||
case Failure(e) => log.error(e)("db failure")
|
|
||||||
}
|
|
||||||
case Some(_) =>
|
|
||||||
// send "char already exists"
|
|
||||||
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(1))
|
|
||||||
}
|
|
||||||
case Failure(e) =>
|
|
||||||
log.error(e)("db failure")
|
|
||||||
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(3))
|
|
||||||
sendAvatars(account)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case DeleteAvatar(id) =>
|
case DeleteAvatar(id) =>
|
||||||
|
|
@ -1482,7 +1447,8 @@ class AvatarActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case UpdatePurchaseTime(definition, time) =>
|
case UpdatePurchaseTime(definition, time) =>
|
||||||
var newTimes = avatar.cooldowns.purchase
|
var theTimes = avatar.cooldowns.purchase
|
||||||
|
var updateTheTimes: Boolean = false
|
||||||
AvatarActor
|
AvatarActor
|
||||||
.resolveSharedPurchaseTimeNames(AvatarActor.resolvePurchaseTimeName(avatar.faction, definition))
|
.resolveSharedPurchaseTimeNames(AvatarActor.resolvePurchaseTimeName(avatar.faction, definition))
|
||||||
.foreach {
|
.foreach {
|
||||||
|
|
@ -1490,7 +1456,8 @@ class AvatarActor(
|
||||||
Avatar.purchaseCooldowns.get(item) match {
|
Avatar.purchaseCooldowns.get(item) match {
|
||||||
case Some(cooldown) =>
|
case Some(cooldown) =>
|
||||||
//only send for items with cooldowns
|
//only send for items with cooldowns
|
||||||
newTimes = newTimes.updated(name, time)
|
updateTheTimes = true
|
||||||
|
theTimes = theTimes.updated(name, time)
|
||||||
updatePurchaseTimer(
|
updatePurchaseTimer(
|
||||||
name,
|
name,
|
||||||
cooldown.toSeconds,
|
cooldown.toSeconds,
|
||||||
|
|
@ -1502,7 +1469,9 @@ class AvatarActor(
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avatarCopy(avatar.copy(cooldowns = avatar.cooldowns.copy(purchase = newTimes)))
|
if (updateTheTimes) {
|
||||||
|
avatarCopy(avatar.copy(cooldowns = avatar.cooldowns.copy(purchase = theTimes)))
|
||||||
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case UpdateUseTime(definition, time) =>
|
case UpdateUseTime(definition, time) =>
|
||||||
|
|
@ -1632,11 +1601,7 @@ class AvatarActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case RestoreStamina(stamina) =>
|
case RestoreStamina(stamina) =>
|
||||||
tryRestoreStaminaForSession(stamina) match {
|
tryRestoreStaminaForSession(stamina).collect { actuallyRestoreStamina(stamina, _) }
|
||||||
case Some(sess) =>
|
|
||||||
actuallyRestoreStamina(stamina, sess)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case RestoreStaminaPeriodically(stamina) =>
|
case RestoreStaminaPeriodically(stamina) =>
|
||||||
|
|
@ -1897,11 +1862,7 @@ class AvatarActor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// if we need to start stamina regeneration
|
// if we need to start stamina regeneration
|
||||||
tryRestoreStaminaForSession(stamina = 1) match {
|
tryRestoreStaminaForSession(stamina = 1).collect { _ => defaultStaminaRegen(initialDelay = 0.5f seconds) }
|
||||||
case Some(_) =>
|
|
||||||
defaultStaminaRegen(initialDelay = 0.5f seconds)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
replyTo ! AvatarLoginResponse(avatar)
|
replyTo ! AvatarLoginResponse(avatar)
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
log.error(e)("db failure")
|
log.error(e)("db failure")
|
||||||
|
|
@ -1991,11 +1952,7 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
def restoreStaminaPeriodically(stamina: Int): Unit = {
|
def restoreStaminaPeriodically(stamina: Int): Unit = {
|
||||||
tryRestoreStaminaForSession(stamina) match {
|
tryRestoreStaminaForSession(stamina).collect { actuallyRestoreStaminaIfStationary(stamina, _) }
|
||||||
case Some(sess) =>
|
|
||||||
actuallyRestoreStaminaIfStationary(stamina, sess)
|
|
||||||
case _ => ;
|
|
||||||
}
|
|
||||||
startIfStoppedStaminaRegen(initialDelay = 0.5f seconds)
|
startIfStoppedStaminaRegen(initialDelay = 0.5f seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2633,27 +2590,40 @@ class AvatarActor(
|
||||||
|
|
||||||
def refreshPurchaseTimes(keys: Set[String]): Unit = {
|
def refreshPurchaseTimes(keys: Set[String]): Unit = {
|
||||||
var keysToDrop: Seq[String] = Nil
|
var keysToDrop: Seq[String] = Nil
|
||||||
|
val faction = avatar.faction
|
||||||
keys.foreach { key =>
|
keys.foreach { key =>
|
||||||
avatar.cooldowns.purchase.find { case (name, _) => name.equals(key) } match {
|
avatar
|
||||||
case Some((name, purchaseTime)) =>
|
.cooldowns
|
||||||
|
.purchase
|
||||||
|
.find { case (name, _) => name.equals(key) }
|
||||||
|
.flatMap { case (name, purchaseTime) =>
|
||||||
val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()).getSeconds
|
val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()).getSeconds
|
||||||
Avatar.purchaseCooldowns.find(_._1.Name.equals(name)) match {
|
Avatar
|
||||||
case Some((obj, cooldown)) if cooldown.toSeconds - secondsSincePurchase > 0 =>
|
.purchaseCooldowns
|
||||||
val (_, name) = AvatarActor.resolvePurchaseTimeName(avatar.faction, obj)
|
.find(_._1.Descriptor.equals(name))
|
||||||
updatePurchaseTimer(
|
.collect {
|
||||||
name,
|
case (obj, cooldown) =>
|
||||||
cooldown.toSeconds - secondsSincePurchase,
|
(obj, cooldown.toSeconds - secondsSincePurchase)
|
||||||
obj match {
|
}
|
||||||
case _: KitDefinition => false
|
.orElse {
|
||||||
case _ => true
|
keysToDrop = keysToDrop :+ key //key indicates cooldown, but no cooldown delay
|
||||||
}
|
None
|
||||||
)
|
|
||||||
|
|
||||||
case _ =>
|
|
||||||
keysToDrop = keysToDrop :+ key //key has timed-out
|
|
||||||
}
|
}
|
||||||
case _ => ;
|
}
|
||||||
}
|
.collect {
|
||||||
|
case (obj, remainingTime) if remainingTime > 0 =>
|
||||||
|
val (_, resolvedName) = AvatarActor.resolvePurchaseTimeName(faction, obj)
|
||||||
|
updatePurchaseTimer(
|
||||||
|
resolvedName,
|
||||||
|
remainingTime,
|
||||||
|
obj match {
|
||||||
|
case _: KitDefinition => false
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case (_, _) =>
|
||||||
|
keysToDrop = keysToDrop :+ key //key has timed-out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (keysToDrop.nonEmpty) {
|
if (keysToDrop.nonEmpty) {
|
||||||
val cdown = avatar.cooldowns
|
val cdown = avatar.cooldowns
|
||||||
|
|
@ -2999,4 +2969,131 @@ class AvatarActor(
|
||||||
def updateToolDischarge(stats: EquipmentStat): Unit = {
|
def updateToolDischarge(stats: EquipmentStat): Unit = {
|
||||||
avatar.scorecard.rate(stats)
|
avatar.scorecard.rate(stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def createAvatar(
|
||||||
|
account: Account,
|
||||||
|
name: String,
|
||||||
|
sex: CharacterSex,
|
||||||
|
empire: PlanetSideEmpire.Value,
|
||||||
|
head: Int,
|
||||||
|
voice: CharacterVoice.Value
|
||||||
|
): Unit = {
|
||||||
|
import ctx._
|
||||||
|
ctx.run(
|
||||||
|
query[persistence.Avatar]
|
||||||
|
.filter(_.name ilike lift(name))
|
||||||
|
.map(a => (a.accountId, a.deleted, a.created))
|
||||||
|
)
|
||||||
|
.onComplete {
|
||||||
|
case Success(foundCharacters) if foundCharacters.size == 1 =>
|
||||||
|
testFoundCharacters(
|
||||||
|
account,
|
||||||
|
name,
|
||||||
|
foundCharacters
|
||||||
|
.collect { case (a, b, c) => (a, b, c.toDate.getTime) }
|
||||||
|
)
|
||||||
|
case Success(foundCharacters) if foundCharacters.nonEmpty =>
|
||||||
|
testFoundCharacters(
|
||||||
|
account,
|
||||||
|
name,
|
||||||
|
foundCharacters
|
||||||
|
.map { case (a, b, created) => (a, b, created.toDate.getTime) }
|
||||||
|
.sortBy { case (_, _, created) => created }
|
||||||
|
.toList
|
||||||
|
)
|
||||||
|
case Success(_) =>
|
||||||
|
//create new character
|
||||||
|
actuallyCreateNewCharacter(account.id, account.name, name, sex, empire, head, voice)
|
||||||
|
.onComplete(_ => sendAvatars(account))
|
||||||
|
case Failure(e) =>
|
||||||
|
log.error(e)("db failure")
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3))
|
||||||
|
sendAvatars(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def testFoundCharacters(
|
||||||
|
account: Account,
|
||||||
|
name: String,
|
||||||
|
foundCharacters: IterableOnce[(Int, Boolean, Long)]): Unit = {
|
||||||
|
val foundCharactersIterator = foundCharacters.iterator
|
||||||
|
if (foundCharactersIterator.exists { case (_, deleted, _ ) => !deleted } ||
|
||||||
|
foundCharactersIterator.exists { case (accountId, _, _) => accountId != account.id }) {
|
||||||
|
//send "char already exists"
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 1))
|
||||||
|
} else {
|
||||||
|
//reactivate character
|
||||||
|
reactivateCharacter(account.id, account.name, name)
|
||||||
|
.onComplete(_ => sendAvatars(account))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def actuallyCreateNewCharacter(
|
||||||
|
accountId: Int,
|
||||||
|
accountName: String,
|
||||||
|
name: String,
|
||||||
|
sex: CharacterSex,
|
||||||
|
empire: PlanetSideEmpire.Value,
|
||||||
|
head: Int,
|
||||||
|
voice: CharacterVoice.Value,
|
||||||
|
bep: Long = Config.app.game.newAvatar.br.experience,
|
||||||
|
cep: Long = Config.app.game.newAvatar.cr.experience
|
||||||
|
): Future[Boolean] = {
|
||||||
|
val output: Promise[Boolean] = Promise[Boolean]()
|
||||||
|
import ctx._
|
||||||
|
val result = for {
|
||||||
|
_ <- ctx.run(
|
||||||
|
query[persistence.Avatar]
|
||||||
|
.insert(
|
||||||
|
_.name -> lift(name),
|
||||||
|
_.accountId -> lift(accountId),
|
||||||
|
_.factionId -> lift(empire.id),
|
||||||
|
_.headId -> lift(head),
|
||||||
|
_.voiceId -> lift(voice.id),
|
||||||
|
_.genderId -> lift(sex.value),
|
||||||
|
_.bep -> lift(bep),
|
||||||
|
_.cep -> lift(cep)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield ()
|
||||||
|
result.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
log.debug(s"AvatarActor: created character $name for account $accountName")
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass)
|
||||||
|
output.success(true)
|
||||||
|
case Failure(e) =>
|
||||||
|
log.error(e)("db failure")
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3))
|
||||||
|
output.success(false)
|
||||||
|
}
|
||||||
|
output.future
|
||||||
|
}
|
||||||
|
|
||||||
|
private def reactivateCharacter(
|
||||||
|
accountId: Int,
|
||||||
|
accountName: String,
|
||||||
|
characterName: String
|
||||||
|
): Future[Boolean] = {
|
||||||
|
val output: Promise[Boolean] = Promise[Boolean]()
|
||||||
|
import ctx._
|
||||||
|
val result = for {
|
||||||
|
out <- ctx.run(
|
||||||
|
query[persistence.Avatar]
|
||||||
|
.filter(a => a.accountId == lift(accountId))
|
||||||
|
.filter(a => a.name ilike lift(characterName))
|
||||||
|
.update(_.deleted -> lift(false))
|
||||||
|
)
|
||||||
|
} yield out
|
||||||
|
result.onComplete {
|
||||||
|
case Success(_) =>
|
||||||
|
log.debug(s"AvatarActor: character belonging to $accountName with name $characterName reactivated")
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Pass)
|
||||||
|
output.success(true)
|
||||||
|
case Failure(e) =>
|
||||||
|
log.error(e)("db failure")
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 3))
|
||||||
|
output.success(false)
|
||||||
|
}
|
||||||
|
output.future
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,10 +104,10 @@ class SessionAvatarHandlers(
|
||||||
} //ms
|
} //ms
|
||||||
if (!wasVisible ||
|
if (!wasVisible ||
|
||||||
!previouslyInDrawableRange ||
|
!previouslyInDrawableRange ||
|
||||||
|
durationSince > drawConfig.delayMax ||
|
||||||
(!lastMsg.contains(pstateToSave) &&
|
(!lastMsg.contains(pstateToSave) &&
|
||||||
(canSeeReallyFar ||
|
(canSeeReallyFar ||
|
||||||
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
|
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
|
||||||
durationSince > drawConfig.delayMax ||
|
|
||||||
sessionData.canSeeReallyFar ||
|
sessionData.canSeeReallyFar ||
|
||||||
durationSince > targetDelay
|
durationSince > targetDelay
|
||||||
)
|
)
|
||||||
|
|
@ -425,8 +425,7 @@ class SessionAvatarHandlers(
|
||||||
sessionData.zoning.zoningStatus = Zoning.Status.None
|
sessionData.zoning.zoningStatus = Zoning.Status.None
|
||||||
sessionData.zoning.spawn.deadState = DeadState.Dead
|
sessionData.zoning.spawn.deadState = DeadState.Dead
|
||||||
continent.GUID(mount).collect { case obj: Vehicle =>
|
continent.GUID(mount).collect { case obj: Vehicle =>
|
||||||
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
|
sessionData.vehicles.DriverVehicleControl(obj)
|
||||||
sessionData.vehicles.serverVehicleControlVelocity = None
|
|
||||||
sessionData.unaccessContainer(obj)
|
sessionData.unaccessContainer(obj)
|
||||||
}
|
}
|
||||||
sessionData.playerActionsToCancel()
|
sessionData.playerActionsToCancel()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleM
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID}
|
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||||
|
|
||||||
class SessionMountHandlers(
|
class SessionMountHandlers(
|
||||||
val sessionData: SessionData,
|
val sessionData: SessionData,
|
||||||
|
|
@ -56,6 +56,7 @@ class SessionMountHandlers(
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
|
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
|
||||||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||||
|
sessionData.accessContainer(obj)
|
||||||
MountingAction(tplayer, obj, seatNumber)
|
MountingAction(tplayer, obj, seatNumber)
|
||||||
|
|
||||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||||
|
|
@ -237,7 +238,7 @@ class SessionMountHandlers(
|
||||||
}")
|
}")
|
||||||
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
|
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
|
||||||
sessionData.unaccessContainer(obj)
|
sessionData.unaccessContainer(obj)
|
||||||
DismountAction(tplayer, obj, seatNum)
|
DismountVehicleAction(tplayer, obj, seatNum)
|
||||||
|
|
||||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||||
continent.VehicleEvents ! VehicleServiceMessage(
|
continent.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
|
@ -288,6 +289,43 @@ class SessionMountHandlers(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||||
|
* @param tplayer the player
|
||||||
|
* @param obj the mountable object
|
||||||
|
* @param seatNum the mount out of which which the player is disembarking
|
||||||
|
*/
|
||||||
|
def DismountVehicleAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = {
|
||||||
|
DismountAction(tplayer, obj, seatNum)
|
||||||
|
//until vehicles maintain synchronized momentum without a driver
|
||||||
|
obj match {
|
||||||
|
case v: Vehicle
|
||||||
|
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||||
|
sessionData.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||||
|
sessionData.vehicles.ServerVehicleOverrideStop(v)
|
||||||
|
}
|
||||||
|
v.Velocity = Vector3.Zero
|
||||||
|
continent.VehicleEvents ! VehicleServiceMessage(
|
||||||
|
continent.id,
|
||||||
|
VehicleAction.VehicleState(
|
||||||
|
tplayer.GUID,
|
||||||
|
v.GUID,
|
||||||
|
unk1 = 0,
|
||||||
|
v.Position,
|
||||||
|
v.Orientation,
|
||||||
|
vel = None,
|
||||||
|
v.Flying,
|
||||||
|
unk3 = 0,
|
||||||
|
unk4 = 0,
|
||||||
|
wheel_direction = 15,
|
||||||
|
unk5 = false,
|
||||||
|
unk6 = v.Cloaked
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common activities/procedure when a player dismounts a valid mountable object.
|
* Common activities/procedure when a player dismounts a valid mountable object.
|
||||||
* @param tplayer the player
|
* @param tplayer the player
|
||||||
|
|
@ -305,7 +343,7 @@ class SessionMountHandlers(
|
||||||
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
|
||||||
continent.VehicleEvents ! VehicleServiceMessage(
|
continent.VehicleEvents ! VehicleServiceMessage(
|
||||||
continent.id,
|
continent.id,
|
||||||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2=false)
|
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,17 +55,17 @@ class SessionSquadHandlers(
|
||||||
/* packet */
|
/* packet */
|
||||||
|
|
||||||
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = {
|
||||||
val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
// val SquadDefinitionActionMessage(u1, u2, action) = pkt
|
||||||
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))
|
// squadService ! SquadServiceMessage(player, continent, SquadServiceAction.Definition(u1, u2, action))
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit = {
|
def handleSquadMemberRequest(pkt: SquadMembershipRequest): Unit = {
|
||||||
val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
// val SquadMembershipRequest(request_type, char_id, unk3, player_name, unk5) = pkt
|
||||||
squadService ! SquadServiceMessage(
|
// squadService ! SquadServiceMessage(
|
||||||
player,
|
// player,
|
||||||
continent,
|
// continent,
|
||||||
SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
// SquadServiceAction.Membership(request_type, char_id, unk3, player_name, unk5)
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleSquadWaypointRequest(pkt: SquadWaypointRequest): Unit = {
|
def handleSquadWaypointRequest(pkt: SquadWaypointRequest): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class SessionVehicleHandlers(
|
||||||
val resolvedPlayerGuid = if (player.HasGUID) {
|
val resolvedPlayerGuid = if (player.HasGUID) {
|
||||||
player.GUID
|
player.GUID
|
||||||
} else {
|
} else {
|
||||||
Service.defaultPlayerGUID
|
PlanetSideGUID(-1)
|
||||||
}
|
}
|
||||||
val isNotSameTarget = resolvedPlayerGuid != guid
|
val isNotSameTarget = resolvedPlayerGuid != guid
|
||||||
reply match {
|
reply match {
|
||||||
|
|
@ -202,17 +202,19 @@ class SessionVehicleHandlers(
|
||||||
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
|
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
|
||||||
val reverseSpeed = if (strafe > 1) { 0 } else { speed }
|
val reverseSpeed = if (strafe > 1) { 0 } else { speed }
|
||||||
//strafe or reverse, not both
|
//strafe or reverse, not both
|
||||||
sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed)
|
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||||
sendResponse(ServerVehicleOverrideMsg(
|
vehicle,
|
||||||
lock_accelerator=true,
|
ServerVehicleOverrideMsg(
|
||||||
lock_wheel=true,
|
lock_accelerator=true,
|
||||||
reverse=true,
|
lock_wheel=true,
|
||||||
unk4=false,
|
reverse=true,
|
||||||
lock_vthrust=0,
|
unk4=false,
|
||||||
strafe,
|
lock_vthrust=0,
|
||||||
reverseSpeed,
|
strafe,
|
||||||
unk8=Some(0)
|
reverseSpeed,
|
||||||
))
|
unk8=Some(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
context.system.scheduler.scheduleOnce(
|
context.system.scheduler.scheduleOnce(
|
||||||
delay milliseconds,
|
delay milliseconds,
|
||||||
|
|
@ -220,25 +222,27 @@ class SessionVehicleHandlers(
|
||||||
VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay))
|
VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay))
|
||||||
)
|
)
|
||||||
|
|
||||||
case VehicleResponse.KickCargo(_, _, _)
|
case VehicleResponse.KickCargo(cargo, _, _)
|
||||||
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive =>
|
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive =>
|
||||||
sessionData.vehicles.serverVehicleControlVelocity = None
|
sessionData.vehicles.TotalDriverVehicleControl(cargo)
|
||||||
sendResponse(ServerVehicleOverrideMsg(
|
|
||||||
lock_accelerator=false,
|
|
||||||
lock_wheel=false,
|
|
||||||
reverse=false,
|
|
||||||
unk4=false,
|
|
||||||
lock_vthrust=0,
|
|
||||||
lock_strafe=0,
|
|
||||||
movement_speed=0,
|
|
||||||
unk8=None
|
|
||||||
))
|
|
||||||
|
|
||||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
|
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
|
||||||
if player.VisibleSlots.contains(player.DrawnSlot) =>
|
if player.VisibleSlots.contains(player.DrawnSlot) =>
|
||||||
val vehicle_guid = vehicle.GUID
|
val vehicle_guid = vehicle.GUID
|
||||||
sessionData.playerActionsToCancel()
|
sessionData.playerActionsToCancel()
|
||||||
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
|
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||||
|
vehicle,
|
||||||
|
ServerVehicleOverrideMsg(
|
||||||
|
lock_accelerator=false,
|
||||||
|
lock_wheel=false,
|
||||||
|
reverse=false,
|
||||||
|
unk4=false,
|
||||||
|
lock_vthrust=0,
|
||||||
|
lock_strafe=0,
|
||||||
|
movement_speed=0,
|
||||||
|
unk8=None
|
||||||
|
)
|
||||||
|
)
|
||||||
sessionData.terminals.CancelAllProximityUnits()
|
sessionData.terminals.CancelAllProximityUnits()
|
||||||
player.DrawnSlot = Player.HandsDownSlot
|
player.DrawnSlot = Player.HandsDownSlot
|
||||||
sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1=true))
|
sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1=true))
|
||||||
|
|
@ -253,33 +257,51 @@ class SessionVehicleHandlers(
|
||||||
}
|
}
|
||||||
|
|
||||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) =>
|
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) =>
|
||||||
val vehicle_guid = vehicle.GUID
|
val vehicleGuid = vehicle.GUID
|
||||||
|
val playerGuid = player.GUID
|
||||||
sessionData.playerActionsToCancel()
|
sessionData.playerActionsToCancel()
|
||||||
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
|
|
||||||
sessionData.terminals.CancelAllProximityUnits()
|
sessionData.terminals.CancelAllProximityUnits()
|
||||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
|
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attribute_type=22, attribute_value=1L)) //mount points off
|
||||||
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
|
sendResponse(PlanetsideAttributeMessage(playerGuid, attribute_type=21, vehicleGuid)) //ownership
|
||||||
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
|
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
|
||||||
case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
|
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
|
||||||
val vehicle_guid = vehicle.GUID
|
|
||||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=0L)) //mount points on
|
|
||||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||||
sessionData.vehicles.ServerVehicleLock(vehicle)
|
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||||
|
vehicle,
|
||||||
|
ServerVehicleOverrideMsg(
|
||||||
|
lock_accelerator=true,
|
||||||
|
lock_wheel=true,
|
||||||
|
reverse=false,
|
||||||
|
unk4=false,
|
||||||
|
lock_vthrust=0,
|
||||||
|
lock_strafe=0,
|
||||||
|
movement_speed=0,
|
||||||
|
unk8=Some(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sessionData.vehicles.serverVehicleControlVelocity = Some(-1)
|
||||||
|
|
||||||
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
|
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
|
||||||
val vdef = vehicle.Definition
|
val vdef = vehicle.Definition
|
||||||
sessionData.vehicles.ServerVehicleOverride(
|
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||||
vehicle,
|
vehicle,
|
||||||
vdef.AutoPilotSpeed1,
|
ServerVehicleOverrideMsg(
|
||||||
flight= if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 }
|
lock_accelerator=true,
|
||||||
|
lock_wheel=true,
|
||||||
|
reverse=false,
|
||||||
|
unk4=false,
|
||||||
|
lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 },
|
||||||
|
lock_strafe=0,
|
||||||
|
movement_speed=vdef.AutoPilotSpeed1,
|
||||||
|
unk8=Some(0)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
|
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
|
||||||
session = session.copy(avatar = avatar.copy(vehicle = Some(vehicle.GUID)))
|
sessionData.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||||
sessionData.vehicles.DriverVehicleControl(vehicle, vehicle.Definition.AutoPilotSpeed2)
|
|
||||||
|
|
||||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||||
sendResponse(ChatMsg(
|
sendResponse(ChatMsg(
|
||||||
|
|
|
||||||
|
|
@ -268,22 +268,23 @@ class VehicleOperations(
|
||||||
|
|
||||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
||||||
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
||||||
sessionData.validObject(mountable_guid, decorator = "MountVehicle") match {
|
sessionData.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||||
case Some(obj: Mountable) =>
|
case obj: Mountable =>
|
||||||
obj.Actor ! Mountable.TryMount(player, entry_point)
|
obj.Actor ! Mountable.TryMount(player, entry_point)
|
||||||
case Some(_) =>
|
case _: Mountable =>
|
||||||
|
log.warn(
|
||||||
|
s"DismountVehicleMsg: ${player.Name} can not mount while server has asserted control; please wait"
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
log.error(s"MountVehicleMsg: object ${mountable_guid.guid} not a mountable thing, ${player.Name}")
|
log.error(s"MountVehicleMsg: object ${mountable_guid.guid} not a mountable thing, ${player.Name}")
|
||||||
case None => ;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
def handleDismountVehicle(pkt: DismountVehicleMsg): Unit = {
|
||||||
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
val DismountVehicleMsg(player_guid, bailType, wasKickedByDriver) = pkt
|
||||||
|
val dError: (String, Player)=> Unit = dismountError(bailType, wasKickedByDriver)
|
||||||
//TODO optimize this later
|
//TODO optimize this later
|
||||||
//common warning for this section
|
//common warning for this section
|
||||||
def dismountWarning(note: String): Unit = {
|
|
||||||
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
|
||||||
}
|
|
||||||
if (player.GUID == player_guid) {
|
if (player.GUID == player_guid) {
|
||||||
//normally disembarking from a mount
|
//normally disembarking from a mount
|
||||||
(sessionData.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
(sessionData.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||||
|
|
@ -295,16 +296,9 @@ class VehicleOperations(
|
||||||
case out @ Some(_: Mountable) =>
|
case out @ Some(_: Mountable) =>
|
||||||
out
|
out
|
||||||
case _ =>
|
case _ =>
|
||||||
dismountWarning(
|
dError(s"DismountVehicleMsg: player ${player.Name} not considered seated in a mountable entity", player)
|
||||||
s"DismountVehicleMsg: player ${player.Name}_guid not considered seated in a mountable entity"
|
|
||||||
)
|
|
||||||
sendResponse(DismountVehicleMsg(player_guid, bailType, wasKickedByDriver))
|
|
||||||
None
|
None
|
||||||
}) match {
|
}) match {
|
||||||
case Some(_) if serverVehicleControlVelocity.nonEmpty =>
|
|
||||||
log.debug(
|
|
||||||
s"DismountVehicleMsg: ${player.Name} can not dismount from vehicle while server has asserted control; please wait"
|
|
||||||
)
|
|
||||||
case Some(obj: Mountable) =>
|
case Some(obj: Mountable) =>
|
||||||
obj.PassengerInSeat(player) match {
|
obj.PassengerInSeat(player) match {
|
||||||
case Some(seat_num) =>
|
case Some(seat_num) =>
|
||||||
|
|
@ -325,15 +319,14 @@ class VehicleOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
dismountWarning(
|
dError(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}", player)
|
||||||
s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
dismountWarning(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}")
|
dError(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}", player)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//kicking someone else out of a mount; need to own that mount/mountable
|
//kicking someone else out of a mount; need to own that mount/mountable
|
||||||
|
val dWarn: (String, Player)=> Unit = dismountWarning(bailType, wasKickedByDriver)
|
||||||
player.avatar.vehicle match {
|
player.avatar.vehicle match {
|
||||||
case Some(obj_guid) =>
|
case Some(obj_guid) =>
|
||||||
(
|
(
|
||||||
|
|
@ -353,23 +346,47 @@ class VehicleOperations(
|
||||||
case Some(seat_num) =>
|
case Some(seat_num) =>
|
||||||
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
obj.Actor ! Mountable.TryDismount(tplayer, seat_num, bailType)
|
||||||
case None =>
|
case None =>
|
||||||
dismountWarning(
|
dError(s"DismountVehicleMsg: can not find where other player ${tplayer.Name} is seated in mountable $obj_guid", tplayer)
|
||||||
s"DismountVehicleMsg: can not find where other player ${player.Name}_guid is seated in mountable $obj_guid"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
case (None, _) => ;
|
case (None, _) =>
|
||||||
log.warn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle")
|
dWarn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle", player)
|
||||||
case (_, None) => ;
|
case (_, None) =>
|
||||||
log.warn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}")
|
dWarn(s"DismountVehicleMsg: player $player_guid could not be found to kick, ${player.Name}", player)
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"DismountVehicleMsg: object is either not a Mountable or not a Player")
|
dWarn(s"DismountVehicleMsg: object is either not a Mountable or not a Player", player)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle")
|
dWarn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle", player)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def dismountWarning(
|
||||||
|
bailAs: BailType.Value,
|
||||||
|
kickedByDriver: Boolean
|
||||||
|
)
|
||||||
|
(
|
||||||
|
note: String,
|
||||||
|
player: Player
|
||||||
|
): Unit = {
|
||||||
|
log.warn(note)
|
||||||
|
player.VehicleSeated = None
|
||||||
|
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def dismountError(
|
||||||
|
bailAs: BailType.Value,
|
||||||
|
kickedByDriver: Boolean
|
||||||
|
)
|
||||||
|
(
|
||||||
|
note: String,
|
||||||
|
player: Player
|
||||||
|
): Unit = {
|
||||||
|
log.error(s"$note; some vehicle might not know that ${player.Name} is no longer sitting in it")
|
||||||
|
player.VehicleSeated = None
|
||||||
|
sendResponse(DismountVehicleMsg(player.GUID, bailAs, kickedByDriver))
|
||||||
|
}
|
||||||
|
|
||||||
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = {
|
def handleMountVehicleCargo(pkt: MountVehicleCargoMsg): Unit = {
|
||||||
val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt
|
val MountVehicleCargoMsg(_, cargo_guid, carrier_guid, _) = pkt
|
||||||
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
|
(continent.GUID(cargo_guid), continent.GUID(carrier_guid)) match {
|
||||||
|
|
@ -514,87 +531,27 @@ class VehicleOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is applied to vehicles that are leaving a cargo vehicle's cargo hold to auto reverse them out
|
* Place the current vehicle under the control of the driver's commands,
|
||||||
* Lock all applicable controls of the current vehicle
|
* but leave it in a cancellable auto-drive.
|
||||||
* Set the vehicle to move in reverse
|
* @param vehicle the vehicle
|
||||||
*/
|
*/
|
||||||
def ServerVehicleLockReverse(): Unit = {
|
def ServerVehicleOverrideStop(vehicle: Vehicle): Unit = {
|
||||||
serverVehicleControlVelocity = Some(-1)
|
val vehicleGuid = vehicle.GUID
|
||||||
sendResponse(
|
session = session.copy(avatar = avatar.copy(vehicle = Some(vehicleGuid)))
|
||||||
ServerVehicleOverrideMsg(
|
sessionData.vehicles.DriverVehicleControl(vehicle, vehicle.Definition.AutoPilotSpeed2)
|
||||||
lock_accelerator = true,
|
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attribute_type=22, attribute_value=0L)) //mount points on
|
||||||
lock_wheel = true,
|
|
||||||
reverse = true,
|
|
||||||
unk4 = true,
|
|
||||||
lock_vthrust = 0,
|
|
||||||
lock_strafe = 1,
|
|
||||||
movement_speed = 2,
|
|
||||||
unk8 = Some(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is applied to vehicles that are leaving a cargo vehicle's cargo hold to strafe right out of the cargo hold for vehicles that are mounted sideways e.g. router/BFR
|
|
||||||
* Lock all applicable controls of the current vehicle
|
|
||||||
* Set the vehicle to strafe right
|
|
||||||
*/
|
|
||||||
def ServerVehicleLockStrafeRight(): Unit = {
|
|
||||||
serverVehicleControlVelocity = Some(-1)
|
|
||||||
sendResponse(
|
|
||||||
ServerVehicleOverrideMsg(
|
|
||||||
lock_accelerator = true,
|
|
||||||
lock_wheel = true,
|
|
||||||
reverse = false,
|
|
||||||
unk4 = true,
|
|
||||||
lock_vthrust = 0,
|
|
||||||
lock_strafe = 3,
|
|
||||||
movement_speed = 0,
|
|
||||||
unk8 = Some(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is applied to vehicles that are leaving a cargo vehicle's cargo hold to strafe left out of the cargo hold for vehicles that are mounted sideways e.g. router/BFR
|
|
||||||
* Lock all applicable controls of the current vehicle
|
|
||||||
* Set the vehicle to strafe left
|
|
||||||
*/
|
|
||||||
def ServerVehicleLockStrafeLeft(): Unit = {
|
|
||||||
serverVehicleControlVelocity = Some(-1)
|
|
||||||
sendResponse(
|
|
||||||
ServerVehicleOverrideMsg(
|
|
||||||
lock_accelerator = true,
|
|
||||||
lock_wheel = true,
|
|
||||||
reverse = false,
|
|
||||||
unk4 = true,
|
|
||||||
lock_vthrust = 0,
|
|
||||||
lock_strafe = 2,
|
|
||||||
movement_speed = 0,
|
|
||||||
unk8 = Some(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock all applicable controls of the current vehicle.
|
|
||||||
* This includes forward motion, turning, and, if applicable, strafing.
|
|
||||||
* @param vehicle the vehicle being controlled
|
|
||||||
*/
|
|
||||||
def ServerVehicleLock(vehicle: Vehicle): Unit = {
|
|
||||||
serverVehicleControlVelocity = Some(-1)
|
|
||||||
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=true, lock_wheel=true, reverse=false, unk4=false, 0, 1, 0, Some(0)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place the current vehicle under the control of the server's commands.
|
* Place the current vehicle under the control of the server's commands.
|
||||||
* @param vehicle the vehicle
|
* @param vehicle vehicle to be controlled;
|
||||||
* @param speed how fast the vehicle is moving forward
|
* the client's player who is receiving this packet should be mounted as its driver, but this is not explicitly tested
|
||||||
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
|
* @param pkt packet to instigate server control
|
||||||
*/
|
*/
|
||||||
def ServerVehicleOverride(vehicle: Vehicle, speed: Int = 0, flight: Int = 0): Unit = {
|
def ServerVehicleOverrideWithPacket(vehicle: Vehicle, pkt: ServerVehicleOverrideMsg): Unit = {
|
||||||
serverVehicleControlVelocity = Some(speed)
|
serverVehicleControlVelocity = Some(pkt.movement_speed)
|
||||||
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=true, lock_wheel=true, reverse=false, unk4=false, flight, 0, speed, Some(0)))
|
vehicle.DeploymentState = DriveState.AutoPilot
|
||||||
|
sendResponse(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -605,9 +562,20 @@ class VehicleOperations(
|
||||||
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
|
* @param flight whether the vehicle is ascending or not, if the vehicle is an applicable type
|
||||||
*/
|
*/
|
||||||
def DriverVehicleControl(vehicle: Vehicle, speed: Int = 0, flight: Int = 0): Unit = {
|
def DriverVehicleControl(vehicle: Vehicle, speed: Int = 0, flight: Int = 0): Unit = {
|
||||||
if (serverVehicleControlVelocity.nonEmpty) {
|
if (vehicle.DeploymentState == DriveState.AutoPilot) {
|
||||||
serverVehicleControlVelocity = None
|
TotalDriverVehicleControlWithPacket(
|
||||||
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=false, lock_wheel=false, reverse=false, unk4=true, flight, 0, speed, None))
|
vehicle,
|
||||||
|
ServerVehicleOverrideMsg(
|
||||||
|
lock_accelerator=false,
|
||||||
|
lock_wheel=false,
|
||||||
|
reverse=false,
|
||||||
|
unk4=true,
|
||||||
|
lock_vthrust=flight,
|
||||||
|
lock_strafe=0,
|
||||||
|
movement_speed=speed,
|
||||||
|
unk8=None
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,15 +586,45 @@ class VehicleOperations(
|
||||||
* @param vehicle the vehicle
|
* @param vehicle the vehicle
|
||||||
*/
|
*/
|
||||||
def ConditionalDriverVehicleControl(vehicle: Vehicle): Unit = {
|
def ConditionalDriverVehicleControl(vehicle: Vehicle): Unit = {
|
||||||
if (serverVehicleControlVelocity.nonEmpty && !serverVehicleControlVelocity.contains(0)) {
|
if (vehicle.DeploymentState == DriveState.AutoPilot) {
|
||||||
TotalDriverVehicleControl(vehicle)
|
TotalDriverVehicleControl(vehicle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection ScalaUnusedSymbol
|
/**
|
||||||
|
* Place the current vehicle under the control of the driver's commands,
|
||||||
|
* but leave it in a cancellable auto-drive.
|
||||||
|
* Stop all movement entirely.
|
||||||
|
* @param vehicle the vehicle
|
||||||
|
*/
|
||||||
def TotalDriverVehicleControl(vehicle: Vehicle): Unit = {
|
def TotalDriverVehicleControl(vehicle: Vehicle): Unit = {
|
||||||
|
TotalDriverVehicleControlWithPacket(
|
||||||
|
vehicle,
|
||||||
|
ServerVehicleOverrideMsg(
|
||||||
|
lock_accelerator=false,
|
||||||
|
lock_wheel=false,
|
||||||
|
reverse=false,
|
||||||
|
unk4=false,
|
||||||
|
lock_vthrust=0,
|
||||||
|
lock_strafe=0,
|
||||||
|
movement_speed=0,
|
||||||
|
unk8=None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place the current vehicle under the control of the driver's commands,
|
||||||
|
* but leave it in a cancellable auto-drive.
|
||||||
|
* Stop all movement entirely.
|
||||||
|
* @param vehicle the vehicle;
|
||||||
|
* the client's player who is receiving this packet should be mounted as its driver, but this is not explicitly tested
|
||||||
|
* @param pkt packet to instigate cancellable control
|
||||||
|
*/
|
||||||
|
def TotalDriverVehicleControlWithPacket(vehicle: Vehicle, pkt: ServerVehicleOverrideMsg): Unit = {
|
||||||
serverVehicleControlVelocity = None
|
serverVehicleControlVelocity = None
|
||||||
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=false, lock_wheel=false, reverse=false, unk4=false, 0, 0, 0, None))
|
vehicle.DeploymentState = DriveState.Mobile
|
||||||
|
sendResponse(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -82,19 +82,16 @@ object Deployables {
|
||||||
val zone = target.Zone
|
val zone = target.Zone
|
||||||
val events = zone.LocalEvents
|
val events = zone.LocalEvents
|
||||||
val item = target.Definition.Item
|
val item = target.Definition.Item
|
||||||
target.OwnerName match {
|
target.OwnerName
|
||||||
case Some(owner) =>
|
.foreach { owner =>
|
||||||
zone.Players.find { p => owner.equals(p.name) } match {
|
zone.LivePlayers.find { p => owner.equals(p.Name) }
|
||||||
case Some(p) =>
|
.orElse(zone.Corpses.find { c => owner.equals(c.Name) })
|
||||||
if (p.deployables.Remove(target)) {
|
.foreach { p =>
|
||||||
events ! LocalServiceMessage(owner, LocalAction.DeployableUIFor(item))
|
p.Actor ! Player.LoseDeployable(target)
|
||||||
}
|
}
|
||||||
case None => ;
|
|
||||||
}
|
|
||||||
target.Owner = None
|
target.Owner = None
|
||||||
target.OwnerName = None
|
target.OwnerName = None
|
||||||
case None => ;
|
}
|
||||||
}
|
|
||||||
events ! LocalServiceMessage(
|
events ! LocalServiceMessage(
|
||||||
s"${target.Faction}",
|
s"${target.Faction}",
|
||||||
LocalAction.DeployableMapIcon(
|
LocalAction.DeployableMapIcon(
|
||||||
|
|
@ -114,6 +111,7 @@ object Deployables {
|
||||||
* @return all previously-owned deployables after they have been processed;
|
* @return all previously-owned deployables after they have been processed;
|
||||||
* boomers are listed before all other deployable types
|
* boomers are listed before all other deployable types
|
||||||
*/
|
*/
|
||||||
|
//noinspection ScalaUnusedSymbol
|
||||||
def Disown(zone: Zone, avatar: Avatar, replyTo: ActorRef): List[Deployable] = {
|
def Disown(zone: Zone, avatar: Avatar, replyTo: ActorRef): List[Deployable] = {
|
||||||
avatar.deployables
|
avatar.deployables
|
||||||
.Clear()
|
.Clear()
|
||||||
|
|
|
||||||
|
|
@ -5411,6 +5411,7 @@ object GlobalDefinitions {
|
||||||
dynomite.Tile = InventoryTile.Tile22
|
dynomite.Tile = InventoryTile.Tile22
|
||||||
|
|
||||||
trhev_dualcycler.Name = "trhev_dualcycler"
|
trhev_dualcycler.Name = "trhev_dualcycler"
|
||||||
|
trhev_dualcycler.Descriptor = "trhev_antipersonnel"
|
||||||
trhev_dualcycler.Size = EquipmentSize.Max
|
trhev_dualcycler.Size = EquipmentSize.Max
|
||||||
trhev_dualcycler.AmmoTypes += dualcycler_ammo
|
trhev_dualcycler.AmmoTypes += dualcycler_ammo
|
||||||
trhev_dualcycler.ProjectileTypes += dualcycler_projectile
|
trhev_dualcycler.ProjectileTypes += dualcycler_projectile
|
||||||
|
|
@ -5428,6 +5429,7 @@ object GlobalDefinitions {
|
||||||
trhev_dualcycler.FireModes(2).Magazine = 200
|
trhev_dualcycler.FireModes(2).Magazine = 200
|
||||||
|
|
||||||
trhev_pounder.Name = "trhev_pounder"
|
trhev_pounder.Name = "trhev_pounder"
|
||||||
|
trhev_pounder.Descriptor = "trhev_antivehicular"
|
||||||
trhev_pounder.Size = EquipmentSize.Max
|
trhev_pounder.Size = EquipmentSize.Max
|
||||||
trhev_pounder.AmmoTypes += pounder_ammo
|
trhev_pounder.AmmoTypes += pounder_ammo
|
||||||
trhev_pounder.ProjectileTypes += pounder_projectile
|
trhev_pounder.ProjectileTypes += pounder_projectile
|
||||||
|
|
@ -5461,6 +5463,7 @@ object GlobalDefinitions {
|
||||||
trhev_pounder.FireModes(5).Magazine = 30
|
trhev_pounder.FireModes(5).Magazine = 30
|
||||||
|
|
||||||
trhev_burster.Name = "trhev_burster"
|
trhev_burster.Name = "trhev_burster"
|
||||||
|
trhev_burster.Descriptor = "trhev_antiaircraft"
|
||||||
trhev_burster.Size = EquipmentSize.Max
|
trhev_burster.Size = EquipmentSize.Max
|
||||||
trhev_burster.AmmoTypes += burster_ammo
|
trhev_burster.AmmoTypes += burster_ammo
|
||||||
trhev_burster.ProjectileTypes += burster_projectile
|
trhev_burster.ProjectileTypes += burster_projectile
|
||||||
|
|
@ -5470,6 +5473,7 @@ object GlobalDefinitions {
|
||||||
trhev_burster.FireModes.head.Magazine = 40
|
trhev_burster.FireModes.head.Magazine = 40
|
||||||
|
|
||||||
nchev_scattercannon.Name = "nchev_scattercannon"
|
nchev_scattercannon.Name = "nchev_scattercannon"
|
||||||
|
nchev_scattercannon.Descriptor = "nchev_antipersonnel"
|
||||||
nchev_scattercannon.Size = EquipmentSize.Max
|
nchev_scattercannon.Size = EquipmentSize.Max
|
||||||
nchev_scattercannon.AmmoTypes += scattercannon_ammo
|
nchev_scattercannon.AmmoTypes += scattercannon_ammo
|
||||||
nchev_scattercannon.ProjectileTypes += scattercannon_projectile
|
nchev_scattercannon.ProjectileTypes += scattercannon_projectile
|
||||||
|
|
@ -5490,6 +5494,7 @@ object GlobalDefinitions {
|
||||||
nchev_scattercannon.FireModes(2).Chamber = 10 //40 shells * 10 pellets = 400
|
nchev_scattercannon.FireModes(2).Chamber = 10 //40 shells * 10 pellets = 400
|
||||||
|
|
||||||
nchev_falcon.Name = "nchev_falcon"
|
nchev_falcon.Name = "nchev_falcon"
|
||||||
|
nchev_falcon.Descriptor = "nchev_antivehicular"
|
||||||
nchev_falcon.Size = EquipmentSize.Max
|
nchev_falcon.Size = EquipmentSize.Max
|
||||||
nchev_falcon.AmmoTypes += falcon_ammo
|
nchev_falcon.AmmoTypes += falcon_ammo
|
||||||
nchev_falcon.ProjectileTypes += falcon_projectile
|
nchev_falcon.ProjectileTypes += falcon_projectile
|
||||||
|
|
@ -5499,6 +5504,7 @@ object GlobalDefinitions {
|
||||||
nchev_falcon.FireModes.head.Magazine = 20
|
nchev_falcon.FireModes.head.Magazine = 20
|
||||||
|
|
||||||
nchev_sparrow.Name = "nchev_sparrow"
|
nchev_sparrow.Name = "nchev_sparrow"
|
||||||
|
nchev_sparrow.Descriptor = "nchev_antiaircraft"
|
||||||
nchev_sparrow.Size = EquipmentSize.Max
|
nchev_sparrow.Size = EquipmentSize.Max
|
||||||
nchev_sparrow.AmmoTypes += sparrow_ammo
|
nchev_sparrow.AmmoTypes += sparrow_ammo
|
||||||
nchev_sparrow.ProjectileTypes += sparrow_projectile
|
nchev_sparrow.ProjectileTypes += sparrow_projectile
|
||||||
|
|
@ -5508,6 +5514,7 @@ object GlobalDefinitions {
|
||||||
nchev_sparrow.FireModes.head.Magazine = 12
|
nchev_sparrow.FireModes.head.Magazine = 12
|
||||||
|
|
||||||
vshev_quasar.Name = "vshev_quasar"
|
vshev_quasar.Name = "vshev_quasar"
|
||||||
|
vshev_quasar.Descriptor = "vshev_antipersonnel"
|
||||||
vshev_quasar.Size = EquipmentSize.Max
|
vshev_quasar.Size = EquipmentSize.Max
|
||||||
vshev_quasar.AmmoTypes += quasar_ammo
|
vshev_quasar.AmmoTypes += quasar_ammo
|
||||||
vshev_quasar.ProjectileTypes += quasar_projectile
|
vshev_quasar.ProjectileTypes += quasar_projectile
|
||||||
|
|
@ -5523,6 +5530,7 @@ object GlobalDefinitions {
|
||||||
vshev_quasar.FireModes(1).Magazine = 120
|
vshev_quasar.FireModes(1).Magazine = 120
|
||||||
|
|
||||||
vshev_comet.Name = "vshev_comet"
|
vshev_comet.Name = "vshev_comet"
|
||||||
|
vshev_comet.Descriptor = "vshev_antivehicular"
|
||||||
vshev_comet.Size = EquipmentSize.Max
|
vshev_comet.Size = EquipmentSize.Max
|
||||||
vshev_comet.AmmoTypes += comet_ammo
|
vshev_comet.AmmoTypes += comet_ammo
|
||||||
vshev_comet.ProjectileTypes += comet_projectile
|
vshev_comet.ProjectileTypes += comet_projectile
|
||||||
|
|
@ -5532,6 +5540,7 @@ object GlobalDefinitions {
|
||||||
vshev_comet.FireModes.head.Magazine = 10
|
vshev_comet.FireModes.head.Magazine = 10
|
||||||
|
|
||||||
vshev_starfire.Name = "vshev_starfire"
|
vshev_starfire.Name = "vshev_starfire"
|
||||||
|
vshev_starfire.Descriptor = "vshev_antiaircraft"
|
||||||
vshev_starfire.Size = EquipmentSize.Max
|
vshev_starfire.Size = EquipmentSize.Max
|
||||||
vshev_starfire.AmmoTypes += starfire_ammo
|
vshev_starfire.AmmoTypes += starfire_ammo
|
||||||
vshev_starfire.ProjectileTypes += starfire_projectile
|
vshev_starfire.ProjectileTypes += starfire_projectile
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import net.psforever.objects.sourcing.PlayerSource
|
||||||
import net.psforever.objects.vital.collision.CollisionReason
|
import net.psforever.objects.vital.collision.CollisionReason
|
||||||
import net.psforever.objects.vital.environment.EnvironmentReason
|
import net.psforever.objects.vital.environment.EnvironmentReason
|
||||||
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason}
|
||||||
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
|
import net.psforever.objects.vital.interaction.{Adversarial, DamageInteraction, DamageResult}
|
||||||
import net.psforever.services.hart.ShuttleState
|
import net.psforever.services.hart.ShuttleState
|
||||||
import net.psforever.packet.PlanetSideGamePacket
|
import net.psforever.packet.PlanetSideGamePacket
|
||||||
|
|
||||||
|
|
@ -368,8 +368,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
|
|
||||||
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
|
case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) =>
|
||||||
log.info(s"${player.Name} wants to change equipment loadout to their option #${msg.unk1 + 1}")
|
log.info(s"${player.Name} wants to change equipment loadout to their option #${msg.unk1 + 1}")
|
||||||
val fallbackSubtype = 0
|
|
||||||
val fallbackSuit = ExoSuitType.Standard
|
|
||||||
val originalSuit = player.ExoSuit
|
val originalSuit = player.ExoSuit
|
||||||
val originalSubtype = Loadout.DetermineSubtype(player)
|
val originalSubtype = Loadout.DetermineSubtype(player)
|
||||||
//sanitize exo-suit for change
|
//sanitize exo-suit for change
|
||||||
|
|
@ -392,29 +390,39 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
}) ++ dropHolsters ++ dropInventory
|
}) ++ dropHolsters ++ dropInventory
|
||||||
//a loadout with a prohibited exo-suit type will result in the fallback exo-suit type
|
//a loadout with a prohibited exo-suit type will result in the fallback exo-suit type
|
||||||
//imposed 5min delay on mechanized exo-suit switches
|
//imposed 5min delay on mechanized exo-suit switches
|
||||||
val (nextSuit, nextSubtype) =
|
val (nextSuit, nextSubtype) = {
|
||||||
if (
|
lazy val fallbackSuit = if (Players.CertificationToUseExoSuit(player, originalSuit, originalSubtype)) {
|
||||||
Players.CertificationToUseExoSuit(player, exosuit, subtype) &&
|
//TODO will we ever need to check for the cooldown status of an original non-MAX exo-suit?
|
||||||
(if (exosuit == ExoSuitType.MAX) {
|
(originalSuit, originalSubtype)
|
||||||
player.ResistArmMotion(PlayerControl.maxRestriction)
|
} else {
|
||||||
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
|
(ExoSuitType.Standard, 0)
|
||||||
player.avatar.purchaseCooldown(weapon) match {
|
}
|
||||||
case Some(_) => false
|
if (Players.CertificationToUseExoSuit(player, exosuit, subtype)) {
|
||||||
case None =>
|
if (exosuit == ExoSuitType.MAX) {
|
||||||
|
val weapon = GlobalDefinitions.MAXArms(subtype, player.Faction)
|
||||||
|
val cooldown = player.avatar.purchaseCooldown(weapon)
|
||||||
|
if (originalSubtype == subtype) {
|
||||||
|
(exosuit, subtype) //same MAX subtype is free
|
||||||
|
} else if (cooldown.nonEmpty) {
|
||||||
|
fallbackSuit //different MAX subtype can not have cooldown
|
||||||
|
} else {
|
||||||
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
|
avatarActor ! AvatarActor.UpdatePurchaseTime(weapon)
|
||||||
true
|
(exosuit, subtype) //switching for first time causes cooldown
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(exosuit, subtype)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.ResistArmMotion(Player.neverRestrict)
|
log.warn(
|
||||||
true
|
s"${player.Name} no longer has permission to wear the exo-suit type $exosuit; will wear ${fallbackSuit._1} instead"
|
||||||
})
|
)
|
||||||
) {
|
fallbackSuit
|
||||||
(exosuit, subtype)
|
}
|
||||||
|
}
|
||||||
|
if (nextSuit == ExoSuitType.MAX) {
|
||||||
|
player.ResistArmMotion(PlayerControl.maxRestriction)
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
player.ResistArmMotion(Player.neverRestrict)
|
||||||
s"${player.Name} no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead"
|
|
||||||
)
|
|
||||||
(fallbackSuit, fallbackSubtype)
|
|
||||||
}
|
}
|
||||||
//sanitize (incoming) inventory
|
//sanitize (incoming) inventory
|
||||||
//TODO equipment permissions; these loops may be expanded upon in future
|
//TODO equipment permissions; these loops may be expanded upon in future
|
||||||
|
|
@ -802,7 +810,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
//choose
|
//choose
|
||||||
if (target.Health > 0) {
|
if (target.Health > 0) {
|
||||||
//alive
|
//alive
|
||||||
if (target.Health <= 25 && target.Health + damageToHealth > 25 &&
|
if (target.Health <= 25 &&
|
||||||
(player.avatar.implants.flatten.find { _.definition.implantType == ImplantType.SecondWind } match {
|
(player.avatar.implants.flatten.find { _.definition.implantType == ImplantType.SecondWind } match {
|
||||||
case Some(wind) => wind.initialized
|
case Some(wind) => wind.initialized
|
||||||
case _ => false
|
case _ => false
|
||||||
|
|
@ -810,8 +818,8 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
//activate second wind
|
//activate second wind
|
||||||
player.Health += 25
|
player.Health += 25
|
||||||
player.LogActivity(HealFromImplant(ImplantType.SecondWind, 25))
|
player.LogActivity(HealFromImplant(ImplantType.SecondWind, 25))
|
||||||
avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind)
|
|
||||||
avatarActor ! AvatarActor.RestoreStamina(25)
|
avatarActor ! AvatarActor.RestoreStamina(25)
|
||||||
|
avatarActor ! AvatarActor.ResetImplant(ImplantType.SecondWind)
|
||||||
}
|
}
|
||||||
//take damage/update
|
//take damage/update
|
||||||
DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
DamageAwareness(target, cause, damageToHealth, damageToArmor, damageToStamina, damageToCapacitor)
|
||||||
|
|
@ -1025,21 +1033,23 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
)
|
)
|
||||||
//TODO other methods of death?
|
//TODO other methods of death?
|
||||||
val pentry = PlayerSource(target)
|
val pentry = PlayerSource(target)
|
||||||
(cause.adversarial match {
|
cause
|
||||||
case out @ Some(_) =>
|
.adversarial
|
||||||
out
|
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
|
||||||
case _ =>
|
.orElse {
|
||||||
target.LastDamage.collect {
|
target.LastDamage.collect {
|
||||||
case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis =>
|
case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis =>
|
||||||
attack.adversarial
|
attack
|
||||||
|
.adversarial
|
||||||
|
.collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out }
|
||||||
}.flatten
|
}.flatten
|
||||||
}) match {
|
} match {
|
||||||
case Some(adversarial) =>
|
case Some(adversarial) =>
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
zoneChannel,
|
zoneChannel,
|
||||||
AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement)
|
AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement)
|
||||||
)
|
)
|
||||||
case None =>
|
case _ =>
|
||||||
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
|
events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1288,6 +1298,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
suicide()
|
suicide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection ScalaUnusedSymbol
|
||||||
def doInteractingWithGantryField(
|
def doInteractingWithGantryField(
|
||||||
obj: PlanetSideServerObject,
|
obj: PlanetSideServerObject,
|
||||||
body: PieceOfEnvironment,
|
body: PieceOfEnvironment,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2018 PSForever
|
// Copyright (c) 2018 PSForever
|
||||||
package net.psforever.objects.ce
|
package net.psforever.objects.ce
|
||||||
|
|
||||||
import akka.actor.{ActorContext, Cancellable}
|
import akka.actor.ActorContext
|
||||||
import net.psforever.objects.{Default, TelepadDeployable, Vehicle}
|
import net.psforever.objects.{Default, TelepadDeployable, Vehicle}
|
||||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||||
import net.psforever.objects.serverobject.structures.Amenity
|
import net.psforever.objects.serverobject.structures.Amenity
|
||||||
|
|
@ -147,6 +147,12 @@ object TelepadLike {
|
||||||
LocalAction.SendResponse(GenericObjectActionMessage(telepadGUID, 28))
|
LocalAction.SendResponse(GenericObjectActionMessage(telepadGUID, 28))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def InitializeTelepadDeployable(zone: Zone, internal: InternalTelepad, pad: TelepadDeployable): Unit = {
|
||||||
|
internal.Telepad = pad.GUID
|
||||||
|
TelepadLike.StartRouterInternalTelepad(zone, internal.Owner.GUID, internal)
|
||||||
|
pad.Actor ! TelepadLike.Activate(internal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,8 +163,6 @@ object TelepadLike {
|
||||||
* @param obj an entity that extends `TelepadLike`
|
* @param obj an entity that extends `TelepadLike`
|
||||||
*/
|
*/
|
||||||
class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor {
|
class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor {
|
||||||
var setup: Cancellable = Default.Cancellable
|
|
||||||
|
|
||||||
def receive: akka.actor.Actor.Receive = {
|
def receive: akka.actor.Actor.Receive = {
|
||||||
case TelepadLike.Activate(o: InternalTelepad) if obj eq o =>
|
case TelepadLike.Activate(o: InternalTelepad) if obj eq o =>
|
||||||
obj.Active = true
|
obj.Active = true
|
||||||
|
|
@ -166,11 +170,9 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor {
|
||||||
case TelepadLike.Deactivate(o: InternalTelepad) if obj eq o =>
|
case TelepadLike.Deactivate(o: InternalTelepad) if obj eq o =>
|
||||||
obj.Active = false
|
obj.Active = false
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
zone.GUID(obj.Telepad) match {
|
zone.GUID(obj.Telepad).collect {
|
||||||
case Some(oldTpad: TelepadDeployable)
|
case oldTpad: TelepadDeployable if oldTpad.Active =>
|
||||||
if !obj.Active && !setup.isCancelled =>
|
|
||||||
oldTpad.Actor ! TelepadLike.SeverLink(obj)
|
oldTpad.Actor ! TelepadLike.SeverLink(obj)
|
||||||
case _ => ;
|
|
||||||
}
|
}
|
||||||
obj.Telepad = None
|
obj.Telepad = None
|
||||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0)))
|
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0)))
|
||||||
|
|
@ -178,15 +180,20 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor {
|
||||||
case TelepadLike.RequestLink(tpad: TelepadDeployable) =>
|
case TelepadLike.RequestLink(tpad: TelepadDeployable) =>
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
if (obj.Active) {
|
if (obj.Active) {
|
||||||
zone.GUID(obj.Telepad) match {
|
zone
|
||||||
case Some(oldTpad: TelepadDeployable) if !obj.Active && !setup.isCancelled =>
|
.GUID(obj.Telepad)
|
||||||
oldTpad.Actor ! TelepadLike.SeverLink(obj)
|
.collect {
|
||||||
case _ => ;
|
case oldTpad: TelepadDeployable if oldTpad eq tpad =>
|
||||||
}
|
Some(oldTpad)
|
||||||
obj.Telepad = tpad.GUID
|
case oldTpad: TelepadDeployable if oldTpad.Active =>
|
||||||
//zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.StartRouterInternalTelepad(obj.Owner.GUID, obj.GUID, obj))
|
oldTpad.Actor ! TelepadLike.SeverLink(obj)
|
||||||
TelepadLike.StartRouterInternalTelepad(zone, obj.Owner.GUID, obj)
|
TelepadLike.InitializeTelepadDeployable(zone, obj, tpad)
|
||||||
tpad.Actor ! TelepadLike.Activate(obj)
|
Some(oldTpad)
|
||||||
|
}
|
||||||
|
.orElse {
|
||||||
|
TelepadLike.InitializeTelepadDeployable(zone, obj, tpad)
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val channel = obj.Owner.asInstanceOf[Vehicle].OwnerName.getOrElse("")
|
val channel = obj.Owner.asInstanceOf[Vehicle].OwnerName.getOrElse("")
|
||||||
zone.LocalEvents ! LocalServiceMessage(channel, LocalAction.RouterTelepadMessage("@Teleport_NotDeployed"))
|
zone.LocalEvents ! LocalServiceMessage(channel, LocalAction.RouterTelepadMessage("@Teleport_NotDeployed"))
|
||||||
|
|
@ -200,6 +207,6 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor {
|
||||||
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0)))
|
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
health,
|
health,
|
||||||
unk4 = false,
|
unk4 = false,
|
||||||
no_mount_points = false,
|
no_mount_points = false,
|
||||||
obj.DeploymentState,
|
SterilizedDeploymentState(obj),
|
||||||
unk5 = false,
|
unk5 = false,
|
||||||
unk6 = false,
|
unk6 = false,
|
||||||
obj.Cloaked,
|
obj.Cloaked,
|
||||||
|
|
@ -105,6 +105,13 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
.toList
|
.toList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def SterilizedDeploymentState(obj: Vehicle): DriveState.Value = {
|
||||||
|
obj.DeploymentState match {
|
||||||
|
case state if state.id < 0 => DriveState.Mobile
|
||||||
|
case state => state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Normal
|
protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Normal
|
||||||
|
|
||||||
protected def SpecificFormatData(obj: Vehicle): Option[SpecificVehicleData] = None
|
protected def SpecificFormatData(obj: Vehicle): Option[SpecificVehicleData] = None
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,9 @@ object AmenitySource {
|
||||||
)
|
)
|
||||||
amenity.copy(occupants = obj match {
|
amenity.copy(occupants = obj match {
|
||||||
case o: Mountable =>
|
case o: Mountable =>
|
||||||
o.Seats.values.flatMap { _.occupants }.map { p => PlayerSource.inSeat(p, o, amenity) }.toList
|
o.Seats
|
||||||
|
.collect { case (num, seat) if seat.isOccupied => (num, seat.occupants.head) }
|
||||||
|
.map { case (num, p) => PlayerSource.inSeat(p, amenity, num) }.toList
|
||||||
case _ =>
|
case _ =>
|
||||||
Nil
|
Nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -109,17 +109,17 @@ object PlayerSource {
|
||||||
* even if this function is entirely for the purpose of establishing that the player is an occupant of the mountable entity.<br>
|
* even if this function is entirely for the purpose of establishing that the player is an occupant of the mountable entity.<br>
|
||||||
* Don't think too much about it.
|
* Don't think too much about it.
|
||||||
* @param player player
|
* @param player player
|
||||||
* @param mount mountable entity in which the player should be seated
|
|
||||||
* @param source a `SourceEntry` for the aforementioned mountable entity
|
* @param source a `SourceEntry` for the aforementioned mountable entity
|
||||||
|
* @param seatNumber the attributed seating index in which the player is mounted in `source`
|
||||||
* @return a `PlayerSource` entity
|
* @return a `PlayerSource` entity
|
||||||
*/
|
*/
|
||||||
def inSeat(player: Player, mount: Mountable, source: SourceEntry): PlayerSource = {
|
def inSeat(player: Player, source: SourceEntry, seatNumber: Int): PlayerSource = {
|
||||||
val exosuit = player.ExoSuit
|
val exosuit = player.ExoSuit
|
||||||
val faction = player.Faction
|
val faction = player.Faction
|
||||||
PlayerSource(
|
PlayerSource(
|
||||||
player.Definition,
|
player.Definition,
|
||||||
exosuit,
|
exosuit,
|
||||||
Some((source, mount.PassengerInSeat(player).get)),
|
Some((source, seatNumber)),
|
||||||
player.Health,
|
player.Health,
|
||||||
player.Armor,
|
player.Armor,
|
||||||
player.Position,
|
player.Position,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,9 @@ object TurretSource {
|
||||||
)
|
)
|
||||||
turret.copy(occupants = obj match {
|
turret.copy(occupants = obj match {
|
||||||
case o: Mountable =>
|
case o: Mountable =>
|
||||||
o.Seats.values.flatMap { _.occupants }.map { p => PlayerSource.inSeat(p, o, turret) }.toList
|
o.Seats
|
||||||
|
.collect { case (num, seat) if seat.isOccupied => (num, seat.occupants.head) }
|
||||||
|
.map { case (num, p) => PlayerSource.inSeat(p, turret, num) }.toList
|
||||||
case _ =>
|
case _ =>
|
||||||
Nil
|
Nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ object VehicleSource {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
vehicle.copy(occupants = {
|
vehicle.copy(occupants = {
|
||||||
obj.Seats.values.map { seat =>
|
obj.Seats.map { case (seatNumber, seat) =>
|
||||||
seat.occupant match {
|
seat.occupant match {
|
||||||
case Some(p) => PlayerSource.inSeat(p, obj, vehicle) //shallow
|
case Some(p) => PlayerSource.inSeat(p, vehicle, seatNumber) //shallow
|
||||||
case _ => PlayerSource.Nobody
|
case _ => PlayerSource.Nobody
|
||||||
}
|
}
|
||||||
}.toList
|
}.toList
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior {
|
||||||
|
|
||||||
def UpdateNtuUI(vehicle: Vehicle with NtuContainer): Unit = {
|
def UpdateNtuUI(vehicle: Vehicle with NtuContainer): Unit = {
|
||||||
if (vehicle.Seats.values.exists(_.isOccupied)) {
|
if (vehicle.Seats.values.exists(_.isOccupied)) {
|
||||||
val display = scala.math.ceil(vehicle.NtuCapacitorScaled).toLong
|
val display = vehicle.NtuCapacitorScaled.toLong
|
||||||
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||||
vehicle.Actor.toString,
|
vehicle.Actor.toString,
|
||||||
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 45, display)
|
VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 45, display)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import scala.concurrent.duration._
|
||||||
class AntControl(vehicle: Vehicle)
|
class AntControl(vehicle: Vehicle)
|
||||||
extends DeployingVehicleControl(vehicle)
|
extends DeployingVehicleControl(vehicle)
|
||||||
with AntTransferBehavior {
|
with AntTransferBehavior {
|
||||||
def ChargeTransferObject = vehicle
|
def ChargeTransferObject: Vehicle = vehicle
|
||||||
|
|
||||||
override def commonEnabledBehavior: Receive = super.commonEnabledBehavior.orElse(antBehavior)
|
override def commonEnabledBehavior: Receive = super.commonEnabledBehavior.orElse(antBehavior)
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ class AntControl(vehicle: Vehicle)
|
||||||
vehicle.Actor,
|
vehicle.Actor,
|
||||||
TransferBehavior.Charging(Ntu.Nanites)
|
TransferBehavior.Charging(Ntu.Nanites)
|
||||||
)
|
)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ class AntControl(vehicle: Vehicle)
|
||||||
state match {
|
state match {
|
||||||
case DriveState.Undeploying =>
|
case DriveState.Undeploying =>
|
||||||
TryStopChargingEvent(vehicle)
|
TryStopChargingEvent(vehicle)
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import net.psforever.types._
|
||||||
class DeployingVehicleControl(vehicle: Vehicle)
|
class DeployingVehicleControl(vehicle: Vehicle)
|
||||||
extends VehicleControl(vehicle)
|
extends VehicleControl(vehicle)
|
||||||
with DeploymentBehavior {
|
with DeploymentBehavior {
|
||||||
def DeploymentObject = vehicle
|
def DeploymentObject: Vehicle = vehicle
|
||||||
|
|
||||||
override def commonEnabledBehavior : Receive = super.commonEnabledBehavior.orElse(deployBehavior)
|
override def commonEnabledBehavior : Receive = super.commonEnabledBehavior.orElse(deployBehavior)
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
||||||
override def commonDeleteBehavior : Receive =
|
override def commonDeleteBehavior : Receive =
|
||||||
super.commonDeleteBehavior
|
super.commonDeleteBehavior
|
||||||
.orElse {
|
.orElse {
|
||||||
case msg : Deployment.TryUndeploy =>
|
case msg: Deployment.TryUndeploy =>
|
||||||
deployBehavior.apply(msg)
|
deployBehavior.apply(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ class DeployingVehicleControl(vehicle: Vehicle)
|
||||||
* Even when disabled, the vehicle can be made to undeploy.
|
* Even when disabled, the vehicle can be made to undeploy.
|
||||||
*/
|
*/
|
||||||
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
override def PrepareForDisabled(kickPassengers: Boolean) : Unit = {
|
||||||
vehicle.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
|
TryUndeployStateChange(DriveState.Undeploying)
|
||||||
super.PrepareForDisabled(kickPassengers)
|
super.PrepareForDisabled(kickPassengers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,10 @@ class RouterControl(vehicle: Vehicle)
|
||||||
override def specificResponseToDeployment(state: DriveState.Value): Unit = {
|
override def specificResponseToDeployment(state: DriveState.Value): Unit = {
|
||||||
state match {
|
state match {
|
||||||
case DriveState.Deployed =>
|
case DriveState.Deployed =>
|
||||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
vehicle.Utility(UtilityType.internal_router_telepad_deployable).collect {
|
||||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Activate(util)
|
case util: Utility.InternalTelepad => util.Actor ! TelepadLike.Activate(util)
|
||||||
case _ => ;
|
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,11 +39,10 @@ class RouterControl(vehicle: Vehicle)
|
||||||
override def specificResponseToUndeployment(state: DriveState.Value): Unit = {
|
override def specificResponseToUndeployment(state: DriveState.Value): Unit = {
|
||||||
state match {
|
state match {
|
||||||
case DriveState.Undeploying =>
|
case DriveState.Undeploying =>
|
||||||
vehicle.Utility(UtilityType.internal_router_telepad_deployable) match {
|
vehicle.Utility(UtilityType.internal_router_telepad_deployable).collect {
|
||||||
case Some(util: Utility.InternalTelepad) => util.Actor ! TelepadLike.Deactivate(util)
|
case util: Utility.InternalTelepad => util.Actor ! TelepadLike.Deactivate(util)
|
||||||
case _ => ;
|
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import net.psforever.objects.inventory.{GridInventory, InventoryItem}
|
||||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObjectControl}
|
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObjectControl}
|
||||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||||
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior}
|
||||||
|
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||||
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
import net.psforever.objects.serverobject.damage.{AggravatedBehavior, DamageableVehicle}
|
||||||
import net.psforever.objects.serverobject.environment._
|
import net.psforever.objects.serverobject.environment._
|
||||||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||||
|
|
@ -120,6 +121,9 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
case Vehicle.Ownership(Some(player)) =>
|
case Vehicle.Ownership(Some(player)) =>
|
||||||
GainOwnership(player)
|
GainOwnership(player)
|
||||||
|
|
||||||
|
case Mountable.TryMount(user, mountPoint) if vehicle.DeploymentState == DriveState.AutoPilot =>
|
||||||
|
sender() ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, mountPoint))
|
||||||
|
|
||||||
case msg @ Mountable.TryMount(player, mount_point) =>
|
case msg @ Mountable.TryMount(player, mount_point) =>
|
||||||
mountBehavior.apply(msg)
|
mountBehavior.apply(msg)
|
||||||
mountCleanup(mount_point, player)
|
mountCleanup(mount_point, player)
|
||||||
|
|
@ -864,10 +868,15 @@ class VehicleControl(vehicle: Vehicle)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override protected def DestructionAwareness(target: Target, cause: DamageResult): Unit = {
|
||||||
|
passengerRadiationCloudTimer.cancel()
|
||||||
|
super.DestructionAwareness(target, cause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object VehicleControl {
|
object VehicleControl {
|
||||||
import net.psforever.objects.vital.{ShieldCharge}
|
import net.psforever.objects.vital.ShieldCharge
|
||||||
|
|
||||||
private case class PrepareForDeletion()
|
private case class PrepareForDeletion()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ import scodec.codecs._
|
||||||
* For flight vehicles, for `n`, the forward air speed for the value in this packet will be at least `1.18 * n`.
|
* For flight vehicles, for `n`, the forward air speed for the value in this packet will be at least `1.18 * n`.
|
||||||
* This approximation is not always going to be accurate but serves as a good rule of thumb.
|
* This approximation is not always going to be accurate but serves as a good rule of thumb.
|
||||||
* @param lock_accelerator driver has no control over vehicle acceleration
|
* @param lock_accelerator driver has no control over vehicle acceleration
|
||||||
* @param lock_wheel driver has no control over vehicle turning
|
* @param lock_wheel driver has no control over vehicle turning;
|
||||||
|
* generally, the driver never has turning control
|
||||||
* @param reverse move in reverse
|
* @param reverse move in reverse
|
||||||
* 0 = forward
|
* 0 = forward
|
||||||
* 1 = reverse
|
* 1 = reverse
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,7 @@ object DriveState extends Enumeration {
|
||||||
val State7 = Value(7) //unknown; not encountered on a vehicle that can deploy; functions like Mobile
|
val State7 = Value(7) //unknown; not encountered on a vehicle that can deploy; functions like Mobile
|
||||||
val State127 = Value(127) //unknown
|
val State127 = Value(127) //unknown
|
||||||
|
|
||||||
val Kneeling = Value(-1) //flag bfr kneeling state; should not not encode
|
//the following values should never be encoded
|
||||||
|
val Kneeling = Value(-1) //flag bfr kneeling state
|
||||||
|
val AutoPilot = Value(-2) //when emerging from spawn pad, or being kicked from a ferry, during server guidance
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ case class GameConfig(
|
||||||
newAvatar: NewAvatar,
|
newAvatar: NewAvatar,
|
||||||
hart: HartConfig,
|
hart: HartConfig,
|
||||||
sharedMaxCooldown: Boolean,
|
sharedMaxCooldown: Boolean,
|
||||||
|
sharedBfrCooldown: Boolean,
|
||||||
baseCertifications: Seq[Certification],
|
baseCertifications: Seq[Certification],
|
||||||
warpGates: WarpGateConfig,
|
warpGates: WarpGateConfig,
|
||||||
cavernRotation: CavernRotationConfig,
|
cavernRotation: CavernRotationConfig,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import org.specs2.mutable.Specification
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import net.psforever.objects.avatar.Avatar
|
import net.psforever.objects.avatar.Avatar
|
||||||
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalMech, ImplantTerminalMechControl}
|
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.base.DamageResolution
|
import net.psforever.objects.vital.base.DamageResolution
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
|
|
@ -327,21 +327,17 @@ class DamageableEntityDamageTest extends ActorTest {
|
||||||
gen.Actor ! Vitality.Damage(applyDamageTo)
|
gen.Actor ! Vitality.Damage(applyDamageTo)
|
||||||
val msg1 = avatarProbe.receiveOne(500 milliseconds)
|
val msg1 = avatarProbe.receiveOne(500 milliseconds)
|
||||||
val msg2 = activityProbe.receiveOne(500 milliseconds)
|
val msg2 = activityProbe.receiveOne(500 milliseconds)
|
||||||
assert(
|
msg1 match {
|
||||||
msg1 match {
|
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(ValidPlanetSideGUID(2), 0, 3600)) => ()
|
||||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
|
case _ => assert(false, "DamageableEntity:handle taking damage - player not messaged")
|
||||||
case _ => false
|
}
|
||||||
}
|
msg2 match {
|
||||||
)
|
case activity: Zone.HotSpot.Activity
|
||||||
assert(
|
if activity.attacker == PlayerSource(player1) &&
|
||||||
msg2 match {
|
activity.defender == SourceEntry(gen) &&
|
||||||
case activity: Zone.HotSpot.Activity =>
|
activity.location == Vector3(1, 0, 0) => ()
|
||||||
activity.attacker == PlayerSource(player1) &&
|
case _ => assert(false, "DamageableEntity:handle taking damage - activity not messaged")
|
||||||
activity.defender == SourceEntry(gen) &&
|
}
|
||||||
activity.location == Vector3(1, 0, 0)
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -320,15 +320,12 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest {
|
||||||
jmine.Actor ! Deployable.Deconstruct()
|
jmine.Actor ! Deployable.Deconstruct()
|
||||||
val eventsMsgs = eventsProbe.receiveN(3, 10.seconds)
|
val eventsMsgs = eventsProbe.receiveN(3, 10.seconds)
|
||||||
eventsMsgs.head match {
|
eventsMsgs.head match {
|
||||||
case LocalServiceMessage("test", LocalAction.EliminateDeployable(`jmine`, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ;
|
case LocalServiceMessage("test",LocalAction.EliminateDeployable(mine, ValidPlanetSideGUID(1), Vector3(1.0,2.0,3.0),2))
|
||||||
|
if mine eq jmine => ;
|
||||||
case _ => assert(false, "owned deconstruct test - not eliminating deployable")
|
case _ => assert(false, "owned deconstruct test - not eliminating deployable")
|
||||||
}
|
}
|
||||||
eventsMsgs(1) match {
|
eventsMsgs(1) match {
|
||||||
case LocalServiceMessage("TestCharacter1", LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ;
|
case LocalServiceMessage(
|
||||||
case _ => assert(false, "")
|
|
||||||
}
|
|
||||||
eventsMsgs(2) match {
|
|
||||||
case LocalServiceMessage(
|
|
||||||
"TR",
|
"TR",
|
||||||
LocalAction.DeployableMapIcon(
|
LocalAction.DeployableMapIcon(
|
||||||
PlanetSideGUID(0),
|
PlanetSideGUID(0),
|
||||||
|
|
@ -338,6 +335,10 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest {
|
||||||
) => ;
|
) => ;
|
||||||
case _ => assert(false, "owned deconstruct test - not removing icon")
|
case _ => assert(false, "owned deconstruct test - not removing icon")
|
||||||
}
|
}
|
||||||
|
eventsMsgs(2) match {
|
||||||
|
case LocalServiceMessage("TestCharacter1", LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ;
|
||||||
|
case _ => assert(false, "")
|
||||||
|
}
|
||||||
|
|
||||||
assert(deployableList.isEmpty, "owned deconstruct test - deployable still in list")
|
assert(deployableList.isEmpty, "owned deconstruct test - deployable still in list")
|
||||||
assert(!avatar.deployables.Contains(jmine), "owned deconstruct test - avatar still owns deployable")
|
assert(!avatar.deployables.Contains(jmine), "owned deconstruct test - avatar still owns deployable")
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import net.psforever.objects.vital.interaction.DamageInteraction
|
||||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||||
|
import net.psforever.services.local.LocalAction.DeployableMapIcon
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -447,9 +448,9 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
||||||
assert(!h_mine.Destroyed)
|
assert(!h_mine.Destroyed)
|
||||||
|
|
||||||
h_mine.Actor ! Vitality.Damage(applyDamageToH)
|
h_mine.Actor ! Vitality.Damage(applyDamageToH)
|
||||||
val eventMsgs = eventsProbe.receiveN(5, 200 milliseconds)
|
val eventMsgs = eventsProbe.receiveN(4, 200 milliseconds)
|
||||||
val p1Msgs = player1Probe.receiveN(1, 200 milliseconds)
|
val p1Msgs = player1Probe.receiveN(1, 200 milliseconds)
|
||||||
player2Probe.expectNoMessage(200 milliseconds)
|
val p2Msgs = player2Probe.receiveN(1, 200 milliseconds)
|
||||||
eventMsgs.head match {
|
eventMsgs.head match {
|
||||||
case Zone.HotSpot.Conflict(target, attacker, _)
|
case Zone.HotSpot.Conflict(target, attacker, _)
|
||||||
if (target.Definition eq h_mine.Definition) && (attacker eq pSource) => ;
|
if (target.Definition eq h_mine.Definition) && (attacker eq pSource) => ;
|
||||||
|
|
@ -461,10 +462,6 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
eventMsgs(2) match {
|
eventMsgs(2) match {
|
||||||
case LocalServiceMessage("TestCharacter2", LocalAction.DeployableUIFor(DeployedItem.he_mine)) => ;
|
|
||||||
case _ => assert(false, "")
|
|
||||||
}
|
|
||||||
eventMsgs(3) match {
|
|
||||||
case LocalServiceMessage(
|
case LocalServiceMessage(
|
||||||
"NC",
|
"NC",
|
||||||
LocalAction.DeployableMapIcon(
|
LocalAction.DeployableMapIcon(
|
||||||
|
|
@ -475,7 +472,7 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
||||||
) => ;
|
) => ;
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
eventMsgs(4) match {
|
eventMsgs(3) match {
|
||||||
case AvatarServiceMessage(
|
case AvatarServiceMessage(
|
||||||
"test",
|
"test",
|
||||||
AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero)
|
AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero)
|
||||||
|
|
@ -486,7 +483,10 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
|
||||||
case Vitality.Damage(_) => ;
|
case Vitality.Damage(_) => ;
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
assert(!avatar2.deployables.Contains(h_mine))
|
p2Msgs.head match {
|
||||||
|
case Player.LoseDeployable(_) => ;
|
||||||
|
case _ => assert(false, "")
|
||||||
|
}
|
||||||
assert(h_mine.Destroyed)
|
assert(h_mine.Destroyed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -558,14 +558,10 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
|
||||||
assert(!h_mine.Destroyed)
|
assert(!h_mine.Destroyed)
|
||||||
|
|
||||||
h_mine.Actor ! Vitality.Damage(applyDamageTo)
|
h_mine.Actor ! Vitality.Damage(applyDamageTo)
|
||||||
val eventMsgs = eventsProbe.receiveN(4, 200 milliseconds)
|
val eventMsgs = eventsProbe.receiveN(3, 200 milliseconds)
|
||||||
player1Probe.expectNoMessage(200 milliseconds)
|
player1Probe.expectNoMessage(200 milliseconds)
|
||||||
player2Probe.expectNoMessage(200 milliseconds)
|
val p2Msgs = player2Probe.receiveN(1, 200 milliseconds)
|
||||||
eventMsgs.head match {
|
eventMsgs.head match {
|
||||||
case LocalServiceMessage("TestCharacter2", LocalAction.DeployableUIFor(DeployedItem.he_mine)) => ;
|
|
||||||
case _ => assert(false, "")
|
|
||||||
}
|
|
||||||
eventMsgs(1) match {
|
|
||||||
case LocalServiceMessage(
|
case LocalServiceMessage(
|
||||||
"NC",
|
"NC",
|
||||||
LocalAction.DeployableMapIcon(
|
LocalAction.DeployableMapIcon(
|
||||||
|
|
@ -576,17 +572,21 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
|
||||||
) => ;
|
) => ;
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
eventMsgs(2) match {
|
eventMsgs(1) match {
|
||||||
case AvatarServiceMessage(
|
case AvatarServiceMessage(
|
||||||
"test",
|
"test",
|
||||||
AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero)
|
AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero)
|
||||||
) => ;
|
) => ;
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
eventMsgs(3) match {
|
eventMsgs(2) match {
|
||||||
case LocalServiceMessage("test", LocalAction.TriggerEffect(_, "detonate_damaged_mine", PlanetSideGUID(2))) => ;
|
case LocalServiceMessage("test", LocalAction.TriggerEffect(_, "detonate_damaged_mine", PlanetSideGUID(2))) => ;
|
||||||
case _ => assert(false, "")
|
case _ => assert(false, "")
|
||||||
}
|
}
|
||||||
|
p2Msgs.head match {
|
||||||
|
case Player.LoseDeployable(_) => ;
|
||||||
|
case _ => assert(false, "")
|
||||||
|
}
|
||||||
assert(h_mine.Health <= h_mine.Definition.DamageDestroysAt)
|
assert(h_mine.Health <= h_mine.Definition.DamageDestroysAt)
|
||||||
assert(h_mine.Destroyed)
|
assert(h_mine.Destroyed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue