diff --git a/server/src/main/resources/db/migration/V014__ImplantInit.sql b/server/src/main/resources/db/migration/V014__ImplantInit.sql
new file mode 100644
index 000000000..e972e2f9d
--- /dev/null
+++ b/server/src/main/resources/db/migration/V014__ImplantInit.sql
@@ -0,0 +1,3 @@
+/* New */
+ALTER TABLE implant
+ADD COLUMN timer SMALLINT NOT NULL DEFAULT 0;
diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
index b27f882ba..9e28f7387 100644
--- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala
+++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala
@@ -11,7 +11,8 @@ import net.psforever.objects.Session
import net.psforever.objects.avatar.ModePermissions
import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity}
import net.psforever.objects.sourcing.{TurretSource, VehicleSource}
-import net.psforever.objects.vital.ReconstructionActivity
+import net.psforever.packet.game.ImplantAction
+import net.psforever.services.avatar.AvatarServiceResponse
import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement}
import org.joda.time.{LocalDateTime, Seconds}
@@ -117,9 +118,6 @@ object AvatarActor {
/** Log in the currently selected avatar. Must have first sent SelectAvatar. */
final case class LoginAvatar(replyTo: ActorRef[AvatarLoginResponse]) extends Command
- /** Send implants to client */
- final case class CreateImplants() extends Command
-
/** Replace avatar instance with the provided one */
final case class ReplaceAvatar(avatar: Avatar) extends Command
@@ -173,23 +171,23 @@ object AvatarActor {
/** Activate an implant (must already be initialized) */
final case class ActivateImplant(implantType: ImplantType) extends Command
- /** Deactivate an implant */
+ /** Deactivate an implant (must already be activated) */
final case class DeactivateImplant(implantType: ImplantType) extends Command
- /** Deactivate all non-passive implants that are in use */
- final case class DeactivateActiveImplants() extends Command
+ /** Deactivate all non-passive implants that have been activated */
+ final case object DeactivateActiveImplants extends Command
- /** Start implant initialization timers (after zoning or respawn) */
- final case class InitializeImplants() extends Command
+ /** Start all implant initialization timers (this will also hard restart all active timers) */
+ final case object InitializeImplants extends Command
- /** Deinitialize implants (before zoning or respawning) */
- final case class DeinitializeImplants() extends Command
+ /** Set all implants to deactivated and deinitialized; do not restart the initialization process */
+ final case object DeinitializeImplants extends Command
- /** Deinitialize a certain implant, then initialize it again */
+ /** Set a certain implant to deactivated and deinitialized; restart the initialization process */
final case class ResetImplant(implant: ImplantType) extends Command
- /** Shorthand for DeinitializeImplants and InitializeImplants */
- final case class ResetImplants() extends Command
+ /** Set all active non-passive implants to deactivated and restart the initialization process all un-initialized implants */
+ final case object SoftResetImplants extends Command
/** Set the avatar's lookingForSquad */
final case class SetLookingForSquad(lfs: Boolean) extends Command
@@ -234,7 +232,7 @@ object AvatarActor {
final case class SetStamina(stamina: Int) extends Command
- final case class SetImplantInitialized(implantType: ImplantType) extends Command
+ private case class SetImplantInitialized(implantType: ImplantType) extends Command
final case class MemberListRequest(action: MemberAction.Value, name: String) extends Command
@@ -839,6 +837,24 @@ object AvatarActor {
_.useCooldowns -> lift(buildClobfromCooldowns(avatar.cooldowns.use))
)
)
+ lazy val curr = System.currentTimeMillis()
+ avatar
+ .implants
+ .collect {
+ case Some(implant) if implant.timer > 0 =>
+ (implant.definition.Name, math.max(0L, (implant.timer - curr) / 1000L).toInt)
+ case Some(implant) =>
+ (implant.definition.Name, 0)
+ }
+ .foreach {
+ case (name, delay) =>
+ ctx.run(
+ query[persistence.Implant]
+ .filter { _.avatarId == lift(avatarId) }
+ .filter { _.name.equals(lift(name)) }
+ .update { _.timer -> lift(delay) }
+ )
+ }
out.completeWith(Future(1))
case _ =>
out.completeWith(Future(0))
@@ -1000,6 +1016,16 @@ object AvatarActor {
decoration = ProgressDecoration(cosmetics = convertedCosmetics)
)
}
+
+ private def initializationTime(implant: Implant): FiniteDuration = {
+ val timer = implant.timer
+ val normalDuration = implant.definition.InitializationDuration
+ (if (timer == 0) {
+ normalDuration
+ } else {
+ math.max(0L, (timer - System.currentTimeMillis()) / 1000L)
+ }).seconds
+ }
}
class AvatarActor(
@@ -1015,7 +1041,7 @@ class AvatarActor(
private[this] val log = org.log4s.getLogger
var account: Option[Account] = None
var session: Option[Session] = None
- val implantTimers: mutable.Map[Int, Cancellable] = mutable.Map()
+ val implantTimers: Array[Cancellable] = Array.fill(3)(Default.Cancellable)
var staminaRegenTimer: Cancellable = Default.Cancellable
var _avatar: Option[Avatar] = None
var saveLockerFunc: () => Unit = storeNewLocker
@@ -1292,22 +1318,6 @@ class AvatarActor(
Behaviors.same
- case CreateImplants() =>
- avatar.implants.zipWithIndex.foreach {
- case (Some(implant), index) =>
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(
- session.get.player.GUID,
- ImplantAction.Add,
- index,
- implant.definition.implantType.value
- )
- )
- case _ => ()
- }
- deinitializeImplants()
- Behaviors.same
-
case LearnImplant(terminalGuid, definition) =>
// TODO there used to be a terminal check here, do we really need it?
buyImplantAction(terminalGuid, definition)
@@ -1466,113 +1476,36 @@ class AvatarActor(
avatarCopy(avatar.copy(vehicle = vehicle))
Behaviors.same
- case ActivateImplant(implantType) =>
- avatar.implants.zipWithIndex.collectFirst {
- case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
- } match {
- case Some((implant, slot)) =>
- if (!implant.initialized) {
- log.warn(s"requested activation of uninitialized implant $implantType")
- } else if (
- !consumeThisMuchStamina(implant.definition.ActivationStaminaCost) ||
- avatar.stamina < implant.definition.StaminaCost
- ) {
- // not enough stamina to activate
- } else if (implant.definition.implantType.disabledFor.contains(session.get.player.ExoSuit)) {
- // TODO can this really happen? can we prevent it?
- } else {
- avatarCopy(
- avatar.copy(
- implants = avatar.implants.updated(slot, Some(implant.copy(active = true)))
- )
- )
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Activation, slot, 1)
- )
- // Activation sound / effect
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- session.get.zone.id,
- AvatarAction.PlanetsideAttribute(
- session.get.player.GUID,
- 28,
- implant.definition.implantType.value * 2 + 1
- )
- )
- implantTimers.get(slot).foreach(_.cancel())
- val interval = implant.definition.GetCostIntervalByExoSuit(session.get.player.ExoSuit).milliseconds
- // TODO costInterval should be an option ^
- if (interval.toMillis > 0) {
- implantTimers(slot) = context.system.scheduler.scheduleWithFixedDelay(interval, interval)(() => {
- val player = session.get.player
- if (
- implantType match {
- case ImplantType.AdvancedRegen =>
- // for every 1hp: 2sp (running), 1.5sp (standing), 1sp (crouched)
- // to simulate '1.5sp (standing)', find if 0.0...1.0 * 100 is an even number
- val cost = implant.definition.StaminaCost -
- (if (player.Crouching || (!player.isMoving && (math.random() * 100) % 2 == 1)) 1 else 0)
- val aliveAndWounded = player.isAlive && player.Health < player.MaxHealth
- if (aliveAndWounded && consumeThisMuchStamina(cost)) {
- //heal
- val originalHealth = player.Health
- val zone = player.Zone
- val events = zone.AvatarEvents
- val guid = player.GUID
- val newHealth = player.Health = originalHealth + 1
- player.LogActivity(HealFromImplant(implantType, 1))
- events ! AvatarServiceMessage(
- zone.id,
- AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)
- )
- false
- } else {
- !aliveAndWounded
- }
- case _ =>
- !player.isAlive || !consumeThisMuchStamina(implant.definition.StaminaCost)
- }
- ) {
- context.self ! DeactivateImplant(implantType)
- }
- })
- }
- }
-
- case None => log.error(s"requested activation of unknown implant $implantType")
- }
+ case SetImplantInitialized(implantType) =>
+ actuallyInitializeImplant(implantType)
Behaviors.same
- case SetImplantInitialized(implantType) =>
- avatar.implants.zipWithIndex.collectFirst {
- case (Some(implant), index) if implant.definition.implantType == implantType => index
- } match {
- case Some(index) =>
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 1)
- )
- avatarCopy(avatar.copy(implants = avatar.implants.map {
- case Some(implant) if implant.definition.implantType == implantType =>
- Some(implant.copy(initialized = true))
- case other => other
- }))
-
- case None => log.error(s"set initialized called for unknown implant $implantType")
- }
-
+ case ActivateImplant(implantType) =>
+ activateImplant(implantType)
Behaviors.same
case DeactivateImplant(implantType) =>
deactivateImplant(implantType)
Behaviors.same
- case DeactivateActiveImplants() =>
- avatar.implants.indices.foreach { index =>
- avatar.implants(index).foreach { implant =>
- if (implant.active && implant.definition.GetCostIntervalByExoSuit(session.get.player.ExoSuit) > 0) {
- deactivateImplant(implant.definition.implantType)
- }
- }
- }
+ case DeactivateActiveImplants =>
+ deactivateActiveImplants()
+ Behaviors.same
+
+ case InitializeImplants =>
+ startInitializeImplants()
+ Behaviors.same
+
+ case DeinitializeImplants =>
+ deinitializeImplants()
+ Behaviors.same
+
+ case ResetImplant(implantType) =>
+ startReinitializeImplant(implantType)
+ Behaviors.same
+
+ case SoftResetImplants =>
+ softResetImplants()
Behaviors.same
case RestoreStamina(stamina) =>
@@ -1596,83 +1529,6 @@ class AvatarActor(
defaultStaminaRegen(duration)
Behaviors.same
- case InitializeImplants() =>
- initializeImplants()
- Behaviors.same
-
- case DeinitializeImplants() =>
- deinitializeImplants()
- Behaviors.same
-
- case ResetImplant(implantType) =>
- resetAnImplant(implantType)
- Behaviors.same
-
- case ResetImplants() =>
- val player = session.get.player
- // Get time of when you spawned after a deconstruction or zoning activity.
- val lastDecon: Long = player.History.findLast {entry => entry.isInstanceOf[ReconstructionActivity]} match {
- case Some(entry) => entry.time
- case _ => 0L
- }
- // Get time of when you entered the world or respawned after death.
- val lastRespawn: Long = player.History.findLast {entry => entry.isInstanceOf[SpawningActivity]} match {
- case Some(entry) => entry.time
- case _ => 0L
- }
- // You didn't die. You deconstructed or changed zones via warpgate/IA/recall.
- // When you respawn after death, it does both recon and spawn activities, hence the minus 3000 to make sure
- // this doesn't happen at respawn after death.
- if (lastDecon - 3000 > lastRespawn) {
- deinitializeImplants()
- val implants = avatar.implants
- implants.zipWithIndex.foreach {
- case (Some(implant), slot) =>
- sessionActor ! SessionActor.SendResponse(
- CreateShortcutMessage(
- session.get.player.GUID,
- slot + 2,
- Some(implant.definition.implantType.shortcut)
- )
- )
- // If the amount of time that has passed since you entered the world or died is > how long it takes to
- // initialize this implant, initialize it after 1 second.
- if ((System.currentTimeMillis() / 1000) - (lastRespawn / 1000) > implant.definition.InitializationDuration) {
- implantTimers.get(slot).foreach(_.cancel())
- implantTimers(slot) = context.scheduleOnce(
- 1.seconds,
- context.self,
- SetImplantInitialized(implant.definition.implantType)
- )
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- avatar.name,
- AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
- )
- }
- // If the implant initialization timer hasn't quite finished, calculate a reduced timer based on last spawn activity
- else {
- val remainingTime = (lastRespawn / 1000).seconds - (System.currentTimeMillis() / 1000).seconds + implant.definition.InitializationDuration.seconds
- implantTimers.get(slot).foreach(_.cancel())
- implantTimers(slot) = context.scheduleOnce(
- remainingTime,
- context.self,
- SetImplantInitialized(implant.definition.implantType)
- )
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- avatar.name,
- AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
- )
- }
- case (None, _) =>
- }
- }
- // You just entered the world or died. Implants reset and timers start from scratch
- else {
- deinitializeImplants()
- initializeImplants()
- }
- Behaviors.same
-
case UpdateToolDischarge(stats) =>
updateToolDischarge(stats)
Behaviors.same
@@ -1898,7 +1754,7 @@ class AvatarActor(
.receiveSignal {
case (_, PostStop) =>
staminaRegenTimer.cancel()
- implantTimers.values.foreach(_.cancel())
+ implantTimers.foreach(_.cancel())
supportExperienceTimer.cancel()
if (supportExperiencePool > 0) {
AvatarActor.setBepOnly(avatar.id, avatar.bep + supportExperiencePool)
@@ -1940,63 +1796,6 @@ class AvatarActor(
def performAvatarLogin(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
performAvatarLogin0(avatarId, accountId, replyTo)
- /*import ctx._
- val result = for {
- //log this login
- _ <- ctx.run(
- query[persistence.Avatar]
- .filter(_.id == lift(avatarId))
- .update(_.lastLogin -> lift(LocalDateTime.now()))
- )
- //log this choice of faction (no empire switching)
- _ <- ctx.run(
- query[persistence.Account]
- .filter(_.id == lift(accountId))
- .update(
- _.lastFactionId -> lift(avatar.faction.id),
- _.avatarLoggedIn -> lift(avatarId)
- )
- )
- //retrieve avatar data
- loadouts <- initializeAllLoadouts()
- implants <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(avatarId)))
- certs <- ctx.run(query[persistence.Certification].filter(_.avatarId == lift(avatarId)))
- locker <- loadLocker(avatarId)
- friends <- loadFriendList(avatarId)
- ignored <- loadIgnoredList(avatarId)
- shortcuts <- loadShortcuts(avatarId)
- saved <- AvatarActor.loadSavedAvatarData(avatarId)
- debt <- AvatarActor.loadExperienceDebt(avatarId)
- card <- AvatarActor.loadCampaignKdaData(avatarId)
- } yield (loadouts, implants, certs, locker, friends, ignored, shortcuts, saved, debt, card)
- result.onComplete {
- case Success((_loadouts, implants, certs, lockerInv, friendsList, ignoredList, shortcutList, saved, debt, card)) =>
- avatarCopy(
- avatar.copy(
- loadouts = avatar.loadouts.copy(suit = _loadouts),
- certifications =
- certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
- implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
- shortcuts = shortcutList,
- locker = lockerInv,
- people = MemberLists(
- friend = friendsList,
- ignored = ignoredList
- ),
- cooldowns = Cooldowns(
- purchase = AvatarActor.buildCooldownsFromClob(saved.purchaseCooldowns, Avatar.purchaseCooldowns, log),
- use = AvatarActor.buildCooldownsFromClob(saved.useCooldowns, Avatar.useCooldowns, log)
- ),
- scorecard = card
- )
- )
- // if we need to start stamina regeneration
- tryRestoreStaminaForSession(stamina = 1).collect { _ => defaultStaminaRegen(initialDelay = 0.5f seconds) }
- experienceDebt = debt
- replyTo ! AvatarLoginResponse(avatar)
- case Failure(e) =>
- log.error(e)("db failure")
- }*/
}
def performAvatarLogin0(avatarId: Long, accountId: Long, replyTo: ActorRef[AvatarLoginResponse]): Unit = {
@@ -2038,11 +1837,21 @@ class AvatarActor(
} yield (certs, implants, locker, debt)
result.onComplete {
case Success((certs, implants, lockerInv, debt)) =>
+ val curr = System.currentTimeMillis()
avatarCopy(
avatar.copy(
certifications =
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
- implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
+ implants = implants.map { imp =>
+ val timerEqualsZero = imp.timer == 0
+ val initTimer: Long = if (timerEqualsZero) {
+ 0L
+ } else {
+ curr + imp.timer * 1000L //convert from seconds to milliseconds time in future
+ }
+ val definition = imp.toImplantDefinition
+ Some(Implant(definition, initialized = timerEqualsZero, timer = initTimer, active = timerEqualsZero && definition.Passive))
+ }.padTo(3, None),
locker = lockerInv
)
)
@@ -2163,7 +1972,7 @@ class AvatarActor(
if (originalFatigued && !isFatigued) {
avatar.implants.zipWithIndex.foreach {
case (Some(_), slot) =>
- sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(guid, ImplantAction.OutOfStamina, slot, 0))
+ sendAvatarImplantMessageToSelf(guid, ImplantAction.OutOfStamina, slot, value = 0)
case _ => ()
}
}
@@ -2183,8 +1992,8 @@ class AvatarActor(
* meaning that he will only be able to walk, all implants will deactivate,
* and all exertion that require stamina use will become impossible until a threshold of stamina is regained.
* @param stamina an amount to drain
- * @return `true`, as long as the requested amount of stamina can be drained in total;
- * `false`, otherwise
+ * @return `false`, as long as the requested amount of stamina can be drained in total, or tif stamina equals zero;
+ * `true`, otherwise
*/
def consumeThisMuchStamina(stamina: Int): Boolean = {
if (stamina < 1) {
@@ -2204,9 +2013,7 @@ class AvatarActor(
if (implant.active) {
deactivateImplant(implant.definition.implantType)
}
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(player.GUID, ImplantAction.OutOfStamina, slot, 1)
- )
+ sendAvatarImplantMessageToSelf(player.GUID, ImplantAction.OutOfStamina, slot, value = 1)
case _ => ()
}
}
@@ -2214,7 +2021,7 @@ class AvatarActor(
} else if (becomeFatigued) {
avatarCopy(avatar.copy(implants = avatar.implants.zipWithIndex.collect {
case (Some(implant), slot) if implant.active =>
- implantTimers.get(slot).foreach(_.cancel())
+ cancelImplantInitializedTimer(slot)
Some(implant.copy(active = false))
case (out, _) =>
out
@@ -2224,119 +2031,6 @@ class AvatarActor(
}
}
- def initializeImplants(): Unit = {
- avatar.implants.zipWithIndex.foreach {
- case (Some(implant), slot) =>
- // TODO if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
- // for now, just write into slots 2, 3 and 4
- sessionActor ! SessionActor.SendResponse(
- CreateShortcutMessage(
- session.get.player.GUID,
- slot + 2,
- Some(implant.definition.implantType.shortcut)
- )
- )
-
- implantTimers.get(slot).foreach(_.cancel())
- implantTimers(slot) = context.scheduleOnce(
- implant.definition.InitializationDuration.seconds,
- context.self,
- SetImplantInitialized(implant.definition.implantType)
- )
-
- // Start client side initialization timer, visible on the character screen
- // Progress accumulates according to the client's knowledge of the implant initialization time
- // What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s\
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- avatar.name,
- AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
- )
-
- case (None, _) => ()
- }
- }
-
- def deinitializeImplants(): Unit = {
- avatarCopy(avatar.copy(implants = avatar.implants.zipWithIndex.map {
- case (Some(implant), slot) =>
- if (implant.active) {
- deactivateImplant(implant.definition.implantType)
- }
- if (implant.initialized) {
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- session.get.zone.id,
- AvatarAction.SendResponse(
- Service.defaultPlayerGUID,
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
- )
- )
- }
- Some(implant.copy(initialized = false, active = false))
- case (None, _) => None
- }))
- }
-
- def resetAnImplant(implantType: ImplantType): Unit = {
- avatar.implants.zipWithIndex.find {
- case (Some(imp), _) => imp.definition.implantType == implantType
- case (None, _) => false
- } match {
- case Some((Some(imp), index)) =>
- //deactivate
- if (imp.active) {
- deactivateImplant(implantType)
- }
- //deinitialize
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- session.get.zone.id,
- AvatarAction.SendResponse(
- Service.defaultPlayerGUID,
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 0)
- )
- )
- avatarCopy(
- avatar.copy(
- implants = avatar.implants.updated(index, Some(imp.copy(initialized = false, active = false)))
- )
- )
- //restart initialization process
- implantTimers.get(index).foreach(_.cancel())
- implantTimers(index) = context.scheduleOnce(
- imp.definition.InitializationDuration.seconds,
- context.self,
- SetImplantInitialized(implantType)
- )
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- avatar.name,
- AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(index + 6, 0))
- )
- case _ => ()
- }
- }
-
- def deactivateImplant(implantType: ImplantType): Unit = {
- avatar.implants.zipWithIndex.collectFirst {
- case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
- } match {
- case Some((implant, slot)) =>
- implantTimers.get(slot).foreach(_.cancel())
- avatarCopy(
- avatar.copy(
- implants = avatar.implants.updated(slot, Some(implant.copy(active = false)))
- )
- )
- // Deactivation sound / effect
- session.get.zone.AvatarEvents ! AvatarServiceMessage(
- session.get.zone.id,
- AvatarAction.PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2)
- )
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Activation, slot, 0)
- )
- case None => log.error(s"requested deactivation of unknown implant $implantType")
- }
- }
-
/** Send list of avatars to client (show character selection screen) */
def sendAvatars(account: Account): Unit = {
import ctx._
@@ -3115,9 +2809,7 @@ class AvatarActor(
)
.onComplete {
case Success(_) =>
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0)
- )
+ sendAvatarImplantMessageToSelf(pguid, ImplantAction.Remove, index, value = 0)
case Failure(exception) =>
log.error(exception)("db failure")
}
@@ -3573,16 +3265,30 @@ class AvatarActor(
AvatarActor.basicLoginCertifications.diff(certs).foreach { learnCertificationInTheFuture }
}
- def buyImplantAction(
- terminalGuid: PlanetSideGUID,
- definition: ImplantDefinition
- ): Unit = {
+ private def sendAvatarImplantMessageToSelf(
+ guid: PlanetSideGUID,
+ action: ImplantAction.Value,
+ index: Int,
+ value: Int
+ ): Unit = {
+ import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
+ import net.psforever.services.avatar.{AvatarResponse => RESP}
+ sessionActor.toClassic ! AvatarServiceResponse("", guid, RESP.AvatarImplant(action, index, value))
+ }
+
+ private def buyImplantAction(
+ terminalGuid: PlanetSideGUID,
+ definition: ImplantDefinition
+ ): Unit = {
buyImplantInTheFuture(definition).onComplete {
case Success(true) =>
sessionActor ! SessionActor.SendResponse(
ItemTransactionResultMessage(terminalGuid, TransactionType.Buy, success = true)
)
- resetAnImplant(definition.implantType)
+ findImplantByType(definition.implantType).foreach {
+ case (implant, slot) =>
+ updateAvatarForImplant(implant, slot, startInitializeImplant(AvatarActor.initializationTime(implant)))
+ }
sessionActor ! SessionActor.CharSaved
case _ =>
sessionActor ! SessionActor.SendResponse(
@@ -3591,7 +3297,7 @@ class AvatarActor(
}
}
- def buyImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
+ private def buyImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
val out: Promise[Boolean] = Promise()
avatar.implants.zipWithIndex.collectFirst {
case (Some(implant), _) if implant.definition.implantType == definition.implantType => None
@@ -3604,14 +3310,7 @@ class AvatarActor(
.onComplete {
case Success(_) =>
replaceAvatar(avatar.copy(implants = avatar.implants.updated(index, Some(Implant(definition)))))
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(
- session.get.player.GUID,
- ImplantAction.Add,
- index,
- definition.implantType.value
- )
- )
+ sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Add, index, definition.implantType.value)
out.completeWith(Future(true))
case Failure(exception) =>
log.error(exception)("db failure")
@@ -3624,10 +3323,10 @@ class AvatarActor(
out.future
}
- def sellImplantAction(
- terminalGuid: PlanetSideGUID,
- definition: ImplantDefinition
- ): Unit = {
+ private def sellImplantAction(
+ terminalGuid: PlanetSideGUID,
+ definition: ImplantDefinition
+ ): Unit = {
sellImplantInTheFuture(definition).onComplete {
case Success(true) =>
sessionActor ! SessionActor.SendResponse(
@@ -3641,7 +3340,7 @@ class AvatarActor(
}
}
- def sellImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
+ private def sellImplantInTheFuture(definition: ImplantDefinition): Future[Boolean] = {
val out: Promise[Boolean] = Promise()
avatar.implants.zipWithIndex.collectFirst {
case (Some(implant), index) if implant.definition.implantType == definition.implantType => index
@@ -3657,10 +3356,8 @@ class AvatarActor(
)
.onComplete {
case Success(_) =>
- replaceAvatar(avatar.copy(implants = avatar.implants.updated(index, None)))
- sessionActor ! SessionActor.SendResponse(
- AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, index, 0)
- )
+ updateAvatarForImplant(index)
+ sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Remove, index, value = 0)
out.completeWith(Future(true))
case Failure(exception) =>
log.error(exception)("db failure")
@@ -3673,9 +3370,321 @@ class AvatarActor(
out.future
}
- def removeAllImplants(): Unit = {
- avatar.implants.collect { case Some(imp) => imp.definition }.foreach { sellImplantInTheFuture }
- context.self ! ResetImplants()
+ private def findImplantByType(implantType: ImplantType): Option[(Implant, Int)] = {
+ avatar
+ .implants
+ .zipWithIndex
+ .collectFirst {
+ case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
+ }
+ }
+
+ private def updateAvatarForImplant(
+ implant: Implant,
+ slot: Int,
+ implantFunc: (Implant, Int) => Implant
+ ): Unit = {
+ avatarCopy(avatar.copy(implants = avatar.implants.updated(slot, Some(implantFunc(implant, slot)))))
+ }
+
+ private def updateAvatarForImplant(slot: Int): Unit = {
+ avatarCopy(avatar.copy(implants = avatar.implants.updated(slot, None)))
+ }
+
+ private def startInitializeImplants(): Unit = {
+ avatar.implants.zipWithIndex.foreach {
+ case (Some(implant), slot) =>
+ // TODO if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
+ // for now, just write into slots 2, 3 and 4
+ sessionActor ! SessionActor.SendResponse(
+ CreateShortcutMessage(
+ session.get.player.GUID,
+ slot + 2,
+ Some(implant.definition.implantType.shortcut)
+ )
+ )
+ startInitializeImplant(AvatarActor.initializationTime(implant))(implant, slot)
+ case (None, _) => ()
+ }
+ }
+
+ private def startReinitializeImplant(implantType: ImplantType): Unit = {
+ findImplantByType(implantType).collect {
+ case (implant, slot) if implant.active =>
+ updateAvatarForImplant(deactivateImplant(implant, slot), slot, startReinitializeImplant)
+ case (implant, slot) =>
+ updateAvatarForImplant(implant, slot, startReinitializeImplant)
+ }
+ }
+
+ private def startReinitializeImplant(implant: Implant, slot: Int): Implant = {
+ //deinitialize
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ session.get.zone.id,
+ AvatarAction.AvatarImplant(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
+ )
+ startInitializeImplant(AvatarActor.initializationTime(implant))(implant, slot)
+ }
+
+ private def startInitializeImplant(delay: FiniteDuration)(implant: Implant, slot: Int): Implant = {
+ if (implantTimers.lift(slot).exists(_.isCancelled)) {
+ val curr = System.currentTimeMillis()
+ val implantTimer = implant.timer
+ val (actualDelay, futureDelay, actionProgress) = calculateImplantTimerStats(implant, delay)
+ //start initialization process
+ setImplantInitializedTimer(implant, slot, actualDelay)
+ // Start client-side initialization timer, visible on the character screen
+ // Progress accumulates according to the client's knowledge of the implant initialization time
+ // What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ avatar.name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, actionProgress))
+ )
+ implant.copy(initialized = false, active = false, timer = futureDelay)
+ } else {
+ implant
+ }
+ }
+
+ private def calculateImplantTimerStats(implant: Implant, delay: FiniteDuration): (FiniteDuration, Long, Long) = {
+ val curr = System.currentTimeMillis()
+ val implantTimer = implant.timer
+ if (implantTimer > curr) {
+ val countedDelay = math.max(0L, (implantTimer - curr) / 1000L)
+ val fullNormalDelay = implant.definition.InitializationDuration.toFloat
+ val progress = (100f * ((fullNormalDelay - countedDelay.toFloat) / fullNormalDelay)).toLong
+ (countedDelay.seconds, implantTimer, progress)
+ } else {
+ (delay, curr + delay.toMillis, 0L)
+ }
+ }
+
+ private def deinitializeImplants(): Unit = {
+ avatarCopy(avatar.copy(implants = avatar
+ .implants
+ .zipWithIndex
+ .collect {
+ case (Some(implant), slot) if implant.active =>
+ Some(deinitializeImplant(deactivateImplant(implant, slot), slot))
+ case (Some(implant), slot) if implant.initialized =>
+ Some(deinitializeImplant(implant, slot))
+ case (Some(implant), slot) if implantTimers.lift(slot).exists(timer => !timer.isCancelled) =>
+ Some(stopImplantInitializationTimer(implant, slot))
+ case (implantOpt, _) =>
+ implantOpt
+ }
+ ))
+ }
+
+ private def deinitializeImplant(implant: Implant, slot: Int): Implant = {
+ val outImplant = stopImplantInitializationTimer(implant, slot)
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ session.get.zone.id,
+ AvatarAction.AvatarImplant(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
+ )
+ outImplant
+ }
+
+ def stopImplantInitializationTimer(implant: Implant, slot: Int): Implant = {
+ cancelImplantInitializedTimer(slot)
+ //can not formally stop the initialization time on the character information window; set it to 100 to make it look blank
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ avatar.name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 100))
+ )
+ implant.copy(initialized = false, active = false, timer = 0L)
+ }
+
+ private def actuallyInitializeImplant(implantType: ImplantType): Unit = {
+ findImplantByType(implantType)
+ .collect {
+ case (implant, slot) =>
+ sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Initialization, slot, value = 1)
+ cancelImplantInitializedTimer(slot)
+ avatarCopy(avatar.copy(implants = avatar.implants.map {
+ case Some(implant)
+ if implant.definition.implantType == implantType && implant.definition.Passive =>
+ activateImplantPackets(implant, slot)
+ Some(implant.copy(initialized = true, active = true, timer = 0))
+ case Some(implant)
+ if implant.definition.implantType == implantType =>
+ Some(implant.copy(initialized = true, timer = 0))
+ case other =>
+ other
+ }))
+ Some(implant)
+ }
+ .orElse {
+ log.error(s"set initialized called for unknown implant $implantType")
+ None
+ }
+ }
+
+ private def setImplantInitializedTimer(implant: Implant, slot: Int, delay: FiniteDuration): Unit = {
+ implantTimers.lift(slot).foreach(_.cancel())
+ implantTimers.update(slot, context.scheduleOnce(
+ delay,
+ context.self,
+ SetImplantInitialized(implant.definition.implantType)
+ ))
+ }
+
+ private def cancelImplantInitializedTimer(slot: Int): Unit = {
+ implantTimers.lift(slot).foreach(_.cancel())
+ implantTimers.update(slot, Default.Cancellable)
+ }
+
+ private def deactivateImplant(implantType: ImplantType): Unit = {
+ avatar.implants.zipWithIndex.collectFirst {
+ case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
+ } match {
+ case Some((implant, slot)) =>
+ updateAvatarForImplant(implant, slot, deactivateImplant)
+ case None =>
+ log.error(s"requested deactivation of unknown implant $implantType")
+ }
+ }
+
+ private def deactivateActiveImplants(): Unit = {
+ avatar
+ .implants
+ .zipWithIndex
+ .collect {
+ case (Some(implant), slot) if implant.active && !implant.definition.Passive =>
+ updateAvatarForImplant(implant, slot, deactivateImplant)
+ }
+ }
+
+ private def deactivateImplant(implant: Implant, slot: Int): Implant = {
+ cancelImplantInitializedTimer(slot)
+ // Deactivation sound / effect
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ session.get.zone.id,
+ AvatarAction.PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2)
+ )
+ sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Activation, slot, value = 0)
+ implant.copy(active = false)
+ }
+
+ private def activateImplant(implantType: ImplantType): Unit = {
+ findImplantByType(implantType)
+ .collect { case (implant, slot) =>
+ activateImplant(implant, slot)
+ Some(true)
+ }
+ .orElse {
+ log.error(s"requested activation of unknown implant $implantType")
+ None
+ }
+ }
+
+ private def activateImplant(implant: Implant, slot: Int): Unit = {
+ if (!implant.initialized) {
+ log.warn(s"requested activation of uninitialized implant ${implant.definition.implantType}")
+ } else if (
+ !consumeThisMuchStamina(implant.definition.ActivationStaminaCost) ||
+ avatar.stamina < implant.definition.StaminaCost
+ ) {
+ // not enough stamina to activate
+ } else if (implant.definition.implantType.disabledFor.contains(session.get.player.ExoSuit)) {
+ // TODO can this really happen? can we prevent it?
+ } else {
+ avatarCopy(
+ avatar.copy(
+ implants = avatar.implants.updated(slot, Some(implant.copy(active = true)))
+ )
+ )
+ activateImplantPackets(implant, slot)
+ implantTimers.lift(slot).foreach(_.cancel())
+ val interval = implant.definition.GetCostIntervalByExoSuit(session.get.player.ExoSuit).milliseconds
+ if (interval.toMillis > 0) {
+ val stopConditionTest: (Implant, Player) => Boolean = implant.definition.implantType match {
+ case ImplantType.AdvancedRegen => staminaDrainByIntervalAdvancedRegen
+ case _ => staminaDrainByIntervalSomeImplant
+ }
+ val stopConditionFunc: () => Unit = staminaDrainByIntervalOngoing(implant, slot, session.get.player, stopConditionTest)
+ implantTimers.update(slot, context.system.scheduler.scheduleWithFixedDelay(interval, interval)(() => stopConditionFunc()))
+ }
+ }
+ }
+
+ private def staminaDrainByIntervalOngoing(
+ implant: Implant,
+ slot: Int,
+ player: Player,
+ func: (Implant, Player) => Boolean
+ )(): Unit = {
+ if (func(implant, player)) {
+ updateAvatarForImplant(implant, slot, deactivateImplant)
+ }
+ }
+
+ private def staminaDrainByIntervalSomeImplant(implant: Implant, player: Player): Boolean = {
+ !player.isAlive || !consumeThisMuchStamina(implant.definition.StaminaCost)
+ }
+
+ private def staminaDrainByIntervalAdvancedRegen(implant: Implant, player: Player): Boolean = {
+ // for every 1hp: 2sp (running), 1.5sp (standing), 1sp (crouched)
+ // to simulate '1.5sp (standing)', find if 0.0...1.0 * 100 is an even number
+ val cost = implant.definition.StaminaCost -
+ (if (player.Crouching || (!player.isMoving && (math.random() * 100) % 2 == 1)) 1 else 0)
+ val aliveAndWounded = player.isAlive && player.Health < player.MaxHealth
+ if (aliveAndWounded && consumeThisMuchStamina(cost)) {
+ //heal
+ val originalHealth = player.Health
+ val zone = player.Zone
+ val guid = player.GUID
+ val newHealth = player.Health = originalHealth + 1
+ val events = zone.AvatarEvents
+ player.LogActivity(HealFromImplant(implant.definition.implantType, 1))
+ events ! AvatarServiceMessage(
+ zone.id,
+ AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)
+ )
+ false
+ } else {
+ !aliveAndWounded
+ }
+ }
+
+ private def activateImplantPackets(implant: Implant, slot: Int): Unit = {
+ sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Activation, slot, value = 1)
+ // Activation sound / effect
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ session.get.zone.id,
+ AvatarAction.PlanetsideAttribute(
+ session.get.player.GUID,
+ 28,
+ implant.definition.implantType.value * 2 + 1
+ )
+ )
+ }
+
+ private def softResetImplants() : Unit = {
+ avatarCopy(
+ avatar.copy(implants = avatar
+ .implants
+ .zipWithIndex
+ .map {
+ case (Some(implant), slot) if implant.active && !implant.definition.Passive =>
+ //deactivate active non-passive implant
+ Some(deactivateImplant(implant, slot))
+ case (Some(implant), slot) if !implant.initialized && implantTimers.lift(slot).exists(_.isCancelled) =>
+ //restart stopped/unstarted initialization process
+ Some(startReinitializeImplant(implant, slot))
+ case (implantOpt @ Some(implant), slot) =>
+ //update ongoing progress
+ val actionProgress = calculateImplantTimerStats(implant, AvatarActor.initializationTime(implant))._3
+ session.get.zone.AvatarEvents ! AvatarServiceMessage(
+ avatar.name,
+ AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, actionProgress))
+ )
+ implantOpt
+ case (None, _) =>
+ None
+ }
+ )
+ )
}
def resetSupportExperienceTimer(previousBep: Long, previousDelay: Long): Unit = {
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index 7ea23d760..d24b0cee3 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -1,8 +1,29 @@
// Copyright (c) 2016, 2020, 2024 PSForever
package net.psforever.actors.session
-import akka.actor.{Actor, Cancellable, MDCContextAware, typed}
+import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware, typed}
import net.psforever.actors.session.normal.NormalMode
+import net.psforever.actors.session.support.ZoningOperations
+import net.psforever.objects.TurretDeployable
+import net.psforever.objects.serverobject.CommonMessages
+import net.psforever.objects.serverobject.containable.Containable
+import net.psforever.objects.serverobject.deploy.Deployment
+import net.psforever.objects.serverobject.mount.Mountable
+import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
+import net.psforever.objects.zones.Zone
+import net.psforever.packet.PlanetSideGamePacket
+import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
+import net.psforever.services.{InterstellarClusterService => ICS}
+import net.psforever.services.CavernRotationService
+import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
+import net.psforever.services.ServiceManager.LookupResult
+import net.psforever.services.account.{PlayerToken, ReceiveAccountData}
+import net.psforever.services.avatar.AvatarServiceResponse
+import net.psforever.services.chat.ChatService
+import net.psforever.services.galaxy.GalaxyServiceResponse
+import net.psforever.services.local.LocalServiceResponse
+import net.psforever.services.teamwork.SquadServiceResponse
+import net.psforever.services.vehicle.VehicleServiceResponse
import org.joda.time.LocalDateTime
import org.log4s.MDC
@@ -105,23 +126,18 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
private def inTheGame: Receive = {
- /* used for the game's heartbeat */
case SessionActor.StartHeartbeat =>
+ //used for the game's heartbeat
startHeartbeat()
case SessionActor.PokeClient =>
- middlewareActor ! MiddlewareActor.Send(KeepAliveMessage())
+ pokeClient()
case SessionActor.SetMode(newMode) =>
- if (mode != newMode) {
- logic.switchFrom(data.session)
- }
- mode = newMode
- logic = mode.setup(data)
- logic.switchTo(data.session)
+ changeMode(newMode)
case packet =>
- logic.parse(sender())(packet)
+ parse(sender())(packet)
}
private def startHeartbeat(): Unit = {
@@ -135,4 +151,467 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
SessionActor.PokeClient
)
}
+
+ private def pokeClient(): Unit = {
+ middlewareActor ! MiddlewareActor.Send(KeepAliveMessage())
+ }
+
+ private def changeMode(newMode: PlayerMode): Unit = {
+ if (mode != newMode) {
+ logic.switchFrom(data.session)
+ mode = newMode
+ logic = mode.setup(data)
+ }
+ logic.switchTo(data.session)
+ }
+
+ private def parse(sender: ActorRef): Receive = {
+ /* really common messages (very frequently, every life) */
+ case packet: PlanetSideGamePacket =>
+ handleGamePkt(packet)
+
+ case AvatarServiceResponse(toChannel, guid, reply) =>
+ logic.avatarResponse.handle(toChannel, guid, reply)
+
+ case GalaxyServiceResponse(_, reply) =>
+ logic.galaxy.handle(reply)
+
+ case LocalServiceResponse(toChannel, guid, reply) =>
+ logic.local.handle(toChannel, guid, reply)
+
+ case Mountable.MountMessages(tplayer, reply) =>
+ logic.mountResponse.handle(tplayer, reply)
+
+ case SquadServiceResponse(_, excluded, response) =>
+ logic.squad.handle(response, excluded)
+
+ case Terminal.TerminalMessage(tplayer, msg, order) =>
+ logic.terminals.handle(tplayer, msg, order)
+
+ case VehicleServiceResponse(toChannel, guid, reply) =>
+ logic.vehicleResponse.handle(toChannel, guid, reply)
+
+ case ChatService.MessageResponse(fromSession, message, _) =>
+ logic.chat.handleIncomingMessage(message, fromSession)
+
+ case SessionActor.SendResponse(packet) =>
+ data.sendResponse(packet)
+
+ case SessionActor.CharSaved =>
+ logic.general.handleRenewCharSavedTimer()
+
+ case SessionActor.CharSavedMsg =>
+ logic.general.handleRenewCharSavedTimerMsg()
+
+ /* common messages (maybe once every respawn) */
+ case ICS.SpawnPointResponse(response) =>
+ data.zoning.handleSpawnPointResponse(response)
+
+ case SessionActor.NewPlayerLoaded(tplayer) =>
+ data.zoning.spawn.handleNewPlayerLoaded(tplayer)
+
+ case SessionActor.PlayerLoaded(tplayer) =>
+ data.zoning.spawn.handlePlayerLoaded(tplayer)
+
+ case Zone.Population.PlayerHasLeft(zone, playerOpt) =>
+ data.zoning.spawn.handlePlayerHasLeft(zone, playerOpt)
+
+ case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
+ data.zoning.spawn.handlePlayerCanNotSpawn(zone, tplayer)
+
+ case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
+ data.zoning.spawn.handlePlayerAlreadySpawned(zone, tplayer)
+
+ case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
+ data.zoning.spawn.handleCanNotSpawn(zone, vehicle, reason)
+
+ case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
+ data.zoning.spawn.handleCanNotDespawn(zone, vehicle, reason)
+
+ case ICS.ZoneResponse(Some(zone)) =>
+ data.zoning.handleZoneResponse(zone)
+
+ /* uncommon messages (once a session) */
+ case ICS.ZonesResponse(zones) =>
+ data.zoning.handleZonesResponse(zones)
+
+ case SessionActor.SetAvatar(avatar) =>
+ logic.general.handleSetAvatar(avatar)
+
+ case PlayerToken.LoginInfo(name, Zone.Nowhere, _) =>
+ data.zoning.spawn.handleLoginInfoNowhere(name, sender)
+
+ case PlayerToken.LoginInfo(name, inZone, optionalSavedData) =>
+ data.zoning.spawn.handleLoginInfoSomewhere(name, inZone, optionalSavedData, sender)
+
+ case PlayerToken.RestoreInfo(playerName, inZone, pos) =>
+ data.zoning.spawn.handleLoginInfoRestore(playerName, inZone, pos, sender)
+
+ case PlayerToken.CanNotLogin(playerName, reason) =>
+ data.zoning.spawn.handleLoginCanNot(playerName, reason)
+
+ case ReceiveAccountData(account) =>
+ logic.general.handleReceiveAccountData(account)
+
+ case AvatarActor.AvatarResponse(avatar) =>
+ logic.general.handleAvatarResponse(avatar)
+
+ case AvatarActor.AvatarLoginResponse(avatar) =>
+ data.zoning.spawn.avatarLoginResponse(avatar)
+
+ case SessionActor.SetCurrentAvatar(tplayer, max_attempts, attempt) =>
+ data.zoning.spawn.ReadyToSetCurrentAvatar(tplayer, max_attempts, attempt)
+
+ case SessionActor.SetConnectionState(state) =>
+ data.connectionState = state
+
+ case SessionActor.AvatarLoadingSync(state) =>
+ data.zoning.spawn.handleAvatarLoadingSync(state)
+
+ /* uncommon messages (utility, or once in a while) */
+ case ZoningOperations.AvatarAwardMessageBundle(pkts, delay) =>
+ data.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)
+
+ case CommonMessages.ProgressEvent(delta, finishedAction, stepAction, tick) =>
+ data.general.handleProgressChange(delta, finishedAction, stepAction, tick)
+
+ case CommonMessages.Progress(rate, finishedAction, stepAction) =>
+ data.general.setupProgressChange(rate, finishedAction, stepAction)
+
+ case CavernRotationService.CavernRotationServiceKey.Listing(listings) =>
+ listings.head ! SendCavernRotationUpdates(data.context.self)
+
+ case LookupResult("propertyOverrideManager", endpoint) =>
+ data.zoning.propertyOverrideManagerLoadOverrides(endpoint)
+
+ case SessionActor.UpdateIgnoredPlayers(msg) =>
+ logic.galaxy.handleUpdateIgnoredPlayers(msg)
+
+ case SessionActor.UseCooldownRenewed(definition, _) =>
+ logic.general.handleUseCooldownRenew(definition)
+
+ case Deployment.CanDeploy(obj, state) =>
+ logic.vehicles.handleCanDeploy(obj, state)
+
+ case Deployment.CanUndeploy(obj, state) =>
+ logic.vehicles.handleCanUndeploy(obj, state)
+
+ case Deployment.CanNotChangeDeployment(obj, state, reason) =>
+ logic.vehicles.handleCanNotChangeDeployment(obj, state, reason)
+
+ /* rare messages */
+ case ProximityUnit.StopAction(term, _) =>
+ logic.terminals.ops.LocalStopUsingProximityUnit(term)
+
+ case SessionActor.Suicide() =>
+ data.general.suicide(data.player)
+
+ case SessionActor.Recall() =>
+ data.zoning.handleRecall()
+
+ case SessionActor.InstantAction() =>
+ data.zoning.handleInstantAction()
+
+ case SessionActor.Quit() =>
+ data.zoning.handleQuit()
+
+ case ICS.DroppodLaunchDenial(errorCode, _) =>
+ data.zoning.handleDroppodLaunchDenial(errorCode)
+
+ case ICS.DroppodLaunchConfirmation(zone, position) =>
+ data.zoning.LoadZoneLaunchDroppod(zone, position)
+
+ case SessionActor.PlayerFailedToLoad(tplayer) =>
+ data.zoning.spawn.handlePlayerFailedToLoad(tplayer)
+
+ /* csr only */
+ case SessionActor.SetSpeed(speed) =>
+ logic.general.handleSetSpeed(speed)
+
+ case SessionActor.SetFlying(isFlying) =>
+ logic.general.handleSetFlying(isFlying)
+
+ case SessionActor.SetSpectator(isSpectator) =>
+ logic.general.handleSetSpectator(isSpectator)
+
+ case SessionActor.Kick(player, time) =>
+ logic.general.handleKick(player, time)
+
+ case SessionActor.SetZone(zoneId, position) =>
+ data.zoning.handleSetZone(zoneId, position)
+
+ case SessionActor.SetPosition(position) =>
+ data.zoning.spawn.handleSetPosition(position)
+
+ case SessionActor.SetSilenced(silenced) =>
+ logic.general.handleSilenced(silenced)
+
+ /* catch these messages */
+ case _: ProximityUnit.Action => ()
+
+ case _: Zone.Vehicle.HasSpawned => ()
+
+ case _: Zone.Vehicle.HasDespawned => ()
+
+ case Zone.Deployable.IsDismissed(obj: TurretDeployable) => //only if target deployable was never fully introduced
+ logic.local.handleTurretDeployableIsDismissed(obj)
+
+ case Zone.Deployable.IsDismissed(obj) => //only if target deployable was never fully introduced
+ logic.local.handleDeployableIsDismissed(obj)
+
+ case msg: Containable.ItemPutInSlot =>
+ logic.general.handleItemPutInSlot(msg)
+
+ case msg: Containable.CanNotPutItemInSlot =>
+ logic.general.handleCanNotPutItemInSlot(msg)
+
+ case default =>
+ logic.general.handleReceiveDefaultMessage(default, sender)
+ }
+
+ private def handleGamePkt: PlanetSideGamePacket => Unit = {
+ case packet: ConnectToWorldRequestMessage =>
+ logic.general.handleConnectToWorldRequest(packet)
+
+ case packet: MountVehicleCargoMsg =>
+ logic.mountResponse.handleMountVehicleCargo(packet)
+
+ case packet: DismountVehicleCargoMsg =>
+ logic.mountResponse.handleDismountVehicleCargo(packet)
+
+ case packet: CharacterCreateRequestMessage =>
+ logic.general.handleCharacterCreateRequest(packet)
+
+ case packet: CharacterRequestMessage =>
+ logic.general.handleCharacterRequest(packet)
+
+ case _: KeepAliveMessage =>
+ data.keepAliveFunc()
+
+ case packet: BeginZoningMessage =>
+ data.zoning.handleBeginZoning(packet)
+
+ case packet: PlayerStateMessageUpstream =>
+ logic.general.handlePlayerStateUpstream(packet)
+
+ case packet: ChildObjectStateMessage =>
+ logic.vehicles.handleChildObjectState(packet)
+
+ case packet: VehicleStateMessage =>
+ logic.vehicles.handleVehicleState(packet)
+
+ case packet: VehicleSubStateMessage =>
+ logic.vehicles.handleVehicleSubState(packet)
+
+ case packet: FrameVehicleStateMessage =>
+ logic.vehicles.handleFrameVehicleState(packet)
+
+ case packet: ProjectileStateMessage =>
+ logic.shooting.handleProjectileState(packet)
+
+ case packet: LongRangeProjectileInfoMessage =>
+ logic.shooting.handleLongRangeProjectileState(packet)
+
+ case packet: ReleaseAvatarRequestMessage =>
+ data.zoning.spawn.handleReleaseAvatarRequest(packet)
+
+ case packet: SpawnRequestMessage =>
+ data.zoning.spawn.handleSpawnRequest(packet)
+
+ case packet: ChatMsg =>
+ logic.chat.handleChatMsg(packet)
+
+ case packet: SetChatFilterMessage =>
+ logic.chat.handleChatFilter(packet)
+
+ case packet: VoiceHostRequest =>
+ logic.general.handleVoiceHostRequest(packet)
+
+ case packet: VoiceHostInfo =>
+ logic.general.handleVoiceHostInfo(packet)
+
+ case packet: ChangeAmmoMessage =>
+ logic.shooting.handleChangeAmmo(packet)
+
+ case packet: ChangeFireModeMessage =>
+ logic.shooting.handleChangeFireMode(packet)
+
+ case packet: ChangeFireStateMessage_Start =>
+ logic.shooting.handleChangeFireStateStart(packet)
+
+ case packet: ChangeFireStateMessage_Stop =>
+ logic.shooting.handleChangeFireStateStop(packet)
+
+ case packet: EmoteMsg =>
+ logic.general.handleEmote(packet)
+
+ case packet: DropItemMessage =>
+ logic.general.handleDropItem(packet)
+
+ case packet: PickupItemMessage =>
+ logic.general.handlePickupItem(packet)
+
+ case packet: ReloadMessage =>
+ logic.shooting.handleReload(packet)
+
+ case packet: ObjectHeldMessage =>
+ logic.general.handleObjectHeld(packet)
+
+ case packet: AvatarJumpMessage =>
+ logic.general.handleAvatarJump(packet)
+
+ case packet: ZipLineMessage =>
+ logic.general.handleZipLine(packet)
+
+ case packet: RequestDestroyMessage =>
+ logic.general.handleRequestDestroy(packet)
+
+ case packet: MoveItemMessage =>
+ logic.general.handleMoveItem(packet)
+
+ case packet: LootItemMessage =>
+ logic.general.handleLootItem(packet)
+
+ case packet: AvatarImplantMessage =>
+ logic.general.handleAvatarImplant(packet)
+
+ case packet: UseItemMessage =>
+ logic.general.handleUseItem(packet)
+
+ case packet: UnuseItemMessage =>
+ logic.general.handleUnuseItem(packet)
+
+ case packet: ProximityTerminalUseMessage =>
+ logic.terminals.handleProximityTerminalUse(packet)
+
+ case packet: DeployObjectMessage =>
+ logic.general.handleDeployObject(packet)
+
+ case packet: GenericObjectActionMessage =>
+ logic.general.handleGenericObjectAction(packet)
+
+ case packet: GenericObjectActionAtPositionMessage =>
+ logic.general.handleGenericObjectActionAtPosition(packet)
+
+ case packet: GenericObjectStateMsg =>
+ logic.general.handleGenericObjectState(packet)
+
+ case packet: GenericActionMessage =>
+ logic.general.handleGenericAction(packet)
+
+ case packet: ItemTransactionMessage =>
+ logic.terminals.handleItemTransaction(packet)
+
+ case packet: FavoritesRequest =>
+ logic.terminals.handleFavoritesRequest(packet)
+
+ case packet: WeaponDelayFireMessage =>
+ logic.shooting.handleWeaponDelayFire(packet)
+
+ case packet: WeaponDryFireMessage =>
+ logic.shooting.handleWeaponDryFire(packet)
+
+ case packet: WeaponFireMessage =>
+ logic.shooting.handleWeaponFire(packet)
+
+ case packet: WeaponLazeTargetPositionMessage =>
+ logic.shooting.handleWeaponLazeTargetPosition(packet)
+
+ case _: UplinkRequest => ()
+
+ case packet: HitMessage =>
+ logic.shooting.handleDirectHit(packet)
+
+ case packet: SplashHitMessage =>
+ logic.shooting.handleSplashHit(packet)
+
+ case packet: LashMessage =>
+ logic.shooting.handleLashHit(packet)
+
+ case packet: AIDamage =>
+ logic.shooting.handleAIDamage(packet)
+
+ case packet: AvatarFirstTimeEventMessage =>
+ logic.general.handleAvatarFirstTimeEvent(packet)
+
+ case packet: WarpgateRequest =>
+ data.zoning.handleWarpgateRequest(packet)
+
+ case packet: MountVehicleMsg =>
+ logic.mountResponse.handleMountVehicle(packet)
+
+ case packet: DismountVehicleMsg =>
+ logic.mountResponse.handleDismountVehicle(packet)
+
+ case packet: DeployRequestMessage =>
+ logic.vehicles.handleDeployRequest(packet)
+
+ case packet: AvatarGrenadeStateMessage =>
+ logic.shooting.handleAvatarGrenadeState(packet)
+
+ case packet: SquadDefinitionActionMessage =>
+ logic.squad.handleSquadDefinitionAction(packet)
+
+ case packet: SquadMembershipRequest =>
+ logic.squad.handleSquadMemberRequest(packet)
+
+ case packet: SquadWaypointRequest =>
+ logic.squad.handleSquadWaypointRequest(packet)
+
+ case packet: GenericCollisionMsg =>
+ logic.general.handleGenericCollision(packet)
+
+ case packet: BugReportMessage =>
+ logic.general.handleBugReport(packet)
+
+ case packet: BindPlayerMessage =>
+ logic.general.handleBindPlayer(packet)
+
+ case packet: PlanetsideAttributeMessage =>
+ logic.general.handlePlanetsideAttribute(packet)
+
+ case packet: FacilityBenefitShieldChargeRequestMessage =>
+ logic.general.handleFacilityBenefitShieldChargeRequest(packet)
+
+ case packet: BattleplanMessage =>
+ logic.general.handleBattleplan(packet)
+
+ case packet: CreateShortcutMessage =>
+ logic.general.handleCreateShortcut(packet)
+
+ case packet: ChangeShortcutBankMessage =>
+ logic.general.handleChangeShortcutBank(packet)
+
+ case packet: FriendsRequest =>
+ logic.general.handleFriendRequest(packet)
+
+ case packet: DroppodLaunchRequestMessage =>
+ data.zoning.handleDroppodLaunchRequest(packet)
+
+ case packet: InvalidTerrainMessage =>
+ logic.general.handleInvalidTerrain(packet)
+
+ case packet: ActionCancelMessage =>
+ logic.general.handleActionCancel(packet)
+
+ case packet: TradeMessage =>
+ logic.general.handleTrade(packet)
+
+ case packet: DisplayedAwardMessage =>
+ logic.general.handleDisplayedAward(packet)
+
+ case packet: ObjectDetectedMessage =>
+ logic.general.handleObjectDetected(packet)
+
+ case packet: TargetingImplantRequest =>
+ logic.general.handleTargetingImplantRequest(packet)
+
+ case packet: HitHint =>
+ logic.general.handleHitHint(packet)
+
+ case _: OutfitRequest => ()
+
+ case pkt =>
+ data.log.warn(s"Unhandled GamePacket $pkt")
+ }
}
diff --git a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala
index 0a84b1d3a..bdda54dab 100644
--- a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala
@@ -3,6 +3,8 @@ package net.psforever.actors.session.normal
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.support.AvatarHandlerFunctions
+import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
+import net.psforever.types.ImplantType
import scala.concurrent.duration._
//
@@ -155,6 +157,37 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
}
}
+ case AvatarResponse.AvatarImplant(ImplantAction.Add, implant_slot, value)
+ if value == ImplantType.SecondWind.value =>
+ sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Add, implant_slot, 7))
+ //second wind does not normally load its icon into the shortcut hotbar
+ avatar
+ .shortcuts
+ .zipWithIndex
+ .find { case (s, _) => s.isEmpty}
+ .foreach { case (_, index) =>
+ sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, Some(ImplantType.SecondWind.shortcut)))
+ }
+
+ case AvatarResponse.AvatarImplant(ImplantAction.Remove, implant_slot, value)
+ if value == ImplantType.SecondWind.value =>
+ sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Remove, implant_slot, value))
+ //second wind does not normally unload its icon from the shortcut hotbar
+ val shortcut = {
+ val imp = ImplantType.SecondWind.shortcut
+ net.psforever.objects.avatar.Shortcut(imp.code, imp.tile) //case class
+ }
+ avatar
+ .shortcuts
+ .zipWithIndex
+ .find { case (s, _) => s.contains(shortcut) }
+ .foreach { case (_, index) =>
+ sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, None))
+ }
+
+ case AvatarResponse.AvatarImplant(action, implant_slot, value) =>
+ sendResponse(AvatarImplantMessage(resolvedPlayerGuid, action, implant_slot, value))
+
case AvatarResponse.ObjectHeld(slot, _)
if isSameTarget && player.VisibleSlots.contains(slot) =>
sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
@@ -395,7 +428,7 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
avatarActor ! AvatarActor.AwardBep(bep, expType)
}
- case AvatarResponse.AwardCep(charId, cep) =>
+ case AvatarResponse.AwardCep(charId, cep) =>
//if the target player, always award (some) CEP
if (charId == player.CharId) {
avatarActor ! AvatarActor.AwardCep(cep)
@@ -469,7 +502,8 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
case AvatarResponse.Release(tplayer) if isNotSameTarget =>
sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer)
- case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid =>
+ case AvatarResponse.Revive(revivalTargetGuid)
+ if resolvedPlayerGuid == revivalTargetGuid =>
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
sessionLogic.zoning.spawn.reviveTimer.cancel()
sessionLogic.zoning.spawn.deadState = DeadState.Alive
diff --git a/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala
index f50d1e15b..f82492cf2 100644
--- a/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala
@@ -65,12 +65,13 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
- case GalaxyResponse.UnlockedZoneUpdate(zone) => ;
+ case GalaxyResponse.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
- val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR)
- val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC)
- val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS)
+ val pop = zone.LivePlayers.distinctBy(_.CharId)
+ val popTR = pop.count(_.Faction == PlanetSideEmpire.TR)
+ val popNC = pop.count(_.Faction == PlanetSideEmpire.NC)
+ val popVS = pop.count(_.Faction == PlanetSideEmpire.VS)
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
diff --git a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala
index 812bf74af..65881caf9 100644
--- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala
@@ -2,7 +2,7 @@
package net.psforever.actors.session.normal
import akka.actor.typed.scaladsl.adapter._
-import akka.actor.{ActorContext, typed}
+import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
import net.psforever.login.WorldSession.{CallBackForTask, ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
@@ -17,6 +17,7 @@ import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObject}
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.CaptureFlag
@@ -39,7 +40,7 @@ import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ObjectClass
-import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
+import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, Shortcut, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@@ -116,9 +117,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
}
ops.fallHeightTracker(pos.z)
- // if (isCrouching && !player.Crouching) {
- // //dev stuff goes here
- // }
+ if (isCrouching && !player.Crouching) {
+ //dev stuff goes here
+ sendResponse(CreateShortcutMessage(player.GUID, 2, Some(Shortcut.Implant("second_wind"))))
+ }
player.Position = pos
player.Velocity = vel
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
@@ -161,8 +163,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
case None => ()
}
val eagleEye: Boolean = ops.canSeeReallyFar
- val isNotVisible: Boolean = player.spectator ||
- sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing ||
+ val isNotVisible: Boolean = sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing ||
(player.isAlive && sessionLogic.zoning.spawn.deadState == DeadState.RespawnTime)
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
@@ -190,19 +191,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = {
- log.debug(s"$pkt")
- sendResponse(VoiceHostKill())
- sendResponse(
- ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
- )
+ ops.noVoicedChat(pkt)
}
def handleVoiceHostInfo(pkt: VoiceHostInfo): Unit = {
- log.debug(s"$pkt")
- sendResponse(VoiceHostKill())
- sendResponse(
- ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
- )
+ ops.noVoicedChat(pkt)
}
def handleEmote(pkt: EmoteMsg): Unit = {
@@ -269,7 +262,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
//travel along the zipline in the direction specified
sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos))
case 1 =>
- //disembark from zipline at destination!
+ //disembark from zipline at destination
sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos))
case 2 =>
//get off by force
@@ -572,11 +565,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
//aphelion_laser discharge (no target)
sessionLogic.shooting.HandleWeaponFireAccountability(objectGuid, PlanetSideGUID(Projectile.baseUID))
} else {
- sessionLogic.validObject(player.VehicleSeated, decorator = "GenericObjectAction/Vehicle") match {
- case Some(vehicle: Vehicle)
+ sessionLogic.validObject(player.VehicleSeated, decorator = "GenericObjectAction/Vehicle") collect {
+ case vehicle: Vehicle
if vehicle.OwnerName.contains(player.Name) =>
vehicle.Actor ! ServerObject.GenericObjectAction(objectGuid, code, Some(tool))
- case _ =>
}
}
case _ =>
@@ -724,7 +716,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
val (target1, target2, bailProtectStatus, velocity) = (ctype, sessionLogic.validObject(p, decorator = "GenericCollision/Primary")) match {
case (CollisionIs.OfInfantry, out @ Some(user: Player))
if user == player =>
- val bailStatus = session.flying || player.spectator || session.speed > 1f || player.BailProtection
+ val bailStatus = session.flying || session.speed > 1f || player.BailProtection
player.BailProtection = false
val v = if (player.avatar.implants.exists {
case Some(implant) => implant.definition.implantType == ImplantType.Surge && implant.active
@@ -946,6 +938,20 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
/* messages */
+ def handleRenewCharSavedTimer(): Unit = {
+ ops.renewCharSavedTimer(
+ Config.app.game.savedMsg.interruptedByAction.fixed,
+ Config.app.game.savedMsg.interruptedByAction.variable
+ )
+ }
+
+ def handleRenewCharSavedTimerMsg(): Unit = {
+ ops.displayCharSavedMsgThenRenewTimer(
+ Config.app.game.savedMsg.interruptedByAction.fixed,
+ Config.app.game.savedMsg.interruptedByAction.variable
+ )
+ }
+
def handleSetAvatar(avatar: Avatar): Unit = {
session = session.copy(avatar = avatar)
if (session.player != null) {
@@ -991,6 +997,18 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
player.silenced = isSilenced
}
+ def handleItemPutInSlot(msg: Containable.ItemPutInSlot): Unit = {
+ log.debug(s"ItemPutInSlot: $msg")
+ }
+
+ def handleCanNotPutItemInSlot(msg: Containable.CanNotPutItemInSlot): Unit = {
+ log.debug(s"CanNotPutItemInSlot: $msg")
+ }
+
+ def handleReceiveDefaultMessage(default: Any, sender: ActorRef): Unit = {
+ log.warn(s"Invalid packet class received: $default from $sender")
+ }
+
/* supporting functions */
private def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
diff --git a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala
index b638b0d12..8ddb6896b 100644
--- a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala
@@ -20,6 +20,16 @@ object LocalHandlerLogic {
class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: ActorContext) extends LocalHandlerFunctions {
def sessionLogic: SessionData = ops.sessionLogic
+ /* messages */
+
+ def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
+ ops.handleTurretDeployableIsDismissed(obj)
+ }
+
+ def handleDeployableIsDismissed(obj: Deployable): Unit = {
+ ops.handleDeployableIsDismissed(obj)
+ }
+
/* response handlers */
/**
diff --git a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala
index b4325d743..211cc48ef 100644
--- a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala
@@ -81,7 +81,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
v.isFlying =>
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
- case _ => ;
+ case _ => ()
}
case None =>
@@ -450,7 +450,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
val playerGuid: PlanetSideGUID = tplayer.GUID
val objGuid: PlanetSideGUID = obj.GUID
sessionLogic.actionsToCancel()
- avatarActor ! AvatarActor.DeactivateActiveImplants()
+ avatarActor ! AvatarActor.DeactivateActiveImplants
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
continent.VehicleEvents ! VehicleServiceMessage(
diff --git a/src/main/scala/net/psforever/actors/session/normal/NormalMode.scala b/src/main/scala/net/psforever/actors/session/normal/NormalMode.scala
index d6fd141e3..1a88bf9f3 100644
--- a/src/main/scala/net/psforever/actors/session/normal/NormalMode.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/NormalMode.scala
@@ -1,36 +1,8 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.normal
-import akka.actor.Actor.Receive
-import akka.actor.ActorRef
import net.psforever.actors.session.support.{ChatFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
-import net.psforever.objects.Players
-import net.psforever.packet.game.UplinkRequest
-import net.psforever.services.chat.ChatService
-//
-import net.psforever.actors.session.{AvatarActor, SessionActor}
-import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData, ZoningOperations}
-import net.psforever.objects.TurretDeployable
-import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
-import net.psforever.objects.serverobject.CommonMessages
-import net.psforever.objects.serverobject.containable.Containable
-import net.psforever.objects.serverobject.deploy.Deployment
-import net.psforever.objects.serverobject.mount.Mountable
-import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
-import net.psforever.objects.zones.Zone
-import net.psforever.packet.PlanetSideGamePacket
-import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, KeepAliveMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
-import net.psforever.services.{InterstellarClusterService => ICS}
-import net.psforever.services.CavernRotationService
-import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
-import net.psforever.services.ServiceManager.LookupResult
-import net.psforever.services.account.{PlayerToken, ReceiveAccountData}
-import net.psforever.services.avatar.AvatarServiceResponse
-import net.psforever.services.galaxy.GalaxyServiceResponse
-import net.psforever.services.local.LocalServiceResponse
-import net.psforever.services.teamwork.SquadServiceResponse
-import net.psforever.services.vehicle.VehicleServiceResponse
-import net.psforever.util.Config
+import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData}
class NormalModeLogic(data: SessionData) extends ModeLogic {
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
@@ -44,473 +16,6 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
val terminals: TerminalHandlerFunctions = TerminalHandlerLogic(data.terminals)
val vehicles: VehicleFunctions = VehicleLogic(data.vehicles)
val vehicleResponse: VehicleHandlerFunctions = VehicleHandlerLogic(data.vehicleResponseOperations)
-
- def parse(sender: ActorRef): Receive = {
- /* really common messages (very frequently, every life) */
- case packet: PlanetSideGamePacket =>
- handleGamePkt(packet)
-
- case AvatarServiceResponse(toChannel, guid, reply) =>
- avatarResponse.handle(toChannel, guid, reply)
-
- case GalaxyServiceResponse(_, reply) =>
- galaxy.handle(reply)
-
- case LocalServiceResponse(toChannel, guid, reply) =>
- local.handle(toChannel, guid, reply)
-
- case Mountable.MountMessages(tplayer, reply) =>
- mountResponse.handle(tplayer, reply)
-
- case SquadServiceResponse(_, excluded, response) =>
- squad.handle(response, excluded)
-
- case Terminal.TerminalMessage(tplayer, msg, order) =>
- terminals.handle(tplayer, msg, order)
-
- case VehicleServiceResponse(toChannel, guid, reply) =>
- vehicleResponse.handle(toChannel, guid, reply)
-
- case ChatService.MessageResponse(fromSession, message, _) =>
- chat.handleIncomingMessage(message, fromSession)
-
- case SessionActor.SendResponse(packet) =>
- data.sendResponse(packet)
-
- case SessionActor.CharSaved =>
- general.ops.renewCharSavedTimer(
- Config.app.game.savedMsg.interruptedByAction.fixed,
- Config.app.game.savedMsg.interruptedByAction.variable
- )
-
- case SessionActor.CharSavedMsg =>
- general.ops.displayCharSavedMsgThenRenewTimer(
- Config.app.game.savedMsg.renewal.fixed,
- Config.app.game.savedMsg.renewal.variable
- )
-
- /* common messages (maybe once every respawn) */
- case ICS.SpawnPointResponse(response) =>
- data.zoning.handleSpawnPointResponse(response)
-
- case SessionActor.NewPlayerLoaded(tplayer) =>
- data.zoning.spawn.handleNewPlayerLoaded(tplayer)
-
- case SessionActor.PlayerLoaded(tplayer) =>
- data.zoning.spawn.handlePlayerLoaded(tplayer)
-
- case Zone.Population.PlayerHasLeft(zone, None) =>
- data.log.debug(s"PlayerHasLeft: ${data.player.Name} does not have a body on ${zone.id}")
-
- case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
- if (tplayer.isAlive) {
- data.log.info(s"${tplayer.Name} has left zone ${zone.id}")
- }
-
- case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
- data.log.warn(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
-
- case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
- data.log.warn(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
-
- case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
- data.log.warn(
- s"${data.player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
- )
-
- case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
- data.log.warn(
- s"${data.player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
- )
-
- case ICS.ZoneResponse(Some(zone)) =>
- data.zoning.handleZoneResponse(zone)
-
- /* uncommon messages (once a session) */
- case ICS.ZonesResponse(zones) =>
- data.zoning.handleZonesResponse(zones)
-
- case SessionActor.SetAvatar(avatar) =>
- general.handleSetAvatar(avatar)
-
- case PlayerToken.LoginInfo(name, Zone.Nowhere, _) =>
- data.zoning.spawn.handleLoginInfoNowhere(name, sender)
-
- case PlayerToken.LoginInfo(name, inZone, optionalSavedData) =>
- data.zoning.spawn.handleLoginInfoSomewhere(name, inZone, optionalSavedData, sender)
-
- case PlayerToken.RestoreInfo(playerName, inZone, pos) =>
- data.zoning.spawn.handleLoginInfoRestore(playerName, inZone, pos, sender)
-
- case PlayerToken.CanNotLogin(playerName, reason) =>
- data.zoning.spawn.handleLoginCanNot(playerName, reason)
-
- case ReceiveAccountData(account) =>
- general.handleReceiveAccountData(account)
-
- case AvatarActor.AvatarResponse(avatar) =>
- general.handleAvatarResponse(avatar)
-
- case AvatarActor.AvatarLoginResponse(avatar) =>
- data.zoning.spawn.avatarLoginResponse(avatar)
-
- case SessionActor.SetCurrentAvatar(tplayer, max_attempts, attempt) =>
- data.zoning.spawn.ReadyToSetCurrentAvatar(tplayer, max_attempts, attempt)
-
- case SessionActor.SetConnectionState(state) =>
- data.connectionState = state
-
- case SessionActor.AvatarLoadingSync(state) =>
- data.zoning.spawn.handleAvatarLoadingSync(state)
-
- /* uncommon messages (utility, or once in a while) */
- case ZoningOperations.AvatarAwardMessageBundle(pkts, delay) =>
- data.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)
-
- case CommonMessages.ProgressEvent(delta, finishedAction, stepAction, tick) =>
- general.ops.handleProgressChange(delta, finishedAction, stepAction, tick)
-
- case CommonMessages.Progress(rate, finishedAction, stepAction) =>
- general.ops.setupProgressChange(rate, finishedAction, stepAction)
-
- case CavernRotationService.CavernRotationServiceKey.Listing(listings) =>
- listings.head ! SendCavernRotationUpdates(data.context.self)
-
- case LookupResult("propertyOverrideManager", endpoint) =>
- data.zoning.propertyOverrideManagerLoadOverrides(endpoint)
-
- case SessionActor.UpdateIgnoredPlayers(msg) =>
- galaxy.handleUpdateIgnoredPlayers(msg)
-
- case SessionActor.UseCooldownRenewed(definition, _) =>
- general.handleUseCooldownRenew(definition)
-
- case Deployment.CanDeploy(obj, state) =>
- vehicles.handleCanDeploy(obj, state)
-
- case Deployment.CanUndeploy(obj, state) =>
- vehicles.handleCanUndeploy(obj, state)
-
- case Deployment.CanNotChangeDeployment(obj, state, reason) =>
- vehicles.handleCanNotChangeDeployment(obj, state, reason)
-
- /* rare messages */
- case ProximityUnit.StopAction(term, _) =>
- terminals.ops.LocalStopUsingProximityUnit(term)
-
- case SessionActor.Suicide() =>
- general.ops.suicide(data.player)
-
- case SessionActor.Recall() =>
- data.zoning.handleRecall()
-
- case SessionActor.InstantAction() =>
- data.zoning.handleInstantAction()
-
- case SessionActor.Quit() =>
- data.zoning.handleQuit()
-
- case ICS.DroppodLaunchDenial(errorCode, _) =>
- data.zoning.handleDroppodLaunchDenial(errorCode)
-
- case ICS.DroppodLaunchConfirmation(zone, position) =>
- data.zoning.LoadZoneLaunchDroppod(zone, position)
-
- case SessionActor.PlayerFailedToLoad(tplayer) =>
- data.failWithError(s"${tplayer.Name} failed to load anywhere")
-
- /* csr only */
- case SessionActor.SetSpeed(speed) =>
- general.handleSetSpeed(speed)
-
- case SessionActor.SetFlying(isFlying) =>
- general.handleSetFlying(isFlying)
-
- case SessionActor.SetSpectator(isSpectator) =>
- general.handleSetSpectator(isSpectator)
-
- case SessionActor.Kick(player, time) =>
- general.handleKick(player, time)
-
- case SessionActor.SetZone(zoneId, position) =>
- data.zoning.handleSetZone(zoneId, position)
-
- case SessionActor.SetPosition(position) =>
- data.zoning.spawn.handleSetPosition(position)
-
- case SessionActor.SetSilenced(silenced) =>
- general.handleSilenced(silenced)
-
- /* catch these messages */
- case _: ProximityUnit.Action => ;
-
- case _: Zone.Vehicle.HasSpawned => ;
-
- case _: Zone.Vehicle.HasDespawned => ;
-
- case Zone.Deployable.IsDismissed(obj: TurretDeployable) => //only if target deployable was never fully introduced
- Players.buildCooldownReset(data.continent, data.player.Name, obj)
- TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(data.continent.GUID, obj))
-
- case Zone.Deployable.IsDismissed(obj) => //only if target deployable was never fully introduced
- Players.buildCooldownReset(data.continent, data.player.Name, obj)
- TaskWorkflow.execute(GUIDTask.unregisterObject(data.continent.GUID, obj))
-
- case msg: Containable.ItemPutInSlot =>
- data.log.debug(s"ItemPutInSlot: $msg")
-
- case msg: Containable.CanNotPutItemInSlot =>
- data.log.debug(s"CanNotPutItemInSlot: $msg")
-
- case default =>
- data.log.warn(s"Invalid packet class received: $default from $sender")
- }
-
- private def handleGamePkt: PlanetSideGamePacket => Unit = {
- case packet: ConnectToWorldRequestMessage =>
- general.handleConnectToWorldRequest(packet)
-
- case packet: MountVehicleCargoMsg =>
- mountResponse.handleMountVehicleCargo(packet)
-
- case packet: DismountVehicleCargoMsg =>
- mountResponse.handleDismountVehicleCargo(packet)
-
- case packet: CharacterCreateRequestMessage =>
- general.handleCharacterCreateRequest(packet)
-
- case packet: CharacterRequestMessage =>
- general.handleCharacterRequest(packet)
-
- case _: KeepAliveMessage =>
- data.keepAliveFunc()
-
- case packet: BeginZoningMessage =>
- data.zoning.handleBeginZoning(packet)
-
- case packet: PlayerStateMessageUpstream =>
- general.handlePlayerStateUpstream(packet)
-
- case packet: ChildObjectStateMessage =>
- vehicles.handleChildObjectState(packet)
-
- case packet: VehicleStateMessage =>
- vehicles.handleVehicleState(packet)
-
- case packet: VehicleSubStateMessage =>
- vehicles.handleVehicleSubState(packet)
-
- case packet: FrameVehicleStateMessage =>
- vehicles.handleFrameVehicleState(packet)
-
- case packet: ProjectileStateMessage =>
- shooting.handleProjectileState(packet)
-
- case packet: LongRangeProjectileInfoMessage =>
- shooting.handleLongRangeProjectileState(packet)
-
- case packet: ReleaseAvatarRequestMessage =>
- data.zoning.spawn.handleReleaseAvatarRequest(packet)
-
- case packet: SpawnRequestMessage =>
- data.zoning.spawn.handleSpawnRequest(packet)
-
- case packet: ChatMsg =>
- chat.handleChatMsg(packet)
-
- case packet: SetChatFilterMessage =>
- chat.handleChatFilter(packet)
-
- case packet: VoiceHostRequest =>
- general.handleVoiceHostRequest(packet)
-
- case packet: VoiceHostInfo =>
- general.handleVoiceHostInfo(packet)
-
- case packet: ChangeAmmoMessage =>
- shooting.handleChangeAmmo(packet)
-
- case packet: ChangeFireModeMessage =>
- shooting.handleChangeFireMode(packet)
-
- case packet: ChangeFireStateMessage_Start =>
- shooting.handleChangeFireStateStart(packet)
-
- case packet: ChangeFireStateMessage_Stop =>
- shooting.handleChangeFireStateStop(packet)
-
- case packet: EmoteMsg =>
- general.handleEmote(packet)
-
- case packet: DropItemMessage =>
- general.handleDropItem(packet)
-
- case packet: PickupItemMessage =>
- general.handlePickupItem(packet)
-
- case packet: ReloadMessage =>
- shooting.handleReload(packet)
-
- case packet: ObjectHeldMessage =>
- general.handleObjectHeld(packet)
-
- case packet: AvatarJumpMessage =>
- general.handleAvatarJump(packet)
-
- case packet: ZipLineMessage =>
- general.handleZipLine(packet)
-
- case packet: RequestDestroyMessage =>
- general.handleRequestDestroy(packet)
-
- case packet: MoveItemMessage =>
- general.handleMoveItem(packet)
-
- case packet: LootItemMessage =>
- general.handleLootItem(packet)
-
- case packet: AvatarImplantMessage =>
- general.handleAvatarImplant(packet)
-
- case packet: UseItemMessage =>
- general.handleUseItem(packet)
-
- case packet: UnuseItemMessage =>
- general.handleUnuseItem(packet)
-
- case packet: ProximityTerminalUseMessage =>
- terminals.handleProximityTerminalUse(packet)
-
- case packet: DeployObjectMessage =>
- general.handleDeployObject(packet)
-
- case packet: GenericObjectActionMessage =>
- general.handleGenericObjectAction(packet)
-
- case packet: GenericObjectActionAtPositionMessage =>
- general.handleGenericObjectActionAtPosition(packet)
-
- case packet: GenericObjectStateMsg =>
- general.handleGenericObjectState(packet)
-
- case packet: GenericActionMessage =>
- general.handleGenericAction(packet)
-
- case packet: ItemTransactionMessage =>
- terminals.handleItemTransaction(packet)
-
- case packet: FavoritesRequest =>
- terminals.handleFavoritesRequest(packet)
-
- case packet: WeaponDelayFireMessage =>
- shooting.handleWeaponDelayFire(packet)
-
- case packet: WeaponDryFireMessage =>
- shooting.handleWeaponDryFire(packet)
-
- case packet: WeaponFireMessage =>
- shooting.handleWeaponFire(packet)
-
- case packet: WeaponLazeTargetPositionMessage =>
- shooting.handleWeaponLazeTargetPosition(packet)
-
- case _: UplinkRequest => ()
-
- case packet: HitMessage =>
- shooting.handleDirectHit(packet)
-
- case packet: SplashHitMessage =>
- shooting.handleSplashHit(packet)
-
- case packet: LashMessage =>
- shooting.handleLashHit(packet)
-
- case packet: AIDamage =>
- shooting.handleAIDamage(packet)
-
- case packet: AvatarFirstTimeEventMessage =>
- general.handleAvatarFirstTimeEvent(packet)
-
- case packet: WarpgateRequest =>
- data.zoning.handleWarpgateRequest(packet)
-
- case packet: MountVehicleMsg =>
- mountResponse.handleMountVehicle(packet)
-
- case packet: DismountVehicleMsg =>
- mountResponse.handleDismountVehicle(packet)
-
- case packet: DeployRequestMessage =>
- vehicles.handleDeployRequest(packet)
-
- case packet: AvatarGrenadeStateMessage =>
- shooting.handleAvatarGrenadeState(packet)
-
- case packet: SquadDefinitionActionMessage =>
- squad.handleSquadDefinitionAction(packet)
-
- case packet: SquadMembershipRequest =>
- squad.handleSquadMemberRequest(packet)
-
- case packet: SquadWaypointRequest =>
- squad.handleSquadWaypointRequest(packet)
-
- case packet: GenericCollisionMsg =>
- general.handleGenericCollision(packet)
-
- case packet: BugReportMessage =>
- general.handleBugReport(packet)
-
- case packet: BindPlayerMessage =>
- general.handleBindPlayer(packet)
-
- case packet: PlanetsideAttributeMessage =>
- general.handlePlanetsideAttribute(packet)
-
- case packet: FacilityBenefitShieldChargeRequestMessage =>
- general.handleFacilityBenefitShieldChargeRequest(packet)
-
- case packet: BattleplanMessage =>
- general.handleBattleplan(packet)
-
- case packet: CreateShortcutMessage =>
- general.handleCreateShortcut(packet)
-
- case packet: ChangeShortcutBankMessage =>
- general.handleChangeShortcutBank(packet)
-
- case packet: FriendsRequest =>
- general.handleFriendRequest(packet)
-
- case packet: DroppodLaunchRequestMessage =>
- data.zoning.handleDroppodLaunchRequest(packet)
-
- case packet: InvalidTerrainMessage =>
- general.handleInvalidTerrain(packet)
-
- case packet: ActionCancelMessage =>
- general.handleActionCancel(packet)
-
- case packet: TradeMessage =>
- general.handleTrade(packet)
-
- case packet: DisplayedAwardMessage =>
- general.handleDisplayedAward(packet)
-
- case packet: ObjectDetectedMessage =>
- general.handleObjectDetected(packet)
-
- case packet: TargetingImplantRequest =>
- general.handleTargetingImplantRequest(packet)
-
- case packet: HitHint =>
- general.handleHitHint(packet)
-
- case _: OutfitRequest => ()
-
- case pkt =>
- data.log.warn(s"Unhandled GamePacket $pkt")
- }
}
case object NormalMode extends PlayerMode {
diff --git a/src/main/scala/net/psforever/actors/session/normal/TerminalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/TerminalHandlerLogic.scala
index 5b6618562..bf9eb1dc7 100644
--- a/src/main/scala/net/psforever/actors/session/normal/TerminalHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/TerminalHandlerLogic.scala
@@ -112,7 +112,7 @@ class TerminalHandlerLogic(val ops: SessionTerminalHandlers, implicit val contex
ops.lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, _, _)
- if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty || tplayer.spectator =>
+ if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty =>
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
ops.lastTerminalOrderFulfillment = true
diff --git a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala
index f6fe40c83..6b293ed75 100644
--- a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala
@@ -5,7 +5,7 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle, Vehicles}
+import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight
@@ -41,7 +41,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
is_decelerating,
is_cloaked
) = pkt
- GetVehicleAndSeat() match {
+ ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.persist()
@@ -100,7 +100,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
log.error(
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
)
- case _ => ;
+ case _ => ()
}
if (player.death_by == -1) {
sessionLogic.kickedByAdministration()
@@ -124,7 +124,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
unk9,
unkA
) = pkt
- GetVehicleAndSeat() match {
+ ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.persist()
@@ -198,7 +198,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
log.error(
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
)
- case _ => ;
+ case _ => ()
}
if (player.death_by == -1) {
sessionLogic.kickedByAdministration()
@@ -213,7 +213,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
case _ => (None, None)
}) match {
- case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ;
+ case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ()
case _ =>
sessionLogic.persist()
sessionLogic.turnCounterFunc(player.GUID)
@@ -241,33 +241,33 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
def handleVehicleSubState(pkt: VehicleSubStateMessage): Unit = {
val VehicleSubStateMessage(vehicle_guid, _, pos, ang, vel, unk1, _) = pkt
- sessionLogic.validObject(vehicle_guid, decorator = "VehicleSubState") match {
- case Some(obj: Vehicle) =>
- import net.psforever.login.WorldSession.boolToInt
- obj.Position = pos
- obj.Orientation = ang
- obj.Velocity = vel
- sessionLogic.updateBlockMap(obj, pos)
- obj.zoneInteractions()
- continent.VehicleEvents ! VehicleServiceMessage(
- continent.id,
- VehicleAction.VehicleState(
- player.GUID,
- vehicle_guid,
- unk1,
- pos,
- ang,
- obj.Velocity,
- obj.Flying,
- 0,
- 0,
- 15,
- unk5 = false,
- obj.Cloaked
+ sessionLogic.validObject(vehicle_guid, decorator = "VehicleSubState")
+ .collect {
+ case obj: Vehicle =>
+ import net.psforever.login.WorldSession.boolToInt
+ obj.Position = pos
+ obj.Orientation = ang
+ obj.Velocity = vel
+ sessionLogic.updateBlockMap(obj, pos)
+ obj.zoneInteractions()
+ continent.VehicleEvents ! VehicleServiceMessage(
+ continent.id,
+ VehicleAction.VehicleState(
+ player.GUID,
+ vehicle_guid,
+ unk1,
+ pos,
+ ang,
+ obj.Velocity,
+ obj.Flying,
+ 0,
+ 0,
+ 15,
+ unk5 = false,
+ obj.Cloaked
+ )
)
- )
- case _ => ()
- }
+ }
}
def handleDeployRequest(pkt: DeployRequestMessage): Unit = {
@@ -282,7 +282,6 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
log.warn(s"${player.Name} must be mounted as the driver to request a deployment change")
} else {
log.info(s"${player.Name} is requesting a deployment change for ${obj.Definition.Name} - $deploy_state")
- obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
continent.Transport ! Zone.Vehicle.TryDeploymentChange(obj, deploy_state)
}
obj
@@ -329,46 +328,6 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
/* support functions */
- /**
- * If the player is mounted in some entity, find that entity and get the mount index number at which the player is sat.
- * The priority of object confirmation is `direct` then `occupant.VehicleSeated`.
- * Once an object is found, the remainder are ignored.
- * @param direct a game object in which the player may be sat
- * @param occupant the player who is sat and may have specified the game object in which mounted
- * @return a tuple consisting of a vehicle reference and a mount index
- * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
- * `(None, None)`, otherwise (even if the vehicle can be determined)
- */
- private def GetMountableAndSeat(
- direct: Option[PlanetSideGameObject with Mountable],
- occupant: Player,
- zone: Zone
- ): (Option[PlanetSideGameObject with Mountable], Option[Int]) =
- direct.orElse(zone.GUID(occupant.VehicleSeated)) match {
- case Some(obj: PlanetSideGameObject with Mountable) =>
- obj.PassengerInSeat(occupant) match {
- case index @ Some(_) =>
- (Some(obj), index)
- case None =>
- (None, None)
- }
- case _ =>
- (None, None)
- }
-
- /**
- * If the player is seated in a vehicle, find that vehicle and get the mount index number at which the player is sat.
- * @see `GetMountableAndSeat`
- * @return a tuple consisting of a vehicle reference and a mount index
- * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
- * `(None, None)`, otherwise (even if the vehicle can be determined)
- */
- private def GetVehicleAndSeat(): (Option[Vehicle], Option[Int]) =
- GetMountableAndSeat(None, player, continent) match {
- case (Some(v: Vehicle), Some(seat)) => (Some(v), Some(seat))
- case _ => (None, None)
- }
-
/**
* Common reporting behavior when a `Deployment` object fails to properly transition between states.
* @param obj the game object that could not
diff --git a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
index 38f398828..05608922b 100644
--- a/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/normal/WeaponAndProjectileLogic.scala
@@ -88,6 +88,7 @@ object WeaponAndProjectileLogic {
Seq(start + endStart * (sqrt - b), start + endStart * (b + sqrt) * -1f)
}.filter(p => Vector3.DistanceSquared(start, p) <= a)
}
+
/**
* Preparation for explosion damage that utilizes the Scorpion's little buddy sub-projectiles.
* The main difference from "normal" server-side explosion
@@ -497,9 +498,7 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
}
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
//cleanup
- if (projectile.HasGUID) {
- continent.Projectile ! ZoneProjectile.Remove(projectile.GUID)
- }
+ continent.Projectile ! ZoneProjectile.Remove(projectile.GUID)
}
case None => ()
}
diff --git a/src/main/scala/net/psforever/actors/session/spectator/GalaxyHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/GalaxyHandlerLogic.scala
index 7ecff65b9..d3b6abf63 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/GalaxyHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/GalaxyHandlerLogic.scala
@@ -65,7 +65,7 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
- case GalaxyResponse.UnlockedZoneUpdate(zone) => ;
+ case GalaxyResponse.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR)
diff --git a/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala
index 2332b5115..806747a5a 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.spectator
-import akka.actor.{ActorContext, typed}
+import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
import net.psforever.objects.{Account, GlobalDefinitions, LivePlayerList, PlanetSideGameObject, Player, TelepadDeployable, Tool, Vehicle}
@@ -11,15 +11,16 @@ import net.psforever.objects.ce.{Deployable, TelepadLike}
import net.psforever.objects.definition.{BasicDefinition, KitDefinition, SpecialExoSuitDefinition}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.CommonMessages
+import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.zones.ZoneProjectile
import net.psforever.packet.PlanetSideGamePacket
-import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
+import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.account.AccountPersistenceService
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
-import net.psforever.types.{ChatMessageType, DriveState, ExoSuitType, PlanetSideGUID, Vector3}
+import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
import net.psforever.util.Config
object GeneralLogic {
@@ -79,19 +80,11 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = {
- log.debug(s"$pkt")
- sendResponse(VoiceHostKill())
- sendResponse(
- ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
- )
+ ops.noVoicedChat(pkt)
}
def handleVoiceHostInfo(pkt: VoiceHostInfo): Unit = {
- log.debug(s"$pkt")
- sendResponse(VoiceHostKill())
- sendResponse(
- ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
- )
+ ops.noVoicedChat(pkt)
}
def handleEmote(pkt: EmoteMsg): Unit = {
@@ -410,6 +403,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
/* messages */
+ def handleRenewCharSavedTimer(): Unit = { /* intentionally blank */ }
+
+ def handleRenewCharSavedTimerMsg(): Unit = { /* intentionally blank */ }
+
def handleSetAvatar(avatar: Avatar): Unit = {
session = session.copy(avatar = avatar)
if (session.player != null) {
@@ -455,6 +452,12 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
player.silenced = isSilenced
}
+ def handleItemPutInSlot(msg: Containable.ItemPutInSlot): Unit = { /* intentionally blank */ }
+
+ def handleCanNotPutItemInSlot(msg: Containable.CanNotPutItemInSlot): Unit = { /* intentionally blank */ }
+
+ def handleReceiveDefaultMessage(default: Any, sender: ActorRef): Unit = { /* intentionally blank */ }
+
/* supporting functions */
private def handleUseDoor(door: Door, equipment: Option[Equipment]): Unit = {
diff --git a/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala
index e0406f564..21d85d7a1 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/LocalHandlerLogic.scala
@@ -4,6 +4,7 @@ package net.psforever.actors.session.spectator
import akka.actor.ActorContext
import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers}
import net.psforever.objects.ce.Deployable
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
@@ -20,6 +21,16 @@ object LocalHandlerLogic {
class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: ActorContext) extends LocalHandlerFunctions {
def sessionLogic: SessionData = ops.sessionLogic
+ /* messages */
+
+ def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
+ TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
+ }
+
+ def handleDeployableIsDismissed(obj: Deployable): Unit = {
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
+ }
+
/* response handlers */
/**
diff --git a/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala
index f5a6caea8..f6c3a4a43 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala
@@ -66,7 +66,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
v.isFlying =>
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
- case _ => ;
+ case _ => ()
}
case None =>
diff --git a/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala b/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala
index 2f5647711..b950274db 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala
@@ -1,8 +1,6 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.spectator
-import akka.actor.Actor.Receive
-import akka.actor.ActorRef
import net.psforever.actors.session.support.{AvatarHandlerFunctions, ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.{BattleRank, CommandRank, DeployableToolbox, FirstTimeEvents, Implant, ProgressDecoration, Shortcut => AvatarShortcut}
@@ -14,32 +12,14 @@ import net.psforever.packet.game.{DeployableInfo, DeployableObjectsInfoMessage,
import net.psforever.packet.game.objectcreate.{ObjectClass, ObjectCreateMessageParent, RibbonBars}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
-import net.psforever.services.chat.{ChatService, SpectatorChannel}
+import net.psforever.services.chat.SpectatorChannel
import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage}
import net.psforever.types.{CapacitorStateType, ChatMessageType, ExoSuitType, MeritCommendation, SquadRequestType}
//
-import net.psforever.actors.session.{AvatarActor, SessionActor}
-import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData, ZoningOperations}
-import net.psforever.objects.TurretDeployable
-import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
-import net.psforever.objects.serverobject.CommonMessages
-import net.psforever.objects.serverobject.containable.Containable
-import net.psforever.objects.serverobject.deploy.Deployment
-import net.psforever.objects.serverobject.mount.Mountable
+import net.psforever.actors.session.AvatarActor
+import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData}
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
-import net.psforever.objects.zones.Zone
-import net.psforever.packet.PlanetSideGamePacket
-import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, KeepAliveMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
-import net.psforever.services.{InterstellarClusterService => ICS}
-import net.psforever.services.CavernRotationService
-import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
-import net.psforever.services.ServiceManager.LookupResult
-import net.psforever.services.account.{PlayerToken, ReceiveAccountData}
-import net.psforever.services.avatar.AvatarServiceResponse
-import net.psforever.services.galaxy.GalaxyServiceResponse
-import net.psforever.services.local.LocalServiceResponse
-import net.psforever.services.teamwork.SquadServiceResponse
-import net.psforever.services.vehicle.VehicleServiceResponse
+import net.psforever.packet.game.{ChatMsg, CreateShortcutMessage, UnuseItemMessage}
class SpectatorModeLogic(data: SessionData) extends ModeLogic {
val avatarResponse: AvatarHandlerFunctions = AvatarHandlerLogic(data.avatarResponse)
@@ -186,463 +166,6 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
zoning.zoneReload = true
zoning.spawn.randomRespawn(0.seconds) //to sanctuary
}
-
- def parse(sender: ActorRef): Receive = {
- /* really common messages (very frequently, every life) */
- case packet: PlanetSideGamePacket =>
- handleGamePkt(packet)
-
- case AvatarServiceResponse(toChannel, guid, reply) =>
- avatarResponse.handle(toChannel, guid, reply)
-
- case GalaxyServiceResponse(_, reply) =>
- galaxy.handle(reply)
-
- case LocalServiceResponse(toChannel, guid, reply) =>
- local.handle(toChannel, guid, reply)
-
- case Mountable.MountMessages(tplayer, reply) =>
- mountResponse.handle(tplayer, reply)
-
- case SquadServiceResponse(_, excluded, response) =>
- squad.handle(response, excluded)
-
- case Terminal.TerminalMessage(tplayer, msg, order) =>
- terminals.handle(tplayer, msg, order)
-
- case VehicleServiceResponse(toChannel, guid, reply) =>
- vehicleResponse.handle(toChannel, guid, reply)
-
- case ChatService.MessageResponse(fromSession, message, _) =>
- chat.handleIncomingMessage(message, fromSession)
-
- case SessionActor.SendResponse(packet) =>
- data.sendResponse(packet)
-
- case SessionActor.CharSaved => ()
-
- case SessionActor.CharSavedMsg => ()
-
- /* common messages (maybe once every respawn) */
- case ICS.SpawnPointResponse(response) =>
- data.zoning.handleSpawnPointResponse(response)
-
- case SessionActor.NewPlayerLoaded(tplayer) =>
- data.zoning.spawn.handleNewPlayerLoaded(tplayer)
-
- case SessionActor.PlayerLoaded(tplayer) =>
- data.zoning.spawn.handlePlayerLoaded(tplayer)
-
- case Zone.Population.PlayerHasLeft(zone, None) =>
- data.log.debug(s"PlayerHasLeft: ${data.player.Name} does not have a body on ${zone.id}")
-
- case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
- if (tplayer.isAlive) {
- data.log.info(s"${tplayer.Name} has left zone ${zone.id}")
- }
-
- case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
- data.log.warn(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
-
- case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
- data.log.warn(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
-
- case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
- data.log.warn(
- s"${data.player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
- )
-
- case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
- data.log.warn(
- s"${data.player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
- )
-
- case ICS.ZoneResponse(Some(zone)) =>
- data.zoning.handleZoneResponse(zone)
-
- /* uncommon messages (once a session) */
- case ICS.ZonesResponse(zones) =>
- data.zoning.handleZonesResponse(zones)
-
- case SessionActor.SetAvatar(avatar) =>
- general.handleSetAvatar(avatar)
-
- case PlayerToken.LoginInfo(name, Zone.Nowhere, _) =>
- data.zoning.spawn.handleLoginInfoNowhere(name, sender)
-
- case PlayerToken.LoginInfo(name, inZone, optionalSavedData) =>
- data.zoning.spawn.handleLoginInfoSomewhere(name, inZone, optionalSavedData, sender)
-
- case PlayerToken.RestoreInfo(playerName, inZone, pos) =>
- data.zoning.spawn.handleLoginInfoRestore(playerName, inZone, pos, sender)
-
- case PlayerToken.CanNotLogin(playerName, reason) =>
- data.zoning.spawn.handleLoginCanNot(playerName, reason)
-
- case ReceiveAccountData(account) =>
- general.handleReceiveAccountData(account)
-
- case AvatarActor.AvatarResponse(avatar) =>
- general.handleAvatarResponse(avatar)
-
- case AvatarActor.AvatarLoginResponse(avatar) =>
- data.zoning.spawn.avatarLoginResponse(avatar)
-
- case SessionActor.SetCurrentAvatar(tplayer, max_attempts, attempt) =>
- data.zoning.spawn.ReadyToSetCurrentAvatar(tplayer, max_attempts, attempt)
-
- case SessionActor.SetConnectionState(state) =>
- data.connectionState = state
-
- case SessionActor.AvatarLoadingSync(state) =>
- data.zoning.spawn.handleAvatarLoadingSync(state)
-
- /* uncommon messages (utility, or once in a while) */
- case ZoningOperations.AvatarAwardMessageBundle(pkts, delay) =>
- data.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)
-
- case CommonMessages.ProgressEvent(delta, finishedAction, stepAction, tick) =>
- general.ops.handleProgressChange(delta, finishedAction, stepAction, tick)
-
- case CommonMessages.Progress(rate, finishedAction, stepAction) =>
- general.ops.setupProgressChange(rate, finishedAction, stepAction)
-
- case CavernRotationService.CavernRotationServiceKey.Listing(listings) =>
- listings.head ! SendCavernRotationUpdates(data.context.self)
-
- case LookupResult("propertyOverrideManager", endpoint) =>
- data.zoning.propertyOverrideManagerLoadOverrides(endpoint)
-
- case SessionActor.UpdateIgnoredPlayers(msg) =>
- galaxy.handleUpdateIgnoredPlayers(msg)
-
- case SessionActor.UseCooldownRenewed(definition, _) =>
- general.handleUseCooldownRenew(definition)
-
- case Deployment.CanDeploy(obj, state) =>
- vehicles.handleCanDeploy(obj, state)
-
- case Deployment.CanUndeploy(obj, state) =>
- vehicles.handleCanUndeploy(obj, state)
-
- case Deployment.CanNotChangeDeployment(obj, state, reason) =>
- vehicles.handleCanNotChangeDeployment(obj, state, reason)
-
- /* rare messages */
- case ProximityUnit.StopAction(term, _) =>
- terminals.ops.LocalStopUsingProximityUnit(term)
-
- case SessionActor.Suicide() =>
- general.ops.suicide(data.player)
-
- case SessionActor.Recall() =>
- data.zoning.handleRecall()
-
- case SessionActor.InstantAction() =>
- data.zoning.handleInstantAction()
-
- case SessionActor.Quit() =>
- data.zoning.handleQuit()
-
- case ICS.DroppodLaunchDenial(errorCode, _) =>
- data.zoning.handleDroppodLaunchDenial(errorCode)
-
- case ICS.DroppodLaunchConfirmation(zone, position) =>
- data.zoning.LoadZoneLaunchDroppod(zone, position)
-
- case SessionActor.PlayerFailedToLoad(tplayer) =>
- data.failWithError(s"${tplayer.Name} failed to load anywhere")
-
- /* csr only */
- case SessionActor.SetSpeed(speed) =>
- general.handleSetSpeed(speed)
-
- case SessionActor.SetFlying(isFlying) =>
- general.handleSetFlying(isFlying)
-
- case SessionActor.SetSpectator(isSpectator) =>
- general.handleSetSpectator(isSpectator)
-
- case SessionActor.Kick(player, time) =>
- general.handleKick(player, time)
-
- case SessionActor.SetZone(zoneId, position) =>
- data.zoning.handleSetZone(zoneId, position)
-
- case SessionActor.SetPosition(position) =>
- data.zoning.spawn.handleSetPosition(position)
-
- case SessionActor.SetSilenced(silenced) =>
- general.handleSilenced(silenced)
-
- /* catch these messages */
- case _: ProximityUnit.Action => ;
-
- case _: Zone.Vehicle.HasSpawned => ;
-
- case _: Zone.Vehicle.HasDespawned => ;
-
- case Zone.Deployable.IsDismissed(obj: TurretDeployable) => //only if target deployable was never fully introduced
- TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(data.continent.GUID, obj))
-
- case Zone.Deployable.IsDismissed(obj) => //only if target deployable was never fully introduced
- TaskWorkflow.execute(GUIDTask.unregisterObject(data.continent.GUID, obj))
-
- case msg: Containable.ItemPutInSlot =>
- data.log.debug(s"ItemPutInSlot: $msg")
-
- case msg: Containable.CanNotPutItemInSlot =>
- data.log.debug(s"CanNotPutItemInSlot: $msg")
-
- case _ => ()
- }
-
- private def handleGamePkt: PlanetSideGamePacket => Unit = {
- case packet: ConnectToWorldRequestMessage =>
- general.handleConnectToWorldRequest(packet)
-
- case packet: MountVehicleCargoMsg =>
- mountResponse.handleMountVehicleCargo(packet)
-
- case packet: DismountVehicleCargoMsg =>
- mountResponse.handleDismountVehicleCargo(packet)
-
- case packet: CharacterCreateRequestMessage =>
- general.handleCharacterCreateRequest(packet)
-
- case packet: CharacterRequestMessage =>
- general.handleCharacterRequest(packet)
-
- case _: KeepAliveMessage =>
- data.keepAliveFunc()
-
- case packet: BeginZoningMessage =>
- data.zoning.handleBeginZoning(packet)
-
- case packet: PlayerStateMessageUpstream =>
- general.handlePlayerStateUpstream(packet)
-
- case packet: ChildObjectStateMessage =>
- vehicles.handleChildObjectState(packet)
-
- case packet: VehicleStateMessage =>
- vehicles.handleVehicleState(packet)
-
- case packet: VehicleSubStateMessage =>
- vehicles.handleVehicleSubState(packet)
-
- case packet: FrameVehicleStateMessage =>
- vehicles.handleFrameVehicleState(packet)
-
- case packet: ProjectileStateMessage =>
- shooting.handleProjectileState(packet)
-
- case packet: LongRangeProjectileInfoMessage =>
- shooting.handleLongRangeProjectileState(packet)
-
- case packet: ReleaseAvatarRequestMessage =>
- data.zoning.spawn.handleReleaseAvatarRequest(packet)
-
- case packet: SpawnRequestMessage =>
- data.zoning.spawn.handleSpawnRequest(packet)
-
- case packet: ChatMsg =>
- chat.handleChatMsg(packet)
-
- case packet: SetChatFilterMessage =>
- chat.handleChatFilter(packet)
-
- case packet: VoiceHostRequest =>
- general.handleVoiceHostRequest(packet)
-
- case packet: VoiceHostInfo =>
- general.handleVoiceHostInfo(packet)
-
- case packet: ChangeAmmoMessage =>
- shooting.handleChangeAmmo(packet)
-
- case packet: ChangeFireModeMessage =>
- shooting.handleChangeFireMode(packet)
-
- case packet: ChangeFireStateMessage_Start =>
- shooting.handleChangeFireStateStart(packet)
-
- case packet: ChangeFireStateMessage_Stop =>
- shooting.handleChangeFireStateStop(packet)
-
- case packet: EmoteMsg =>
- general.handleEmote(packet)
-
- case packet: DropItemMessage =>
- general.handleDropItem(packet)
-
- case packet: PickupItemMessage =>
- general.handlePickupItem(packet)
-
- case packet: ReloadMessage =>
- shooting.handleReload(packet)
-
- case packet: ObjectHeldMessage =>
- general.handleObjectHeld(packet)
-
- case packet: AvatarJumpMessage =>
- general.handleAvatarJump(packet)
-
- case packet: ZipLineMessage =>
- general.handleZipLine(packet)
-
- case packet: RequestDestroyMessage =>
- general.handleRequestDestroy(packet)
-
- case packet: MoveItemMessage =>
- general.handleMoveItem(packet)
-
- case packet: LootItemMessage =>
- general.handleLootItem(packet)
-
- case packet: AvatarImplantMessage =>
- general.handleAvatarImplant(packet)
-
- case packet: UseItemMessage =>
- general.handleUseItem(packet)
-
- case packet: UnuseItemMessage =>
- general.handleUnuseItem(packet)
-
- case packet: ProximityTerminalUseMessage =>
- terminals.handleProximityTerminalUse(packet)
-
- case packet: DeployObjectMessage =>
- general.handleDeployObject(packet)
-
- case packet: GenericObjectActionMessage =>
- general.handleGenericObjectAction(packet)
-
- case packet: GenericObjectActionAtPositionMessage =>
- general.handleGenericObjectActionAtPosition(packet)
-
- case packet: GenericObjectStateMsg =>
- general.handleGenericObjectState(packet)
-
- case packet: GenericActionMessage =>
- general.handleGenericAction(packet)
-
- case packet: ItemTransactionMessage =>
- terminals.handleItemTransaction(packet)
-
- case packet: FavoritesRequest =>
- terminals.handleFavoritesRequest(packet)
-
- case packet: WeaponDelayFireMessage =>
- shooting.handleWeaponDelayFire(packet)
-
- case packet: WeaponDryFireMessage =>
- shooting.handleWeaponDryFire(packet)
-
- case packet: WeaponFireMessage =>
- shooting.handleWeaponFire(packet)
-
- case packet: WeaponLazeTargetPositionMessage =>
- shooting.handleWeaponLazeTargetPosition(packet)
-
- case packet: UplinkRequest =>
- shooting.handleUplinkRequest(packet)
-
- case packet: HitMessage =>
- shooting.handleDirectHit(packet)
-
- case packet: SplashHitMessage =>
- shooting.handleSplashHit(packet)
-
- case packet: LashMessage =>
- shooting.handleLashHit(packet)
-
- case packet: AIDamage =>
- shooting.handleAIDamage(packet)
-
- case packet: AvatarFirstTimeEventMessage =>
- general.handleAvatarFirstTimeEvent(packet)
-
- case packet: WarpgateRequest =>
- data.zoning.handleWarpgateRequest(packet)
-
- case packet: MountVehicleMsg =>
- mountResponse.handleMountVehicle(packet)
-
- case packet: DismountVehicleMsg =>
- mountResponse.handleDismountVehicle(packet)
-
- case packet: DeployRequestMessage =>
- vehicles.handleDeployRequest(packet)
-
- case packet: AvatarGrenadeStateMessage =>
- shooting.handleAvatarGrenadeState(packet)
-
- case packet: SquadDefinitionActionMessage =>
- squad.handleSquadDefinitionAction(packet)
-
- case packet: SquadMembershipRequest =>
- squad.handleSquadMemberRequest(packet)
-
- case packet: SquadWaypointRequest =>
- squad.handleSquadWaypointRequest(packet)
-
- case packet: GenericCollisionMsg =>
- general.handleGenericCollision(packet)
-
- case packet: BugReportMessage =>
- general.handleBugReport(packet)
-
- case packet: BindPlayerMessage =>
- general.handleBindPlayer(packet)
-
- case packet: PlanetsideAttributeMessage =>
- general.handlePlanetsideAttribute(packet)
-
- case packet: FacilityBenefitShieldChargeRequestMessage =>
- general.handleFacilityBenefitShieldChargeRequest(packet)
-
- case packet: BattleplanMessage =>
- general.handleBattleplan(packet)
-
- case packet: CreateShortcutMessage =>
- general.handleCreateShortcut(packet)
-
- case packet: ChangeShortcutBankMessage =>
- general.handleChangeShortcutBank(packet)
-
- case packet: FriendsRequest =>
- general.handleFriendRequest(packet)
-
- case packet: DroppodLaunchRequestMessage =>
- data.zoning.handleDroppodLaunchRequest(packet)
-
- case packet: InvalidTerrainMessage =>
- general.handleInvalidTerrain(packet)
-
- case packet: ActionCancelMessage =>
- general.handleActionCancel(packet)
-
- case packet: TradeMessage =>
- general.handleTrade(packet)
-
- case packet: DisplayedAwardMessage =>
- general.handleDisplayedAward(packet)
-
- case packet: ObjectDetectedMessage =>
- general.handleObjectDetected(packet)
-
- case packet: TargetingImplantRequest =>
- general.handleTargetingImplantRequest(packet)
-
- case packet: HitHint =>
- general.handleHitHint(packet)
-
- case _: OutfitRequest => ()
-
- case pkt =>
- data.log.warn(s"Unhandled GamePacket $pkt")
- }
}
object SpectatorModeLogic {
diff --git a/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala
index 355c1fdc5..10fdb8ed6 100644
--- a/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala
+++ b/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala
@@ -5,11 +5,10 @@ import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, VehicleFunctions, VehicleOperations}
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle, Vehicles}
+import net.psforever.objects.{Vehicle, Vehicles}
import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.vehicles.control.BfrFlight
-import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{DriveState, Vector3}
@@ -41,7 +40,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
is_decelerating,
is_cloaked
) = pkt
- GetVehicleAndSeat() match {
+ ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.persist()
@@ -100,7 +99,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
log.error(
s"VehicleState: ${player.Name} should not be dispatching this kind of packet from vehicle ${vehicle_guid.guid} when not the driver (actually, seat $index)"
)
- case _ => ;
+ case _ => ()
}
if (player.death_by == -1) {
sessionLogic.kickedByAdministration()
@@ -124,7 +123,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
unk9,
unkA
) = pkt
- GetVehicleAndSeat() match {
+ ops.GetVehicleAndSeat() match {
case (Some(obj), Some(0)) =>
//we're driving the vehicle
sessionLogic.persist()
@@ -296,46 +295,6 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex
/* support functions */
- /**
- * If the player is mounted in some entity, find that entity and get the mount index number at which the player is sat.
- * The priority of object confirmation is `direct` then `occupant.VehicleSeated`.
- * Once an object is found, the remainder are ignored.
- * @param direct a game object in which the player may be sat
- * @param occupant the player who is sat and may have specified the game object in which mounted
- * @return a tuple consisting of a vehicle reference and a mount index
- * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
- * `(None, None)`, otherwise (even if the vehicle can be determined)
- */
- private def GetMountableAndSeat(
- direct: Option[PlanetSideGameObject with Mountable],
- occupant: Player,
- zone: Zone
- ): (Option[PlanetSideGameObject with Mountable], Option[Int]) =
- direct.orElse(zone.GUID(occupant.VehicleSeated)) match {
- case Some(obj: PlanetSideGameObject with Mountable) =>
- obj.PassengerInSeat(occupant) match {
- case index @ Some(_) =>
- (Some(obj), index)
- case None =>
- (None, None)
- }
- case _ =>
- (None, None)
- }
-
- /**
- * If the player is seated in a vehicle, find that vehicle and get the mount index number at which the player is sat.
- * @see `GetMountableAndSeat`
- * @return a tuple consisting of a vehicle reference and a mount index
- * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
- * `(None, None)`, otherwise (even if the vehicle can be determined)
- */
- private def GetVehicleAndSeat(): (Option[Vehicle], Option[Int]) =
- GetMountableAndSeat(None, player, continent) match {
- case (Some(v: Vehicle), Some(seat)) => (Some(v), Some(seat))
- case _ => (None, None)
- }
-
/**
* Common reporting behavior when a `Deployment` object fails to properly transition between states.
* @param obj the game object that could not
diff --git a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala
index 9ae7677a5..26d1db691 100644
--- a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala
@@ -2,6 +2,7 @@
package net.psforever.actors.session.support
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
+import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.sourcing.PlayerSource
import scala.collection.mutable
@@ -122,6 +123,10 @@ trait GeneralFunctions extends CommonSessionInterfacingFunctionality {
/* messages */
+ def handleRenewCharSavedTimer(): Unit
+
+ def handleRenewCharSavedTimerMsg(): Unit
+
def handleSetAvatar(avatar: Avatar): Unit
def handleReceiveAccountData(account: Account): Unit
@@ -139,6 +144,12 @@ trait GeneralFunctions extends CommonSessionInterfacingFunctionality {
def handleKick(player: Player, time: Option[Long]): Unit
def handleSilenced(isSilenced: Boolean): Unit
+
+ def handleItemPutInSlot(msg: Containable.ItemPutInSlot): Unit
+
+ def handleCanNotPutItemInSlot(msg: Containable.CanNotPutItemInSlot): Unit
+
+ def handleReceiveDefaultMessage(default: Any, sender: ActorRef): Unit
}
class GeneralOperations(
@@ -741,6 +752,14 @@ class GeneralOperations(
sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents=false, "", "@charsaved", None))
}
+ def noVoicedChat(pkt: PlanetSideGamePacket): Unit = {
+ log.debug(s"$pkt")
+ sendResponse(VoiceHostKill())
+ sendResponse(
+ ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
+ )
+ }
+
override protected[session] def actionsToCancel(): Unit = {
progressBarValue = None
kitToBeUsed = None
diff --git a/src/main/scala/net/psforever/actors/session/support/PlayerMode.scala b/src/main/scala/net/psforever/actors/session/support/PlayerMode.scala
index 6c5040057..46e7f52ed 100644
--- a/src/main/scala/net/psforever/actors/session/support/PlayerMode.scala
+++ b/src/main/scala/net/psforever/actors/session/support/PlayerMode.scala
@@ -1,8 +1,6 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.support
-import akka.actor.Actor.Receive
-import akka.actor.ActorRef
import net.psforever.objects.Session
trait ModeLogic {
@@ -21,8 +19,6 @@ trait ModeLogic {
def switchTo(session: Session): Unit = { /* to override */ }
def switchFrom(session: Session): Unit = { /* to override */ }
-
- def parse(sender: ActorRef): Receive
}
trait PlayerMode {
diff --git a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala
index df53c650e..456e3a237 100644
--- a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala
+++ b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala
@@ -2,16 +2,35 @@
package net.psforever.actors.session.support
import akka.actor.ActorContext
+import net.psforever.objects.{Players, TurretDeployable}
+import net.psforever.objects.ce.Deployable
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.services.local.LocalResponse
import net.psforever.types.PlanetSideGUID
trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality {
def ops: SessionLocalHandlers
+ def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit
+
+ def handleDeployableIsDismissed(obj: Deployable): Unit
+
def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit
}
class SessionLocalHandlers(
val sessionLogic: SessionData,
implicit val context: ActorContext
- ) extends CommonSessionInterfacingFunctionality
+ ) extends CommonSessionInterfacingFunctionality {
+
+
+ def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit = {
+ Players.buildCooldownReset(continent, player.Name, obj)
+ TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
+ }
+
+ def handleDeployableIsDismissed(obj: Deployable): Unit = {
+ Players.buildCooldownReset(continent, player.Name, obj)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
+ }
+}
diff --git a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala
index 470db3569..1ccb6bf3f 100644
--- a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala
+++ b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala
@@ -74,7 +74,7 @@ class SessionSquadHandlers(
sendResponse(
SquadDefinitionActionMessage(PlanetSideGUID(0), index, SquadAction.ListSquadFavorite(loadout.task))
)
- case (None, _) => ;
+ case (None, _) => ()
}
//non-squad GUID-0 counts as the settings when not joined with a squad
sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.IdentifyAsSquadLeader()))
diff --git a/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala
index 21cc9f9d6..347139e6a 100644
--- a/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala
+++ b/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala
@@ -68,6 +68,26 @@ class SessionTerminalHandlers(
)
}
+ /**
+ * Construct tasking that adds a completed and registered vehicle into the scene.
+ * Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
+ * @param vehicle the `Vehicle` object
+ * @see `RegisterVehicleFromSpawnPad`
+ * @return a `TaskBundle` message
+ */
+ def registerVehicle(vehicle: Vehicle): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ private val localVehicle = vehicle
+
+ override def description(): String = s"register a ${localVehicle.Definition.Name}"
+
+ def action(): Future[Any] = Future(true)
+ },
+ List(GUIDTask.registerVehicle(continent.GUID, vehicle))
+ )
+ }
+
/**
* na
* @param terminal na
@@ -188,26 +208,6 @@ class SessionTerminalHandlers(
}
}
- /**
- * Construct tasking that adds a completed and registered vehicle into the scene.
- * Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
- * @param vehicle the `Vehicle` object
- * @see `RegisterVehicleFromSpawnPad`
- * @return a `TaskBundle` message
- */
- def registerVehicle(vehicle: Vehicle): TaskBundle = {
- TaskBundle(
- new StraightforwardTask() {
- private val localVehicle = vehicle
-
- override def description(): String = s"register a ${localVehicle.Definition.Name}"
-
- def action(): Future[Any] = Future(true)
- },
- List(GUIDTask.registerVehicle(continent.GUID, vehicle))
- )
- }
-
override protected[session] def actionsToCancel(): Unit = {
lastTerminalOrderFulfillment = true
usingMedicalTerminal = None
diff --git a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala
index d56107506..f23c61d38 100644
--- a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala
@@ -66,6 +66,19 @@ class VehicleOperations(
(None, None)
}
+ /**
+ * If the player is seated in a vehicle, find that vehicle and get the mount index number at which the player is sat.
+ * @see `GetMountableAndSeat`
+ * @return a tuple consisting of a vehicle reference and a mount index
+ * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
+ * `(None, None)`, otherwise (even if the vehicle can be determined)
+ */
+ def GetVehicleAndSeat(): (Option[Vehicle], Option[Int]) =
+ GetMountableAndSeat(None, player, continent) match {
+ case (Some(v: Vehicle), Some(seat)) => (Some(v), Some(seat))
+ case _ => (None, None)
+ }
+
/**
* If the player is seated in a vehicle, find that vehicle and get the mount index number at which the player is sat.
*
@@ -86,19 +99,6 @@ class VehicleOperations(
case _ => (None, None)
}
- /**
- * If the player is seated in a vehicle, find that vehicle and get the mount index number at which the player is sat.
- * @see `GetMountableAndSeat`
- * @return a tuple consisting of a vehicle reference and a mount index
- * if and only if the vehicle is known to this client and the `WorldSessioNActor`-global `player` occupies it;
- * `(None, None)`, otherwise (even if the vehicle can be determined)
- */
- def GetVehicleAndSeat(): (Option[Vehicle], Option[Int]) =
- GetMountableAndSeat(None, player, continent) match {
- case (Some(v: Vehicle), Some(seat)) => (Some(v), Some(seat))
- case _ => (None, None)
- }
-
/**
* Place the current vehicle under the control of the driver's commands,
* but leave it in a cancellable auto-drive.
diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
index ca3f4a125..5981f3850 100644
--- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
+++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala
@@ -288,7 +288,7 @@ class ZoningOperations(
obj.Definition.DeployCategory == DeployableCategory.Sensors &&
!obj.Destroyed &&
(obj match {
- case jObj: JammableUnit => !jObj.Jammed;
+ case jObj: JammableUnit => !jObj.Jammed
case _ => true
})
)
@@ -449,7 +449,7 @@ class ZoningOperations(
if (vehicle.Shields > 0) {
sendResponse(PlanetsideAttributeMessage(vguid, vehicle.Definition.shieldUiAttribute, vehicle.Shields))
}
- case _ => ; //no vehicle
+ case _ => () //no vehicle
}
//vehicle wreckages
wreckages.foreach(vehicle => {
@@ -487,7 +487,7 @@ class ZoningOperations(
sendResponse(PlanetsideAttributeMessage(silo.GUID, 49, 1)) // silo orb particle effect
case Some(_: WarpGate) =>
sendResponse(PlanetsideAttributeMessage(obj.GUID, 49, 1)) // ant orb particle effect
- case _ => ;
+ case _ => ()
}
}
deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach { obj =>
@@ -518,7 +518,7 @@ class ZoningOperations(
objDef.Packet.ConstructorData(obj).get
)
)
- case _ => ;
+ case _ => ()
}
//mount terminal occupants
continent.GUID(terminal_guid) match {
@@ -534,9 +534,9 @@ class ZoningOperations(
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
)
- case _ => ;
+ case _ => ()
}
- case _ => ;
+ case _ => ()
}
})
//facility turrets
@@ -558,7 +558,7 @@ class ZoningOperations(
objDef.Packet.ConstructorData(obj).get
)
)
- case _ => ;
+ case _ => ()
}
}
//reserved ammunition?
@@ -575,7 +575,7 @@ class ZoningOperations(
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
)
- case _ => ;
+ case _ => ()
}
turret.Target.collect {
target =>
@@ -918,7 +918,7 @@ class ZoningOperations(
def beginZoningCountdown(runnable: Runnable): Unit = {
val descriptor = zoningType.toString.toLowerCase
if (zoningStatus == Zoning.Status.Request) {
- avatarActor ! AvatarActor.DeactivateActiveImplants()
+ avatarActor ! AvatarActor.DeactivateActiveImplants
zoningStatus = Zoning.Status.Countdown
val (time, origin) = ZoningStartInitialMessageAndTimer()
zoningCounter = time
@@ -1071,7 +1071,7 @@ class ZoningOperations(
)
)
}
- case _ => ;
+ case _ => ()
}
}
@@ -1092,7 +1092,7 @@ class ZoningOperations(
case Some(obj) if obj.Condition == PlanetSideGeneratorState.Destroyed || building.NtuLevel == 0 =>
sendResponse(PlanetsideAttributeMessage(guid, 48, 1)) //amenities disabled; red warning lights
sendResponse(PlanetsideAttributeMessage(guid, 38, 0)) //disable spawn target on deployment map
- case _ => ;
+ case _ => ()
}
// capitol force dome state
if (building.IsCapitol && building.ForceDomeActive) {
@@ -1177,7 +1177,7 @@ class ZoningOperations(
LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252))
)
}
- case _ => ;
+ case _ => ()
}
}
@@ -1495,7 +1495,7 @@ class ZoningOperations(
// remove owner
vehicle.Actor ! Vehicle.Ownership(None)
- case _ => ;
+ case _ => ()
}
avatarActor ! AvatarActor.SetVehicle(None)
}
@@ -1736,7 +1736,7 @@ class ZoningOperations(
case Success(overrides: List[Any]) =>
//safe to cast like this
sendResponse(PropertyOverrideMessage(overrides.map { _.asInstanceOf[PropertyOverrideMessage.GamePropertyScope] }))
- case _ => ;
+ case _ => ()
}
}
@@ -2047,8 +2047,6 @@ class ZoningOperations(
sessionLogic.persist = UpdatePersistenceAndRefs
tplayer.avatar = avatar
session = session.copy(player = tplayer)
- avatarActor ! AvatarActor.CreateImplants()
- avatarActor ! AvatarActor.InitializeImplants()
//LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar
val weaponsEnabled = !(mapName.equals("map11") || mapName.equals("map12") || mapName.equals("map13"))
sendResponse(LoadMapMessage(mapName, id, 40100, 25, weaponsEnabled, map.checksum))
@@ -2119,6 +2117,40 @@ class ZoningOperations(
}
}
+ def handlePlayerHasLeft(zone: Zone, playerOpt: Option[Player]): Unit = {
+ playerOpt match {
+ case None =>
+ log.debug(s"PlayerHasLeft: ${player.Name} does not have a body on ${zone.id}")
+ case Some(tplayer) if tplayer.isAlive =>
+ log.info(s"${tplayer.Name} has left zone ${zone.id}")
+ case _ => ()
+ }
+ }
+
+ def handlePlayerCanNotSpawn(zone: Zone, tplayer: Player): Unit = {
+ log.warn(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
+ }
+
+ def handlePlayerAlreadySpawned(zone: Zone, tplayer: Player): Unit = {
+ log.warn(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
+ }
+
+ def handleCanNotSpawn(zone: Zone, vehicle: Vehicle, reason: String): Unit = {
+ log.warn(
+ s"${player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
+ )
+ }
+
+ def handleCanNotDespawn(zone: Zone, vehicle: Vehicle, reason: String): Unit = {
+ log.warn(
+ s"${player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
+ )
+ }
+
+ def handlePlayerFailedToLoad(tplayer: Player): Unit = {
+ sessionLogic.failWithError(s"${tplayer.Name} failed to load anywhere")
+ }
+
/* support functions */
private def dropMedicalApplicators(p: Player): Unit = {
@@ -2230,7 +2262,7 @@ class ZoningOperations(
val armor = player.Armor
val events = continent.VehicleEvents
val zoneid = continent.id
- avatarActor ! AvatarActor.ResetImplants()
+ avatarActor ! AvatarActor.SoftResetImplants
player.Spawn()
if (health != 0) {
player.Health = health
@@ -2289,7 +2321,7 @@ class ZoningOperations(
carrierInfo match {
case (Some(carrier), Some((index, _))) =>
CargoMountBehaviorForUs(carrier, vehicle, index)
- case _ => ;
+ case _ => ()
}
data
}
@@ -2458,7 +2490,7 @@ class ZoningOperations(
// workaround to make sure player is spawned with full stamina
player.avatar = player.avatar.copy(stamina = avatar.maxStamina)
avatarActor ! AvatarActor.RestoreStamina(avatar.maxStamina)
- avatarActor ! AvatarActor.ResetImplants()
+ avatarActor ! AvatarActor.DeinitializeImplants
zones.exp.ToDatabase.reportRespawns(tplayer.CharId, ScoreCard.reviveCount(player.avatar.scorecard.CurrentLife))
val obj = Player.Respawn(tplayer)
DefinitionUtil.applyDefaultLoadout(obj)
@@ -2476,7 +2508,7 @@ class ZoningOperations(
def FriskDeadBody(obj: Player): Unit = {
if (!obj.isAlive) {
obj.Slot(4).Equipment match {
- case None => ;
+ case None => ()
case Some(knife) =>
RemoveOldEquipmentFromInventory(obj)(knife)
}
@@ -2485,7 +2517,7 @@ class ZoningOperations(
if (GlobalDefinitions.isMaxArms(arms.Definition)) {
RemoveOldEquipmentFromInventory(obj)(arms)
}
- case _ => ;
+ case _ => ()
}
//disown boomers and drop triggers
val boomers = avatar.deployables.ClearDeployable(DeployedItem.boomer)
@@ -2493,7 +2525,7 @@ class ZoningOperations(
continent.GUID(boomer) match {
case Some(obj: BoomerDeployable) =>
obj.Actor ! Deployable.Ownership(None)
- case Some(_) | None => ;
+ case Some(_) | None => ()
}
})
removeBoomerTriggersFromInventory().foreach(trigger => { sessionLogic.general.normalItemDrop(obj, continent)(trigger) })
@@ -2760,9 +2792,9 @@ class ZoningOperations(
// new player is spawning
val newPlayer = RespawnClone(player)
newPlayer.LogActivity(SpawningActivity(PlayerSource(newPlayer), toZoneNumber, toSpawnPoint))
- LoadZoneAsPlayUsing(newPlayer, pos, ori, toSide, zoneId)
+ LoadZoneAsPlayerUsing(newPlayer, pos, ori, toSide, zoneId)
} else {
- avatarActor ! AvatarActor.DeactivateActiveImplants()
+ avatarActor ! AvatarActor.DeactivateActiveImplants
val betterSpawnPoint = physSpawnPoint.collect { case o: PlanetSideGameObject with FactionAffinity with InGameHistory => o }
interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
case Some(vehicle: Vehicle) => // driver or passenger in vehicle using a warp gate, or a droppod
@@ -2779,11 +2811,11 @@ class ZoningOperations(
AvatarAction.ObjectDelete(player_guid, player_guid, 4)
)
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
- LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
+ LoadZoneAsPlayerUsing(player, pos, ori, toSide, zoneId)
case _ => //player is logging in
InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint)
- LoadZoneAsPlayUsing(player, pos, ori, toSide, zoneId)
+ LoadZoneAsPlayerUsing(player, pos, ori, toSide, zoneId)
}
}
}
@@ -2797,7 +2829,7 @@ class ZoningOperations(
* @param onThisSide description of the containing environment
* @param goingToZone common designation for the zone
*/
- private def LoadZoneAsPlayUsing(
+ private def LoadZoneAsPlayerUsing(
target: Player,
position: Vector3,
orientation: Vector3,
@@ -2924,9 +2956,9 @@ class ZoningOperations(
tplayer.Actor ! JammableUnit.ClearJammeredStatus()
tplayer.Actor ! JammableUnit.ClearJammeredSound()
}
+ avatarActor ! AvatarActor.SoftResetImplants
val originalDeadState = deadState
deadState = DeadState.Alive
- avatarActor ! AvatarActor.ResetImplants()
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
initializeShortcutsAndBank(guid, tavatar.shortcuts)
//Favorites lists
@@ -3035,7 +3067,7 @@ class ZoningOperations(
case (Some(vehicle), _) =>
//passenger
vehicle.Actor ! Vehicle.UpdateZoneInteractionProgressUI(tplayer)
- case _ => ;
+ case _ => ()
}
interstellarFerryTopLevelGUID = None
if (loadConfZone && sessionLogic.connectionState == 100) {
@@ -3329,7 +3361,7 @@ class ZoningOperations(
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
sessionLogic.general.accessContainer(vehicle)
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
- case _ => ;
+ case _ => ()
//we can't find a vehicle? and we're still here? that's bad
player.VehicleSeated = None
}
@@ -3517,7 +3549,7 @@ class ZoningOperations(
delay: Long
): Unit = {
messageBundles match {
- case Nil => ;
+ case Nil => ()
case x :: Nil =>
x.foreach {
sendResponse
@@ -3554,10 +3586,7 @@ class ZoningOperations(
def startDeconstructing(obj: SpawnTube): Unit = {
log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns")
- avatar.implants.collect {
- case Some(implant) if implant.active && !implant.definition.Passive =>
- avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
- }
+ avatarActor ! AvatarActor.DeactivateActiveImplants
if (player.ExoSuit != ExoSuitType.MAX) {
player.Actor ! PlayerControl.ObjectHeld(Player.HandsDownSlot, updateMyHolsterArm = true)
}
@@ -3581,10 +3610,11 @@ class ZoningOperations(
}
def randomRespawn(time: FiniteDuration = 300.seconds): Unit = {
+ val faction = player.Faction
reviveTimer = context.system.scheduler.scheduleOnce(time) {
cluster ! ICS.GetRandomSpawnPoint(
- Zones.sanctuaryZoneNumber(player.Faction),
- player.Faction,
+ Zones.sanctuaryZoneNumber(faction),
+ faction,
Seq(SpawnGroup.Sanctuary),
context.self
)
diff --git a/src/main/scala/net/psforever/objects/BoomerDeployable.scala b/src/main/scala/net/psforever/objects/BoomerDeployable.scala
index 612fbe112..1679bbe36 100644
--- a/src/main/scala/net/psforever/objects/BoomerDeployable.scala
+++ b/src/main/scala/net/psforever/objects/BoomerDeployable.scala
@@ -4,8 +4,10 @@ package net.psforever.objects
import akka.actor.{ActorContext, Props}
import net.psforever.objects.ce.{Deployable, DeployedItem}
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
+import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
+import net.psforever.objects.vital.Vitality
import net.psforever.objects.vital.etc.TriggerUsedReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.Zone
@@ -36,7 +38,8 @@ class BoomerDeployable(cdef: ExplosiveDeployableDefinition)
}
}
-class BoomerDeployableDefinition(private val objectId: Int) extends ExplosiveDeployableDefinition(objectId) {
+class BoomerDeployableDefinition(private val objectId: Int)
+ extends ExplosiveDeployableDefinition(objectId) {
override def Initialize(obj: Deployable, context: ActorContext): Unit = {
obj.Actor =
context.actorOf(Props(classOf[BoomerDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
@@ -58,8 +61,7 @@ class BoomerDeployableControl(mine: BoomerDeployable)
case CommonMessages.Use(player, Some(trigger: BoomerTrigger)) if mine.Trigger.contains(trigger) =>
// the trigger damages the mine, which sets it off, which causes an explosion
// think of this as an initiator to the proper explosion
- mine.Destroyed = true
- ExplosiveDeployableControl.DamageResolution(
+ HandleDamage(
mine,
DamageInteraction(
SourceEntry(mine),
@@ -68,8 +70,7 @@ class BoomerDeployableControl(mine: BoomerDeployable)
).calculate()(mine),
damage = 0
)
-
- case _ => ;
+ case _ => ()
}
def loseOwnership(@unused faction: PlanetSideEmpire.Value): Unit = {
@@ -97,14 +98,27 @@ class BoomerDeployableControl(mine: BoomerDeployable)
container.Slot(index).Equipment = None
case Some(Zone.EquipmentIs.OnGround()) =>
zone.Ground ! Zone.Ground.RemoveItem(guid)
- case _ => ;
+ case _ => ()
}
zone.AvatarEvents! AvatarServiceMessage(
zone.id,
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid)
)
TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, trigger))
- case None => ;
+ case None => ()
}
}
+
+ /**
+ * Boomers are not bothered by explosive sympathy
+ * but can still be affected by sources of jammering.
+ * @param obj the entity being damaged
+ * @param damage the amount of damage
+ * @param data historical information about the damage
+ * @return `true`, if the target can be affected;
+ * `false`, otherwise
+ */
+ override def CanDetonate(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
+ super.CanDetonate(obj, damage, data) || data.cause.isInstanceOf[TriggerUsedReason]
+ }
}
diff --git a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
index 928abb746..b32bf2e4d 100644
--- a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
+++ b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2018 PSForever
package net.psforever.objects
-import akka.actor.{Actor, ActorContext, ActorRef, Props}
+import akka.actor.Actor
import net.psforever.objects.ce._
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallDeployableConverter
@@ -11,8 +11,6 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity}
import net.psforever.objects.serverobject.damage.Damageable.Target
-import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, SourceEntry}
-import net.psforever.objects.vital.etc.TrippedMineReason
import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
import net.psforever.objects.vital.{SimpleResolutions, Vitality}
import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult}
@@ -23,6 +21,7 @@ import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
+import scala.annotation.unused
import scala.concurrent.duration._
class ExplosiveDeployable(cdef: ExplosiveDeployableDefinition)
@@ -36,7 +35,7 @@ object ExplosiveDeployable {
final case class TriggeredBy(obj: PlanetSideServerObject)
}
-class ExplosiveDeployableDefinition(private val objectId: Int)
+abstract class ExplosiveDeployableDefinition(private val objectId: Int)
extends DeployableDefinition(objectId) {
Name = "explosive_deployable"
DeployCategory = DeployableCategory.Mines
@@ -45,6 +44,8 @@ class ExplosiveDeployableDefinition(private val objectId: Int)
private var detonateOnJamming: Boolean = true
+ private var stability: Boolean = false
+
var triggerRadius: Float = 0f
def DetonateOnJamming: Boolean = detonateOnJamming
@@ -54,15 +55,11 @@ class ExplosiveDeployableDefinition(private val objectId: Int)
DetonateOnJamming
}
- override def Initialize(obj: Deployable, context: ActorContext): Unit = {
- obj.Actor =
- context.actorOf(Props(classOf[MineDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
- }
-}
+ def Stable: Boolean = stability
-object ExplosiveDeployableDefinition {
- def apply(dtype: DeployedItem.Value): ExplosiveDeployableDefinition = {
- new ExplosiveDeployableDefinition(dtype.id)
+ def Stable_=(stableState: Boolean): Boolean = {
+ stability = stableState
+ Stable
}
}
@@ -90,14 +87,36 @@ abstract class ExplosiveDeployableControl(mine: ExplosiveDeployable)
val originalHealth = mine.Health
val cause = applyDamageTo(mine)
val damage = originalHealth - mine.Health
- if (CanDetonate(mine, damage, cause.interaction)) {
- ExplosiveDeployableControl.DamageResolution(mine, cause, damage)
+ if (Interaction(mine, damage, cause.interaction)) {
+ HandleDamage(mine, cause, damage)
} else {
mine.Health = originalHealth
}
}
}
+ final def HandleDamage(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
+ target.LogActivity(cause)
+ if (CanDetonate(target, damage, cause.interaction)) {
+ ExplosiveDeployableControl.doExplosion(target, cause)
+ } else if (target.Health == 0) {
+ ExplosiveDeployableControl.DestructionAwareness(target, cause)
+ } else {
+ ExplosiveDeployableControl.DamageAwareness(target, cause, damage)
+ }
+ }
+
+ def Interaction(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
+ val actualDamage: Int = if (!mine.Definition.Stable && data.cause.source.SympatheticExplosion) {
+ math.max(damage, 1)
+ } else {
+ damage
+ }
+ !mine.Destroyed &&
+ Damageable.adversarialOrHackableChecks(obj, data) &&
+ (CanDetonate(obj, actualDamage, data) || Damageable.CanDamage(obj, actualDamage, data))
+ }
+
/**
* A supplement for checking target susceptibility
* to account for sympathetic explosives even if there is no damage.
@@ -110,45 +129,46 @@ abstract class ExplosiveDeployableControl(mine: ExplosiveDeployable)
* @return `true`, if the target can be affected;
* `false`, otherwise
*/
- def CanDetonate(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
- !mine.Destroyed && (if (damage == 0 && data.cause.source.SympatheticExplosion) {
- Damageable.CanDamageOrJammer(mine, damage = 1, data)
- } else {
- Damageable.CanDamageOrJammer(mine, damage, data)
- })
+ def CanDetonate(obj: Vitality with FactionAffinity, @unused damage: Int, data: DamageInteraction): Boolean = {
+ val sourceDef = data.cause.source
+ val mineDef = mine.Definition
+ val explodeFromSympathy: Boolean = sourceDef.SympatheticExplosion && !mineDef.Stable
+ val explodeFromJammer: Boolean = ExplosiveDeployableControl.CanJammer(mine, data)
+ !mine.Destroyed && (explodeFromSympathy || explodeFromJammer)
}
}
object ExplosiveDeployableControl {
+ def CanJammer(mine: ExplosiveDeployable, data: DamageInteraction): Boolean = {
+ Damageable.adversarialOrHackableChecks(mine, data) &&
+ data.cause.source.AdditionalEffect &&
+ mine.Definition.DetonateOnJamming
+ }
+
/**
* na
* @param target na
* @param cause na
* @param damage na
*/
- def DamageResolution(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
- target.LogActivity(cause)
- if (cause.interaction.cause.source.SympatheticExplosion) {
- explodes(target, cause)
- DestructionAwareness(target, cause)
- } else if (target.Health == 0) {
- DestructionAwareness(target, cause)
- } else if (!target.Jammed && Damageable.CanJammer(target, cause.interaction)) {
- if ( {
- target.Jammed = cause.interaction.cause match {
- case o: ProjectileReason =>
- val radius = o.projectile.profile.DamageRadius
- Vector3.DistanceSquared(cause.interaction.hitPos, cause.interaction.target.Position) < radius * radius
- case _ =>
- true
+ def DamageAwareness(target: ExplosiveDeployable, cause: DamageResult, damage: Int): Unit = {
+ if (
+ !target.Jammed &&
+ CanJammer(target, cause.interaction) &&
+ {
+ target.Jammed = cause.interaction.cause match {
+ case o: ProjectileReason =>
+ val radius = o.projectile.profile.DamageRadius
+ Vector3.DistanceSquared(cause.interaction.hitPos, cause.interaction.target.Position) < radius * radius
+ case _ =>
+ true
+ }
}
+ ) {
+ if (target.Definition.DetonateOnJamming) {
+ explodes(target, cause)
}
- ) {
- if (target.Definition.DetonateOnJamming) {
- explodes(target, cause)
- }
- DestructionAwareness(target, cause)
- }
+ DestructionAwareness(target, cause)
}
}
@@ -158,6 +178,7 @@ object ExplosiveDeployableControl {
* @param cause na
*/
def explodes(target: Damageable.Target, cause: DamageResult): Unit = {
+ target.Destroyed = true
target.Health = 1 // short-circuit logic in DestructionAwareness
val zone = target.Zone
zone.Activity ! Zone.HotSpot.Activity(cause)
@@ -170,6 +191,11 @@ object ExplosiveDeployableControl {
)
}
+ def doExplosion(target: ExplosiveDeployable, cause: DamageResult): Unit = {
+ explodes(target, cause)
+ DestructionAwareness(target, cause)
+ }
+
/**
* na
* @param target na
@@ -252,109 +278,3 @@ object ExplosiveDeployableControl {
) <= maxDistance
}
}
-
-class MineDeployableControl(mine: ExplosiveDeployable)
- extends ExplosiveDeployableControl(mine) {
-
- def receive: Receive =
- commonMineBehavior
- .orElse {
- case ExplosiveDeployable.TriggeredBy(obj) =>
- setTriggered(Some(obj), delay = 200)
-
- case MineDeployableControl.Triggered() =>
- explodes(testForTriggeringTarget(
- mine,
- mine.Definition.innateDamage.map { _.DamageRadius }.getOrElse(mine.Definition.triggerRadius)
- ))
-
- case _ => ;
- }
-
- override def finalizeDeployable(callback: ActorRef): Unit = {
- super.finalizeDeployable(callback)
- //initial triggering upon build
- setTriggered(testForTriggeringTarget(mine, mine.Definition.triggerRadius), delay = 1000)
- }
-
- def testForTriggeringTarget(mine: ExplosiveDeployable, range: Float): Option[PlanetSideServerObject] = {
- val position = mine.Position
- val faction = mine.Faction
- val range2 = range * range
- val sector = mine.Zone.blockMap.sector(position, range)
- (sector.livePlayerList ++ sector.vehicleList)
- .find { thing => thing.Faction != faction && Vector3.DistanceSquared(thing.Position, position) < range2 }
- }
-
- def setTriggered(instigator: Option[PlanetSideServerObject], delay: Long): Unit = {
- instigator match {
- case Some(_) if isConstructed.contains(true) && setup.isCancelled =>
- //re-use the setup timer here
- import scala.concurrent.ExecutionContext.Implicits.global
- setup = context.system.scheduler.scheduleOnce(delay milliseconds, self, MineDeployableControl.Triggered())
- case _ => ;
- }
- }
-
- def explodes(instigator: Option[PlanetSideServerObject]): Unit = {
- instigator match {
- case Some(_) =>
- //explosion
- mine.Destroyed = true
- ExplosiveDeployableControl.DamageResolution(
- mine,
- DamageInteraction(
- SourceEntry(mine),
- MineDeployableControl.trippedMineReason(mine),
- mine.Position
- ).calculate()(mine),
- damage = 0
- )
- case None =>
- //reset
- setup = Default.Cancellable
- }
- }
-}
-
-object MineDeployableControl {
- private case class Triggered()
-
- def trippedMineReason(mine: ExplosiveDeployable): TrippedMineReason = {
- lazy val deployableSource = DeployableSource(mine)
- val zone = mine.Zone
- val ownerName = mine.OwnerName
- val blame = zone
- .Players
- .find(a => ownerName.contains(a.name))
- .collect { a =>
- val name = a.name
- assignBlameToFrom(name, zone.LivePlayers)
- .orElse(assignBlameToFrom(name, zone.Corpses))
- .getOrElse {
- val player = PlayerSource(name, mine.Faction, mine.Position) //might report minor inconsistencies, e.g., exo-suit type
- player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife)
- }
- }
- .getOrElse(deployableSource)
- TrippedMineReason(deployableSource, blame)
- }
-
- /**
- * Find a player with a given name from this list of possible players.
- * If the player is seated, attach a shallow copy of the mounting information.
- * @param name player name
- * @param blameList possible players in which to find the player name
- * @return discovered player as a reference, or `None` if not found
- */
- private def assignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = {
- blameList
- .find(_.Name.equals(name))
- .map { player =>
- PlayerSource
- .mountableAndSeat(player)
- .map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) }
- .getOrElse { PlayerSource(player) }
- }
- }
-}
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 778f9abeb..d9297e6c7 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -1019,9 +1019,9 @@ object GlobalDefinitions {
*/
val boomer: BoomerDeployableDefinition = BoomerDeployableDefinition(DeployedItem.boomer)
- val he_mine: ExplosiveDeployableDefinition = ExplosiveDeployableDefinition(DeployedItem.he_mine)
+ val he_mine: MineDeployableDefinition = MineDeployableDefinition(DeployedItem.he_mine)
- val jammer_mine: ExplosiveDeployableDefinition = ExplosiveDeployableDefinition(DeployedItem.jammer_mine)
+ val jammer_mine: MineDeployableDefinition = MineDeployableDefinition(DeployedItem.jammer_mine)
val spitfire_turret: TurretDeployableDefinition = TurretDeployableDefinition(DeployedItem.spitfire_turret)
diff --git a/src/main/scala/net/psforever/objects/MineDeployableControl.scala b/src/main/scala/net/psforever/objects/MineDeployableControl.scala
new file mode 100644
index 000000000..3b393378a
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/MineDeployableControl.scala
@@ -0,0 +1,137 @@
+// Copyright (c) 2024 PSForever
+package net.psforever.objects
+
+import akka.actor.{ActorContext, ActorRef, Props}
+import net.psforever.objects.ce.{Deployable, DeployedItem}
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, SourceEntry}
+import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.etc.TrippedMineReason
+import net.psforever.objects.vital.interaction.DamageInteraction
+import net.psforever.types.Vector3
+
+import scala.concurrent.duration._
+
+class MineDeployableDefinition(private val objectId: Int)
+ extends ExplosiveDeployableDefinition(objectId) {
+ override def Initialize(obj: Deployable, context: ActorContext): Unit = {
+ obj.Actor =
+ context.actorOf(Props(classOf[MineDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
+ }
+}
+
+object MineDeployableDefinition {
+ def apply(dtype: DeployedItem.Value): MineDeployableDefinition = {
+ new MineDeployableDefinition(dtype.id)
+ }
+}
+
+class MineDeployableControl(mine: ExplosiveDeployable)
+ extends ExplosiveDeployableControl(mine) {
+
+ def receive: Receive =
+ commonMineBehavior
+ .orElse {
+ case ExplosiveDeployable.TriggeredBy(obj) =>
+ setTriggered(Some(obj), delay = 200)
+
+ case MineDeployableControl.Triggered() =>
+ explodes(testForTriggeringTarget(
+ mine,
+ mine.Definition.innateDamage.map { _.DamageRadius }.getOrElse(mine.Definition.triggerRadius)
+ ))
+
+ case _ => ()
+ }
+
+ override def finalizeDeployable(callback: ActorRef): Unit = {
+ super.finalizeDeployable(callback)
+ //initial triggering upon build
+ setTriggered(testForTriggeringTarget(mine, mine.Definition.triggerRadius), delay = 1000)
+ }
+
+ def testForTriggeringTarget(mine: ExplosiveDeployable, range: Float): Option[PlanetSideServerObject] = {
+ val position = mine.Position
+ val faction = mine.Faction
+ val range2 = range * range
+ val sector = mine.Zone.blockMap.sector(position, range)
+ (sector.livePlayerList ++ sector.vehicleList)
+ .find { thing => thing.Faction != faction && Vector3.DistanceSquared(thing.Position, position) < range2 }
+ }
+
+ def setTriggered(instigator: Option[PlanetSideServerObject], delay: Long): Unit = {
+ instigator
+ .collect {
+ case _ if isConstructed.contains(true) && setup.isCancelled =>
+ //re-use the setup timer here
+ import scala.concurrent.ExecutionContext.Implicits.global
+ setup = context.system.scheduler.scheduleOnce(delay milliseconds, self, MineDeployableControl.Triggered())
+ }
+ }
+
+ override def CanDetonate(obj: Vitality with FactionAffinity, damage: Int, data: DamageInteraction): Boolean = {
+ super.CanDetonate(obj, damage, data) || data.cause.isInstanceOf[TrippedMineReason]
+ }
+
+ def explodes(instigator: Option[PlanetSideServerObject]): Unit = {
+ //reset
+ setup = Default.Cancellable
+ instigator
+ .collect {
+ case _ =>
+ //explosion
+ HandleDamage(
+ mine,
+ DamageInteraction(
+ SourceEntry(mine),
+ MineDeployableControl.trippedMineReason(mine),
+ mine.Position
+ ).calculate()(mine),
+ damage = 0
+ )
+ }
+ }
+}
+
+object MineDeployableControl {
+ private case class Triggered()
+
+ def trippedMineReason(mine: ExplosiveDeployable): TrippedMineReason = {
+ lazy val deployableSource = DeployableSource(mine)
+ val zone = mine.Zone
+ val ownerName = mine.OwnerName
+ val blame = zone
+ .Players
+ .find(a => ownerName.contains(a.name))
+ .collect { a =>
+ val name = a.name
+ assignBlameToFrom(name, zone.LivePlayers)
+ .orElse(assignBlameToFrom(name, zone.Corpses))
+ .getOrElse {
+ val player = PlayerSource(name, mine.Faction, mine.Position) //might report minor inconsistencies, e.g., exo-suit type
+ player.copy(unique = player.unique.copy(charId = a.id), progress = a.scorecard.CurrentLife)
+ }
+ }
+ .getOrElse(deployableSource)
+ TrippedMineReason(deployableSource, blame)
+ }
+
+ /**
+ * Find a player with a given name from this list of possible players.
+ * If the player is seated, attach a shallow copy of the mounting information.
+ * @param name player name
+ * @param blameList possible players in which to find the player name
+ * @return discovered player as a reference, or `None` if not found
+ */
+ private def assignBlameToFrom(name: String, blameList: List[Player]): Option[SourceEntry] = {
+ blameList
+ .find(_.Name.equals(name))
+ .map { player =>
+ PlayerSource
+ .mountableAndSeat(player)
+ .map { case (mount, seat) => PlayerSource.inSeat(player, mount, seat) }
+ .getOrElse { PlayerSource(player) }
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/avatar/Implant.scala b/src/main/scala/net/psforever/objects/avatar/Implant.scala
index d42c15996..90b338928 100644
--- a/src/main/scala/net/psforever/objects/avatar/Implant.scala
+++ b/src/main/scala/net/psforever/objects/avatar/Implant.scala
@@ -6,11 +6,19 @@ import net.psforever.packet.game.objectcreate.ImplantEntry
case class Implant(
definition: ImplantDefinition,
active: Boolean = false,
- initialized: Boolean = false
- //initializationTime: FiniteDuration
+ initialized: Boolean = false,
+ timer: Long = 0L
) {
def toEntry: ImplantEntry = {
- // TODO initialization time?
- new ImplantEntry(definition.implantType, None, active)
+ val initState = if (!initialized) {
+ if (timer > 0) {
+ Some(math.max(0, ((timer - System.currentTimeMillis()) / 1000L).toInt))
+ } else {
+ Some(definition.InitializationDuration.toInt)
+ }
+ } else {
+ None
+ }
+ new ImplantEntry(definition.implantType, initState, active)
}
}
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index c2ee863c9..4245c7688 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -476,7 +476,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
Deployables.initializeConstructionItem(player.avatar.certifications, citem)
}
//deactivate non-passive implants
- avatarActor ! AvatarActor.DeactivateActiveImplants()
+ avatarActor ! AvatarActor.DeactivateActiveImplants
val zone = player.Zone
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
@@ -659,7 +659,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
afterHolsters.foreach(elem => player.Slot(elem.start).Equipment = elem.obj)
afterInventory.foreach(elem => player.Inventory.InsertQuickly(elem.start, elem.obj))
//deactivate non-passive implants
- avatarActor ! AvatarActor.DeactivateActiveImplants()
+ avatarActor ! AvatarActor.DeactivateActiveImplants
player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Zone.id,
AvatarAction.ChangeExosuit(
@@ -944,7 +944,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
CancelJammeredSound(target)
super.CancelJammeredStatus(target)
//uninitialize implants
- avatarActor ! AvatarActor.DeinitializeImplants()
+ avatarActor ! AvatarActor.DeinitializeImplants
//log historical event
target.LogActivity(cause)
@@ -1073,13 +1073,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
* @param dur the duration of the timer, in milliseconds
*/
override def StartJammeredStatus(target: Any, dur: Int): Unit = {
- avatarActor ! AvatarActor.DeinitializeImplants()
+ avatarActor ! AvatarActor.DeinitializeImplants
avatarActor ! AvatarActor.SuspendStaminaRegeneration(5 seconds)
super.StartJammeredStatus(target, dur)
}
override def CancelJammeredStatus(target: Any): Unit = {
- avatarActor ! AvatarActor.InitializeImplants()
+ avatarActor ! AvatarActor.SoftResetImplants
super.CancelJammeredStatus(target)
}
diff --git a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
index 9d86393ce..d6a551a01 100644
--- a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
+++ b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
@@ -69,7 +69,7 @@ trait DeployableBehavior {
if DeployableObject.OwnerGuid.nonEmpty =>
val obj = DeployableObject
if (constructed.contains(true)) {
- loseOwnership(obj, obj.Faction)
+ loseOwnership(obj, PlanetSideEmpire.NEUTRAL)
} else {
obj.OwnerGuid = None
}
@@ -103,9 +103,9 @@ trait DeployableBehavior {
* may also affect deployable operation
*/
def loseOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value): Unit = {
- DeployableBehavior.changeOwership(
+ DeployableBehavior.changeOwnership(
obj,
- obj.Faction,
+ toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Service.defaultPlayerGUID)
)
startOwnerlessDecay()
@@ -140,7 +140,7 @@ trait DeployableBehavior {
val obj = DeployableObject
obj.AssignOwnership(player)
decay.cancel()
- DeployableBehavior.changeOwership(
+ DeployableBehavior.changeOwnership(
obj,
toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, obj.OwnerGuid.get)
@@ -290,12 +290,12 @@ object DeployableBehavior {
* @param toFaction na
* @param info na
*/
- def changeOwership(obj: Deployable, toFaction: PlanetSideEmpire.Value, info: DeployableInfo): Unit = {
- val guid = obj.GUID
- val zone = obj.Zone
- val localEvents = zone.LocalEvents
+ def changeOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value, info: DeployableInfo): Unit = {
val originalFaction = obj.Faction
if (originalFaction != toFaction) {
+ val guid = obj.GUID
+ val zone = obj.Zone
+ val localEvents = zone.LocalEvents
obj.Faction = toFaction
//visual tells in regards to ownership by faction
zone.AvatarEvents ! AvatarServiceMessage(
diff --git a/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala b/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
index d1e6b7247..bb770afc8 100644
--- a/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ImplantDefinition.scala
@@ -83,5 +83,6 @@ class ImplantDefinition(val implantType: ImplantType) extends BasicDefinition {
def GetCostIntervalByExoSuit(exosuit: ExoSuitType.Value): Int =
costIntervalByExoSuit.getOrElse(exosuit, CostIntervalDefault)
+
def CostIntervalByExoSuitHashMap: mutable.Map[ExoSuitType.Value, Int] = costIntervalByExoSuit
}
diff --git a/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala
index ff8e3bc42..0104f46c7 100644
--- a/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala
+++ b/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala
@@ -23,14 +23,14 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerEquipment]()
if (obj.Inventory.Size > 0) {
Success(
DetailedLockerContainerData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, true, None, None, PlanetSideGUID(0)),
Some(InventoryData(MakeDetailedInventory(obj.Inventory)))
)
)
} else {
Success(
DetailedLockerContainerData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0)),
None
)
)
diff --git a/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala
index 938b01c02..6bc019444 100644
--- a/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala
+++ b/src/main/scala/net/psforever/objects/definition/converter/ShieldGeneratorConverter.scala
@@ -19,7 +19,7 @@ class ShieldGeneratorConverter extends ObjectCreateConverter[ShieldGeneratorDepl
obj.Faction,
bops = false,
alternate = false,
- v1 = false,
+ v1 = true,
v2 = None,
jammered = obj.Jammed,
None,
diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala
index da633d60a..d8df36c58 100644
--- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala
+++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsDeployable.scala
@@ -34,7 +34,7 @@ object GlobalDefinitionsDeployable {
boomer.Name = "boomer"
boomer.Descriptor = "Boomers"
- boomer.MaxHealth = 100
+ boomer.MaxHealth = 50
boomer.Damageable = true
boomer.DamageableByFriendlyFire = false
boomer.Repairable = false
@@ -42,6 +42,7 @@ object GlobalDefinitionsDeployable {
boomer.DeployTime = Duration.create(1000, "ms")
boomer.deployAnimation = DeployAnimation.Standard
boomer.interference = InterferenceRange(main = 0.2f)
+ boomer.Stable = true
boomer.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.Splash
SympatheticExplosion = true
@@ -58,7 +59,7 @@ object GlobalDefinitionsDeployable {
he_mine.Name = "he_mine"
he_mine.Descriptor = "Mines"
- he_mine.MaxHealth = 100
+ he_mine.MaxHealth = 25
he_mine.Damageable = true
he_mine.DamageableByFriendlyFire = false
he_mine.Repairable = false
@@ -82,7 +83,7 @@ object GlobalDefinitionsDeployable {
jammer_mine.Name = "jammer_mine"
jammer_mine.Descriptor = "JammerMines"
- jammer_mine.MaxHealth = 100
+ jammer_mine.MaxHealth = 50
jammer_mine.Damageable = true
jammer_mine.DamageableByFriendlyFire = false
jammer_mine.Repairable = false
@@ -91,12 +92,12 @@ object GlobalDefinitionsDeployable {
jammer_mine.interference = InterferenceRange(main = 7f, sharedGroupId = 1, shared = 7f, deployables = 0.1f)
jammer_mine.DetonateOnJamming = false
jammer_mine.triggerRadius = 3f
+ jammer_mine.Stable = true
jammer_mine.innateDamage = new DamageWithPosition {
CausesDamageType = DamageType.Splash
Damage0 = 0
DamageRadius = 10f
DamageAtEdge = 1.0f
- AdditionalEffect = true
JammedEffectDuration += TargetValidation(
EffectTarget.Category.Player,
EffectTarget.Validation.Player
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
index 28c24a5e9..7f1863ffb 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/Damageable.scala
@@ -94,12 +94,12 @@ object Damageable {
* `false`, otherwise
*/
def CanJammer(obj: Vitality with FactionAffinity, data: DamageInteraction): Boolean = {
- data.cause.source.HasJammedEffectDuration &&
+ (data.cause.source.HasJammedEffectDuration || data.cause.source.AdditionalEffect) &&
obj.isInstanceOf[JammableUnit] &&
adversarialOrHackableChecks(obj, data)
}
- private def adversarialOrHackableChecks(obj: Vitality with FactionAffinity, data: DamageInteraction): Boolean = {
+ def adversarialOrHackableChecks(obj: Vitality with FactionAffinity, data: DamageInteraction): Boolean = {
(data.adversarial match {
case Some(adversarial) => adversarial.attacker.Faction != adversarial.defender.Faction
case None => true
diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
index 06403f5f1..2356fb11b 100644
--- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala
@@ -75,10 +75,8 @@ object DamageableMountable {
*/
def DestructionAwareness(target: Damageable.Target with Mountable, cause: DamageResult): Unit = {
val interaction = cause.interaction
- target.Seats
- .values
- .flatMap { _.occupant }
- .collect { case player if player.isAlive =>
+ val targets = target.Seats.values.flatMap(_.occupant).filter(_.isAlive)
+ targets.foreach { player =>
//make llu visible to others in zone if passenger is carrying one
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem())
//player.LogActivity(cause)
diff --git a/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala
index e495f32b1..664facf54 100644
--- a/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala
@@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.deploy
-import akka.actor.Actor
+import akka.actor.{Actor, Cancellable}
+import net.psforever.objects.Default
import net.psforever.types.{DriveState, Vector3}
import net.psforever.services.Service
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@@ -22,8 +23,14 @@ import scala.concurrent.duration._
trait DeploymentBehavior {
_: Actor =>
+ private var deploymentTimer: Cancellable = Default.Cancellable
+
def DeploymentObject: Deployment.DeploymentObject
+ def deploymentPostStop(): Unit = {
+ deploymentTimer.cancel()
+ }
+
val deployBehavior: Receive = {
case Deployment.TryDeploymentChange(state) =>
sender() ! TryDeploymentStateChange(state)
@@ -98,9 +105,10 @@ trait DeploymentBehavior {
obj.Velocity = Some(Vector3.Zero) //no velocity
zone.VehicleEvents ! VehicleServiceMessage(
zoneChannel,
- VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
+ VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero)
)
- context.system.scheduler.scheduleOnce(
+ deploymentTimer.cancel()
+ deploymentTimer = context.system.scheduler.scheduleOnce(
obj.DeployTime milliseconds,
obj.Actor,
Deployment.TryDeploy(DriveState.Deployed)
@@ -110,7 +118,7 @@ trait DeploymentBehavior {
obj.Velocity = Some(Vector3.Zero) //no velocity
zone.VehicleEvents ! VehicleServiceMessage(
zoneChannel,
- VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
+ VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero)
)
state
} else {
@@ -130,10 +138,11 @@ trait DeploymentBehavior {
if (state == DriveState.Undeploying) {
zone.VehicleEvents ! VehicleServiceMessage(
zoneChannel,
- VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
+ VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero)
)
import scala.concurrent.ExecutionContext.Implicits.global
- context.system.scheduler.scheduleOnce(
+ deploymentTimer.cancel()
+ deploymentTimer = context.system.scheduler.scheduleOnce(
obj.UndeployTime milliseconds,
obj.Actor,
Deployment.TryUndeploy(DriveState.Mobile)
@@ -142,7 +151,7 @@ trait DeploymentBehavior {
} else if (state == DriveState.Mobile) {
zone.VehicleEvents ! VehicleServiceMessage(
zoneChannel,
- VehicleAction.DeployRequest(GUID0, guid, state, 0, false, Vector3.Zero)
+ VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero)
)
state
} else {
diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala b/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
index 37641049e..2ad7d1e87 100644
--- a/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/environment/EnvironmentAttribute.scala
@@ -17,9 +17,8 @@ object EnvironmentAttribute {
/** water can only interact with objects that are negatively affected by being exposed to water;
* it's better this way */
def canInteractWith(obj: PlanetSideGameObject): Boolean = {
- obj.Definition.DrownAtMaxDepth ||
- obj.Definition.DisableAtMaxDepth ||
- canInteractWithPlayersAndVehicles(obj) ||
+ (obj.Definition.DrownAtMaxDepth || obj.Definition.DisableAtMaxDepth) &&
+ canInteractWithPlayersAndVehicles(obj) &&
(obj match {
case p: Player => p.VehicleSeated.isEmpty
case v: Vehicle => v.MountedIn.isEmpty
diff --git a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala
index a389564ee..4e3898847 100644
--- a/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/environment/interaction/common/WithDeath.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, Env
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.etc.SuicideReason
import net.psforever.objects.vital.interaction.DamageInteraction
-import net.psforever.objects.vital.{ReconstructionActivity, Vitality}
+import net.psforever.objects.vital.{IncarnationActivity, ReconstructionActivity, Vitality}
import net.psforever.objects.zones.InteractsWithZone
import scala.annotation.unused
@@ -29,7 +29,7 @@ class WithDeath()
@unused data: Option[Any]
): Unit = {
if (!obj.Destroyed) {
- obj.History.findLast { entry => entry.isInstanceOf[ReconstructionActivity] } match {
+ obj.History.findLast { entry => entry.isInstanceOf[IncarnationActivity] } match {
case Some(entry) if System.currentTimeMillis() - entry.time > 4000L =>
obj.Actor ! Vitality.Damage(
DamageInteraction(
diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
index d1de62824..7c5034b63 100644
--- a/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/structures/Amenity.scala
@@ -65,8 +65,8 @@ abstract class Amenity
override def Zone: Zone = {
if (super.Zone != World.Nowhere) {
super.Zone
- } else if (Owner.Zone != World.Nowhere) {
- Owner.Zone
+ } else if (owner.Zone != World.Nowhere) {
+ owner.Zone
} else {
log.warn(s"Amenity $GUID tried to access it's Zone, but doesn't have one.")
World.Nowhere
diff --git a/src/main/scala/net/psforever/objects/vehicles/control/DeployingVehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/DeployingVehicleControl.scala
index 4c33ec918..14faf3d90 100644
--- a/src/main/scala/net/psforever/objects/vehicles/control/DeployingVehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/control/DeployingVehicleControl.scala
@@ -22,6 +22,11 @@ class DeployingVehicleControl(vehicle: Vehicle)
with DeploymentBehavior {
def DeploymentObject: Vehicle = vehicle
+ override def postStop(): Unit = {
+ super.postStop()
+ deploymentPostStop()
+ }
+
override def commonEnabledBehavior : Receive = super.commonEnabledBehavior.orElse(deployBehavior)
/**
diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
index 3ede78821..e65863eae 100644
--- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
@@ -75,15 +75,6 @@ class VehicleControl(vehicle: Vehicle)
def CargoObject: Vehicle = vehicle
def AffectedObject: Vehicle = vehicle
-// SetInteraction(EnvironmentAttribute.Water, doInteractingWithWater)
-// SetInteraction(EnvironmentAttribute.Lava, doInteractingWithLava)
-// SetInteraction(EnvironmentAttribute.Death, doInteractingWithDeath)
-// SetInteraction(EnvironmentAttribute.MovementFieldTrigger, doInteractingWithMovementTrigger)
-// if (!GlobalDefinitions.isFlightVehicle(vehicle.Definition)) {
-// //can recover from sinking disability
-// SetInteractionStop(EnvironmentAttribute.Water, stopInteractingWithWater)
-// }
-
/** cheap flag for whether the vehicle is decaying */
var decaying : Boolean = false
/** primary vehicle decay timer */
diff --git a/src/main/scala/net/psforever/objects/vital/etc/TriggerUsedReason.scala b/src/main/scala/net/psforever/objects/vital/etc/TriggerUsedReason.scala
index 6a8b088c8..fd77eebb8 100644
--- a/src/main/scala/net/psforever/objects/vital/etc/TriggerUsedReason.scala
+++ b/src/main/scala/net/psforever/objects/vital/etc/TriggerUsedReason.scala
@@ -50,7 +50,6 @@ final case class TriggerUsedReason(user: PlayerSource, item_guid: PlanetSideGUID
object TriggerUsedReason {
private val triggered = new DamageProperties {
Damage0 = 1 //token damage
- SympatheticExplosion = true //sets off a boomer
}
/** basic damage, no resisting, quick and simple */
diff --git a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala
index 97c3c77f9..c7e8c18c3 100644
--- a/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala
+++ b/src/main/scala/net/psforever/objects/vital/prop/DamageProperties.scala
@@ -34,7 +34,8 @@ trait DamageProperties
* also used to produce staged projectiles */
private var damageProxy: List[Int] = Nil
/** na;
- * currently used with jammer properties only */
+ * currently used with jammer properties only;
+ * used sepcifically to indicate jammering effect targets explosive deployables */
private var additionalEffect: Boolean = false
/** confers aggravated damage burn to its target */
private var aggravatedDamage: Option[AggravatedDamage] = None
diff --git a/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala b/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
index d194bc880..2a99f4bd3 100644
--- a/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala
@@ -13,25 +13,14 @@ import shapeless.{::, HNil}
* The parameters `purpose` and `tile` are closely related.
* These two fields are consistent for all shortcuts of the same type.
* `purpose` indicates the purpose of the shortcut.
+ * The medkit icon is 0, chat shortcuts are 1, and implants are 2.
* `tile` is related to what kind of graphic is displayed in this shortcut's slot on the hotbar based on its purpose.
- * The parameters `effect1` and `effect2` are exclusive to text macro shortcuts and are defaulted to empty `String`s.
+ * The medkit tile use "medkit", chat shortcuts use "shortcut_macro", and implants are the internal name of the implant.
*
+ * The parameters `effect1` and `effect2` are exclusive to text macro shortcuts and are defaulted to empty `String`s.
* The `shortcut_macro` setting displays a word bubble superimposed by the (first three letters of) `effect1` text.
* Implants and the medkit should have self-explanatory graphics.
- *
- * Tile - Code
- * `advanced_regen` (regeneration) - 2
- * `audio_amplifier` - 2
- * `darklight_vision` - 2
- * `medkit` - 0
- * `melee_booster` - 2
- * `personal_shield` - 2
- * `range_magnifier` - 2
- * `second_wind` - 2
- * `shortcut_macro` - 1
- * `silent_run` (sensor shield) - 2
- * `surge` - 2
- * `targeting` (enhanced targeting) - 2
+ * The implant second wind does not have a graphic shortcut icon.
* @param code the primary use of this shortcut
*/
abstract class Shortcut(val code: Int) {
diff --git a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
index f88bcf039..141266395 100644
--- a/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
+++ b/src/main/scala/net/psforever/packet/game/DamageFeedbackMessage.scala
@@ -1,51 +1,55 @@
// Copyright (c) 2019 PSForever
package net.psforever.packet.game
+import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
-import scodec.Codec
+import scodec.bits.BitVector
+import scodec.{Attempt, Codec}
import scodec.codecs._
import shapeless.{::, HNil}
/**
- * na
- * @param unk1 na
- * @param unk2 if no global unique identifier (below), the alternate identification for the entity
- * @param unk2a the global unique identifier of the entity inflicting the damage
- * @param unk2b if no global unique identifier (above), the name of the entity inflicting the damage
- * @param unk2c if no global unique identifier (above), the object type of the entity inflicting the damage
- * @param unk3 if no global unique identifier (below), the alternate identification for the entity
- * @param unk3a the global unique identifier of the entity absorbing the damage
- * @param unk3b if no global unique identifier (above), the name of the entity absorbing the damage
- * @param unk3c if no global unique identifier (above), the object type of the entity absorbing the damage
- * @param unk3d na
- * @param unk4 an indicator for the target-specific vital statistic being affected
- * @param unk5 the amount of damage
- * @param unk6 na
- */
+ * na
+ * @param unk1 na
+ * @param unk2 a modifier that customizes one of the values for the `unk2...` determination values;
+ * when not using the GUID field, `true` when using the string field
+ * @param unk2a the global unique identifier of the entity inflicting the damage
+ * @param unk2b if no global unique identifier (above), the name of the entity inflicting the damage
+ * @param unk2c if no global unique identifier (above), the object type of the entity inflicting the damage
+ * @param unk3 a modifier that customizes one of the values for the `unk3...` determination values;
+ * when not using the GUID field, `true` when using the string field
+ * @param unk3a the global unique identifier of the entity absorbing the damage
+ * @param unk3b if no global unique identifier (above), the name of the entity absorbing the damage
+ * @param unk3c if no global unique identifier (above), the object type of the entity absorbing the damage
+ * @param unk3d na
+ * @param unk4 an indicator for the target-specific vital statistic being affected
+ * @param unk5 the amount of damage
+ * @param unk6 na
+ */
final case class DamageFeedbackMessage(
- unk1: Int,
- unk2: Boolean,
- unk2a: Option[PlanetSideGUID],
- unk2b: Option[String],
- unk2c: Option[Int],
- unk3: Boolean,
- unk3a: Option[PlanetSideGUID],
- unk3b: Option[String],
- unk3c: Option[Int],
- unk3d: Option[Int],
- unk4: Int,
- unk5: Long,
- unk6: Int
-) extends PlanetSideGamePacket {
+ unk1: Int,
+ unk2: Option[Boolean],
+ unk2a: Option[PlanetSideGUID],
+ unk2b: Option[String],
+ unk2c: Option[Int],
+ unk3: Option[Boolean],
+ unk3a: Option[PlanetSideGUID],
+ unk3b: Option[String],
+ unk3c: Option[Int],
+ unk3d: Option[Int],
+ unk4: Int,
+ unk5: Long,
+ unk6: Int
+ ) extends PlanetSideGamePacket {
assert(
{
val unk2aEmpty = unk2a.isEmpty
val unk2bEmpty = unk2b.isEmpty
val unk2cEmpty = unk2c.isEmpty
- if (unk2a.nonEmpty) unk2bEmpty && unk2cEmpty
- else if (unk2b.nonEmpty) unk2 && unk2aEmpty && unk2cEmpty
- else unk2aEmpty && !unk2 && unk2bEmpty && unk2c.nonEmpty
+ if (!unk2aEmpty) unk2bEmpty && unk2cEmpty
+ else if (!unk2bEmpty) unk2aEmpty && unk2cEmpty
+ else unk2aEmpty && unk2bEmpty && !unk2cEmpty
}
)
assert(
@@ -53,58 +57,104 @@ final case class DamageFeedbackMessage(
val unk3aEmpty = unk3a.isEmpty
val unk3bEmpty = unk3b.isEmpty
val unk3cEmpty = unk3c.isEmpty
- if (unk3a.nonEmpty) unk3bEmpty && unk3cEmpty
- else if (unk3b.nonEmpty) unk3 && unk3aEmpty && unk3cEmpty
- else unk3aEmpty && !unk3 && unk3bEmpty && unk3c.nonEmpty
+ if (!unk3aEmpty) unk3bEmpty && unk3cEmpty
+ else if (!unk3bEmpty) unk3aEmpty && unk3cEmpty
+ else unk3aEmpty && unk3bEmpty && !unk3cEmpty
}
)
assert(unk3a.isEmpty == unk3d.nonEmpty)
type Packet = DamageFeedbackMessage
- def opcode = GamePacketOpcode.DamageFeedbackMessage
- def encode = DamageFeedbackMessage.encode(this)
+ def opcode: Type = GamePacketOpcode.DamageFeedbackMessage
+ def encode: Attempt[BitVector] = DamageFeedbackMessage.encode(this)
}
object DamageFeedbackMessage extends Marshallable[DamageFeedbackMessage] {
- def apply(unk1: Int,
- unk2: PlanetSideGUID,
- unk3: PlanetSideGUID,
- unk4: Int,
- unk5: Long): DamageFeedbackMessage =
- DamageFeedbackMessage(unk1, true, Some(unk2), None, None, true, Some(unk3), None, None, None, unk4, unk5, 0)
+ def apply(
+ unk1: Int,
+ unk2a: Option[PlanetSideGUID],
+ unk2b: Option[String],
+ unk2c: Option[Int],
+ unk3a: Option[PlanetSideGUID],
+ unk3b: Option[String],
+ unk3c: Option[Int],
+ unk3d: Option[Int],
+ unk4: Int,
+ unk5: Long
+ ): DamageFeedbackMessage = {
+ DamageFeedbackMessage(unk1, None, unk2a, unk2b, unk2c, None, unk3a, unk3b, unk3c, unk3d, unk4, unk5, 0)
+ }
+
+ def apply(
+ unk1: Int,
+ unk2: PlanetSideGUID,
+ unk3: PlanetSideGUID,
+ unk4: Int,
+ unk5: Long
+ ): DamageFeedbackMessage = {
+ DamageFeedbackMessage(unk1, None, Some(unk2), None, None, None, Some(unk3), None, None, None, unk4, unk5, 0)
+ }
+
+ private case class EntryFields(
+ usesGuid: Boolean,
+ usesStr: Boolean,
+ guidOpt: Option[PlanetSideGUID],
+ strOpt: Option[String],
+ intOpt: Option[Int]
+ )
+
+ /**
+ * na
+ * @param adjustment na;
+ * can not be a negative number
+ * @return na
+ */
+ private def entityFieldFormatCodec(adjustment: Int): Codec[EntryFields] = {
+ ((bool :: bool) >>:~ { case usesGuid :: usesString :: HNil =>
+ conditional(usesGuid, PlanetSideGUID.codec) ::
+ conditional(!usesGuid && usesString, PacketHelpers.encodedWideStringAligned(adjustment)) ::
+ conditional(!usesGuid && !usesString, uintL(bits = 11))
+ }).xmap[EntryFields](
+ {
+ case (a :: b :: HNil) :: c :: d :: e :: HNil => EntryFields(a, b, c, d, e)
+ },
+ {
+ case EntryFields(a, b, c, d, e) => (a :: b :: HNil) :: c :: d :: e :: HNil
+ }
+ )
+ }
implicit val codec: Codec[DamageFeedbackMessage] = (
- ("unk1" | uint4) ::
- (bool >>:~ { u2 =>
- bool >>:~ { u3 =>
- ("unk2a" | conditional(u2, PlanetSideGUID.codec)) ::
- (("unk2b" | conditional(!u2 && u3, PacketHelpers.encodedWideStringAligned(6))) >>:~ { u2b =>
- ("unk2c" | conditional(!u2 && !u3, uintL(11))) ::
- (bool >>:~ { u5 =>
- bool >>:~ { u6 =>
- ("unk3a" | conditional(u5, PlanetSideGUID.codec)) ::
- ("unk3b" | conditional(
- !u5 && u6,
- PacketHelpers.encodedWideStringAligned(if (u2b.nonEmpty) 3 else 1)
- )) ::
- ("unk3c" | conditional(!u5 && !u6, uintL(11))) ::
- ("unk3d" | conditional(!u5, uint2)) ::
- ("unk4" | uint(3)) ::
- ("unk5" | uint32L) ::
- ("unk6" | uint2)
- }
- })
- })
+ ("unk1" | uint4) :: {
+ entityFieldFormatCodec(adjustment = 4) >>:~ { fieldsA =>
+ val offset = if (fieldsA.usesGuid) { 0 } else if (fieldsA.usesStr) { 6 } else { 5 }
+ entityFieldFormatCodec(offset) >>:~ { fieldsB =>
+ ("unk3d" | conditional(!fieldsB.usesGuid, uint2)) ::
+ ("unk4" | uint(bits = 3)) ::
+ ("unk5" | uint32L) ::
+ ("unk6" | uint2)
+ }
}
- })
- ).xmap[DamageFeedbackMessage](
+ }).xmap[DamageFeedbackMessage](
{
- case u1 :: _ :: u2 :: u2a :: u2b :: u2c :: _ :: u3 :: u3a :: u3b :: u3c :: u3d :: u4 :: u5 :: u6 :: HNil =>
- DamageFeedbackMessage(u1, u2, u2a, u2b, u2c, u3, u3a, u3b, u3c, u3d, u4, u5, u6)
+ case u1 :: EntryFields(_, u2, u2a, u2b, u2c) :: EntryFields(_, u3, u3a, u3b, u3c) :: u3d :: u4 :: u5 :: u6 :: HNil =>
+ val u2False = if (u2a.nonEmpty && !u2) { Some(false) } else { None }
+ val u3False = if (u3a.nonEmpty && !u3) { Some(false) } else { None }
+ DamageFeedbackMessage(u1, u2False, u2a, u2b, u2c, u3False, u3a, u3b, u3c, u3d, u4, u5, u6)
},
{
case DamageFeedbackMessage(u1, u2, u2a, u2b, u2c, u3, u3a, u3b, u3c, u3d, u4, u5, u6) =>
- u1 :: u2a.nonEmpty :: u2 :: u2a :: u2b :: u2c :: u3a.nonEmpty :: u3 :: u3a :: u3b :: u3c :: u3d :: u4 :: u5 :: u6 :: HNil
+ val(u2Boola, u2Boolb) = if (u2a.nonEmpty) {
+ (true, u2.getOrElse(true))
+ } else {
+ (false, u2b.nonEmpty)
+ }
+ val(u3Boola, u3Boolb) = if (u3a.nonEmpty) {
+ (true, u3.getOrElse(true))
+ } else {
+ (false, u3b.nonEmpty)
+ }
+ u1 :: EntryFields(u2Boola, u2Boolb, u2a, u2b, u2c) :: EntryFields(u3Boola, u3Boolb, u3a, u3b, u3c) :: u3d :: u4 :: u5 :: u6 :: HNil
}
)
}
diff --git a/src/main/scala/net/psforever/persistence/Implant.scala b/src/main/scala/net/psforever/persistence/Implant.scala
index 744c025a8..b207db5aa 100644
--- a/src/main/scala/net/psforever/persistence/Implant.scala
+++ b/src/main/scala/net/psforever/persistence/Implant.scala
@@ -5,7 +5,8 @@ import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalDefin
case class Implant(
name: String,
- avatarId: Int
+ avatarId: Int,
+ timer: Int = 0 //seconds to initialize
) {
def toImplantDefinition: ImplantDefinition = {
ImplantTerminalDefinition.implants(name)
diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala
index 8ee7edb52..a927da4a0 100644
--- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala
+++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala
@@ -39,6 +39,10 @@ class AvatarService(zone: Zone) extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype))
)
+ case AvatarAction.AvatarImplant(player_guid, action, implantSlot, status) =>
+ AvatarEvents.publish(
+ AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.AvatarImplant(action, implantSlot, status))
+ )
case AvatarAction.ChangeAmmo(
player_guid,
weapon_guid,
diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala
index 68333969f..7f4134a82 100644
--- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala
+++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala
@@ -11,6 +11,7 @@ import net.psforever.objects.serverobject.environment.interaction.common.Watery.
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
+import net.psforever.packet.game.ImplantAction
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
@@ -27,6 +28,7 @@ object AvatarAction {
sealed trait Action
final case class ArmorChanged(player_guid: PlanetSideGUID, suit: ExoSuitType.Value, subtype: Int) extends Action
+ final case class AvatarImplant(player_guid: PlanetSideGUID, action: ImplantAction.Value, implantSlot: Int, status: Int) extends Action
final case class ChangeAmmo(
player_guid: PlanetSideGUID,
weapon_guid: PlanetSideGUID,
diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala
index 3e0c020cf..c90505842 100644
--- a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala
+++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala
@@ -10,7 +10,7 @@ import net.psforever.objects.serverobject.environment.interaction.common.Watery.
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
-import net.psforever.packet.game.ObjectCreateMessage
+import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage}
import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
import net.psforever.services.GenericEventBusMsg
@@ -24,6 +24,7 @@ object AvatarResponse {
sealed trait Response
final case class ArmorChanged(suit: ExoSuitType.Value, subtype: Int) extends Response
+ final case class AvatarImplant(action: ImplantAction.Value, implantSlot: Int, status: Int) extends Response
final case class ChangeAmmo(
weapon_guid: PlanetSideGUID,
weapon_slot: Int,
diff --git a/src/test/resources/zonemaps/lattice.json b/src/test/resources/zonemaps/lattice.json
new file mode 100644
index 000000000..2c63c0851
--- /dev/null
+++ b/src/test/resources/zonemaps/lattice.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/src/test/scala/game/DamageFeedbackMessageTest.scala b/src/test/scala/game/DamageFeedbackMessageTest.scala
index c8eb4dae4..584ce608f 100644
--- a/src/test/scala/game/DamageFeedbackMessageTest.scala
+++ b/src/test/scala/game/DamageFeedbackMessageTest.scala
@@ -13,13 +13,11 @@ class DamageFeedbackMessageTest extends Specification {
"decode (string 1)" in {
PacketCoding.decodePacket(string).require match {
- case DamageFeedbackMessage(unk1, unk2, unk2a, unk2b, unk2c, unk3, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
+ case DamageFeedbackMessage(unk1, _, unk2a, unk2b, unk2c, _, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
unk1 mustEqual 3
- unk2 mustEqual true
unk2a.contains(PlanetSideGUID(2913)) mustEqual true
unk2b.isEmpty mustEqual true
unk2c.isEmpty mustEqual true
- unk3 mustEqual true
unk3a.contains(PlanetSideGUID(2913)) mustEqual true
unk3b.isEmpty mustEqual true
unk3c.isEmpty mustEqual true
@@ -34,13 +32,11 @@ class DamageFeedbackMessageTest extends Specification {
"decode (string 2)" in {
PacketCoding.decodePacket(string_2).require match {
- case DamageFeedbackMessage(unk1, unk2, unk2a, unk2b, unk2c, unk3, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
+ case DamageFeedbackMessage(unk1, _, unk2a, unk2b, unk2c, _, unk3a, unk3b, unk3c, unk3d, unk4, unk5, unk6) =>
unk1 mustEqual 5
- unk2 mustEqual true
unk2a.contains(PlanetSideGUID(2454)) mustEqual true
unk2b.isEmpty mustEqual true
unk2c.isEmpty mustEqual true
- unk3 mustEqual false
unk3a.contains(PlanetSideGUID(216)) mustEqual true
unk3b.isEmpty mustEqual true
unk3c.isEmpty mustEqual true
@@ -56,18 +52,15 @@ class DamageFeedbackMessageTest extends Specification {
"encode (string 1)" in {
val msg = DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
+ 2
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
@@ -77,11 +70,11 @@ class DamageFeedbackMessageTest extends Specification {
"encode (string 2)" in {
val msg = DamageFeedbackMessage(
5,
- true,
+ None,
Some(PlanetSideGUID(2454)),
None,
None,
- false,
+ Some(false),
Some(PlanetSideGUID(216)),
None,
None,
@@ -99,252 +92,129 @@ class DamageFeedbackMessageTest extends Specification {
//unk2: no parameters
DamageFeedbackMessage(
3,
- true,
None,
None,
None,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
//unk2: two exclusive parameters
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
Some("error"),
None,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
Some(5),
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
DamageFeedbackMessage(
3,
- true,
None,
Some("error"),
Some(5),
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
//unk2: all parameters
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
Some("error"),
Some(5),
- true,
Some(PlanetSideGUID(2913)),
None,
None,
None,
1,
- 2,
- 0
- ) must throwA[AssertionError]
- //unk2: mismatched flag for strings
- DamageFeedbackMessage(
- 3,
- true,
- None,
- None,
- Some(5),
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- None,
- 1,
- 2,
- 0
- ) must throwA[AssertionError]
- DamageFeedbackMessage(
- 3,
- false,
- None,
- Some("error"),
- None,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- None,
- 1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
//unk3: no parameters
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
None,
None,
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
//unk3: two exclusive parameters
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
Some(PlanetSideGUID(2913)),
Some("error"),
None,
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
Some(PlanetSideGUID(2913)),
None,
Some(5),
None,
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
None,
Some("error"),
Some(5),
Some(1),
1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
//unk3: all parameters
DamageFeedbackMessage(
3,
- true,
Some(PlanetSideGUID(2913)),
None,
None,
- true,
Some(PlanetSideGUID(2913)),
Some("error"),
Some(5),
None,
1,
- 2,
- 0
- ) must throwA[AssertionError]
- //unk3: mismatched fields
- DamageFeedbackMessage(
- 3,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- Some(5),
- 1,
- 2,
- 0
- ) must throwA[AssertionError]
- DamageFeedbackMessage(
- 3,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- true,
- None,
- Some("Error"),
- None,
- None,
- 1,
- 2,
- 0
- ) must throwA[AssertionError]
- //unk3: mismatched flag for strings
- DamageFeedbackMessage(
- 3,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- true,
- None,
- None,
- Some(5),
- None,
- 1,
- 2,
- 0
- ) must throwA[AssertionError]
- DamageFeedbackMessage(
- 3,
- true,
- Some(PlanetSideGUID(2913)),
- None,
- None,
- false,
- None,
- Some("error"),
- None,
- None,
- 1,
- 2,
- 0
+ 2
) must throwA[AssertionError]
}
}
diff --git a/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala b/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala
index 6745c33a9..062712a26 100644
--- a/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala
+++ b/src/test/scala/game/objectcreate/AegisShieldGeneratorDataTest.scala
@@ -46,9 +46,17 @@ class AegisShieldGeneratorDataTest extends Specification {
val obj = AegisShieldGeneratorData(
CommonFieldDataWithPlacement(
PlacementData(Vector3(3571.2266f, 3278.0938f, 114.0f), Vector3(0, 0, 90)),
- PlanetSideEmpire.VS,
- 2,
- PlanetSideGUID(2366)
+ CommonFieldData(
+ PlanetSideEmpire.VS,
+ bops = false,
+ alternate = false,
+ v1 = true,
+ v2 = None,
+ jammered = false,
+ v4 = None,
+ v5 = None,
+ PlanetSideGUID(2366)
+ )
),
255
)
diff --git a/src/test/scala/game/objectcreate/WeaponDataTest.scala b/src/test/scala/game/objectcreate/WeaponDataTest.scala
index b7e184b5a..4b2507d1b 100644
--- a/src/test/scala/game/objectcreate/WeaponDataTest.scala
+++ b/src/test/scala/game/objectcreate/WeaponDataTest.scala
@@ -287,7 +287,7 @@ class WeaponDataTest extends Specification {
ObjectClass.energy_cell,
PlanetSideGUID(3548),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
@@ -311,13 +311,13 @@ class WeaponDataTest extends Specification {
ObjectClass.bullet_9mm,
PlanetSideGUID(3918),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
),
AmmoBoxData(
ObjectClass.rocket,
PlanetSideGUID(3941),
1,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
@@ -337,7 +337,7 @@ class WeaponDataTest extends Specification {
WeaponData(
CommonFieldData(PlanetSideEmpire.VS, false, false, false, None, false, None, None, PlanetSideGUID(0)),
0,
- List(InternalSlot(ObjectClass.energy_cell, PlanetSideGUID(3268), 0, CommonFieldData()(false)))
+ List(InternalSlot(ObjectClass.energy_cell, PlanetSideGUID(3268), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 0)(false)))
)
)
val msg = ObjectCreateMessage(ObjectClass.lasher, PlanetSideGUID(3074), obj)
@@ -352,8 +352,8 @@ class WeaponDataTest extends Specification {
CommonFieldData(PlanetSideEmpire.NC, false, false, false, None, false, None, None, PlanetSideGUID(0)),
0,
List(
- AmmoBoxData(ObjectClass.bullet_9mm, PlanetSideGUID(3528), 0, CommonFieldData()(false)),
- AmmoBoxData(ObjectClass.rocket, PlanetSideGUID(3031), 1, CommonFieldData()(false))
+ AmmoBoxData(ObjectClass.bullet_9mm, PlanetSideGUID(3528), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 0)(false)),
+ AmmoBoxData(ObjectClass.rocket, PlanetSideGUID(3031), 1, CommonFieldData(PlanetSideEmpire.NEUTRAL, 0)(false))
)
)
)
diff --git a/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala b/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
index fd92006cb..377bee18c 100644
--- a/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
+++ b/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
@@ -1655,7 +1655,10 @@ class DetailedCharacterDataTest extends Specification {
List(InternalSlot(ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1)))
)
),
- InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)),
+ InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
+ None
+ )),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)),
@@ -1837,7 +1840,10 @@ class DetailedCharacterDataTest extends Specification {
List(InternalSlot(ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1)))
)
),
- InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)),
+ InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
+ None
+ )),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)),
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)),
diff --git a/src/test/scala/game/objectcreatevehicle/BattleframeRoboticsTest.scala b/src/test/scala/game/objectcreatevehicle/BattleframeRoboticsTest.scala
index 32685f61c..a4848de7a 100644
--- a/src/test/scala/game/objectcreatevehicle/BattleframeRoboticsTest.scala
+++ b/src/test/scala/game/objectcreatevehicle/BattleframeRoboticsTest.scala
@@ -275,7 +275,7 @@ class BattleframeRoboticsTest extends Specification {
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(340), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false))
+ InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(340), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
),
@@ -284,7 +284,7 @@ class BattleframeRoboticsTest extends Specification {
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(342), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false))
+ InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(342), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
),
@@ -293,7 +293,7 @@ class BattleframeRoboticsTest extends Specification {
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.aphelion_plasma_rocket_ammo, PlanetSideGUID(359), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false))
+ InternalSlot(ObjectClass.aphelion_plasma_rocket_ammo, PlanetSideGUID(359), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
)
@@ -325,7 +325,7 @@ class BattleframeRoboticsTest extends Specification {
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(371), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false))
+ InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(371), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
),
@@ -334,7 +334,7 @@ class BattleframeRoboticsTest extends Specification {
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(376), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false))
+ InternalSlot(ObjectClass.aphelion_ppa_ammo, PlanetSideGUID(376), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
)
diff --git a/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala b/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala
index 894325c67..7e0d2c1c9 100644
--- a/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala
+++ b/src/test/scala/game/objectcreatevehicle/NonstandardVehiclesTest.scala
@@ -22,7 +22,7 @@ class NonstandardVehiclesTest extends Specification {
guid mustEqual PlanetSideGUID(3595)
parent.isDefined mustEqual false
data match {
- case DroppodData(basic, health, burn, unk) =>
+ case DroppodData(basic, health, burn, _) =>
basic.pos.coord mustEqual Vector3(5108.0f, 6164.0f, 1023.9844f)
basic.pos.orient mustEqual Vector3.z(90.0f)
@@ -88,7 +88,17 @@ class NonstandardVehiclesTest extends Specification {
val obj = DroppodData(
CommonFieldDataWithPlacement(
PlacementData(5108.0f, 6164.0f, 1023.9844f, 0f, 0f, 90.0f),
- CommonFieldData(PlanetSideEmpire.VS, 2)
+ CommonFieldData(
+ PlanetSideEmpire.VS,
+ bops = false,
+ alternate = false,
+ v1 = true,
+ v2 = None,
+ jammered = false,
+ v4 = None,
+ v5 = None,
+ PlanetSideGUID(0)
+ )
)
)
val msg = ObjectCreateMessage(ObjectClass.droppod, PlanetSideGUID(3595), obj)
diff --git a/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala b/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
index c0c3aadda..64d5ce055 100644
--- a/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
+++ b/src/test/scala/game/objectcreatevehicle/NormalVehiclesTest.scala
@@ -325,14 +325,14 @@ class NormalVehiclesTest extends Specification {
PlanetSideGUID(400),
1,
WeaponData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
0,
List(
InternalSlot(
ObjectClass.hellfire_ammo,
PlanetSideGUID(432),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
@@ -368,11 +368,11 @@ class NormalVehiclesTest extends Specification {
PlanetSideGUID(91),
1,
WeaponData(
- CommonFieldData(PlanetSideEmpire.VS, 2),
+ CommonFieldData(PlanetSideEmpire.VS, false, false, true, None, false, None, None, PlanetSideGUID(0)),
0,
List(
- InternalSlot(ObjectClass.bullet_75mm, PlanetSideGUID(92), 0, CommonFieldData()(false)),
- InternalSlot(ObjectClass.bullet_25mm, PlanetSideGUID(93), 1, CommonFieldData()(false))
+ InternalSlot(ObjectClass.bullet_75mm, PlanetSideGUID(92), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, Some(false), None, PlanetSideGUID(0))),
+ InternalSlot(ObjectClass.bullet_25mm, PlanetSideGUID(93), 1, CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, Some(false), None, PlanetSideGUID(0)))
)
)
)
@@ -407,14 +407,14 @@ class NormalVehiclesTest extends Specification {
PlanetSideGUID(383),
5,
WeaponData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
0,
List(
InternalSlot(
ObjectClass.bullet_20mm,
PlanetSideGUID(420),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
@@ -424,14 +424,14 @@ class NormalVehiclesTest extends Specification {
PlanetSideGUID(556),
6,
WeaponData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
0,
List(
InternalSlot(
ObjectClass.bullet_20mm,
PlanetSideGUID(575),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
diff --git a/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala b/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
index cb6be6795..568052c8f 100644
--- a/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
+++ b/src/test/scala/game/objectcreatevehicle/VariantVehiclesTest.scala
@@ -140,13 +140,13 @@ class VariantVehiclesTest extends Specification {
ObjectClass.ancient_ammo_vehicle,
PlanetSideGUID(366),
0,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
),
InternalSlot(
ObjectClass.ancient_ammo_vehicle,
PlanetSideGUID(385),
1,
- CommonFieldData(PlanetSideEmpire.NEUTRAL, 2)(false)
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, Some(false), None, PlanetSideGUID(0))
)
)
)
diff --git a/src/test/scala/objects/ConverterTest.scala b/src/test/scala/objects/ConverterTest.scala
index d20476c9d..691a06e77 100644
--- a/src/test/scala/objects/ConverterTest.scala
+++ b/src/test/scala/objects/ConverterTest.scala
@@ -371,7 +371,7 @@ class ConverterTest extends Specification {
PlanetSideEmpire.TR,
false,
false,
- false,
+ true,
None,
false,
Some(true),
@@ -547,8 +547,17 @@ class ConverterTest extends Specification {
pkt mustEqual AegisShieldGeneratorData(
CommonFieldDataWithPlacement(
PlacementData(Vector3.Zero, Vector3.Zero),
- PlanetSideEmpire.TR,
- 0
+ CommonFieldData(
+ PlanetSideEmpire.TR,
+ bops = false,
+ alternate = false,
+ v1 = true,
+ v2 = None,
+ jammered = false,
+ v4 = None,
+ v5 = None,
+ PlanetSideGUID(0)
+ )
),
255
)
@@ -742,7 +751,7 @@ class ConverterTest extends Specification {
obj.Definition.Packet.DetailedConstructorData(obj) match {
case Success(pkt) =>
pkt mustEqual DetailedLockerContainerData(
- CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
+ CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0)),
None
)
case _ =>
diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala
index 798b4beb2..ce9364c6f 100644
--- a/src/test/scala/objects/DamageableTest.scala
+++ b/src/test/scala/objects/DamageableTest.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2020 PSForever
package objects
-import akka.actor.Props
+import akka.actor.{ActorRef, Props}
import akka.actor.testkit.typed.scaladsl.ActorTestKit
import akka.testkit.TestProbe
import base.{ActorTest, FreedContextActorTest}
@@ -31,17 +31,19 @@ import org.specs2.mutable.Specification
import scala.concurrent.duration._
import net.psforever.objects.avatar.Avatar
+import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalMech, ImplantTerminalMechControl}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.projectile.ProjectileReason
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
class DamageableTest extends Specification {
- val player1 = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val pSource = PlayerSource(player1)
- val weaponA = Tool(GlobalDefinitions.phoenix) //decimator
- val projectileA = weaponA.Projectile
+ val player1: Player = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
+ val pSource: PlayerSource = PlayerSource(player1)
+ val weaponA: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectileA: ProjectileDefinition = weaponA.Projectile
"Damageable" should {
"permit damage" in {
@@ -282,16 +284,20 @@ essentially, treat them more as generic entities whose object types are damageab
see specific object type tests in relation to what those object types does above and beyond that during damage
*/
class DamageableEntityDamageTest extends ActorTest {
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(5))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
+ override def Activity: ActorRef = activityProbe.ref
+ override def AvatarEvents: ActorRef = avatarProbe.ref
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val gen = Generator(GlobalDefinitions.generator) //guid=2
- val player1 =
- Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val gen: Generator = Generator(GlobalDefinitions.generator) //guid=2
+ val player1: Player = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
@@ -300,17 +306,11 @@ class DamageableEntityDamageTest extends ActorTest {
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
-
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val buildingProbe = TestProbe()
- zone.Activity = activityProbe.ref
- zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(gen),
ProjectileReason(
DamageResolution.Hit,
@@ -319,14 +319,14 @@ class DamageableEntityDamageTest extends ActorTest {
),
Vector3(1,0,0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
"DamageableEntity" should {
"handle taking damage" in {
gen.Actor ! Vitality.Damage(applyDamageTo)
- val msg1 = avatarProbe.receiveOne(500 milliseconds)
- val msg2 = activityProbe.receiveOne(500 milliseconds)
+ val msg1 = avatarProbe.receiveOne(5000 milliseconds)
+ val msg2 = activityProbe.receiveOne(5000 milliseconds)
msg1 match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(ValidPlanetSideGUID(2), 0, 3600)) => ()
case _ => assert(false, "DamageableEntity:handle taking damage - player not messaged")
@@ -344,34 +344,34 @@ class DamageableEntityDamageTest extends ActorTest {
class DamageableEntityDestroyedTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(5))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
- val activityProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
zone.Activity = activityProbe.ref
- val mech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
+ val mech: ImplantTerminalMech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
mech.Position = Vector3(1, 0, 0)
mech.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], mech), "mech-control")
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Position = Vector3(14, 0, 0) //<14m from generator; dies
player1.Spawn()
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = mech
- val buildingProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
building.Actor = buildingProbe.ref
guid.register(building, 1)
guid.register(mech, 2)
guid.register(player1, 3)
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(mech),
ProjectileReason(
DamageResolution.Hit,
@@ -388,7 +388,7 @@ class DamageableEntityDestroyedTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -420,14 +420,14 @@ class DamageableEntityDestroyedTest extends ActorTest {
class DamageableEntityNotDestroyTwice extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val gen = Generator(GlobalDefinitions.generator) //guid=2
- val player1 =
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val gen: Generator = Generator(GlobalDefinitions.generator) //guid=2
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn()
guid.register(building, 1)
@@ -438,16 +438,16 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val buildingProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(gen),
ProjectileReason(
DamageResolution.Hit,
@@ -464,7 +464,7 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -493,14 +493,20 @@ class DamageableEntityNotDestroyTwice extends ActorTest {
class DamageableAmenityTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
+
+ override def AvatarEvents: ActorRef = avatarProbe.ref
+ override def Activity: ActorRef = activityProbe.ref
GUID(guid)
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val term = Terminal(GlobalDefinitions.order_terminal) //guid=2
- val player1 =
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val term: Terminal = Terminal(GlobalDefinitions.order_terminal) //guid=2
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn()
guid.register(building, 1)
@@ -510,17 +516,13 @@ class DamageableAmenityTest extends ActorTest {
building.Zone = zone
building.Amenities = term
term.Position = Vector3(1, 0, 0)
+ term.Zone = zone
term.Actor = system.actorOf(Props(classOf[TerminalControl], term), "terminal-control")
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val buildingProbe = TestProbe()
- zone.Activity = activityProbe.ref
- zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(term),
ProjectileReason(
DamageResolution.Hit,
@@ -537,7 +539,7 @@ class DamageableAmenityTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -549,31 +551,23 @@ class DamageableAmenityTest extends ActorTest {
assert(!term.Destroyed)
term.Actor ! Vitality.Damage(applyDamageTo)
- val msg1234 = avatarProbe.receiveN(4, 500 milliseconds)
- assert(
- msg1234.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
- case _ => false
- }
- )
- assert(
- msg1234(1) match {
- case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
- case _ => false
- }
- )
- assert(
- msg1234(2) match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 1)) => true
- case _ => false
- }
- )
- assert(
- msg1234(3) match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 1)) => true
- case _ => false
- }
- )
+ val msg1234 = avatarProbe.receiveN(4, 3000 milliseconds)
+ msg1234.head match {
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => ()
+ case _ => assert(false)
+ }
+ msg1234(1) match {
+ case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => ()
+ case _ => assert(false)
+ }
+ msg1234(2) match {
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 1)) => ()
+ case _ => assert(false)
+ }
+ msg1234(3) match {
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 1)) => ()
+ case _ => assert(false)
+ }
assert(term.Health <= term.Definition.DamageDestroysAt)
assert(term.Destroyed)
}
@@ -583,19 +577,19 @@ class DamageableAmenityTest extends ActorTest {
class DamageableMountableDamageTest extends ActorTest {
//TODO this test with not send HitHint packets because LivePlayers is not being allocated for the players in the zone
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val mech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
- val player1 =
- Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val mech: ImplantTerminalMech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
+ val player1: Player = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
+ player1.Zone = zone
player1.Spawn()
player1.Position = Vector3(2, 2, 2)
- val player2 =
- Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
+ val player2: Player = Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
+ player2.Zone = zone
player2.Spawn()
guid.register(building, 1)
guid.register(mech, 2)
@@ -606,16 +600,16 @@ class DamageableMountableDamageTest extends ActorTest {
building.Amenities = mech
mech.Position = Vector3(1, 0, 0)
mech.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], mech), "mech-control")
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val buildingProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(mech),
ProjectileReason(
DamageResolution.Hit,
@@ -632,7 +626,7 @@ class DamageableMountableDamageTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
mech.Seats(0).mount(player2) //mount the player
player2.VehicleSeated = Some(mech.GUID) //mount the player
expectNoMessage(200 milliseconds)
@@ -643,8 +637,8 @@ class DamageableMountableDamageTest extends ActorTest {
assert(mech.Health == mech.Definition.DefaultHealth)
mech.Actor ! Vitality.Damage(applyDamageTo)
- val msg1_3 = avatarProbe.receiveN(2, 500 milliseconds)
- val msg2 = activityProbe.receiveOne(500 milliseconds)
+ val msg1_3 = avatarProbe.receiveN(2, 5000 milliseconds)
+ val msg2 = activityProbe.receiveOne(5000 milliseconds)
assert(
msg1_3.head match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
@@ -677,23 +671,25 @@ class DamageableMountableDamageTest extends ActorTest {
class DamageableMountableDestroyTest extends ActorTest {
//TODO this test with not send HitHint packets because LivePlayers is not being allocated for the players in the zone
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val mech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
- val player1 =
- Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val mech: ImplantTerminalMech = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) //guid=2
+ val player1: Player = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
+ mech.Zone = zone
player1.Spawn()
+ player1.Zone = zone
player1.Position = Vector3(2, 2, 2)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn()
- val player2Probe = TestProbe()
+ player2.Zone = zone
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
guid.register(building, 1)
guid.register(mech, 2)
@@ -704,16 +700,16 @@ class DamageableMountableDestroyTest extends ActorTest {
building.Amenities = mech
mech.Position = Vector3(1, 0, 0)
mech.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], mech), "mech-control")
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val buildingProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(mech),
ProjectileReason(
DamageResolution.Hit,
@@ -730,7 +726,7 @@ class DamageableMountableDestroyTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
mech.Seats(0).mount(player2) //mount the player
player2.VehicleSeated = Some(mech.GUID) //mount the player
expectNoMessage(200 milliseconds)
@@ -746,24 +742,19 @@ class DamageableMountableDestroyTest extends ActorTest {
val msg12 = avatarProbe.receiveN(2, 500 milliseconds)
player1Probe.expectNoMessage(500 milliseconds)
val msg3 = player2Probe.receiveOne(200 milliseconds)
- assert(
- msg12.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
- case _ => false
- }
- )
- assert(
- msg12(1) match {
- case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true
- case _ => false
- }
- )
- assert(
- msg3 match {
- case Player.Die(_) => true
- case _ => false
- }
- )
+
+ msg12.head match {
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => ()
+ case _ => assert(false)
+ }
+ msg12(1) match {
+ case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => ()
+ case _ => assert(false)
+ }
+ msg3 match {
+ case Player.Die(_) => ()
+ case _ => assert(false)
+ }
assert(mech.Health <= mech.Definition.DamageDestroysAt)
assert(mech.Destroyed)
}
@@ -772,13 +763,13 @@ class DamageableMountableDestroyTest extends ActorTest {
class DamageableWeaponTurretDamageTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
@@ -788,17 +779,17 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
turret.LogActivity(SpawningActivity(SourceEntry(turret), zone.Number, None)) //seed a spawn event
- val tSource = SourceEntry(turret)
- val player1 =
+ val tSource: SourceEntry = SourceEntry(turret)
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn()
player1.Position = Vector3(2, 2, 2)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
guid.register(turret, 2)
guid.register(player1, 3)
@@ -806,10 +797,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
turret.Seats(0).mount(player2)
player2.VehicleSeated = turret.GUID
- val weapon = Tool(GlobalDefinitions.suppressor)
- val projectile = weapon.Projectile
- val pSource = PlayerSource(player1)
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.suppressor)
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val pSource: PlayerSource = PlayerSource(player1)
+ val resolved: DamageInteraction = DamageInteraction(
tSource,
ProjectileReason(
DamageResolution.Hit,
@@ -826,7 +817,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -870,13 +861,13 @@ class DamageableWeaponTurretDamageTest extends ActorTest {
class DamageableWeaponTurretJammerTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
@@ -885,18 +876,18 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
turret.Actor = system.actorOf(Props(classOf[TurretControl], turret), "turret-control")
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
- val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
+ val turretWeapon: Tool = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn()
player1.Position = Vector3(2, 2, 2)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
guid.register(turret, 2)
@@ -907,10 +898,10 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
turret.Seats(0).mount(player2)
player2.VehicleSeated = turret.GUID
- val weapon = Tool(GlobalDefinitions.jammer_grenade)
- val projectile = weapon.Projectile
- val turretSource = SourceEntry(turret)
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.jammer_grenade)
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val turretSource: SourceEntry = SourceEntry(turret)
+ val resolved: DamageInteraction = DamageInteraction(
turretSource,
ProjectileReason(
DamageResolution.Hit,
@@ -927,7 +918,7 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -967,15 +958,15 @@ class DamageableWeaponTurretJammerTest extends ActorTest {
class DamageableWeaponTurretDestructionTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
- val buildingProbe = TestProbe()
+ val building: Building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
+ val buildingProbe: TestProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
@@ -986,18 +977,18 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
turret.Actor = system.actorOf(Props(classOf[FacilityTurretControl], turret), "turret-control")
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
- val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
+ val turretWeapon: Tool = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn()
player1.Position = Vector3(2, 2, 2)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(1, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=4
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
guid.register(building, 1)
@@ -1012,11 +1003,11 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
building.Zone = zone
building.Amenities = turret
- val turretSource = SourceEntry(turret)
+ val turretSource: SourceEntry = SourceEntry(turret)
//turret.History(EntitySpawn(turretSource, zone)) //seed a spawn event
- val weaponA = Tool(GlobalDefinitions.jammer_grenade)
- val projectileA = weaponA.Projectile
- val resolvedA = DamageInteraction(
+ val weaponA: Tool = Tool(GlobalDefinitions.jammer_grenade)
+ val projectileA: ProjectileDefinition = weaponA.Projectile
+ val resolvedA: DamageInteraction = DamageInteraction(
turretSource,
ProjectileReason(
DamageResolution.Hit,
@@ -1033,11 +1024,11 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageToA = resolvedA.calculate()
+ val applyDamageToA: Output = resolvedA.calculate()
- val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
- val projectileB = weaponB.Projectile
- val resolvedB = DamageInteraction(
+ val weaponB: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectileB: ProjectileDefinition = weaponB.Projectile
+ val resolvedB: DamageInteraction = DamageInteraction(
turretSource,
ProjectileReason(
@@ -1055,7 +1046,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageToB = resolvedB.calculate()
+ val applyDamageToB: Output = resolvedB.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1089,7 +1080,7 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
assert(false, s"DamageableWeaponTurretDestructionTest-2: ${msg12_4(1)}")
}
msg3 match {
- case Player.Die(_) => true
+ case Player.Die(_) => ()
case _ =>
assert(false, s"DamageableWeaponTurretDestructionTest-3: player not dead - $msg3")
}
@@ -1104,10 +1095,8 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
assert(false, s"DamageableWeaponTurretDestructionTest-5: ${msg56.head}")
}
msg56(1) match {
- case VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(t, _, TurretUpgrade.None, _)) if t eq turret => ;
- true
- case _ =>
- assert(false, s"DamageableWeaponTurretDestructionTest-6: ${msg56(1)}")
+ case VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(t, _, TurretUpgrade.None, _)) if t eq turret => ()
+ case _ => assert(false, s"DamageableWeaponTurretDestructionTest-6: ${msg56(1)}")
}
assert(turret.Health <= turret.Definition.DamageDestroysAt)
assert(!turret.Jammed)
@@ -1118,33 +1107,33 @@ class DamageableWeaponTurretDestructionTest extends ActorTest {
class DamageableVehicleDamageTest extends ActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
- val atv = Vehicle(GlobalDefinitions.quadstealth) //guid=1
+ val atv: Vehicle = Vehicle(GlobalDefinitions.quadstealth) //guid=1
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
atv.Position = Vector3(1, 0, 0)
//atv.History(EntitySpawn(turretSource, zone)) //seed a spawn event
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
player1.Spawn()
player1.Position = Vector3(2, 0, 0)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
guid.register(atv, 1)
@@ -1154,11 +1143,11 @@ class DamageableVehicleDamageTest extends ActorTest {
atv.Seats(0).mount(player2)
player2.VehicleSeated = atv.GUID
- val weapon = Tool(GlobalDefinitions.suppressor)
- val projectile = weapon.Projectile
- val pSource = PlayerSource(player1)
- val vehicleSource = SourceEntry(atv)
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.suppressor)
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val pSource: PlayerSource = PlayerSource(player1)
+ val vehicleSource: SourceEntry = SourceEntry(atv)
+ val resolved: DamageInteraction = DamageInteraction(
vehicleSource,
ProjectileReason(
DamageResolution.Hit,
@@ -1175,7 +1164,7 @@ class DamageableVehicleDamageTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1228,13 +1217,13 @@ class DamageableVehicleDamageTest extends ActorTest {
class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
@@ -1242,28 +1231,28 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
import akka.actor.typed.scaladsl.adapter._
zone.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
- val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=1 & 4,5,6,7,8,9
+ val lodestar: Vehicle = Vehicle(GlobalDefinitions.lodestar) //guid=1 & 4,5,6,7,8,9
lodestar.Position = Vector3(1, 0, 0)
//lodestar.History(EntitySpawn(VehicleSource(lodestar), zone)) //seed a spawn event
- val atv = Vehicle(GlobalDefinitions.quadstealth) //guid=11
+ val atv: Vehicle = Vehicle(GlobalDefinitions.quadstealth) //guid=11
atv.Position = Vector3(1, 0, 0)
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
player1.Spawn()
player1.Position = Vector3(2, 0, 0)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
- val player3 =
+ val player3: Player =
Player(Avatar(0, "TestCharacter3", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=10
player3.Spawn()
- val player3Probe = TestProbe()
+ val player3Probe: TestProbe = TestProbe()
player3.Actor = player3Probe.ref
guid.register(lodestar, 1)
@@ -1289,11 +1278,11 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
lodestar.CargoHolds(1).mount(atv)
atv.MountedIn = lodestar.GUID
- val weapon = Tool(GlobalDefinitions.phoenix) //decimator
- val projectile = weapon.Projectile
- val pSource = PlayerSource(player1)
- val vSource = SourceEntry(lodestar)
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.phoenix) //decimator
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val pSource: PlayerSource = PlayerSource(player1)
+ val vSource: SourceEntry = SourceEntry(lodestar)
+ val resolved: DamageInteraction = DamageInteraction(
vSource,
ProjectileReason(
DamageResolution.Hit,
@@ -1310,7 +1299,7 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1364,40 +1353,40 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest {
class DamageableVehicleJammeringMountedTest extends FreedContextActorTest {
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
- val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
+ val atv: Vehicle = Vehicle(GlobalDefinitions.quadassault) //guid=1
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
atv.Position = Vector3(1, 0, 0)
- val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
+ val atvWeapon: Tool = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
- val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=6
+ val lodestar: Vehicle = Vehicle(GlobalDefinitions.lodestar) //guid=6
lodestar.Position = Vector3(1, 0, 0)
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=7
player1.Spawn()
player1.Position = Vector3(2, 0, 0)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=8
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
player2.Actor = player2Probe.ref
- val player3 =
+ val player3: Player =
Player(Avatar(0, "TestCharacter3", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=9
player3.Spawn()
- val player3Probe = TestProbe()
+ val player3Probe: TestProbe = TestProbe()
player3.Actor = player3Probe.ref
guid.register(atv, 1)
@@ -1424,10 +1413,10 @@ class DamageableVehicleJammeringMountedTest extends FreedContextActorTest {
lodestar.CargoHolds(1).mount(atv)
atv.MountedIn = lodestar.GUID
- val vehicleSource = SourceEntry(lodestar)
- val weapon = Tool(GlobalDefinitions.jammer_grenade)
- val projectile = weapon.Projectile
- val resolved = DamageInteraction(
+ val vehicleSource: SourceEntry = SourceEntry(lodestar)
+ val weapon: Tool = Tool(GlobalDefinitions.jammer_grenade)
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val resolved: DamageInteraction = DamageInteraction(
vehicleSource,
ProjectileReason(
DamageResolution.Hit,
@@ -1444,7 +1433,7 @@ class DamageableVehicleJammeringMountedTest extends FreedContextActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1478,34 +1467,38 @@ class DamageableVehicleJammeringMountedTest extends FreedContextActorTest {
}
class DamageableVehicleDestroyTest extends ActorTest {
+ val activityProbe: TestProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val vehicleProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(10))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
+ override def Activity: ActorRef = activityProbe.ref
+ override def AvatarEvents: ActorRef = avatarProbe.ref
+ override def VehicleEvents: ActorRef = vehicleProbe.ref
+
GUID(guid)
}
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
zone.actor = ActorTestKit().createTestProbe[ZoneActor.Command]().ref
- zone.Activity = activityProbe.ref
- zone.AvatarEvents = avatarProbe.ref
- zone.VehicleEvents = vehicleProbe.ref
- val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
+ val atv: Vehicle = Vehicle(GlobalDefinitions.quadassault) //guid=1
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
+ atv.Zone = zone
atv.Position = Vector3(1, 0, 0)
- val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
+ val atvWeapon: Tool = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
player1.Spawn()
+ player1.Zone = zone
player1.Position = Vector3(2, 0, 0)
- val player1Probe = TestProbe()
+ val player1Probe: TestProbe = TestProbe()
player1.Actor = player1Probe.ref
- val player2 =
+ val player2: Player =
Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=3
player2.Spawn()
- val player2Probe = TestProbe()
+ val player2Probe: TestProbe = TestProbe()
+ player2.Zone = zone
player2.Actor = player2Probe.ref
guid.register(atv, 1)
@@ -1513,14 +1506,13 @@ class DamageableVehicleDestroyTest extends ActorTest {
guid.register(player2, 3)
guid.register(atvWeapon, 4)
guid.register(atvWeapon.AmmoSlot.Box, 5)
- atv.Zone = zone
atv.Seats(0).mount(player2)
player2.VehicleSeated = atv.GUID
- val weapon = Tool(GlobalDefinitions.suppressor)
- val projectile = weapon.Projectile
- val vehicleSource = SourceEntry(atv)
- val resolved = DamageInteraction(
+ val weapon: Tool = Tool(GlobalDefinitions.suppressor)
+ val projectile: ProjectileDefinition = weapon.Projectile
+ val vehicleSource: SourceEntry = SourceEntry(atv)
+ val resolved: DamageInteraction = DamageInteraction(
vehicleSource,
ProjectileReason(
DamageResolution.Hit,
@@ -1537,7 +1529,7 @@ class DamageableVehicleDestroyTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
@@ -1550,32 +1542,20 @@ class DamageableVehicleDestroyTest extends ActorTest {
assert(!atv.Destroyed)
atv.Actor ! Vitality.Damage(applyDamageTo)
- val msg124 = avatarProbe.receiveN(3, 500 milliseconds)
- val msg3 = player2Probe.receiveOne(200 milliseconds)
- assert(
- msg124.head match {
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
- }
- )
- assert(
- msg124(1) match {
- case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => true
- case _ => false
- }
- )
- assert(
- msg3 match {
- case Player.Die(_) => true
- case _ => false
- }
- )
- assert(
- msg124(2) match {
- case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(4), _)) => true
- case _ => false
- }
- )
+ val msg124 = avatarProbe.receiveN(2, 3000 milliseconds)
+ val msg3 = player2Probe.receiveOne(3000 milliseconds)
+ msg124.head match {
+ case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => ()
+ case _ => assert(false)
+ }
+ msg124(1) match {
+ case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => ()
+ case _ => assert(false)
+ }
+ msg3 match {
+ case Player.Die(_) => ()
+ case _ => assert(false)
+ }
assert(atv.Health <= atv.Definition.DamageDestroysAt)
assert(atv.Destroyed)
//
@@ -1583,221 +1563,221 @@ class DamageableVehicleDestroyTest extends ActorTest {
}
}
-class DamageableVehicleDestroyMountedTest extends FreedContextActorTest {
- val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
- atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
- atv.Position = Vector3(1, 0, 0)
- val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
-
- val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=6
- lodestar.Position = Vector3(1, 0, 0)
-
- val player1 =
- Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=7
- player1.Spawn()
- player1.Position = Vector3(2, 0, 0)
- val player1Probe = TestProbe()
- player1.Actor = player1Probe.ref
- val player2 =
- Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=8
- player2.Spawn()
- val player2Probe = TestProbe()
- player2.Actor = player2Probe.ref
- val player3 =
- Player(Avatar(0, "TestCharacter3", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=9
- player3.Spawn()
- val player3Probe = TestProbe()
- player3.Actor = player3Probe.ref
-
- val activityProbe = TestProbe()
- val avatarProbe = TestProbe()
- val vehicleProbe = TestProbe()
- val catchall = TestProbe()
- val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
- GUID(guid)
- override def LivePlayers = List(player1, player2, player3)
- override def Activity = activityProbe.ref
- override def AvatarEvents = avatarProbe.ref
- override def VehicleEvents = vehicleProbe.ref
- import akka.actor.typed.scaladsl.adapter._
- this.actor = catchall.ref.toTyped[ZoneActor.Command]
- }
-
- guid.register(atv, 1)
- guid.register(atvWeapon, 2)
- guid.register(atvWeapon.AmmoSlot.Box, 3)
- guid.register(lodestar, 4)
- guid.register(lodestar.Utilities(2)(), 5)
- guid.register(lodestar.Utilities(3)(), 6)
- guid.register(lodestar.Utilities(4)(), 7)
- guid.register(lodestar.Utilities(5)(), 8)
- guid.register(lodestar.Utilities(6)(), 9)
- guid.register(lodestar.Utilities(7)(), 10)
- guid.register(player1, 11)
- guid.register(player2, 12)
- guid.register(player3, 13)
-
- lodestar.Definition.Initialize(lodestar, context)
- atv.Zone = zone
- lodestar.Zone = zone
- atv.Seats(0).mount(player2)
- player2.VehicleSeated = atv.GUID
- lodestar.Seats(0).mount(player3)
- player3.VehicleSeated = lodestar.GUID
- lodestar.CargoHolds(1).mount(atv)
- atv.MountedIn = lodestar.GUID
-
- val vehicleSource = SourceEntry(lodestar)
- val weaponA = Tool(GlobalDefinitions.jammer_grenade)
- val projectileA = weaponA.Projectile
- val resolvedA = DamageInteraction(
- vehicleSource,
- ProjectileReason(
- DamageResolution.Hit,
- Projectile(
- projectileA,
- weaponA.Definition,
- weaponA.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
- lodestar.DamageModel
- ),
- Vector3(1, 0, 0)
- )
- val applyDamageToA = resolvedA.calculate()
-
- val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
- val projectileB = weaponB.Projectile
- val resolvedB = DamageInteraction(
- vehicleSource,
- ProjectileReason(
- DamageResolution.Hit,
- Projectile(
- projectileB,
- weaponB.Definition,
- weaponB.FireMode,
- PlayerSource(player1),
- 0,
- Vector3(2, 0, 0),
- Vector3(-1, 0, 0)
- ),
- lodestar.DamageModel
- ),
- Vector3(1, 0, 0)
- )
- val applyDamageToB = resolvedB.calculate()
- expectNoMessage(200 milliseconds)
- //we're not testing that the math is correct
-
- "handle jammering with mounted vehicles" in {
- lodestar.Health = lodestar.Definition.DamageDestroysAt + 1 //initial state manip
- atv.Shields = 1 //initial state manip
- assert(lodestar.Health > lodestar.Definition.DamageDestroysAt)
- assert(!lodestar.Jammed)
- assert(!lodestar.Destroyed)
- assert(atv.Health == atv.Definition.DefaultHealth)
- assert(atv.Shields == 1)
- assert(!atv.Jammed)
- assert(!atv.Destroyed)
-
- lodestar.Actor ! Vitality.Damage(applyDamageToA)
- vehicleProbe.receiveOne(500 milliseconds) //flush jammered message
- avatarProbe.expectNoMessage(200 milliseconds)
- player1Probe.expectNoMessage(200 milliseconds)
- player2Probe.expectNoMessage(200 milliseconds)
- player3Probe.expectNoMessage(200 milliseconds)
- assert(lodestar.Health > lodestar.Definition.DamageDestroysAt)
- assert(lodestar.Jammed)
- assert(!lodestar.Destroyed)
- assert(atv.Health == atv.Definition.DefaultHealth)
- assert(atv.Shields == 1)
- assert(!atv.Jammed)
- assert(!atv.Destroyed)
-
- lodestar.Actor ! Vitality.Damage(applyDamageToB)
- val msg_avatar = avatarProbe.receiveN(5, 500 milliseconds)
- avatarProbe.expectNoMessage(10 milliseconds)
- val msg_player2 = player2Probe.receiveOne(200 milliseconds)
- player2Probe.expectNoMessage(10 milliseconds)
- val msg_player3 = player3Probe.receiveOne(200 milliseconds)
- player3Probe.expectNoMessage(10 milliseconds)
- val msg_vehicle = vehicleProbe.receiveN(2, 200 milliseconds)
- vehicleProbe.expectNoMessage(10 milliseconds)
- assert(
- msg_avatar.exists({
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(4), 0, _)) => true
- case _ => false
- })
- )
- assert(
- msg_avatar.exists({
- case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(4), _, _, Vector3(1, 0, 0))) => true
- case _ => false
- })
- )
- assert(
- msg_avatar.exists({
- case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
- case _ => false
- })
- )
- assert(
- msg_avatar.exists({
- case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => true
- case _ => false
- })
- )
- assert(
- msg_avatar.exists({
- case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(2), _)) => true
- case _ => false
- })
- )
- assert(
- msg_player2 match {
- case Player.Die(_) => true
- case _ => false
- }
- )
- assert(
- msg_player3 match {
- case Player.Die(_) => true
- case _ => false
- }
- )
- assert(
- msg_vehicle.exists({
- case VehicleServiceMessage(
- "test",
- VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(4), 27, 0)
- ) =>
- true
- case _ => false
- })
- )
- assert(
- msg_vehicle.exists({
- case VehicleServiceMessage(
- "test",
- VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(1), 68, 0)
- ) =>
- true
- case _ => false
- })
- )
- assert(lodestar.Health <= lodestar.Definition.DamageDestroysAt)
- assert(!lodestar.Jammed)
- assert(lodestar.Destroyed)
- assert(atv.Health <= atv.Definition.DefaultHealth)
- assert(atv.Shields == 0)
- assert(!atv.Jammed)
- assert(atv.Destroyed)
- }
-}
+//class DamageableVehicleDestroyMountedTest extends FreedContextActorTest {
+// val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1
+// atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "atv-control")
+// atv.Position = Vector3(1, 0, 0)
+// val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool] //guid=4 & 5
+//
+// val lodestar = Vehicle(GlobalDefinitions.lodestar) //guid=6
+// lodestar.Position = Vector3(1, 0, 0)
+//
+// val player1 =
+// Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=7
+// player1.Spawn()
+// player1.Position = Vector3(2, 0, 0)
+// val player1Probe = TestProbe()
+// player1.Actor = player1Probe.ref
+// val player2 =
+// Player(Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=8
+// player2.Spawn()
+// val player2Probe = TestProbe()
+// player2.Actor = player2Probe.ref
+// val player3 =
+// Player(Avatar(0, "TestCharacter3", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=9
+// player3.Spawn()
+// val player3Probe = TestProbe()
+// player3.Actor = player3Probe.ref
+//
+// val activityProbe = TestProbe()
+// val avatarProbe = TestProbe()
+// val vehicleProbe = TestProbe()
+// val catchall = TestProbe()
+// val guid = new NumberPoolHub(new MaxNumberSource(15))
+// val zone = new Zone("test", new ZoneMap("test"), 0) {
+// override def SetupNumberPools() = {}
+// GUID(guid)
+// override def LivePlayers = List(player1, player2, player3)
+// override def Activity = activityProbe.ref
+// override def AvatarEvents = avatarProbe.ref
+// override def VehicleEvents = vehicleProbe.ref
+// import akka.actor.typed.scaladsl.adapter._
+// this.actor = catchall.ref.toTyped[ZoneActor.Command]
+// }
+//
+// guid.register(atv, 1)
+// guid.register(atvWeapon, 2)
+// guid.register(atvWeapon.AmmoSlot.Box, 3)
+// guid.register(lodestar, 4)
+// guid.register(lodestar.Utilities(2)(), 5)
+// guid.register(lodestar.Utilities(3)(), 6)
+// guid.register(lodestar.Utilities(4)(), 7)
+// guid.register(lodestar.Utilities(5)(), 8)
+// guid.register(lodestar.Utilities(6)(), 9)
+// guid.register(lodestar.Utilities(7)(), 10)
+// guid.register(player1, 11)
+// guid.register(player2, 12)
+// guid.register(player3, 13)
+//
+// lodestar.Definition.Initialize(lodestar, context)
+// atv.Zone = zone
+// lodestar.Zone = zone
+// atv.Seats(0).mount(player2)
+// player2.VehicleSeated = atv.GUID
+// lodestar.Seats(0).mount(player3)
+// player3.VehicleSeated = lodestar.GUID
+// lodestar.CargoHolds(1).mount(atv)
+// atv.MountedIn = lodestar.GUID
+//
+// val vehicleSource = SourceEntry(lodestar)
+// val weaponA = Tool(GlobalDefinitions.jammer_grenade)
+// val projectileA = weaponA.Projectile
+// val resolvedA = DamageInteraction(
+// vehicleSource,
+// ProjectileReason(
+// DamageResolution.Hit,
+// Projectile(
+// projectileA,
+// weaponA.Definition,
+// weaponA.FireMode,
+// PlayerSource(player1),
+// 0,
+// Vector3(2, 0, 0),
+// Vector3(-1, 0, 0)
+// ),
+// lodestar.DamageModel
+// ),
+// Vector3(1, 0, 0)
+// )
+// val applyDamageToA = resolvedA.calculate()
+//
+// val weaponB = Tool(GlobalDefinitions.phoenix) //decimator
+// val projectileB = weaponB.Projectile
+// val resolvedB = DamageInteraction(
+// vehicleSource,
+// ProjectileReason(
+// DamageResolution.Hit,
+// Projectile(
+// projectileB,
+// weaponB.Definition,
+// weaponB.FireMode,
+// PlayerSource(player1),
+// 0,
+// Vector3(2, 0, 0),
+// Vector3(-1, 0, 0)
+// ),
+// lodestar.DamageModel
+// ),
+// Vector3(1, 0, 0)
+// )
+// val applyDamageToB = resolvedB.calculate()
+// expectNoMessage(200 milliseconds)
+// //we're not testing that the math is correct
+//
+// "handle jammering with mounted vehicles" in {
+// lodestar.Health = lodestar.Definition.DamageDestroysAt + 1 //initial state manip
+// atv.Shields = 1 //initial state manip
+// assert(lodestar.Health > lodestar.Definition.DamageDestroysAt)
+// assert(!lodestar.Jammed)
+// assert(!lodestar.Destroyed)
+// assert(atv.Health == atv.Definition.DefaultHealth)
+// assert(atv.Shields == 1)
+// assert(!atv.Jammed)
+// assert(!atv.Destroyed)
+//
+// lodestar.Actor ! Vitality.Damage(applyDamageToA)
+// vehicleProbe.receiveOne(500 milliseconds) //flush jammered message
+// avatarProbe.expectNoMessage(200 milliseconds)
+// player1Probe.expectNoMessage(200 milliseconds)
+// player2Probe.expectNoMessage(200 milliseconds)
+// player3Probe.expectNoMessage(200 milliseconds)
+// assert(lodestar.Health > lodestar.Definition.DamageDestroysAt)
+// assert(lodestar.Jammed)
+// assert(!lodestar.Destroyed)
+// assert(atv.Health == atv.Definition.DefaultHealth)
+// assert(atv.Shields == 1)
+// assert(!atv.Jammed)
+// assert(!atv.Destroyed)
+//
+// lodestar.Actor ! Vitality.Damage(applyDamageToB)
+// val msg_avatar = avatarProbe.receiveN(2, 500 milliseconds) //2
+// avatarProbe.expectNoMessage(10 milliseconds)
+// val msg_player2 = player2Probe.receiveOne(200 milliseconds)
+// player2Probe.expectNoMessage(10 milliseconds)
+// val msg_player3 = player3Probe.receiveOne(200 milliseconds)
+// player3Probe.expectNoMessage(10 milliseconds)
+// val msg_vehicle = receiveN(2, 200 milliseconds)
+// vehicleProbe.expectNoMessage(10 milliseconds)
+// assert(
+// msg_avatar.exists({
+// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(4), 0, _)) => true
+// case _ => false
+// })
+// )
+// assert(
+// msg_avatar.exists({
+// case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(4), _, _, Vector3(1, 0, 0))) => true
+// case _ => false
+// })
+// )
+// assert(
+// msg_avatar.exists({
+// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
+// case _ => false
+// })
+// )
+// assert(
+// msg_avatar.exists({
+// case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => true
+// case _ => false
+// })
+// )
+// assert(
+// msg_avatar.exists({
+// case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(2), _)) => true
+// case _ => false
+// })
+// )
+// assert(
+// msg_player2 match {
+// case Player.Die(_) => true
+// case _ => false
+// }
+// )
+// assert(
+// msg_player3 match {
+// case Player.Die(_) => true
+// case _ => false
+// }
+// )
+// assert(
+// msg_vehicle.exists({
+// case VehicleServiceMessage(
+// "test",
+// VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(4), 27, 0)
+// ) =>
+// true
+// case _ => false
+// })
+// )
+// assert(
+// msg_vehicle.exists({
+// case VehicleServiceMessage(
+// "test",
+// VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(1), 68, 0)
+// ) =>
+// true
+// case _ => false
+// })
+// )
+// assert(lodestar.Health <= lodestar.Definition.DamageDestroysAt)
+// assert(!lodestar.Jammed)
+// assert(lodestar.Destroyed)
+// assert(atv.Health <= atv.Definition.DefaultHealth)
+// assert(atv.Shields == 0)
+// assert(!atv.Jammed)
+// assert(atv.Destroyed)
+// }
+//}
object DamageableTest {}
diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala
index d8a276367..7a3992cfc 100644
--- a/src/test/scala/objects/DeployableTest.scala
+++ b/src/test/scala/objects/DeployableTest.scala
@@ -9,7 +9,7 @@ import net.psforever.objects.ballistics._
import net.psforever.objects.ce.{Deployable, DeployedItem}
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
-import net.psforever.objects.serverobject.mount.{MountInfo, Mountable}
+import net.psforever.objects.serverobject.mount.{MountInfo, Mountable, SeatDefinition}
import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap}
import net.psforever.objects.{TurretDeployable, _}
@@ -25,7 +25,6 @@ import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.projectile.ProjectileReason
import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
-import net.psforever.services.local.LocalAction.DeployableMapIcon
import scala.collection.mutable
import scala.concurrent.duration._
@@ -39,12 +38,13 @@ class DeployableTest extends Specification {
obj.OwnerGuid.contains(PlanetSideGUID(10)) mustEqual true
}
- "know its owner by GUID" in {
-// val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
-// obj.OwnerName.isEmpty mustEqual true
-// obj.OwnerName = "TestCharacter"
-// obj.OwnerName.contains("TestCharacter") mustEqual true
- ko
+ "know its owner by name" in {
+ val obj = new ExplosiveDeployable(GlobalDefinitions.he_mine)
+ val owner = Player(Avatar(1, "TestCharacter", PlanetSideEmpire.TR, CharacterSex.Male, 1, CharacterVoice.Mute))
+ owner.GUID = PlanetSideGUID(1)
+ owner.Spawn()
+ obj.AssignOwnership(owner)
+ obj.OwnerName.contains("TestCharacter") mustEqual true
}
"know its faction allegiance" in {
@@ -449,17 +449,16 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
assert(!h_mine.Destroyed)
h_mine.Actor ! Vitality.Damage(applyDamageToH)
- val eventMsgs = eventsProbe.receiveN(4, 200 milliseconds)
- val p1Msgs = player1Probe.receiveN(1, 200 milliseconds)
- val p2Msgs = player2Probe.receiveN(1, 200 milliseconds)
+ val p1Msgs = player1Probe.receiveN(1, 5000 milliseconds)
+ val eventMsgs = eventsProbe.receiveN(3, 5000 milliseconds)
eventMsgs.head match {
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) => ()
case _ => assert(false, "")
}
eventMsgs(1) match {
case LocalServiceMessage("test", LocalAction.Detonate(PlanetSideGUID(2), target))
- if target eq h_mine => ;
+ if target eq h_mine => ()
case _ => assert(false, "")
}
eventMsgs(2) match {
@@ -473,21 +472,10 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
) => ;
case _ => assert(false, "")
}
- eventMsgs(3) match {
- case AvatarServiceMessage(
- "test",
- AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero)
- ) => ;
- case _ => assert(false, "")
- }
p1Msgs.head match {
- case Vitality.Damage(_) => ;
+ case Vitality.Damage(_) => ()
case _ => assert(false, "")
}
- p2Msgs.head match {
- case Player.LoseDeployable(_) => ;
- case _ => assert(false, "")
- }
assert(h_mine.Destroyed)
}
}
@@ -529,8 +517,7 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
guid.register(player2, 4)
guid.register(weapon, 5)
h_mine.Zone = zone
- h_mine.OwnerGuid = player2
- //h_mine.OwnerName = player2.Name
+ h_mine.AssignOwnership(player2)
h_mine.Faction = PlanetSideEmpire.NC
h_mine.Actor = system.actorOf(Props(classOf[MineDeployableControl], h_mine), "h-mine-control")
@@ -748,6 +735,7 @@ class TurretControlBetrayalMountTest extends ActorTest {
val obj = new TurretDeployable(
new TurretDeployableDefinition(685) {
MountPoints += 1 -> MountInfo(0, Vector3.Zero)
+ Seats += 0 -> new SeatDefinition()
FactionLocked = false
} //required (defaults to true)
) {
diff --git a/src/test/scala/objects/FacilityTurretTest.scala b/src/test/scala/objects/FacilityTurretTest.scala
index 359bafa7a..4fcc4f218 100644
--- a/src/test/scala/objects/FacilityTurretTest.scala
+++ b/src/test/scala/objects/FacilityTurretTest.scala
@@ -102,26 +102,32 @@ class FacilityTurretControl1Test extends ActorTest {
}
class FacilityTurretControl2Test extends ActorTest {
- val player = Player(Avatar(0, "", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj = FacilityTurret(GlobalDefinitions.manned_turret)
- obj.GUID = PlanetSideGUID(1)
- obj.Zone = new Zone("test", new ZoneMap("test"), 0) {
+ //todo why does the terminal actor terminate when the building faction is set to a different value?
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
}
+ val player = Player(Avatar(0, "", PlanetSideEmpire.NEUTRAL, CharacterSex.Male, 0, CharacterVoice.Mute))
+ player.Spawn()
+ player.Zone = zone
+ player.GUID = PlanetSideGUID(2)
+ val obj = FacilityTurret(GlobalDefinitions.manned_turret)
+ obj.GUID = PlanetSideGUID(1)
+ obj.Zone = zone
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
- val bldg = Building("Building", guid = 0, map_id = 0, Zone.Nowhere, StructureType.Building)
+ val bldg = Building("Building", guid = 0, map_id = 0, zone, StructureType.Building)
bldg.Amenities = obj
- bldg.Faction = PlanetSideEmpire.TR
+ bldg.Zone = zone
+ //bldg.Faction = PlanetSideEmpire.TR
+ val resultProbe = TestProbe()
"FacilityTurretControl" should {
"mount on faction affiliation when FactionLock is true" in {
- assert(player.Faction == PlanetSideEmpire.TR)
- assert(obj.Faction == PlanetSideEmpire.TR)
+ //assert(player.Faction == obj.Faction)
assert(obj.Definition.FactionLocked)
- obj.Actor ! Mountable.TryMount(player, 1)
- val reply = receiveOne(300 milliseconds)
+ obj.Actor.tell(Mountable.TryMount(player, 1), resultProbe.ref)
+ val reply = resultProbe.receiveOne(5000 milliseconds)
reply match {
case msg: Mountable.MountMessages =>
assert(msg.response.isInstanceOf[Mountable.CanMount])
@@ -133,12 +139,22 @@ class FacilityTurretControl2Test extends ActorTest {
}
class FacilityTurretControl3Test extends ActorTest {
+ val zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools() = {}
+ this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
+ }
val player = Player(Avatar(0, "", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj = FacilityTurret(GlobalDefinitions.manned_turret)
- obj.GUID = PlanetSideGUID(1)
+ player.Spawn()
+ player.Zone = zone
+ player.GUID = PlanetSideGUID(1)
+ val obj = FacilityTurret(GlobalDefinitions.manned_turret)
+ obj.GUID = PlanetSideGUID(2)
+ obj.Zone = zone
obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control")
- val bldg = Building("Building", guid = 0, map_id = 0, Zone.Nowhere, StructureType.Building)
+ val bldg = Building("Building", guid = 0, map_id = 0, zone, StructureType.Building)
bldg.Amenities = obj
+ bldg.Zone = zone
+ val resultProbe = TestProbe()
"FacilityTurretControl" should {
"block seating on mismatched faction affiliation when FactionLock is true" in {
@@ -146,8 +162,8 @@ class FacilityTurretControl3Test extends ActorTest {
assert(obj.Faction == PlanetSideEmpire.NEUTRAL)
assert(obj.Definition.FactionLocked)
- obj.Actor ! Mountable.TryMount(player, 1)
- val reply = receiveOne(300 milliseconds)
+ obj.Actor.tell(Mountable.TryMount(player, 1), resultProbe.ref)
+ val reply = resultProbe.receiveOne(5000 milliseconds)
reply match {
case msg: Mountable.MountMessages =>
assert(msg.response.isInstanceOf[Mountable.CanNotMount])
diff --git a/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala b/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala
index 2e72d7776..23d2765e1 100644
--- a/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala
+++ b/src/test/scala/objects/InteractsWithZoneEnvironmentTest.scala
@@ -1,26 +1,32 @@
package objects
+import akka.actor.ActorRef
import akka.testkit.TestProbe
import base.ActorTest
-import net.psforever.objects.definition.ObjectDefinition
+import net.psforever.objects.Player
+import net.psforever.objects.avatar.Avatar
import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
import net.psforever.objects.serverobject.environment._
-import net.psforever.objects.serverobject.environment.interaction.{EscapeFromEnvironment, InteractWithEnvironment, InteractingWithEnvironment}
-import net.psforever.objects.vital.{Vitality, VitalityDefinition}
+import net.psforever.objects.serverobject.environment.interaction.RespondsToZoneEnvironment
+import net.psforever.objects.vital.Vitality
import net.psforever.objects.zones.{InteractsWithZone, Zone, ZoneMap}
-import net.psforever.types.{PlanetSideEmpire, Vector3}
+import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, PlanetSideGUID, Vector3}
import scala.concurrent.duration._
class InteractsWithZoneEnvironmentTest extends ActorTest {
- val pool1 = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
- val pool2 = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 15, 5, 10))
- val pool3 = Pool(EnvironmentAttribute.Lava, DeepSquare(-1, 15, 10, 10, 5))
- val testZone = {
+ val pool1: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 10, 10, 0, 0))
+ val pool2: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(5, 10, 15, 5, 10))
+ val pool3: Pool = Pool(EnvironmentAttribute.Lava, DeepSquare(5, 15, 10, 10, 5))
+ val zoneEvents: TestProbe = TestProbe()
+ val testZone: Zone = {
val testMap = new ZoneMap(name = "test-map") {
environment = List(pool1, pool2, pool3)
}
- new Zone("test-zone", testMap, zoneNumber = 0)
+ new Zone("test-zone", testMap, zoneNumber = 0) {
+ override def AvatarEvents: ActorRef = zoneEvents.ref
+ }
}
testZone.blockMap.addTo(pool1)
testZone.blockMap.addTo(pool2)
@@ -32,8 +38,8 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
val obj = InteractsWithZoneEnvironmentTest.testObject()
obj.Zone = testZone
obj.Actor = testProbe.ref
+ obj.Position = Vector3(0,0,50)
- assert(obj.Position == Vector3.Zero)
obj.zoneInteractions()
testProbe.expectNoMessage(max = 500 milliseconds)
}
@@ -44,12 +50,12 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
obj.Zone = testZone
obj.Actor = testProbe.ref
- obj.Position = Vector3(1,1,-2)
+ obj.Position = Vector3(1,1,2)
obj.zoneInteractions()
val msg = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
@@ -57,28 +63,28 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
testProbe.expectNoMessage(max = 500 milliseconds)
}
- "acknowledge ceasation of interaction when moved out of a previous occupied the critical region (just once)" in {
+ "acknowledge cessation of interaction when moved out of a previous occupied the critical region (just once)" in {
val testProbe = TestProbe()
val obj = InteractsWithZoneEnvironmentTest.testObject()
obj.Zone = testZone
obj.Actor = testProbe.ref
- obj.Position = Vector3(1,1,-2)
+ obj.Position = Vector3(1,1,2)
obj.zoneInteractions()
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg1 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
- obj.Position = Vector3(1,1,1)
+ obj.Position = Vector3(1,1,50)
obj.zoneInteractions()
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg2 match {
- case EscapeFromEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
@@ -92,26 +98,25 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
obj.Zone = testZone
obj.Actor = testProbe.ref
- obj.Position = Vector3(7,7,-2)
+ obj.Position = Vector3(7,7,2)
obj.zoneInteractions()
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg1 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
- obj.Position = Vector3(12,7,-2)
+ obj.Position = Vector3(12,7,2)
obj.zoneInteractions()
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg2 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
- assert(pool1.attribute == pool2.attribute)
}
"transition between two different critical regions when the regions have different attributes" in {
@@ -120,32 +125,37 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
obj.Zone = testZone
obj.Actor = testProbe.ref
- obj.Position = Vector3(7,7,-2)
+ obj.Position = Vector3(7,7,2)
obj.zoneInteractions()
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg1 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
- obj.Position = Vector3(7,12,-2)
+ obj.Position = Vector3(7,12,2)
obj.zoneInteractions()
- val msgs = testProbe.receiveN(2, max = 250 milliseconds)
+ val msgs = testProbe.receiveN(3, max = 250 milliseconds)
assert(
msgs.head match {
- case EscapeFromEnvironment(b, _) => (b eq pool1)
+ case Vitality.Damage(_) => true
case _ => false
}
)
assert(
msgs(1) match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case AuraEffectBehavior.StartEffect(Aura.Fire, _) => true
+ case _ => false
+ }
+ )
+ assert(
+ msgs(2) match {
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Lava, _, _, _) => true
case _ => false
}
)
- assert(pool1.attribute != pool3.attribute)
}
}
@@ -155,22 +165,24 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
obj.Zone = testZone
obj.Actor = testProbe.ref
- obj.Position = Vector3(1,1,-2)
+ obj.Position = Vector3(1,1,2)
obj.zoneInteractions()
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg1 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
obj.allowInteraction = false
val msg2 = testProbe.receiveOne(max = 250 milliseconds)
- msg2 match {
- case EscapeFromEnvironment(b, _) => (b eq pool1)
- case _ => assert( false)
- }
+ assert(
+ msg2 match {
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
+ case _ => false
+ }
+ )
obj.zoneInteractions()
testProbe.expectNoMessage(max = 500 milliseconds)
}
@@ -182,7 +194,7 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
obj.Actor = testProbe.ref
obj.allowInteraction = false
- obj.Position = Vector3(1,1,-2)
+ obj.Position = Vector3(1,1,2)
obj.zoneInteractions()
testProbe.expectNoMessage(max = 500 milliseconds)
@@ -190,7 +202,7 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
val msg1 = testProbe.receiveOne(max = 250 milliseconds)
assert(
msg1 match {
- case InteractingWithEnvironment(b, _) => (b eq pool1)
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, _) => true
case _ => false
}
)
@@ -199,15 +211,9 @@ class InteractsWithZoneEnvironmentTest extends ActorTest {
object InteractsWithZoneEnvironmentTest {
def testObject(): PlanetSideServerObject with InteractsWithZone = {
- new PlanetSideServerObject
- with InteractsWithZone {
- interaction(new InteractWithEnvironment())
- def Faction: PlanetSideEmpire.Value = PlanetSideEmpire.VS
- def DamageModel = null
- def Definition: ObjectDefinition with VitalityDefinition = new ObjectDefinition(objectId = 0) with VitalityDefinition {
- Damageable = true
- DrownAtMaxDepth = true
- }
- }
+ val p = new Player(Avatar(1, "test", PlanetSideEmpire.VS, CharacterSex.Male, 1, CharacterVoice.Mute))
+ p.GUID = PlanetSideGUID(1)
+ p.Spawn()
+ p
}
}
diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala
index 6bac58879..d10e7a85b 100644
--- a/src/test/scala/objects/PlayerControlTest.scala
+++ b/src/test/scala/objects/PlayerControlTest.scala
@@ -1,20 +1,20 @@
// Copyright (c) 2020 PSForever
package objects
+import akka.actor.{ActorRef => ClassicActorRef}
import akka.actor.typed.ActorRef
import akka.actor.{ActorSystem, Props}
-import akka.actor.typed.scaladsl.adapter._
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.actors.session.AvatarActor
-import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.{Avatar, Certification, PlayerControl}
import net.psforever.objects.ballistics._
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
-import net.psforever.objects.vital.Vitality
+import net.psforever.objects.vital.{SpawningActivity, Vitality}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.objects._
+import net.psforever.objects.definition.ProjectileDefinition
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.serverobject.environment.{DeepSquare, EnvironmentAttribute, Pool}
@@ -22,6 +22,7 @@ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.base.DamageResolution
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.projectile.ProjectileReason
+import net.psforever.objects.vital.resolution.ResolutionCalculations.Output
import net.psforever.packet.game._
import net.psforever.types._
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@@ -29,17 +30,17 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
class PlayerControlHealTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val player2 =
+ val player2: Player =
Player(Avatar(1, "TestCharacter2", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1, player2)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1, player2)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
player1.Zone = zone
@@ -52,7 +53,7 @@ class PlayerControlHealTest extends ActorTest {
guid.register(player2.avatar.locker, 6)
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2, null), "player2-control")
- val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
+ val tool: Tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -114,15 +115,15 @@ class PlayerControlHealTest extends ActorTest {
}
}
class PlayerControlHealSelfTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
player1.Zone = zone
@@ -131,7 +132,7 @@ class PlayerControlHealSelfTest extends ActorTest {
guid.register(player1.avatar.locker, 5)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, null), "player1-control")
- val tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
+ val tool: Tool = Tool(GlobalDefinitions.medicalapplicator) //guid=3 & 4
guid.register(player1, 1)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
@@ -189,18 +190,18 @@ class PlayerControlHealSelfTest extends ActorTest {
}
class PlayerControlRepairTest extends ActorTest {
- val avatar = Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)
+ val avatar: Avatar = Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)
.copy(certifications = Set(Certification.Engineering))
- val player1 = Player(avatar) //guid=1
- val player2 =
+ val player1: Player = Player(avatar) //guid=1
+ val player2: Player =
Player(Avatar(1, "TestCharacter2", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1, player2)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1, player2)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
player1.Zone = zone
@@ -213,7 +214,7 @@ class PlayerControlRepairTest extends ActorTest {
guid.register(player2.avatar.locker, 6)
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2, null), "player2-control")
- val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
+ val tool: Tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -286,16 +287,16 @@ class PlayerControlRepairTest extends ActorTest {
}
class PlayerControlRepairSelfTest extends ActorTest {
- val avatar = Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)
+ val avatar: Avatar = Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)
.copy(certifications = Set(Certification.Engineering))
- val player1 = Player(avatar) //guid=1
- val avatarProbe = TestProbe()
+ val player1: Player = Player(avatar) //guid=1
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
player1.Zone = zone
@@ -304,7 +305,7 @@ class PlayerControlRepairSelfTest extends ActorTest {
guid.register(player1.avatar.locker, 5)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, null), "player1-control")
- val tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
+ val tool: Tool = Tool(GlobalDefinitions.bank) //guid=3 & 4
guid.register(player1, 1)
guid.register(tool, 3)
guid.register(tool.AmmoSlot.Box, 4)
@@ -362,19 +363,19 @@ class PlayerControlRepairSelfTest extends ActorTest {
}
class PlayerControlDamageTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val player2 =
+ val player2: Player =
Player(Avatar(1, "TestCharacter2", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
- val avatarProbe = TestProbe()
- val activityProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1, player2)
- override def AvatarEvents = avatarProbe.ref
- override def Activity = activityProbe.ref
+ override def LivePlayers: List[Player] = List(player1, player2)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
+ override def Activity: ClassicActorRef = activityProbe.ref
}
player1.Zone = zone
@@ -388,10 +389,10 @@ class PlayerControlDamageTest extends ActorTest {
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2, avatarActor), name = "player2-control")
- val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
- val projectile = tool.Projectile
- val player1Source = PlayerSource(player1)
- val resolved = DamageInteraction(
+ val tool: Tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
+ val projectile: ProjectileDefinition = tool.Projectile
+ val player1Source: PlayerSource = PlayerSource(player1)
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(player2),
ProjectileReason(
DamageResolution.Hit,
@@ -408,7 +409,7 @@ class PlayerControlDamageTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -467,19 +468,19 @@ class PlayerControlDamageTest extends ActorTest {
}
class PlayerControlDeathStandingTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val player2 =
+ val player2: Player =
Player(Avatar(1, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=2
- val avatarProbe = TestProbe()
- val activityProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
+ val activityProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val zone = new Zone("test", new ZoneMap("test"), 0) {
- override def SetupNumberPools() = {}
+ val zone: Zone = new Zone("test", new ZoneMap("test"), 0) {
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1, player2)
- override def AvatarEvents = avatarProbe.ref
- override def Activity = activityProbe.ref
+ override def LivePlayers: List[Player] = List(player1, player2)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
+ override def Activity: ClassicActorRef = activityProbe.ref
}
player1.Zone = zone
@@ -493,10 +494,10 @@ class PlayerControlDeathStandingTest extends ActorTest {
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
player2.Actor = system.actorOf(Props(classOf[PlayerControl], player2, avatarActor), name = "player2-control")
- val tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
- val projectile = tool.Projectile
- val player1Source = PlayerSource(player1)
- val resolved = DamageInteraction(
+ val tool: Tool = Tool(GlobalDefinitions.suppressor) //guid 3 & 4
+ val projectile: ProjectileDefinition = tool.Projectile
+ val player1Source: PlayerSource = PlayerSource(player1)
+ val resolved: DamageInteraction = DamageInteraction(
SourceEntry(player2),
ProjectileReason(
DamageResolution.Hit,
@@ -513,7 +514,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
),
Vector3(1, 0, 0)
)
- val applyDamageTo = resolved.calculate()
+ val applyDamageTo: Output = resolved.calculate()
guid.register(player1, 1)
guid.register(player2, 2)
guid.register(tool, 3)
@@ -543,8 +544,8 @@ class PlayerControlDeathStandingTest extends ActorTest {
)
assert(
msg_stamina match {
- case AvatarActor.DeinitializeImplants() => true
- case _ => false
+ case AvatarActor.DeinitializeImplants => true
+ case _ => false
}
)
assert(
@@ -684,8 +685,8 @@ class PlayerControlDeathStandingTest extends ActorTest {
// activityProbe.expectNoMessage(200 milliseconds)
// assert(
// msg_stamina match {
-// case AvatarActor.DeinitializeImplants() => true
-// case _ => false
+// case AvatarActor.DeinitializeImplants => true
+// case _ => false
// }
// )
// assert(
@@ -774,22 +775,22 @@ class PlayerControlDeathStandingTest extends ActorTest {
//}
class PlayerControlInteractWithWaterTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
- val zone = new Zone(
+ val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
+ val zone: Zone = new Zone(
id = "test",
new ZoneMap(name = "test-map") {
environment = List(pool)
},
zoneNumber = 0
) {
- override def SetupNumberPools() = {}
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
zone.blockMap.addTo(player1)
zone.blockMap.addTo(pool)
@@ -828,22 +829,22 @@ class PlayerControlInteractWithWaterTest extends ActorTest {
}
class PlayerControlStopInteractWithWaterTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
- val zone = new Zone(
+ val pool: Pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
+ val zone: Zone = new Zone(
id = "test",
new ZoneMap(name = "test-map") {
environment = List(pool)
},
zoneNumber = 0
) {
- override def SetupNumberPools() = {}
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
zone.blockMap.addTo(player1)
zone.blockMap.addTo(pool)
@@ -893,23 +894,23 @@ class PlayerControlStopInteractWithWaterTest extends ActorTest {
}
class PlayerControlInteractWithLavaTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Lava, DeepSquare(-1, 10, 10, 0, 0))
- val zone = new Zone(
+ val pool: Pool = Pool(EnvironmentAttribute.Lava, DeepSquare(-1, 10, 10, 0, 0))
+ val zone: Zone = new Zone(
id = "test-map",
new ZoneMap(name = "test-map") {
environment = List(pool)
},
zoneNumber = 0
) {
- override def SetupNumberPools() = {}
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
- override def Activity = TestProbe().ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
+ override def Activity: ClassicActorRef = TestProbe().ref
}
zone.blockMap.addTo(player1)
zone.blockMap.addTo(pool)
@@ -948,49 +949,49 @@ class PlayerControlInteractWithLavaTest extends ActorTest {
}
)
assert(player1.Health > 0) //still alive?
- probe.receiveOne(65 seconds) //wait until player1's implants deinitialize
+ probe.receiveOne(65 seconds)
assert(player1.Health == 0) //ded
}
}
}
class PlayerControlInteractWithDeathTest extends ActorTest {
- val player1 =
+ val player1: Player =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
- val avatarProbe = TestProbe()
+ val avatarProbe: TestProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Death, DeepSquare(-1, 10, 10, 0, 0))
- val zone = new Zone(
+ val pool: Pool = Pool(EnvironmentAttribute.Death, DeepSquare(10, 10, 10, 0, 0))
+ val zone: Zone = new Zone(
id = "test-map",
new ZoneMap(name = "test-map") {
environment = List(pool)
},
zoneNumber = 0
) {
- override def SetupNumberPools() = {}
+ override def SetupNumberPools(): Unit = {}
GUID(guid)
- override def LivePlayers = List(player1)
- override def AvatarEvents = avatarProbe.ref
- override def Activity = TestProbe().ref
+ override def LivePlayers: List[Player] = List(player1)
+ override def Activity: ClassicActorRef = TestProbe().ref
+ override def AvatarEvents: ClassicActorRef = avatarProbe.ref
}
- zone.blockMap.addTo(player1)
- zone.blockMap.addTo(pool)
+ guid.register(player1, 1)
+ guid.register(player1.avatar.locker, 5)
player1.Zone = zone
player1.Spawn()
- guid.register(player1.avatar.locker, 5)
+ player1.Position = Vector3(5,5,3) //right in the pool
+ zone.blockMap.addTo(player1)
+ zone.blockMap.addTo(pool)
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control")
-
- guid.register(player1, 1)
+ player1.LogActivity(SpawningActivity(PlayerSource(player1), 0, None))
"PlayerControl" should {
- "take continuous damage if player steps into a pool of death" in {
+ "kill the player if that player steps into a pool of death" in {
assert(player1.Health == 100) //alive
- player1.Position = Vector3(5,5,-3) //right in the pool
+ probe.expectNoMessage(5.seconds)
player1.zoneInteractions() //trigger
-
- probe.receiveOne(250 milliseconds) //wait until oplayer1's implants deinitialize
+ probe.receiveOne(3.seconds)
assert(player1.Health == 0) //ded
}
}
diff --git a/src/test/scala/objects/VehicleControlTest.scala b/src/test/scala/objects/VehicleControlTest.scala
index 460618e7f..b756d25e0 100644
--- a/src/test/scala/objects/VehicleControlTest.scala
+++ b/src/test/scala/objects/VehicleControlTest.scala
@@ -14,12 +14,13 @@ import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.environment._
-import net.psforever.objects.serverobject.environment.interaction.{EscapeFromEnvironment, InteractingWithEnvironment}
+import net.psforever.objects.serverobject.environment.interaction.{EscapeFromEnvironment, InteractingWithEnvironment, RespondsToZoneEnvironment}
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
import net.psforever.objects.serverobject.mount.Mountable
+import net.psforever.objects.sourcing.VehicleSource
import net.psforever.objects.vehicles.VehicleLockState
import net.psforever.objects.vehicles.control.VehicleControl
-import net.psforever.objects.vital.{ShieldCharge, Vitality}
+import net.psforever.objects.vital.{ShieldCharge, SpawningActivity, Vitality}
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game._
import net.psforever.services.ServiceManager
@@ -634,9 +635,10 @@ class VehicleControlInteractWithWaterTest extends ActorTest {
val player1 =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
val avatarProbe = TestProbe()
+ val playerProbe = TestProbe()
val vehicleProbe = TestProbe()
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Water, DeepSquare(-1, 10, 10, 0, 0))
+ val pool = Pool(EnvironmentAttribute.Water, DeepSquare(10, 10, 10, 0, 0))
val zone = new Zone(
id = "test-zone",
new ZoneMap(name = "test-map") {
@@ -649,7 +651,7 @@ class VehicleControlInteractWithWaterTest extends ActorTest {
override def LivePlayers = List(player1)
override def Vehicles = List(vehicle)
override def AvatarEvents = avatarProbe.ref
- override def VehicleEvents = vehicleProbe.ref
+ override def VehicleEvents = avatarProbe.ref
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
}
@@ -666,38 +668,24 @@ class VehicleControlInteractWithWaterTest extends ActorTest {
vehicle.Seats(0).mount(player1)
player1.VehicleSeated = vehicle.GUID
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
- player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control")
- vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-control")
+ player1.Actor = playerProbe.ref
+ vehicle.Actor = vehicleProbe.ref
"VehicleControl" should {
"causes disability when the vehicle drives too deep in water" in {
- vehicle.Position = Vector3(5,5,-3) //right in the pool
+ vehicle.Position = Vector3(5,5,3) //right in the pool
vehicle.zoneInteractions() //trigger
- val msg_drown = avatarProbe.receiveOne(250 milliseconds)
- assert(
- msg_drown match {
- case AvatarServiceMessage(
- "TestCharacter1",
- AvatarAction.OxygenState(
- OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f),
- Some(OxygenStateTarget(PlanetSideGUID(2), _, OxygenState.Suffocation, 100f))
- )
- ) => true
- case _ => false
- }
- )
- //player will die in 60s
- //vehicle will disable in 5s; driver will be kicked
- val msg_kick = vehicleProbe.receiveOne(10 seconds)
- msg_kick match {
- case VehicleServiceMessage(
- "test-zone",
- VehicleAction.KickPassenger(PlanetSideGUID(1), 4, _, PlanetSideGUID(2))
- ) => assert(true)
- case _ => assert(false)
- }
- //player will die, but detailing players death messages is not necessary for this test
+ val msg_drown = playerProbe.receiveOne(250 milliseconds)
+ assert(msg_drown match {
+ case InteractingWithEnvironment(body, _) => body eq pool
+ case _ => false
+ })
+ val msg_disable = vehicleProbe.receiveOne(10 seconds)
+ assert(msg_disable match {
+ case RespondsToZoneEnvironment.Timer(EnvironmentAttribute.Water, _, _, VehicleControl.Disable(true)) => true
+ case _ => false
+ })
}
}
}
@@ -835,7 +823,7 @@ class VehicleControlInteractWithDeathTest extends ActorTest {
val player1 =
Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) //guid=1
val guid = new NumberPoolHub(new MaxNumberSource(15))
- val pool = Pool(EnvironmentAttribute.Death, DeepSquare(-1, 10, 10, 0, 0))
+ val pool = Pool(EnvironmentAttribute.Death, DeepSquare(5, 10, 10, 0, 0))
val zone = new Zone(
id = "test-zone",
new ZoneMap(name = "test-map") {
@@ -866,15 +854,17 @@ class VehicleControlInteractWithDeathTest extends ActorTest {
val (probe, avatarActor) = PlayerControlTest.DummyAvatar(system)
player1.Actor = system.actorOf(Props(classOf[PlayerControl], player1, avatarActor), "player1-control")
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-control")
+ vehicle.LogActivity(SpawningActivity(VehicleSource(vehicle), 0, None))
"VehicleControl" should {
"take continuous damage if vehicle drives into a pool of death" in {
assert(vehicle.Health > 0) //alive
assert(player1.Health == 100) //alive
- vehicle.Position = Vector3(5,5,-3) //right in the pool
+ vehicle.Position = Vector3(5,5,1) //right in the pool
+ probe.expectNoMessage(5 seconds)
vehicle.zoneInteractions() //trigger
- probe.receiveOne(2 seconds) //wait until player1's implants deinitialize
+ probe.receiveOne(2 seconds)
assert(vehicle.Health == 0) //ded
assert(player1.Health == 0) //ded
}